Cycles: persistent images option
authorSergey Sharybin <sergey.vfx@gmail.com>
Fri, 9 Nov 2012 08:46:53 +0000 (08:46 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Fri, 9 Nov 2012 08:46:53 +0000 (08:46 +0000)
This option enables keeping loaded images in the memory in-between
of rendering.

Implemented by keeping render engine alive for until Render structure
is being freed.

Cycles will free all data when render finishes, optionally keeping
image manager untouched. All shaders, meshes, objects will be
re-allocated next time rendering happens.

Cycles cession and scene will be re-created from scratch if render/
scene parameters were changed.

This will also allow to keep compiled OSL shaders in memory without
need to re-compile them again.

P.S. Performance panel could be cleaned up a bit, not so much happy
     with it's vertical alignment currently but not sure how to make
     it look better.

 P.P.S. Currently the only way to free images from the device is to
       disable Persistent Images option and start rendering.

16 files changed:
intern/cycles/blender/addon/__init__.py
intern/cycles/blender/addon/engine.py
intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_python.cpp
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_session.h
intern/cycles/blender/blender_sync.cpp
intern/cycles/render/scene.cpp
intern/cycles/render/scene.h
intern/cycles/render/session.cpp
intern/cycles/render/session.h
intern/cycles/util/util_progress.h
source/blender/render/intern/include/render_types.h
source/blender/render/intern/source/external_engine.c
source/blender/render/intern/source/pipeline.c

index 16697c08b2bd6e546c57e869a6ae243725e8590d..0fad2ac561845bb4455284f63d86eec83ab3853f 100644 (file)
@@ -48,7 +48,11 @@ class CyclesRender(bpy.types.RenderEngine):
 
     # final render
     def update(self, data, scene):
-        engine.create(self, data, scene)
+        if not self.session:
+            engine.create(self, data, scene)
+        else:
+            engine.reset(self, data, scene)
+
         engine.update(self, data, scene)
 
     def render(self, scene):
index e4f80cb4d5d12bb8edf6560103c1b0182f2387ee..ca5cbee9325d0cc16bc5bde167e04a5a5ed579b6 100644 (file)
@@ -61,6 +61,13 @@ def render(engine):
         _cycles.render(engine.session)
 
 
+def reset(engine, data, scene):
+    import _cycles
+    data = data.as_pointer()
+    scene = scene.as_pointer()
+    _cycles.reset(engine.session, data, scene)
+
+
 def update(engine, data, scene):
     import _cycles
     _cycles.sync(engine.session)
index 0b8ca6e0fbecff7159479fc973b489ab1f997559..32b07f47ba8e4ddfe73e0bfd52cbd56180e89181 100644 (file)
@@ -297,6 +297,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
                 default=False,
                 )
 
+        cls.use_persistent_images = BoolProperty(
+                name="Persistent Images",
+                description="Keep images loaded on the device so they could be reused by next render",
+                default=False,
+                )
+
     @classmethod
     def unregister(cls):
         del bpy.types.Scene.cycles
index 9cc58e65bef434aa07cc3a0e8a3d6000991e151c..d8d8c11baa6addaf48d393c3c810ffbd2dc0dd8f 100644 (file)
@@ -216,6 +216,10 @@ class CyclesRender_PT_performance(CyclesButtonsPanel, Panel):
         sub.label(text="Viewport:")
         sub.prop(cscene, "preview_start_resolution")
 
+        sub = col.column(align=True)
+        sub.label(text="Final Render:")
+        sub.prop(cscene, "use_persistent_images")
+
 
 class CyclesRender_PT_layers(CyclesButtonsPanel, Panel):
     bl_label = "Layers"
index 3b78651a6a1f826d2cb98fad1ab1cb2c118348aa..fbc88f1056f2d39d279a2a7df2e1ea7bd39b2a1a 100644 (file)
@@ -146,6 +146,32 @@ static PyObject *draw_func(PyObject *self, PyObject *args)
        Py_RETURN_NONE;
 }
 
