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

@@@ -19,7 -19,7 +19,7 @@@
  bl_info = {
      "name": "Cycles Render Engine",
      "author": "",
 -    "blender": (2, 76, 0),
 +    "blender": (2, 80, 0),
      "location": "Info header, render engine menu",
      "description": "Cycles Render Engine integration",
      "warning": "",
@@@ -37,6 -37,8 +37,8 @@@ if "bpy" in locals()
          importlib.reload(version_update)
      if "ui" in locals():
          importlib.reload(ui)
+     if "operators" in locals():
+         importlib.reload(operators)
      if "properties" in locals():
          importlib.reload(properties)
      if "presets" in locals():
@@@ -52,7 -54,7 +54,7 @@@ from . import 
  
  class CyclesRender(bpy.types.RenderEngine):
      bl_idname = 'CYCLES'
 -    bl_label = "Cycles Render"
 +    bl_label = "Cycles"
      bl_use_shading_nodes = True
      bl_use_preview = True
      bl_use_exclude_layers = True
          engine.free(self)
  
      # final render
 -    def update(self, data, scene):
 +    def update(self, data, depsgraph):
          if not self.session:
              if self.is_preview:
                  cscene = bpy.context.scene.cycles
                  use_osl = cscene.shading_system and cscene.device == 'CPU'
  
 -                engine.create(self, data, scene,
 -                              None, None, None, use_osl)
 +                engine.create(self, data, preview_osl=use_osl)
              else:
 -                engine.create(self, data, scene)
 -        else:
 -            engine.reset(self, data, scene)
 +                engine.create(self, data)
 +
 +        engine.reset(self, data, depsgraph)
  
 -    def render(self, scene):
 -        engine.render(self)
 +    def render(self, depsgraph):
 +        engine.render(self, depsgraph)
  
 -    def bake(self, scene, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result):
 -        engine.bake(self, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result)
 +    def bake(self, depsgraph, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result):
 +        engine.bake(self, depsgraph, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result)
  
      # viewport render
      def view_update(self, context):
          if not self.session:
 -            engine.create(self, context.blend_data, context.scene,
 +            engine.create(self, context.blend_data,
                            context.region, context.space_data, context.region_data)
 -        engine.update(self, context.blend_data, context.scene)
 +
 +        engine.reset(self, context.blend_data, context.depsgraph)
 +        engine.sync(self, context.depsgraph, context.blend_data)
  
      def view_draw(self, context):
 -        engine.draw(self, context.region, context.space_data, context.region_data)
 +        engine.draw(self, context.depsgraph, context.region, context.space_data, context.region_data)
  
      def update_script_node(self, node):
          if engine.with_osl():
@@@ -119,6 -120,7 +121,7 @@@ classes = 
  def register():
      from bpy.utils import register_class
      from . import ui
+     from . import operators
      from . import properties
      from . import presets
      import atexit
  
      properties.register()
      ui.register()
+     operators.register()
      presets.register()
  
      for cls in classes:
  def unregister():
      from bpy.utils import unregister_class
      from . import ui
+     from . import operators
      from . import properties
      from . import presets
      import atexit
      bpy.app.handlers.version_update.remove(version_update.do_versions)
  
      ui.unregister()
+     operators.unregister()
      properties.unregister()
      presets.unregister()
  
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)
  # <pep8 compliant>
  
  import bpy
 -from bpy.props import (BoolProperty,
 -                       EnumProperty,
 -                       FloatProperty,
 -                       IntProperty,
 -                       PointerProperty,
 -                       StringProperty)
 +from bpy.props import (
 +    BoolProperty,
 +    EnumProperty,
 +    FloatProperty,
 +    IntProperty,
 +    PointerProperty,
 +    StringProperty,
 +)
 +
 +from math import pi
  
  # enums
  
@@@ -158,591 -154,586 +158,591 @@@ enum_texture_limit = 
  
  
  class CyclesRenderSettings(bpy.types.PropertyGroup):
 -    @classmethod
 -    def register(cls):
 -        bpy.types.Scene.cycles = PointerProperty(
 -            name="Cycles Render Settings",
 -            description="Cycles render settings",
 -            type=cls,
 -        )
 -        cls.device = EnumProperty(
 -            name="Device",
 -            description="Device to use for rendering",
 -            items=enum_devices,
 -            default='CPU',
 -        )
 -        cls.feature_set = EnumProperty(
 -            name="Feature Set",
 -            description="Feature set to use for rendering",
 -            items=enum_feature_set,
 -            default='SUPPORTED',
 -        )
 -        cls.shading_system = BoolProperty(
 -            name="Open Shading Language",
 -            description="Use Open Shading Language (CPU rendering only)",
 -        )
  
 -        cls.progressive = EnumProperty(
 -            name="Integrator",
 -            description="Method to sample lights and materials",
 -            items=enum_integrator,
 -            default='PATH',
 -        )
 +    device: EnumProperty(
 +        name="Device",
 +        description="Device to use for rendering",
 +        items=enum_devices,
 +        default='CPU',
 +    )
 +    feature_set: EnumProperty(
 +        name="Feature Set",
 +        description="Feature set to use for rendering",
 +        items=enum_feature_set,
 +        default='SUPPORTED',
 +    )
 +    shading_system: BoolProperty(
 +        name="Open Shading Language",
 +        description="Use Open Shading Language (CPU rendering only)",
 +    )
  
 -        cls.use_square_samples = BoolProperty(
 -            name="Square Samples",
 -            description="Square sampling values for easier artist control",
 -            default=False,
 -        )
 +    progressive: EnumProperty(
 +        name="Integrator",
 +        description="Method to sample lights and materials",
 +        items=enum_integrator,
 +        default='PATH',
 +    )
  
 -        cls.samples = IntProperty(
 -            name="Samples",
 -            description="Number of samples to render for each pixel",
 -            min=1, max=2147483647,
 -            default=128,
 -        )
 -        cls.preview_samples = IntProperty(
 -            name="Preview Samples",
 -            description="Number of samples to render in the viewport, unlimited if 0",
 -            min=0, max=2147483647,
 -            default=32,
 -        )
 -        cls.preview_pause = BoolProperty(
 -            name="Pause Preview",
 -            description="Pause all viewport preview renders",
 -            default=False,
 -        )
 -        cls.preview_active_layer = BoolProperty(
 -            name="Preview Active Layer",
 -            description="Preview active render layer in viewport",
 -            default=False,
 -        )
 +    use_square_samples: BoolProperty(
 +        name="Square Samples",
 +        description="Square sampling values for easier artist control",
 +        default=False,
 +    )
  
 -        cls.aa_samples = IntProperty(
 -            name="AA Samples",
 -            description="Number of antialiasing samples to render for each pixel",
 -            min=1, max=2097151,
 -            default=128,
 -        )
 -        cls.preview_aa_samples = IntProperty(
 -            name="AA Samples",
 -            description="Number of antialiasing samples to render in the viewport, unlimited if 0",
 -            min=0, max=2097151,
 -            default=32,
 -        )
 -        cls.diffuse_samples = IntProperty(
 -            name="Diffuse Samples",
 -            description="Number of diffuse bounce samples to render for each AA sample",
 -            min=1, max=1024,
 -            default=1,
 -        )
 -        cls.glossy_samples = IntProperty(
 -            name="Glossy Samples",
 -            description="Number of glossy bounce samples to render for each AA sample",
 -            min=1, max=1024,
 -            default=1,
 -        )
 -        cls.transmission_samples = IntProperty(
 -            name="Transmission Samples",
 -            description="Number of transmission bounce samples to render for each AA sample",
 -            min=1, max=1024,
 -            default=1,
 -        )
 -        cls.ao_samples = IntProperty(
 -            name="Ambient Occlusion Samples",
 -            description="Number of ambient occlusion samples to render for each AA sample",
 -            min=1, max=1024,
 -            default=1,
 -        )
 -        cls.mesh_light_samples = IntProperty(
 -            name="Mesh Light Samples",
 -            description="Number of mesh emission light samples to render for each AA sample",
 -            min=1, max=1024,
 -            default=1,
 -        )
 +    samples: IntProperty(
 +        name="Samples",
 +        description="Number of samples to render for each pixel",
 +        min=1, max=2147483647,
 +        default=128,
 +    )
 +    preview_samples: IntProperty(
 +        name="Preview Samples",
 +        description="Number of samples to render in the viewport, unlimited if 0",
 +        min=0, max=2147483647,
 +        default=32,
 +    )
 +    preview_pause: BoolProperty(
 +        name="Pause Preview",
 +        description="Pause all viewport preview renders",
 +        default=False,
 +    )
 +    aa_samples: IntProperty(
 +        name="AA Samples",
 +        description="Number of antialiasing samples to render for each pixel",
 +        min=1, max=2097151,
 +        default=128,
 +    )
 +    preview_aa_samples: IntProperty(
 +        name="AA Samples",
 +        description="Number of antialiasing samples to render in the viewport, unlimited if 0",
 +        min=0, max=2097151,
 +        default=32,
 +    )
 +    diffuse_samples: IntProperty(
 +        name="Diffuse Samples",
 +        description="Number of diffuse bounce samples to render for each AA sample",
 +        min=1, max=1024,
 +        default=1,
 +    )
 +    glossy_samples: IntProperty(
 +        name="Glossy Samples",
 +        description="Number of glossy bounce samples to render for each AA sample",
 +        min=1, max=1024,
 +        default=1,
 +    )
 +    transmission_samples: IntProperty(
 +        name="Transmission Samples",
 +        description="Number of transmission bounce samples to render for each AA sample",
 +        min=1, max=1024,
 +        default=1,
 +    )
 +    ao_samples: IntProperty(
 +        name="Ambient Occlusion Samples",
 +        description="Number of ambient occlusion samples to render for each AA sample",
 +        min=1, max=1024,
 +        default=1,
 +    )
 +    mesh_light_samples: IntProperty(
 +        name="Mesh Light Samples",
 +        description="Number of mesh emission light samples to render for each AA sample",
 +        min=1, max=1024,
 +        default=1,
 +    )
  
 -        cls.subsurface_samples = IntProperty(
 -            name="Subsurface Samples",
 -            description="Number of subsurface scattering samples to render for each AA sample",
 -            min=1, max=1024,
 -            default=1,
 -        )
 +    subsurface_samples: IntProperty(
 +        name="Subsurface Samples",
 +        description="Number of subsurface scattering samples to render for each AA sample",
 +        min=1, max=1024,
 +        default=1,
 +    )
  
 -        cls.volume_samples = IntProperty(
 -            name="Volume Samples",
 -            description="Number of volume scattering samples to render for each AA sample",
 -            min=1, max=1024,
 -            default=1,
 -        )
 +    volume_samples: IntProperty(
 +        name="Volume Samples",
 +        description="Number of volume scattering samples to render for each AA sample",
 +        min=1, max=1024,
 +        default=1,
 +    )
  
 -        cls.sampling_pattern = EnumProperty(
 -            name="Sampling Pattern",
 -            description="Random sampling pattern used by the integrator",
 -            items=enum_sampling_pattern,
 -            default='SOBOL',
 -        )
 +    sampling_pattern: EnumProperty(
 +        name="Sampling Pattern",
 +        description="Random sampling pattern used by the integrator",
 +        items=enum_sampling_pattern,
 +        default='SOBOL',
 +    )
  
 -        cls.use_layer_samples = EnumProperty(
 -            name="Layer Samples",
 -            description="How to use per render layer sample settings",
 -            items=enum_use_layer_samples,
 -            default='USE',
 -        )
 +    use_layer_samples: EnumProperty(
 +        name="Layer Samples",
 +        description="How to use per view layer sample settings",
 +        items=enum_use_layer_samples,
 +        default='USE',
 +    )
  
 -        cls.sample_all_lights_direct = BoolProperty(
 -            name="Sample All Direct Lights",
 -            description="Sample all lights (for direct samples), rather than randomly picking one",
 -            default=True,
 -        )
 +    sample_all_lights_direct: BoolProperty(
 +        name="Sample All Direct Lights",
 +        description="Sample all lights (for direct samples), rather than randomly picking one",
 +        default=True,
 +    )
  
 -        cls.sample_all_lights_indirect = BoolProperty(
 -            name="Sample All Indirect Lights",
 -            description="Sample all lights (for indirect samples), rather than randomly picking one",
 -            default=True,
 -        )
 -        cls.light_sampling_threshold = FloatProperty(
 -            name="Light Sampling Threshold",
 -            description="Probabilistically terminate light samples when the light contribution is below this threshold (more noise but faster rendering). "
 -            "Zero disables the test and never ignores lights",
 -            min=0.0, max=1.0,
 -            default=0.01,
 -        )
 +    sample_all_lights_indirect: BoolProperty(
 +        name="Sample All Indirect Lights",
 +        description="Sample all lights (for indirect samples), rather than randomly picking one",
 +        default=True,
 +    )
 +    light_sampling_threshold: FloatProperty(
 +        name="Light Sampling Threshold",
 +        description="Probabilistically terminate light samples when the light contribution is below this threshold (more noise but faster rendering). "
 +        "Zero disables the test and never ignores lights",
 +        min=0.0, max=1.0,
 +        default=0.01,
 +    )
  
 -        cls.caustics_reflective = BoolProperty(
 -            name="Reflective Caustics",
 -            description="Use reflective caustics, resulting in a brighter image (more noise but added realism)",
 -            default=True,
 -        )
 +    caustics_reflective: BoolProperty(
 +        name="Reflective Caustics",
 +        description="Use reflective caustics, resulting in a brighter image (more noise but added realism)",
 +        default=True,
 +    )
  
 -        cls.caustics_refractive = BoolProperty(
 -            name="Refractive Caustics",
 -            description="Use refractive caustics, resulting in a brighter image (more noise but added realism)",
 -            default=True,
 -        )
 +    caustics_refractive: BoolProperty(
 +        name="Refractive Caustics",
 +        description="Use refractive caustics, resulting in a brighter image (more noise but added realism)",
 +        default=True,
 +    )
  
 -        cls.blur_glossy = FloatProperty(
 -            name="Filter Glossy",
 -            description="Adaptively blur glossy shaders after blurry bounces, "
 -            "to reduce noise at the cost of accuracy",
 -            min=0.0, max=10.0,
 -            default=1.0,
 -        )
 +    blur_glossy: FloatProperty(
 +        name="Filter Glossy",
 +        description="Adaptively blur glossy shaders after blurry bounces, "
 +        "to reduce noise at the cost of accuracy",
 +        min=0.0, max=10.0,
 +        default=1.0,
 +    )
  
 -        cls.max_bounces = IntProperty(
 -            name="Max Bounces",
 -            description="Total maximum number of bounces",
 -            min=0, max=1024,
 -            default=12,
 -        )
 +    max_bounces: IntProperty(
 +        name="Max Bounces",
 +        description="Total maximum number of bounces",
 +        min=0, max=1024,
 +        default=12,
 +    )
  
 -        cls.diffuse_bounces = IntProperty(
 -            name="Diffuse Bounces",
 -            description="Maximum number of diffuse reflection bounces, bounded by total maximum",
 -            min=0, max=1024,
 -            default=4,
 -        )
 -        cls.glossy_bounces = IntProperty(
 -            name="Glossy Bounces",
 -            description="Maximum number of glossy reflection bounces, bounded by total maximum",
 -            min=0, max=1024,
 -            default=4,
 -        )
 -        cls.transmission_bounces = IntProperty(
 -            name="Transmission Bounces",
 -            description="Maximum number of transmission bounces, bounded by total maximum",
 -            min=0, max=1024,
 -            default=12,
 -        )
 -        cls.volume_bounces = IntProperty(
 -            name="Volume Bounces",
 -            description="Maximum number of volumetric scattering events",
 -            min=0, max=1024,
 -            default=0,
 -        )
 +    diffuse_bounces: IntProperty(
 +        name="Diffuse Bounces",
 +        description="Maximum number of diffuse reflection bounces, bounded by total maximum",
 +        min=0, max=1024,
 +        default=4,
 +    )
 +    glossy_bounces: IntProperty(
 +        name="Glossy Bounces",
 +        description="Maximum number of glossy reflection bounces, bounded by total maximum",
 +        min=0, max=1024,
 +        default=4,
 +    )
 +    transmission_bounces: IntProperty(
 +        name="Transmission Bounces",
 +        description="Maximum number of transmission bounces, bounded by total maximum",
 +        min=0, max=1024,
 +        default=12,
 +    )
 +    volume_bounces: IntProperty(
 +        name="Volume Bounces",
 +        description="Maximum number of volumetric scattering events",
 +        min=0, max=1024,
 +        default=0,
 +    )
  
 -        cls.transparent_max_bounces = IntProperty(
 -            name="Transparent Max Bounces",
 -            description="Maximum number of transparent bounces",
 -            min=0, max=1024,
 -            default=8,
 -        )
 +    transparent_max_bounces: IntProperty(
 +        name="Transparent Max Bounces",
 +        description="Maximum number of transparent bounces",
 +        min=0, max=1024,
 +        default=8,
 +    )
  
 -        cls.volume_step_size = FloatProperty(
 -            name="Step Size",
 -            description="Distance between volume shader samples when rendering the volume "
 -            "(lower values give more accurate and detailed results, but also increased render time)",
 -            default=0.1,
 -            min=0.0000001, max=100000.0, soft_min=0.01, soft_max=1.0, precision=4
 -        )
 +    volume_step_size: FloatProperty(
 +        name="Step Size",
 +        description="Distance between volume shader samples when rendering the volume "
 +        "(lower values give more accurate and detailed results, but also increased render time)",
 +        default=0.1,
 +        min=0.0000001, max=100000.0, soft_min=0.01, soft_max=1.0, precision=4,
 +        unit='LENGTH'
 +    )
  
 -        cls.volume_max_steps = IntProperty(
 -            name="Max Steps",
 -            description="Maximum number of steps through the volume before giving up, "
 -            "to avoid extremely long render times with big objects or small step sizes",
 -            default=1024,
 -            min=2, max=65536
 -        )
 +    volume_max_steps: IntProperty(
 +        name="Max Steps",
 +        description="Maximum number of steps through the volume before giving up, "
 +        "to avoid extremely long render times with big objects or small step sizes",
 +        default=1024,
 +        min=2, max=65536
 +    )
  
 -        cls.dicing_rate = FloatProperty(
 -            name="Dicing Rate",
 -            description="Size of a micropolygon in pixels",
 -            min=0.1, max=1000.0, soft_min=0.5,
 -            default=1.0,
 -            subtype="PIXEL"
 -        )
 -        cls.preview_dicing_rate = FloatProperty(
 -            name="Preview Dicing Rate",
 -            description="Size of a micropolygon in pixels during preview render",
 -            min=0.1, max=1000.0, soft_min=0.5,
 -            default=8.0,
 -            subtype="PIXEL"
 -        )
 +    dicing_rate: FloatProperty(
 +        name="Dicing Rate",
 +        description="Size of a micropolygon in pixels",
 +        min=0.1, max=1000.0, soft_min=0.5,
 +        default=1.0,
 +        subtype='PIXEL'
 +    )
 +    preview_dicing_rate: FloatProperty(
 +        name="Preview Dicing Rate",
 +        description="Size of a micropolygon in pixels during preview render",
 +        min=0.1, max=1000.0, soft_min=0.5,
 +        default=8.0,
 +        subtype='PIXEL'
 +    )
  
 -        cls.max_subdivisions = IntProperty(
 -            name="Max Subdivisions",
 -            description="Stop subdividing when this level is reached even if the dice rate would produce finer tessellation",
 -            min=0, max=16,
 -            default=12,
 -        )
 +    max_subdivisions: IntProperty(
 +        name="Max Subdivisions",
 +        description="Stop subdividing when this level is reached even if the dice rate would produce finer tessellation",
 +        min=0, max=16,
 +        default=12,
 +    )
  
 -        cls.dicing_camera = PointerProperty(
 -            name="Dicing Camera",
 -            description="Camera to use as reference point when subdividing geometry, useful to avoid crawling "
 -            "artifacts in animations when the scene camera is moving",
 -            type=bpy.types.Object,
 -            poll=lambda self, obj: obj.type == 'CAMERA',
 -        )
 -        cls.offscreen_dicing_scale = FloatProperty(
 -            name="Offscreen Dicing Scale",
 -            description="Multiplier for dicing rate of geometry outside of the camera view. The dicing rate "
 -            "of objects is gradually increased the further they are outside the camera view. "
 -            "Lower values provide higher quality reflections and shadows for off screen objects, "
 -            "while higher values use less memory",
 -            min=1.0, soft_max=25.0,
 -            default=4.0,
 -        )
 +    dicing_camera: PointerProperty(
 +        name="Dicing Camera",
 +        description="Camera to use as reference point when subdividing geometry, useful to avoid crawling "
 +        "artifacts in animations when the scene camera is moving",
 +        type=bpy.types.Object,
 +        poll=lambda self, obj: obj.type == 'CAMERA',
 +    )
 +    offscreen_dicing_scale: FloatProperty(
 +        name="Offscreen Dicing Scale",
 +        description="Multiplier for dicing rate of geometry outside of the camera view. The dicing rate "
 +        "of objects is gradually increased the further they are outside the camera view. "
 +        "Lower values provide higher quality reflections and shadows for off screen objects, "
 +        "while higher values use less memory",
 +        min=1.0, soft_max=25.0,
 +        default=4.0,
 +    )
  
 -        cls.film_exposure = FloatProperty(
 -            name="Exposure",
 -            description="Image brightness scale",
 -            min=0.0, max=10.0,
 -            default=1.0,
 -        )
 -        cls.film_transparent = BoolProperty(
 -            name="Transparent",
 -            description="World background is transparent, for compositing the render over another background",
 -            default=False,
 -        )
 -        cls.film_transparent_glass = BoolProperty(
 -            name="Transparent Glass",
 -            description="Render transmissive surfaces as transparent, for compositing glass over another background",
 -            default=False,
 -        )
 -        cls.film_transparent_roughness = FloatProperty(
 -            name="Transparent Roughness Threshold",
 -            description="For transparent transmission, keep surfaces with roughness above the threshold opaque",
 -            min=0.0, max=1.0,
 -            default=0.1,
 -        )
 +    film_exposure: FloatProperty(
 +        name="Exposure",
 +        description="Image brightness scale",
 +        min=0.0, max=10.0,
 +        default=1.0,
 +    )
 +    film_transparent: BoolProperty(
 +        name="Transparent",
 +        description="World background is transparent, for compositing the render over another background",
 +        default=False,
 +    )
 +    film_transparent_glass: BoolProperty(
 +        name="Transparent Glass",
 +        description="Render transmissive surfaces as transparent, for compositing glass over another background",
 +        default=False,
 +    )
 +    film_transparent_roughness: FloatProperty(
 +        name="Transparent Roughness Threshold",
 +        description="For transparent transmission, keep surfaces with roughness above the threshold opaque",
 +        min=0.0, max=1.0,
 +        default=0.1,
 +    )
  
 -        # Really annoyingly, we have to keep it around for a few releases,
 -        # otherwise forward compatibility breaks in really bad manner: CRASH!
 -        #
 -        # TODO(sergey): Remove this during 2.8x series of Blender.
 -        cls.filter_type = EnumProperty(
 -            name="Filter Type",
 -            description="Pixel filter type",
 -            items=enum_filter_types,
 -            default='BLACKMAN_HARRIS',
 -        )
 +    # Really annoyingly, we have to keep it around for a few releases,
 +    # otherwise forward compatibility breaks in really bad manner: CRASH!
 +    #
 +    # TODO(sergey): Remove this during 2.8x series of Blender.
 +    filter_type: EnumProperty(
 +        name="Filter Type",
 +        description="Pixel filter type",
 +        items=enum_filter_types,
 +        default='BLACKMAN_HARRIS',
 +    )
  
 -        cls.pixel_filter_type = EnumProperty(
 -            name="Filter Type",
 -            description="Pixel filter type",
 -            items=enum_filter_types,
 -            default='BLACKMAN_HARRIS',
 -        )
 +    pixel_filter_type: EnumProperty(
 +        name="Filter Type",
 +        description="Pixel filter type",
 +        items=enum_filter_types,
 +        default='BLACKMAN_HARRIS',
 +    )
  
 -        cls.filter_width = FloatProperty(
 -            name="Filter Width",
 -            description="Pixel filter width",
 -            min=0.01, max=10.0,
 -            default=1.5,
 -        )
 +    filter_width: FloatProperty(
 +        name="Filter Width",
 +        description="Pixel filter width",
 +        min=0.01, max=10.0,
 +        default=1.5,
 +        subtype='PIXEL'
 +    )
  
 -        cls.seed = IntProperty(
 -            name="Seed",
 -            description="Seed value for integrator to get different noise patterns",
 -            min=0, max=2147483647,
 -            default=0,
 -        )
 +    seed: IntProperty(
 +        name="Seed",
 +        description="Seed value for integrator to get different noise patterns",
 +        min=0, max=2147483647,
 +        default=0,
 +    )
  
 -        cls.use_animated_seed = BoolProperty(
 -            name="Use Animated Seed",
 -            description="Use different seed values (and hence noise patterns) at different frames",
 -            default=False,
 -        )
 +    use_animated_seed: BoolProperty(
 +        name="Use Animated Seed",
 +        description="Use different seed values (and hence noise patterns) at different frames",
 +        default=False,
 +    )
  
 -        cls.sample_clamp_direct = FloatProperty(
 -            name="Clamp Direct",
 -            description="If non-zero, the maximum value for a direct sample, "
 -            "higher values will be scaled down to avoid too "
 -            "much noise and slow convergence at the cost of accuracy",
 -            min=0.0, max=1e8,
 -            default=0.0,
 -        )
 +    sample_clamp_direct: FloatProperty(
 +        name="Clamp Direct",
 +        description="If non-zero, the maximum value for a direct sample, "
 +        "higher values will be scaled down to avoid too "
 +        "much noise and slow convergence at the cost of accuracy",
 +        min=0.0, max=1e8,
 +        default=0.0,
 +    )
  
 -        cls.sample_clamp_indirect = FloatProperty(
 -            name="Clamp Indirect",
 -            description="If non-zero, the maximum value for an indirect sample, "
 -            "higher values will be scaled down to avoid too "
 -            "much noise and slow convergence at the cost of accuracy",
 -            min=0.0, max=1e8,
 -            default=10.0,
 -        )
 +    sample_clamp_indirect: FloatProperty(
 +        name="Clamp Indirect",
 +        description="If non-zero, the maximum value for an indirect sample, "
 +        "higher values will be scaled down to avoid too "
 +        "much noise and slow convergence at the cost of accuracy",
 +        min=0.0, max=1e8,
 +        default=10.0,
 +    )
  
 -        cls.debug_tile_size = IntProperty(
 -            name="Tile Size",
 -            description="",
 -            min=1, max=4096,
 -            default=1024,
 -        )
 +    debug_tile_size: IntProperty(
 +        name="Tile Size",
 +        description="",
 +        min=1, max=4096,
 +        default=1024,
 +    )
  
 -        cls.preview_start_resolution = IntProperty(
 -            name="Start Resolution",
 -            description="Resolution to start rendering preview at, "
 -            "progressively increasing it to the full viewport size",
 -            min=8, max=16384,
 -            default=64,
 -        )
 +    preview_start_resolution: IntProperty(
 +        name="Start Resolution",
 +        description="Resolution to start rendering preview at, "
 +        "progressively increasing it to the full viewport size",
 +        min=8, max=16384,
 +        default=64,
 +        subtype='PIXEL'
 +    )
  
 -        cls.debug_reset_timeout = FloatProperty(
 -            name="Reset timeout",
 -            description="",
 -            min=0.01, max=10.0,
 -            default=0.1,
 -        )
 -        cls.debug_cancel_timeout = FloatProperty(
 -            name="Cancel timeout",
 -            description="",
 -            min=0.01, max=10.0,
 -            default=0.1,
 -        )
 -        cls.debug_text_timeout = FloatProperty(
 -            name="Text timeout",
 -            description="",
 -            min=0.01, max=10.0,
 -            default=1.0,
 -        )
 +    debug_reset_timeout: FloatProperty(
 +        name="Reset timeout",
 +        description="",
 +        min=0.01, max=10.0,
 +        default=0.1,
 +    )
 +    debug_cancel_timeout: FloatProperty(
 +        name="Cancel timeout",
 +        description="",
 +        min=0.01, max=10.0,
 +        default=0.1,
 +    )
 +    debug_text_timeout: FloatProperty(
 +        name="Text timeout",
 +        description="",
 +        min=0.01, max=10.0,
 +        default=1.0,
 +    )
  
 -        cls.debug_bvh_type = EnumProperty(
 -            name="Viewport BVH Type",
 -            description="Choose between faster updates, or faster render",
 -            items=enum_bvh_types,
 -            default='DYNAMIC_BVH',
 -        )
 -        cls.debug_use_spatial_splits = BoolProperty(
 -            name="Use Spatial Splits",
 -            description="Use BVH spatial splits: longer builder time, faster render",
 -            default=False,
 -        )
 -        cls.debug_use_hair_bvh = BoolProperty(
 -            name="Use Hair BVH",
 -            description="Use special type BVH optimized for hair (uses more ram but renders faster)",
 -            default=True,
 -        )
 -        cls.use_bvh_embree = BoolProperty(
 -            name="Use Embree",
 -            description="Use Embree as ray accelerator",
 -            default=False,
 -        )
 -        cls.debug_bvh_time_steps = IntProperty(
 -            name="BVH Time Steps",
 -            description="Split BVH primitives by this number of time steps to speed up render time in cost of memory",
 -            default=0,
 -            min=0, max=16,
 -        )
 -        cls.tile_order = EnumProperty(
 -            name="Tile Order",
 -            description="Tile order for rendering",
 -            items=enum_tile_order,
 -            default='HILBERT_SPIRAL',
 -            options=set(),  # Not animatable!
 -        )
 -        cls.use_progressive_refine = BoolProperty(
 -            name="Progressive Refine",
 -            description="Instead of rendering each tile until it is finished, "
 -            "refine the whole image progressively "
 -            "(this renders somewhat slower, "
 -            "but time can be saved by manually stopping the render when the noise is low enough)",
 -            default=False,
 -        )
 +    debug_bvh_type: EnumProperty(
 +        name="Viewport BVH Type",
 +        description="Choose between faster updates, or faster render",
 +        items=enum_bvh_types,
 +        default='DYNAMIC_BVH',
 +    )
 +    use_bvh_embree: BoolProperty(
 +        name="Use Embree",
 +        description="Use Embree as ray accelerator",
 +        default=False,
 +    )
 +    debug_use_spatial_splits: BoolProperty(
 +        name="Use Spatial Splits",
 +        description="Use BVH spatial splits: longer builder time, faster render",
 +        default=False,
 +    )
 +    debug_use_hair_bvh: BoolProperty(
 +        name="Use Hair BVH",
 +        description="Use special type BVH optimized for hair (uses more ram but renders faster)",
 +        default=True,
 +    )
 +    debug_bvh_time_steps: IntProperty(
 +        name="BVH Time Steps",
 +        description="Split BVH primitives by this number of time steps to speed up render time in cost of memory",
 +        default=0,
 +        min=0, max=16,
 +    )
 +    tile_order: EnumProperty(
 +        name="Tile Order",
 +        description="Tile order for rendering",
 +        items=enum_tile_order,
 +        default='HILBERT_SPIRAL',
 +        options=set(),  # Not animatable!
 +    )
 +    use_progressive_refine: BoolProperty(
 +        name="Progressive Refine",
 +        description="Instead of rendering each tile until it is finished, "
 +        "refine the whole image progressively "
 +        "(this renders somewhat slower, "
 +        "but time can be saved by manually stopping the render when the noise is low enough)",
 +        default=False,
 +    )
  
 -        cls.bake_type = EnumProperty(
 -            name="Bake Type",
 -            default='COMBINED',
 -            description="Type of pass to bake",
 -            items=(
 -                ('COMBINED', "Combined", ""),
 -                ('AO', "Ambient Occlusion", ""),
 -                ('SHADOW', "Shadow", ""),
 -                ('NORMAL', "Normal", ""),
 -                ('UV', "UV", ""),
 -                ('ROUGHNESS', "Roughness", ""),
 -                ('EMIT', "Emit", ""),
 -                ('ENVIRONMENT', "Environment", ""),
 -                ('DIFFUSE', "Diffuse", ""),
 -                ('GLOSSY', "Glossy", ""),
 -                ('TRANSMISSION', "Transmission", ""),
 -                ('SUBSURFACE', "Subsurface", ""),
 -            ),
 -        )
 +    bake_type: EnumProperty(
 +        name="Bake Type",
 +        default='COMBINED',
 +        description="Type of pass to bake",
 +        items=(
 +            ('COMBINED', "Combined", ""),
 +            ('AO', "Ambient Occlusion", ""),
 +            ('SHADOW', "Shadow", ""),
 +            ('NORMAL', "Normal", ""),
 +            ('UV', "UV", ""),
 +            ('ROUGHNESS', "Roughness", ""),
 +            ('EMIT', "Emit", ""),
 +            ('ENVIRONMENT', "Environment", ""),
 +            ('DIFFUSE', "Diffuse", ""),
 +            ('GLOSSY', "Glossy", ""),
 +            ('TRANSMISSION', "Transmission", ""),
 +            ('SUBSURFACE', "Subsurface", ""),
 +        ),
 +    )
  
 -        cls.use_camera_cull = BoolProperty(
 -            name="Use Camera Cull",
 -            description="Allow objects to be culled based on the camera frustum",
 -            default=False,
 -        )
 +    use_camera_cull: BoolProperty(
 +        name="Use Camera Cull",
 +        description="Allow objects to be culled based on the camera frustum",
 +        default=False,
 +    )
  
 -        cls.camera_cull_margin = FloatProperty(
 -            name="Camera Cull Margin",
 -            description="Margin for the camera space culling",
 -            default=0.1,
 -            min=0.0, max=5.0
 -        )
 +    camera_cull_margin: FloatProperty(
 +        name="Camera Cull Margin",
 +        description="Margin for the camera space culling",
 +        default=0.1,
 +        min=0.0, max=5.0,
 +        subtype='FACTOR'
 +    )
  
 -        cls.use_distance_cull = BoolProperty(
 -            name="Use Distance Cull",
 -            description="Allow objects to be culled based on the distance from camera",
 -            default=False,
 -        )
 +    use_distance_cull: BoolProperty(
 +        name="Use Distance Cull",
 +        description="Allow objects to be culled based on the distance from camera",
 +        default=False,
 +    )
  
 -        cls.distance_cull_margin = FloatProperty(
 -            name="Cull Distance",
 -            description="Cull objects which are further away from camera than this distance",
 -            default=50,
 -            min=0.0
 -        )
 +    distance_cull_margin: FloatProperty(
 +        name="Cull Distance",
 +        description="Cull objects which are further away from camera than this distance",
 +        default=50,
 +        min=0.0,
 +        unit='LENGTH'
 +    )
  
 -        cls.motion_blur_position = EnumProperty(
 -            name="Motion Blur Position",
 -            default='CENTER',
 -            description="Offset for the shutter's time interval, allows to change the motion blur trails",
 -            items=(
 -                ('START', "Start on Frame", "The shutter opens at the current frame"),
 -                ('CENTER', "Center on Frame", "The shutter is open during the current frame"),
 -                ('END', "End on Frame", "The shutter closes at the current frame"),
 -            ),
 -        )
 +    motion_blur_position: EnumProperty(
 +        name="Motion Blur Position",
 +        default='CENTER',
 +        description="Offset for the shutter's time interval, allows to change the motion blur trails",
 +        items=(
 +            ('START', "Start on Frame", "The shutter opens at the current frame"),
 +            ('CENTER', "Center on Frame", "The shutter is open during the current frame"),
 +            ('END', "End on Frame", "The shutter closes at the current frame"),
 +        ),
 +    )
  
 -        cls.rolling_shutter_type = EnumProperty(
 -            name="Shutter Type",
 -            default='NONE',
 -            description="Type of rolling shutter effect matching CMOS-based cameras",
 -            items=(
 -                ('NONE', "None", "No rolling shutter effect used"),
 -                ('TOP', "Top-Bottom", "Sensor is being scanned from top to bottom")
 -                # TODO(seergey): Are there real cameras with different scanning direction?
 -            ),
 -        )
 +    rolling_shutter_type: EnumProperty(
 +        name="Shutter Type",
 +        default='NONE',
 +        description="Type of rolling shutter effect matching CMOS-based cameras",
 +        items=(
 +            ('NONE', "None", "No rolling shutter effect used"),
 +            ('TOP', "Top-Bottom", "Sensor is being scanned from top to bottom")
 +            # TODO(seergey): Are there real cameras with different scanning direction?
 +        ),
 +    )
  
 -        cls.rolling_shutter_duration = FloatProperty(
 -            name="Rolling Shutter Duration",
 -            description="Scanline \"exposure\" time for the rolling shutter effect",
 -            default=0.1,
 -            min=0.0, max=1.0,
 -        )
 +    rolling_shutter_duration: FloatProperty(
 +        name="Rolling Shutter Duration",
 +        description="Scanline \"exposure\" time for the rolling shutter effect",
 +        default=0.1,
 +        min=0.0, max=1.0,
 +    )
  
 -        cls.texture_limit = EnumProperty(
 -            name="Viewport Texture Limit",
 -            default='OFF',
 -            description="Limit texture size used by viewport rendering",
 -            items=enum_texture_limit
 -        )
 +    texture_limit: EnumProperty(
 +        name="Viewport Texture Limit",
 +        default='OFF',
 +        description="Limit texture size used by viewport rendering",
 +        items=enum_texture_limit
 +    )
  
 -        cls.texture_limit_render = EnumProperty(
 -            name="Render Texture Limit",
 -            default='OFF',
 -            description="Limit texture size used by final rendering",
 -            items=enum_texture_limit
 -        )
 +    texture_limit_render: EnumProperty(
 +        name="Render Texture Limit",
 +        default='OFF',
 +        description="Limit texture size used by final rendering",
 +        items=enum_texture_limit
 +    )
  
 -        cls.ao_bounces = IntProperty(
 -            name="AO Bounces",
 -            default=0,
 -            description="Approximate indirect light with background tinted ambient occlusion at the specified bounce, 0 disables this feature",
 -            min=0, max=1024,
 -        )
 +    ao_bounces: IntProperty(
 +        name="AO Bounces",
 +        default=0,
 +        description="Approximate indirect light with background tinted ambient occlusion at the specified bounce, 0 disables this feature",
 +        min=0, max=1024,
 +    )
  
 -        cls.ao_bounces_render = IntProperty(
 -            name="AO Bounces Render",
 -            default=0,
 -            description="Approximate indirect light with background tinted ambient occlusion at the specified bounce, 0 disables this feature",
 -            min=0, max=1024,
 -        )
 +    ao_bounces_render: IntProperty(
 +        name="AO Bounces Render",
 +        default=0,
 +        description="Approximate indirect light with background tinted ambient occlusion at the specified bounce, 0 disables this feature",
 +        min=0, max=1024,
 +    )
  
 -        # Various fine-tuning debug flags
 -
 -        def devices_update_callback(self, context):
 -            import _cycles
 -            scene = context.scene.as_pointer()
 -            return _cycles.debug_flags_update(scene)
 -
 -        cls.debug_use_cpu_avx2 = BoolProperty(name="AVX2", default=True)
 -        cls.debug_use_cpu_avx = BoolProperty(name="AVX", default=True)
 -        cls.debug_use_cpu_sse41 = BoolProperty(name="SSE41", default=True)
 -        cls.debug_use_cpu_sse3 = BoolProperty(name="SSE3", default=True)
 -        cls.debug_use_cpu_sse2 = BoolProperty(name="SSE2", default=True)
 -        cls.debug_bvh_layout = EnumProperty(
 -            name="BVH Layout",
 -            items=enum_bvh_layouts,
 -            default='BVH8',
 -        )
 -        cls.debug_use_cpu_split_kernel = BoolProperty(name="Split Kernel", default=False)
 -
 -        cls.debug_use_cuda_adaptive_compile = BoolProperty(name="Adaptive Compile", default=False)
 -        cls.debug_use_cuda_split_kernel = BoolProperty(name="Split Kernel", default=False)
 -
 -        cls.debug_opencl_kernel_type = EnumProperty(
 -            name="OpenCL Kernel Type",
 -            default='DEFAULT',
 -            items=(
 -                ('DEFAULT', "Default", ""),
 -                ('MEGA', "Mega", ""),
 -                ('SPLIT', "Split", ""),
 -            ),
 -            update=devices_update_callback
 -        )
 +    # Various fine-tuning debug flags
  
 -        cls.debug_opencl_device_type = EnumProperty(
 -            name="OpenCL Device Type",
 -            default='ALL',
 -            items=(
 -                ('NONE', "None", ""),
 -                ('ALL', "All", ""),
 -                ('DEFAULT', "Default", ""),
 -                ('CPU', "CPU", ""),
 -                ('GPU', "GPU", ""),
 -                ('ACCELERATOR', "Accelerator", ""),
 -            ),
 -            update=devices_update_callback
 -        )
 +    def _devices_update_callback(self, context):
 +        import _cycles
 +        scene = context.scene.as_pointer()
 +        return _cycles.debug_flags_update(scene)
 +
 +    debug_use_cpu_avx2: BoolProperty(name="AVX2", default=True)
 +    debug_use_cpu_avx: BoolProperty(name="AVX", default=True)
 +    debug_use_cpu_sse41: BoolProperty(name="SSE41", default=True)
 +    debug_use_cpu_sse3: BoolProperty(name="SSE3", default=True)
 +    debug_use_cpu_sse2: BoolProperty(name="SSE2", default=True)
 +    debug_bvh_layout: EnumProperty(
 +        name="BVH Layout",
 +        items=enum_bvh_layouts,
 +        default='BVH8',
 +    )
 +    debug_use_cpu_split_kernel: BoolProperty(name="Split Kernel", default=False)
 +
 +    debug_use_cuda_adaptive_compile: BoolProperty(name="Adaptive Compile", default=False)
 +    debug_use_cuda_split_kernel: BoolProperty(name="Split Kernel", default=False)
 +
 +    debug_opencl_kernel_type: EnumProperty(
 +        name="OpenCL Kernel Type",
 +        default='DEFAULT',
 +        items=(
 +            ('DEFAULT', "Default", ""),
 +            ('MEGA', "Mega", ""),
 +            ('SPLIT', "Split", ""),
 +        ),
 +        update=_devices_update_callback
 +    )
  
 -        cls.debug_opencl_kernel_single_program = BoolProperty(
 -            name="Single Program",
 -            default=True,
 -            update=devices_update_callback,
 -        )
 +    debug_opencl_device_type: EnumProperty(
 +        name="OpenCL Device Type",
 +        default='ALL',
 +        items=(
 +            ('NONE', "None", ""),
 +            ('ALL', "All", ""),
 +            ('DEFAULT', "Default", ""),
 +            ('CPU', "CPU", ""),
 +            ('GPU', "GPU", ""),
 +            ('ACCELERATOR', "Accelerator", ""),
 +        ),
 +        update=_devices_update_callback
 +    )
  
 -        cls.debug_use_opencl_debug = BoolProperty(name="Debug OpenCL", default=False)
 +    debug_opencl_kernel_single_program: BoolProperty(
 +        name="Single Program",
 +        default=True,
 +        update=_devices_update_callback,
 +    )
 +    del _devices_update_callback
 +
 +    debug_use_opencl_debug: BoolProperty(name="Debug OpenCL", default=False)
 +
 +    debug_opencl_mem_limit: IntProperty(
 +        name="Memory limit",
 +        default=0,
 +        description="Artificial limit on OpenCL memory usage in MB (0 to disable limit)"
 +    )
  
 -        cls.debug_opencl_mem_limit = IntProperty(name="Memory limit", default=0,
 -                                                 description="Artificial limit on OpenCL memory usage in MB (0 to disable limit)")
 +    @classmethod
 +    def register(cls):
 +        bpy.types.Scene.cycles = PointerProperty(
 +            name="Cycles Render Settings",
 +            description="Cycles render settings",
 +            type=cls,
 +        )
  
      @classmethod
      def unregister(cls):
  
  
  class CyclesCameraSettings(bpy.types.PropertyGroup):
 +
 +    aperture_type: EnumProperty(
 +        name="Aperture Type",
 +        description="Use f-stop number or aperture radius",
 +        items=enum_aperture_types,
 +        default='RADIUS',
 +    )
 +    aperture_fstop: FloatProperty(
 +        name="Aperture f-stop",
 +        description="F-stop ratio (lower numbers give more defocus, higher numbers give a sharper image)",
 +        min=0.0, soft_min=0.1, soft_max=64.0,
 +        default=5.6,
 +        step=10,
 +        precision=1,
 +    )
 +    aperture_size: FloatProperty(
 +        name="Aperture Size",
 +        description="Radius of the aperture for depth of field (higher values give more defocus)",
 +        min=0.0, soft_max=10.0,
 +        default=0.0,
 +        step=1,
 +        precision=4,
 +        subtype='DISTANCE',
 +    )
 +    aperture_blades: IntProperty(
 +        name="Aperture Blades",
 +        description="Number of blades in aperture for polygonal bokeh (at least 3)",
 +        min=0, max=100,
 +        default=0,
 +    )
 +    aperture_rotation: FloatProperty(
 +        name="Aperture Rotation",
 +        description="Rotation of blades in aperture",
 +        soft_min=-pi, soft_max=pi,
 +        subtype='ANGLE',
 +        default=0,
 +    )
 +    aperture_ratio: FloatProperty(
 +        name="Aperture Ratio",
 +        description="Distortion to simulate anamorphic lens bokeh",
 +        min=0.01, soft_min=1.0, soft_max=2.0,
 +        default=1.0,
 +        precision=4,
 +    )
 +    panorama_type: EnumProperty(
 +        name="Panorama Type",
 +        description="Distortion to use for the calculation",
 +        items=enum_panorama_types,
 +        default='FISHEYE_EQUISOLID',
 +    )
 +    fisheye_fov: FloatProperty(
 +        name="Field of View",
 +        description="Field of view for the fisheye lens",
 +        min=0.1745, soft_max=2.0 * pi, max=10.0 * pi,
 +        subtype='ANGLE',
 +        default=pi,
 +    )
 +    fisheye_lens: FloatProperty(
 +        name="Fisheye Lens",
 +        description="Lens focal length (mm)",
 +        min=0.01, soft_max=15.0, max=100.0,
 +        default=10.5,
 +    )
 +    latitude_min: FloatProperty(
 +        name="Min Latitude",
 +        description="Minimum latitude (vertical angle) for the equirectangular lens",
 +        min=-0.5 * pi, max=0.5 * pi,
 +        subtype='ANGLE',
 +        default=-0.5 * pi,
 +    )
 +    latitude_max: FloatProperty(
 +        name="Max Latitude",
 +        description="Maximum latitude (vertical angle) for the equirectangular lens",
 +        min=-0.5 * pi, max=0.5 * pi,
 +        subtype='ANGLE',
 +        default=0.5 * pi,
 +    )
 +    longitude_min: FloatProperty(
 +        name="Min Longitude",
 +        description="Minimum longitude (horizontal angle) for the equirectangular lens",
 +        min=-pi, max=pi,
 +        subtype='ANGLE',
 +        default=-pi,
 +    )
 +    longitude_max: FloatProperty(
 +        name="Max Longitude",
 +        description="Maximum longitude (horizontal angle) for the equirectangular lens",
 +        min=-pi, max=pi,
 +        subtype='ANGLE',
 +        default=pi,
 +    )
 +
      @classmethod
      def register(cls):
 -        import math
 -
          bpy.types.Camera.cycles = PointerProperty(
              name="Cycles Camera Settings",
              description="Cycles camera settings",
              type=cls,
          )
  
 -        cls.aperture_type = EnumProperty(
 -            name="Aperture Type",
 -            description="Use f-stop number or aperture radius",
 -            items=enum_aperture_types,
 -            default='RADIUS',
 -        )
 -        cls.aperture_fstop = FloatProperty(
 -            name="Aperture f-stop",
 -            description="F-stop ratio (lower numbers give more defocus, higher numbers give a sharper image)",
 -            min=0.0, soft_min=0.1, soft_max=64.0,
 -            default=5.6,
 -            step=10,
 -            precision=1,
 -        )
 -        cls.aperture_size = FloatProperty(
 -            name="Aperture Size",
 -            description="Radius of the aperture for depth of field (higher values give more defocus)",
 -            min=0.0, soft_max=10.0,
 -            default=0.0,
 -            step=1,
 -            precision=4,
 -            subtype='DISTANCE',
 -        )
 -        cls.aperture_blades = IntProperty(
 -            name="Aperture Blades",
 -            description="Number of blades in aperture for polygonal bokeh (at least 3)",
 -            min=0, max=100,
 -            default=0,
 -        )
 -        cls.aperture_rotation = FloatProperty(
 -            name="Aperture Rotation",
 -            description="Rotation of blades in aperture",
 -            soft_min=-math.pi, soft_max=math.pi,
 -            subtype='ANGLE',
 -            default=0,
 -        )
 -        cls.aperture_ratio = FloatProperty(
 -            name="Aperture Ratio",
 -            description="Distortion to simulate anamorphic lens bokeh",
 -            min=0.01, soft_min=1.0, soft_max=2.0,
 -            default=1.0,
 -            precision=4,
 -        )
 -        cls.panorama_type = EnumProperty(
 -            name="Panorama Type",
 -            description="Distortion to use for the calculation",
 -            items=enum_panorama_types,
 -            default='FISHEYE_EQUISOLID',
 -        )
 -        cls.fisheye_fov = FloatProperty(
 -            name="Field of View",
 -            description="Field of view for the fisheye lens",
 -            min=0.1745, soft_max=2.0 * math.pi, max=10.0 * math.pi,
 -            subtype='ANGLE',
 -            default=math.pi,
 -        )
 -        cls.fisheye_lens = FloatProperty(
 -            name="Fisheye Lens",
 -            description="Lens focal length (mm)",
 -            min=0.01, soft_max=15.0, max=100.0,
 -            default=10.5,
 -        )
 -        cls.latitude_min = FloatProperty(
 -            name="Min Latitude",
 -            description="Minimum latitude (vertical angle) for the equirectangular lens",
 -            min=-0.5 * math.pi, max=0.5 * math.pi,
 -            subtype='ANGLE',
 -            default=-0.5 * math.pi,
 -        )
 -        cls.latitude_max = FloatProperty(
 -            name="Max Latitude",
 -            description="Maximum latitude (vertical angle) for the equirectangular lens",
 -            min=-0.5 * math.pi, max=0.5 * math.pi,
 -            subtype='ANGLE',
 -            default=0.5 * math.pi,
 -        )
 -        cls.longitude_min = FloatProperty(
 -            name="Min Longitude",
 -            description="Minimum longitude (horizontal angle) for the equirectangular lens",
 -            min=-math.pi, max=math.pi,
 -            subtype='ANGLE',
 -            default=-math.pi,
 -        )
 -        cls.longitude_max = FloatProperty(
 -            name="Max Longitude",
 -            description="Maximum longitude (horizontal angle) for the equirectangular lens",
 -            min=-math.pi, max=math.pi,
 -            subtype='ANGLE',
 -            default=math.pi,
 -        )
 -
      @classmethod
      def unregister(cls):
          del bpy.types.Camera.cycles
  
  
  class CyclesMaterialSettings(bpy.types.PropertyGroup):
 +
 +    sample_as_light: BoolProperty(
 +        name="Multiple Importance Sample",
 +        description="Use multiple importance sampling for this material, "
 +        "disabling may reduce overall noise for large "
 +        "objects that emit little light compared to other light sources",
 +        default=True,
 +    )
 +    use_transparent_shadow: BoolProperty(
 +        name="Transparent Shadows",
 +        description="Use transparent shadows for this material if it contains a Transparent BSDF, "
 +        "disabling will render faster but not give accurate shadows",
 +        default=True,
 +    )
 +    homogeneous_volume: BoolProperty(
 +        name="Homogeneous Volume",
 +        description="When using volume rendering, assume volume has the same density everywhere "
 +        "(not using any textures), for faster rendering",
 +        default=False,
 +    )
 +    volume_sampling: EnumProperty(
 +        name="Volume Sampling",
 +        description="Sampling method to use for volumes",
 +        items=enum_volume_sampling,
 +        default='MULTIPLE_IMPORTANCE',
 +    )
 +
 +    volume_interpolation: EnumProperty(
 +        name="Volume Interpolation",
 +        description="Interpolation method to use for smoke/fire volumes",
 +        items=enum_volume_interpolation,
 +        default='LINEAR',
 +    )
 +
 +    displacement_method: EnumProperty(
 +        name="Displacement Method",
 +        description="Method to use for the displacement",
 +        items=enum_displacement_methods,
 +        default='BUMP',
 +    )
 +
      @classmethod
      def register(cls):
          bpy.types.Material.cycles = PointerProperty(
              description="Cycles material settings",
              type=cls,
          )
 -        cls.sample_as_light = BoolProperty(
 -            name="Multiple Importance Sample",
 -            description="Use multiple importance sampling for this material, "
 -            "disabling may reduce overall noise for large "
 -            "objects that emit little light compared to other light sources",
 -            default=True,
 -        )
 -        cls.use_transparent_shadow = BoolProperty(
 -            name="Transparent Shadows",
 -            description="Use transparent shadows for this material if it contains a Transparent BSDF, "
 -            "disabling will render faster but not give accurate shadows",
 -            default=True,
 -        )
 -        cls.homogeneous_volume = BoolProperty(
 -            name="Homogeneous Volume",
 -            description="When using volume rendering, assume volume has the same density everywhere "
 -            "(not using any textures), for faster rendering",
 -            default=False,
 -        )
 -        cls.volume_sampling = EnumProperty(
 -            name="Volume Sampling",
 -            description="Sampling method to use for volumes",
 -            items=enum_volume_sampling,
 -            default='MULTIPLE_IMPORTANCE',
 -        )
 -
 -        cls.volume_interpolation = EnumProperty(
 -            name="Volume Interpolation",
 -            description="Interpolation method to use for smoke/fire volumes",
 -            items=enum_volume_interpolation,
 -            default='LINEAR',
 -        )
 -
 -        cls.displacement_method = EnumProperty(
 -            name="Displacement Method",
 -            description="Method to use for the displacement",
 -            items=enum_displacement_methods,
 -            default='BUMP',
 -        )
  
      @classmethod
      def unregister(cls):
          del bpy.types.Material.cycles
  
  
 -class CyclesLampSettings(bpy.types.PropertyGroup):
 +class CyclesLightSettings(bpy.types.PropertyGroup):
 +
 +    cast_shadow: BoolProperty(
 +        name="Cast Shadow",
 +        description="Light casts shadows",
 +        default=True,
 +    )
 +    samples: IntProperty(
 +        name="Samples",
 +        description="Number of light samples to render for each AA sample",
 +        min=1, max=10000,
 +        default=1,
 +    )
 +    max_bounces: IntProperty(
 +        name="Max Bounces",
 +        description="Maximum number of bounces the light will contribute to the render",
 +        min=0, max=1024,
 +        default=1024,
 +    )
 +    use_multiple_importance_sampling: BoolProperty(
 +        name="Multiple Importance Sample",
 +        description="Use multiple importance sampling for the light, "
 +        "reduces noise for area lights and sharp glossy materials",
 +        default=True,
 +    )
 +    is_portal: BoolProperty(
 +        name="Is Portal",
 +        description="Use this area light to guide sampling of the background, "
 +        "note that this will make the light invisible",
 +        default=False,
 +    )
 +
      @classmethod
      def register(cls):
 -        bpy.types.Lamp.cycles = PointerProperty(
 -            name="Cycles Lamp Settings",
 -            description="Cycles lamp settings",
 +        bpy.types.Light.cycles = PointerProperty(
 +            name="Cycles Light Settings",
 +            description="Cycles light settings",
              type=cls,
          )
 -        cls.cast_shadow = BoolProperty(
 -            name="Cast Shadow",
 -            description="Lamp casts shadows",
 -            default=True,
 -        )
 -        cls.samples = IntProperty(
 -            name="Samples",
 -            description="Number of light samples to render for each AA sample",
 -            min=1, max=10000,
 -            default=1,
 -        )
 -        cls.max_bounces = IntProperty(
 -            name="Max Bounces",
 -            description="Maximum number of bounces the light will contribute to the render",
 -            min=0, max=1024,
 -            default=1024,
 -        )
 -        cls.use_multiple_importance_sampling = BoolProperty(
 -            name="Multiple Importance Sample",
 -            description="Use multiple importance sampling for the lamp, "
 -            "reduces noise for area lamps and sharp glossy materials",
 -            default=True,
 -        )
 -        cls.is_portal = BoolProperty(
 -            name="Is Portal",
 -            description="Use this area lamp to guide sampling of the background, "
 -            "note that this will make the lamp invisible",
 -            default=False,
 -        )
  
      @classmethod
      def unregister(cls):
 -        del bpy.types.Lamp.cycles
 +        del bpy.types.Light.cycles
  
  
  class CyclesWorldSettings(bpy.types.PropertyGroup):
 +
 +    sampling_method: EnumProperty(
 +        name="Sampling method",
 +        description="How to sample the background light",
 +        items=enum_world_mis,
 +        default='AUTOMATIC',
 +    )
 +    sample_map_resolution: IntProperty(
 +        name="Map Resolution",
 +        description="Importance map size is resolution x resolution/2; "
 +        "higher values potentially produce less noise, at the cost of memory and speed",
 +        min=4, max=8192,
 +        default=1024,
 +    )
 +    samples: IntProperty(
 +        name="Samples",
 +        description="Number of light samples to render for each AA sample",
 +        min=1, max=10000,
 +        default=1,
 +    )
 +    max_bounces: IntProperty(
 +        name="Max Bounces",
 +        description="Maximum number of bounces the background light will contribute to the render",
 +        min=0, max=1024,
 +        default=1024,
 +    )
 +    homogeneous_volume: BoolProperty(
 +        name="Homogeneous Volume",
 +        description="When using volume rendering, assume volume has the same density everywhere"
 +        "(not using any textures), for faster rendering",
 +        default=False,
 +    )
 +    volume_sampling: EnumProperty(
 +        name="Volume Sampling",
 +        description="Sampling method to use for volumes",
 +        items=enum_volume_sampling,
 +        default='EQUIANGULAR',
 +    )
 +    volume_interpolation: EnumProperty(
 +        name="Volume Interpolation",
 +        description="Interpolation method to use for volumes",
 +        items=enum_volume_interpolation,
 +        default='LINEAR',
 +    )
 +
      @classmethod
      def register(cls):
          bpy.types.World.cycles = PointerProperty(
              description="Cycles world settings",
              type=cls,
          )
 -        cls.sampling_method = EnumProperty(
 -            name="Sampling method",
 -            description="How to sample the background light",
 -            items=enum_world_mis,
 -            default='AUTOMATIC',
 -        )
 -        cls.sample_map_resolution = IntProperty(
 -            name="Map Resolution",
 -            description="Importance map size is resolution x resolution/2; "
 -            "higher values potentially produce less noise, at the cost of memory and speed",
 -            min=4, max=8192,
 -            default=1024,
 -        )
 -        cls.samples = IntProperty(
 -            name="Samples",
 -            description="Number of light samples to render for each AA sample",
 -            min=1, max=10000,
 -            default=1,
 -        )
 -        cls.max_bounces = IntProperty(
 -            name="Max Bounces",
 -            description="Maximum number of bounces the background light will contribute to the render",
 -            min=0, max=1024,
 -            default=1024,
 -        )
 -        cls.homogeneous_volume = BoolProperty(
 -            name="Homogeneous Volume",
 -            description="When using volume rendering, assume volume has the same density everywhere"
 -            "(not using any textures), for faster rendering",
 -            default=False,
 -        )
 -        cls.volume_sampling = EnumProperty(
 -            name="Volume Sampling",
 -            description="Sampling method to use for volumes",
 -            items=enum_volume_sampling,
 -            default='EQUIANGULAR',
 -        )
 -
 -        cls.volume_interpolation = EnumProperty(
 -            name="Volume Interpolation",
 -            description="Interpolation method to use for volumes",
 -            items=enum_volume_interpolation,
 -            default='LINEAR',
 -        )
  
      @classmethod
      def unregister(cls):
  
  
  class CyclesVisibilitySettings(bpy.types.PropertyGroup):
 +
 +    camera: BoolProperty(
 +        name="Camera",
 +        description="Object visibility for camera rays",
 +        default=True,
 +    )
 +    diffuse: BoolProperty(
 +        name="Diffuse",
 +        description="Object visibility for diffuse reflection rays",
 +        default=True,
 +    )
 +    glossy: BoolProperty(
 +        name="Glossy",
 +        description="Object visibility for glossy reflection rays",
 +        default=True,
 +    )
 +    transmission: BoolProperty(
 +        name="Transmission",
 +        description="Object visibility for transmission rays",
 +        default=True,
 +    )
 +    shadow: BoolProperty(
 +        name="Shadow",
 +        description="Object visibility for shadow rays",
 +        default=True,
 +    )
 +    scatter: BoolProperty(
 +        name="Volume Scatter",
 +        description="Object visibility for volume scatter rays",
 +        default=True,
 +    )
 +
      @classmethod
      def register(cls):
          bpy.types.Object.cycles_visibility = PointerProperty(
              type=cls,
          )
  
 -        cls.camera = BoolProperty(
 -            name="Camera",
 -            description="Object visibility for camera rays",
 -            default=True,
 -        )
 -        cls.diffuse = BoolProperty(
 -            name="Diffuse",
 -            description="Object visibility for diffuse reflection rays",
 -            default=True,
 -        )
 -        cls.glossy = BoolProperty(
 -            name="Glossy",
 -            description="Object visibility for glossy reflection rays",
 -            default=True,
 -        )
 -        cls.transmission = BoolProperty(
 -            name="Transmission",
 -            description="Object visibility for transmission rays",
 -            default=True,
 -        )
 -        cls.shadow = BoolProperty(
 -            name="Shadow",
 -            description="Object visibility for shadow rays",
 -            default=True,
 -        )
 -        cls.scatter = BoolProperty(
 -            name="Volume Scatter",
 -            description="Object visibility for volume scatter rays",
 -            default=True,
 -        )
 -
      @classmethod
      def unregister(cls):
          del bpy.types.Object.cycles_visibility
@@@ -1094,71 -1080,70 +1094,71 @@@ class CyclesMeshSettings(bpy.types.Prop
  
  
  class CyclesObjectSettings(bpy.types.PropertyGroup):
 -    @classmethod
 -    def register(cls):
 -        bpy.types.Object.cycles = PointerProperty(
 -            name="Cycles Object Settings",
 -            description="Cycles object settings",
 -            type=cls,
 -        )
  
 -        cls.use_motion_blur = BoolProperty(
 -            name="Use Motion Blur",
 -            description="Use motion blur for this object",
 -            default=True,
 -        )
 +    use_motion_blur: BoolProperty(
 +        name="Use Motion Blur",
 +        description="Use motion blur for this object",
 +        default=True,
 +    )
  
 -        cls.use_deform_motion = BoolProperty(
 -            name="Use Deformation Motion",
 -            description="Use deformation motion blur for this object",
 -            default=True,
 -        )
 +    use_deform_motion: BoolProperty(
 +        name="Use Deformation Motion",
 +        description="Use deformation motion blur for this object",
 +        default=True,
 +    )
  
 -        cls.motion_steps = IntProperty(
 -            name="Motion Steps",
 -            description="Control accuracy of motion blur, more steps gives more memory usage (actual number of steps is 2^(steps - 1))",
 -            min=1, soft_max=8,
 -            default=1,
 -        )
 +    motion_steps: IntProperty(
 +        name="Motion Steps",
 +        description="Control accuracy of motion blur, more steps gives more memory usage (actual number of steps is 2^(steps - 1))",
 +        min=1, soft_max=8,
 +        default=1,
 +    )
  
 -        cls.use_camera_cull = BoolProperty(
 -            name="Use Camera Cull",
 -            description="Allow this object and its duplicators to be culled by camera space culling",
 -            default=False,
 -        )
 +    use_camera_cull: BoolProperty(
 +        name="Use Camera Cull",
 +        description="Allow this object and its duplicators to be culled by camera space culling",
 +        default=False,
 +    )
  
 -        cls.use_distance_cull = BoolProperty(
 -            name="Use Distance Cull",
 -            description="Allow this object and its duplicators to be culled by distance from camera",
 -            default=False,
 -        )
 +    use_distance_cull: BoolProperty(
 +        name="Use Distance Cull",
 +        description="Allow this object and its duplicators to be culled by distance from camera",
 +        default=False,
 +    )
  
 -        cls.use_adaptive_subdivision = BoolProperty(
 -            name="Use Adaptive Subdivision",
 -            description="Use adaptive render time subdivision",
 -            default=False,
 -        )
 +    use_adaptive_subdivision: BoolProperty(
 +        name="Use Adaptive Subdivision",
 +        description="Use adaptive render time subdivision",
 +        default=False,
 +    )
  
 -        cls.dicing_rate = FloatProperty(
 -            name="Dicing Scale",
 -            description="Multiplier for scene dicing rate (located in the Geometry Panel)",
 -            min=0.1, max=1000.0, soft_min=0.5,
 -            default=1.0,
 -        )
 +    dicing_rate: FloatProperty(
 +        name="Dicing Scale",
 +        description="Multiplier for scene dicing rate (located in the Subdivision panel)",
 +        min=0.1, max=1000.0, soft_min=0.5,
 +        default=1.0,
 +    )
  
 -        cls.is_shadow_catcher = BoolProperty(
 -            name="Shadow Catcher",
 -            description="Only render shadows on this object, for compositing renders into real footage",
 -            default=False,
 -        )
 +    is_shadow_catcher: BoolProperty(
 +        name="Shadow Catcher",
 +        description="Only render shadows on this object, for compositing renders into real footage",
 +        default=False,
 +    )
  
 -        cls.is_holdout = BoolProperty(
 -            name="Holdout",
 -            description="Render objects as a holdout or matte, creating a "
 -            "hole in the image with zero alpha, to fill out in "
 -            "compositing with real footange or another render",
 -            default=False,
 +    is_holdout: BoolProperty(
 +        name="Holdout",
 +        description="Render objects as a holdout or matte, creating a "
 +        "hole in the image with zero alpha, to fill out in "
 +        "compositing with real footage or another render",
 +        default=False,
 +    )
 +
 +    @classmethod
 +    def register(cls):
 +        bpy.types.Object.cycles = PointerProperty(
 +            name="Cycles Object Settings",
 +            description="Cycles object settings",
 +            type=cls,
          )
  
      @classmethod
  
  
  class CyclesCurveRenderSettings(bpy.types.PropertyGroup):
 +
 +    primitive: EnumProperty(
 +        name="Primitive",
 +        description="Type of primitive used for hair rendering",
 +        items=enum_curve_primitives,
 +        default='LINE_SEGMENTS',
 +    )
 +    shape: EnumProperty(
 +        name="Shape",
 +        description="Form of hair",
 +        items=enum_curve_shape,
 +        default='THICK',
 +    )
 +    cull_backfacing: BoolProperty(
 +        name="Cull Back-faces",
 +        description="Do not test the back-face of each strand",
 +        default=True,
 +    )
 +    use_curves: BoolProperty(
 +        name="Use Cycles Hair Rendering",
 +        description="Activate Cycles hair rendering for particle system",
 +        default=True,
 +    )
 +    resolution: IntProperty(
 +        name="Resolution",
 +        description="Resolution of generated mesh",
 +        min=3, max=64,
 +        default=3,
 +    )
 +    minimum_width: FloatProperty(
 +        name="Minimal width",
 +        description="Minimal pixel width for strands (0 - deactivated)",
 +        min=0.0, max=100.0,
 +        default=0.0,
 +        subtype='PIXEL'
 +    )
 +    maximum_width: FloatProperty(
 +        name="Maximal width",
 +        description="Maximum extension that strand radius can be increased by",
 +        min=0.0, max=100.0,
 +        default=0.1,
 +        subtype='PIXEL'
 +    )
 +    subdivisions: IntProperty(
 +        name="Subdivisions",
 +        description="Number of subdivisions used in Cardinal curve intersection (power of 2)",
 +        min=0, max=24,
 +        default=4,
 +    )
 +
      @classmethod
      def register(cls):
          bpy.types.Scene.cycles_curves = PointerProperty(
              description="Cycles hair rendering settings",
              type=cls,
          )
 -        cls.primitive = EnumProperty(
 -            name="Primitive",
 -            description="Type of primitive used for hair rendering",
 -            items=enum_curve_primitives,
 -            default='LINE_SEGMENTS',
 -        )
 -        cls.shape = EnumProperty(
 -            name="Shape",
 -            description="Form of hair",
 -            items=enum_curve_shape,
 -            default='THICK',
 -        )
 -        cls.cull_backfacing = BoolProperty(
 -            name="Cull back-faces",
 -            description="Do not test the back-face of each strand",
 -            default=True,
 -        )
 -        cls.use_curves = BoolProperty(
 -            name="Use Cycles Hair Rendering",
 -            description="Activate Cycles hair rendering for particle system",
 -            default=True,
 -        )
 -        cls.resolution = IntProperty(
 -            name="Resolution",
 -            description="Resolution of generated mesh",
 -            min=3, max=64,
 -            default=3,
 -        )
 -        cls.minimum_width = FloatProperty(
 -            name="Minimal width",
 -            description="Minimal pixel width for strands (0 - deactivated)",
 -            min=0.0, max=100.0,
 -            default=0.0,
 -        )
 -        cls.maximum_width = FloatProperty(
 -            name="Maximal width",
 -            description="Maximum extension that strand radius can be increased by",
 -            min=0.0, max=100.0,
 -            default=0.1,
 -        )
 -        cls.subdivisions = IntProperty(
 -            name="Subdivisions",
 -            description="Number of subdivisions used in Cardinal curve intersection (power of 2)",
 -            min=0, max=24,
 -            default=4,
 -        )
  
      @classmethod
      def unregister(cls):
  
  
  def update_render_passes(self, context):
 -    scene = context.scene
 -    rd = scene.render
 -    rl = rd.layers.active
 -    rl.update_render_passes()
 +    view_layer = context.view_layer
 +    view_layer.update_render_passes()
  
  
  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):
 -        del bpy.types.ParticleSettings.cycles
 +        del bpy.types.ViewLayer.cycles
  
  
  class CyclesDeviceSettings(bpy.types.PropertyGroup):
 -    @classmethod
 -    def register(cls):
 -        cls.id = StringProperty(name="ID")
 -        cls.name = StringProperty(name="Name")
 -        cls.use = BoolProperty(name="Use", default=True)
 -        cls.type = EnumProperty(name="Type", items=enum_device_type, default='CUDA')
 +    id: StringProperty(name="ID")
 +    name: StringProperty(name="Name")
 +    use: BoolProperty(name="Use", default=True)
 +    type: EnumProperty(name="Type", items=enum_device_type, default='CUDA')
  
  
  class CyclesPreferences(bpy.types.AddonPreferences):
              list.append(('OPENCL', "OpenCL", "Use OpenCL for GPU acceleration", 2))
          return list
  
 -    compute_device_type = EnumProperty(
 +    compute_device_type: EnumProperty(
          name="Compute Device Type",
          description="Device to use for computation (rendering with Cycles)",
          items=get_device_types,
      )
  
 -    devices = bpy.props.CollectionProperty(type=CyclesDeviceSettings)
 +    devices: bpy.props.CollectionProperty(type=CyclesDeviceSettings)
  
      def find_existing_device_entry(self, device):
          for device_entry in self.devices:
@@@ -1525,12 -1556,13 +1531,12 @@@ def register()
      bpy.utils.register_class(CyclesRenderSettings)
      bpy.utils.register_class(CyclesCameraSettings)
      bpy.utils.register_class(CyclesMaterialSettings)
 -    bpy.utils.register_class(CyclesLampSettings)
 +    bpy.utils.register_class(CyclesLightSettings)
      bpy.utils.register_class(CyclesWorldSettings)
      bpy.utils.register_class(CyclesVisibilitySettings)
      bpy.utils.register_class(CyclesMeshSettings)
      bpy.utils.register_class(CyclesObjectSettings)
      bpy.utils.register_class(CyclesCurveRenderSettings)
 -    bpy.utils.register_class(CyclesCurveSettings)
      bpy.utils.register_class(CyclesDeviceSettings)
      bpy.utils.register_class(CyclesPreferences)
      bpy.utils.register_class(CyclesRenderLayerSettings)
@@@ -1540,12 -1572,13 +1546,12 @@@ def unregister()
      bpy.utils.unregister_class(CyclesRenderSettings)
      bpy.utils.unregister_class(CyclesCameraSettings)
      bpy.utils.unregister_class(CyclesMaterialSettings)
 -    bpy.utils.unregister_class(CyclesLampSettings)
 +    bpy.utils.unregister_class(CyclesLightSettings)
      bpy.utils.unregister_class(CyclesWorldSettings)
      bpy.utils.unregister_class(CyclesMeshSettings)
      bpy.utils.unregister_class(CyclesObjectSettings)
      bpy.utils.unregister_class(CyclesVisibilitySettings)
      bpy.utils.unregister_class(CyclesCurveRenderSettings)
 -    bpy.utils.unregister_class(CyclesCurveSettings)
      bpy.utils.unregister_class(CyclesDeviceSettings)
      bpy.utils.unregister_class(CyclesPreferences)
      bpy.utils.unregister_class(CyclesRenderLayerSettings)
  # <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"
 +    preset_add_operator = "render.cycles_sampling_preset_add"
      COMPAT_ENGINES = {'CYCLES'}
 -    draw = Menu.draw_preset
  
  
 -class CYCLES_MT_integrator_presets(Menu):
 +class CYCLES_PT_integrator_presets(PresetMenu):
      bl_label = "Integrator Presets"
      preset_subdir = "cycles/integrator"
      preset_operator = "script.execute_preset"
 +    preset_add_operator = "render.cycles_integrator_preset_add"
      COMPAT_ENGINES = {'CYCLES'}
 -    draw = Menu.draw_preset
  
  
  class CyclesButtonsPanel:
  
      @classmethod
      def poll(cls, context):
 -        rd = context.scene.render
 -        return rd.engine in cls.COMPAT_ENGINES
 +        return context.engine in cls.COMPAT_ENGINES
 +
 +
 +class CyclesNodeButtonsPanel:
 +    bl_space_type = "NODE_EDITOR"
 +    bl_region_type = "UI"
 +    COMPAT_ENGINES = {'CYCLES'}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        return context.engine in cls.COMPAT_ENGINES
  
  
  def get_device_type(context):
 -    return context.user_preferences.addons[__package__].preferences.compute_device_type
 +    return context.preferences.addons[__package__].preferences.compute_device_type
  
  
  def use_cpu(context):
@@@ -102,7 -91,7 +98,7 @@@ def show_device_active(context)
      cscene = context.scene.cycles
      if cscene.device != 'GPU':
          return True
 -    return context.user_preferences.addons[__package__].preferences.has_active_device()
 +    return context.preferences.addons[__package__].preferences.has_active_device()
  
  
  def draw_samples_info(layout, context):
      if use_branched_path(context) or (cscene.use_square_samples and integrator == 'PATH'):
          col = layout.column(align=True)
          col.scale_y = 0.6
 -        col.label("Total Samples:")
 +        col.label(text="Total Samples:")
          col.separator()
          if integrator == 'PATH':
 -            col.label("%s AA" % aa)
 +            col.label(text="%s AA" % aa)
          else:
 -            col.label("%s AA, %s Diffuse, %s Glossy, %s Transmission" %
 +            col.label(text="%s AA, %s Diffuse, %s Glossy, %s Transmission" %
                        (aa, d * aa, g * aa, t * aa))
              col.separator()
 -            col.label("%s AO, %s Mesh Light, %s Subsurface, %s Volume" %
 +            col.label(text="%s AO, %s Mesh Light, %s Subsurface, %s Volume" %
                        (ao * aa, ml * aa, sss * aa, vol * aa))
  
  
  class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel):
      bl_label = "Sampling"
 -    bl_options = {'DEFAULT_CLOSED'}
 +
 +    def draw_header_preset(self, context):
 +        CYCLES_PT_sampling_presets.draw_panel_header(self.layout)
  
      def draw(self, context):
          layout = self.layout
          scene = context.scene
          cscene = scene.cycles
  
 -        row = layout.row(align=True)
 -        row.menu("CYCLES_MT_sampling_presets", text=bpy.types.CYCLES_MT_sampling_presets.bl_label)
 -        row.operator("render.cycles_sampling_preset_add", text="", icon="ZOOMIN")
 -        row.operator("render.cycles_sampling_preset_add", text="", icon="ZOOMOUT").remove_active = True
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        row = layout.row()
 -        sub = row.row()
 -        sub.prop(cscene, "progressive", text="")
 -        row.prop(cscene, "use_square_samples")
 +        layout.prop(cscene, "progressive")
  
 -        split = layout.split()
 +        if cscene.progressive == 'PATH' or use_branched_path(context) is False:
 +            col = layout.column(align=True)
 +            col.prop(cscene, "samples", text="Render")
 +            col.prop(cscene, "preview_samples", text="Viewport")
  
 -        col = split.column()
 -        sub = col.column(align=True)
 -        sub.label("Settings:")
 +            draw_samples_info(layout, context)
 +        else:
 +            col = layout.column(align=True)
 +            col.prop(cscene, "aa_samples", text="Render")
 +            col.prop(cscene, "preview_aa_samples", text="Viewport")
  
 -        seed_sub = sub.row(align=True)
 -        seed_sub.prop(cscene, "seed")
 -        seed_sub.prop(cscene, "use_animated_seed", text="", icon="TIME")
  
 -        sub.prop(cscene, "sample_clamp_direct")
 -        sub.prop(cscene, "sample_clamp_indirect")
 -        sub.prop(cscene, "light_sampling_threshold")
 +class CYCLES_RENDER_PT_sampling_sub_samples(CyclesButtonsPanel, Panel):
 +    bl_label = "Sub Samples"
 +    bl_parent_id = "CYCLES_RENDER_PT_sampling"
  
 -        if cscene.progressive == 'PATH' or use_branched_path(context) is False:
 -            col = split.column()
 -            sub = col.column(align=True)
 -            sub.label(text="Samples:")
 -            sub.prop(cscene, "samples", text="Render")
 -            sub.prop(cscene, "preview_samples", text="Preview")
 -        else:
 -            sub.label(text="AA Samples:")
 -            sub.prop(cscene, "aa_samples", text="Render")
 -            sub.prop(cscene, "preview_aa_samples", text="Preview")
 +    @classmethod
 +    def poll(self, context):
 +        scene = context.scene
 +        cscene = scene.cycles
 +        return cscene.progressive != 'PATH' and use_branched_path(context)
  
 -            col = split.column()
 -            sub = col.column(align=True)
 -            sub.label(text="Samples:")
 -            sub.prop(cscene, "diffuse_samples", text="Diffuse")
 -            sub.prop(cscene, "glossy_samples", text="Glossy")
 -            sub.prop(cscene, "transmission_samples", text="Transmission")
 -            sub.prop(cscene, "ao_samples", text="AO")
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        scene = context.scene
 +        cscene = scene.cycles
 +
 +        col = layout.column(align=True)
 +        col.prop(cscene, "diffuse_samples", text="Diffuse")
 +        col.prop(cscene, "glossy_samples", text="Glossy")
 +        col.prop(cscene, "transmission_samples", text="Transmission")
 +        col.prop(cscene, "ao_samples", text="AO")
 +
 +        sub = col.row(align=True)
 +        sub.active = use_sample_all_lights(context)
 +        sub.prop(cscene, "mesh_light_samples", text="Mesh Light")
 +        col.prop(cscene, "subsurface_samples", text="Subsurface")
 +        col.prop(cscene, "volume_samples", text="Volume")
 +
 +        draw_samples_info(layout, context)
  
 -            subsub = sub.row(align=True)
 -            subsub.active = use_sample_all_lights(context)
 -            subsub.prop(cscene, "mesh_light_samples", text="Mesh Light")
  
 -            sub.prop(cscene, "subsurface_samples", text="Subsurface")
 -            sub.prop(cscene, "volume_samples", text="Volume")
 +class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
 +    bl_label = "Advanced"
 +    bl_parent_id = "CYCLES_RENDER_PT_sampling"
 +    bl_options = {'DEFAULT_CLOSED'}
  
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        scene = context.scene
 +        cscene = scene.cycles
 +
 +        row = layout.row(align=True)
 +        row.prop(cscene, "seed")
 +        row.prop(cscene, "use_animated_seed", text="", icon='TIME')
 +
 +        layout.prop(cscene, "sampling_pattern", text="Pattern")
 +
 +        layout.prop(cscene, "use_square_samples")
 +
 +        layout.separator()
 +
 +        col = layout.column(align=True)
 +        col.prop(cscene, "light_sampling_threshold", text="Light Threshold")
 +
 +        if cscene.progressive != 'PATH' and use_branched_path(context):
              col = layout.column(align=True)
              col.prop(cscene, "sample_all_lights_direct")
              col.prop(cscene, "sample_all_lights_indirect")
  
 -        layout.row().prop(cscene, "sampling_pattern", text="Pattern")
 -
 -        for rl in scene.render.layers:
 -            if rl.samples > 0:
 +        for view_layer in scene.view_layers:
 +            if view_layer.samples > 0:
                  layout.separator()
                  layout.row().prop(cscene, "use_layer_samples")
                  break
  
 -        draw_samples_info(layout, context)
  
 +class CYCLES_RENDER_PT_sampling_total(CyclesButtonsPanel, Panel):
 +    bl_label = "Total Samples"
 +    bl_parent_id = "CYCLES_RENDER_PT_sampling"
  
 -class CYCLES_RENDER_PT_geometry(CyclesButtonsPanel, Panel):
 -    bl_label = "Geometry"
 +    @classmethod
 +    def poll(self, context):
 +        scene = context.scene
 +        cscene = scene.cycles
 +
 +        if cscene.use_square_samples:
 +            return True
 +
 +        return cscene.progressive != 'PATH' and use_branched_path(context)
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        cscene = context.scene.cycles
 +        integrator = cscene.progressive
 +
 +        # Calculate sample values
 +        if integrator == 'PATH':
 +            aa = cscene.samples
 +            if cscene.use_square_samples:
 +                aa = aa * aa
 +        else:
 +            aa = cscene.aa_samples
 +            d = cscene.diffuse_samples
 +            g = cscene.glossy_samples
 +            t = cscene.transmission_samples
 +            ao = cscene.ao_samples
 +            ml = cscene.mesh_light_samples
 +            sss = cscene.subsurface_samples
 +            vol = cscene.volume_samples
 +
 +            if cscene.use_square_samples:
 +                aa = aa * aa
 +                d = d * d
 +                g = g * g
 +                t = t * t
 +                ao = ao * ao
 +                ml = ml * ml
 +                sss = sss * sss
 +                vol = vol * vol
 +
 +        col = layout.column(align=True)
 +        col.scale_y = 0.6
 +        if integrator == 'PATH':
 +            col.label(text="%s AA" % aa)
 +        else:
 +            col.label(text="%s AA, %s Diffuse, %s Glossy, %s Transmission" %
 +                      (aa, d * aa, g * aa, t * aa))
 +            col.separator()
 +            col.label(text="%s AO, %s Mesh Light, %s Subsurface, %s Volume" %
 +                      (ao * aa, ml * aa, sss * aa, vol * aa))
 +
 +
 +class CYCLES_RENDER_PT_subdivision(CyclesButtonsPanel, Panel):
 +    bl_label = "Subdivision"
      bl_options = {'DEFAULT_CLOSED'}
  
 +    @classmethod
 +    def poll(self, context):
 +        return context.scene.cycles.feature_set == 'EXPERIMENTAL'
 +
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          scene = context.scene
          cscene = scene.cycles
 -        ccscene = scene.cycles_curves
  
 -        row = layout.row()
 -        row.label("Volume Sampling:")
 -        row = layout.row()
 -        row.prop(cscene, "volume_step_size")
 -        row.prop(cscene, "volume_max_steps")
 +        col = layout.column()
 +        sub = col.column(align=True)
 +        sub.prop(cscene, "dicing_rate", text="Dicing Rate Render")
 +        sub.prop(cscene, "preview_dicing_rate", text="Preview")
  
 -        layout.separator()
 +        col.separator()
  
 -        if cscene.feature_set == 'EXPERIMENTAL':
 -            layout.label("Subdivision Rate:")
 -            split = layout.split()
 +        col.prop(cscene, "offscreen_dicing_scale", text="Offscreen Scale")
 +        col.prop(cscene, "max_subdivisions")
  
 -            col = split.column()
 -            sub = col.column(align=True)
 -            sub.prop(cscene, "dicing_rate", text="Render")
 -            sub.prop(cscene, "preview_dicing_rate", text="Preview")
 +        col.prop(cscene, "dicing_camera")
  
 -            col = split.column()
 -            col.prop(cscene, "offscreen_dicing_scale", text="Offscreen Scale")
 -            col.prop(cscene, "max_subdivisions")
  
 -            layout.prop(cscene, "dicing_camera")
 +class CYCLES_RENDER_PT_hair(CyclesButtonsPanel, Panel):
 +    bl_label = "Hair"
 +    bl_options = {'DEFAULT_CLOSED'}
  
 -            layout.separator()
 +    def draw_header(self, context):
 +        layout = self.layout
 +        scene = context.scene
 +        ccscene = scene.cycles_curves
  
 -        layout.label("Hair:")
 -        layout.prop(ccscene, "use_curves", text="Use Hair")
 -        col = layout.column()
 -        col.active = ccscene.use_curves
 +        layout.prop(ccscene, "use_curves", text="")
  
 -        col.prop(ccscene, "primitive", text="Primitive")
 -        col.prop(ccscene, "shape", text="Shape")
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 +        scene = context.scene
 +        ccscene = scene.cycles_curves
 +
 +        layout.active = ccscene.use_curves
 +
 +        col = layout.column()
 +        col.prop(ccscene, "minimum_width", text="Min Pixels")
 +        col.prop(ccscene, "maximum_width", text="Max Extension")
 +        col.prop(ccscene, "shape", text="Shape")
          if not (ccscene.primitive in {'CURVE_SEGMENTS', 'LINE_SEGMENTS'} and ccscene.shape == 'RIBBONS'):
              col.prop(ccscene, "cull_backfacing", text="Cull back-faces")
 +        col.prop(ccscene, "primitive", text="Primitive")
  
          if ccscene.primitive == 'TRIANGLES' and ccscene.shape == 'THICK':
              col.prop(ccscene, "resolution", text="Resolution")
          elif ccscene.primitive == 'CURVE_SEGMENTS':
              col.prop(ccscene, "subdivisions", text="Curve subdivisions")
  
 -        row = col.row()
 -        row.prop(ccscene, "minimum_width", text="Min Pixels")
 -        row.prop(ccscene, "maximum_width", text="Max Extension")
 +
 +class CYCLES_RENDER_PT_volumes(CyclesButtonsPanel, Panel):
 +    bl_label = "Volumes"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        scene = context.scene
 +        cscene = scene.cycles
 +
 +        col = layout.column()
 +        col.prop(cscene, "volume_step_size", text="Step Size")
 +        col.prop(cscene, "volume_max_steps", text="Max Steps")
  
  
  class CYCLES_RENDER_PT_light_paths(CyclesButtonsPanel, Panel):
      bl_label = "Light Paths"
      bl_options = {'DEFAULT_CLOSED'}
  
 +    def draw_header_preset(self, context):
 +        CYCLES_PT_integrator_presets.draw_panel_header(self.layout)
 +
 +    def draw(self, context):
 +        pass
 +
 +
 +class CYCLES_RENDER_PT_light_paths_max_bounces(CyclesButtonsPanel, Panel):
 +    bl_label = "Max Bounces"
 +    bl_parent_id = "CYCLES_RENDER_PT_light_paths"
 +
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          scene = context.scene
          cscene = scene.cycles
  
 -        row = layout.row(align=True)
 -        row.menu("CYCLES_MT_integrator_presets", text=bpy.types.CYCLES_MT_integrator_presets.bl_label)
 -        row.operator("render.cycles_integrator_preset_add", text="", icon="ZOOMIN")
 -        row.operator("render.cycles_integrator_preset_add", text="", icon="ZOOMOUT").remove_active = True
 +        col = layout.column(align=True)
 +        col.prop(cscene, "max_bounces", text="Total")
  
 -        split = layout.split()
 +        col = layout.column(align=True)
 +        col.prop(cscene, "diffuse_bounces", text="Diffuse")
 +        col.prop(cscene, "glossy_bounces", text="Glossy")
 +        col.prop(cscene, "transparent_max_bounces", text="Transparency")
 +        col.prop(cscene, "transmission_bounces", text="Transmission")
 +        col.prop(cscene, "volume_bounces", text="Volume")
  
 -        col = split.column()
  
 -        sub = col.column(align=True)
 -        sub.label("Transparency:")
 -        sub.prop(cscene, "transparent_max_bounces", text="Max")
 +class CYCLES_RENDER_PT_light_paths_clamping(CyclesButtonsPanel, Panel):
 +    bl_label = "Clamping"
 +    bl_parent_id = "CYCLES_RENDER_PT_light_paths"
  
 -        col.separator()
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        col.prop(cscene, "caustics_reflective")
 -        col.prop(cscene, "caustics_refractive")
 -        col.prop(cscene, "blur_glossy")
 +        scene = context.scene
 +        cscene = scene.cycles
  
 -        col = split.column()
 +        col = layout.column(align=True)
 +        col.prop(cscene, "sample_clamp_direct", text="Direct Light")
 +        col.prop(cscene, "sample_clamp_indirect", text="Indirect Light")
  
 -        sub = col.column(align=True)
 -        sub.label(text="Bounces:")
 -        sub.prop(cscene, "max_bounces", text="Max")
  
 -        sub = col.column(align=True)
 -        sub.prop(cscene, "diffuse_bounces", text="Diffuse")
 -        sub.prop(cscene, "glossy_bounces", text="Glossy")
 -        sub.prop(cscene, "transmission_bounces", text="Transmission")
 -        sub.prop(cscene, "volume_bounces", text="Volume")
 +class CYCLES_RENDER_PT_light_paths_caustics(CyclesButtonsPanel, Panel):
 +    bl_label = "Caustics"
 +    bl_parent_id = "CYCLES_RENDER_PT_light_paths"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        scene = context.scene
 +        cscene = scene.cycles
 +
 +        col = layout.column()
 +        col.prop(cscene, "blur_glossy")
 +        col.prop(cscene, "caustics_reflective")
 +        col.prop(cscene, "caustics_refractive")
  
  
  class CYCLES_RENDER_PT_motion_blur(CyclesButtonsPanel, Panel):
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          scene = context.scene
          cscene = scene.cycles
          col = layout.column()
          col.prop(cscene, "motion_blur_position", text="Position")
          col.prop(rd, "motion_blur_shutter")
 +        col.separator()
 +        col.prop(cscene, "rolling_shutter_type", text="Rolling Shutter")
 +        sub = col.column()
 +        sub.active = cscene.rolling_shutter_type != 'NONE'
 +        sub.prop(cscene, "rolling_shutter_duration")
 +
 +
 +class CYCLES_RENDER_PT_motion_blur_curve(CyclesButtonsPanel, Panel):
 +    bl_label = "Shutter Curve"
 +    bl_parent_id = "CYCLES_RENDER_PT_motion_blur"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        scene = context.scene
 +        rd = scene.render
 +        layout.active = rd.use_motion_blur
  
          col = layout.column()
 -        col.label("Shutter curve:")
 +
          col.template_curve_mapping(rd, "motion_blur_shutter_curve")
  
          col = layout.column(align=True)
          row.operator("render.shutter_curve_preset", icon='LINCURVE', text="").shape = 'LINE'
          row.operator("render.shutter_curve_preset", icon='NOCURVE', text="").shape = 'MAX'
  
 -        col = layout.column()
 -        col.prop(cscene, "rolling_shutter_type")
 -        row = col.row()
 -        row.active = cscene.rolling_shutter_type != 'NONE'
 -        row.prop(cscene, "rolling_shutter_duration")
 -
  
  class CYCLES_RENDER_PT_film(CyclesButtonsPanel, Panel):
      bl_label = "Film"
 +    bl_options = {'DEFAULT_CLOSED'}
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +        scene = context.scene
 +        cscene = scene.cycles
 +
 +        col = layout.column()
 +        col.prop(cscene, "film_exposure")
 +
 +
 +class CYCLES_RENDER_PT_film_transparency(CyclesButtonsPanel, Panel):
 +    bl_label = "Transparency"
 +    bl_parent_id = "CYCLES_RENDER_PT_film"
 +
 +    def draw_header(self, context):
 +        layout = self.layout
  
          scene = context.scene
          cscene = scene.cycles
  
 -        split = layout.split()
 +        layout.prop(cscene, "film_transparent", text="")
  
 -        col = split.column()
 -        col.prop(cscene, "film_exposure")
 -        col.separator()
 -        sub = col.column(align=True)
 -        sub.prop(cscene, "pixel_filter_type", text="")
 -        if cscene.pixel_filter_type != 'BOX':
 -            sub.prop(cscene, "filter_width", text="Width")
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +        scene = context.scene
 +        cscene = scene.cycles
  
 -        col = split.column()
 -        col.prop(cscene, "film_transparent")
 -        sub = col.row()
 -        sub.prop(cscene, "film_transparent_glass", text="Transparent Glass")
 -        sub.active = cscene.film_transparent
 -        sub = col.row()
 -        sub.prop(cscene, "film_transparent_roughness", text="Roughness Threshold")
 +        layout.active = cscene.film_transparent
 +
 +        col = layout.column()
 +        col.prop(cscene, "film_transparent_glass", text="Transparent Glass")
 +
 +        sub = col.column()
          sub.active = cscene.film_transparent and cscene.film_transparent_glass
 +        sub.prop(cscene, "film_transparent_roughness", text="Roughness Threshold")
 +
 +
 +class CYCLES_RENDER_PT_film_pixel_filter(CyclesButtonsPanel, Panel):
 +    bl_label = "Pixel Filter"
 +    bl_parent_id = "CYCLES_RENDER_PT_film"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +        scene = context.scene
 +        cscene = scene.cycles
 +
 +        col = layout.column()
 +        col.prop(cscene, "pixel_filter_type", text="Type")
 +        if cscene.pixel_filter_type != 'BOX':
 +            col.prop(cscene, "filter_width", text="Width")
  
  
  class CYCLES_RENDER_PT_performance(CyclesButtonsPanel, Panel):
      bl_label = "Performance"
      bl_options = {'DEFAULT_CLOSED'}
  
 +    def draw(self, context):
 +        pass
 +
 +
 +class CYCLES_RENDER_PT_performance_threads(CyclesButtonsPanel, Panel):
 +    bl_label = "Threads"
 +    bl_parent_id = "CYCLES_RENDER_PT_performance"
 +
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          scene = context.scene
          rd = scene.render
 -        cscene = scene.cycles
 -
 -        split = layout.split()
  
 -        col = split.column(align=True)
 +        col = layout.column()
  
 -        col.label(text="Threads:")
 -        col.row(align=True).prop(rd, "threads_mode", expand=True)
 +        col.prop(rd, "threads_mode")
          sub = col.column(align=True)
          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)
              row.prop(cscene, "use_bvh_embree")
 -        row = col.row()
          col.prop(cscene, "debug_use_spatial_splits")
 -        row = col.row()
 -        row.active = not cscene.use_bvh_embree or not _cycles.with_embree
 -        row.prop(cscene, "debug_use_hair_bvh")
 +        sub = col.column()
 +        sub.active = not cscene.use_bvh_embree or not _cycles.with_embree
 +        sub.prop(cscene, "debug_use_hair_bvh")
 +        sub = col.column()
 +        sub.active = not cscene.debug_use_spatial_splits and not cscene.use_bvh_embree
 +        sub.prop(cscene, "debug_bvh_time_steps")
 +
  
 -        row = col.row()
 -        row.active = not cscene.debug_use_spatial_splits and not cscene.use_bvh_embree
 -        row.prop(cscene, "debug_bvh_time_steps")
 +class CYCLES_RENDER_PT_performance_final_render(CyclesButtonsPanel, Panel):
 +    bl_label = "Final Render"
 +    bl_parent_id = "CYCLES_RENDER_PT_performance"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        scene = context.scene
 +        rd = scene.render
  
          col = layout.column()
 -        col.label(text="Viewport Resolution:")
 -        split = col.split()
 -        split.prop(rd, "preview_pixel_size", text="")
 -        split.prop(cscene, "preview_start_resolution")
  
 +        col.prop(rd, "use_save_buffers")
 +        col.prop(rd, "use_persistent_data", text="Persistent Images")
  
 -class CYCLES_RENDER_PT_layer_options(CyclesButtonsPanel, Panel):
 -    bl_label = "Layer"
 -    bl_context = "render_layer"
 +
 +class CYCLES_RENDER_PT_performance_viewport(CyclesButtonsPanel, Panel):
 +    bl_label = "Viewport"
 +    bl_parent_id = "CYCLES_RENDER_PT_performance"
  
      def draw(self, context):
          layout = self.layout
 -        with_freestyle = bpy.app.build_options.freestyle
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          scene = context.scene
          rd = scene.render
 -        rl = rd.layers.active
 +        cscene = scene.cycles
  
 -        split = layout.split()
 +        col = layout.column()
 +        col.prop(rd, "preview_pixel_size", text="Pixel Size")
 +        col.prop(cscene, "preview_start_resolution", text="Start Pixels")
  
 -        col = split.column()
 -        col.prop(scene, "layers", text="Scene")
 -        col.prop(rl, "layers_exclude", text="Exclude")
  
 -        col = split.column()
 -        col.prop(rl, "layers", text="Layer")
 -        col.prop(rl, "layers_zmask", text="Mask Layer")
 +class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel):
 +    bl_label = "Filter"
 +    bl_options = {'DEFAULT_CLOSED'}
 +    bl_context = "view_layer"
  
 -        split = layout.split()
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        col = split.column()
 -        col.label(text="Material:")
 -        col.prop(rl, "material_override", text="")
 -        col.separator()
 -        col.prop(rl, "samples")
 +        with_freestyle = bpy.app.build_options.freestyle
  
 -        col = split.column()
 -        col.prop(rl, "use_sky", "Use Environment")
 -        col.prop(rl, "use_ao", "Use AO")
 -        col.prop(rl, "use_solid", "Use Surfaces")
 -        col.prop(rl, "use_strand", "Use Hair")
 +        scene = context.scene
 +        rd = scene.render
 +        view_layer = context.view_layer
 +
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
 +
 +        col = flow.column()
 +        col.prop(view_layer, "use_sky", text="Environment")
 +        col = flow.column()
 +        col.prop(view_layer, "use_ao", text="Ambient Occlusion")
 +        col = flow.column()
 +        col.prop(view_layer, "use_solid", text="Surfaces")
 +        col = flow.column()
 +        col.prop(view_layer, "use_strand", text="Hair")
          if with_freestyle:
 -            row = col.row()
 -            row.prop(rl, "use_freestyle", "Use Freestyle")
 -            row.active = rd.use_freestyle
 +            col = flow.column()
 +            col.prop(view_layer, "use_freestyle", text="Freestyle")
 +            col.active = rd.use_freestyle
 +
 +
 +class CYCLES_RENDER_PT_override(CyclesButtonsPanel, Panel):
 +    bl_label = "Override"
 +    bl_options = {'DEFAULT_CLOSED'}
 +    bl_context = "view_layer"
  
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -class CYCLES_RENDER_PT_layer_passes(CyclesButtonsPanel, Panel):
 +        view_layer = context.view_layer
 +
 +        layout.prop(view_layer, "material_override")
 +        layout.prop(view_layer, "samples")
 +
 +
 +class CYCLES_RENDER_PT_passes(CyclesButtonsPanel, Panel):
      bl_label = "Passes"
 -    bl_context = "render_layer"
 +    bl_context = "view_layer"
      bl_options = {'DEFAULT_CLOSED'}
  
 +    def draw(self, context):
 +        pass
 +
 +
 +class CYCLES_RENDER_PT_passes_data(CyclesButtonsPanel, Panel):
 +    bl_label = "Data"
 +    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
  
          scene = context.scene
          rd = scene.render
 -        rl = rd.layers.active
 -        crl = rl.cycles
 +        view_layer = context.view_layer
 +        cycles_view_layer = view_layer.cycles
 +
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
 +        col = flow.column()
 +        col.prop(view_layer, "use_pass_combined")
 +        col = flow.column()
 +        col.prop(view_layer, "use_pass_z")
 +        col = flow.column()
 +        col.prop(view_layer, "use_pass_mist")
 +        col = flow.column()
 +        col.prop(view_layer, "use_pass_normal")
 +        col = flow.column()
 +        col.prop(view_layer, "use_pass_vector")
 +        col.active = not rd.use_motion_blur
 +        col = flow.column()
 +        col.prop(view_layer, "use_pass_uv")
 +        col = flow.column()
 +        col.prop(view_layer, "use_pass_object_index")
 +        col = flow.column()
 +        col.prop(view_layer, "use_pass_material_index")
  
 -        split = layout.split()
 +        layout.separator()
  
 -        col = split.column()
 -        col.prop(rl, "use_pass_combined")
 -        col.prop(rl, "use_pass_z")
 -        col.prop(rl, "use_pass_mist")
 -        col.prop(rl, "use_pass_normal")
 -        row = col.row()
 -        row.prop(rl, "use_pass_vector")
 -        row.active = not rd.use_motion_blur
 -        col.prop(rl, "use_pass_uv")
 -        col.prop(rl, "use_pass_object_index")
 -        col.prop(rl, "use_pass_material_index")
 -        col.separator()
 -        col.prop(rl, "use_pass_shadow")
 -        col.prop(rl, "use_pass_ambient_occlusion")
 -        col.separator()
 -        col.prop(crl, "denoising_store_passes", text="Denoising Data")
 -        col.separator()
 -        col.prop(rl, "pass_alpha_threshold")
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
 +        col = flow.column()
 +        col.prop(cycles_view_layer, "denoising_store_passes", text="Denoising Data")
 +        col = flow.column()
 +        col.prop(cycles_view_layer, "pass_debug_render_time", text="Render Time")
  
 -        col = split.column()
 -        col.label(text="Diffuse:")
 -        row = col.row(align=True)
 -        row.prop(rl, "use_pass_diffuse_direct", text="Direct", toggle=True)
 -        row.prop(rl, "use_pass_diffuse_indirect", text="Indirect", toggle=True)
 -        row.prop(rl, "use_pass_diffuse_color", text="Color", toggle=True)
 -        col.label(text="Glossy:")
 -        row = col.row(align=True)
 -        row.prop(rl, "use_pass_glossy_direct", text="Direct", toggle=True)
 -        row.prop(rl, "use_pass_glossy_indirect", text="Indirect", toggle=True)
 -        row.prop(rl, "use_pass_glossy_color", text="Color", toggle=True)
 -        col.label(text="Transmission:")
 -        row = col.row(align=True)
 -        row.prop(rl, "use_pass_transmission_direct", text="Direct", toggle=True)
 -        row.prop(rl, "use_pass_transmission_indirect", text="Indirect", toggle=True)
 -        row.prop(rl, "use_pass_transmission_color", text="Color", toggle=True)
 -        col.label(text="Subsurface:")
 -        row = col.row(align=True)
 -        row.prop(rl, "use_pass_subsurface_direct", text="Direct", toggle=True)
 -        row.prop(rl, "use_pass_subsurface_indirect", text="Indirect", toggle=True)
 -        row.prop(rl, "use_pass_subsurface_color", text="Color", toggle=True)
 -        col.label(text="Volume:")
 -        row = col.row(align=True)
 -        row.prop(crl, "use_pass_volume_direct", text="Direct", toggle=True)
 -        row.prop(crl, "use_pass_volume_indirect", text="Indirect", toggle=True)
 +        layout.separator()
  
 -        col.separator()
 -        col.prop(rl, "use_pass_emit", text="Emission")
 -        col.prop(rl, "use_pass_environment")
 +        layout.prop(view_layer, "pass_alpha_threshold")
  
 -        col = layout.column()
 -        col.prop(crl, "pass_debug_render_time")
 -        if _cycles.with_cycles_debug:
 -            col.prop(crl, "pass_debug_bvh_traversed_nodes")
 -            col.prop(crl, "pass_debug_bvh_traversed_instances")
 -            col.prop(crl, "pass_debug_bvh_intersections")
 -            col.prop(crl, "pass_debug_ray_bounces")
 -
 -        crl = rl.cycles
 -        layout.label("Cryptomatte:")
 -        row = layout.row(align=True)
 -        row.prop(crl, "use_pass_crypto_object", text="Object", toggle=True)
 -        row.prop(crl, "use_pass_crypto_material", text="Material", toggle=True)
 -        row.prop(crl, "use_pass_crypto_asset", text="Asset", toggle=True)
 -        row = layout.row(align=True)
 -        row.prop(crl, "pass_crypto_depth")
 -        row = layout.row(align=True)
 -        row.active = use_cpu(context)
 -        row.prop(crl, "pass_crypto_accurate", text="Accurate Mode")
  
 -class CYCLES_RENDER_PT_views(CyclesButtonsPanel, Panel):
 -    bl_label = "Views"
 -    bl_context = "render_layer"
 -    bl_options = {'DEFAULT_CLOSED'}
 +class CYCLES_RENDER_PT_passes_light(CyclesButtonsPanel, Panel):
 +    bl_label = "Light"
 +    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
 +
 +        view_layer = context.view_layer
 +        cycles_view_layer = view_layer.cycles
 +
 +        split = layout.split(factor=0.35)
 +        split.use_property_split = False
 +        split.label(text="Diffuse")
 +        row = split.row(align=True)
 +        row.prop(view_layer, "use_pass_diffuse_direct", text="Direct", toggle=True)
 +        row.prop(view_layer, "use_pass_diffuse_indirect", text="Indirect", toggle=True)
 +        row.prop(view_layer, "use_pass_diffuse_color", text="Color", toggle=True)
 +
 +        split = layout.split(factor=0.35)
 +        split.use_property_split = False
 +        split.label(text="Glossy")
 +        row = split.row(align=True)
 +        row.prop(view_layer, "use_pass_glossy_direct", text="Direct", toggle=True)
 +        row.prop(view_layer, "use_pass_glossy_indirect", text="Indirect", toggle=True)
 +        row.prop(view_layer, "use_pass_glossy_color", text="Color", toggle=True)
 +
 +        split = layout.split(factor=0.35)
 +        split.use_property_split = False
 +        split.label(text="Transmission")
 +        row = split.row(align=True)
 +        row.prop(view_layer, "use_pass_transmission_direct", text="Direct", toggle=True)
 +        row.prop(view_layer, "use_pass_transmission_indirect", text="Indirect", toggle=True)
 +        row.prop(view_layer, "use_pass_transmission_color", text="Color", toggle=True)
 +
 +        split = layout.split(factor=0.35)
 +        split.use_property_split = False
 +        split.label(text="Subsurface")
 +        row = split.row(align=True)
 +        row.prop(view_layer, "use_pass_subsurface_direct", text="Direct", toggle=True)
 +        row.prop(view_layer, "use_pass_subsurface_indirect", text="Indirect", toggle=True)
 +        row.prop(view_layer, "use_pass_subsurface_color", text="Color", toggle=True)
 +
 +        split = layout.split(factor=0.35)
 +        split.use_property_split = False
 +        split.label(text="Volume")
 +        row = split.row(align=True)
 +        row.prop(cycles_view_layer, "use_pass_volume_direct", text="Direct", toggle=True)
 +        row.prop(cycles_view_layer, "use_pass_volume_indirect", text="Indirect", toggle=True)
 +
 +        col = layout.column(align=True)
 +        col.prop(view_layer, "use_pass_emit", text="Emission")
 +        col.prop(view_layer, "use_pass_environment")
 +        col.prop(view_layer, "use_pass_shadow")
 +        col.prop(view_layer, "use_pass_ambient_occlusion", text="Ambient Occlusion")
  
 -    def draw_header(self, context):
 -        rd = context.scene.render
 -        self.layout.prop(rd, "use_multiview", text="")
 +
 +class CYCLES_RENDER_PT_passes_crypto(CyclesButtonsPanel, Panel):
 +    bl_label = "Cryptomatte"
 +    bl_context = "view_layer"
 +    bl_parent_id = "CYCLES_RENDER_PT_passes"
  
      def draw(self, context):
 +        import _cycles
 +
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        scene = context.scene
 -        rd = scene.render
 -        rv = rd.views.active
 +        cycles_view_layer = context.view_layer.cycles
  
 -        layout.active = rd.use_multiview
 -        basic_stereo = (rd.views_format == 'STEREO_3D')
 +        row = layout.row(align=True)
 +        row.use_property_split = False
 +        row.prop(cycles_view_layer, "use_pass_crypto_object", text="Object", toggle=True)
 +        row.prop(cycles_view_layer, "use_pass_crypto_material", text="Material", toggle=True)
 +        row.prop(cycles_view_layer, "use_pass_crypto_asset", text="Asset", toggle=True)
  
 -        row = layout.row()
 -        row.prop(rd, "views_format", expand=True)
 +        layout.prop(cycles_view_layer, "pass_crypto_depth", text="Levels")
  
 -        if basic_stereo:
 -            row = layout.row()
 -            row.template_list("RENDERLAYER_UL_renderviews", "name", rd, "stereo_views", rd.views, "active_index", rows=2)
 +        row = layout.row(align=True)
 +        row.active = use_cpu(context)
 +        row.prop(cycles_view_layer, "pass_crypto_accurate", text="Accurate Mode")
  
 -            row = layout.row()
 -            row.label(text="File Suffix:")
 -            row.prop(rv, "file_suffix", text="")
  
 -        else:
 -            row = layout.row()
 -            row.template_list("RENDERLAYER_UL_renderviews", "name", rd, "views", rd.views, "active_index", rows=2)
 +class CYCLES_RENDER_PT_passes_debug(CyclesButtonsPanel, Panel):
 +    bl_label = "Debug"
 +    bl_context = "view_layer"
 +    bl_parent_id = "CYCLES_RENDER_PT_passes"
  
 -            col = row.column(align=True)
 -            col.operator("scene.render_view_add", icon='ZOOMIN', text="")
 -            col.operator("scene.render_view_remove", icon='ZOOMOUT', text="")
 +    @classmethod
 +    def poll(cls, context):
 +        import _cycles
 +        return _cycles.with_cycles_debug
  
 -            row = layout.row()
 -            row.label(text="Camera Suffix:")
 -            row.prop(rv, "camera_suffix", text="")
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        cycles_view_layer = context.view_layer.cycles
 +
 +        layout.prop(cycles_view_layer, "pass_debug_bvh_traversed_nodes")
 +        layout.prop(cycles_view_layer, "pass_debug_bvh_traversed_instances")
 +        layout.prop(cycles_view_layer, "pass_debug_bvh_intersections")
 +        layout.prop(cycles_view_layer, "pass_debug_ray_bounces")
  
  
  class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
      bl_label = "Denoising"
 -    bl_context = "render_layer"
 +    bl_context = "view_layer"
      bl_options = {'DEFAULT_CLOSED'}
  
      def draw_header(self, context):
 -        rd = context.scene.render
 -        rl = rd.layers.active
 -        crl = rl.cycles
 -        cscene = context.scene.cycles
 +        scene = context.scene
 +        view_layer = context.view_layer
 +        cycles_view_layer = view_layer.cycles
          layout = self.layout
  
 -        layout.prop(crl, "use_denoising", text="")
 +        layout.prop(cycles_view_layer, "use_denoising", text="")
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          scene = context.scene
 -        cscene = scene.cycles
 -        rd = scene.render
 -        rl = rd.layers.active
 -        crl = rl.cycles
 +        view_layer = context.view_layer
 +        cycles_view_layer = view_layer.cycles
  
          split = layout.split()
 -        split.active = crl.use_denoising
 +        split.active = cycles_view_layer.use_denoising
 +
 +        layout = layout.column(align=True)
 +        layout.prop(cycles_view_layer, "denoising_radius", text="Radius")
 +        layout.prop(cycles_view_layer, "denoising_strength", slider=True, text="Strength")
 +        layout.prop(cycles_view_layer, "denoising_feature_strength", slider=True, text="Feature Strength")
 +        layout.prop(cycles_view_layer, "denoising_relative_pca")
 +
 +        layout.separator()
 +
 +        split = layout.split(factor=0.5)
 +        split.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes
  
          col = split.column()
 -        sub = col.column(align=True)
 -        sub.prop(crl, "denoising_radius", text="Radius")
 -        sub.prop(crl, "denoising_strength", slider=True, text="Strength")
 +        col.alignment = 'RIGHT'
 +        col.label(text="Diffuse")
 +
 +        row = split.row(align=True)
 +        row.use_property_split = False
 +        row.prop(cycles_view_layer, "denoising_diffuse_direct", text="Direct", toggle=True)
 +        row.prop(cycles_view_layer, "denoising_diffuse_indirect", text="Indirect", toggle=True)
 +
 +        split = layout.split(factor=0.5)
 +        split.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes
  
          col = split.column()
 -        sub = col.column(align=True)
 -        sub.prop(crl, "denoising_feature_strength", slider=True, text="Feature Strength")
 -        sub.prop(crl, "denoising_relative_pca")
 +        col.alignment = 'RIGHT'
 +        col.label(text="Glossy")
  
 -        layout.separator()
 +        row = split.row(align=True)
 +        row.use_property_split = False
 +        row.prop(cycles_view_layer, "denoising_glossy_direct", text="Direct", toggle=True)
 +        row.prop(cycles_view_layer, "denoising_glossy_indirect", text="Indirect", toggle=True)
  
 -        row = layout.row()
 -        row.active = crl.use_denoising or crl.denoising_store_passes
 -        row.label(text="Diffuse:")
 -        sub = row.row(align=True)
 -        sub.prop(crl, "denoising_diffuse_direct", text="Direct", toggle=True)
 -        sub.prop(crl, "denoising_diffuse_indirect", text="Indirect", toggle=True)
 +        split = layout.split(factor=0.5)
 +        split.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes
  
 -        row = layout.row()
 -        row.active = crl.use_denoising or crl.denoising_store_passes
 -        row.label(text="Glossy:")
 -        sub = row.row(align=True)
 -        sub.prop(crl, "denoising_glossy_direct", text="Direct", toggle=True)
 -        sub.prop(crl, "denoising_glossy_indirect", text="Indirect", toggle=True)
 +        col = split.column()
 +        col.alignment = 'RIGHT'
 +        col.label(text="Transmission")
  
 -        row = layout.row()
 -        row.active = crl.use_denoising or crl.denoising_store_passes
 -        row.label(text="Transmission:")
 -        sub = row.row(align=True)
 -        sub.prop(crl, "denoising_transmission_direct", text="Direct", toggle=True)
 -        sub.prop(crl, "denoising_transmission_indirect", text="Indirect", toggle=True)
 +        row = split.row(align=True)
 +        row.use_property_split = False
 +        row.prop(cycles_view_layer, "denoising_transmission_direct", text="Direct", toggle=True)
 +        row.prop(cycles_view_layer, "denoising_transmission_indirect", text="Indirect", toggle=True)
  
 -        row = layout.row()
 -        row.active = crl.use_denoising or crl.denoising_store_passes
 -        row.label(text="Subsurface:")
 -        sub = row.row(align=True)
 -        sub.prop(crl, "denoising_subsurface_direct", text="Direct", toggle=True)
 -        sub.prop(crl, "denoising_subsurface_indirect", text="Indirect", toggle=True)
 +        split = layout.split(factor=0.5)
 +        split.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes
 +
 +        col = split.column()
 +        col.alignment = 'RIGHT'
 +        col.label(text="Subsurface")
 +
 +        row = split.row(align=True)
 +        row.use_property_split = False
 +        row.prop(cycles_view_layer, "denoising_subsurface_direct", text="Direct", toggle=True)
 +        row.prop(cycles_view_layer, "denoising_subsurface_indirect", text="Indirect", toggle=True)
  
  
  class CYCLES_PT_post_processing(CyclesButtonsPanel, Panel):
      bl_label = "Post Processing"
      bl_options = {'DEFAULT_CLOSED'}
 +    bl_context = "output"
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          rd = context.scene.render
  
 -        split = layout.split()
 -
 -        col = split.column()
 +        col = layout.column(align=True)
          col.prop(rd, "use_compositing")
          col.prop(rd, "use_sequencer")
  
 -        col = split.column()
 -        col.prop(rd, "dither_intensity", text="Dither", slider=True)
 +        layout.prop(rd, "dither_intensity", text="Dither", slider=True)
  
  
  class CYCLES_CAMERA_PT_dof(CyclesButtonsPanel, Panel):
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
  
          cam = context.camera
 -        ccam = cam.cycles
 -        dof_options = cam.gpu_dof
  
          split = layout.split()
  
          col = split.column()
 -        col.label("Focus:")
 -        col.prop(cam, "dof_object", text="")
 +        col.prop(cam, "dof_object", text="Focus Object")
  
          sub = col.row()
          sub.active = cam.dof_object is None
          sub.prop(cam, "dof_distance", text="Distance")
  
 -        hq_support = dof_options.is_hq_supported
 -        sub = col.column(align=True)
 -        sub.label("Viewport:")
 -        subhq = sub.column()
 -        subhq.active = hq_support
 -        subhq.prop(dof_options, "use_high_quality")
 -        sub.prop(dof_options, "fstop")
 -        if dof_options.use_high_quality and hq_support:
 -            sub.prop(dof_options, "blades")
  
 -        col = split.column()
 +class CYCLES_CAMERA_PT_dof_aperture(CyclesButtonsPanel, Panel):
 +    bl_label = "Aperture"
 +    bl_parent_id = "CYCLES_CAMERA_PT_dof"
  
 -        col.label("Aperture:")
 -        sub = col.column(align=True)
 -        sub.prop(ccam, "aperture_type", text="")
 +    @classmethod
 +    def poll(cls, context):
 +        return context.camera and CyclesButtonsPanel.poll(context)
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
 +
 +        cam = context.camera
 +        ccam = cam.cycles
 +
 +        col = flow.column()
 +        col.prop(ccam, "aperture_type")
          if ccam.aperture_type == 'RADIUS':
 -            sub.prop(ccam, "aperture_size", text="Size")
 +            col.prop(ccam, "aperture_size", text="Size")
          elif ccam.aperture_type == 'FSTOP':
 -            sub.prop(ccam, "aperture_fstop", text="Number")
 +            col.prop(ccam, "aperture_fstop", text="Number")
 +        col.separator()
  
 -        sub = col.column(align=True)
 -        sub.prop(ccam, "aperture_blades", text="Blades")
 -        sub.prop(ccam, "aperture_rotation", text="Rotation")
 -        sub.prop(ccam, "aperture_ratio", text="Ratio")
 +        col = flow.column()
 +        col.prop(ccam, "aperture_blades", text="Blades")
 +        col.prop(ccam, "aperture_rotation", text="Rotation")
 +        col.prop(ccam, "aperture_ratio", text="Ratio")
 +
 +
 +class CYCLES_CAMERA_PT_dof_viewport(CyclesButtonsPanel, Panel):
 +    bl_label = "Viewport"
 +    bl_parent_id = "CYCLES_CAMERA_PT_dof"
 +
 +    @classmethod
 +    def poll(cls, context):
 +        return context.camera and CyclesButtonsPanel.poll(context)
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
 +
 +        cam = context.camera
 +        dof_options = cam.gpu_dof
 +
 +        sub = flow.column(align=True)
 +        sub.prop(dof_options, "fstop")
 +        sub.prop(dof_options, "blades")
  
  
  class CYCLES_PT_context_material(CyclesButtonsPanel, Panel):
  
      @classmethod
      def poll(cls, context):
 -        return (context.material or context.object) and CyclesButtonsPanel.poll(context)
 +        if context.active_object and context.active_object.type == 'GPENCIL':
 +            return False
 +        else:
 +            return (context.material or context.object) and CyclesButtonsPanel.poll(context)
  
      def draw(self, context):
          layout = self.layout
              row.template_list("MATERIAL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows)
  
              col = row.column(align=True)
 -            col.operator("object.material_slot_add", icon='ZOOMIN', text="")
 -            col.operator("object.material_slot_remove", icon='ZOOMOUT', text="")
 +            col.operator("object.material_slot_add", icon='ADD', text="")
 +            col.operator("object.material_slot_remove", icon='REMOVE', text="")
  
              col.menu("MATERIAL_MT_specials", icon='DOWNARROW_HLT', text="")
  
                  row.operator("object.material_slot_select", text="Select")
                  row.operator("object.material_slot_deselect", text="Deselect")
  
 -        split = layout.split(percentage=0.65)
 +        split = layout.split(factor=0.65)
  
          if ob:
              split.template_ID(ob, "active_material", new="material.new")
@@@ -1157,7 -827,7 +1155,7 @@@ class CYCLES_OBJECT_PT_motion_blur(Cycl
          if CyclesButtonsPanel.poll(context) and ob:
              if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA'}:
                  return True
 -            if ob.dupli_type == 'GROUP' and ob.dupli_group:
 +            if ob.instance_type == 'COLLECTION' and ob.instance_collection:
                  return True
              # TODO(sergey): More duplicator types here?
          return False
@@@ -1201,103 -871,70 +1199,82 @@@ class CYCLES_OBJECT_PT_cycles_settings(
      def poll(cls, context):
          ob = context.object
          return (CyclesButtonsPanel.poll(context) and
 -                ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LAMP'}) or
 -                        (ob.dupli_type == 'GROUP' and ob.dupli_group)))
 +                ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT'}) or
 +                        (ob.instance_type == 'COLLECTION' and ob.instance_collection)))
 +
 +    def draw(self, context):
 +        pass
 +
 +
 +class CYCLES_OBJECT_PT_cycles_settings_ray_visibility(CyclesButtonsPanel, Panel):
 +    bl_label = "Ray Visibility"
 +    bl_parent_id = "CYCLES_OBJECT_PT_cycles_settings"
 +    bl_context = "object"
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          scene = context.scene
 -        cscene = scene.cycles
          ob = context.object
          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')
  
      ntree = id_data.node_tree
  
 -    node = find_node(id_data, output_type)
 -    if not node:
 -        layout.label(text="No output node")
 -    else:
 +    node = ntree.get_output_node('CYCLES')
 +    if node:
          input = find_node_input(node, input_name)
 -        layout.template_node_view(ntree, node, input)
 +        if input:
 +            layout.template_node_view(ntree, node, input)
 +        else:
 +            layout.label(text="Incompatible output node")
 +    else:
 +        layout.label(text="No output node")
  
      return True
  
  
 -class CYCLES_LAMP_PT_preview(CyclesButtonsPanel, Panel):
 +class CYCLES_LIGHT_PT_preview(CyclesButtonsPanel, Panel):
      bl_label = "Preview"
      bl_context = "data"
      bl_options = {'DEFAULT_CLOSED'}
  
      @classmethod
      def poll(cls, context):
 -        return context.lamp and \
 -            not (context.lamp.type == 'AREA' and
 -                 context.lamp.cycles.is_portal) \
 -            and CyclesButtonsPanel.poll(context)
 +        return (
 +            context.light and
 +            not (
 +                context.light.type == 'AREA' and
 +                context.light.cycles.is_portal
 +            ) and
 +            CyclesButtonsPanel.poll(context)
 +        )
  
      def draw(self, context):
 -        self.layout.template_preview(context.lamp)
 +        self.layout.template_preview(context.light)
  
  
 -class CYCLES_LAMP_PT_lamp(CyclesButtonsPanel, Panel):
 -    bl_label = "Lamp"
 +class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel):
 +    bl_label = "Light"
      bl_context = "data"
  
      @classmethod
      def poll(cls, context):
 -        return context.lamp and CyclesButtonsPanel.poll(context)
 +        return context.light and CyclesButtonsPanel.poll(context)
  
      def draw(self, context):
          layout = self.layout
  
 -        lamp = context.lamp
 -        clamp = lamp.cycles
 +        light = context.light
 +        clamp = light.cycles
          # cscene = context.scene.cycles
  
 -        layout.prop(lamp, "type", expand=True)
 +        layout.prop(light, "type", expand=True)
  
 -        split = layout.split()
 -        col = split.column(align=True)
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        if lamp.type in {'POINT', 'SUN', 'SPOT'}:
 -            col.prop(lamp, "shadow_soft_size", text="Size")
 -        elif lamp.type == 'AREA':
 -            col.prop(lamp, "shape", text="")
 +        col = layout.column()
 +
 +        if light.type in {'POINT', 'SUN', 'SPOT'}:
 +            col.prop(light, "shadow_soft_size", text="Size")
 +        elif light.type == 'AREA':
 +            col.prop(light, "shape", text="Shape")
              sub = col.column(align=True)
  
 -            if lamp.shape == 'SQUARE':
 -                sub.prop(lamp, "size")
 -            elif lamp.shape == 'RECTANGLE':
 -                sub.prop(lamp, "size", text="Size X")
 -                sub.prop(lamp, "size_y", text="Size Y")
 +            if light.shape in {'SQUARE', 'DISK'}:
 +                sub.prop(light, "size")
 +            elif light.shape in {'RECTANGLE', 'ELLIPSE'}:
 +                sub.prop(light, "size", text="Size X")
 +                sub.prop(light, "size_y", text="Y")
  
 -        if not (lamp.type == 'AREA' and clamp.is_portal):
 -            sub = col.column(align=True)
 +        if not (light.type == 'AREA' and clamp.is_portal):
 +            sub = col.column()
              if use_branched_path(context):
                  subsub = sub.row(align=True)
                  subsub.active = use_sample_all_lights(context)
                  subsub.prop(clamp, "samples")
              sub.prop(clamp, "max_bounces")
  
 -        col = split.column()
 -
          sub = col.column(align=True)
 -        sub.active = not (lamp.type == 'AREA' and clamp.is_portal)
 +        sub.active = not (light.type == 'AREA' and clamp.is_portal)
          sub.prop(clamp, "cast_shadow")
          sub.prop(clamp, "use_multiple_importance_sampling", text="Multiple Importance")
  
 -        if lamp.type == 'AREA':
 +        if light.type == 'AREA':
              col.prop(clamp, "is_portal", text="Portal")
  
 -        if lamp.type == 'HEMI':
 -            layout.label(text="Not supported, interpreted as sun lamp")
  
 -
 -class CYCLES_LAMP_PT_nodes(CyclesButtonsPanel, Panel):
 +class CYCLES_LIGHT_PT_nodes(CyclesButtonsPanel, Panel):
      bl_label = "Nodes"
      bl_context = "data"
  
      @classmethod
      def poll(cls, context):
 -        return context.lamp and not (context.lamp.type == 'AREA' and
 -                                     context.lamp.cycles.is_portal) and \
 +        return context.light and not (context.light.type == 'AREA' and
 +                                      context.light.cycles.is_portal) and \
              CyclesButtonsPanel.poll(context)
  
      def draw(self, context):
          layout = self.layout
  
 -        lamp = context.lamp
 -        if not panel_node_draw(layout, lamp, 'OUTPUT_LAMP', 'Surface'):
 -            layout.prop(lamp, "color")
 +        light = context.light
 +        if not panel_node_draw(layout, light, 'OUTPUT_LIGHT', 'Surface'):
 +            layout.prop(light, "color")
  
  
 -class CYCLES_LAMP_PT_spot(CyclesButtonsPanel, Panel):
 +class CYCLES_LIGHT_PT_spot(CyclesButtonsPanel, Panel):
      bl_label = "Spot Shape"
      bl_context = "data"
  
      @classmethod
      def poll(cls, context):
 -        lamp = context.lamp
 -        return (lamp and lamp.type == 'SPOT') and CyclesButtonsPanel.poll(context)
 +        light = context.light
 +        return (light and light.type == 'SPOT') and CyclesButtonsPanel.poll(context)
  
 -    def draw(self, context):
 -        layout = self.layout
 -
 -        lamp = context.lamp
 -
 -        split = layout.split()
 -
 -        col = split.column()
 -        sub = col.column()
 -        sub.prop(lamp, "spot_size", text="Size")
 -        sub.prop(lamp, "spot_blend", text="Blend", slider=True)
 +    def draw(self, context):
 +        layout = self.layout
 +        light = context.light
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        col = split.column()
 -        col.prop(lamp, "show_cone")
 +        col = layout.column()
 +        col.prop(light, "spot_size", text="Size")
 +        col.prop(light, "spot_blend", text="Blend", slider=True)
 +        col.prop(light, "show_cone")
  
  
  class CYCLES_WORLD_PT_preview(CyclesButtonsPanel, Panel):
@@@ -1455,7 -1092,7 +1432,7 @@@ class CYCLES_WORLD_PT_surface(CyclesBut
          world = context.world
  
          if not panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Surface'):
 -            layout.prop(world, "horizon_color", text="Color")
 +            layout.prop(world, "color")
  
  
  class CYCLES_WORLD_PT_volume(CyclesButtonsPanel, Panel):
  class CYCLES_WORLD_PT_ambient_occlusion(CyclesButtonsPanel, Panel):
      bl_label = "Ambient Occlusion"
      bl_context = "world"
 +    bl_options = {'DEFAULT_CLOSED'}
  
      @classmethod
      def poll(cls, context):
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          light = context.world.light_settings
          scene = context.scene
  
 -        row = layout.row()
 -        sub = row.row()
 +        col = layout.column()
 +        sub = col.column()
          sub.active = light.use_ambient_occlusion or scene.render.use_simplify
          sub.prop(light, "ao_factor", text="Factor")
 -        row.prop(light, "distance", text="Distance")
 +        col.prop(light, "distance", text="Distance")
  
  
  class CYCLES_WORLD_PT_mist(CyclesButtonsPanel, Panel):
      def poll(cls, context):
          if CyclesButtonsPanel.poll(context):
              if context.world:
 -                for rl in context.scene.render.layers:
 -                    if rl.use_pass_mist:
 +                for view_layer in context.scene.view_layers:
 +                    if view_layer.use_pass_mist:
                          return True
  
          return False
@@@ -1565,67 -1199,35 +1542,67 @@@ class CYCLES_WORLD_PT_settings(CyclesBu
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        world = context.world
 -        cworld = world.cycles
 -        # cscene = context.scene.cycles
 +        layout.column()
  
 -        split = layout.split()
  
 -        col = split.column()
 +class CYCLES_WORLD_PT_settings_surface(CyclesButtonsPanel, Panel):
 +    bl_label = "Surface"
 +    bl_parent_id = "CYCLES_WORLD_PT_settings"
 +    bl_context = "world"
 +
 +    @classmethod
 +    def poll(cls, context):
 +        return context.world and CyclesButtonsPanel.poll(context)
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        world = context.world
 +        cworld = world.cycles
  
 -        col.label(text="Surface:")
 +        col = layout.column()
          col.prop(cworld, "sampling_method", text="Sampling")
  
 -        sub = col.column(align=True)
 +        sub = col.column()
          sub.active = cworld.sampling_method != 'NONE'
          subsub = sub.row(align=True)
          subsub.active = cworld.sampling_method == 'MANUAL'
          subsub.prop(cworld, "sample_map_resolution")
          if use_branched_path(context):
 -            subsub = sub.row(align=True)
 +            subsub = sub.column(align=True)
              subsub.active = use_sample_all_lights(context)
              subsub.prop(cworld, "samples")
          sub.prop(cworld, "max_bounces")
  
 -        col = split.column()
 -        col.label(text="Volume:")
 +
 +class CYCLES_WORLD_PT_settings_volume(CyclesButtonsPanel, Panel):
 +    bl_label = "Volume"
 +    bl_parent_id = "CYCLES_WORLD_PT_settings"
 +    bl_context = "world"
 +
 +    @classmethod
 +    def poll(cls, context):
 +        return context.world and CyclesButtonsPanel.poll(context)
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        world = context.world
 +        cworld = world.cycles
 +
 +        col = layout.column()
 +
          sub = col.column()
          sub.active = use_cpu(context)
 -        sub.prop(cworld, "volume_sampling", text="")
 -        col.prop(cworld, "volume_interpolation", text="")
 +        sub.prop(cworld, "volume_sampling", text="Sampling")
 +        col.prop(cworld, "volume_interpolation", text="Interpolation")
          col.prop(cworld, "homogeneous_volume", text="Homogeneous")
  
  
@@@ -1696,68 -1298,228 +1673,68 @@@ class CYCLES_MATERIAL_PT_displacement(C
  class CYCLES_MATERIAL_PT_settings(CyclesButtonsPanel, Panel):
      bl_label = "Settings"
      bl_context = "material"
 -
 -    @classmethod
 -    def poll(cls, context):
 -        return context.material and CyclesButtonsPanel.poll(context)
 -
 -    def draw(self, context):
 -        layout = self.layout
 -
 -        mat = context.material
 -        cmat = mat.cycles
 -
 -        split = layout.split()
 -        col = split.column()
 -        col.label(text="Surface:")
 -        col.prop(cmat, "sample_as_light", text="Multiple Importance")
 -        col.prop(cmat, "use_transparent_shadow")
 -
 -        col.separator()
 -        col.label(text="Geometry:")
 -        col.prop(cmat, "displacement_method", text="")
 -
 -        col = split.column()
 -        col.label(text="Volume:")
 -        sub = col.column()
 -        sub.active = use_cpu(context)
 -        sub.prop(cmat, "volume_sampling", text="")
 -        col.prop(cmat, "volume_interpolation", text="")
 -        col.prop(cmat, "homogeneous_volume", text="Homogeneous")
 -
 -        col.separator()
 -        col.prop(mat, "pass_index")
 -
 -
 -class CYCLES_MATERIAL_PT_viewport(CyclesButtonsPanel, Panel):
 -    bl_label = "Viewport"
 -    bl_context = "material"
      bl_options = {'DEFAULT_CLOSED'}
  
      @classmethod
      def poll(cls, context):
          return context.material and CyclesButtonsPanel.poll(context)
  
 -    def draw(self, context):
 -        mat = context.material
 -
 +    @staticmethod
 +    def draw_shared(self, mat):
          layout = self.layout
 -        split = layout.split()
 -
 -        col = split.column(align=True)
 -        col.label("Color:")
 -        col.prop(mat, "diffuse_color", text="")
 -        col.prop(mat, "alpha")
 -
 -        col.separator()
 -        col.label("Alpha:")
 -        col.prop(mat.game_settings, "alpha_blend", text="")
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        col = split.column(align=True)
 -        col.label("Specular:")
 -        col.prop(mat, "specular_color", text="")
 -        col.prop(mat, "specular_hardness", text="Hardness")
 -
 -
 -class CYCLES_TEXTURE_PT_context(CyclesButtonsPanel, Panel):
 -    bl_label = ""
 -    bl_context = "texture"
 -    bl_options = {'HIDE_HEADER'}
 -    COMPAT_ENGINES = {'CYCLES'}
 +        layout.prop(mat, "pass_index")
  
      def draw(self, context):
 -        layout = self.layout
 -
 -        tex = context.texture
 -        space = context.space_data
 -        pin_id = space.pin_id
 -        use_pin_id = space.use_pin_id
 -        user = context.texture_user
 -
 -        space.use_limited_texture_context = False
 -
 -        if not (use_pin_id and isinstance(pin_id, bpy.types.Texture)):
 -            pin_id = None
 -
 -        if not pin_id:
 -            layout.template_texture_user()
 -
 -        if user or pin_id:
 -            layout.separator()
 -
 -            split = layout.split(percentage=0.65)
 -            col = split.column()
 -
 -            if pin_id:
 -                col.template_ID(space, "pin_id")
 -            else:
 -                propname = context.texture_user_property.identifier
 -                col.template_ID(user, propname, new="texture.new")
 +        self.draw_shared(self, context.material)
  
 -            if tex:
 -                split = layout.split(percentage=0.2)
 -                split.label(text="Type:")
 -                split.prop(tex, "type", text="")
  
 +class CYCLES_MATERIAL_PT_settings_surface(CyclesButtonsPanel, Panel):
 +    bl_label = "Surface"
 +    bl_parent_id = "CYCLES_MATERIAL_PT_settings"
 +    bl_context = "material"
  
 -class CYCLES_TEXTURE_PT_node(CyclesButtonsPanel, Panel):
 -    bl_label = "Node"
 -    bl_context = "texture"
 -
 -    @classmethod
 -    def poll(cls, context):
 -        node = context.texture_node
 -        return node and CyclesButtonsPanel.poll(context)
 -
 -    def draw(self, context):
 +    @staticmethod
 +    def draw_shared(self, mat):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        node = context.texture_node
 -        ntree = node.id_data
 -        layout.template_node_view(ntree, node, None)
 -
 -
 -class CYCLES_TEXTURE_PT_mapping(CyclesButtonsPanel, Panel):
 -    bl_label = "Mapping"
 -    bl_context = "texture"
 +        cmat = mat.cycles
  
 -    @classmethod
 -    def poll(cls, context):
 -        node = context.texture_node
 -        # TODO(sergey): perform a faster/nicer check?
 -        return node and hasattr(node, 'texture_mapping') and CyclesButtonsPanel.poll(context)
 +        col = layout.column()
 +        col.prop(cmat, "sample_as_light", text="Multiple Importance")
 +        col.prop(cmat, "use_transparent_shadow")
 +        col.prop(cmat, "displacement_method", text="Displacement Method")
  
      def draw(self, context):
 -        layout = self.layout
 +        self.draw_shared(self, context.material)
  
 -        node = context.texture_node
  
 -        mapping = node.texture_mapping
 -
 -        layout.prop(mapping, "vector_type", expand=True)
 -
 -        row = layout.row()
 -
 -        row.column().prop(mapping, "translation")
 -        row.column().prop(mapping, "rotation")
 -        row.column().prop(mapping, "scale")
 -
 -        layout.label(text="Projection:")
 -
 -        row = layout.row()
 -        row.prop(mapping, "mapping_x", text="")
 -        row.prop(mapping, "mapping_y", text="")
 -        row.prop(mapping, "mapping_z", text="")
 -
 -
 -class CYCLES_TEXTURE_PT_colors(CyclesButtonsPanel, Panel):
 -    bl_label = "Color"
 -    bl_context = "texture"
 -    bl_options = {'DEFAULT_CLOSED'}
 -
 -    @classmethod
 -    def poll(cls, context):
 -        # node = context.texture_node
 -        return False
 -        # return node and CyclesButtonsPanel.poll(context)
 +class CYCLES_MATERIAL_PT_settings_volume(CyclesButtonsPanel, Panel):
 +    bl_label = "Volume"
 +    bl_parent_id = "CYCLES_MATERIAL_PT_settings"
 +    bl_context = "material"
  
 -    def draw(self, context):
 +    @staticmethod
 +    def draw_shared(self, context, mat):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        node = context.texture_node
 -
 -        mapping = node.color_mapping
 -
 -        split = layout.split()
 -
 -        col = split.column()
 -        col.label(text="Blend:")
 -        col.prop(mapping, "blend_type", text="")
 -        col.prop(mapping, "blend_factor", text="Factor")
 -        col.prop(mapping, "blend_color", text="")
 -
 -        col = split.column()
 -        col.label(text="Adjust:")
 -        col.prop(mapping, "brightness")
 -        col.prop(mapping, "contrast")
 -        col.prop(mapping, "saturation")
 -
 -        layout.separator()
 -
 -        layout.prop(mapping, "use_color_ramp", text="Ramp")
 -        if mapping.use_color_ramp:
 -            layout.template_color_ramp(mapping, "color_ramp", expand=True)
 -
 -
 -class CYCLES_PARTICLE_PT_textures(CyclesButtonsPanel, Panel):
 -    bl_label = "Textures"
 -    bl_context = "particle"
 -    bl_options = {'DEFAULT_CLOSED'}
 +        cmat = mat.cycles
  
 -    @classmethod
 -    def poll(cls, context):
 -        psys = context.particle_system
 -        return psys and CyclesButtonsPanel.poll(context)
 +        col = layout.column()
 +        sub = col.column()
 +        sub.active = use_cpu(context)
 +        sub.prop(cmat, "volume_sampling", text="Sampling")
 +        col.prop(cmat, "volume_interpolation", text="Interpolation")
 +        col.prop(cmat, "homogeneous_volume", text="Homogeneous")
  
      def draw(self, context):
 -        layout = self.layout
 -
 -        psys = context.particle_system
 -        part = psys.settings
 -
 -        row = layout.row()
 -        row.template_list("TEXTURE_UL_texslots", "", part, "texture_slots", part, "active_texture_index", rows=2)
 -
 -        col = row.column(align=True)
 -        col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP'
 -        col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN'
 -        col.menu("TEXTURE_MT_specials", icon='DOWNARROW_HLT', text="")
 -
 -        if not part.active_texture:
 -            layout.template_ID(part, "active_texture", new="texture.new")
 -        else:
 -            slot = part.texture_slots[part.active_texture_index]
 -            layout.template_ID(slot, "texture", new="texture.new")
 +        self.draw_shared(self, context, context.material)
  
  
  class CYCLES_RENDER_PT_bake(CyclesButtonsPanel, Panel):
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False  # No animation.
  
          scene = context.scene
          cscene = scene.cycles
          cbk = scene.render.bake
 -
 -        layout.operator("object.bake", icon='RENDER_STILL').type = cscene.bake_type
 -
 -        col = layout.column()
 -        col.prop(cscene, "bake_type")
 +        rd = scene.render
  
          col = layout.column()
 +        col.prop(rd, "use_bake_multires")
 +        if rd.use_bake_multires:
 +            col.prop(rd, "bake_type")
  
 -        if cscene.bake_type == 'NORMAL':
 -            col.prop(cbk, "normal_space", text="Space")
 +            col = layout.column()
 +            col.prop(rd, "bake_margin")
 +            col.prop(rd, "use_bake_clear")
  
 -            row = col.row(align=True)
 -            row.label(text="Swizzle:")
 -            row.prop(cbk, "normal_r", text="")
 -            row.prop(cbk, "normal_g", text="")
 -            row.prop(cbk, "normal_b", text="")
 +            if rd.bake_type == 'DISPLACEMENT':
 +                col.prop(rd, "use_bake_lores_mesh")
  
 -        elif cscene.bake_type == 'COMBINED':
 -            row = col.row(align=True)
 -            row.prop(cbk, "use_pass_direct", toggle=True)
 -            row.prop(cbk, "use_pass_indirect", toggle=True)
 +            col.operator("object.bake_image", icon='RENDER_STILL')
  
 -            split = col.split()
 -            split.active = cbk.use_pass_direct or cbk.use_pass_indirect
 -
 -            col = split.column()
 -            col.prop(cbk, "use_pass_diffuse")
 -            col.prop(cbk, "use_pass_glossy")
 -            col.prop(cbk, "use_pass_transmission")
 +        else:
 +            col.prop(cscene, "bake_type")
 +
 +            col = layout.column()
 +
 +            if cscene.bake_type == 'NORMAL':
 +                col.prop(cbk, "normal_space", text="Space")
 +
 +                sub = col.column(align=True)
 +                sub.prop(cbk, "normal_r", text="Swizzle R")
 +                sub.prop(cbk, "normal_g", text="G")
 +                sub.prop(cbk, "normal_b", text="B")
 +
 +            elif cscene.bake_type == 'COMBINED':
 +                row = col.row(align=True)
 +                row.use_property_split = False
 +                row.prop(cbk, "use_pass_direct", toggle=True)
 +                row.prop(cbk, "use_pass_indirect", toggle=True)
 +
 +                col = col.column()
 +                col.active = cbk.use_pass_direct or cbk.use_pass_indirect
 +                col.prop(cbk, "use_pass_diffuse")
 +                col.prop(cbk, "use_pass_glossy")
 +                col.prop(cbk, "use_pass_transmission")
 +                col.prop(cbk, "use_pass_subsurface")
 +                col.prop(cbk, "use_pass_ambient_occlusion")
 +                col.prop(cbk, "use_pass_emit")
 +
 +            elif cscene.bake_type in {'DIFFUSE', 'GLOSSY', 'TRANSMISSION', 'SUBSURFACE'}:
 +                row = col.row(align=True)
 +                row.use_property_split = False
 +                row.prop(cbk, "use_pass_direct", toggle=True)
 +                row.prop(cbk, "use_pass_indirect", toggle=True)
 +                row.prop(cbk, "use_pass_color", toggle=True)
  
 -            col = split.column()
 -            col.prop(cbk, "use_pass_subsurface")
 -            col.prop(cbk, "use_pass_ambient_occlusion")
 -            col.prop(cbk, "use_pass_emit")
 +            layout.separator()
  
 -        elif cscene.bake_type in {'DIFFUSE', 'GLOSSY', 'TRANSMISSION', 'SUBSURFACE'}:
 -            row = col.row(align=True)
 -            row.prop(cbk, "use_pass_direct", toggle=True)
 -            row.prop(cbk, "use_pass_indirect", toggle=True)
 -            row.prop(cbk, "use_pass_color", toggle=True)
 +            col = layout.column()
 +            col.prop(cbk, "margin")
 +            col.prop(cbk, "use_clear", text="Clear Image")
  
 -        layout.separator()
 +            col.separator()
  
 -        split = layout.split()
 +            col.prop(cbk, "use_selected_to_active")
 +            sub = col.column()
 +            sub.active = cbk.use_selected_to_active
 +            sub.prop(cbk, "use_cage", text="Cage")
 +            if cbk.use_cage:
 +                sub.prop(cbk, "cage_extrusion", text="Extrusion")
 +                sub.prop(cbk, "cage_object", text="Cage Object")
 +            else:
 +                sub.prop(cbk, "cage_extrusion", text="Ray Distance")
  
 -        col = split.column()
 -        col.prop(cbk, "margin")
 -        col.prop(cbk, "use_clear")
 +            layout.separator()
  
 -        col = split.column()
 -        col.prop(cbk, "use_selected_to_active")
 -        sub = col.column()
 -        sub.active = cbk.use_selected_to_active
 -        sub.prop(cbk, "use_cage", text="Cage")
 -        if cbk.use_cage:
 -            sub.prop(cbk, "cage_extrusion", text="Extrusion")
 -            sub.prop_search(cbk, "cage_object", scene, "objects", text="")
 -        else:
 -            sub.prop(cbk, "cage_extrusion", text="Ray Distance")
 +            layout.operator("object.bake", icon='RENDER_STILL').type = cscene.bake_type
  
  
  class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel):
  
          col = layout.column()
  
 -        col.label('CPU Flags:')
 +        col.label(text="CPU Flags:")
          row = col.row(align=True)
          row.prop(cscene, "debug_use_cpu_sse2", toggle=True)
          row.prop(cscene, "debug_use_cpu_sse3", toggle=True)
          col.separator()
  
          col = layout.column()
 -        col.label('CUDA Flags:')
 +        col.label(text="CUDA Flags:")
          col.prop(cscene, "debug_use_cuda_adaptive_compile")
          col.prop(cscene, "debug_use_cuda_split_kernel")
  
          col.separator()
  
          col = layout.column()
 -        col.label('OpenCL Flags:')
 +        col.label(text="OpenCL Flags:")
          col.prop(cscene, "debug_opencl_kernel_type", text="Kernel")
          col.prop(cscene, "debug_opencl_device_type", text="Device")
          col.prop(cscene, "debug_opencl_kernel_single_program", text="Single Program")
          col.prop(cscene, "debug_bvh_type")
  
  
 -class CYCLES_PARTICLE_PT_curve_settings(CyclesButtonsPanel, Panel):
 -    bl_label = "Cycles Hair Settings"
 -    bl_context = "particle"
 +class CYCLES_RENDER_PT_simplify(CyclesButtonsPanel, Panel):
 +    bl_label = "Simplify"
 +    bl_context = "render"
 +    bl_options = {'DEFAULT_CLOSED'}
 +    COMPAT_ENGINES = {'CYCLES'}
  
 -    @classmethod
 -    def poll(cls, context):
 -        scene = context.scene
 -        ccscene = scene.cycles_curves
 -        psys = context.particle_system
 -        use_curves = ccscene.use_curves and psys
 -        return CyclesButtonsPanel.poll(context) and use_curves and psys.settings.type == 'HAIR'
 +    def draw_header(self, context):
 +        rd = context.scene.render
 +        self.layout.prop(rd, "use_simplify", text="")
  
      def draw(self, context):
 -        layout = self.layout
 +        pass
  
 -        psys = context.particle_settings
 -        cpsys = psys.cycles
  
 -        row = layout.row()
 -        row.prop(cpsys, "shape", text="Shape")
 +class CYCLES_RENDER_PT_simplify_viewport(CyclesButtonsPanel, Panel):
 +    bl_label = "Viewport"
 +    bl_context = "render"
 +    bl_parent_id = "CYCLES_RENDER_PT_simplify"
 +    COMPAT_ENGINES = {'CYCLES'}
  
 -        layout.label(text="Thickness:")
 -        row = layout.row()
 -        row.prop(cpsys, "root_width", text="Root")
 -        row.prop(cpsys, "tip_width", text="Tip")
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
 -        row = layout.row()
 -        row.prop(cpsys, "radius_scale", text="Scaling")
 -        row.prop(cpsys, "use_closetip", text="Close tip")
 +        scene = context.scene
 +        rd = scene.render
 +        cscene = scene.cycles
  
 +        layout.active = rd.use_simplify
  
 -class CYCLES_SCENE_PT_simplify(CyclesButtonsPanel, Panel):
 -    bl_label = "Simplify"
 -    bl_context = "scene"
 -    COMPAT_ENGINES = {'CYCLES'}
 +        col = layout.column()
 +        col.prop(rd, "simplify_subdivision", text="Max Subdivision")
 +        col.prop(rd, "simplify_child_particles", text="Child Particles")
 +        col.prop(cscene, "texture_limit", text="Texture Limit")
 +        col.prop(cscene, "ao_bounces", text="AO Bounces")
  
 -    def draw_header(self, context):
 -        rd = context.scene.render
 -        self.layout.prop(rd, "use_simplify", text="")
 +
 +class CYCLES_RENDER_PT_simplify_render(CyclesButtonsPanel, Panel):
 +    bl_label = "Render"
 +    bl_context = "render"
 +    bl_parent_id = "CYCLES_RENDER_PT_simplify"
 +    COMPAT_ENGINES = {'CYCLES'}
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
          scene = context.scene
          rd = scene.render
  
          layout.active = rd.use_simplify
  
 -        col = layout.column(align=True)
 -        col.label(text="Subdivision")
 -        row = col.row(align=True)
 -        row.prop(rd, "simplify_subdivision", text="Viewport")
 -        row.prop(rd, "simplify_subdivision_render", text="Render")
 +        col = layout.column()
  
 -        col = layout.column(align=True)
 -        col.label(text="Child Particles")
 -        row = col.row(align=True)
 -        row.prop(rd, "simplify_child_particles", text="Viewport")
 -        row.prop(rd, "simplify_child_particles_render", text="Render")
 +        col.prop(rd, "simplify_subdivision_render", text="Max Subdivision")
 +        col.prop(rd, "simplify_child_particles_render", text="Child Particles")
 +        col.prop(cscene, "texture_limit_render", text="Texture Limit")
 +        col.prop(cscene, "ao_bounces_render", text="AO Bounces")
  
 -        col = layout.column(align=True)
 -        split = col.split()
 -        sub = split.column()
 -        sub.label(text="Texture Limit Viewport")
 -        sub.prop(cscene, "texture_limit", text="")
 -        sub = split.column()
 -        sub.label(text="Texture Limit Render")
 -        sub.prop(cscene, "texture_limit_render", text="")
  
 -        split = layout.split()
 -        col = split.column()
 +class CYCLES_RENDER_PT_simplify_culling(CyclesButtonsPanel, Panel):
 +    bl_label = "Culling"
 +    bl_context = "render"
 +    bl_parent_id = "CYCLES_RENDER_PT_simplify"
 +    bl_options = {'DEFAULT_CLOSED'}
 +    COMPAT_ENGINES = {'CYCLES'}
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        scene = context.scene
 +        rd = scene.render
 +        cscene = scene.cycles
 +
 +        layout.active = rd.use_simplify
 +
 +        col = layout.column()
          col.prop(cscene, "use_camera_cull")
 -        row = col.row()
 -        row.active = cscene.use_camera_cull
 -        row.prop(cscene, "camera_cull_margin")
 +        sub = col.column()
 +        sub.active = cscene.use_camera_cull
 +        sub.prop(cscene, "camera_cull_margin")
  
 -        col = split.column()
 +        col = layout.column()
          col.prop(cscene, "use_distance_cull")
 -        row = col.row()
 -        row.active = cscene.use_distance_cull
 -        row.prop(cscene, "distance_cull_margin", text="Distance")
 +        sub = col.column()
 +        sub.active = cscene.use_distance_cull
 +        sub.prop(cscene, "distance_cull_margin", text="Distance")
  
 -        split = layout.split()
 -        col = split.column()
 -        col.prop(cscene, "ao_bounces")
  
 -        col = split.column()
 -        col.prop(cscene, "ao_bounces_render")
 +class CYCLES_NODE_PT_settings(CyclesNodeButtonsPanel, Panel):
 +    bl_label = "Settings"
 +    bl_category = "Node"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        snode = context.space_data
 +        return CyclesNodeButtonsPanel.poll(context) and \
 +               snode.tree_type == 'ShaderNodeTree' and snode.id and \
 +               snode.id.bl_rna.identifier == 'Material'
 +
 +    def draw(self, context):
 +        material = context.space_data.id
 +        CYCLES_MATERIAL_PT_settings.draw_shared(self, material)
 +
 +
 +class CYCLES_NODE_PT_settings_surface(CyclesNodeButtonsPanel, Panel):
 +    bl_label = "Surface"
 +    bl_category = "Node"
 +    bl_parent_id = "CYCLES_NODE_PT_settings"
 +
 +    def draw(self, context):
 +        material = context.space_data.id
 +        CYCLES_MATERIAL_PT_settings_surface.draw_shared(self, material)
 +
 +
 +class CYCLES_NODE_PT_settings_volume(CyclesNodeButtonsPanel, Panel):
 +    bl_label = "Volume"
 +    bl_category = "Node"
 +    bl_parent_id = "CYCLES_NODE_PT_settings"
 +
 +    def draw(self, context):
 +        material = context.space_data.id
 +        CYCLES_MATERIAL_PT_settings_volume.draw_shared(self, context, material)
  
  
  def draw_device(self, context):
      scene = context.scene
      layout = self.layout
 +    layout.use_property_split = True
 +    layout.use_property_decorate = False
  
 -    if scene.render.engine == 'CYCLES':
 +    if context.engine == 'CYCLES':
          from . import engine
          cscene = scene.cycles
  
 -        layout.prop(cscene, "feature_set")
 +        col = layout.column()
 +        col.prop(cscene, "feature_set")
 +
 +        scene = context.scene
  
 -        split = layout.split(percentage=1 / 3)
 -        split.label("Device:")
 -        row = split.row()
 -        row.active = show_device_active(context)
 -        row.prop(cscene, "device", text="")
 +        col = layout.column()
 +        col.active = show_device_active(context)
 +        col.prop(cscene, "device")
  
 +        from . import engine
          if engine.with_osl() and use_cpu(context):
 -            layout.prop(cscene, "shading_system")
 +            col.prop(cscene, "shading_system")
  
  
  def draw_pause(self, context):
      layout = self.layout
      scene = context.scene
  
 -    if scene.render.engine == "CYCLES":
 +    if context.engine == "CYCLES":
          view = context.space_data
  
 -        if view.viewport_shade == 'RENDERED':
 +        if view.shading.type == 'RENDERED':
              cscene = scene.cycles
 -            layername = scene.render.layers.active.name
 -            layout.prop(cscene, "preview_pause", icon="PAUSE", text="")
 -            layout.prop(cscene, "preview_active_layer", icon="RENDERLAYERS", text=layername)
 +            layout.prop(cscene, "preview_pause", icon='PAUSE', text="")
  
  
  def get_panels():
          'DATA_PT_area',
          'DATA_PT_camera_dof',
          'DATA_PT_falloff_curve',
 -        'DATA_PT_lamp',
 +        'DATA_PT_light',
          'DATA_PT_preview',
 -        'DATA_PT_shadow',
          'DATA_PT_spot',
 -        'DATA_PT_sunsky',
          'MATERIAL_PT_context_material',
 -        'MATERIAL_PT_diffuse',
 -        'MATERIAL_PT_flare',
 -        'MATERIAL_PT_halo',
 -        'MATERIAL_PT_mirror',
 -        'MATERIAL_PT_options',
 -        'MATERIAL_PT_pipeline',
          'MATERIAL_PT_preview',
 -        'MATERIAL_PT_shading',
 -        'MATERIAL_PT_shadow',
 -        'MATERIAL_PT_specular',
 -        'MATERIAL_PT_sss',
 -        'MATERIAL_PT_strand',
 -        'MATERIAL_PT_transp',
 -        'MATERIAL_PT_volume_density',
 -        'MATERIAL_PT_volume_integration',
 -        'MATERIAL_PT_volume_lighting',
 -        'MATERIAL_PT_volume_options',
 -        'MATERIAL_PT_volume_shading',
 -        'MATERIAL_PT_volume_transp',
 -        'RENDERLAYER_PT_layer_options',
 -        'RENDERLAYER_PT_layer_passes',
 -        'RENDERLAYER_PT_views',
 -        'RENDER_PT_antialiasing',
 -        'RENDER_PT_bake',
 -        'RENDER_PT_motion_blur',
 -        'RENDER_PT_performance',
 +        'VIEWLAYER_PT_filter',
 +        'VIEWLAYER_PT_layer_passes',
          'RENDER_PT_post_processing',
 -        'RENDER_PT_shading',
 -        'SCENE_PT_simplify',
 -        'TEXTURE_PT_context_texture',
 -        'WORLD_PT_ambient_occlusion',
 -        'WORLD_PT_environment_lighting',
 -        'WORLD_PT_gather',
 -        'WORLD_PT_indirect_lighting',
 -        'WORLD_PT_mist',
 -        'WORLD_PT_preview',
 -        'WORLD_PT_world'
 +        'RENDER_PT_simplify',
      }
  
      panels = []
  
  
  classes = (
 -    CYCLES_MT_sampling_presets,
 -    CYCLES_MT_integrator_presets,
 +    CYCLES_PT_sampling_presets,
 +    CYCLES_PT_integrator_presets,
      CYCLES_RENDER_PT_sampling,
 -    CYCLES_RENDER_PT_geometry,
 +    CYCLES_RENDER_PT_sampling_sub_samples,
 +    CYCLES_RENDER_PT_sampling_advanced,
      CYCLES_RENDER_PT_light_paths,
 +    CYCLES_RENDER_PT_light_paths_max_bounces,
 +    CYCLES_RENDER_PT_light_paths_clamping,
 +    CYCLES_RENDER_PT_light_paths_caustics,
 +    CYCLES_RENDER_PT_volumes,
 +    CYCLES_RENDER_PT_subdivision,
 +    CYCLES_RENDER_PT_hair,
 +    CYCLES_RENDER_PT_simplify,
 +    CYCLES_RENDER_PT_simplify_viewport,
 +    CYCLES_RENDER_PT_simplify_render,
 +    CYCLES_RENDER_PT_simplify_culling,
      CYCLES_RENDER_PT_motion_blur,
 +    CYCLES_RENDER_PT_motion_blur_curve,
      CYCLES_RENDER_PT_film,
 +    CYCLES_RENDER_PT_film_pixel_filter,
 +    CYCLES_RENDER_PT_film_transparency,
      CYCLES_RENDER_PT_performance,
 -    CYCLES_RENDER_PT_layer_options,
 -    CYCLES_RENDER_PT_layer_passes,
 -    CYCLES_RENDER_PT_views,
 +    CYCLES_RENDER_PT_performance_threads,
 +    CYCLES_RENDER_PT_performance_tiles,
 +    CYCLES_RENDER_PT_performance_acceleration_structure,
 +    CYCLES_RENDER_PT_performance_final_render,
 +    CYCLES_RENDER_PT_performance_viewport,
 +    CYCLES_RENDER_PT_filter,
 +    CYCLES_RENDER_PT_override,
 +    CYCLES_RENDER_PT_passes,
 +    CYCLES_RENDER_PT_passes_data,
 +    CYCLES_RENDER_PT_passes_light,
 +    CYCLES_RENDER_PT_passes_crypto,
 +    CYCLES_RENDER_PT_passes_debug,
      CYCLES_RENDER_PT_denoising,
      CYCLES_PT_post_processing,
      CYCLES_CAMERA_PT_dof,
 +    CYCLES_CAMERA_PT_dof_aperture,
 +    CYCLES_CAMERA_PT_dof_viewport,
      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,
      CYCLES_WORLD_PT_mist,
      CYCLES_WORLD_PT_ray_visibility,
      CYCLES_WORLD_PT_settings,
 +    CYCLES_WORLD_PT_settings_surface,
 +    CYCLES_WORLD_PT_settings_volume,
      CYCLES_MATERIAL_PT_preview,
      CYCLES_MATERIAL_PT_surface,
      CYCLES_MATERIAL_PT_volume,
      CYCLES_MATERIAL_PT_displacement,
      CYCLES_MATERIAL_PT_settings,
 -    CYCLES_MATERIAL_PT_viewport,
 -    CYCLES_TEXTURE_PT_context,
 -    CYCLES_TEXTURE_PT_node,
 -    CYCLES_TEXTURE_PT_mapping,
 -    CYCLES_TEXTURE_PT_colors,
 -    CYCLES_PARTICLE_PT_textures,
 +    CYCLES_MATERIAL_PT_settings_surface,
 +    CYCLES_MATERIAL_PT_settings_volume,
      CYCLES_RENDER_PT_bake,
      CYCLES_RENDER_PT_debug,
 -    CYCLES_PARTICLE_PT_curve_settings,
 -    CYCLES_SCENE_PT_simplify,
 +    CYCLES_NODE_PT_settings,
 +    CYCLES_NODE_PT_settings_surface,
 +    CYCLES_NODE_PT_settings_volume,
  )
  
  
  def register():
      from bpy.utils import register_class
  
 -    bpy.types.RENDER_PT_render.append(draw_device)
 +    bpy.types.RENDER_PT_context.append(draw_device)
      bpy.types.VIEW3D_HT_header.append(draw_pause)
  
      for panel in get_panels():
  def unregister():
      from bpy.utils import unregister_class
  
 -    bpy.types.RENDER_PT_render.remove(draw_device)
 +    bpy.types.RENDER_PT_context.remove(draw_device)
      bpy.types.VIEW3D_HT_header.remove(draw_pause)
  
      for panel in get_panels():
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__ */
  
  #include "blender/CCL_api.h"
  
+ #include "blender/blender_device.h"
  #include "blender/blender_sync.h"
  #include "blender/blender_session.h"
  
+ #include "render/denoising.h"
  #include "util/util_debug.h"
  #include "util/util_foreach.h"
  #include "util/util_logging.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);
        BL::BlendData data(dataptr);
  
 -      PointerRNA sceneptr;
 -      RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyscene), &sceneptr);
 -      BL::Scene scene(sceneptr);
 -
        PointerRNA regionptr;
        RNA_pointer_create(NULL, &RNA_Region, pylong_as_voidptr_typesafe(pyregion), &regionptr);
        BL::Region region(regionptr);
                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);
  }
  
