Cycles: Add support for debug passes

Currently only summed number of traversal steps and intersections used by the
camera ray intersection pass is implemented, but in the future we will support
more debug passes which would help checking what things makes the scene slow.
Example of such extra passes could be number of bounces, time spent on the
shader tree evaluation and so.

Implementation from the Cycles side is pretty much straightforward, could only
mention here that it's a build-time option disabled by default.

From the blender side it's implemented as a PASS_DEBUG with several subtypes
possible. This way we don't need to create an extra DNA pass type for each of
the debug passes, saving us a bits.

Reviewers: campbellbarton

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D813
This commit is contained in:
Sergey Sharybin 2014-10-04 19:00:26 +06:00
parent 8ac3c3d3ee
commit 27d660ad20
21 changed files with 223 additions and 2 deletions

@ -282,6 +282,9 @@ set(CYCLES_CUDA_BINARIES_ARCH sm_20 sm_21 sm_30 sm_35 sm_50 CACHE STRING "CUDA a
mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH)
unset(PLATFORM_DEFAULT)
option(WITH_CYCLES_LOGGING "Build cycles with logging support" OFF)
option(WITH_CYCLES_DEBUG "Build cycles with with extra debug capabilties" OFF)
mark_as_advanced(WITH_CYCLES_LOGGING)
mark_as_advanced(WITH_CYCLES_DEBUG)
# LLVM
option(WITH_LLVM "Use LLVM" OFF)

@ -192,7 +192,8 @@ def validate_arguments(args, bc):
'BF_DEBUG_CFLAGS', 'BF_DEBUG_CCFLAGS', 'BF_DEBUG_CXXFLAGS',
'C_WARN', 'CC_WARN', 'CXX_WARN',
'LLIBS', 'PLATFORM_LINKFLAGS', 'MACOSX_ARCHITECTURE', 'MACOSX_SDK', 'XCODE_CUR_VER', 'C_COMPILER_ID',
'BF_CYCLES_CUDA_BINARIES_ARCH', 'BF_PROGRAM_LINKFLAGS', 'MACOSX_DEPLOYMENT_TARGET'
'BF_CYCLES_CUDA_BINARIES_ARCH', 'BF_PROGRAM_LINKFLAGS', 'MACOSX_DEPLOYMENT_TARGET',
'WITH_BF_CYCLES_DEBUG'
]
@ -586,6 +587,7 @@ def read_opts(env, cfg, args):
('BF_CYCLES_CUDA_NVCC', 'CUDA nvcc compiler path', ''),
('BF_CYCLES_CUDA_ENV', 'preset environement nvcc will execute in', ''),
('BF_CYCLES_CUDA_BINARIES_ARCH', 'CUDA architectures to compile binaries for', []),
(BoolVariable('WITH_BF_CYCLES_DEBUG', 'Build Cycles engine with extra debugging capabilities', False)),
(BoolVariable('WITH_BF_OIIO', 'Build with OpenImageIO', False)),
(BoolVariable('WITH_BF_STATICOIIO', 'Statically link to OpenImageIO', False)),

