Merge branch 'blender2.7'
authorJeroen Bakker <j.bakker@atmind.nl>
Fri, 15 Mar 2019 15:28:33 +0000 (16:28 +0100)
committerJeroen Bakker <j.bakker@atmind.nl>
Fri, 15 Mar 2019 15:28:33 +0000 (16:28 +0100)
1  2 
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_session.h
intern/cycles/device/device.h
intern/cycles/device/device_multi.cpp
intern/cycles/render/session.cpp

index 501e4fec13f10ad0cceda4261828ed5abecee5c5,27541800804151f563b3eb580740559591aa4a52..f1cdda5cb13c0613c268ebbe7ad3f0f4de10625e
@@@ -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,25 @@@ static void add_cryptomatte_layer(BL::R
        render_add_metadata(b_rr, prefix+"manifest", manifest);
  }
  
 -void BlenderSession::stamp_view_layer_metadata_do(const string& prefix)
 +/* 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(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());
 +      b_rr.stamp_data_add_field((prefix + "render_time").c_str(),
 +                                make_human_readable_time(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());
  }
  
 -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, session_params);
 +      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, 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);
 -                      }
 +      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 +646,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 +833,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();
  
@@@ -987,6 -933,11 +987,11 @@@ void BlenderSession::get_status(string
        session->progress.get_status(status, substatus);
  }
  
+ void BlenderSession::get_kernel_status(string& kernel_status)
+ {
+       session->progress.get_kernel_status(kernel_status);
+ }
  void BlenderSession::get_progress(float& progress, double& total_time, double& render_time)
  {
        session->progress.get_time(total_time, render_time);
@@@ -1005,8 -956,8 +1010,8 @@@ void BlenderSession::update_bake_progre
  
  void BlenderSession::update_status_progress()
  {
-       string timestatus, status, substatus;
+       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_peak = (float)session->stats.mem_peak / 1024.0f / 1024.0f;
  
        get_status(status, substatus);
+       get_kernel_status(kernel_status);
        get_progress(progress, total_time, render_time);
  
        if(progress > 0)
                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(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;
@@@ -1397,9 -1355,6 +1405,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 1915cdb36f1e051c5353bd14bfbfb602c3fa7a3b,2c0a83cf6e766ce60356d5ba70a1d16fbc98d73c..2bfb9e56c376ea7dfad0bac5ecc2714279d34da8
@@@ -35,13 -35,14 +35,13 @@@ class RenderTile
  class BlenderSession {
  public:
        BlenderSession(BL::RenderEngine& b_engine,
 -                     BL::UserPreferences& b_userpref,
 +                     BL::Preferences& b_userpref,
                       BL::BlendData& b_data,
 -                     BL::Scene& b_scene);
 +                     bool preview_osl);
  
        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);
        void free_session();
  
        void reset_session(BL::BlendData& b_data,
 -                         BL::Scene& b_scene);
 +                         BL::Depsgraph& b_depsgraph);
  
        /* offline render */
 -      void render();
 +      void render(BL::Depsgraph& b_depsgraph);
  
 -      void bake(BL::Object& b_object,
 +      void bake(BL::Depsgraph& b_depsgrah,
 +                BL::Object& b_object,
                  const string& pass_type,
                  const int custom_flag,
                  const int object_id,
        void update_render_tile(RenderTile& rtile, bool highlight);
  
        /* interactive updates */
 -      void synchronize();
 +      void synchronize(BL::Depsgraph& b_depsgraph);
  
        /* drawing */
        bool draw(int w, int h);
        void tag_redraw();
        void tag_update();
        void get_status(string& status, string& substatus);
+       void get_kernel_status(string& kernel_status);
        void get_progress(float& progress, double& total_time, double& render_time);
        void test_cancel();
        void update_status_progress();
        double last_redraw_time;
  
        BL::RenderEngine b_engine;
 -      BL::UserPreferences b_userpref;
 +      BL::Preferences b_userpref;
        BL::BlendData b_data;
        BL::RenderSettings b_render;
 +      BL::Depsgraph b_depsgraph;
 +      /* NOTE: Blender's scene might become invalid after call
 +       * free_blender_memory_if_possible().
 +       */
        BL::Scene b_scene;
        BL::SpaceView3D b_v3d;
        BL::RegionView3D b_rv3d;
        double last_status_time;
  
        int width, height;
 +      bool preview_osl;
        double start_resize_time;
  
        void *python_thread_state;
        static bool print_render_stats;
  
  protected:
 -      void stamp_view_layer_metadata(const string& view_layer_name);
 -      void stamp_view_layer_metadata_do(const string& prefix);
 +      void stamp_view_layer_metadata(Scene *scene, const string& view_layer_name);
  
        void do_write_update_render_result(BL::RenderResult& b_rr,
                                           BL::RenderLayer& b_rlay,
                                        float *pixels,
                                        const size_t pixels_size,
                                        const bool free_cache);
 +      void builtin_images_load();
  
        /* Update tile manager to reflect resumable render settings. */
        void update_resumable_tile_manager(int num_samples);
 +
 +      /* Is used after each render layer synchronization is done with the goal
 +       * of freeing render engine data which is held from Blender side (for
 +       * example, dependency graph).
 +       */
 +      void free_blender_memory_if_possible();
  };
  
  CCL_NAMESPACE_END
index 08b0e7435fe7b40415f08b85b5455784711a7b17,6f3208e955f18e71ac7af5058b0fdaae9d7e65e1..4db8d10a4aafc0dcbf6b58f3607cdb447efaaa88
@@@ -56,6 -56,14 +56,14 @@@ enum DeviceTypeMask 
        DEVICE_MASK_ALL = ~0
  };
  