+static PyObject *reset_func(PyObject *self, PyObject *args)
+{
+       PyObject *pysession, *pydata, *pyscene;
+
+       if(!PyArg_ParseTuple(args, "OOO", &pysession, &pydata, &pyscene))
+               return NULL;
+
+       BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
+
+       PointerRNA dataptr;
+       RNA_id_pointer_create((ID*)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);
+
+       Py_BEGIN_ALLOW_THREADS
+
+       session->reset_session(b_data, b_scene);
+
+       Py_END_ALLOW_THREADS
+
+       Py_RETURN_NONE;
+}
+
 static PyObject *sync_func(PyObject *self, PyObject *value)
 {
        Py_BEGIN_ALLOW_THREADS
@@ -352,6 +378,7 @@ static PyMethodDef methods[] = {
        {"render", render_func, METH_O, ""},
        {"draw", draw_func, METH_VARARGS, ""},
        {"sync", sync_func, METH_O, ""},
+       {"reset", reset_func, METH_VARARGS, ""},
 #ifdef WITH_OSL
        {"osl_update_node", osl_update_node_func, METH_VARARGS, ""},
        {"osl_compile", osl_compile_func, METH_VARARGS, ""},
index 01bd5f013e3bee2960d6e2b494c3c8d8efc0541e..fcc3d89f5d83a02ff22ca08509db1e8072002684 100644 (file)
@@ -109,9 +109,50 @@ void BlenderSession::create_session()
        session->reset(buffer_params, session_params.samples);
 }
 
+void BlenderSession::reset_session(BL::BlendData b_data_, BL::Scene b_scene_)
+{
+       b_data = b_data_;
+       b_scene = b_scene_;
+
+       SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
+       SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
+
+       if(scene->params.modified(scene_params) ||
+          session->params.modified(session_params))
+       {
+               /* 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;
+
+               create_session();
+
+               return;
+       }
+
+       session->progress.reset();
+       scene->reset();
+
+       /* peak memory usage should show current render peak, not peak for all renders
+        * made by this render session
+        */
+       session->stats.mem_peak = session->stats.mem_used;
+
+       /* sync object should be re-created */
+       sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
+       sync->sync_data(b_v3d, b_engine.camera_override());
+       sync->sync_camera(b_engine.camera_override(), width, height);
+
+       BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene, PointerRNA_NULL, PointerRNA_NULL, scene->camera, width, height);
+       session->reset(buffer_params, session_params.samples);
+}
+
 void BlenderSession::free_session()
 {
-       delete sync;
+       if(sync)
+               delete sync;
+
        delete session;
 }
 
@@ -304,6 +345,15 @@ void BlenderSession::render()
        /* clear callback */
        session->write_render_tile_cb = NULL;
        session->update_render_tile_cb = NULL;
+
+       /* free all memory used (host and device), so we wouldn't leave render
+        * engine with extra memory allocated
+        */
+
+       session->device_free();
+
+       delete sync;
+       sync = NULL;
 }
 
 void BlenderSession::do_write_update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile, bool do_update_only)
index d52e0103bbfe282c843aeb4579b4f0e8ff271e79..7f3973ae8733e4f144fee5deadc84e6cc126ae26 100644 (file)
@@ -46,6 +46,8 @@ public:
        void create_session();
        void free_session();
 
+       void reset_session(BL::BlendData b_data, BL::Scene b_scene);
+
        /* offline render */
        void render();
 
index 024cb1685d0eab188968d88412bd67c72cbcbea1..eacfbdadba05014b16d41ba6a3def5ab3e931b03 100644 (file)
@@ -294,6 +294,8 @@ SceneParams BlenderSync::get_scene_params(BL::Scene b_scene, bool background)
        params.use_bvh_spatial_split = RNA_boolean_get(&cscene, "debug_use_spatial_splits");
        params.use_bvh_cache = (background)? RNA_boolean_get(&cscene, "use_cache"): false;
 
+       params.persistent_images = (background)? RNA_boolean_get(&cscene, "use_persistent_images"): false;
+
        return params;
 }
 
