From e760972221e68d3c81f2ee3687cc71836dde8ae9 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Wed, 4 Dec 2019 19:57:28 +0100 Subject: Cycles: support for custom shader AOVs Custom render passes are added in the Shader AOVs panel in the view layer settings, with a name and data type. In shader nodes, an AOV Output node is then used to output either a value or color to the pass. Arbitrary names can be used for these passes, as long as they don't conflict with built-in passes that are enabled. The AOV Output node can be used in both material and world shader nodes. Implemented by Lukas, with tweaks by Brecht. Differential Revision: https://developer.blender.org/D4837 --- intern/cycles/blender/addon/engine.py | 124 +++++++++++++-------- intern/cycles/blender/addon/operators.py | 32 ++++++ intern/cycles/blender/addon/properties.py | 38 +++++++ intern/cycles/blender/addon/ui.py | 38 +++++++ intern/cycles/blender/blender_session.cpp | 17 +-- intern/cycles/blender/blender_shader.cpp | 6 + intern/cycles/blender/blender_sync.cpp | 31 ++++-- intern/cycles/kernel/CMakeLists.txt | 2 + intern/cycles/kernel/kernel_bake.h | 12 +- intern/cycles/kernel/kernel_emission.h | 5 +- intern/cycles/kernel/kernel_passes.h | 74 ------------ intern/cycles/kernel/kernel_path.h | 14 ++- intern/cycles/kernel/kernel_path_branched.h | 4 +- intern/cycles/kernel/kernel_shader.h | 7 +- intern/cycles/kernel/kernel_shadow.h | 2 +- intern/cycles/kernel/kernel_subsurface.h | 2 +- intern/cycles/kernel/kernel_types.h | 9 ++ intern/cycles/kernel/kernel_write_passes.h | 95 ++++++++++++++++ intern/cycles/kernel/osl/osl_services.cpp | 1 + .../kernel/split/kernel_indirect_background.h | 4 +- intern/cycles/kernel/split/kernel_shader_eval.h | 4 +- intern/cycles/kernel/svm/svm.h | 13 +++ intern/cycles/kernel/svm/svm_aov.h | 49 ++++++++ intern/cycles/kernel/svm/svm_types.h | 3 + intern/cycles/render/buffers.cpp | 14 +-- intern/cycles/render/buffers.h | 8 +- intern/cycles/render/film.cpp | 43 ++++++- intern/cycles/render/film.h | 2 + intern/cycles/render/graph.cpp | 35 ++++-- intern/cycles/render/graph.h | 5 + intern/cycles/render/nodes.cpp | 52 +++++++++ intern/cycles/render/nodes.h | 20 ++++ intern/cycles/render/shader.cpp | 7 ++ intern/cycles/render/svm.cpp | 62 +++++++++-- intern/cycles/render/svm.h | 1 + 35 files changed, 637 insertions(+), 198 deletions(-) create mode 100644 intern/cycles/kernel/kernel_write_passes.h create mode 100644 intern/cycles/kernel/svm/svm_aov.h (limited to 'intern') diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index 013d86a560b..ee7ac7737c0 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -223,65 +223,95 @@ def system_info(): import _cycles return _cycles.system_info() - -def register_passes(engine, scene, srl): - engine.register_pass(scene, srl, "Combined", 4, "RGBA", 'COLOR') - - if srl.use_pass_z: engine.register_pass(scene, srl, "Depth", 1, "Z", 'VALUE') - if srl.use_pass_mist: engine.register_pass(scene, srl, "Mist", 1, "Z", 'VALUE') - if srl.use_pass_normal: engine.register_pass(scene, srl, "Normal", 3, "XYZ", 'VECTOR') - if srl.use_pass_vector: engine.register_pass(scene, srl, "Vector", 4, "XYZW", 'VECTOR') - if srl.use_pass_uv: engine.register_pass(scene, srl, "UV", 3, "UVA", 'VECTOR') - if srl.use_pass_object_index: engine.register_pass(scene, srl, "IndexOB", 1, "X", 'VALUE') - if srl.use_pass_material_index: engine.register_pass(scene, srl, "IndexMA", 1, "X", 'VALUE') - if srl.use_pass_shadow: engine.register_pass(scene, srl, "Shadow", 3, "RGB", 'COLOR') - if srl.use_pass_ambient_occlusion: engine.register_pass(scene, srl, "AO", 3, "RGB", 'COLOR') - if srl.use_pass_diffuse_direct: engine.register_pass(scene, srl, "DiffDir", 3, "RGB", 'COLOR') - if srl.use_pass_diffuse_indirect: engine.register_pass(scene, srl, "DiffInd", 3, "RGB", 'COLOR') - if srl.use_pass_diffuse_color: engine.register_pass(scene, srl, "DiffCol", 3, "RGB", 'COLOR') - if srl.use_pass_glossy_direct: engine.register_pass(scene, srl, "GlossDir", 3, "RGB", 'COLOR') - if srl.use_pass_glossy_indirect: engine.register_pass(scene, srl, "GlossInd", 3, "RGB", 'COLOR') - if srl.use_pass_glossy_color: engine.register_pass(scene, srl, "GlossCol", 3, "RGB", 'COLOR') - if srl.use_pass_transmission_direct: engine.register_pass(scene, srl, "TransDir", 3, "RGB", 'COLOR') - if srl.use_pass_transmission_indirect: engine.register_pass(scene, srl, "TransInd", 3, "RGB", 'COLOR') - if srl.use_pass_transmission_color: engine.register_pass(scene, srl, "TransCol", 3, "RGB", 'COLOR') - if srl.use_pass_subsurface_direct: engine.register_pass(scene, srl, "SubsurfaceDir", 3, "RGB", 'COLOR') - if srl.use_pass_subsurface_indirect: engine.register_pass(scene, srl, "SubsurfaceInd", 3, "RGB", 'COLOR') - if srl.use_pass_subsurface_color: engine.register_pass(scene, srl, "SubsurfaceCol", 3, "RGB", 'COLOR') - if srl.use_pass_emit: engine.register_pass(scene, srl, "Emit", 3, "RGB", 'COLOR') - if srl.use_pass_environment: engine.register_pass(scene, srl, "Env", 3, "RGB", 'COLOR') - +def list_render_passes(srl): + # Builtin Blender passes. + yield ("Combined", "RGBA", 'COLOR') + + if srl.use_pass_z: yield ("Depth", "Z", 'VALUE') + if srl.use_pass_mist: yield ("Mist", "Z", 'VALUE') + if srl.use_pass_normal: yield ("Normal", "XYZ", 'VECTOR') + if srl.use_pass_vector: yield ("Vector", "XYZW", 'VECTOR') + if srl.use_pass_uv: yield ("UV", "UVA", 'VECTOR') + if srl.use_pass_object_index: yield ("IndexOB", "X", 'VALUE') + if srl.use_pass_material_index: yield ("IndexMA", "X", 'VALUE') + if srl.use_pass_shadow: yield ("Shadow", "RGB", 'COLOR') + if srl.use_pass_ambient_occlusion: yield ("AO", "RGB", 'COLOR') + if srl.use_pass_diffuse_direct: yield ("DiffDir", "RGB", 'COLOR') + if srl.use_pass_diffuse_indirect: yield ("DiffInd", "RGB", 'COLOR') + if srl.use_pass_diffuse_color: yield ("DiffCol", "RGB", 'COLOR') + if srl.use_pass_glossy_direct: yield ("GlossDir", "RGB", 'COLOR') + if srl.use_pass_glossy_indirect: yield ("GlossInd", "RGB", 'COLOR') + if srl.use_pass_glossy_color: yield ("GlossCol", "RGB", 'COLOR') + if srl.use_pass_transmission_direct: yield ("TransDir", "RGB", 'COLOR') + if srl.use_pass_transmission_indirect: yield ("TransInd", "RGB", 'COLOR') + if srl.use_pass_transmission_color: yield ("TransCol", "RGB", 'COLOR') + if srl.use_pass_subsurface_direct: yield ("SubsurfaceDir", "RGB", 'COLOR') + if srl.use_pass_subsurface_indirect: yield ("SubsurfaceInd", "RGB", 'COLOR') + if srl.use_pass_subsurface_color: yield ("SubsurfaceCol", "RGB", 'COLOR') + if srl.use_pass_emit: yield ("Emit", "RGB", 'COLOR') + if srl.use_pass_environment: yield ("Env", "RGB", 'COLOR') + + # Cycles specific passes. crl = srl.cycles - if crl.pass_debug_render_time: engine.register_pass(scene, srl, "Debug Render Time", 1, "X", 'VALUE') - if crl.pass_debug_bvh_traversed_nodes: engine.register_pass(scene, srl, "Debug BVH Traversed Nodes", 1, "X", 'VALUE') - if crl.pass_debug_bvh_traversed_instances: engine.register_pass(scene, srl, "Debug BVH Traversed Instances", 1, "X", 'VALUE') - if crl.pass_debug_bvh_intersections: engine.register_pass(scene, srl, "Debug BVH Intersections", 1, "X", 'VALUE') - if crl.pass_debug_ray_bounces: engine.register_pass(scene, srl, "Debug Ray Bounces", 1, "X", 'VALUE') - if crl.use_pass_volume_direct: engine.register_pass(scene, srl, "VolumeDir", 3, "RGB", 'COLOR') - if crl.use_pass_volume_indirect: engine.register_pass(scene, srl, "VolumeInd", 3, "RGB", 'COLOR') - + if crl.pass_debug_render_time: yield ("Debug Render Time", "X", 'VALUE') + if crl.pass_debug_bvh_traversed_nodes: yield ("Debug BVH Traversed Nodes", "X", 'VALUE') + if crl.pass_debug_bvh_traversed_instances: yield ("Debug BVH Traversed Instances", "X", 'VALUE') + if crl.pass_debug_bvh_intersections: yield ("Debug BVH Intersections", "X", 'VALUE') + if crl.pass_debug_ray_bounces: yield ("Debug Ray Bounces", "X", 'VALUE') + if crl.use_pass_volume_direct: yield ("VolumeDir", "RGB", 'COLOR') + if crl.use_pass_volume_indirect: yield ("VolumeInd", "RGB", 'COLOR') + + # Cryptomatte passes. if crl.use_pass_crypto_object: for i in range(0, crl.pass_crypto_depth, 2): - engine.register_pass(scene, srl, "CryptoObject" + '{:02d}'.format(i//2), 4, "RGBA", 'COLOR') + yield ("CryptoObject" + '{:02d}'.format(i//2), "RGBA", 'COLOR') if crl.use_pass_crypto_material: for i in range(0, crl.pass_crypto_depth, 2): - engine.register_pass(scene, srl, "CryptoMaterial" + '{:02d}'.format(i//2), 4, "RGBA", 'COLOR') + yield ("CryptoMaterial" + '{:02d}'.format(i//2), "RGBA", 'COLOR') if srl.cycles.use_pass_crypto_asset: for i in range(0, srl.cycles.pass_crypto_depth, 2): - engine.register_pass(scene, srl, "CryptoAsset" + '{:02d}'.format(i//2), 4, "RGBA", 'COLOR') + yield ("CryptoAsset" + '{:02d}'.format(i//2), "RGBA", 'COLOR') + # Denoising passes. if crl.use_denoising or crl.denoising_store_passes: - engine.register_pass(scene, srl, "Noisy Image", 4, "RGBA", 'COLOR') + yield ("Noisy Image", "RGBA", 'COLOR') if crl.denoising_store_passes: - engine.register_pass(scene, srl, "Denoising Normal", 3, "XYZ", 'VECTOR') - engine.register_pass(scene, srl, "Denoising Albedo", 3, "RGB", 'COLOR') - engine.register_pass(scene, srl, "Denoising Depth", 1, "Z", 'VALUE') - engine.register_pass(scene, srl, "Denoising Shadowing", 1, "X", 'VALUE') - engine.register_pass(scene, srl, "Denoising Variance", 3, "RGB", 'COLOR') - engine.register_pass(scene, srl, "Denoising Intensity", 1, "X", 'VALUE') + yield ("Denoising Normal", "XYZ", 'VECTOR') + yield ("Denoising Albedo", "RGB", 'COLOR') + yield ("Denoising Depth", "Z", 'VALUE') + yield ("Denoising Shadowing", "X", 'VALUE') + yield ("Denoising Variance", "RGB", 'COLOR') + yield ("Denoising Intensity", "X", 'VALUE') clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect", "denoising_glossy_direct", "denoising_glossy_indirect", "denoising_transmission_direct", "denoising_transmission_indirect", "denoising_subsurface_direct", "denoising_subsurface_indirect") if any(getattr(crl, option) for option in clean_options): - engine.register_pass(scene, srl, "Denoising Clean", 3, "RGB", 'COLOR') + yield ("Denoising Clean", "RGB", 'COLOR') + + # Custom AOV passes. + for aov in crl.aovs: + if aov.type == 'VALUE': + yield (aov.name, "X", 'VALUE') + else: + yield (aov.name, "RGBA", 'COLOR') + +def register_passes(engine, scene, view_layer): + # Detect duplicate render pass names, first one wins. + listed = set() + for name, channelids, channeltype in list_render_passes(view_layer): + if name not in listed: + engine.register_pass(scene, view_layer, name, len(channelids), channelids, channeltype) + listed.add(name) + +def detect_conflicting_passes(view_layer): + # Detect conflicting render pass names for UI. + counter = {} + for name, _, _ in list_render_passes(view_layer): + counter[name] = counter.get(name, 0) + 1 + + for aov in view_layer.cycles.aovs: + if counter[aov.name] > 1: + aov.conflict = "Conflicts with another render pass with the same name" + else: + aov.conflict = "" diff --git a/intern/cycles/blender/addon/operators.py b/intern/cycles/blender/addon/operators.py index e75d3ab7549..80bb663330b 100644 --- a/intern/cycles/blender/addon/operators.py +++ b/intern/cycles/blender/addon/operators.py @@ -44,6 +44,36 @@ class CYCLES_OT_use_shading_nodes(Operator): return {'FINISHED'} +class CYCLES_OT_add_aov(bpy.types.Operator): + """Add an AOV pass""" + bl_idname="cycles.add_aov" + bl_label="Add AOV" + + def execute(self, context): + view_layer = context.view_layer + cycles_view_layer = view_layer.cycles + + cycles_view_layer.aovs.add() + + view_layer.update_render_passes() + return {'FINISHED'} + + +class CYCLES_OT_remove_aov(bpy.types.Operator): + """Remove an AOV pass""" + bl_idname="cycles.remove_aov" + bl_label="Remove AOV" + + def execute(self, context): + view_layer = context.view_layer + cycles_view_layer = view_layer.cycles + + cycles_view_layer.aovs.remove(cycles_view_layer.active_aov) + + view_layer.update_render_passes() + return {'FINISHED'} + + class CYCLES_OT_denoise_animation(Operator): "Denoise rendered animation sequence using current scene and view " \ "layer settings. Requires denoising data passes and output to " \ @@ -167,6 +197,8 @@ class CYCLES_OT_merge_images(Operator): classes = ( CYCLES_OT_use_shading_nodes, + CYCLES_OT_add_aov, + CYCLES_OT_remove_aov, CYCLES_OT_denoise_animation, CYCLES_OT_merge_images ) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 26e1a6a223a..e09f15b46e8 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -19,6 +19,7 @@ import bpy from bpy.props import ( BoolProperty, + CollectionProperty, EnumProperty, FloatProperty, IntProperty, @@ -31,6 +32,7 @@ from math import pi # enums import _cycles +from . import engine enum_devices = ( ('CPU', "CPU", "Use CPU for rendering"), @@ -190,6 +192,10 @@ enum_view3d_shading_render_pass= ( ('MIST', "Mist", "Show the Mist render pass", 32), ) +enum_aov_types = ( + ('VALUE', "Value", "Write a Value pass", 0), + ('COLOR', "Color", "Write a Color pass", 1), +) class CyclesRenderSettings(bpy.types.PropertyGroup): @@ -1218,8 +1224,29 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup): def update_render_passes(self, context): view_layer = context.view_layer view_layer.update_render_passes() + engine.detect_conflicting_passes(view_layer) +class CyclesAOVPass(bpy.types.PropertyGroup): + name: StringProperty( + name="Name", + description="Name of the pass, to use in the AOV Output shader node", + update=update_render_passes, + default="AOV" + ) + type: EnumProperty( + name="Type", + description="Pass data type", + update=update_render_passes, + items=enum_aov_types, + default='COLOR' + ) + conflict: StringProperty( + name="Conflict", + description="If there is a conflict with another render passes, message explaining why", + default="" + ) + class CyclesRenderLayerSettings(bpy.types.PropertyGroup): pass_debug_bvh_traversed_nodes: BoolProperty( @@ -1378,6 +1405,15 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup): update=update_render_passes, ) + aovs: CollectionProperty( + type=CyclesAOVPass, + description="Custom render passes that can be output by shader nodes", + ) + active_aov: IntProperty( + default=0, + min=0 + ) + @classmethod def register(cls): bpy.types.ViewLayer.cycles = PointerProperty( @@ -1552,6 +1588,7 @@ def register(): bpy.utils.register_class(CyclesCurveRenderSettings) bpy.utils.register_class(CyclesDeviceSettings) bpy.utils.register_class(CyclesPreferences) + bpy.utils.register_class(CyclesAOVPass) bpy.utils.register_class(CyclesRenderLayerSettings) bpy.utils.register_class(CyclesView3DShadingSettings) @@ -1573,5 +1610,6 @@ def unregister(): bpy.utils.unregister_class(CyclesCurveRenderSettings) bpy.utils.unregister_class(CyclesDeviceSettings) bpy.utils.unregister_class(CyclesPreferences) + bpy.utils.unregister_class(CyclesAOVPass) bpy.utils.unregister_class(CyclesRenderLayerSettings) bpy.utils.unregister_class(CyclesView3DShadingSettings) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 4ff154d4bcd..011c83d44b8 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -918,6 +918,42 @@ class CYCLES_RENDER_PT_passes_debug(CyclesButtonsPanel, Panel): layout.prop(cycles_view_layer, "pass_debug_ray_bounces") +class CYCLES_RENDER_UL_aov(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + row = layout.row() + split = row.split(factor=0.65) + icon = 'ERROR' if item.conflict else 'NONE' + split.row().prop(item, "name", text="", icon=icon, emboss=False) + split.row().prop(item, "type", text="", emboss=False) + + +class CYCLES_RENDER_PT_passes_aov(CyclesButtonsPanel, Panel): + bl_label = "Shader AOV" + bl_context = "view_layer" + bl_parent_id = "CYCLES_RENDER_PT_passes" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + cycles_view_layer = context.view_layer.cycles + + row = layout.row() + col = row.column() + col.template_list("CYCLES_RENDER_UL_aov", "aovs", cycles_view_layer, "aovs", cycles_view_layer, "active_aov", rows=2) + + col = row.column() + sub = col.column(align=True) + sub.operator("cycles.add_aov", icon='ADD', text="") + sub.operator("cycles.remove_aov", icon='REMOVE', text="") + + if cycles_view_layer.active_aov < len(cycles_view_layer.aovs): + active_aov = cycles_view_layer.aovs[cycles_view_layer.active_aov] + if active_aov.conflict: + layout.label(text=active_aov.conflict, icon='ERROR') + + class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel): bl_label = "Denoising" bl_context = "view_layer" @@ -2233,6 +2269,8 @@ classes = ( CYCLES_RENDER_PT_passes_light, CYCLES_RENDER_PT_passes_crypto, CYCLES_RENDER_PT_passes_debug, + CYCLES_RENDER_UL_aov, + CYCLES_RENDER_PT_passes_aov, CYCLES_RENDER_PT_filter, CYCLES_RENDER_PT_override, CYCLES_RENDER_PT_denoising, diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 1f0816a6edb..53f2fdb91b9 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -793,18 +793,13 @@ void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay, for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) { BL::RenderPass b_pass(*b_iter); - - /* find matching pass type */ - PassType pass_type = BlenderSync::get_pass_type(b_pass); int components = b_pass.channels(); - bool read = false; - if (pass_type != PASS_NONE) { - /* copy pixels */ - read = buffers->get_pass_rect( - pass_type, exposure, sample, components, &pixels[0], b_pass.name()); - } - else { + /* Copy pixels from regular render passes. */ + bool read = buffers->get_pass_rect(b_pass.name(), exposure, sample, components, &pixels[0]); + + /* If denoising pass, */ + if (!read) { int denoising_offset = BlenderSync::get_denoising_pass(b_pass); if (denoising_offset >= 0) { read = buffers->get_denoising_pass_rect( @@ -822,7 +817,7 @@ void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay, else { /* copy combined pass */ BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str())); - if (buffers->get_pass_rect(PASS_COMBINED, exposure, sample, 4, &pixels[0], "Combined")) + if (buffers->get_pass_rect("Combined", exposure, sample, 4, &pixels[0])) b_combined_pass.rect(&pixels[0]); } } diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 412e54ea29d..c3564eac940 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -921,6 +921,12 @@ static ShaderNode *add_node(Scene *scene, disp->attribute = ""; node = disp; } + else if (b_node.is_a(&RNA_ShaderNodeOutputAOV)) { + BL::ShaderNodeOutputAOV b_aov_node(b_node); + OutputAOVNode *aov = new OutputAOVNode(); + aov->name = b_aov_node.name(); + node = aov; + } if (node) { node->name = b_node.name(); diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index f04455ff75e..bb52c740bfb 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -531,7 +531,7 @@ vector BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa if (pass_type == PASS_MOTION && scene->integrator->motion_blur) continue; if (pass_type != PASS_NONE) - Pass::add(pass_type, passes); + Pass::add(pass_type, passes, b_pass.name().c_str()); } PointerRNA crp = RNA_pointer_get(&b_view_layer.ptr, "cycles"); @@ -570,32 +570,32 @@ vector BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa #ifdef __KERNEL_DEBUG__ if (get_boolean(crp, "pass_debug_bvh_traversed_nodes")) { b_engine.add_pass("Debug BVH Traversed Nodes", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_BVH_TRAVERSED_NODES, passes); + Pass::add(PASS_BVH_TRAVERSED_NODES, passes, "Debug BVH Traversed Nodes"); } if (get_boolean(crp, "pass_debug_bvh_traversed_instances")) { b_engine.add_pass("Debug BVH Traversed Instances", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes); + Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes, "Debug BVH Traversed Instances"); } if (get_boolean(crp, "pass_debug_bvh_intersections")) { b_engine.add_pass("Debug BVH Intersections", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_BVH_INTERSECTIONS, passes); + Pass::add(PASS_BVH_INTERSECTIONS, passes, "Debug BVH Intersections"); } if (get_boolean(crp, "pass_debug_ray_bounces")) { b_engine.add_pass("Debug Ray Bounces", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_RAY_BOUNCES, passes); + Pass::add(PASS_RAY_BOUNCES, passes, "Debug Ray Bounces"); } #endif if (get_boolean(crp, "pass_debug_render_time")) { b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_RENDER_TIME, passes); + Pass::add(PASS_RENDER_TIME, passes, "Debug Render Time"); } if (get_boolean(crp, "use_pass_volume_direct")) { b_engine.add_pass("VolumeDir", 3, "RGB", b_view_layer.name().c_str()); - Pass::add(PASS_VOLUME_DIRECT, passes); + Pass::add(PASS_VOLUME_DIRECT, passes, "VolumeDir"); } if (get_boolean(crp, "use_pass_volume_indirect")) { b_engine.add_pass("VolumeInd", 3, "RGB", b_view_layer.name().c_str()); - Pass::add(PASS_VOLUME_INDIRECT, passes); + Pass::add(PASS_VOLUME_INDIRECT, passes, "VolumeInd"); } /* Cryptomatte stores two ID/weight pairs per RGBA layer. @@ -635,6 +635,21 @@ vector BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa CRYPT_ACCURATE); } + RNA_BEGIN (&crp, b_aov, "aovs") { + bool is_color = (get_enum(b_aov, "type") == 1); + string name = get_string(b_aov, "name"); + + if (is_color) { + b_engine.add_pass(name.c_str(), 4, "RGBA", b_view_layer.name().c_str()); + Pass::add(PASS_AOV_COLOR, passes, name.c_str()); + } + else { + b_engine.add_pass(name.c_str(), 1, "X", b_view_layer.name().c_str()); + Pass::add(PASS_AOV_VALUE, passes, name.c_str()); + } + } + RNA_END; + return passes; } diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 4077a1ad516..99172f30b8b 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -129,6 +129,7 @@ set(SRC_HEADERS kernel_types.h kernel_volume.h kernel_work_stealing.h + kernel_write_passes.h ) set(SRC_KERNELS_CPU_HEADERS @@ -182,6 +183,7 @@ set(SRC_CLOSURE_HEADERS set(SRC_SVM_HEADERS svm/svm.h svm/svm_ao.h + svm/svm_aov.h svm/svm_attribute.h svm/svm_bevel.h svm/svm_blackbody.h diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index cd1ca5ea7ec..8e5a279e6cd 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -45,7 +45,7 @@ ccl_device_inline void compute_light_pass( path_state_init(kg, &emission_sd, &state, rng_hash, sample, NULL); /* evaluate surface shader */ - shader_eval_surface(kg, sd, &state, state.flag); + shader_eval_surface(kg, sd, &state, NULL, state.flag); /* TODO, disable more closures we don't need besides transparent */ shader_bsdf_disable_transparency(kg, sd); @@ -209,12 +209,12 @@ ccl_device float3 kernel_bake_evaluate_direct_indirect(KernelGlobals *kg, } else { /* surface color of the pass only */ - shader_eval_surface(kg, sd, state, 0); + shader_eval_surface(kg, sd, state, NULL, 0); return kernel_bake_shader_bsdf(kg, sd, type); } } else { - shader_eval_surface(kg, sd, state, 0); + shader_eval_surface(kg, sd, state, NULL, 0); color = kernel_bake_shader_bsdf(kg, sd, type); } @@ -332,7 +332,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, case SHADER_EVAL_EMISSION: { if (type != SHADER_EVAL_NORMAL || (sd.flag & SD_HAS_BUMP)) { int path_flag = (type == SHADER_EVAL_EMISSION) ? PATH_RAY_EMISSION : 0; - shader_eval_surface(kg, &sd, &state, path_flag); + shader_eval_surface(kg, &sd, &state, NULL, path_flag); } if (type == SHADER_EVAL_NORMAL) { @@ -445,7 +445,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, /* evaluate */ int path_flag = 0; /* we can't know which type of BSDF this is for */ - shader_eval_surface(kg, &sd, &state, path_flag | PATH_RAY_EMISSION); + shader_eval_surface(kg, &sd, &state, NULL, path_flag | PATH_RAY_EMISSION); out = shader_background_eval(&sd); break; } @@ -524,7 +524,7 @@ ccl_device void kernel_background_evaluate(KernelGlobals *kg, /* evaluate */ int path_flag = 0; /* we can't know which type of BSDF this is for */ - shader_eval_surface(kg, &sd, &state, path_flag | PATH_RAY_EMISSION); + shader_eval_surface(kg, &sd, &state, NULL, path_flag | PATH_RAY_EMISSION); float3 color = shader_background_eval(&sd); /* write output */ diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index 459280cf433..e70958c2b06 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -73,7 +73,7 @@ ccl_device_noinline_cpu float3 direct_emissive_eval(KernelGlobals *kg, /* No proper path flag, we're evaluating this for all closures. that's * weak but we'd have to do multiple evaluations otherwise. */ path_state_modify_bounce(state, true); - shader_eval_surface(kg, emission_sd, state, PATH_RAY_EMISSION); + shader_eval_surface(kg, emission_sd, state, NULL, PATH_RAY_EMISSION); path_state_modify_bounce(state, false); /* Evaluate closures. */ @@ -294,6 +294,7 @@ ccl_device_noinline_cpu bool indirect_lamp_emission(KernelGlobals *kg, ccl_device_noinline_cpu float3 indirect_background(KernelGlobals *kg, ShaderData *emission_sd, ccl_addr_space PathState *state, + ccl_global float *buffer, ccl_addr_space Ray *ray) { #ifdef __BACKGROUND__ @@ -322,7 +323,7 @@ ccl_device_noinline_cpu float3 indirect_background(KernelGlobals *kg, # endif path_state_modify_bounce(state, true); - shader_eval_surface(kg, emission_sd, state, state->flag | PATH_RAY_EMISSION); + shader_eval_surface(kg, emission_sd, state, buffer, state->flag | PATH_RAY_EMISSION); path_state_modify_bounce(state, false); L = shader_background_eval(emission_sd); diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h index 3e423e42573..828add9dc13 100644 --- a/intern/cycles/kernel/kernel_passes.h +++ b/intern/cycles/kernel/kernel_passes.h @@ -14,85 +14,11 @@ * limitations under the License. */ -#if defined(__SPLIT_KERNEL__) || defined(__KERNEL_CUDA__) -# define __ATOMIC_PASS_WRITE__ -#endif - #include "kernel/kernel_id_passes.h" CCL_NAMESPACE_BEGIN -ccl_device_inline void kernel_write_pass_float(ccl_global float *buffer, float value) -{ - ccl_global float *buf = buffer; -#ifdef __ATOMIC_PASS_WRITE__ - atomic_add_and_fetch_float(buf, value); -#else - *buf += value; -#endif -} - -ccl_device_inline void kernel_write_pass_float3(ccl_global float *buffer, float3 value) -{ -#ifdef __ATOMIC_PASS_WRITE__ - ccl_global float *buf_x = buffer + 0; - ccl_global float *buf_y = buffer + 1; - ccl_global float *buf_z = buffer + 2; - - atomic_add_and_fetch_float(buf_x, value.x); - atomic_add_and_fetch_float(buf_y, value.y); - atomic_add_and_fetch_float(buf_z, value.z); -#else - ccl_global float3 *buf = (ccl_global float3 *)buffer; - *buf += value; -#endif -} - -ccl_device_inline void kernel_write_pass_float4(ccl_global float *buffer, float4 value) -{ -#ifdef __ATOMIC_PASS_WRITE__ - ccl_global float *buf_x = buffer + 0; - ccl_global float *buf_y = buffer + 1; - ccl_global float *buf_z = buffer + 2; - ccl_global float *buf_w = buffer + 3; - - atomic_add_and_fetch_float(buf_x, value.x); - atomic_add_and_fetch_float(buf_y, value.y); - atomic_add_and_fetch_float(buf_z, value.z); - atomic_add_and_fetch_float(buf_w, value.w); -#else - ccl_global float4 *buf = (ccl_global float4 *)buffer; - *buf += value; -#endif -} - #ifdef __DENOISING_FEATURES__ -ccl_device_inline void kernel_write_pass_float_variance(ccl_global float *buffer, float value) -{ - kernel_write_pass_float(buffer, value); - - /* The online one-pass variance update that's used for the mega-kernel can't easily be - * implemented with atomics, - * so for the split kernel the E[x^2] - 1/N * (E[x])^2 fallback is used. */ - kernel_write_pass_float(buffer + 1, value * value); -} - -# ifdef __ATOMIC_PASS_WRITE__ -# define kernel_write_pass_float3_unaligned kernel_write_pass_float3 -# else -ccl_device_inline void kernel_write_pass_float3_unaligned(ccl_global float *buffer, float3 value) -{ - buffer[0] += value.x; - buffer[1] += value.y; - buffer[2] += value.z; -} -# endif - -ccl_device_inline void kernel_write_pass_float3_variance(ccl_global float *buffer, float3 value) -{ - kernel_write_pass_float3_unaligned(buffer, value); - kernel_write_pass_float3_unaligned(buffer + 3, value * value); -} ccl_device_inline void kernel_write_denoising_shadow(KernelGlobals *kg, ccl_global float *buffer, diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index 55abe39c465..693efad8c50 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -27,6 +27,7 @@ #include "kernel/geom/geom.h" #include "kernel/bvh/bvh.h" +#include "kernel/kernel_write_passes.h" #include "kernel/kernel_accumulate.h" #include "kernel/kernel_shader.h" #include "kernel/kernel_light.h" @@ -116,6 +117,7 @@ ccl_device_forceinline void kernel_path_background(KernelGlobals *kg, ccl_addr_space Ray *ray, float3 throughput, ShaderData *sd, + ccl_global float *buffer, PathRadiance *L) { /* eval background shader if nothing hit */ @@ -136,7 +138,7 @@ ccl_device_forceinline void kernel_path_background(KernelGlobals *kg, #ifdef __BACKGROUND__ /* sample background shader */ - float3 L_background = indirect_background(kg, sd, state, ray); + float3 L_background = indirect_background(kg, sd, state, buffer, ray); path_radiance_accum_background(L, state, throughput, L_background); #endif /* __BACKGROUND__ */ } @@ -267,7 +269,7 @@ ccl_device_forceinline bool kernel_path_shader_apply(KernelGlobals *kg, float3 bg = make_float3(0.0f, 0.0f, 0.0f); if (!kernel_data.background.transparent) { - bg = indirect_background(kg, emission_sd, state, ray); + bg = indirect_background(kg, emission_sd, state, NULL, ray); } path_radiance_accum_shadowcatcher(L, throughput, bg); } @@ -418,7 +420,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, /* Shade background. */ if (!hit) { - kernel_path_background(kg, state, ray, throughput, sd, L); + kernel_path_background(kg, state, ray, throughput, sd, NULL, L); break; } else if (path_state_ao_bounce(kg, state)) { @@ -434,7 +436,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, # endif /* Evaluate shader. */ - shader_eval_surface(kg, sd, state, state->flag); + shader_eval_surface(kg, sd, state, NULL, state->flag); shader_prepare_closures(sd, state); /* Apply shadow catcher, holdout, emission. */ @@ -556,7 +558,7 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg, /* Shade background. */ if (!hit) { - kernel_path_background(kg, state, ray, throughput, &sd, L); + kernel_path_background(kg, state, ray, throughput, &sd, buffer, L); break; } else if (path_state_ao_bounce(kg, state)) { @@ -572,7 +574,7 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg, # endif /* Evaluate shader. */ - shader_eval_surface(kg, &sd, state, state->flag); + shader_eval_surface(kg, &sd, state, buffer, state->flag); shader_prepare_closures(&sd, state); /* Apply shadow catcher, holdout, emission. */ diff --git a/intern/cycles/kernel/kernel_path_branched.h b/intern/cycles/kernel/kernel_path_branched.h index ea6b23e7eb4..39309379f0c 100644 --- a/intern/cycles/kernel/kernel_path_branched.h +++ b/intern/cycles/kernel/kernel_path_branched.h @@ -405,7 +405,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg, /* Shade background. */ if (!hit) { - kernel_path_background(kg, &state, &ray, throughput, &sd, L); + kernel_path_background(kg, &state, &ray, throughput, &sd, buffer, L); break; } @@ -417,7 +417,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg, if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { # endif - shader_eval_surface(kg, &sd, &state, state.flag); + shader_eval_surface(kg, &sd, &state, buffer, state.flag); shader_merge_closures(&sd); /* Apply shadow catcher, holdout, emission. */ diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 7ccb99cad2a..d03faff4242 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -1076,6 +1076,7 @@ ccl_device float3 shader_holdout_eval(KernelGlobals *kg, ShaderData *sd) ccl_device void shader_eval_surface(KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, + ccl_global float *buffer, int path_flag) { PROFILING_INIT(kg, PROFILING_SHADER_EVAL); @@ -1107,7 +1108,7 @@ ccl_device void shader_eval_surface(KernelGlobals *kg, #endif { #ifdef __SVM__ - svm_eval_nodes(kg, sd, state, SHADER_TYPE_SURFACE, path_flag); + svm_eval_nodes(kg, sd, state, buffer, SHADER_TYPE_SURFACE, path_flag); #else if (sd->object == OBJECT_NONE) { sd->closure_emission_background = make_float3(0.8f, 0.8f, 0.8f); @@ -1319,7 +1320,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg, else # endif { - svm_eval_nodes(kg, sd, state, SHADER_TYPE_VOLUME, path_flag); + svm_eval_nodes(kg, sd, state, NULL, SHADER_TYPE_VOLUME, path_flag); } # endif @@ -1348,7 +1349,7 @@ ccl_device void shader_eval_displacement(KernelGlobals *kg, else # endif { - svm_eval_nodes(kg, sd, state, SHADER_TYPE_DISPLACEMENT, 0); + svm_eval_nodes(kg, sd, state, NULL, SHADER_TYPE_DISPLACEMENT, 0); } #endif } diff --git a/intern/cycles/kernel/kernel_shadow.h b/intern/cycles/kernel/kernel_shadow.h index 61fcc61264a..b3ae29932da 100644 --- a/intern/cycles/kernel/kernel_shadow.h +++ b/intern/cycles/kernel/kernel_shadow.h @@ -71,7 +71,7 @@ ccl_device_forceinline bool shadow_handle_transparent_isect(KernelGlobals *kg, /* Attenuation from transparent surface. */ if (!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) { path_state_modify_bounce(state, true); - shader_eval_surface(kg, shadow_sd, state, PATH_RAY_SHADOW); + shader_eval_surface(kg, shadow_sd, state, NULL, PATH_RAY_SHADOW); path_state_modify_bounce(state, false); *throughput *= shader_bsdf_transparency(kg, shadow_sd); } diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h index dbe2c12ce81..23e30db1b08 100644 --- a/intern/cycles/kernel/kernel_subsurface.h +++ b/intern/cycles/kernel/kernel_subsurface.h @@ -138,7 +138,7 @@ ccl_device void subsurface_color_bump_blur( if (bump || texture_blur > 0.0f) { /* average color and normal at incoming point */ - shader_eval_surface(kg, sd, state, state->flag); + shader_eval_surface(kg, sd, state, NULL, state->flag); float3 in_color = shader_bssrdf_sum(sd, (bump) ? N : NULL, NULL); /* we simply divide out the average color and multiply with the average diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 7306c32d7c8..c35e345763a 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -222,6 +222,8 @@ typedef enum ShaderEvalType { SHADER_EVAL_TRANSMISSION_COLOR, SHADER_EVAL_SUBSURFACE_COLOR, SHADER_EVAL_EMISSION, + SHADER_EVAL_AOV_COLOR, + SHADER_EVAL_AOV_VALUE, /* light passes */ SHADER_EVAL_AO, @@ -371,6 +373,8 @@ typedef enum PassType { #endif PASS_RENDER_TIME, PASS_CRYPTOMATTE, + PASS_AOV_COLOR, + PASS_AOV_VALUE, PASS_CATEGORY_MAIN_END = 31, PASS_MIST = 32, @@ -1244,6 +1248,11 @@ typedef struct KernelFilm { int pass_denoising_clean; int denoising_flags; + int pass_aov_color; + int pass_aov_value; + int pad1; + int pad2; + /* XYZ to rendering color space transform. float4 instead of float3 to * ensure consistent padding/alignment across devices. */ float4 xyz_to_r; diff --git a/intern/cycles/kernel/kernel_write_passes.h b/intern/cycles/kernel/kernel_write_passes.h new file mode 100644 index 00000000000..410218d91d4 --- /dev/null +++ b/intern/cycles/kernel/kernel_write_passes.h @@ -0,0 +1,95 @@ +/* + * Copyright 2011-2013 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. + */ + +#if defined(__SPLIT_KERNEL__) || defined(__KERNEL_CUDA__) +# define __ATOMIC_PASS_WRITE__ +#endif + +CCL_NAMESPACE_BEGIN + +ccl_device_inline void kernel_write_pass_float(ccl_global float *buffer, float value) +{ + ccl_global float *buf = buffer; +#ifdef __ATOMIC_PASS_WRITE__ + atomic_add_and_fetch_float(buf, value); +#else + *buf += value; +#endif +} + +ccl_device_inline void kernel_write_pass_float3(ccl_global float *buffer, float3 value) +{ +#ifdef __ATOMIC_PASS_WRITE__ + ccl_global float *buf_x = buffer + 0; + ccl_global float *buf_y = buffer + 1; + ccl_global float *buf_z = buffer + 2; + + atomic_add_and_fetch_float(buf_x, value.x); + atomic_add_and_fetch_float(buf_y, value.y); + atomic_add_and_fetch_float(buf_z, value.z); +#else + ccl_global float3 *buf = (ccl_global float3 *)buffer; + *buf += value; +#endif +} + +ccl_device_inline void kernel_write_pass_float4(ccl_global float *buffer, float4 value) +{ +#ifdef __ATOMIC_PASS_WRITE__ + ccl_global float *buf_x = buffer + 0; + ccl_global float *buf_y = buffer + 1; + ccl_global float *buf_z = buffer + 2; + ccl_global float *buf_w = buffer + 3; + + atomic_add_and_fetch_float(buf_x, value.x); + atomic_add_and_fetch_float(buf_y, value.y); + atomic_add_and_fetch_float(buf_z, value.z); + atomic_add_and_fetch_float(buf_w, value.w); +#else + ccl_global float4 *buf = (ccl_global float4 *)buffer; + *buf += value; +#endif +} + +#ifdef __DENOISING_FEATURES__ +ccl_device_inline void kernel_write_pass_float_variance(ccl_global float *buffer, float value) +{ + kernel_write_pass_float(buffer, value); + + /* The online one-pass variance update that's used for the megakernel can't easily be implemented + * with atomics, so for the split kernel the E[x^2] - 1/N * (E[x])^2 fallback is used. */ + kernel_write_pass_float(buffer + 1, value * value); +} + +# ifdef __ATOMIC_PASS_WRITE__ +# define kernel_write_pass_float3_unaligned kernel_write_pass_float3 +# else +ccl_device_inline void kernel_write_pass_float3_unaligned(ccl_global float *buffer, float3 value) +{ + buffer[0] += value.x; + buffer[1] += value.y; + buffer[2] += value.z; +} +# endif + +ccl_device_inline void kernel_write_pass_float3_variance(ccl_global float *buffer, float3 value) +{ + kernel_write_pass_float3_unaligned(buffer, value); + kernel_write_pass_float3_unaligned(buffer + 3, value * value); +} +#endif /* __DENOISING_FEATURES__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 1b161fbc8ee..767bd7702ae 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -44,6 +44,7 @@ #include "kernel/kernel_globals.h" #include "kernel/kernel_color.h" #include "kernel/kernel_random.h" +#include "kernel/kernel_write_passes.h" #include "kernel/kernel_projection.h" #include "kernel/kernel_differential.h" #include "kernel/kernel_montecarlo.h" diff --git a/intern/cycles/kernel/split/kernel_indirect_background.h b/intern/cycles/kernel/split/kernel_indirect_background.h index b1c65f61e2c..6d500650cc0 100644 --- a/intern/cycles/kernel/split/kernel_indirect_background.h +++ b/intern/cycles/kernel/split/kernel_indirect_background.h @@ -58,8 +58,10 @@ ccl_device void kernel_indirect_background(KernelGlobals *kg) ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; float3 throughput = kernel_split_state.throughput[ray_index]; ShaderData *sd = kernel_split_sd(sd, ray_index); + uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; + ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - kernel_path_background(kg, state, ray, throughput, sd, L); + kernel_path_background(kg, state, ray, throughput, sd, buffer, L); kernel_split_path_end(kg, ray_index); } } diff --git a/intern/cycles/kernel/split/kernel_shader_eval.h b/intern/cycles/kernel/split/kernel_shader_eval.h index 8e39c9797e5..c760a2b2049 100644 --- a/intern/cycles/kernel/split/kernel_shader_eval.h +++ b/intern/cycles/kernel/split/kernel_shader_eval.h @@ -50,8 +50,10 @@ ccl_device void kernel_shader_eval(KernelGlobals *kg) ccl_global char *ray_state = kernel_split_state.ray_state; if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; + uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; + ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, state->flag); + shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, buffer, state->flag); #ifdef __BRANCHED_PATH__ if (kernel_data.integrator.branched) { shader_merge_closures(kernel_split_sd(sd, ray_index)); diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 18f086d6726..c4d7164a4d8 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -164,6 +164,7 @@ CCL_NAMESPACE_END #include "kernel/svm/svm_math_util.h" #include "kernel/svm/svm_mapping_util.h" +#include "kernel/svm/svm_aov.h" #include "kernel/svm/svm_attribute.h" #include "kernel/svm/svm_gradient.h" #include "kernel/svm/svm_blackbody.h" @@ -218,6 +219,7 @@ CCL_NAMESPACE_BEGIN ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, + ccl_global float *buffer, ShaderType type, int path_flag) { @@ -467,6 +469,17 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, case NODE_IES: svm_node_ies(kg, sd, stack, node, &offset); break; + case NODE_AOV_START: + if (!svm_node_aov_check(state, buffer)) { + return; + } + break; + case NODE_AOV_COLOR: + svm_node_aov_color(kg, sd, stack, node, buffer); + break; + case NODE_AOV_VALUE: + svm_node_aov_value(kg, sd, stack, node, buffer); + break; # endif /* __EXTRA_NODES__ */ #endif /* NODES_GROUP(NODE_GROUP_LEVEL_2) */ diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h new file mode 100644 index 00000000000..899e466d099 --- /dev/null +++ b/intern/cycles/kernel/svm/svm_aov.h @@ -0,0 +1,49 @@ +/* + * Copyright 2011-2013 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 bool svm_node_aov_check(ccl_addr_space PathState *state, + ccl_global float *buffer) +{ + int path_flag = state->flag; + + bool is_primary = (path_flag & PATH_RAY_CAMERA) && (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)); + + return ((buffer != NULL) && is_primary); +} + +ccl_device void svm_node_aov_color( + KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ccl_global float *buffer) +{ + float3 val = stack_load_float3(stack, node.y); + + if (buffer) { + kernel_write_pass_float4(buffer + kernel_data.film.pass_aov_color + 4 * node.z, + make_float4(val.x, val.y, val.z, 1.0f)); + } +} + +ccl_device void svm_node_aov_value( + KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ccl_global float *buffer) +{ + float val = stack_load_float(stack, node.y); + + if (buffer) { + kernel_write_pass_float(buffer + kernel_data.film.pass_aov_value + node.z, val); + } +} +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 040e7c6a0f8..8dbb147e76a 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -150,6 +150,9 @@ typedef enum ShaderNodeType { NODE_VERTEX_COLOR, NODE_VERTEX_COLOR_BUMP_DX, NODE_VERTEX_COLOR_BUMP_DY, + NODE_AOV_START, + NODE_AOV_VALUE, + NODE_AOV_COLOR, } ShaderNodeType; typedef enum NodeAttributeType { diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 49e15d9eaf1..fe8606e1939 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -234,7 +234,7 @@ bool RenderBuffers::get_denoising_pass_rect( } bool RenderBuffers::get_pass_rect( - PassType type, float exposure, int sample, int components, float *pixels, const string &name) + const string &name, float exposure, int sample, int components, float *pixels) { if (buffer.data() == NULL) { return false; @@ -245,18 +245,14 @@ bool RenderBuffers::get_pass_rect( for (size_t j = 0; j < params.passes.size(); j++) { Pass &pass = params.passes[j]; - if (pass.type != type) { + /* Pass is identified by both type and name, multiple of the same type + * may exist with a different name. */ + if (pass.name != name) { pass_offset += pass.components; continue; } - /* Tell Cryptomatte passes apart by their name. */ - if (pass.type == PASS_CRYPTOMATTE) { - if (pass.name != name) { - pass_offset += pass.components; - continue; - } - } + PassType type = pass.type; float *in = buffer.data() + pass_offset; int pass_stride = params.get_passes_size(); diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h index 74bb656c5a4..1042b42810f 100644 --- a/intern/cycles/render/buffers.h +++ b/intern/cycles/render/buffers.h @@ -88,12 +88,8 @@ class RenderBuffers { void zero(); bool copy_from_device(); - bool get_pass_rect(PassType type, - float exposure, - int sample, - int components, - float *pixels, - const string &name); + bool get_pass_rect( + const string &name, float exposure, int sample, int components, float *pixels); bool get_denoising_pass_rect( int offset, float exposure, int sample, int components, float *pixels); }; diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 7f5bec2a66e..379b0e6c214 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -163,6 +163,12 @@ void Pass::add(PassType type, vector &passes, const char *name) case PASS_CRYPTOMATTE: pass.components = 4; break; + case PASS_AOV_COLOR: + pass.components = 4; + break; + case PASS_AOV_VALUE: + pass.components = 1; + break; default: assert(false); break; @@ -327,7 +333,7 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) kfilm->pass_stride = 0; kfilm->use_light_pass = use_light_visibility || use_sample_clamp; - bool have_cryptomatte = false; + bool have_cryptomatte = false, have_aov_color = false, have_aov_value = false; for (size_t i = 0; i < passes.size(); i++) { Pass &pass = passes[i]; @@ -464,6 +470,18 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) kfilm->pass_stride; have_cryptomatte = true; break; + case PASS_AOV_COLOR: + if (!have_aov_color) { + kfilm->pass_aov_color = kfilm->pass_stride; + have_aov_color = true; + } + break; + case PASS_AOV_VALUE: + if (!have_aov_value) { + kfilm->pass_aov_value = kfilm->pass_stride; + have_aov_value = true; + } + break; default: assert(false); break; @@ -569,4 +587,27 @@ void Film::tag_update(Scene * /*scene*/) need_update = true; } +int Film::get_aov_offset(string name, bool &is_color) +{ + int num_color = 0, num_value = 0; + foreach (const Pass &pass, passes) { + if (pass.type == PASS_AOV_COLOR) { + num_color++; + } + else if (pass.type == PASS_AOV_VALUE) { + num_value++; + } + else { + continue; + } + + if (pass.name == name) { + is_color = (pass.type == PASS_AOV_COLOR); + return (is_color ? num_color : num_value) - 1; + } + } + + return -1; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/film.h b/intern/cycles/render/film.h index 2c4e07d60ae..48673af0114 100644 --- a/intern/cycles/render/film.h +++ b/intern/cycles/render/film.h @@ -93,6 +93,8 @@ class Film : public Node { bool modified(const Film &film); void tag_passes_update(Scene *scene, const vector &passes_, bool update_passes = true); void tag_update(Scene *scene); + + int get_aov_offset(string name, bool &is_color); }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index 501d1e33a9b..0e520c700a7 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -55,6 +55,25 @@ bool check_node_inputs_traversed(const ShaderNode *node, const ShaderNodeSet &do } /* namespace */ +/* Sockets */ + +void ShaderInput::disconnect() +{ + if (link) { + link->links.erase(remove(link->links.begin(), link->links.end(), this), link->links.end()); + } + link = NULL; +} + +void ShaderOutput::disconnect() +{ + foreach (ShaderInput *sock, links) { + sock->link = NULL; + } + + links.clear(); +} + /* Node */ ShaderNode::ShaderNode(const NodeType *type) : Node(type) @@ -285,11 +304,7 @@ void ShaderGraph::disconnect(ShaderOutput *from) assert(!finalized); simplified = false; - foreach (ShaderInput *sock, from->links) { - sock->link = NULL; - } - - from->links.clear(); + from->disconnect(); } void ShaderGraph::disconnect(ShaderInput *to) @@ -298,10 +313,7 @@ void ShaderGraph::disconnect(ShaderInput *to) assert(to->link); simplified = false; - ShaderOutput *from = to->link; - - to->link = NULL; - from->links.erase(remove(from->links.begin(), from->links.end(), to), from->links.end()); + to->disconnect(); } void ShaderGraph::relink(ShaderInput *from, ShaderInput *to) @@ -782,6 +794,11 @@ void ShaderGraph::clean(Scene *scene) /* break cycles */ break_cycles(output(), visited, on_stack); + foreach (ShaderNode *node, nodes) { + if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) { + break_cycles(node, visited, on_stack); + } + } /* disconnect unused nodes */ foreach (ShaderNode *node, nodes) { diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h index cade04de374..0ea7935f714 100644 --- a/intern/cycles/render/graph.h +++ b/intern/cycles/render/graph.h @@ -67,6 +67,7 @@ enum ShaderNodeSpecialType { SHADER_SPECIAL_TYPE_COMBINE_CLOSURE, SHADER_SPECIAL_TYPE_OUTPUT, SHADER_SPECIAL_TYPE_BUMP, + SHADER_SPECIAL_TYPE_OUTPUT_AOV, }; /* Input @@ -104,6 +105,8 @@ class ShaderInput { ((Node *)parent)->set(socket_type, f); } + void disconnect(); + const SocketType &socket_type; ShaderNode *parent; ShaderOutput *link; @@ -130,6 +133,8 @@ class ShaderOutput { return socket_type.type; } + void disconnect(); + const SocketType &socket_type; ShaderNode *parent; vector links; diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 26f16d5ee80..5e12d79bc6b 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -5709,6 +5709,58 @@ void ClampNode::compile(OSLCompiler &compiler) compiler.add(this, "node_clamp"); } +/* AOV Output */ + +NODE_DEFINE(OutputAOVNode) +{ + NodeType *type = NodeType::add("aov_output", create, NodeType::SHADER); + + SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f)); + SOCKET_IN_FLOAT(value, "Value", 0.0f); + + SOCKET_STRING(name, "AOV Name", ustring("")); + + return type; +} + +OutputAOVNode::OutputAOVNode() : ShaderNode(node_type) +{ + special_type = SHADER_SPECIAL_TYPE_OUTPUT_AOV; + slot = -1; +} + +void OutputAOVNode::simplify_settings(Scene *scene) +{ + slot = scene->film->get_aov_offset(name.string(), is_color); + if (slot == -1) { + slot = scene->film->get_aov_offset(name.string(), is_color); + } + + if (slot == -1 || is_color) { + input("Value")->disconnect(); + } + if (slot == -1 || !is_color) { + input("Color")->disconnect(); + } +} + +void OutputAOVNode::compile(SVMCompiler &compiler) +{ + assert(slot >= 0); + + if (is_color) { + compiler.add_node(NODE_AOV_COLOR, compiler.stack_assign(input("Color")), slot); + } + else { + compiler.add_node(NODE_AOV_VALUE, compiler.stack_assign(input("Value")), slot); + } +} + +void OutputAOVNode::compile(OSLCompiler & /*compiler*/) +{ + /* TODO */ +} + /* Math */ NODE_DEFINE(MathNode) diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index ae762f3db1c..54124cd2175 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -189,6 +189,26 @@ class OutputNode : public ShaderNode { } }; +class OutputAOVNode : public ShaderNode { + public: + SHADER_NODE_CLASS(OutputAOVNode) + virtual void simplify_settings(Scene *scene); + + float value; + float3 color; + + ustring name; + + /* Don't allow output node de-duplication. */ + virtual bool equals(const ShaderNode & /*other*/) + { + return false; + } + + int slot; + bool is_color; +}; + class GradientTextureNode : public TextureNode { public: SHADER_NODE_CLASS(GradientTextureNode) diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 00d9cd5e672..3b73fa4139f 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -225,6 +225,13 @@ Shader::~Shader() bool Shader::is_constant_emission(float3 *emission) { + /* If the shader has AOVs, they need to be evaluated, so we can't skip the shader. */ + foreach (ShaderNode *node, graph->nodes) { + if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) { + return false; + } + } + ShaderInput *surf = graph->output()->input("Surface"); if (surf->link == NULL) { diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index f69c89b1fd4..8466742ae38 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -554,6 +554,24 @@ void SVMCompiler::generated_shared_closure_nodes(ShaderNode *root_node, } } +void SVMCompiler::generate_aov_node(ShaderNode *node, CompilerState *state) +{ + /* execute dependencies for node */ + foreach (ShaderInput *in, node->inputs) { + if (in->link != NULL) { + ShaderNodeSet dependencies; + find_dependencies(dependencies, state->nodes_done, in); + generate_svm_nodes(dependencies, state); + } + } + + /* compile node itself */ + generate_node(node, state->nodes_done); + + state->nodes_done.insert(node); + state->nodes_done_flag[node->id] = true; +} + void SVMCompiler::generate_multi_closure(ShaderNode *root_node, ShaderNode *node, CompilerState *state) @@ -703,21 +721,21 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty current_graph = graph; /* get input in output node */ - ShaderNode *node = graph->output(); + ShaderNode *output = graph->output(); ShaderInput *clin = NULL; switch (type) { case SHADER_TYPE_SURFACE: - clin = node->input("Surface"); + clin = output->input("Surface"); break; case SHADER_TYPE_VOLUME: - clin = node->input("Volume"); + clin = output->input("Volume"); break; case SHADER_TYPE_DISPLACEMENT: - clin = node->input("Displacement"); + clin = output->input("Displacement"); break; case SHADER_TYPE_BUMP: - clin = node->input("Normal"); + clin = output->input("Normal"); break; default: assert(0); @@ -728,10 +746,10 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty memset((void *)&active_stack, 0, sizeof(active_stack)); current_svm_nodes.clear(); - foreach (ShaderNode *node_iter, graph->nodes) { - foreach (ShaderInput *input, node_iter->inputs) + foreach (ShaderNode *node, graph->nodes) { + foreach (ShaderInput *input, node->inputs) input->stack_offset = SVM_STACK_INVALID; - foreach (ShaderOutput *output, node_iter->outputs) + foreach (ShaderOutput *output, node->outputs) output->stack_offset = SVM_STACK_INVALID; } @@ -745,6 +763,7 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty } if (shader->used) { + CompilerState state(graph); if (clin->link) { bool generate = false; @@ -769,13 +788,36 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty } if (generate) { - CompilerState state(graph); generate_multi_closure(clin->link->parent, clin->link->parent, &state); } } /* compile output node */ - node->compile(*this); + output->compile(*this); + + if (type == SHADER_TYPE_SURFACE) { + vector aov_outputs; + foreach (ShaderNode *node, graph->nodes) { + if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) { + OutputAOVNode *aov_node = static_cast(node); + if (aov_node->slot >= 0) { + aov_outputs.push_back(aov_node); + } + } + } + if (aov_outputs.size() > 0) { + /* AOV passes are only written if the object is directly visible, so + * there is no point in evaluating all the nodes generated only for the + * AOV outputs if that's not the case. Therefore, we insert + * NODE_AOV_START into the shader before the AOV-only nodes are + * generated which tells the kernel that it can stop evaluation + * early if AOVs will not be written. */ + add_node(NODE_AOV_START, 0, 0, 0); + foreach (OutputAOVNode *node, aov_outputs) { + generate_aov_node(node, &state); + } + } + } } /* add node to restore state after bump shader has finished */ diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index 7eac3e19bb3..d1534567bea 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -204,6 +204,7 @@ class SVMCompiler { ShaderInput *input, ShaderNode *skip_node = NULL); void generate_node(ShaderNode *node, ShaderNodeSet &done); + void generate_aov_node(ShaderNode *node, CompilerState *state); void generate_closure_node(ShaderNode *node, CompilerState *state); void generated_shared_closure_nodes(ShaderNode *root_node, ShaderNode *node, -- cgit v1.2.3