Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/intern
diff options
context:
space:
mode:
authorLukas Stockner <lukasstockner97>2019-12-04 21:57:28 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2019-12-10 22:44:46 +0300
commite760972221e68d3c81f2ee3687cc71836dde8ae9 (patch)
treeb1a2efbb17c05a429e4509d336a1eb14c73cfb8c /intern
parent35b5888b157d05d378df3acc899d28856a9eb9a4 (diff)
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
Diffstat (limited to 'intern')
-rw-r--r--intern/cycles/blender/addon/engine.py124
-rw-r--r--intern/cycles/blender/addon/operators.py32
-rw-r--r--intern/cycles/blender/addon/properties.py38
-rw-r--r--intern/cycles/blender/addon/ui.py38
-rw-r--r--intern/cycles/blender/blender_session.cpp17
-rw-r--r--intern/cycles/blender/blender_shader.cpp6
-rw-r--r--intern/cycles/blender/blender_sync.cpp31
-rw-r--r--intern/cycles/kernel/CMakeLists.txt2
-rw-r--r--intern/cycles/kernel/kernel_bake.h12
-rw-r--r--intern/cycles/kernel/kernel_emission.h5
-rw-r--r--intern/cycles/kernel/kernel_passes.h74
-rw-r--r--intern/cycles/kernel/kernel_path.h14
-rw-r--r--intern/cycles/kernel/kernel_path_branched.h4
-rw-r--r--intern/cycles/kernel/kernel_shader.h7
-rw-r--r--intern/cycles/kernel/kernel_shadow.h2
-rw-r--r--intern/cycles/kernel/kernel_subsurface.h2
-rw-r--r--intern/cycles/kernel/kernel_types.h9
-rw-r--r--intern/cycles/kernel/kernel_write_passes.h95
-rw-r--r--intern/cycles/kernel/osl/osl_services.cpp1
-rw-r--r--intern/cycles/kernel/split/kernel_indirect_background.h4
-rw-r--r--intern/cycles/kernel/split/kernel_shader_eval.h4
-rw-r--r--intern/cycles/kernel/svm/svm.h13
-rw-r--r--intern/cycles/kernel/svm/svm_aov.h49
-rw-r--r--intern/cycles/kernel/svm/svm_types.h3
-rw-r--r--intern/cycles/render/buffers.cpp14
-rw-r--r--intern/cycles/render/buffers.h8
-rw-r--r--intern/cycles/render/film.cpp43
-rw-r--r--intern/cycles/render/film.h2
-rw-r--r--intern/cycles/render/graph.cpp35
-rw-r--r--intern/cycles/render/graph.h5
-rw-r--r--intern/cycles/render/nodes.cpp52
-rw-r--r--intern/cycles/render/nodes.h20
-rw-r--r--intern/cycles/render/shader.cpp7
-rw-r--r--intern/cycles/render/svm.cpp62
-rw-r--r--intern/cycles/render/svm.h1
35 files changed, 637 insertions, 198 deletions
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<Pass> 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<Pass> 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<Pass> 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<Pass> &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<Pass> &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<ShaderInput *> 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<OutputAOVNode *> aov_outputs;
+ foreach (ShaderNode *node, graph->nodes) {
+ if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) {
+ OutputAOVNode *aov_node = static_cast<OutputAOVNode *>(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,