index 15031b9500c999be9b565d5e182d4757dcd2ec02..a2febc31f60d69b01e479f9cc8b28bd2104cc32c 100644 (file)
@@ -62,33 +62,11 @@ Scene::Scene(const SceneParams& params_, const DeviceInfo& device_info_)
 
 Scene::~Scene()
 {
-       if(device) camera->device_free(device, &dscene);
-       delete camera;
-
-       if(device) filter->device_free(device, &dscene);
-       delete filter;
-
-       if(device) film->device_free(device, &dscene);
-       delete film;
-
-       if(device) background->device_free(device, &dscene);
-       delete background;
-
-       if(device) mesh_manager->device_free(device, &dscene);
-       delete mesh_manager;
-
-       if(device) object_manager->device_free(device, &dscene);
-       delete object_manager;
-
-       if(device) integrator->device_free(device, &dscene);
-       delete integrator;
-
-       if(device) shader_manager->device_free(device, &dscene);
-       delete shader_manager;
-
-       if(device) light_manager->device_free(device, &dscene);
-       delete light_manager;
+       free_memory(true);
+}
 
+void Scene::free_memory(bool final)
+{
        foreach(Shader *s, shaders)
                delete s;
        foreach(Mesh *m, meshes)
@@ -100,11 +78,44 @@ Scene::~Scene()
        foreach(ParticleSystem *p, particle_systems)
                delete p;
 
-       if(device) image_manager->device_free(device, &dscene);
-       delete image_manager;
-
-       if(device) particle_system_manager->device_free(device, &dscene);
-       delete particle_system_manager;
+       if(device) {
+               camera->device_free(device, &dscene);
+               filter->device_free(device, &dscene);
+               film->device_free(device, &dscene);
+               background->device_free(device, &dscene);
+               integrator->device_free(device, &dscene);
+
+               object_manager->device_free(device, &dscene);
+               mesh_manager->device_free(device, &dscene);
+               shader_manager->device_free(device, &dscene);
+               light_manager->device_free(device, &dscene);
+
+               particle_system_manager->device_free(device, &dscene);
+
+               if(!params.persistent_images || final)
+                       image_manager->device_free(device, &dscene);
+       }
+
+       if(final) {
+               delete filter;
+               delete camera;
+               delete film;
+               delete background;
+               delete integrator;
+               delete object_manager;
+               delete mesh_manager;
+               delete shader_manager;
+               delete light_manager;
+               delete particle_system_manager;
+               delete image_manager;
+       }
+       else {
+               shaders.clear();
+               meshes.clear();
+               objects.clear();
+               lights.clear();
+               particle_systems.clear();
+       }
 }
 
 void Scene::device_update(Device *device_, Progress& progress)
@@ -229,5 +240,22 @@ bool Scene::need_reset()
                || particle_system_manager->need_update);
 }
 
+void Scene::reset()
+{
+       shader_manager->add_default(this);
+
+       /* ensure all objects are updated */
+       camera->tag_update();
+       filter->tag_update(this);
+       film->tag_update(this);
+       background->tag_update(this);
+       integrator->tag_update(this);
+}
+
+void Scene::device_free()
+{
+       free_memory(false);
+}
+
 CCL_NAMESPACE_END
 
index bd45c1c04e61fea1673410fdc37bce7c2900cfc4..92ef692b4b9ed558b593c5a13fe06e62113d8caf 100644 (file)
@@ -120,6 +120,7 @@ public:
        bool use_bvh_cache;
        bool use_bvh_spatial_split;
        bool use_qbvh;
+       bool persistent_images;
 
        SceneParams()
        {
@@ -139,7 +140,8 @@ public:
                && bvh_type == params.bvh_type
                && use_bvh_cache == params.use_bvh_cache
                && use_bvh_spatial_split == params.use_bvh_spatial_split
-               && use_qbvh == params.use_qbvh); }
+               && use_qbvh == params.use_qbvh
+               && persistent_images == params.persistent_images); }
 };
 
 /* Scene */
