Cycles: persistent images option
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.
This commit is contained in:
@@ -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):
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
@@ -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, ""},
|
||||
|
@@ -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)
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
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);
|
||||
|
||||
if(device) particle_system_manager->device_free(device, &dscene);
|
||||
delete particle_system_manager;
|
||||
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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -130,6 +130,7 @@ public:
|
||||
void set_samples(int samples);
|
||||
void set_pause(bool pause);
|
||||
|
||||
void device_free();
|
||||
protected:
|
||||
struct DelayedReset {
|
||||
thread_mutex mutex;
|
||||
|
@@ -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_)
|
||||
{
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user