@ -128,6 +128,7 @@ add_definitions(
-DWITH_MULTI
)
# Logging capabilities using GLog library.
if(WITH_CYCLES_LOGGING)
add_definitions(-DWITH_CYCLES_LOGGING)
add_definitions(-DGOOGLE_GLOG_DLL_DECL=)
@ -146,6 +147,11 @@ if(WITH_CYCLES_LOGGING)
endif()
endif()
# Debugging capabilities (debug passes etc).
if(WITH_CYCLES_DEBUG)
add_definitions(-DWITH_CYCLES_DEBUG)
endif()
include_directories(
SYSTEM
${BOOST_INCLUDE_DIR}

@ -59,6 +59,9 @@ if env['WITH_BF_CYCLES_OSL']:
defs.append('OSL_STATIC_LIBRARY')
incs.append(cycles['BF_OSL_INC'])
if env['WITH_BF_CYCLES_DEBUG']:
defs.append('WITH_CYCLES_DEBUG')
incs.extend('. bvh render device kernel kernel/osl kernel/svm util subd'.split())
incs.extend('#intern/guardedalloc #source/blender/makesrna #source/blender/makesdna #source/blender/blenlib'.split())
incs.extend('#source/blender/blenloader ../../source/blender/makesrna/intern'.split())

@ -261,6 +261,14 @@ static PassType get_pass_type(BL::RenderPass b_pass)
case BL::RenderPass::type_SPECULAR:
case BL::RenderPass::type_REFLECTION:
return PASS_NONE;
#ifdef WITH_CYCLES_DEBUG
case BL::RenderPass::type_DEBUG:
{
if(b_pass.debug_type() == BL::RenderPass::debug_type_BVH_TRAVERSAL_STEPS)
return PASS_BVH_TRAVERSAL_STEPS;
break;
}
#endif
}
return PASS_NONE;
@ -423,6 +431,9 @@ void BlenderSession::render()
/* add passes */
vector<Pass> passes;
Pass::add(PASS_COMBINED, passes);
#ifdef WITH_CYCLES_DEBUG
Pass::add(PASS_BVH_TRAVERSAL_STEPS, passes);
#endif
if(session_params.device.advanced_shading) {

@ -24,6 +24,7 @@ set(SRC_HEADERS
kernel_compat_cpu.h
kernel_compat_cuda.h
kernel_compat_opencl.h
kernel_debug.h
kernel_differential.h
kernel_emission.h
kernel_film.h

@ -68,6 +68,10 @@ ccl_device bool BVH_FUNCTION_NAME
isect->u = 0.0f;
isect->v = 0.0f;
#if defined(__KERNEL_DEBUG__)
isect->num_traversal_steps = 0;
#endif
#if defined(__KERNEL_SSE2__)
const shuffle_swap_t shuf_identity = shuffle_swap_identity();
const shuffle_swap_t shuf_swap = shuffle_swap_swap();
@ -226,6 +230,10 @@ ccl_device bool BVH_FUNCTION_NAME
--stackPtr;
}
}
#if defined(__KERNEL_DEBUG__)
isect->num_traversal_steps++;
#endif
}
/* if node is leaf, fetch triangle list */
@ -274,6 +282,10 @@ ccl_device bool BVH_FUNCTION_NAME
}
}
#if defined(__KERNEL_DEBUG__)
isect->num_traversal_steps++;
#endif
/* shadow ray early termination */
#if defined(__KERNEL_SSE2__)
if(hit) {

@ -0,0 +1,35 @@
/*
* Copyright 2011-2014 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
CCL_NAMESPACE_BEGIN
ccl_device_inline void debug_data_init(DebugData *debug_data)
{
debug_data->num_bvh_traversal_steps = 0;
}
ccl_device_inline void kernel_write_debug_passes(KernelGlobals *kg,
ccl_global float *buffer,
PathState *state,
DebugData *debug_data,
int sample)
{
kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_traversal_steps,
sample,
debug_data->num_bvh_traversal_steps);
}
CCL_NAMESPACE_END

@ -45,6 +45,10 @@
#include "kernel_path_surface.h"
#include "kernel_path_volume.h"
#ifdef __KERNEL_DEBUG__
# include "kernel_debug.h"
#endif
CCL_NAMESPACE_BEGIN
ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray,
@ -473,6 +477,11 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
PathState state;
path_state_init(kg, &state, rng, sample, &ray);
#ifdef __KERNEL_DEBUG__
DebugData debug_data;
debug_data_init(&debug_data);
#endif
/* path iteration */
for(;;) {
/* intersect scene */
@ -499,6 +508,12 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
bool hit = scene_intersect(kg, &ray, visibility, &isect, NULL, 0.0f, 0.0f);
#endif
#ifdef __KERNEL_DEBUG__
if(state.flag & PATH_RAY_CAMERA) {
debug_data.num_bvh_traversal_steps += isect.num_traversal_steps;
}
#endif
#ifdef __LAMP_MIS__
if(kernel_data.integrator.use_lamp_mis && !(state.flag & PATH_RAY_CAMERA)) {
/* ray starting from previous non-transparent bounce */
@ -719,6 +734,10 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
kernel_write_light_passes(kg, buffer, &L, sample);
#ifdef __KERNEL_DEBUG__
kernel_write_debug_passes(kg, buffer, &state, &debug_data, sample);
#endif
return make_float4(L_sum.x, L_sum.y, L_sum.z, 1.0f - L_transparent);
}
@ -864,6 +883,11 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
PathState state;
path_state_init(kg, &state, rng, sample, &ray);
#ifdef __KERNEL_DEBUG__
DebugData debug_data;
debug_data_init(&debug_data);
#endif
for(;;) {
/* intersect scene */
Intersection isect;
@ -889,6 +913,12 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
bool hit = scene_intersect(kg, &ray, visibility, &isect, NULL, 0.0f, 0.0f);
#endif
#ifdef __KERNEL_DEBUG__
if(state.flag & PATH_RAY_CAMERA) {
debug_data.num_bvh_traversal_steps += isect.num_traversal_steps;
}
#endif
#ifdef __VOLUME__
/* volume attenuation, emission, scatter */
if(state.volume_stack[0].shader != SHADER_NONE) {
@ -1144,6 +1174,10 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
kernel_write_light_passes(kg, buffer, &L, sample);
#ifdef __KERNEL_DEBUG__
kernel_write_debug_passes(kg, buffer, &state, &debug_data, sample);
#endif
return make_float4(L_sum.x, L_sum.y, L_sum.z, 1.0f - L_transparent);
}

@ -157,6 +157,10 @@ CCL_NAMESPACE_BEGIN
#define __HAIR__
#endif
#ifdef WITH_CYCLES_DEBUG
# define __KERNEL_DEBUG__
#endif
/* Random Numbers */
typedef uint RNG;
@ -313,6 +317,9 @@ typedef enum PassType {
PASS_SUBSURFACE_INDIRECT = 8388608,
PASS_SUBSURFACE_COLOR = 16777216,
PASS_LIGHT = 33554432, /* no real pass, used to force use_light_pass */
#ifdef __KERNEL_DEBUG__
PASS_BVH_TRAVERSAL_STEPS = 67108864,
#endif
} PassType;
#define PASS_ALL (~0)
@ -453,6 +460,10 @@ typedef struct Intersection {
int prim;
int object;
int type;
#ifdef __KERNEL_DEBUG__
int num_traversal_steps;
#endif
} Intersection;
/* Primitives */
@ -850,6 +861,11 @@ typedef struct KernelFilm {
float mist_start;
float mist_inv_depth;
float mist_falloff;
#ifdef __KERNEL_DEBUG__
int pass_bvh_traversal_steps;
int pad[3];
#endif
} KernelFilm;
typedef struct KernelBackground {
@ -976,6 +992,14 @@ typedef struct KernelData {
KernelTables tables;
} KernelData;
#ifdef __KERNEL_DEBUG__
typedef struct DebugData {
// Total number of BVH node travesal steps and primitives intersections
// for the camera rays.
int num_bvh_traversal_steps;
} DebugData;
#endif
CCL_NAMESPACE_END
#endif /* __KERNEL_TYPES_H__ */

@ -190,6 +190,14 @@ bool RenderBuffers::get_pass_rect(PassType type, float exposure, int sample, int
pixels[0] = clamp(f*scale_exposure, 0.0f, 1.0f);
}
}
#ifdef WITH_CYCLES_DEBUG
else if(type == PASS_BVH_TRAVERSAL_STEPS) {
for(int i = 0; i < size; i++, in += pass_stride, pixels++) {
float f = *in;
pixels[0] = f;
}
}
#endif
else {
for(int i = 0; i < size; i++, in += pass_stride, pixels++) {
float f = *in;

@ -146,6 +146,12 @@ void Pass::add(PassType type, vector<Pass>& passes)
case PASS_LIGHT:
/* ignores */
break;
#ifdef WITH_CYCLES_DEBUG
case PASS_BVH_TRAVERSAL_STEPS:
pass.components = 1;
pass.exposure = false;
break;
#endif
}
passes.push_back(pass);
@ -388,6 +394,13 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
case PASS_LIGHT:
kfilm->use_light_pass = 1;
break;
#ifdef WITH_CYCLES_DEBUG
case PASS_BVH_TRAVERSAL_STEPS:
kfilm->pass_bvh_traversal_steps = kfilm->pass_stride;
break;
#endif
case PASS_NONE:
break;
}

@ -242,6 +242,7 @@ typedef enum ScenePassType {
SCE_PASS_SUBSURFACE_DIRECT = (1 << 28),
SCE_PASS_SUBSURFACE_INDIRECT = (1 << 29),
SCE_PASS_SUBSURFACE_COLOR = (1 << 30),
SCE_PASS_DEBUG = (1 << 31), /* This is a virtual pass. */
} ScenePassType;
/* note, srl->passflag is treestore element 'nr' in outliner, short still... */

@ -117,6 +117,9 @@ if env['WITH_BF_OCEANSIM']:
if env['WITH_BF_CYCLES']:
defs.append('WITH_CYCLES')
if env['WITH_BF_CYCLES_DEBUG']:
defs.append('WITH_CYCLES_DEBUG')
if env['WITH_BF_SDL']:
defs.append('WITH_SDL')

@ -155,6 +155,9 @@ set(INC_SYS
if(WITH_CYCLES)
add_definitions(-DWITH_CYCLES)
if(WITH_CYCLES_DEBUG)
add_definitions(-DWITH_CYCLES_DEBUG)
endif()
endif()
if(WITH_PYTHON)

@ -140,6 +140,8 @@ if env['WITH_BF_COLLADA']:
if env['WITH_BF_CYCLES']:
defs.append('WITH_CYCLES')
if env['WITH_BF_CYCLES_DEBUG']:
defs.append('WITH_CYCLES_DEBUG')
if env['WITH_BF_FREESTYLE']:
defs.append('WITH_FREESTYLE')

@ -71,6 +71,9 @@ EnumPropertyItem render_pass_type_items[] = {
{SCE_PASS_SUBSURFACE_DIRECT, "SUBSURFACE_DIRECT", 0, "Subsurface Direct", ""},
{SCE_PASS_SUBSURFACE_INDIRECT, "SUBSURFACE_INDIRECT", 0, "Subsurface Indirect", ""},
{SCE_PASS_SUBSURFACE_COLOR, "SUBSURFACE_COLOR", 0, "Subsurface Color", ""},
#ifdef WITH_CYCLES_DEBUG
{SCE_PASS_DEBUG, "DEBUG", 0, "Pass used for render engine debugging", ""},
#endif
{0, NULL, 0, NULL, NULL}
};
@ -683,6 +686,10 @@ static void rna_def_render_pass(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
static EnumPropertyItem render_pass_debug_type_items[] = {
{RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS, "BVH_TRAVERSAL_STEPS", 0, "BVH Traversal Steps", ""},
};
srna = RNA_def_struct(brna, "RenderPass", NULL);
RNA_def_struct_ui_text(srna, "Render Pass", "");
@ -712,6 +719,11 @@ static void rna_def_render_pass(BlenderRNA *brna)
RNA_def_property_dynamic_array_funcs(prop, "rna_RenderPass_rect_get_length");
RNA_def_property_float_funcs(prop, "rna_RenderPass_rect_get", "rna_RenderPass_rect_set", NULL);
prop = RNA_def_property(srna, "debug_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "debug_type");
RNA_def_property_enum_items(prop, render_pass_debug_type_items);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_define_verify_sdna(1);
}

@ -162,6 +162,10 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
if(WITH_CYCLES AND WITH_CYCLES_DEBUG)
add_definitions(-DWITH_CYCLES_DEBUG)
endif()
if(APPLE)
# SSE math is enabled by default on x86_64
if(CMAKE_OSX_ARCHITECTURES MATCHES "i386")

@ -103,6 +103,9 @@ if env['WITH_BF_GAMEENGINE']:
if env['WITH_BF_INTERNATIONAL']:
defs.append('WITH_INTERNATIONAL')
if env['WITH_BF_CYCLES'] and env['WITH_VF_CYCLES_DEBUG']:
defs.append('WITH_CYCLES_DEBUG')
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'):
incs += ' ' + env['BF_PTHREADS_INC']

@ -72,8 +72,13 @@ typedef struct RenderPass {
char chan_id[8]; /* amount defined in openexr_multi.h */
float *rect;
int rectx, recty;
int debug_type;
} RenderPass;
enum {
RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS = 0,
};
/* a renderlayer is a full image, but with all passes and samples */
/* size of the rects is defined in RenderResult */
/* after render, the Combined pass is in rectf, for renderlayers read from files it is a real pass */

@ -48,6 +48,9 @@
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_report.h"
#ifdef WITH_CYCLES_DEBUG
# include "BKE_scene.h"
#endif
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@ -402,7 +405,7 @@ static int passtype_from_name(const char *str)
/********************************** New **************************************/
static void render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype)
static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype)
{
const char *typestr = get_pass_name(passtype, 0);
RenderPass *rpass = MEM_callocN(sizeof(RenderPass), typestr);
@ -438,8 +441,34 @@ static void render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channel
rect[x] = 10e10;
}
}
return rpass;
}
#ifdef WITH_CYCLES_DEBUG
static const char *debug_pass_type_name_get(int debug_type)
{
switch (debug_type) {
case RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS:
return "BVH Traversal Steps";
}
return "Unknown";
}
static RenderPass *render_layer_add_debug_pass(RenderResult *rr,
RenderLayer *rl,
int channels,
int pass_type,
int debug_type)
{
RenderPass *rpass = render_layer_add_pass(rr, rl, channels, pass_type);
rpass->debug_type = debug_type;
BLI_strncpy(rpass->name,
debug_pass_type_name_get(debug_type),
sizeof(rpass->name));
return rpass;
}
#endif
/* called by main render as well for parts */
/* will read info from Render *re to define layers */
/* called in threads */
@ -578,6 +607,13 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_INDIRECT);
if (srl->passflag & SCE_PASS_SUBSURFACE_COLOR)
render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR);
#ifdef WITH_CYCLES_DEBUG
if(BKE_scene_use_new_shading_nodes(re->scene)) {
render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG,
RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS);
}
#endif
}
/* sss, previewrender and envmap don't do layers, so we make a default one */
if (BLI_listbase_is_empty(&rr->layers) && !(layername && layername[0])) {