Cycles: Free Blender side data as soon as we don't need it
authorSergey Sharybin <sergey.vfx@gmail.com>
Fri, 9 Nov 2018 15:14:15 +0000 (16:14 +0100)
committerSergey Sharybin <sergey.vfx@gmail.com>
Fri, 9 Nov 2018 16:53:02 +0000 (17:53 +0100)
Currently this is possible after built-in images are loaded in memory.
Allows to save memory used by dependency graph and copy-on-write.

In practice this lowers peak system memory usage from 52GB to 42GB on
a production file of spring 03_035_A.lighting.

Note, that this only applies to F12 and command line renders.

Bigger note, that this optimization is currently only possible if
there are no grease pencil objects to be rendered.

intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_session.h
source/blender/makesrna/intern/rna_render.c
source/blender/render/extern/include/RE_engine.h
source/blender/render/intern/source/external_engine.c

index 28b0f4faf63f2ea2c276cbfa0d2b28c78152e68f..17da8b43d29dde52b39ceeac1d1083e875ebd542 100644 (file)
@@ -470,6 +470,11 @@ void BlenderSession::render(BL::Depsgraph& b_depsgraph_)
                                &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.
+                */
+               free_blender_memory_if_possible();
+
                /* 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));
@@ -1406,4 +1411,16 @@ void BlenderSession::update_resumable_tile_manager(int num_samples)
        session->tile_manager.range_num_samples = 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 99b14a09a5cbd051934aa2e967d141980b956793..fdeba7b2f8194ef67a892a786ee147b55ae2fbc5 100644 (file)
@@ -106,6 +106,9 @@ public:
        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;
@@ -172,6 +175,12 @@ protected:
 
        /* 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 1e9f68c577b92be53ec76ade4500dad41e03cd5a..46b77b80d49194a4a8d785fc7b0900d971ba2234 100644 (file)
@@ -685,6 +685,9 @@ static void rna_def_render_engine(BlenderRNA *brna)
        parm = RNA_def_int(func, "pixel_size", 0, 1, 8, "Pixel Size", "", 1, 8);
        RNA_def_function_return(func, parm);
 
+       RNA_def_function(srna, "free_blender_memory", "RE_engine_free_blender_memory");
+       RNA_def_function_ui_description(func, "Free Blender side memory of render engine");
+
        RNA_define_verify_sdna(0);
 
        prop = RNA_def_property(srna, "is_animation", PROP_BOOLEAN, PROP_NONE);
index 1ca889f23478bf5f44062b7fd76f517694ef3ae1..faa4c8b3184ef31b3cd2d37df0a052982a911114 100644 (file)
@@ -193,4 +193,6 @@ struct RenderData *RE_engine_get_render_data(struct Render *re);
 void RE_bake_engine_set_engine_parameters(
         struct Render *re, struct Main *bmain, struct Scene *scene);
 
+void RE_engine_free_blender_memory(struct RenderEngine *engine);
+
 #endif /* __RE_ENGINE_H__ */
index a06389bfcd45f6e879b800e4c2cdce462e6bc303..758600e89aa2a8cdf9dcd305ef03ec57c14eaea7 100644 (file)
@@ -738,8 +738,13 @@ int RE_engine_render(Render *re, int do_all)
 
                        type->render(engine, engine->depsgraph);
 
-                       /* grease pencil render over previous render result */
-                       if (!RE_engine_test_break(engine)) {
+                       /* Grease pencil render over previous render result.
+                        *
+                        * NOTE: External engine might have been requested to free its
+                        * dependency graph, which is only allowed if there is no grease
+                        * pencil (pipeline is taking care of that).
+                        */
+                       if (!RE_engine_test_break(engine) && engine->depsgraph != NULL) {
                                DRW_render_gpencil(engine, engine->depsgraph);
                        }
 
@@ -813,3 +818,18 @@ void RE_engine_register_pass(struct RenderEngine *engine, struct Scene *scene, s
                }
        }
 }
+
+void RE_engine_free_blender_memory(RenderEngine *engine)
+{
+       /* Weak way to save memory, but not crash grease pencil.
+        *
+        * TODO(sergey): Find better solution for this.
+        * TODO(sergey): Try to find solution which does not involve looping over
+        * all the objects.
+        */
+       if (DRW_render_check_grease_pencil(engine->depsgraph)) {
+               return;
+       }
+       DEG_graph_free(engine->depsgraph);
+       engine->depsgraph = NULL;
+}