@@ -198,6 +200,12 @@ public:
 
        bool need_update();
        bool need_reset();
+
+       void reset();
+       void device_free();
+
+protected:
+       void free_memory(bool final);
 };
 
 CCL_NAMESPACE_END
index 7f6b43c1e002f80d6bd02518ccd3b4c2c48e04fa..41212c2db84ddf38878ac283a4d1bdf1df1db312 100644 (file)
@@ -842,4 +842,18 @@ bool Session::update_progressive_refine(bool cancel)
        return write;
 }
 
+void Session::device_free()
+{
+       scene->device_free();
+
+       foreach(RenderBuffers *buffers, tile_buffers)
+               delete buffers;
+
+       tile_buffers.clear();
+
+       /* used from background render only, so no need to
+        * re-create render/display buffers here
+        */
+}
+
 CCL_NAMESPACE_END
index 7bb0cd1ae019fd4b331a70caf12d830f1db3ed4e..cfc1502287d4a709ab6ce9b3dfabca9b9a6e805a 100644 (file)
@@ -130,6 +130,7 @@ public:
        void set_samples(int samples);
        void set_pause(bool pause);
 
+       void device_free();
 protected:
        struct DelayedReset {
                thread_mutex mutex;
index c97379d87766bd35ac34f8f27c11c7764c1165aa..03e25d4d132ba4fb8df559b732217b675b04e48c 100644 (file)
@@ -68,6 +68,21 @@ public:
                return *this;
        }
 
+       void reset()
+       {
+               tile = 0;
+               sample = 0;
+               start_time = time_dt();
+               total_time = 0.0f;
+               tile_time = 0.0f;
+               status = "Initializing";
+               substatus = "";
+               sync_status = "";
+               sync_substatus = "";
+               cancel = false;
+               cancel_message = "";
+       }
+
        /* cancel */
        void set_cancel(const string& cancel_message_)
        {
index 825fe2ff4d0a15de6096519aa6c674f3b6e3b532..6ed8d6a7b6c2707e2898e12cd58d37b03fa3d9d5 100644 (file)
@@ -59,6 +59,7 @@ struct RenderBuckets;
 struct ObjectInstanceRen;
 struct RayObject;
 struct RayFace;
+struct RenderEngine;
 struct ReportList;
 struct Main;
 
@@ -183,6 +184,9 @@ struct Render
        
        ListBase parts;
        
+       /* render engine */
+       struct RenderEngine *engine;
+       
        /* octree tables and variables for raytrace */
        struct RayObject *raytree;
        struct RayFace *rayfaces;
index e2f347c05f1e954c1863ee6a5ba7046cb4596fc6..736cb5de7c33a041cb644a72b35206c548abde30 100644 (file)
@@ -349,7 +349,12 @@ int RE_engine_render(Render *re, int do_all)
        re->i.totface = re->i.totvert = re->i.totstrand = re->i.totlamp = re->i.tothalo = 0;
 
        /* render */
-       engine = RE_engine_create(type);
+       if(!re->engine)
+               re->engine = RE_engine_create(type);
+
+       engine = re->engine;
+
+       /* TODO: actually link to a parent which shouldn't happen */
        engine->re = re;
 
        if (re->flag & R_ANIMATION)
@@ -389,8 +394,6 @@ int RE_engine_render(Render *re, int do_all)
 
        render_result_free_list(&engine->fullresult, engine->fullresult.first);
 
-       RE_engine_free(engine);
-
        if (BKE_reports_contain(re->reports, RPT_ERROR))
                G.is_break = TRUE;
        
index d305ed06af1879ffbd2e7e78e163ce933eac1f86..369818d37d53b956db5363c797673f3579daa604 100644 (file)
@@ -387,6 +387,9 @@ void RE_InitRenderCB(Render *re)
 /* only call this while you know it will remove the link too */
 void RE_FreeRender(Render *re)
 {
+       if (re->engine)
+               RE_engine_free(re->engine);
+
        BLI_rw_mutex_end(&re->resultmutex);
        
        free_renderdata_tables(re);