Fix T82129: Cycles "Persistent Images" incorrectly retains scene data

The issue stems from the fact that scene arrays are not cleared when rendering is done. This was not really an issue before the introduction of the ownership system (rB429afe0c626a) as the id_map would recreate scene data arrays based on their new content. However, now that the id_maps do not have access to the scene data anymore the arrays are never created.

Another related issue is that the BlenderSync instance is never freed when the persistent data option is activated.

To fix this, we delete nodes created by the id_maps in their destructors, and delete the BlenderSync instance before creating a new one, so the id_maps destructors are actually called.

Reviewed By: brecht

Maniphest Tasks: T82129

Differential Revision: https://developer.blender.org/D9378
This commit is contained in:
Kévin Dietrich 2020-10-29 14:40:29 +01:00
parent 2e41db5277
commit 8c3d42bd0f
Notes: blender-bot 2023-02-14 00:10:08 +01:00
Referenced by issue #82607, Cycles: Blender crash on switching between tabs
Referenced by issue #82129, Cycles "Persistent Images" incorrectly retains lighting & mesh data when rendering multiple times / animations.
9 changed files with 114 additions and 26 deletions

View File

@ -35,10 +35,22 @@ CCL_NAMESPACE_BEGIN
template<typename K, typename T> class id_map {
public:
id_map()
id_map(Scene *scene_) : scene(scene_)
{
}
~id_map()
{
set<T *> nodes;
typename map<K, T *>::iterator jt;
for (jt = b_map.begin(); jt != b_map.end(); jt++) {
nodes.insert(jt->second);
}
scene->delete_nodes(nodes);
}
T *find(const BL::ID &id)
{
return find(id.ptr.owner_id);
@ -98,16 +110,15 @@ template<typename K, typename T> class id_map {
}
/* Combined add and update as needed. */
bool add_or_update(Scene *scene, T **r_data, const BL::ID &id)
bool add_or_update(T **r_data, const BL::ID &id)
{
return add_or_update(scene, r_data, id, id, id.ptr.owner_id);
return add_or_update(r_data, id, id, id.ptr.owner_id);
}
bool add_or_update(Scene *scene, T **r_data, const BL::ID &id, const K &key)
bool add_or_update(T **r_data, const BL::ID &id, const K &key)
{
return add_or_update(scene, r_data, id, id, key);
return add_or_update(r_data, id, id, key);
}
bool add_or_update(
Scene *scene, T **r_data, const BL::ID &id, const BL::ID &parent, const K &key)
bool add_or_update(T **r_data, const BL::ID &id, const BL::ID &parent, const K &key)
{
T *data = find(key);
bool recalc;
@ -146,7 +157,7 @@ template<typename K, typename T> class id_map {
b_map[NULL] = data;
}
void post_sync(Scene *scene, bool do_delete = true)
void post_sync(bool do_delete = true)
{
map<K, T *> new_map;
typedef pair<const K, T *> TMapPair;
@ -177,6 +188,7 @@ template<typename K, typename T> class id_map {
map<K, T *> b_map;
set<T *> used_set;
set<void *> b_recalc;
Scene *scene;
};
/* Object Key

View File

@ -39,9 +39,9 @@ void BlenderSync::sync_light(BL::Object &b_parent,
BL::Light b_light(b_ob.data());
/* Update if either object or light data changed. */
if (!light_map.add_or_update(scene, &light, b_ob, b_parent, key)) {
if (!light_map.add_or_update(&light, b_ob, b_parent, key)) {
Shader *shader;
if (!shader_map.add_or_update(scene, &shader, b_light)) {
if (!shader_map.add_or_update(&shader, b_light)) {
if (light->is_portal)
*use_portal = true;
return;
@ -176,7 +176,7 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal)
Light *light;
ObjectKey key(b_world, 0, b_world, false);
if (light_map.add_or_update(scene, &light, b_world, b_world, key) || world_recalc ||
if (light_map.add_or_update(&light, b_world, b_world, key) || world_recalc ||
b_world.ptr.data != world_map) {
light->type = LIGHT_BACKGROUND;
if (sampling_method == SAMPLING_MANUAL) {

View File

@ -207,7 +207,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* test if we need to sync */
bool object_updated = false;
if (object_map.add_or_update(scene, &object, b_ob, b_parent, key))
if (object_map.add_or_update(&object, b_ob, b_parent, key))
object_updated = true;
/* mesh sync */
@ -405,10 +405,10 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
sync_background_light(b_v3d, use_portal);
/* handle removed data and modified pointers */
light_map.post_sync(scene);
geometry_map.post_sync(scene);
object_map.post_sync(scene);
particle_system_map.post_sync(scene);
light_map.post_sync();
geometry_map.post_sync();
object_map.post_sync();
particle_system_map.post_sync();
}
if (motion)

View File

@ -53,8 +53,7 @@ bool BlenderSync::sync_dupli_particle(BL::Object &b_ob,
ParticleSystem *psys;
bool first_use = !particle_system_map.is_used(key);
bool need_update = particle_system_map.add_or_update(
scene, &psys, b_ob, b_instance.object(), key);
bool need_update = particle_system_map.add_or_update(&psys, b_ob, b_instance.object(), key);
/* no update needed? */
if (!need_update && !object->geometry->need_update && !scene->object_manager->need_update)

View File

@ -238,6 +238,7 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
* See note on create_session().
*/
/* sync object should be re-created */
delete sync;
sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL);

View File

@ -1241,7 +1241,7 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
Shader *shader;
/* test if we need to sync */
if (shader_map.add_or_update(scene, &shader, b_mat) || update_all) {
if (shader_map.add_or_update(&shader, b_mat) || update_all) {
ShaderGraph *graph = new ShaderGraph();
shader->name = b_mat.name().c_str();
@ -1467,7 +1467,7 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all)
Shader *shader;
/* test if we need to sync */
if (shader_map.add_or_update(scene, &shader, b_light) || update_all) {
if (shader_map.add_or_update(&shader, b_light) || update_all) {
ShaderGraph *graph = new ShaderGraph();
/* create nodes */

View File

@ -56,11 +56,11 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine,
: b_engine(b_engine),
b_data(b_data),
b_scene(b_scene),
shader_map(),
object_map(),
geometry_map(),
light_map(),
particle_system_map(),
shader_map(scene),
object_map(scene),
geometry_map(scene),
light_map(scene),
particle_system_map(scene),
world_map(NULL),
world_recalc(false),
scene(scene),
@ -247,7 +247,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
/* Shader sync done at the end, since object sync uses it.
* false = don't delete unused shaders, not supported. */
shader_map.post_sync(scene, false);
shader_map.post_sync(false);
free_data_after_sync(b_depsgraph);

View File

@ -707,4 +707,58 @@ template<> void Scene::delete_node_impl(Shader * /*node*/)
/* don't delete unused shaders, not supported */
}
template<typename T>
static void remove_nodes_in_set(const set<T *> &nodes_set,
vector<T *> &nodes_array,
const NodeOwner *owner)
{
size_t new_size = nodes_array.size();
for (size_t i = 0; i < new_size; ++i) {
T *node = nodes_array[i];
if (nodes_set.find(node) != nodes_set.end()) {
std::swap(nodes_array[i], nodes_array[new_size - 1]);
assert(node->get_owner() == owner);
delete node;
i -= 1;
new_size -= 1;
}
}
nodes_array.resize(new_size);
(void)owner;
}
template<> void Scene::delete_nodes(const set<Light *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, lights, owner);
light_manager->tag_update(this);
}
template<> void Scene::delete_nodes(const set<Geometry *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, geometry, owner);
geometry_manager->tag_update(this);
}
template<> void Scene::delete_nodes(const set<Object *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, objects, owner);
object_manager->tag_update(this);
}
template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, particle_systems, owner);
particle_system_manager->tag_update(this);
}
template<> void Scene::delete_nodes(const set<Shader *> & /*nodes*/, const NodeOwner * /*owner*/)
{
/* don't delete unused shaders, not supported */
}
CCL_NAMESPACE_END

View File

@ -322,6 +322,18 @@ class Scene : public NodeOwner {
(void)owner;
}
/* Remove all nodes in the set from the appropriate data arrays, and tag the
* specific managers for an update. This assumes that the scene owns the nodes.
*/
template<typename T> void delete_nodes(const set<T *> &nodes)
{
delete_nodes(nodes, this);
}
/* Same as above, but specify the actual owner of all the nodes in the set.
*/
template<typename T> void delete_nodes(const set<T *> &nodes, const NodeOwner *owner);
protected:
/* Check if some heavy data worth logging was updated.
* Mainly used to suppress extra annoying logging.
@ -381,6 +393,16 @@ template<> void Scene::delete_node_impl(ParticleSystem *node);
template<> void Scene::delete_node_impl(Shader *node);
template<> void Scene::delete_nodes(const set<Light *> &nodes, const NodeOwner *owner);
template<> void Scene::delete_nodes(const set<Geometry *> &nodes, const NodeOwner *owner);
template<> void Scene::delete_nodes(const set<Object *> &nodes, const NodeOwner *owner);
template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const NodeOwner *owner);
template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner *owner);
CCL_NAMESPACE_END
#endif /* __SCENE_H__ */