+ enum DeviceKernelStatus {
+       DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL = 0,
+       DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE,
+       DEVICE_KERNEL_USING_FEATURE_KERNEL,
+       DEVICE_KERNEL_FEATURE_KERNEL_INVALID,
+       DEVICE_KERNEL_UNKNOWN,
+ };
  #define DEVICE_MASK(type) (DeviceTypeMask)(1 << type)
  
  class DeviceInfo {
@@@ -269,26 -277,13 +277,26 @@@ struct DeviceDrawParams 
  class Device {
        friend class device_sub_ptr;
  protected:
 -      Device(DeviceInfo& info_, Stats &stats_, Profiler &profiler_, bool background) : background(background), vertex_buffer(0), info(info_), stats(stats_), profiler(profiler_) {}
 +      enum {
 +              FALLBACK_SHADER_STATUS_NONE = 0,
 +              FALLBACK_SHADER_STATUS_ERROR,
 +              FALLBACK_SHADER_STATUS_SUCCESS,
 +      };
 +
 +      Device(DeviceInfo& info_, Stats &stats_, Profiler &profiler_, bool background) : background(background),
 +          vertex_buffer(0),
 +          fallback_status(FALLBACK_SHADER_STATUS_NONE), fallback_shader_program(0),
 +          info(info_), stats(stats_), profiler(profiler_) {}
  
        bool background;
        string error_msg;
  
        /* used for real time display */
        unsigned int vertex_buffer;
 +      int fallback_status, fallback_shader_program;
 +      int image_texture_location, fullscreen_location;
 +
 +      bool bind_fallback_display_space_shader(const float width, const float height);
  
        virtual device_ptr mem_alloc_sub_ptr(device_memory& /*mem*/, int /*offset*/, int /*size*/)
        {
@@@ -334,6 -329,20 +342,20 @@@ public
                const DeviceRequestedFeatures& /*requested_features*/)
        { return true; }
  
+       /* Wait for device to become available to upload data and receive tasks
+        * This method is used by the OpenCL device to load the
+        * optimized kernels or when not (yet) available load the
+        * generic kernels (only during foreground rendering) */
+       virtual bool wait_for_availability(
+               const DeviceRequestedFeatures& /*requested_features*/)
+       { return true; }
+       /* Check if there are 'better' kernels available to be used
+        * We can switch over to these kernels
+        * This method is used to determine if we can switch the preview kernels
+        * to regular kernels */
+       virtual DeviceKernelStatus get_active_kernel_switch_state()
+       { return DEVICE_KERNEL_USING_FEATURE_KERNEL; }
        /* tasks */
        virtual int get_split_task_count(DeviceTask& task) = 0;
        virtual void task_add(DeviceTask& task) = 0;
        virtual void task_cancel() = 0;
  
        /* opengl drawing */
 -      virtual void draw_pixels(device_memory& mem, int y, int w, int h,
 -              int dx, int dy, int width, int height, bool transparent,
 -              const DeviceDrawParams &draw_params);
 +      virtual void draw_pixels(device_memory& mem, int y,
 +          int w, int h, int width, int height,
 +          int dx, int dy, int dw, int dh,
 +          bool transparent, const DeviceDrawParams &draw_params);
  
  #ifdef WITH_NETWORK
        /* networking */
index 3308af4f53ff29fc2b3ae8a971ce8b72eac9211c,516b86654aa9a6e496bb59d6383d65626657fcc8..efb4d9dd288b8e92ee52a8be4f4d7d2fc631ebe4
@@@ -120,6 -120,37 +120,37 @@@ public
                return true;
        }
  
+       bool wait_for_availability(const DeviceRequestedFeatures& requested_features)
+       {
+               foreach(SubDevice& sub, devices)
+                       if(!sub.device->wait_for_availability(requested_features))
+                               return false;
+               return true;
+       }
+       DeviceKernelStatus get_active_kernel_switch_state()
+       {
+               DeviceKernelStatus result = DEVICE_KERNEL_USING_FEATURE_KERNEL;
+               foreach(SubDevice& sub, devices) {
+                       DeviceKernelStatus subresult = sub.device->get_active_kernel_switch_state();
+                       switch (subresult) {
+                               case DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL:
+                                       result = subresult;
+                                       break;
+                               case DEVICE_KERNEL_FEATURE_KERNEL_INVALID:
+                               case DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE:
+                                       return subresult;
+                               case DEVICE_KERNEL_USING_FEATURE_KERNEL:
+                                       break;
+                       }
+               }
+               return result;
+       }
        void mem_alloc(device_memory& mem)
        {
                device_ptr key = unique_key++;
                        sub.device->const_copy_to(name, host, size);
        }
  
 -      void draw_pixels(device_memory& rgba, int y, int w, int h, int dx, int dy, int width, int height, bool transparent,
 -              const DeviceDrawParams &draw_params)
 +      void draw_pixels(
 +          device_memory& rgba, int y,
 +          int w, int h, int width, int height,
 +          int dx, int dy, int dw, int dh,
 +          bool transparent, const DeviceDrawParams &draw_params)
        {
                device_ptr key = rgba.device_pointer;
                int i = 0, sub_h = h/devices.size();
                        /* adjust math for w/width */
  
                        rgba.device_pointer = sub.ptr_map[key];
 -                      sub.device->draw_pixels(rgba, sy, w, sh, dx, sdy, width, sheight, transparent, draw_params);
 +                      sub.device->draw_pixels(rgba, sy, w, sh, width, sheight, dx, sdy, dw, dh, transparent, draw_params);
                        i++;
                }
  
index 866832333ebaad67d81c09e07b82734caba94f5a,d4b1a5e843bb44b05664ee73d7494f7044f7e775..e9274fbf49e707aa7a5aa51bd50411c9166e9aa8
@@@ -212,6 -212,11 +212,11 @@@ void Session::run_gpu(
                /* advance to next tile */
                bool no_tiles = !tile_manager.next();
  
+               DeviceKernelStatus kernel_state = DEVICE_KERNEL_UNKNOWN;
+               if (no_tiles) {
+                       kernel_state = device->get_active_kernel_switch_state();
+               }
                if(params.background) {
                        /* if no work left and in background mode, we can stop immediately */
                        if(no_tiles) {
                                break;
                        }
                }
+               /* Don't go in pause mode when image was rendered with preview kernels
+                * When feature kernels become available the session will be resetted. */
+               else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
+                       time_sleep(0.1);
+               }
+               else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) {
+                       reset_gpu(tile_manager.params, params.samples);
+               }
                else {
                        /* if in interactive mode, and we are either paused or done for now,
                         * wait for pause condition notify to wake up again */
@@@ -540,6 -555,11 +555,11 @@@ void Session::run_cpu(
                bool no_tiles = !tile_manager.next();
                bool need_tonemap = false;
  
+               DeviceKernelStatus kernel_state = DEVICE_KERNEL_UNKNOWN;
+               if (no_tiles) {
+                       kernel_state = device->get_active_kernel_switch_state();
+               }
                if(params.background) {
                        /* if no work left and in background mode, we can stop immediately */
                        if(no_tiles) {
                                break;
                        }
                }
+               /* Don't go in pause mode when preview kernels are used
+                * When feature kernels become available the session will be resetted. */
+               else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
+                       time_sleep(0.1);
+               }
+               else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) {
+                       reset_cpu(tile_manager.params, params.samples);
+               }
                else {
                        /* if in interactive mode, and we are either paused or done for now,
                         * wait for pause condition notify to wake up again */
@@@ -699,7 -729,7 +729,7 @@@ DeviceRequestedFeatures Session::get_re
        return requested_features;
  }
  
