Merge branch 'blender2.7'
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Mon, 11 Feb 2019 12:37:45 +0000 (13:37 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Mon, 11 Feb 2019 12:37:45 +0000 (13:37 +0100)
1  2 
intern/cycles/blender/addon/__init__.py
intern/cycles/blender/addon/operators.py
intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_device.cpp
intern/cycles/blender/blender_device.h
intern/cycles/blender/blender_python.cpp
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_sync.cpp
intern/cycles/render/session.cpp

index 0000000,c39aa38..ed7f5c6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,133 +1,133 @@@
 -    input_filepath = StringProperty(
+ #
+ # Copyright 2011-2019 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.
+ #
+ # <pep8 compliant>
+ import bpy
+ from bpy.types import Operator
+ from bpy.props import StringProperty
+ class CYCLES_OT_use_shading_nodes(Operator):
+     """Enable nodes on a material, world or light"""
+     bl_idname = "cycles.use_shading_nodes"
+     bl_label = "Use Nodes"
+     @classmethod
+     def poll(cls, context):
+         return (getattr(context, "material", False) or getattr(context, "world", False) or
+                 getattr(context, "light", False))
+     def execute(self, context):
+         if context.material:
+             context.material.use_nodes = True
+         elif context.world:
+             context.world.use_nodes = True
+         elif context.light:
+             context.light.use_nodes = True
+         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 """ \
+     """OpenEXR multilayer files"""
+     bl_idname = "cycles.denoise_animation"
+     bl_label = "Denoise Animation"
 -    output_filepath = StringProperty(
++    input_filepath: StringProperty(
+         name='Input Filepath',
+         description='File path for frames to denoise. If not specified, uses the render file path from the scene',
+         default='',
+         subtype='FILE_PATH')
 -        preferences = context.user_preferences
++    output_filepath: StringProperty(
+         name='Output Filepath',
+         description='If not specified, renders will be denoised in-place',
+         default='',
+         subtype='FILE_PATH')
+     def execute(self, context):
+         import os
 -        render_layer = scene.render.layers.active
++        preferences = context.preferences
+         scene = context.scene
 -                            render_layer.as_pointer(),
++        view_layer = context.view_layer
+         in_filepath = self.input_filepath
+         out_filepath = self.output_filepath
+         if in_filepath == '':
+             in_filepath = scene.render.filepath
+         if out_filepath == '':
+             out_filepath = in_filepath
+         # Backup since we will overwrite the scene path temporarily
+         original_filepath = scene.render.filepath
+         # Expand filepaths for each frame so we match Blender render output exactly.
+         in_filepaths = []
+         out_filepaths = []
+         for frame in range(scene.frame_start, scene.frame_end + 1):
+             scene.render.filepath = in_filepath
+             filepath = scene.render.frame_path(frame=frame)
+             in_filepaths.append(filepath)
+             if not os.path.isfile(filepath):
+                 scene.render.filepath = original_filepath
+                 self.report({'ERROR'}, f"Frame '{filepath}' not found, animation must be complete.")
+                 return {'CANCELLED'}
+             scene.render.filepath = out_filepath
+             filepath = scene.render.frame_path(frame=frame)
+             out_filepaths.append(filepath)
+         scene.render.filepath = original_filepath
+         # Run denoiser
+         # TODO: support cancel and progress reports.
+         import _cycles
+         try:
+             _cycles.denoise(preferences.as_pointer(),
+                             scene.as_pointer(),
++                            view_layer.as_pointer(),
+                             input=in_filepaths,
+                             output=out_filepaths)
+         except Exception as e:
+             self.report({'ERROR'}, str(e))
+             return {'FINISHED'}
+         self.report({'INFO'}, "Denoising completed.")
+         return {'FINISHED'}
+ classes = (
+     CYCLES_OT_use_shading_nodes,
+     CYCLES_OT_denoise_animation
+ )
+ def register():
+     from bpy.utils import register_class
+     for cls in classes:
+         register_class(cls)
+ def unregister():
+     from bpy.utils import unregister_class
+     for cls in classes:
+         unregister_class(cls)
@@@ -1236,163 -1219,209 +1236,169 @@@ def update_render_passes(self, context)
  
  
  class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
 -    @classmethod
 -    def register(cls):
 -        bpy.types.SceneRenderLayer.cycles = PointerProperty(
 -            name="Cycles SceneRenderLayer Settings",
 -            description="Cycles SceneRenderLayer Settings",
 -            type=cls,
 -        )
 -        cls.pass_debug_bvh_traversed_nodes = BoolProperty(
 -            name="Debug BVH Traversed Nodes",
 -            description="Store Debug BVH Traversed Nodes pass",
 -            default=False,
 -            update=update_render_passes,
 -        )
 -        cls.pass_debug_bvh_traversed_instances = BoolProperty(
 -            name="Debug BVH Traversed Instances",
 -            description="Store Debug BVH Traversed Instances pass",
 -            default=False,
 -            update=update_render_passes,
 -        )
 -        cls.pass_debug_bvh_intersections = BoolProperty(
 -            name="Debug BVH Intersections",
 -            description="Store Debug BVH Intersections",
 -            default=False,
 -            update=update_render_passes,
 -        )
 -        cls.pass_debug_ray_bounces = BoolProperty(
 -            name="Debug Ray Bounces",
 -            description="Store Debug Ray Bounces pass",
 -            default=False,
 -            update=update_render_passes,
 -        )
 -        cls.pass_debug_render_time = BoolProperty(
 -            name="Debug Render Time",
 -            description="Render time in milliseconds per sample and pixel",
 -            default=False,
 -            update=update_render_passes,
 -        )
 -        cls.use_pass_volume_direct = BoolProperty(
 -            name="Volume Direct",
 -            description="Deliver direct volumetric scattering pass",
 -            default=False,
 -            update=update_render_passes,
 -        )
 -        cls.use_pass_volume_indirect = BoolProperty(
 -            name="Volume Indirect",
 -            description="Deliver indirect volumetric scattering pass",
 -            default=False,
 -            update=update_render_passes,
 -        )
  
 -        cls.use_denoising = BoolProperty(
 -            name="Use Denoising",
 -            description="Denoise the rendered image",
 -            default=False,
 -            update=update_render_passes,
 -        )
 -        cls.denoising_diffuse_direct = BoolProperty(
 -            name="Diffuse Direct",
 -            description="Denoise the direct diffuse lighting",
 -            default=True,
 -        )
 -        cls.denoising_diffuse_indirect = BoolProperty(
 -            name="Diffuse Indirect",
 -            description="Denoise the indirect diffuse lighting",
 -            default=True,
 -        )
 -        cls.denoising_glossy_direct = BoolProperty(
 -            name="Glossy Direct",
 -            description="Denoise the direct glossy lighting",
 -            default=True,
 -        )
 -        cls.denoising_glossy_indirect = BoolProperty(
 -            name="Glossy Indirect",
 -            description="Denoise the indirect glossy lighting",
 -            default=True,
 -        )
 -        cls.denoising_transmission_direct = BoolProperty(
 -            name="Transmission Direct",
 -            description="Denoise the direct transmission lighting",
 -            default=True,
 -        )
 -        cls.denoising_transmission_indirect = BoolProperty(
 -            name="Transmission Indirect",
 -            description="Denoise the indirect transmission lighting",
 -            default=True,
 -        )
 -        cls.denoising_subsurface_direct = BoolProperty(
 -            name="Subsurface Direct",
 -            description="Denoise the direct subsurface lighting",
 -            default=True,
 -        )
 -        cls.denoising_subsurface_indirect = BoolProperty(
 -            name="Subsurface Indirect",
 -            description="Denoise the indirect subsurface lighting",
 -            default=True,
 -        )
 -        cls.denoising_strength = FloatProperty(
 -            name="Denoising Strength",
 -            description="Controls neighbor pixel weighting for the denoising filter (lower values preserve more detail, but aren't as smooth)",
 -            min=0.0, max=1.0,
 -            default=0.5,
 -        )
 -        cls.denoising_feature_strength = FloatProperty(
 -            name="Denoising Feature Strength",
 -            description="Controls removal of noisy image feature passes (lower values preserve more detail, but aren't as smooth)",
 -            min=0.0, max=1.0,
 -            default=0.5,
 -        )
 -        cls.denoising_radius = IntProperty(
 -            name="Denoising Radius",
 -            description="Size of the image area that's used to denoise a pixel (higher values are smoother, but might lose detail and are slower)",
 -            min=1, max=25,
 -            default=8,
 -        )
 -        cls.denoising_relative_pca = BoolProperty(
 -            name="Relative filter",
 -            description="When removing pixels that don't carry information, use a relative threshold instead of an absolute one (can help to reduce artifacts, but might cause detail loss around edges)",
 -            default=False,
 -        )
 -        cls.denoising_store_passes = BoolProperty(
 -            name="Store denoising passes",
 -            description="Store the denoising feature passes and the noisy image",
 -            default=False,
 -            update=update_render_passes,
 -        )
 -        denoising_neighbor_frames: IntProperty(
 -            name="Neighbor Frames",
 -            description="Number of neighboring frames to use for denoising animations (more frames produce smoother results at the cost of performance)",
 -            min=0, max=7,
 -            default=0,
 -        )
 -        cls.use_pass_crypto_object = BoolProperty(
 -                name="Cryptomatte Object",
 -                description="Render cryptomatte object pass, for isolating objects in compositing",
 -                default=False,
 -                update=update_render_passes,
 -                )
 -        cls.use_pass_crypto_material = BoolProperty(
 -                name="Cryptomatte Material",
 -                description="Render cryptomatte material pass, for isolating materials in compositing",
 -                default=False,
 -                update=update_render_passes,
 -                )
 -        cls.use_pass_crypto_asset = BoolProperty(
 -                name="Cryptomatte Asset",
 -                description="Render cryptomatte asset pass, for isolating groups of objects with the same parent",
 -                default=False,
 -                update=update_render_passes,
 -                )
 -        cls.pass_crypto_depth = IntProperty(
 -                name="Cryptomatte Levels",
 -                description="Sets how many unique objects can be distinguished per pixel",
 -                default=6, min=2, max=16, step=2,
 -                update=update_render_passes,
 -                )
 -        cls.pass_crypto_accurate = BoolProperty(
 -                name="Cryptomatte Accurate",
 -                description="Gerenate a more accurate Cryptomatte pass. CPU only, may render slower and use more memory",
 -                default=True,
 -                update=update_render_passes,
 -                )
 -    @classmethod
 -    def unregister(cls):
 -        del bpy.types.SceneRenderLayer.cycles
 +    pass_debug_bvh_traversed_nodes: BoolProperty(
 +        name="Debug BVH Traversed Nodes",
 +        description="Store Debug BVH Traversed Nodes pass",
 +        default=False,
 +        update=update_render_passes,
 +    )
 +    pass_debug_bvh_traversed_instances: BoolProperty(
 +        name="Debug BVH Traversed Instances",
 +        description="Store Debug BVH Traversed Instances pass",
 +        default=False,
 +        update=update_render_passes,
 +    )
 +    pass_debug_bvh_intersections: BoolProperty(
 +        name="Debug BVH Intersections",
 +        description="Store Debug BVH Intersections",
 +        default=False,
 +        update=update_render_passes,
 +    )
 +    pass_debug_ray_bounces: BoolProperty(
 +        name="Debug Ray Bounces",
 +        description="Store Debug Ray Bounces pass",
 +        default=False,
 +        update=update_render_passes,
 +    )
 +    pass_debug_render_time: BoolProperty(
 +        name="Debug Render Time",
 +        description="Render time in milliseconds per sample and pixel",
 +        default=False,
 +        update=update_render_passes,
 +    )
 +    use_pass_volume_direct: BoolProperty(
 +        name="Volume Direct",
 +        description="Deliver direct volumetric scattering pass",
 +        default=False,
 +        update=update_render_passes,
 +    )
 +    use_pass_volume_indirect: BoolProperty(
 +        name="Volume Indirect",
 +        description="Deliver indirect volumetric scattering pass",
 +        default=False,
 +        update=update_render_passes,
 +    )
  
 +    use_denoising: BoolProperty(
 +        name="Use Denoising",
 +        description="Denoise the rendered image",
 +        default=False,
 +        update=update_render_passes,
 +    )
 +    denoising_diffuse_direct: BoolProperty(
 +        name="Diffuse Direct",
 +        description="Denoise the direct diffuse lighting",
 +        default=True,
 +    )
 +    denoising_diffuse_indirect: BoolProperty(
 +        name="Diffuse Indirect",
 +        description="Denoise the indirect diffuse lighting",
 +        default=True,
 +    )
 +    denoising_glossy_direct: BoolProperty(
 +        name="Glossy Direct",
 +        description="Denoise the direct glossy lighting",
 +        default=True,
 +    )
 +    denoising_glossy_indirect: BoolProperty(
 +        name="Glossy Indirect",
 +        description="Denoise the indirect glossy lighting",
 +        default=True,
 +    )
 +    denoising_transmission_direct: BoolProperty(
 +        name="Transmission Direct",
 +        description="Denoise the direct transmission lighting",
 +        default=True,
 +    )
 +    denoising_transmission_indirect: BoolProperty(
 +        name="Transmission Indirect",
 +        description="Denoise the indirect transmission lighting",
 +        default=True,
 +    )
 +    denoising_subsurface_direct: BoolProperty(
 +        name="Subsurface Direct",
 +        description="Denoise the direct subsurface lighting",
 +        default=True,
 +    )
 +    denoising_subsurface_indirect: BoolProperty(
 +        name="Subsurface Indirect",
 +        description="Denoise the indirect subsurface lighting",
 +        default=True,
 +    )
 +    denoising_strength: FloatProperty(
 +        name="Denoising Strength",
 +        description="Controls neighbor pixel weighting for the denoising filter (lower values preserve more detail, but aren't as smooth)",
 +        min=0.0, max=1.0,
 +        default=0.5,
 +    )
 +    denoising_feature_strength: FloatProperty(
 +        name="Denoising Feature Strength",
 +        description="Controls removal of noisy image feature passes (lower values preserve more detail, but aren't as smooth)",
 +        min=0.0, max=1.0,
 +        default=0.5,
 +    )
 +    denoising_radius: IntProperty(
 +        name="Denoising Radius",
 +        description="Size of the image area that's used to denoise a pixel (higher values are smoother, but might lose detail and are slower)",
 +        min=1, max=25,
 +        default=8,
 +    )
 +    denoising_relative_pca: BoolProperty(
 +        name="Relative filter",
 +        description="When removing pixels that don't carry information, use a relative threshold instead of an absolute one (can help to reduce artifacts, but might cause detail loss around edges)",
 +        default=False,
 +    )
 +    denoising_store_passes: BoolProperty(
 +        name="Store denoising passes",
 +        description="Store the denoising feature passes and the noisy image",
 +        default=False,
 +        update=update_render_passes,
 +    )
++    denoising_neighbor_frames: IntProperty(
++        name="Neighbor Frames",
++        description="Number of neighboring frames to use for denoising animations (more frames produce smoother results at the cost of performance)",
++        min=0, max=7,
++        default=0,
++    )
 +    use_pass_crypto_object: BoolProperty(
 +        name="Cryptomatte Object",
 +        description="Render cryptomatte object pass, for isolating objects in compositing",
 +        default=False,
 +        update=update_render_passes,
 +        )
 +    use_pass_crypto_material: BoolProperty(
 +        name="Cryptomatte Material",
 +        description="Render cryptomatte material pass, for isolating materials in compositing",
 +        default=False,
 +        update=update_render_passes,
 +        )
 +    use_pass_crypto_asset: BoolProperty(
 +        name="Cryptomatte Asset",
 +        description="Render cryptomatte asset pass, for isolating groups of objects with the same parent",
 +        default=False,
 +        update=update_render_passes,
 +        )
 +    pass_crypto_depth: IntProperty(
 +        name="Cryptomatte Levels",
 +        description="Sets how many unique objects can be distinguished per pixel",
 +        default=6, min=2, max=16, step=2,
 +        update=update_render_passes,
 +        )
 +    pass_crypto_accurate: BoolProperty(
 +        name="Cryptomatte Accurate",
 +        description="Generate a more accurate Cryptomatte pass. CPU only, may render slower and use more memory",
 +        default=True,
 +        update=update_render_passes,
 +        )
  
 -class CyclesCurveSettings(bpy.types.PropertyGroup):
      @classmethod
      def register(cls):
 -        bpy.types.ParticleSettings.cycles = PointerProperty(
 -            name="Cycles Hair Settings",
 -            description="Cycles hair settings",
 +        bpy.types.ViewLayer.cycles = PointerProperty(
 +            name="Cycles ViewLayer Settings",
 +            description="Cycles ViewLayer Settings",
              type=cls,
          )
 -        cls.radius_scale = FloatProperty(
 -            name="Radius Scaling",
 -            description="Multiplier of width properties",
 -            min=0.0, max=1000.0,
 -            default=0.01,
 -        )
 -        cls.root_width = FloatProperty(
 -            name="Root Size",
 -            description="Strand's width at root",
 -            min=0.0, max=1000.0,
 -            default=1.0,
 -        )
 -        cls.tip_width = FloatProperty(
 -            name="Tip Multiplier",
 -            description="Strand's width at tip",
 -            min=0.0, max=1000.0,
 -            default=0.0,
 -        )
 -        cls.shape = FloatProperty(
 -            name="Strand Shape",
 -            description="Strand shape parameter",
 -            min=-1.0, max=1.0,
 -            default=0.0,
 -        )
 -        cls.use_closetip = BoolProperty(
 -            name="Close tip",
 -            description="Set tip radius to zero",
 -            default=True,
 -        )
  
      @classmethod
      def unregister(cls):
  # <pep8 compliant>
  
  import bpy
 -import _cycles
 +from bpy_extras.node_utils import find_node_input
 +from bl_operators.presets import PresetMenu
- import _cycles
  
--from bpy.types import (
--    Panel,
-     Operator,
 -    Menu,
--)
++from bpy.types import Panel
  
  
 -class CYCLES_MT_sampling_presets(Menu):
 +class CYCLES_PT_sampling_presets(PresetMenu):
      bl_label = "Sampling Presets"
      preset_subdir = "cycles/sampling"
      preset_operator = "script.execute_preset"
@@@ -602,49 -405,31 +598,51 @@@ class CYCLES_RENDER_PT_performance_thre
          sub.enabled = rd.threads_mode == 'FIXED'
          sub.prop(rd, "threads")
  
 -        col.separator()
  
 -        sub = col.column(align=True)
 -        sub.label(text="Tiles:")
 -        sub.prop(cscene, "tile_order", text="")
 +class CYCLES_RENDER_PT_performance_tiles(CyclesButtonsPanel, Panel):
 +    bl_label = "Tiles"
 +    bl_parent_id = "CYCLES_RENDER_PT_performance"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        sub.prop(rd, "tile_x", text="X")
 +        scene = context.scene
 +        rd = scene.render
 +        cscene = scene.cycles
 +
 +        col = layout.column()
 +
 +        sub = col.column(align=True)
 +        sub.prop(rd, "tile_x", text="Tiles X")
          sub.prop(rd, "tile_y", text="Y")
 +        col.prop(cscene, "tile_order", text="Order")
  
 -        subsub = sub.column()
 -        subsub.active = not rd.use_save_buffers
 -        for rl in rd.layers:
 -            if rl.cycles.use_denoising:
 -                subsub.active = False
 -        subsub.prop(cscene, "use_progressive_refine")
 +        sub = col.column()
 +        sub.active = not rd.use_save_buffers
 +        for view_layer in scene.view_layers:
 +            if view_layer.cycles.use_denoising:
 +                sub.active = False
 +        sub.prop(cscene, "use_progressive_refine")
  
 -        col = split.column()
  
 -        col.label(text="Final Render:")
 -        col.prop(rd, "use_save_buffers")
 -        col.prop(rd, "use_persistent_data", text="Persistent Images")
 +class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Panel):
 +    bl_label = "Acceleration Structure"
 +    bl_parent_id = "CYCLES_RENDER_PT_performance"
  
 -        col.separator()
 +    def draw(self, context):
++        import _cycles
++
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        scene = context.scene
 +        cscene = scene.cycles
 +
 +        col = layout.column()
  
 -        col.label(text="Acceleration structure:")
          if _cycles.with_embree:
              row = col.row()
              row.active = use_cpu(context)
@@@ -1223,81 -883,58 +1221,60 @@@ class CYCLES_OBJECT_PT_cycles_settings_
          cob = ob.cycles
          visibility = ob.cycles_visibility
  
 -        layout.label(text="Ray Visibility:")
 -        flow = layout.column_flow()
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
  
 -        flow.prop(visibility, "camera")
 -        flow.prop(visibility, "diffuse")
 -        flow.prop(visibility, "glossy")
 -        flow.prop(visibility, "transmission")
 -        flow.prop(visibility, "scatter")
 +        col = flow.column()
 +        col.prop(visibility, "camera")
 +        col = flow.column()
 +        col.prop(visibility, "diffuse")
 +        col = flow.column()
 +        col.prop(visibility, "glossy")
 +        col = flow.column()
 +        col.prop(visibility, "transmission")
 +        col = flow.column()
 +        col.prop(visibility, "scatter")
  
 -        if ob.type != 'LAMP':
 -            flow.prop(visibility, "shadow")
 +        if ob.type != 'LIGHT':
 +            col = flow.column()
 +            col.prop(visibility, "shadow")
  
 -        row = layout.row()
 -        row.prop(cob, "is_shadow_catcher")
 -        row.prop(cob, "is_holdout")
 +        layout.separator()
  
 -        col = layout.column()
 -        col.label(text="Performance:")
 -        row = col.row()
 -        sub = row.row()
 -        sub.active = scene.render.use_simplify and cscene.use_camera_cull
 -        sub.prop(cob, "use_camera_cull")
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
  
 -        sub = row.row()
 -        sub.active = scene.render.use_simplify and cscene.use_distance_cull
 -        sub.prop(cob, "use_distance_cull")
 +        col = flow.column()
 +        col.prop(cob, "is_shadow_catcher")
 +        col = flow.column()
 +        col.prop(cob, "is_holdout")
  
  
 -def find_node(material, nodetype):
 -    if material and material.node_tree:
 -        ntree = material.node_tree
 +class CYCLES_OBJECT_PT_cycles_settings_performance(CyclesButtonsPanel, Panel):
 +    bl_label = "Performance"
 +    bl_parent_id = "CYCLES_OBJECT_PT_cycles_settings"
 +    bl_context = "object"
 +
  
 -        active_output_node = None
 -        for node in ntree.nodes:
 -            if getattr(node, "type", None) == nodetype:
 -                if getattr(node, "is_active_output", True):
 -                    return node
 -                if not active_output_node:
 -                    active_output_node = node
 -        return active_output_node
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -    return None
 +        scene = context.scene
 +        cscene = scene.cycles
 +        ob = context.object
 +        cob = ob.cycles
  
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
  
 -def find_node_input(node, name):
 -    for input in node.inputs:
 -        if input.name == name:
 -            return input
 +        col = flow.column()
 +        col.active = scene.render.use_simplify and cscene.use_camera_cull
 +        col.prop(cob, "use_camera_cull")
  
 -    return None
 +        col = flow.column()
 +        col.active = scene.render.use_simplify and cscene.use_distance_cull
 +        col.prop(cob, "use_distance_cull")
  
  
- class CYCLES_OT_use_shading_nodes(Operator):
-     """Enable nodes on a material, world or light"""
-     bl_idname = "cycles.use_shading_nodes"
-     bl_label = "Use Nodes"
-     @classmethod
-     def poll(cls, context):
-         return (getattr(context, "material", False) or getattr(context, "world", False) or
-                 getattr(context, "light", False))
-     def execute(self, context):
-         if context.material:
-             context.material.use_nodes = True
-         elif context.world:
-             context.world.use_nodes = True
-         elif context.light:
-             context.light.use_nodes = True
-         return {'FINISHED'}
  def panel_node_draw(layout, id_data, output_type, input_name):
      if not id_data.use_nodes:
          layout.operator("cycles.use_shading_nodes", icon='NODETREE')
@@@ -2134,13 -1848,10 +2111,12 @@@ classes = 
      CYCLES_PT_context_material,
      CYCLES_OBJECT_PT_motion_blur,
      CYCLES_OBJECT_PT_cycles_settings,
 -    CYCLES_LAMP_PT_preview,
 -    CYCLES_LAMP_PT_lamp,
 -    CYCLES_LAMP_PT_nodes,
 -    CYCLES_LAMP_PT_spot,
 +    CYCLES_OBJECT_PT_cycles_settings_ray_visibility,
 +    CYCLES_OBJECT_PT_cycles_settings_performance,
-     CYCLES_OT_use_shading_nodes,
 +    CYCLES_LIGHT_PT_preview,
 +    CYCLES_LIGHT_PT_light,
 +    CYCLES_LIGHT_PT_nodes,
 +    CYCLES_LIGHT_PT_spot,
      CYCLES_WORLD_PT_preview,
      CYCLES_WORLD_PT_surface,
      CYCLES_WORLD_PT_volume,
index 0000000,d689508..969601b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,109 +1,109 @@@
 -DeviceInfo blender_device_info(BL::UserPreferences& b_userpref, BL::Scene& b_scene, bool background)
+ /*
+  * 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.
+  */
+ #include "blender/blender_device.h"
+ #include "blender/blender_util.h"
+ CCL_NAMESPACE_BEGIN
+ int blender_device_threads(BL::Scene& b_scene)
+ {
+       BL::RenderSettings b_r = b_scene.render();
+       if(b_r.threads_mode() == BL::RenderSettings::threads_mode_FIXED)
+               return b_r.threads();
+       else
+               return 0;
+ }
 -              BL::UserPreferences::addons_iterator b_addon_iter;
 -              for(b_userpref.addons.begin(b_addon_iter); b_addon_iter != b_userpref.addons.end(); ++b_addon_iter) {
++DeviceInfo blender_device_info(BL::Preferences& b_preferences, BL::Scene& b_scene, bool background)
+ {
+       PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+       /* Default to CPU device. */
+       DeviceInfo device = Device::available_devices(DEVICE_MASK_CPU).front();
+       if(get_enum(cscene, "device") == 2) {
+               /* Find network device. */
+               vector<DeviceInfo> devices = Device::available_devices(DEVICE_MASK_NETWORK);
+               if(!devices.empty()) {
+                       device = devices.front();
+               }
+       }
+       else if(get_enum(cscene, "device") == 1) {
+               /* Find cycles preferences. */
+               PointerRNA cpreferences;
++              BL::Preferences::addons_iterator b_addon_iter;
++              for(b_preferences.addons.begin(b_addon_iter); b_addon_iter != b_preferences.addons.end(); ++b_addon_iter) {
+                       if(b_addon_iter->module() == "cycles") {
+                               cpreferences = b_addon_iter->preferences().ptr;
+                               break;
+                       }
+               }
+               /* Test if we are using GPU devices. */
+               enum ComputeDevice {
+                       COMPUTE_DEVICE_CPU = 0,
+                       COMPUTE_DEVICE_CUDA = 1,
+                       COMPUTE_DEVICE_OPENCL = 2,
+                       COMPUTE_DEVICE_NUM = 3,
+               };
+               ComputeDevice compute_device = (ComputeDevice)get_enum(cpreferences,
+                                                                      "compute_device_type",
+                                                                      COMPUTE_DEVICE_NUM,
+                                                                      COMPUTE_DEVICE_CPU);
+               if(compute_device != COMPUTE_DEVICE_CPU) {
+                       /* Query GPU devices with matching types. */
+                       uint mask = DEVICE_MASK_CPU;
+                       if(compute_device == COMPUTE_DEVICE_CUDA) {
+                               mask |= DEVICE_MASK_CUDA;
+                       }
+                       else if(compute_device == COMPUTE_DEVICE_OPENCL) {
+                               mask |= DEVICE_MASK_OPENCL;
+                       }
+                       vector<DeviceInfo> devices = Device::available_devices(mask);
+                       /* Match device preferences and available devices. */
+                       vector<DeviceInfo> used_devices;
+                       RNA_BEGIN(&cpreferences, device, "devices") {
+                               if(get_boolean(device, "use")) {
+                                       string id = get_string(device, "id");
+                                       foreach(DeviceInfo& info, devices) {
+                                               if(info.id == id) {
+                                                       used_devices.push_back(info);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       } RNA_END;
+                       if(!used_devices.empty()) {
+                               int threads = blender_device_threads(b_scene);
+                               device = Device::get_multi_device(used_devices,
+                                                                 threads,
+                                                                 background);
+                       }
+                       /* Else keep using the CPU device that was set before. */
+               }
+       }
+       return device;
+ }
+ CCL_NAMESPACE_END
index 0000000,315cdac..0610209
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,37 +1,37 @@@
 -DeviceInfo blender_device_info(BL::UserPreferences& b_userpref, BL::Scene& b_scene, bool background);
+ /*
+  * 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.
+  */
+ #ifndef __BLENDER_DEVICE_H__
+ #define __BLENDER_DEVICE_H__
+ #include "MEM_guardedalloc.h"
+ #include "RNA_types.h"
+ #include "RNA_access.h"
+ #include "RNA_blender_cpp.h"
+ #include "device/device.h"
+ CCL_NAMESPACE_BEGIN
+ /* Get number of threads to use for rendering. */
+ int blender_device_threads(BL::Scene& b_scene);
+ /* Convert Blender settings to device specification. */
++DeviceInfo blender_device_info(BL::Preferences& b_preferences, BL::Scene& b_scene, bool background);
+ CCL_NAMESPACE_END
+ #endif  /* __BLENDER_DEVICE_H__ */
@@@ -203,10 -206,10 +206,10 @@@ static PyObject *exit_func(PyObject * /
  
  static PyObject *create_func(PyObject * /*self*/, PyObject *args)
  {
-       PyObject *pyengine, *pyuserpref, *pydata, *pyregion, *pyv3d, *pyrv3d;
 -      PyObject *pyengine, *pyuserpref, *pydata, *pyscene, *pyregion, *pyv3d, *pyrv3d;
++      PyObject *pyengine, *pypreferences, *pydata, *pyregion, *pyv3d, *pyrv3d;
        int preview_osl;
  
-       if(!PyArg_ParseTuple(args, "OOOOOOi", &pyengine, &pyuserpref, &pydata,
 -      if(!PyArg_ParseTuple(args, "OOOOOOOi", &pyengine, &pyuserpref, &pydata, &pyscene,
++      if(!PyArg_ParseTuple(args, "OOOOOOi", &pyengine, &pypreferences, &pydata,
                             &pyregion, &pyv3d, &pyrv3d, &preview_osl))
        {
                return NULL;
        RNA_pointer_create(NULL, &RNA_RenderEngine, (void*)PyLong_AsVoidPtr(pyengine), &engineptr);
        BL::RenderEngine engine(engineptr);
  
--      PointerRNA userprefptr;
-       RNA_pointer_create(NULL, &RNA_Preferences, (void*)PyLong_AsVoidPtr(pyuserpref), &userprefptr);
-       BL::Preferences userpref(userprefptr);
 -      RNA_pointer_create(NULL, &RNA_UserPreferences, (void*)PyLong_AsVoidPtr(pyuserpref), &userprefptr);
 -      BL::UserPreferences userpref(userprefptr);
++      PointerRNA preferencesptr;
++      RNA_pointer_create(NULL, &RNA_Preferences, (void*)PyLong_AsVoidPtr(pypreferences), &preferencesptr);
++      BL::Preferences preferences(preferencesptr);
  
        PointerRNA dataptr;
        RNA_main_pointer_create((Main*)PyLong_AsVoidPtr(pydata), &dataptr);
                int width = region.width();
                int height = region.height();
  
-               session = new BlenderSession(engine, userpref, data, v3d, rv3d, width, height);
 -              session = new BlenderSession(engine, userpref, data, scene, v3d, rv3d, width, height);
++              session = new BlenderSession(engine, preferences, data, v3d, rv3d, width, height);
        }
        else {
 -              /* override some settings for preview */
 -              if(engine.is_preview()) {
 -                      PointerRNA cscene = RNA_pointer_get(&sceneptr, "cycles");
 -
 -                      RNA_boolean_set(&cscene, "shading_system", preview_osl);
 -                      RNA_boolean_set(&cscene, "use_progressive_refine", true);
 -              }
 -
                /* offline session or preview render */
-               session = new BlenderSession(engine, userpref, data, preview_osl);
 -              session = new BlenderSession(engine, userpref, data, scene);
++              session = new BlenderSession(engine, preferences, data, preview_osl);
        }
  
 -      python_thread_state_save(&session->python_thread_state);
 -
 -      session->create();
 -
 -      python_thread_state_restore(&session->python_thread_state);
 -
        return PyLong_FromVoidPtr(session);
  }
  
@@@ -627,6 -626,121 +630,121 @@@ static PyObject *opencl_disable_func(Py
  }
  #endif
  
 -      PyObject *pypreferences, *pyscene, *pyrenderlayer;
+ static bool denoise_parse_filepaths(PyObject *pyfilepaths, vector<string>& filepaths)
+ {
+       if(PyUnicode_Check(pyfilepaths)) {
+               const char *filepath = PyUnicode_AsUTF8(pyfilepaths);
+               filepaths.push_back(filepath);
+               return true;
+       }
+       PyObject *sequence = PySequence_Fast(pyfilepaths, "File paths must be a string or sequence of strings");
+       if(sequence == NULL) {
+               return false;
+       }
+       for(Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(sequence); i++) {
+               PyObject *item = PySequence_Fast_GET_ITEM(sequence, i);
+               const char *filepath = PyUnicode_AsUTF8(item);
+               if(filepath == NULL) {
+                       PyErr_SetString(PyExc_ValueError, "File paths must be a string or sequence of strings.");
+                       Py_DECREF(sequence);
+                       return false;
+               }
+               filepaths.push_back(filepath);
+       }
+       Py_DECREF(sequence);
+       return true;
+ }
+ static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *keywords)
+ {
+       static const char *keyword_list[] = {"preferences", "scene", "view_layer",
+                                            "input", "output",
+                                            "tile_size", "samples", NULL};
 -                                       &pypreferences, &pyscene, &pyrenderlayer,
++      PyObject *pypreferences, *pyscene, *pyviewlayer;
+       PyObject *pyinput, *pyoutput = NULL;
+       int tile_size = 0, samples = 0;
+       if (!PyArg_ParseTupleAndKeywords(args, keywords, "OOOO|Oii", (char**)keyword_list,
 -      RNA_pointer_create(NULL, &RNA_UserPreferences, (void*)PyLong_AsVoidPtr(pypreferences), &preferencesptr);
 -      BL::UserPreferences b_preferences(preferencesptr);
++                                       &pypreferences, &pyscene, &pyviewlayer,
+                                                                        &pyinput, &pyoutput,
+                                        &tile_size, &samples)) {
+               return NULL;
+       }
+       /* Get device specification from preferences and scene. */
+       PointerRNA preferencesptr;
 -      PointerRNA renderlayerptr;
 -      RNA_pointer_create((ID*)PyLong_AsVoidPtr(pyscene), &RNA_SceneRenderLayer, PyLong_AsVoidPtr(pyrenderlayer), &renderlayerptr);
 -      PointerRNA crenderlayer = RNA_pointer_get(&renderlayerptr, "cycles");
++      RNA_pointer_create(NULL, &RNA_Preferences, (void*)PyLong_AsVoidPtr(pypreferences), &preferencesptr);
++      BL::Preferences b_preferences(preferencesptr);
+       PointerRNA sceneptr;
+       RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyscene), &sceneptr);
+       BL::Scene b_scene(sceneptr);
+       DeviceInfo device = blender_device_info(b_preferences, b_scene, true);
+       /* Get denoising parameters from view layer. */
 -      params.radius = get_int(crenderlayer, "denoising_radius");
 -      params.strength = get_float(crenderlayer, "denoising_strength");
 -      params.feature_strength = get_float(crenderlayer, "denoising_feature_strength");
 -      params.relative_pca = get_boolean(crenderlayer, "denoising_relative_pca");
 -      params.neighbor_frames = get_int(crenderlayer, "denoising_neighbor_frames");
++      PointerRNA viewlayerptr;
++      RNA_pointer_create((ID*)PyLong_AsVoidPtr(pyscene), &RNA_ViewLayer, PyLong_AsVoidPtr(pyviewlayer), &viewlayerptr);
++      PointerRNA cviewlayer = RNA_pointer_get(&viewlayerptr, "cycles");
+       DenoiseParams params;
++      params.radius = get_int(cviewlayer, "denoising_radius");
++      params.strength = get_float(cviewlayer, "denoising_strength");
++      params.feature_strength = get_float(cviewlayer, "denoising_feature_strength");
++      params.relative_pca = get_boolean(cviewlayer, "denoising_relative_pca");
++      params.neighbor_frames = get_int(cviewlayer, "denoising_neighbor_frames");
+       /* Parse file paths list. */
+       vector<string> input, output;
+       if(!denoise_parse_filepaths(pyinput, input)) {
+               return NULL;
+       }
+       if(pyoutput) {
+               if(!denoise_parse_filepaths(pyoutput, output)) {
+                       return NULL;
+               }
+       }
+       else {
+               output = input;
+       }
+       if(input.empty()) {
+               PyErr_SetString(PyExc_ValueError, "No input file paths specified.");
+               return NULL;
+       }
+       if(input.size() != output.size()) {
+               PyErr_SetString(PyExc_ValueError, "Number of input and output file paths does not match.");
+               return NULL;
+       }
+       /* Create denoiser. */
+       Denoiser denoiser(device);
+       denoiser.params = params;
+       denoiser.input = input;
+       denoiser.output = output;
+       if (tile_size > 0) {
+               denoiser.tile_size = make_int2(tile_size, tile_size);
+       }
+       if (samples > 0) {
+               denoiser.samples_override = samples;
+       }
+       /* Run denoiser. */
+       if(!denoiser.run()) {
+               PyErr_SetString(PyExc_ValueError, denoiser.error.c_str());
+               return NULL;
+       }
+       Py_RETURN_NONE;
+ }
  static PyObject *debug_flags_update_func(PyObject * /*self*/, PyObject *args)
  {
        PyObject *pyscene;
@@@ -428,111 -390,131 +428,107 @@@ void BlenderSession::render(BL::Depsgra
        BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_v3d, b_rv3d, scene->camera, width, height);
  
        /* render each layer */
 -      BL::RenderSettings r = b_scene.render();
 -      BL::RenderSettings::layers_iterator b_layer_iter;
 -      BL::RenderResult::views_iterator b_view_iter;
 +      BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
  
 -      /* We do some special meta attributes when we only have single layer. */
 -      const bool is_single_layer = (r.layers.length() == 1);
 +      /* temporary render result to find needed passes and views */
 +      BL::RenderResult b_rr = begin_render_result(b_engine, 0, 0, 1, 1, b_view_layer.name().c_str(), NULL);
 +      BL::RenderResult::layers_iterator b_single_rlay;
 +      b_rr.layers.begin(b_single_rlay);
 +      BL::RenderLayer b_rlay = *b_single_rlay;
 +      b_rlay_name = b_view_layer.name();
  
 -      for(r.layers.begin(b_layer_iter); b_layer_iter != r.layers.end(); ++b_layer_iter) {
 -              b_rlay_name = b_layer_iter->name();
 +      /* add passes */
 +      vector<Pass> passes = sync->sync_render_passes(b_rlay, b_view_layer, session_params);
 +      buffer_params.passes = passes;
  
 -              /* temporary render result to find needed passes and views */
 -              BL::RenderResult b_rr = begin_render_result(b_engine, 0, 0, 1, 1, b_rlay_name.c_str(), NULL);
 -              BL::RenderResult::layers_iterator b_single_rlay;
 -              b_rr.layers.begin(b_single_rlay);
 +      PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles");
 +      bool full_denoising = get_boolean(crl, "use_denoising");
 +      bool write_denoising_passes = get_boolean(crl, "denoising_store_passes");
  
 -              /* layer will be missing if it was disabled in the UI */
 -              if(b_single_rlay == b_rr.layers.end()) {
 -                      end_render_result(b_engine, b_rr, true, true, false);
 -                      continue;
 -              }
 +      bool run_denoising = full_denoising || write_denoising_passes;
  
 -              BL::RenderLayer b_rlay = *b_single_rlay;
 -
 -              /* add passes */
 -              vector<Pass> passes = sync->sync_render_passes(b_rlay, *b_layer_iter, session_params);
 -              buffer_params.passes = passes;
 -
 -              PointerRNA crl = RNA_pointer_get(&b_layer_iter->ptr, "cycles");
 -              bool full_denoising = get_boolean(crl, "use_denoising");
 -              bool write_denoising_passes = get_boolean(crl, "denoising_store_passes");
 -
 -              bool run_denoising = full_denoising || write_denoising_passes;
 -
 -              session->tile_manager.schedule_denoising = run_denoising;
 -              buffer_params.denoising_data_pass = run_denoising;
 -              buffer_params.denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES);
 -              buffer_params.denoising_prefiltered_pass = write_denoising_passes;
 -
 -              session->params.run_denoising = run_denoising;
 -              session->params.full_denoising = full_denoising;
 -              session->params.write_denoising_passes = write_denoising_passes;
 -              session->params.denoising.radius = get_int(crl, "denoising_radius");
 -              session->params.denoising.strength = get_float(crl, "denoising_strength");
 -              session->params.denoising.feature_strength = get_float(crl, "denoising_feature_strength");
 -              session->params.denoising.relative_pca = get_boolean(crl, "denoising_relative_pca");
 -
 -              scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
 -              scene->film->denoising_clean_pass = buffer_params.denoising_clean_pass;
 -              scene->film->denoising_prefiltered_pass = buffer_params.denoising_prefiltered_pass;
 -              scene->film->pass_alpha_threshold = b_layer_iter->pass_alpha_threshold();
 -              scene->film->tag_passes_update(scene, passes);
 -              scene->film->tag_update(scene);
 -              scene->integrator->tag_update(scene);
 -
 -              int view_index = 0;
 -              for(b_rr.views.begin(b_view_iter); b_view_iter != b_rr.views.end(); ++b_view_iter, ++view_index) {
 -                      b_rview_name = b_view_iter->name();
 -
 -                      /* set the current view */
 -                      b_engine.active_view_set(b_rview_name.c_str());
 -
 -                      /* update scene */
 -                      BL::Object b_camera_override(b_engine.camera_override());
 -                      sync->sync_camera(b_render, b_camera_override, width, height, b_rview_name.c_str());
 -                      sync->sync_data(b_render,
 -                                      b_v3d,
 -                                      b_camera_override,
 -                                      width, height,
 -                                      &python_thread_state,
 -                                      b_rlay_name.c_str());
 -
 -                      /* Make sure all views have different noise patterns. - hardcoded value just to make it random */
 -                      if(view_index != 0) {
 -                              scene->integrator->seed += hash_int_2d(scene->integrator->seed, hash_int(view_index * 0xdeadbeef));
 -                              scene->integrator->tag_update(scene);
 -                      }
 +      session->tile_manager.schedule_denoising = run_denoising;
 +      buffer_params.denoising_data_pass = run_denoising;
 +      buffer_params.denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES);
 +      buffer_params.denoising_prefiltered_pass = write_denoising_passes;
  
 -                      /* Update number of samples per layer. */
 -                      int samples = sync->get_layer_samples();
 -                      bool bound_samples = sync->get_layer_bound_samples();
 -                      int effective_layer_samples;
 +      session->params.run_denoising = run_denoising;
 +      session->params.full_denoising = full_denoising;
 +      session->params.write_denoising_passes = write_denoising_passes;
-       session->params.denoising_radius = get_int(crl, "denoising_radius");
-       session->params.denoising_strength = get_float(crl, "denoising_strength");
-       session->params.denoising_feature_strength = get_float(crl, "denoising_feature_strength");
-       session->params.denoising_relative_pca = get_boolean(crl, "denoising_relative_pca");
++      session->params.denoising.radius = get_int(crl, "denoising_radius");
++      session->params.denoising.strength = get_float(crl, "denoising_strength");
++      session->params.denoising.feature_strength = get_float(crl, "denoising_feature_strength");
++      session->params.denoising.relative_pca = get_boolean(crl, "denoising_relative_pca");
  
 -                      if(samples != 0 && (!bound_samples || (samples < session_params.samples)))
 -                              effective_layer_samples = samples;
 -                      else
 -                              effective_layer_samples = session_params.samples;
 +      scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
 +      scene->film->denoising_clean_pass = buffer_params.denoising_clean_pass;
 +      scene->film->denoising_prefiltered_pass = buffer_params.denoising_prefiltered_pass;
-       session->params.denoising_radius = get_int(crl, "denoising_radius");
-       session->params.denoising_strength = get_float(crl, "denoising_strength");
-       session->params.denoising_feature_strength = get_float(crl, "denoising_feature_strength");
-       session->params.denoising_relative_pca = get_boolean(crl, "denoising_relative_pca");
  
 -                      /* Update tile manager if we're doing resumable render. */
 -                      update_resumable_tile_manager(effective_layer_samples);
 +      scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold();
 +      scene->film->tag_passes_update(scene, passes);
 +      scene->film->tag_update(scene);
 +      scene->integrator->tag_update(scene);
  
 -                      /* Update session itself. */
 -                      session->reset(buffer_params, effective_layer_samples);
 +      BL::RenderResult::views_iterator b_view_iter;
  
 -                      /* render */
 -                      session->start();
 -                      session->wait();
 +      int num_views = 0;
 +      for(b_rr.views.begin(b_view_iter); b_view_iter != b_rr.views.end(); ++b_view_iter) {
 +              num_views++;
 +      }
  
 -                      if(session->progress.get_cancel())
 -                              break;
 -              }
 +      int view_index = 0;
 +      for(b_rr.views.begin(b_view_iter); b_view_iter != b_rr.views.end(); ++b_view_iter, ++view_index) {
 +              b_rview_name = b_view_iter->name();
  
 -              BL::RenderResult b_full_rr = b_engine.get_result();
 -              if(is_single_layer) {
 -                      string num_aa_samples = string_printf("%d", session->params.samples);
 -                      render_add_metadata(b_full_rr, "Cycles Samples", num_aa_samples);
 -                      /* TODO(sergey): Report whether we're doing resumable render
 -                       * and also start/end sample if so.
 -                       */
 -              }
 +              /* set the current view */
 +              b_engine.active_view_set(b_rview_name.c_str());
  
 -              if(scene->film->cryptomatte_passes & CRYPT_OBJECT) {
 -                      add_cryptomatte_layer(b_full_rr, b_rlay_name+".CryptoObject",
 -                                            scene->object_manager->get_cryptomatte_objects(scene));
 -              }
 -              if(scene->film->cryptomatte_passes & CRYPT_MATERIAL) {
 -                      add_cryptomatte_layer(b_full_rr, b_rlay_name+".CryptoMaterial",
 -                                            scene->shader_manager->get_cryptomatte_materials(scene));
 +              /* update scene */
 +              BL::Object b_camera_override(b_engine.camera_override());
 +              sync->sync_camera(b_render, b_camera_override, width, height, b_rview_name.c_str());
 +              sync->sync_data(b_render,
 +                              b_depsgraph,
 +                              b_v3d,
 +                              b_camera_override,
 +                              width, height,
 +                              &python_thread_state);
 +              builtin_images_load();
 +
 +              /* Attempt to free all data which is held by Blender side, since at this
 +               * point we knwo that we've got everything to render current view layer.
 +               */
 +              /* At the moment we only free if we are not doing multi-view (or if we are rendering the last view).
 +               * See T58142/D4239 for discussion.
 +               */
 +              if(view_index == num_views - 1) {
 +                      free_blender_memory_if_possible();
                }
 -              if(scene->film->cryptomatte_passes & CRYPT_ASSET) {
 -                      add_cryptomatte_layer(b_full_rr, b_rlay_name+".CryptoAsset",
 -                                            scene->object_manager->get_cryptomatte_assets(scene));
 +
 +              /* Make sure all views have different noise patterns. - hardcoded value just to make it random */
 +              if(view_index != 0) {
 +                      scene->integrator->seed += hash_int_2d(scene->integrator->seed, hash_int(view_index * 0xdeadbeef));
 +                      scene->integrator->tag_update(scene);
                }
  
 -              /* free result without merging */
 -              end_render_result(b_engine, b_rr, true, true, false);
 +              /* Update number of samples per layer. */
 +              int samples = sync->get_layer_samples();
 +              bool bound_samples = sync->get_layer_bound_samples();
 +              int effective_layer_samples;
 +
 +              if(samples != 0 && (!bound_samples || (samples < session_params.samples)))
 +                      effective_layer_samples = samples;
 +              else
 +                      effective_layer_samples = session_params.samples;
 +
 +              /* Update tile manager if we're doing resumable render. */
 +              update_resumable_tile_manager(effective_layer_samples);
 +
 +              /* Update session itself. */
 +              session->reset(buffer_params, effective_layer_samples);
 +
 +              /* render */
 +              session->start();
 +              session->wait();
  
                if(!b_engine.is_preview() && background && print_render_stats) {
                        RenderStats stats;
@@@ -716,7 -742,7 +717,7 @@@ bool BlenderSync::get_session_pause(BL:
  }
  
  SessionParams BlenderSync::get_session_params(BL::RenderEngine& b_engine,
-                                               BL::Preferences& b_userpref,
 -                                              BL::UserPreferences& b_userpref,
++                                              BL::Preferences& b_preferences,
                                                BL::Scene& b_scene,
                                                bool background)
  {
        /* Background */
        params.background = background;
  
-       /* Default to CPU device. */
-       params.device = Device::available_devices(DEVICE_MASK_CPU).front();
-       if(get_enum(cscene, "device") == 2) {
-               /* Find network device. */
-               vector<DeviceInfo> devices = Device::available_devices(DEVICE_MASK_NETWORK);
-               if(!devices.empty()) {
-                       params.device = devices.front();
-               }
-       }
-       else if(get_enum(cscene, "device") == 1) {
-               /* Find cycles preferences. */
-               PointerRNA b_preferences;
-               BL::Preferences::addons_iterator b_addon_iter;
-               for(b_userpref.addons.begin(b_addon_iter); b_addon_iter != b_userpref.addons.end(); ++b_addon_iter) {
-                       if(b_addon_iter->module() == "cycles") {
-                               b_preferences = b_addon_iter->preferences().ptr;
-                               break;
-                       }
-               }
-               /* Test if we are using GPU devices. */
-               enum ComputeDevice {
-                       COMPUTE_DEVICE_CPU = 0,
-                       COMPUTE_DEVICE_CUDA = 1,
-                       COMPUTE_DEVICE_OPENCL = 2,
-                       COMPUTE_DEVICE_NUM = 3,
-               };
-               ComputeDevice compute_device = (ComputeDevice)get_enum(b_preferences,
-                                                                      "compute_device_type",
-                                                                      COMPUTE_DEVICE_NUM,
-                                                                      COMPUTE_DEVICE_CPU);
-               if(compute_device != COMPUTE_DEVICE_CPU) {
-                       /* Query GPU devices with matching types. */
-                       uint mask = DEVICE_MASK_CPU;
-                       if(compute_device == COMPUTE_DEVICE_CUDA) {
-                               mask |= DEVICE_MASK_CUDA;
-                       }
-                       else if(compute_device == COMPUTE_DEVICE_OPENCL) {
-                               mask |= DEVICE_MASK_OPENCL;
-                       }
-                       vector<DeviceInfo> devices = Device::available_devices(mask);
-                       /* Match device preferences and available devices. */
-                       vector<DeviceInfo> used_devices;
-                       RNA_BEGIN(&b_preferences, device, "devices") {
-                               if(get_boolean(device, "use")) {
-                                       string id = get_string(device, "id");
-                                       foreach(DeviceInfo& info, devices) {
-                                               if(info.id == id) {
-                                                       used_devices.push_back(info);
-                                                       break;
-                                               }
-                                       }
-                               }
-                       } RNA_END;
-                       if(!used_devices.empty()) {
-                               params.device = Device::get_multi_device(used_devices,
-                                                                        params.threads,
-                                                                        params.background);
-                       }
-                       /* Else keep using the CPU device that was set before. */
-               }
-       }
+       /* Device */
+       params.threads = blender_device_threads(b_scene);
 -      params.device = blender_device_info(b_userpref, b_scene, params.background);
++      params.device = blender_device_info(b_preferences, b_scene, params.background);
  
        /* samples */
        int samples = get_int(cscene, "samples");
        params.text_timeout = (double)get_float(cscene, "debug_text_timeout");
  
        /* progressive refine */
 -      params.progressive_refine = get_boolean(cscene, "use_progressive_refine") &&
+       BL::RenderSettings b_r = b_scene.render();
 +      params.progressive_refine = (b_engine.is_preview() ||
 +                                   get_boolean(cscene, "use_progressive_refine")) &&
                                    !b_r.use_save_buffers();
  
        if(params.progressive_refine) {
Simple merge