@@@ -262,22 -283,13 +265,22 @@@ static PyObject *free_func(PyObject * /
        Py_RETURN_NONE;
  }
  
 -static PyObject *render_func(PyObject * /*self*/, PyObject *value)
 +static PyObject *render_func(PyObject * /*self*/, PyObject *args)
  {
 -      BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(value);
 +      PyObject *pysession, *pydepsgraph;
 +
 +      if(!PyArg_ParseTuple(args, "OO", &pysession, &pydepsgraph))
 +              return NULL;
 +
 +      BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
 +
 +      PointerRNA depsgraphptr;
 +      RNA_pointer_create(NULL, &RNA_Depsgraph, (ID*)PyLong_AsVoidPtr(pydepsgraph), &depsgraphptr);
 +      BL::Depsgraph b_depsgraph(depsgraphptr);
  
        python_thread_state_save(&session->python_thread_state);
  
 -      session->render();
 +      session->render(b_depsgraph);
  
        python_thread_state_restore(&session->python_thread_state);
  
  /* pixel_array and result passed as pointers */
  static PyObject *bake_func(PyObject * /*self*/, PyObject *args)
  {
 -      PyObject *pysession, *pyobject;
 +      PyObject *pysession, *pydepsgraph, *pyobject;
        PyObject *pypixel_array, *pyresult;
        const char *pass_type;
        int num_pixels, depth, object_id, pass_filter;
  
 -      if(!PyArg_ParseTuple(args, "OOsiiOiiO", &pysession, &pyobject, &pass_type, &pass_filter, &object_id, &pypixel_array, &num_pixels, &depth, &pyresult))
 +      if(!PyArg_ParseTuple(args, "OOOsiiOiiO", &pysession, &pydepsgraph, &pyobject, &pass_type, &pass_filter, &object_id, &pypixel_array, &num_pixels, &depth, &pyresult))
                return NULL;
  
        BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
  
 +      PointerRNA depsgraphptr;
 +      RNA_pointer_create(NULL, &RNA_Depsgraph, PyLong_AsVoidPtr(pydepsgraph), &depsgraphptr);
 +      BL::Depsgraph b_depsgraph(depsgraphptr);
 +
        PointerRNA objectptr;
        RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyobject), &objectptr);
        BL::Object b_object(objectptr);
  
        python_thread_state_save(&session->python_thread_state);
  
 -      session->bake(b_object, pass_type, pass_filter, object_id, b_bake_pixel, (size_t)num_pixels, depth, (float *)b_result);
 +      session->bake(b_depsgraph, b_object, pass_type, pass_filter, object_id, b_bake_pixel, (size_t)num_pixels, depth, (float *)b_result);
  
        python_thread_state_restore(&session->python_thread_state);
  
  
  static PyObject *draw_func(PyObject * /*self*/, PyObject *args)
  {
 -      PyObject *pysession, *pyv3d, *pyrv3d;
 +      PyObject *pysession, *pygraph, *pyv3d, *pyrv3d;
  
 -      if(!PyArg_ParseTuple(args, "OOO", &pysession, &pyv3d, &pyrv3d))
 +      if(!PyArg_ParseTuple(args, "OOOO", &pysession, &pygraph, &pyv3d, &pyrv3d))
                return NULL;
  
        BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
  
  static PyObject *reset_func(PyObject * /*self*/, PyObject *args)
  {
 -      PyObject *pysession, *pydata, *pyscene;
 +      PyObject *pysession, *pydata, *pydepsgraph;
  
 -      if(!PyArg_ParseTuple(args, "OOO", &pysession, &pydata, &pyscene))
 +      if(!PyArg_ParseTuple(args, "OOO", &pysession, &pydata, &pydepsgraph))
                return NULL;
  
        BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
        RNA_main_pointer_create((Main*)PyLong_AsVoidPtr(pydata), &dataptr);
        BL::BlendData b_data(dataptr);
  
 -      PointerRNA sceneptr;
 -      RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyscene), &sceneptr);
 -      BL::Scene b_scene(sceneptr);
 +      PointerRNA depsgraphptr;
 +      RNA_pointer_create(NULL, &RNA_Depsgraph, PyLong_AsVoidPtr(pydepsgraph), &depsgraphptr);
 +      BL::Depsgraph b_depsgraph(depsgraphptr);
  
        python_thread_state_save(&session->python_thread_state);
  
 -      session->reset_session(b_data, b_scene);
 +      session->reset_session(b_data, b_depsgraph);
  
        python_thread_state_restore(&session->python_thread_state);
  
        Py_RETURN_NONE;
  }
  
 -static PyObject *sync_func(PyObject * /*self*/, PyObject *value)
 +static PyObject *sync_func(PyObject * /*self*/, PyObject *args)
  {
 -      BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(value);
 +      PyObject *pysession, *pydepsgraph;
 +
 +      if(!PyArg_ParseTuple(args, "OO", &pysession, &pydepsgraph))
 +              return NULL;
 +
 +      BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
 +
 +      PointerRNA depsgraphptr;
 +      RNA_pointer_create(NULL, &RNA_Depsgraph, PyLong_AsVoidPtr(pydepsgraph), &depsgraphptr);
 +      BL::Depsgraph b_depsgraph(depsgraphptr);
  
        python_thread_state_save(&session->python_thread_state);
  
 -      session->synchronize();
 +      session->synchronize(b_depsgraph);
  
        python_thread_state_restore(&session->python_thread_state);
  
@@@ -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;
@@@ -772,10 -886,10 +890,10 @@@ static PyMethodDef methods[] = 
        {"exit", exit_func, METH_VARARGS, ""},
        {"create", create_func, METH_VARARGS, ""},
        {"free", free_func, METH_O, ""},
 -      {"render", render_func, METH_O, ""},
 +      {"render", render_func, METH_VARARGS, ""},
        {"bake", bake_func, METH_VARARGS, ""},
        {"draw", draw_func, METH_VARARGS, ""},
 -      {"sync", sync_func, METH_O, ""},
 +      {"sync", sync_func, METH_VARARGS, ""},
        {"reset", reset_func, METH_VARARGS, ""},
  #ifdef WITH_OSL
        {"osl_update_node", osl_update_node_func, METH_VARARGS, ""},
        {"opencl_disable", opencl_disable_func, METH_NOARGS, ""},
  #endif
  
+       /* Standalone denoising */
+       {"denoise", (PyCFunction)denoise_func, METH_VARARGS|METH_KEYWORDS, ""},
        /* Debugging routines */
        {"debug_flags_update", debug_flags_update_func, METH_VARARGS, ""},
        {"debug_flags_reset", debug_flags_reset_func, METH_NOARGS, ""},
@@@ -810,7 -927,7 +931,7 @@@ static struct PyModuleDef module = 
        "Blender cycles render integration",
        -1,
        methods,
 -      NULL, NULL, NULL, NULL
 +      NULL, NULL, NULL, NULL,
  };
  
  CCL_NAMESPACE_END