void Session::load_kernels(bool lock_scene)
bool Session::load_kernels(bool lock_scene)
  {
        thread_scoped_lock scene_lock;
        if(lock_scene) {
                        progress.set_error(message);
                        progress.set_status("Error", message);
                        progress.set_update();
-                       return;
+                       return false;
                }
  
                progress.add_skip_time(timer, false);
  
                kernels_loaded = true;
                loaded_kernel_features = requested_features;
+               return true;
        }
+       return false;
  }
  
  void Session::run()
  {
-       /* load kernels */
-       load_kernels();
        if(params.use_profiling && (params.device.type == DEVICE_CPU)) {
                profiler.start();
        }
@@@ -879,7 -908,7 +908,7 @@@ bool Session::update_scene(
  
        /* update scene */
        if(scene->need_update()) {
-               load_kernels(false);
+               bool new_kernels_needed = load_kernels(false);
  
                /* Update max_closures. */
                KernelIntegrator *kintegrator = &scene->dscene.data.integrator;
                progress.set_status("Updating Scene");
                MEM_GUARDED_CALL(&progress, scene->device_update, device, progress);
  
+               DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state();
+               bool kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE ||
+                                           kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
+               if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
+                       progress.set_kernel_status("Compiling render kernels");
+               }
+               if (new_kernels_needed || kernel_switch_needed) {
+                       progress.set_kernel_status("Compiling render kernels");
+                       device->wait_for_availability(loaded_kernel_features);
+                       progress.set_kernel_status("");
+               }
+               if (kernel_switch_needed) {
+                       reset(tile_manager.params, params.samples);
+               }
                return true;
        }
        return false;
@@@ -944,10 -988,10 +988,10 @@@ void Session::update_status_time(bool s
                                          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 {