Merge branch 'blender2.7'
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Tue, 19 Mar 2019 17:54:17 +0000 (18:54 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Tue, 19 Mar 2019 17:54:17 +0000 (18:54 +0100)
1  2 
intern/cycles/blender/addon/operators.py
intern/cycles/blender/blender_python.cpp
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_util.h

index 63c9868ca6ac209553c248756a09c1c10537425d,f8511dc8de4e32710de5ed9e1997002d0bb8c159..b53679bd3284d51718ef98e07ef4c923f4021bc5
@@@ -22,22 -22,22 +22,22 @@@ from bpy.props import StringPropert
  
  
  class CYCLES_OT_use_shading_nodes(Operator):
 -    """Enable nodes on a material, world or lamp"""
 +    """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, "lamp", False))
 +                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.lamp:
 -            context.lamp.use_nodes = True
 +        elif context.light:
 +            context.light.use_nodes = True
  
          return {'FINISHED'}
  
@@@ -49,13 -49,13 +49,13 @@@ class CYCLES_OT_denoise_animation(Opera
      bl_idname = "cycles.denoise_animation"
      bl_label = "Denoise Animation"
  
 -    input_filepath = StringProperty(
 +    input_filepath: StringProperty(
          name='Input Filepath',
          description='File path for image to denoise. If not specified, uses the render file path and frame range from the scene',
          default='',
          subtype='FILE_PATH')
  
 -    output_filepath = StringProperty(
 +    output_filepath: StringProperty(
          name='Output Filepath',
          description='If not specified, renders will be denoised in-place',
          default='',
@@@ -64,9 -64,9 +64,9 @@@
      def execute(self, context):
          import os
  
 -        preferences = context.user_preferences
 +        preferences = context.preferences
          scene = context.scene
 -        render_layer = scene.render.layers.active
 +        view_layer = context.view_layer
  
          in_filepath = self.input_filepath
          out_filepath = self.output_filepath
          try:
              _cycles.denoise(preferences.as_pointer(),
                              scene.as_pointer(),
 -                            render_layer.as_pointer(),
 +                            view_layer.as_pointer(),
                              input=in_filepaths,
                              output=out_filepaths)
          except Exception as e:
          return {'FINISHED'}
  
  
+ class CYCLES_OT_merge_images(Operator):
+     "Combine OpenEXR multilayer images rendered with different sample" \
+     "ranges into one image with reduced noise."
+     bl_idname = "cycles.merge_images"
+     bl_label = "Merge Images"
+     input_filepath1: StringProperty(
+         name='Input Filepath',
+         description='File path for image to merge',
+         default='',
+         subtype='FILE_PATH')
+     input_filepath2: StringProperty(
+         name='Input Filepath',
+         description='File path for image to merge',
+         default='',
+         subtype='FILE_PATH')
+     output_filepath: StringProperty(
+         name='Output Filepath',
+         description='File path for merged image',
+         default='',
+         subtype='FILE_PATH')
+     def execute(self, context):
+         in_filepaths = [self.input_filepath1, self.input_filepath2]
+         out_filepath = self.output_filepath
+         import _cycles
+         try:
+             _cycles.merge(input=in_filepaths, output=out_filepath)
+         except Exception as e:
+             self.report({'ERROR'}, str(e))
+             return {'FINISHED'}
+         return {'FINISHED'}
  classes = (
      CYCLES_OT_use_shading_nodes,
-     CYCLES_OT_denoise_animation
+     CYCLES_OT_denoise_animation,
+     CYCLES_OT_merge_images
  )
  
  def register():
index 4a1eeeb65c1789a38edcb6c8847a1c91b380e8ca,063b0ede92b46fb4db89b063209d7fb673b4c71f..ccd783073a3596a824844ff116e59cbc00aea094
@@@ -23,6 -23,7 +23,7 @@@
  #include "blender/blender_session.h"
  
  #include "render/denoising.h"
+ #include "render/merge.h"
  
  #include "util/util_debug.h"
  #include "util/util_foreach.h"
@@@ -193,10 -194,10 +194,10 @@@ static PyObject *exit_func(PyObject * /
  
  static PyObject *create_func(PyObject * /*self*/, PyObject *args)
  {
 -      PyObject *pyengine, *pyuserpref, *pydata, *pyscene, *pyregion, *pyv3d, *pyrv3d;
 +      PyObject *pyengine, *pypreferences, *pydata, *pyregion, *pyv3d, *pyrv3d;
        int preview_osl;
  
 -      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_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, 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, 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);
  }
  
@@@ -252,22 -271,13 +253,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);
  
@@@ -642,9 -639,8 +643,8 @@@ static PyObject *opencl_compile_func(Py
  }
  #endif
  
- static bool denoise_parse_filepaths(PyObject *pyfilepaths, vector<string>& filepaths)
+ static bool image_parse_filepaths(PyObject *pyfilepaths, vector<string>& filepaths)
  {
        if(PyUnicode_Check(pyfilepaths)) {
                const char *filepath = PyUnicode_AsUTF8(pyfilepaths);
                filepaths.push_back(filepath);
@@@ -676,12 -672,12 +676,12 @@@ static PyObject *denoise_func(PyObject 
        static const char *keyword_list[] = {"preferences", "scene", "view_layer",
                                             "input", "output",
                                             "tile_size", "samples", NULL};
 -      PyObject *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,
 -                                       &pypreferences, &pyscene, &pyrenderlayer,
 +                                       &pypreferences, &pyscene, &pyviewlayer,
                                                                         &pyinput, &pyoutput,
                                         &tile_size, &samples)) {
                return NULL;
  
        /* Get device specification from preferences and scene. */
        PointerRNA preferencesptr;
 -      RNA_pointer_create(NULL, &RNA_UserPreferences, (void*)PyLong_AsVoidPtr(pypreferences), &preferencesptr);
 -      BL::UserPreferences b_preferences(preferencesptr);
 +      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);
        DeviceInfo device = blender_device_info(b_preferences, b_scene, true);
  
        /* Get denoising parameters from view layer. */
 -      PointerRNA renderlayerptr;
 -      RNA_pointer_create((ID*)PyLong_AsVoidPtr(pyscene), &RNA_SceneRenderLayer, PyLong_AsVoidPtr(pyrenderlayer), &renderlayerptr);
 -      PointerRNA crenderlayer = RNA_pointer_get(&renderlayerptr, "cycles");
 +      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(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");
 +      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)) {
+       if(!image_parse_filepaths(pyinput, input)) {
                return NULL;
        }
  
        if(pyoutput) {
-               if(!denoise_parse_filepaths(pyoutput, output)) {
+               if(!image_parse_filepaths(pyoutput, output)) {
                        return NULL;
                }
        }
        Py_RETURN_NONE;
  }
  
+ static PyObject *merge_func(PyObject * /*self*/, PyObject *args, PyObject *keywords)
+ {
+       static const char *keyword_list[] = {"input", "output", NULL};
+       PyObject *pyinput, *pyoutput = NULL;
+       if (!PyArg_ParseTupleAndKeywords(args, keywords, "OO", (char**)keyword_list, &pyinput, &pyoutput)) {
+               return NULL;
+       }
+       /* Parse input list. */
+       vector<string> input;
+       if(!image_parse_filepaths(pyinput, input)) {
+               return NULL;
+       }
+       /* Parse output string. */
+       if(!PyUnicode_Check(pyoutput)) {
+               PyErr_SetString(PyExc_ValueError, "Output must be a string.");
+               return NULL;
+       }
+       string output = PyUnicode_AsUTF8(pyoutput);
+       /* Merge. */
+       ImageMerger merger;
+       merger.input = input;
+       merger.output = output;
+       if(!merger.run()) {
+               PyErr_SetString(PyExc_ValueError, merger.error.c_str());
+               return NULL;
+       }
+       Py_RETURN_NONE;
+ }
  static PyObject *debug_flags_update_func(PyObject * /*self*/, PyObject *args)
  {
        PyObject *pyscene;
@@@ -902,10 -934,10 +938,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, ""},
  
        /* Standalone denoising */
        {"denoise", (PyCFunction)denoise_func, METH_VARARGS|METH_KEYWORDS, ""},
+       {"merge", (PyCFunction)merge_func, METH_VARARGS|METH_KEYWORDS, ""},
  
        /* Debugging routines */
        {"debug_flags_update", debug_flags_update_func, METH_VARARGS, ""},
@@@ -944,7 -977,7 +981,7 @@@ static struct PyModuleDef module = 
        "Blender cycles render integration",
        -1,
        methods,
 -      NULL, NULL, NULL, NULL
 +      NULL, NULL, NULL, NULL,
  };
  
  CCL_NAMESPACE_END
index a06135b5362377b219d3715a9e9caa698eb128d2,87dc39ca67697f9d0d5c71e943cbc0aff4915d3b..cf856c3b3d4e677b1687992d2dc961a2106923dd
@@@ -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,25 -379,12 +393,16 @@@ static void add_cryptomatte_layer(BL::R
        render_add_metadata(b_rr, prefix+"manifest", manifest);
  }
  
- /* TODO(sergey): Ideally this will be an utility function in util string.h, but
-  * currently is relying on Blender side function, so can not do that. */
- static string make_human_readable_time(double time)
- {
-       char time_str[128];
-       BLI_timecode_string_from_time_simple(time_str, sizeof(time_str), time);
-       return time_str;
- }
 -void BlenderSession::stamp_view_layer_metadata_do(const string& prefix)
 +void BlenderSession::stamp_view_layer_metadata(Scene *scene, const string& view_layer_name)
  {
        BL::RenderResult b_rr = b_engine.get_result();
 +      string prefix = "cycles." + view_layer_name + ".";
 +
        /* 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());
 +      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_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 + ".");
 +      /* Write cryptomatte metadata. */
 +      if(scene->film->cryptomatte_passes & CRYPT_OBJECT) {
 +              add_cryptomatte_layer(b_rr, view_layer_name + ".CryptoObject",
 +                                    scene->object_manager->get_cryptomatte_objects(scene));
 +      }
 +      if(scene->film->cryptomatte_passes & CRYPT_MATERIAL) {
 +              add_cryptomatte_layer(b_rr, view_layer_name + ".CryptoMaterial",
 +                                    scene->shader_manager->get_cryptomatte_materials(scene));
 +      }
 +      if(scene->film->cryptomatte_passes & CRYPT_ASSET) {
 +              add_cryptomatte_layer(b_rr, view_layer_name + ".CryptoAsset",
 +                                    scene->object_manager->get_cryptomatte_assets(scene));
 +      }
 +
 +      /* Store synchronization and bare-render times. */
 +      double total_time, render_time;
 +      session->progress.get_time(total_time, render_time);
 +      b_rr.stamp_data_add_field((prefix + "total_time").c_str(),
-                                 make_human_readable_time(total_time).c_str());
++                                time_human_readable_from_seconds(total_time).c_str());
 +      b_rr.stamp_data_add_field((prefix + "render_time").c_str(),
-                                 make_human_readable_time(render_time).c_str());
++                                time_human_readable_from_seconds(render_time).c_str());
 +      b_rr.stamp_data_add_field((prefix + "synchronization_time").c_str(),
-                                 make_human_readable_time(total_time - render_time).c_str());
++                                time_human_readable_from_seconds(total_time - render_time).c_str());
  }
  
 -void BlenderSession::render()
 +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();
  
 -      for(r.layers.begin(b_layer_iter); b_layer_iter != r.layers.end(); ++b_layer_iter) {
 -              b_rlay_name = b_layer_iter->name();
 +      /* 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();
  
 -              /* 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);
 +      /* add passes */
 +      vector<Pass> passes = sync->sync_render_passes(b_rlay, b_view_layer);
 +      buffer_params.passes = 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;
 -              }
 +      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");
  
 -              BL::RenderLayer b_rlay = *b_single_rlay;
 -
 -              /* add passes */
 -              vector<Pass> passes = sync->sync_render_passes(b_rlay, *b_layer_iter);
 -              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);
 -                      }
 +      bool run_denoising = full_denoising || 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->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;
  
 -                      if(samples != 0 && (!bound_samples || (samples < session_params.samples)))
 -                              effective_layer_samples = samples;
 -                      else
 -                              effective_layer_samples = session_params.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");
  
 -                      /* Update tile manager if we're doing resumable render. */
 -                      update_resumable_tile_manager(effective_layer_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;
  
 -                      /* Update session itself. */
 -                      session->reset(buffer_params, 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);
  
 -                      /* render */
 -                      session->start();
 -                      session->wait();
 +      BL::RenderResult::views_iterator b_view_iter;
  
 -                      if(session->progress.get_cancel())
 -                              break;
 -              }
 +      int num_views = 0;
 +      for(b_rr.views.begin(b_view_iter); b_view_iter != b_rr.views.end(); ++b_view_iter) {
 +              num_views++;
 +      }
  
 -              stamp_view_layer_metadata(b_rlay_name);
 +      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(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));
 +              /* 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_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;
        }
  
 +      /* add metadata */
 +      stamp_view_layer_metadata(scene, b_rlay_name);
 +
 +      /* 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
@@@ -646,8 -604,7 +637,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);
        }
  
@@@ -833,7 -783,7 +824,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();
  
@@@ -1011,10 -957,9 +1002,9 @@@ void BlenderSession::update_bake_progre
  void BlenderSession::update_status_progress()
  {
        string timestatus, status, substatus, kernel_status;
 -      string scene = "";
 +      string scene_status = "";
        float progress;
        double total_time, remaining_time = 0, render_time;
-       char time_str[128];
        float mem_used = (float)session->stats.mem_used / 1024.0f / 1024.0f;
        float mem_peak = (float)session->stats.mem_peak / 1024.0f / 1024.0f;
  
                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 {
 -              timestatus = "Time:" + time_human_readable_from_seconds(total_time) + " | ";
 -      }
 +                      scene_status += ", " + b_rview_name;
  
 -      if(remaining_time > 0) {
 -              timestatus += "Remaining:" + time_human_readable_from_seconds(remaining_time) + " | ";
 -      }
 +              if(remaining_time > 0) {
-                       BLI_timecode_string_from_time_simple(time_str, sizeof(time_str), remaining_time);
-                       timestatus += "Remaining:" + string(time_str) + " | ";
++                      timestatus += "Remaining:" + time_human_readable_from_seconds(remaining_time) + " | ";
 +              }
  
 -      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(kernel_status.size() > 0)
 -              status += " | " + kernel_status;
 +              if(status.size() > 0)
 +                      status = " | " + status;
 +              if(substatus.size() > 0)
 +                      status += " | " + substatus;
 +              if(kernel_status.size() > 0)
 +                      status += " | " + kernel_status;
 +      }
  
        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;
@@@ -1405,9 -1352,6 +1394,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
index b9a1de087056603f8e104f90922e6d9ea3d12e11,a2c1a68c4545c76907e2e8bdc247f7d0ce813436..ec836bd5ec1e68b50545b6e963728211e0cabf68
@@@ -32,8 -32,7 +32,7 @@@
   * todo: clean this up ... */
  
  extern "C" {
- size_t BLI_timecode_string_from_time_simple(char *str, size_t maxlen, double time_seconds);
 -void BKE_image_user_frame_calc(void *iuser, int cfra, int fieldnr);
 +void BKE_image_user_frame_calc(void *iuser, int cfra);
  void BKE_image_user_file_path(void *iuser, void *ima, char *path);
  unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
  float *BKE_image_get_float_pixels_for_frame(void *image, int frame);
@@@ -46,12 -45,12 +45,12 @@@ void python_thread_state_restore(void *
  
  static inline BL::Mesh object_to_mesh(BL::BlendData& data,
                                        BL::Object& object,
 -                                      BL::Scene& scene,
 -                                      bool apply_modifiers,
 -                                      bool render,
 +                                      BL::Depsgraph& depsgraph,
                                        bool calc_undeformed,
                                        Mesh::SubdivisionType subdivision_type)
  {
 +      /* TODO: make this work with copy-on-write, modifiers are already evaluated. */
 +#if 0
        bool subsurf_mod_show_render = false;
        bool subsurf_mod_show_viewport = false;
  
                subsurf_mod.show_render(false);
                subsurf_mod.show_viewport(false);
        }
 +#endif
 +
 +      BL::Mesh mesh(PointerRNA_NULL);
 +      if(object.type() == BL::Object::type_MESH) {
 +              /* TODO: calc_undeformed is not used. */
 +              mesh = BL::Mesh(object.data());
 +
 +              /* Make a copy to split faces if we use autosmooth, otherwise not needed.
 +               * Also in edit mode do we need to make a copy, to ensure data layers like
 +               * UV are not empty. */
 +              if (mesh.is_editmode() ||
 +                  (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE))
 +              {
 +                      mesh = data.meshes.new_from_object(depsgraph, object, false, false);
 +              }
 +      }
 +      else {
 +              mesh = data.meshes.new_from_object(depsgraph, object, true, calc_undeformed);
 +      }
  
 -      BL::Mesh me = data.meshes.new_from_object(scene, object, apply_modifiers, (render)? 2: 1, false, calc_undeformed);
 -
 +#if 0
        if(subdivision_type != Mesh::SUBDIVISION_NONE) {
                BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length()-1];
  
                subsurf_mod.show_render(subsurf_mod_show_render);
                subsurf_mod.show_viewport(subsurf_mod_show_viewport);
        }
 +#endif
  
 -      if((bool)me) {
 -              if(me.use_auto_smooth()) {
 -                      if(subdivision_type == Mesh::SUBDIVISION_CATMULL_CLARK) {
 -                              me.calc_normals_split();
 -                      }
 -                      else {
 -                              me.split_faces(false);
 -                      }
 -              }
 -              if(subdivision_type == Mesh::SUBDIVISION_NONE) {
 -                      me.calc_tessface(true);
 +      if((bool)mesh && subdivision_type == Mesh::SUBDIVISION_NONE) {
 +              if(mesh.use_auto_smooth()) {
 +                      mesh.split_faces(false);
                }
 +
 +              mesh.calc_loop_triangles();
 +      }
 +
 +      return mesh;
 +}
 +
 +static inline void free_object_to_mesh(BL::BlendData& data,
 +                                       BL::Object& object,
 +                                       BL::Mesh& mesh)
 +{
 +      /* Free mesh if we didn't just use the existing one. */
 +      if(object.data().ptr.data != mesh.ptr.data) {
 +              data.meshes.remove(mesh, false, true, false);
        }
 -      return me;
  }
  
  static inline void colorramp_to_array(BL::ColorRamp& ramp,
@@@ -245,14 -220,14 +244,14 @@@ static inline string image_user_file_pa
                                            int cfra)
  {
        char filepath[1024];
 -      BKE_image_user_frame_calc(iuser.ptr.data, cfra, 0);
 +      BKE_image_user_frame_calc(iuser.ptr.data, cfra);
        BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath);
        return string(filepath);
  }
  
  static inline int image_user_frame_number(BL::ImageUser& iuser, int cfra)
  {
 -      BKE_image_user_frame_calc(iuser.ptr.data, cfra, 0);
 +      BKE_image_user_frame_calc(iuser.ptr.data, cfra);
        return iuser.frame_current();
  }
  
@@@ -324,6 -299,46 +323,6 @@@ static inline int4 get_int4(const BL::A
        return make_int4(array[0], array[1], array[2], array[3]);
  }
  
 -static inline uint get_layer(const BL::Array<bool, 20>& array)
 -{
 -      uint layer = 0;
 -
 -      for(uint i = 0; i < 20; i++)
 -              if(array[i])
 -                      layer |= (1 << i);
 -
 -      return layer;
 -}
 -
 -static inline uint get_layer(const BL::Array<bool, 20>& array,
 -                             const BL::Array<bool, 8>& local_array,
 -                             bool is_light = false,
 -                             uint scene_layers = (1 << 20) - 1)
 -{
 -      uint layer = 0;
 -
 -      for(uint i = 0; i < 20; i++)
 -              if(array[i])
 -                      layer |= (1 << i);
 -
 -      if(is_light) {
 -              /* Consider light is visible if it was visible without layer
 -               * override, which matches behavior of Blender Internal.
 -               */
 -              if(layer & scene_layers) {
 -                      for(uint i = 0; i < 8; i++)
 -                              layer |= (1 << (20+i));
 -              }
 -      }
 -      else {
 -              for(uint i = 0; i < 8; i++)
 -                      if(local_array[i])
 -                              layer |= (1 << (20+i));
 -      }
 -
 -      return layer;
 -}
 -
  static inline float3 get_float3(PointerRNA& ptr, const char *name)
  {
        float3 f;