@@@ -30,7 -30,6 +30,7 @@@
  #include "render/shader.h"
  #include "render/stats.h"
  
 +#include "util/util_algorithm.h"
  #include "util/util_color.h"
  #include "util/util_foreach.h"
  #include "util/util_function.h"
@@@ -54,25 -53,23 +54,25 @@@ int BlenderSession::end_resumable_chun
  bool BlenderSession::print_render_stats = false;
  
  BlenderSession::BlenderSession(BL::RenderEngine& b_engine,
 -                               BL::UserPreferences& b_userpref,
 +                               BL::Preferences& b_userpref,
                                 BL::BlendData& b_data,
 -                               BL::Scene& b_scene)
 -: b_engine(b_engine),
 +                               bool preview_osl)
 +: session(NULL),
 +  sync(NULL),
 +  b_engine(b_engine),
    b_userpref(b_userpref),
    b_data(b_data),
    b_render(b_engine.render()),
 -  b_scene(b_scene),
 +  b_depsgraph(PointerRNA_NULL),
 +  b_scene(PointerRNA_NULL),
    b_v3d(PointerRNA_NULL),
    b_rv3d(PointerRNA_NULL),
 +  width(0),
 +  height(0),
 +  preview_osl(preview_osl),
    python_thread_state(NULL)
  {
        /* offline render */
 -
 -      width = render_resolution_x(b_render);
 -      height = render_resolution_y(b_render);
 -
        background = true;
        last_redraw_time = 0.0;
        start_resize_time = 0.0;
  }
  
  BlenderSession::BlenderSession(BL::RenderEngine& b_engine,
 -                               BL::UserPreferences& b_userpref,
 +                               BL::Preferences& b_userpref,
                                 BL::BlendData& b_data,
 -                               BL::Scene& b_scene,
                                 BL::SpaceView3D& b_v3d,
                                 BL::RegionView3D& b_rv3d,
                                 int width, int height)
 -: b_engine(b_engine),
 +: session(NULL),
 +  sync(NULL),
 +  b_engine(b_engine),
    b_userpref(b_userpref),
    b_data(b_data),
 -  b_render(b_scene.render()),
 -  b_scene(b_scene),
 +  b_render(b_engine.render()),
 +  b_depsgraph(PointerRNA_NULL),
 +  b_scene(PointerRNA_NULL),
    b_v3d(b_v3d),
    b_rv3d(b_rv3d),
    width(width),
    height(height),
 +  preview_osl(false),
    python_thread_state(NULL)
  {
        /* 3d view render */
 -
        background = false;
        last_redraw_time = 0.0;
        start_resize_time = 0.0;
@@@ -138,7 -133,6 +138,7 @@@ void BlenderSession::create_session(
  
        /* create scene */
        scene = new Scene(scene_params, session->device);
 +      scene->name = b_scene.name();
  
        /* setup callbacks for builtin image support */
        scene->image_manager->builtin_image_info_cb = function_bind(&BlenderSession::builtin_image_info, this, _1, _2, _3);
  
        session->scene = scene;
  
 +      /* There is no single depsgraph to use for the entire render.
 +       * So we need to handle this differently.
 +       *
 +       * We could loop over the final render result render layers in pipeline and keep Cycles unaware of multiple layers,
 +       * or perhaps move syncing further down in the pipeline.
 +       */
        /* create sync */
        sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
        BL::Object b_camera_override(b_engine.camera_override());
        if(b_v3d) {
 -              if(session_pause == false) {
 -                      /* full data sync */
 -                      sync->sync_view(b_v3d, b_rv3d, width, height);
 -                      sync->sync_data(b_render,
 -                                      b_v3d,
 -                                      b_camera_override,
 -                                      width, height,
 -                                      &python_thread_state,
 -                                      b_rlay_name.c_str());
 -              }
 +              sync->sync_view(b_v3d, b_rv3d, width, height);
        }
        else {
 -              /* for final render we will do full data sync per render layer, only
 -               * do some basic syncing here, no objects or materials for speed */
 -              sync->sync_render_layers(b_v3d, NULL);
 -              sync->sync_integrator();
                sync->sync_camera(b_render, b_camera_override, width, height, "");
        }
  
        update_resumable_tile_manager(session_params.samples);
  }
  
 -void BlenderSession::reset_session(BL::BlendData& b_data_, BL::Scene& b_scene_)
 +void BlenderSession::reset_session(BL::BlendData& b_data, BL::Depsgraph& b_depsgraph)
  {
 -      b_data = b_data_;
 -      b_render = b_engine.render();
 -      b_scene = b_scene_;
 +      this->b_data = b_data;
 +      this->b_depsgraph = b_depsgraph;
 +      this->b_scene = b_depsgraph.scene_eval();
 +
 +      if(preview_osl) {
 +              PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
 +              RNA_boolean_set(&cscene, "shading_system", preview_osl);
 +      }
 +
 +      if(b_v3d) {
 +              this->b_render = b_scene.render();
 +      }
 +      else {
 +              this->b_render = b_engine.render();
 +              width = render_resolution_x(b_render);
 +              height = render_resolution_y(b_render);
 +      }
 +
 +      if(session == NULL) {
 +              create();
 +      }
 +
 +      if(b_v3d) {
 +              /* NOTE: We need to create session, but all the code from below
 +               * will make viewport render to stuck on initialization.
 +               */
 +              return;
 +      }
  
        SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
        SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
  
 -      width = render_resolution_x(b_render);
 -      height = render_resolution_y(b_render);
 -
        if(scene->params.modified(scene_params) ||
           session->params.modified(session_params) ||
           !scene_params.persistent_data)
                /* if scene or session parameters changed, it's easier to simply re-create
                 * them rather than trying to distinguish which settings need to be updated
                 */
 -
 -              delete session;
 -
 +              free_session();
                create_session();
 -
                return;
        }
  
         */
        session->stats.mem_peak = session->stats.mem_used;
  
 +      /* There is no single depsgraph to use for the entire render.
 +       * See note on create_session().
 +       */
        /* sync object should be re-created */
        sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
  
 -      /* for final render we will do full data sync per render layer, only
 -       * do some basic syncing here, no objects or materials for speed */
 -      BL::Object b_camera_override(b_engine.camera_override());
 -      sync->sync_render_layers(b_v3d, NULL);
 -      sync->sync_integrator();
 -      sync->sync_camera(b_render, b_camera_override, width, height, "");
 -
        BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL);
        BL::RegionView3D b_null_region_view3d(PointerRNA_NULL);
        BufferParams buffer_params = BlenderSync::get_buffer_params(b_render,
@@@ -393,32 -379,8 +393,32 @@@ static void add_cryptomatte_layer(BL::R
        render_add_metadata(b_rr, prefix+"manifest", manifest);
  }
  
 -void BlenderSession::render()
 +void BlenderSession::stamp_view_layer_metadata_do(const string& prefix)
 +{
 +      BL::RenderResult b_rr = b_engine.get_result();
 +      /* Configured number of samples for the view layer. */
 +      b_rr.stamp_data_add_field((prefix + "samples").c_str(),
 +                                to_string(session->params.samples).c_str());
 +      /* Store ranged samples information. */
 +      if(session->tile_manager.range_num_samples != -1) {
 +              b_rr.stamp_data_add_field(
 +                      (prefix + "range_start_sample").c_str(),
 +                      to_string(session->tile_manager.range_start_sample).c_str());
 +              b_rr.stamp_data_add_field(
 +                      (prefix + "range_num_samples").c_str(),
 +                      to_string(session->tile_manager.range_num_samples).c_str());
 +      }
 +}
 +
 +void BlenderSession::stamp_view_layer_metadata(const string& view_layer_name)
 +{
 +      stamp_view_layer_metadata_do("cycles." + view_layer_name + ".");
 +}
 +
 +void BlenderSession::render(BL::Depsgraph& b_depsgraph_)
  {
 +      b_depsgraph = b_depsgraph_;
 +
        /* set callback to write out render results */
        session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
        session->update_render_tile_cb = function_bind(&BlenderSession::update_render_tile, this, _1, _2);
        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;
                        break;
        }
  
 +      stamp_view_layer_metadata(b_rlay_name);
 +
 +      /* Write cryptomatte metadata. */
 +      if(scene->film->cryptomatte_passes & CRYPT_OBJECT) {
 +              add_cryptomatte_layer(b_rr, b_rlay_name+".CryptoObject",
 +                                                        scene->object_manager->get_cryptomatte_objects(scene));
 +      }
 +      if(scene->film->cryptomatte_passes & CRYPT_MATERIAL) {
 +              add_cryptomatte_layer(b_rr, b_rlay_name+".CryptoMaterial",
 +                                                        scene->shader_manager->get_cryptomatte_materials(scene));
 +      }
 +      if(scene->film->cryptomatte_passes & CRYPT_ASSET) {
 +              add_cryptomatte_layer(b_rr, b_rlay_name+".CryptoAsset",
 +                                                        scene->object_manager->get_cryptomatte_assets(scene));
 +      }
 +
 +      /* free result without merging */
 +      end_render_result(b_engine, b_rr, true, true, false);
 +
        double total_time, render_time;
        session->progress.get_time(total_time, render_time);
        VLOG(1) << "Total render time: " << total_time;
        session->write_render_tile_cb = function_null;
        session->update_render_tile_cb = function_null;
  
 +      /* TODO: find a way to clear this data for persistent data render */
 +#if 0
        /* free all memory used (host and device), so we wouldn't leave render
         * engine with extra memory allocated
         */
  
        delete sync;
        sync = NULL;
 +#endif
  }
  
  static void populate_bake_data(BakeData *data, const
@@@ -631,8 -591,7 +627,8 @@@ static int bake_pass_filter_get(const i
        return flag;
  }
  
 -void BlenderSession::bake(BL::Object& b_object,
 +void BlenderSession::bake(BL::Depsgraph& b_depsgraph_,
 +                          BL::Object& b_object,
                            const string& pass_type,
                            const int pass_filter,
                            const int object_id,
                            const int /*depth*/,
                            float result[])
  {
 +      b_depsgraph = b_depsgraph_;
 +
        ShaderEvalType shader_type = get_shader_type(pass_type);
  
        /* Set baking flag in advance, so kernel loading can check if we need
                BL::Object b_camera_override(b_engine.camera_override());
                sync->sync_camera(b_render, b_camera_override, width, height, "");
                sync->sync_data(b_render,
 -                                              b_v3d,
 -                                              b_camera_override,
 -                                              width, height,
 -                                              &python_thread_state,
 -                                              b_rlay_name.c_str());
 +                              b_depsgraph,
 +                              b_v3d,
 +                              b_camera_override,
 +                              width, height,
 +                              &python_thread_state);
 +              builtin_images_load();
        }
  
        BakeData *bake_data = NULL;
                        }
                }
  
 -              int object = object_index;
 +              /* Object might have been disabled for rendering or excluded in some
 +               * other way, in that case Blender will report a warning afterwards. */
 +              if (object_index != OBJECT_NONE) {
 +                      int object = object_index;
  
 -              bake_data = scene->bake_manager->init(object, tri_offset, num_pixels);
 -              populate_bake_data(bake_data, object_id, pixel_array, num_pixels);
 +                      bake_data = scene->bake_manager->init(object, tri_offset, num_pixels);
 +                      populate_bake_data(bake_data, object_id, pixel_array, num_pixels);
 +              }
  
                /* set number of samples */
                session->tile_manager.set_samples(session_params.samples);
        }
  
        /* Perform bake. Check cancel to avoid crash with incomplete scene data. */
 -      if(!session->progress.get_cancel()) {
 +      if(!session->progress.get_cancel() && bake_data) {
                scene->bake_manager->bake(scene->device, &scene->dscene, scene, session->progress, shader_type, bake_pass_filter, bake_data, result);
        }
  
@@@ -818,7 -770,7 +814,7 @@@ void BlenderSession::update_render_resu
        do_write_update_render_result(b_rr, b_rlay, rtile, true);
  }
  
 -void BlenderSession::synchronize()
 +void BlenderSession::synchronize(BL::Depsgraph& b_depsgraph_)
  {
        /* only used for viewport render */
        if(!b_v3d)
  
        /* copy recalc flags, outside of mutex so we can decide to do the real
         * synchronization at a later time to not block on running updates */
 -      sync->sync_recalc();
 +      sync->sync_recalc(b_depsgraph_);
  
        /* don't do synchronization if on pause */
        if(session_pause) {
        }
  
        /* data and camera synchronize */
 +      b_depsgraph = b_depsgraph_;
 +
        BL::Object b_camera_override(b_engine.camera_override());
        sync->sync_data(b_render,
 +                      b_depsgraph,
                        b_v3d,
                        b_camera_override,
                        width, height,
 -                      &python_thread_state,
 -                      b_rlay_name.c_str());
 +                      &python_thread_state);
  
        if(b_rv3d)
                sync->sync_view(b_v3d, b_rv3d, width, height);
        else
                sync->sync_camera(b_render, b_camera_override, width, height, "");
  
 +      builtin_images_load();
 +
        /* unlock */
        session->scene->mutex.unlock();
  
@@@ -991,7 -939,7 +987,7 @@@ void BlenderSession::update_bake_progre
  void BlenderSession::update_status_progress()
  {
        string timestatus, status, substatus;
 -      string scene = "";
 +      string scene_status = "";
        float progress;
        double total_time, remaining_time = 0, render_time;
        char time_str[128];
                remaining_time = (1.0 - (double)progress) * (render_time / (double)progress);
  
        if(background) {
 -              scene += " | " + b_scene.name();
 +              scene_status += " | " + scene->name;
                if(b_rlay_name != "")
 -                      scene += ", "  + b_rlay_name;
 +                      scene_status += ", "  + b_rlay_name;
  
                if(b_rview_name != "")
 -                      scene += ", " + b_rview_name;
 -      }
 -      else {
 -              BLI_timecode_string_from_time_simple(time_str, sizeof(time_str), total_time);
 -              timestatus = "Time:" + string(time_str) + " | ";
 -      }
 +                      scene_status += ", " + b_rview_name;
  
 -      if(remaining_time > 0) {
 -              BLI_timecode_string_from_time_simple(time_str, sizeof(time_str), remaining_time);
 -              timestatus += "Remaining:" + string(time_str) + " | ";
 -      }
 +              if(remaining_time > 0) {
 +                      BLI_timecode_string_from_time_simple(time_str, sizeof(time_str), remaining_time);
 +                      timestatus += "Remaining:" + string(time_str) + " | ";
 +              }
  
 -      timestatus += string_printf("Mem:%.2fM, Peak:%.2fM", (double)mem_used, (double)mem_peak);
 +              timestatus += string_printf("Mem:%.2fM, Peak:%.2fM", (double)mem_used, (double)mem_peak);
  
 -      if(status.size() > 0)
 -              status = " | " + status;
 -      if(substatus.size() > 0)
 -              status += " | " + substatus;
 +              if(status.size() > 0)
 +                      status = " | " + status;
 +              if(substatus.size() > 0)
 +                      status += " | " + substatus;
 +      }
  
        double current_time = time_dt();
        /* When rendering in a window, redraw the status at least once per second to keep the elapsed and remaining time up-to-date.
         * For headless rendering, only report when something significant changes to keep the console output readable. */
        if(status != last_status || (!headless && (current_time - last_status_time) > 1.0)) {
 -              b_engine.update_stats("", (timestatus + scene + status).c_str());
 +              b_engine.update_stats("", (timestatus + scene_status + status).c_str());
                b_engine.update_memory_stats(mem_used, mem_peak);
                last_status = status;
                last_status_time = current_time;
@@@ -1382,9 -1334,6 +1378,9 @@@ bool BlenderSession::builtin_image_floa
                fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n");
        }
        else {
 +              /* We originally were passing view_layer here but in reality we need a
 +               * a depsgraph to pass to the RE_point_density_minmax() function.
 +               */
                /* TODO(sergey): Check we're indeed in shader node tree. */
                PointerRNA ptr;
                RNA_pointer_create(NULL, &RNA_Node, builtin_data, &ptr);
                if(b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
                        BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
                        int length;
 -                      int settings = background ? 1 : 0;  /* 1 - render settings, 0 - vewport settings. */
 -                      b_point_density_node.calc_point_density(b_scene, settings, &length, &pixels);
 +                      b_point_density_node.calc_point_density(b_depsgraph, &length, &pixels);
                }
        }
  
        return false;
  }
  
 +void BlenderSession::builtin_images_load()
 +{
 +      /* Force builtin images to be loaded along with Blender data sync. This
 +       * is needed because we may be reading from depsgraph evaluated data which
 +       * can be freed by Blender before Cycles reads it. */
 +      ImageManager *manager = session->scene->image_manager;
 +      Device *device = session->device;
 +      manager->device_load_builtin(device, session->scene, session->progress);
 +}
 +
  void BlenderSession::update_resumable_tile_manager(int num_samples)
  {
        const int num_resumable_chunks = BlenderSession::num_resumable_chunks,
                return;
        }
  
 -      const int num_samples_per_chunk = (int)ceilf((float)num_samples / num_resumable_chunks);
 +      if (num_resumable_chunks > num_samples) {
 +              fprintf(stderr, "Cycles warning: more sample chunks (%d) than samples (%d), "
 +                      "this will cause some samples to be included in multiple chunks.\n",
 +                      num_resumable_chunks, num_samples);
 +      }
 +
 +      const float num_samples_per_chunk = (float)num_samples / num_resumable_chunks;
  
 -      int range_start_sample, range_num_samples;
 +      float range_start_sample, range_num_samples;
        if(current_resumable_chunk != 0) {
                /* Single chunk rendering. */
                range_start_sample = num_samples_per_chunk * (current_resumable_chunk - 1);
                range_start_sample = num_samples_per_chunk * (start_resumable_chunk - 1);
                range_num_samples = num_chunks * num_samples_per_chunk;
        }
 +
 +      /* Round after doing the multiplications with num_chunks and num_samples_per_chunk
 +       * to allow for many small chunks. */
 +      int rounded_range_start_sample = (int)floor(range_start_sample + 0.5f);
 +      int rounded_range_num_samples = max((int)floor(range_num_samples + 0.5f), 1);
 +
        /* Make sure we don't overshoot. */
 -      if(range_start_sample + range_num_samples > num_samples) {
 -              range_num_samples = num_samples - range_num_samples;
 +      if(rounded_range_start_sample + rounded_range_num_samples > num_samples) {
 +              rounded_range_num_samples = num_samples - rounded_range_num_samples;
        }
  
        VLOG(1) << "Samples range start is " << range_start_sample << ", "
                << "number of samples to render is " << range_num_samples;
  
 -      scene->integrator->start_sample = range_start_sample;
 +      scene->integrator->start_sample = rounded_range_start_sample;
        scene->integrator->tag_update(scene);
  
 -      session->tile_manager.range_start_sample = range_start_sample;
 -      session->tile_manager.range_num_samples = range_num_samples;
 +      session->tile_manager.range_start_sample = rounded_range_start_sample;
 +      session->tile_manager.range_num_samples = rounded_range_num_samples;
 +}
 +
 +void BlenderSession::free_blender_memory_if_possible()
 +{
 +      if(!background) {
 +              /* During interactive render we can not free anything: attempts to save
 +               * memory would cause things to be allocated and evaluated for every
 +               * updated sample.
 +               */
 +              return;
 +      }
 +      b_engine.free_blender_memory();
  }
  
  CCL_NAMESPACE_END
@@@ -29,6 -29,7 +29,7 @@@
  
  #include "device/device.h"
  
+ #include "blender/blender_device.h"
  #include "blender/blender_sync.h"
  #include "blender/blender_session.h"
  #include "blender/blender_util.h"
@@@ -78,12 -79,31 +79,12 @@@ BlenderSync::~BlenderSync(
  
  /* Sync */
  
 -bool BlenderSync::sync_recalc()
 +void BlenderSync::sync_recalc(BL::Depsgraph& b_depsgraph)
  {
 -      /* sync recalc flags from blender to cycles. actual update is done separate,
 -       * so we can do it later on if doing it immediate is not suitable */
 -
 -      BL::BlendData::materials_iterator b_mat;
 -      bool has_updated_objects = b_data.objects.is_updated();
 -      for(b_data.materials.begin(b_mat); b_mat != b_data.materials.end(); ++b_mat) {
 -              if(b_mat->is_updated() || (b_mat->node_tree() && b_mat->node_tree().is_updated())) {
 -                      shader_map.set_recalc(*b_mat);
 -              }
 -              else {
 -                      Shader *shader = shader_map.find(*b_mat);
 -                      if(has_updated_objects && shader != NULL && shader->has_object_dependency) {
 -                              shader_map.set_recalc(*b_mat);
 -                      }
 -              }
 -      }
 -
 -      BL::BlendData::lamps_iterator b_lamp;
 -
 -      for(b_data.lamps.begin(b_lamp); b_lamp != b_data.lamps.end(); ++b_lamp)
 -              if(b_lamp->is_updated() || (b_lamp->node_tree() && b_lamp->node_tree().is_updated()))
 -                      shader_map.set_recalc(*b_lamp);
 +      /* Sync recalc flags from blender to cycles. Actual update is done separate,
 +       * so we can do it later on if doing it immediate is not suitable. */
  
 +      bool has_updated_objects = b_depsgraph.id_type_updated(BL::DriverTarget::id_type_OBJECT);
        bool dicing_prop_changed = false;
  
        if(experimental) {
                }
        }
  
 -      BL::BlendData::objects_iterator b_ob;
 +      /* Iterate over all IDs in this depsgraph. */
 +      BL::Depsgraph::updates_iterator b_update;
 +      for(b_depsgraph.updates.begin(b_update); b_update != b_depsgraph.updates.end(); ++b_update) {
 +              BL::ID b_id(b_update->id());
  
 -      for(b_data.objects.begin(b_ob); b_ob != b_data.objects.end(); ++b_ob) {
 -              if(b_ob->is_updated()) {
 -                      object_map.set_recalc(*b_ob);
 -                      light_map.set_recalc(*b_ob);
 +              /* Material */
 +              if(b_id.is_a(&RNA_Material)) {
 +                      BL::Material b_mat(b_id);
 +                      shader_map.set_recalc(b_mat);
 +              }
 +              /* Light */
 +              else if(b_id.is_a(&RNA_Light)) {
 +                      BL::Light b_light(b_id);
 +                      shader_map.set_recalc(b_light);
                }
 +              /* Object */
 +              else if(b_id.is_a(&RNA_Object)) {
 +                      BL::Object b_ob(b_id);
 +                      const bool updated_geometry = b_update->is_updated_geometry();
 +
 +                      if(b_update->is_updated_transform()) {
 +                              object_map.set_recalc(b_ob);
 +                              light_map.set_recalc(b_ob);
 +                      }
  
 -              if(object_is_mesh(*b_ob)) {
 -                      if(b_ob->is_updated_data() || b_ob->data().is_updated() ||
 -                         (dicing_prop_changed && object_subdivision_type(*b_ob, preview, experimental) != Mesh::SUBDIVISION_NONE))
 -                      {
 -                              BL::ID key = BKE_object_is_modified(*b_ob)? *b_ob: b_ob->data();
 -                              mesh_map.set_recalc(key);
 +                      if(object_is_mesh(b_ob)) {
 +                              if(updated_geometry ||
 +                                 (dicing_prop_changed && object_subdivision_type(b_ob, preview, experimental) != Mesh::SUBDIVISION_NONE))
 +                              {
 +                                      BL::ID key = BKE_object_is_modified(b_ob)? b_ob: b_ob.data();
 +                                      mesh_map.set_recalc(key);
 +                              }
 +                      }
 +                      else if(object_is_light(b_ob)) {
 +                              if(updated_geometry) {
 +                                      light_map.set_recalc(b_ob);
 +                              }
 +                      }
 +
 +                      if(updated_geometry) {
 +                              BL::Object::particle_systems_iterator b_psys;
 +                              for(b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); ++b_psys)
 +                                      particle_system_map.set_recalc(b_ob);
                        }
                }
 -              else if(object_is_light(*b_ob)) {
 -                      if(b_ob->is_updated_data() || b_ob->data().is_updated())
 -                              light_map.set_recalc(*b_ob);
 +              /* Mesh */
 +              else if(b_id.is_a(&RNA_Mesh)) {
 +                      BL::Mesh b_mesh(b_id);
 +                      mesh_map.set_recalc(b_mesh);
                }
 -
 -              if(b_ob->is_updated_data()) {
 -                      BL::Object::particle_systems_iterator b_psys;
 -                      for(b_ob->particle_systems.begin(b_psys); b_psys != b_ob->particle_systems.end(); ++b_psys)
 -                              particle_system_map.set_recalc(*b_ob);
 +              /* World */
 +              else if(b_id.is_a(&RNA_World)) {
 +                      BL::World b_world(b_id);
 +                      if(world_map == b_world.ptr.data) {
 +                              world_recalc = true;
 +                      }
                }
        }
  
 -      BL::BlendData::meshes_iterator b_mesh;
 -
 -      for(b_data.meshes.begin(b_mesh); b_mesh != b_data.meshes.end(); ++b_mesh) {
 -              if(b_mesh->is_updated()) {
 -                      mesh_map.set_recalc(*b_mesh);
 +      /* Updates shader with object dependency if objects changed. */
 +      if(has_updated_objects) {
 +              if(scene->default_background->has_object_dependency) {
 +                      world_recalc = true;
                }
 -      }
  
 -      BL::BlendData::worlds_iterator b_world;
 -
 -      for(b_data.worlds.begin(b_world); b_world != b_data.worlds.end(); ++b_world) {
 -              if(world_map == b_world->ptr.data) {
 -                      if(b_world->is_updated() ||
 -                         (b_world->node_tree() && b_world->node_tree().is_updated()))
 -                      {
 -                              world_recalc = true;
 -                      }
 -                      else if(b_world->node_tree() && b_world->use_nodes()) {
 -                              Shader *shader = scene->default_background;
 -                              if(has_updated_objects && shader->has_object_dependency) {
 -                                      world_recalc = true;
 -                              }
 +              foreach(Shader *shader, scene->shaders) {
 +                      if(shader->has_object_dependency) {
 +                              shader->need_sync_object = true;
                        }
                }
        }
 -
 -      bool recalc =
 -              shader_map.has_recalc() ||
 -              object_map.has_recalc() ||
 -              light_map.has_recalc() ||
 -              mesh_map.has_recalc() ||
 -              particle_system_map.has_recalc() ||
 -              BlendDataObjects_is_updated_get(&b_data.ptr) ||
 -              world_recalc;
 -
 -      return recalc;
  }
  
  void BlenderSync::sync_data(BL::RenderSettings& b_render,
 +                            BL::Depsgraph& b_depsgraph,
                              BL::SpaceView3D& b_v3d,
                              BL::Object& b_override,
                              int width, int height,
 -                            void **python_thread_state,
 -                            const char *layer)
 +                            void **python_thread_state)
  {
 -      sync_render_layers(b_v3d, layer);
 +      BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
 +
 +      sync_view_layer(b_v3d, b_view_layer);
        sync_integrator();
        sync_film();
 -      sync_shaders();
 +      sync_shaders(b_depsgraph);
        sync_images();
        sync_curve_settings();
  
           scene->need_motion() == Scene::MOTION_NONE ||
           scene->camera->motion_position == Camera::MOTION_POSITION_CENTER)
        {
 -              sync_objects();
 +              sync_objects(b_depsgraph);
        }
        sync_motion(b_render,
 +                  b_depsgraph,
                    b_override,
                    width, height,
                    python_thread_state);
  
        mesh_synced.clear();
 +
 +      free_data_after_sync(b_depsgraph);
  }
  
  /* Integrator */
@@@ -364,33 -372,75 +365,33 @@@ void BlenderSync::sync_film(
  
  /* Render Layer */
  
 -void BlenderSync::sync_render_layers(BL::SpaceView3D& b_v3d, const char *layer)
 +void BlenderSync::sync_view_layer(BL::SpaceView3D& /*b_v3d*/, BL::ViewLayer& b_view_layer)
  {
 -      PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
 -      string layername;
 -
 -      /* 3d view */
 -      if(b_v3d) {
 -              if(RNA_boolean_get(&cscene, "preview_active_layer")) {
 -                      BL::RenderLayers layers(b_scene.render().ptr);
 -                      layername = layers.active().name();
 -                      layer = layername.c_str();
 -              }
 -              else {
 -                      render_layer.scene_layer = get_layer(b_v3d.layers(), b_v3d.layers_local_view());
 -                      render_layer.layer = render_layer.scene_layer;
 -                      render_layer.exclude_layer = 0;
 -                      render_layer.holdout_layer = 0;
 -                      render_layer.material_override = PointerRNA_NULL;
 -                      render_layer.use_background_shader = true;
 -                      render_layer.use_background_ao = true;
 -                      render_layer.use_hair = true;
 -                      render_layer.use_surfaces = true;
 -                      render_layer.use_viewport_visibility = true;
 -                      render_layer.samples = 0;
 -                      render_layer.bound_samples = false;
 -                      return;
 -              }
 -      }
 -
        /* render layer */
 -      BL::RenderSettings r = b_scene.render();
 -      BL::RenderSettings::layers_iterator b_rlay;
 +      view_layer.name = b_view_layer.name();
 +      view_layer.use_background_shader = b_view_layer.use_sky();
 +      view_layer.use_background_ao = b_view_layer.use_ao();
 +      view_layer.use_surfaces = b_view_layer.use_solid();
 +      view_layer.use_hair = b_view_layer.use_strand();
 +
 +      /* Material override. */
 +      view_layer.material_override = b_view_layer.material_override();
 +
 +      /* Sample override. */
 +      PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
        int use_layer_samples = get_enum(cscene, "use_layer_samples");
 -      bool first_layer = true;
 -      uint layer_override = get_layer(b_engine.layer_override());
 -      uint scene_layers = layer_override ? layer_override : get_layer(b_scene.layers());
 -
 -      for(r.layers.begin(b_rlay); b_rlay != r.layers.end(); ++b_rlay) {
 -              if((!layer && first_layer) || (layer && b_rlay->name() == layer)) {
 -                      render_layer.name = b_rlay->name();
 -
 -                      render_layer.holdout_layer = get_layer(b_rlay->layers_zmask());
 -                      render_layer.exclude_layer = get_layer(b_rlay->layers_exclude());
 -
 -                      render_layer.scene_layer = scene_layers & ~render_layer.exclude_layer;
 -                      render_layer.scene_layer |= render_layer.exclude_layer & render_layer.holdout_layer;
 -
 -                      render_layer.layer = get_layer(b_rlay->layers());
 -                      render_layer.layer |= render_layer.holdout_layer;
 -
 -                      render_layer.material_override = b_rlay->material_override();
 -                      render_layer.use_background_shader = b_rlay->use_sky();
 -                      render_layer.use_background_ao = b_rlay->use_ao();
 -                      render_layer.use_surfaces = b_rlay->use_solid();
 -                      render_layer.use_hair = b_rlay->use_strand();
 -                      render_layer.use_viewport_visibility = false;
 -
 -                      render_layer.bound_samples = (use_layer_samples == 1);
 -                      if(use_layer_samples != 2) {
 -                              int samples = b_rlay->samples();
 -                              if(get_boolean(cscene, "use_square_samples"))
 -                                      render_layer.samples = samples * samples;
 -                              else
 -                                      render_layer.samples = samples;
 -                      }
 -              }
  
 -              first_layer = false;
 +      view_layer.bound_samples = (use_layer_samples == 1);
 +      view_layer.samples = 0;
 +
 +      if(use_layer_samples != 2) {
 +              int samples = b_view_layer.samples();
 +              if(get_boolean(cscene, "use_square_samples"))
 +                      view_layer.samples = samples * samples;
 +              else
 +                      view_layer.samples = samples;
        }
 +
  }
  
  /* Images */
@@@ -503,7 -553,7 +504,7 @@@ int BlenderSync::get_denoising_pass(BL:
  }
  
  vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer& b_rlay,
 -                                             BL::SceneRenderLayer& b_srlay,
 +                                             BL::ViewLayer& b_view_layer,
                                               const SessionParams &session_params)
  {
        vector<Pass> passes;
                        Pass::add(pass_type, passes);
        }
  
 -      PointerRNA crp = RNA_pointer_get(&b_srlay.ptr, "cycles");
 +      PointerRNA crp = RNA_pointer_get(&b_view_layer.ptr, "cycles");
        bool full_denoising = get_boolean(crp, "use_denoising");
        bool write_denoising_passes = get_boolean(crp, "denoising_store_passes");
  
                MAP_OPTION("denoising_subsurface_direct",     DENOISING_CLEAN_SUBSURFACE_DIR);
                MAP_OPTION("denoising_subsurface_indirect",   DENOISING_CLEAN_SUBSURFACE_IND);
  #undef MAP_OPTION
 -              b_engine.add_pass("Noisy Image", 4, "RGBA", b_srlay.name().c_str());
 +              b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str());
        }
  
        if(write_denoising_passes) {
 -              b_engine.add_pass("Denoising Normal",          3, "XYZ", b_srlay.name().c_str());
 -              b_engine.add_pass("Denoising Albedo",          3, "RGB", b_srlay.name().c_str());
 -              b_engine.add_pass("Denoising Depth",           1, "Z",   b_srlay.name().c_str());
 -              b_engine.add_pass("Denoising Shadowing",       1, "X",   b_srlay.name().c_str());
 -              b_engine.add_pass("Denoising Variance",        3, "RGB", b_srlay.name().c_str());
 -              b_engine.add_pass("Denoising Intensity",       1, "X",   b_srlay.name().c_str());
 +              b_engine.add_pass("Denoising Normal",          3, "XYZ", b_view_layer.name().c_str());
 +              b_engine.add_pass("Denoising Albedo",          3, "RGB", b_view_layer.name().c_str());
 +              b_engine.add_pass("Denoising Depth",           1, "Z",   b_view_layer.name().c_str());
 +              b_engine.add_pass("Denoising Shadowing",       1, "X",   b_view_layer.name().c_str());
 +              b_engine.add_pass("Denoising Variance",        3, "RGB", b_view_layer.name().c_str());
 +              b_engine.add_pass("Denoising Intensity",       1, "X",   b_view_layer.name().c_str());
  
                if(scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES) {
 -                      b_engine.add_pass("Denoising Clean",   3, "RGB", b_srlay.name().c_str());
 +                      b_engine.add_pass("Denoising Clean",   3, "RGB", b_view_layer.name().c_str());
                }
        }
  #ifdef __KERNEL_DEBUG__
        if(get_boolean(crp, "pass_debug_bvh_traversed_nodes")) {
 -              b_engine.add_pass("Debug BVH Traversed Nodes", 1, "X", b_srlay.name().c_str());
 +              b_engine.add_pass("Debug BVH Traversed Nodes", 1, "X", b_view_layer.name().c_str());
                Pass::add(PASS_BVH_TRAVERSED_NODES, passes);
        }
        if(get_boolean(crp, "pass_debug_bvh_traversed_instances")) {
 -              b_engine.add_pass("Debug BVH Traversed Instances", 1, "X", b_srlay.name().c_str());
 +              b_engine.add_pass("Debug BVH Traversed Instances", 1, "X", b_view_layer.name().c_str());
                Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes);
        }
        if(get_boolean(crp, "pass_debug_bvh_intersections")) {
 -              b_engine.add_pass("Debug BVH Intersections", 1, "X", b_srlay.name().c_str());
 +              b_engine.add_pass("Debug BVH Intersections", 1, "X", b_view_layer.name().c_str());
                Pass::add(PASS_BVH_INTERSECTIONS, passes);
        }
        if(get_boolean(crp, "pass_debug_ray_bounces")) {
 -              b_engine.add_pass("Debug Ray Bounces", 1, "X", b_srlay.name().c_str());
 +              b_engine.add_pass("Debug Ray Bounces", 1, "X", b_view_layer.name().c_str());
                Pass::add(PASS_RAY_BOUNCES, passes);
        }
  #endif
        if(get_boolean(crp, "pass_debug_render_time")) {
 -              b_engine.add_pass("Debug Render Time", 1, "X", b_srlay.name().c_str());
 +              b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str());
                Pass::add(PASS_RENDER_TIME, passes);
        }
        if(get_boolean(crp, "use_pass_volume_direct")) {
 -              b_engine.add_pass("VolumeDir", 3, "RGB", b_srlay.name().c_str());
 +              b_engine.add_pass("VolumeDir", 3, "RGB", b_view_layer.name().c_str());
                Pass::add(PASS_VOLUME_DIRECT, passes);
        }
        if(get_boolean(crp, "use_pass_volume_indirect")) {
 -              b_engine.add_pass("VolumeInd", 3, "RGB", b_srlay.name().c_str());
 +              b_engine.add_pass("VolumeInd", 3, "RGB", b_view_layer.name().c_str());
                Pass::add(PASS_VOLUME_INDIRECT, passes);
        }
  
        if(get_boolean(crp, "use_pass_crypto_object")) {
                for(int i = 0; i < crypto_depth; ++i) {
                        string passname = cryptomatte_prefix + string_printf("Object%02d", i);
 -                      b_engine.add_pass(passname.c_str(), 4, "RGBA", b_srlay.name().c_str());
 +                      b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
                        Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str());
                }
                scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes | CRYPT_OBJECT);
        if(get_boolean(crp, "use_pass_crypto_material")) {
                for(int i = 0; i < crypto_depth; ++i) {
                        string passname = cryptomatte_prefix + string_printf("Material%02d", i);
 -                      b_engine.add_pass(passname.c_str(), 4, "RGBA", b_srlay.name().c_str());
 +                      b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
                        Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str());
                }
                scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes | CRYPT_MATERIAL);
        if(get_boolean(crp, "use_pass_crypto_asset")) {
                for(int i = 0; i < crypto_depth; ++i) {
                        string passname = cryptomatte_prefix + string_printf("Asset%02d", i);
 -                      b_engine.add_pass(passname.c_str(), 4, "RGBA", b_srlay.name().c_str());
 +                      b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
                        Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str());
                }
                scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes | CRYPT_ASSET);
        return passes;
  }
  
 +void BlenderSync::free_data_after_sync(BL::Depsgraph& b_depsgraph)
 +{
 +      /* When viewport display is not needed during render we can force some
 +       * caches to be releases from blender side in order to reduce peak memory
 +       * footprint during synchronization process.
 +       */
 +      const bool is_interface_locked = b_engine.render() &&
 +                                       b_engine.render().use_lock_interface();
 +      const bool can_free_caches = BlenderSession::headless || is_interface_locked;
 +      if(!can_free_caches) {
 +              return;
 +      }
 +      /* TODO(sergey): We can actually remove the whole dependency graph,
 +       * but that will need some API support first.
 +       */
 +      BL::Depsgraph::objects_iterator b_ob;
 +      for(b_depsgraph.objects.begin(b_ob);
 +          b_ob != b_depsgraph.objects.end();
 +          ++b_ob)
 +      {
 +              b_ob->cache_release();
 +      }
 +}
 +
  /* Scene Parameters */
  
  SceneParams BlenderSync::get_scene_params(BL::Scene& b_scene,
@@@ -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)
  {
        /* feature set */
        params.experimental = (get_enum(cscene, "feature_set") != 0);
  
-       /* threads */
-       BL::RenderSettings b_r = b_scene.render();
-       if(b_r.threads_mode() == BL::RenderSettings::threads_mode_FIXED)
-               params.threads = b_r.threads();
-       else
-               params.threads = 0;
        /* 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) {
 -              BL::RenderSettings::layers_iterator b_rlay;
 -              for(b_r.layers.begin(b_rlay); b_rlay != b_r.layers.end(); ++b_rlay) {
 -                      PointerRNA crl = RNA_pointer_get(&b_rlay->ptr, "cycles");
 +              BL::Scene::view_layers_iterator b_view_layer;
 +              for(b_scene.view_layers.begin(b_view_layer); b_view_layer != b_scene.view_layers.end(); ++b_view_layer) {
 +                      PointerRNA crl = RNA_pointer_get(&b_view_layer->ptr, "cycles");
                        if(get_boolean(crl, "use_denoising")) {
                                params.progressive_refine = false;
                        }
                params.shadingsystem = SHADINGSYSTEM_OSL;
  
        /* color managagement */
 -#ifdef GLEW_MX
 -      /* When using GLEW MX we need to check whether we've got an OpenGL
 -       * context for current window. This is because command line rendering
 -       * doesn't have OpenGL context actually.
 -       */
 -      if(glewGetContext() != NULL)
 -#endif
 -      {
 -              params.display_buffer_linear = GLEW_ARB_half_float_pixel &&
 -                                             b_engine.support_display_space_shader(b_scene);
 -      }
 +      params.display_buffer_linear = b_engine.support_display_space_shader(b_scene);
  
        if(b_engine.is_preview()) {
                /* For preview rendering we're using same timeout as
@@@ -927,9 -927,12 +927,12 @@@ void Session::update_status_time(bool s
                         */
                        substatus += string_printf(", Sample %d/%d", progress.get_current_sample(), num_samples);
                }
-               if(params.run_denoising) {
+               if(params.full_denoising) {
                        substatus += string_printf(", Denoised %d tiles", progress.get_denoised_tiles());
                }
+               else if(params.run_denoising) {
+                       substatus += string_printf(", Prefiltered %d tiles", progress.get_denoised_tiles());
+               }
        }
        else if(tile_manager.num_samples == INT_MAX)
                substatus = string_printf("Path Tracing Sample %d", progressive_sample+1);
                                          num_samples);
  
        if(show_pause) {
 -              status = "Paused";
 +              status = "Rendering Paused";
        }
        else if(show_done) {
 -              status = "Done";
 +              status = "Rendering Done";
                progress.set_end_time(); /* Save end time so that further calls to get_time are accurate. */
        }
        else {
@@@ -976,10 -979,7 +979,7 @@@ void Session::render(
        task.passes_size = tile_manager.params.get_passes_size();
  
        if(params.run_denoising) {
-               task.denoising_radius = params.denoising_radius;
-               task.denoising_strength = params.denoising_strength;
-               task.denoising_feature_strength = params.denoising_feature_strength;
-               task.denoising_relative_pca = params.denoising_relative_pca;
+               task.denoising = params.denoising;
  
                assert(!scene->film->need_update);
                task.pass_stride = scene->film->pass_stride;