Fix crash when making objects to share same mesh

Make it more reliable and predictable way of getting pointer to
an original mesh which came from copy-on-write engine.

Related change: made it (hopefully) more clear name for flags.
This commit is contained in:
2018-06-04 15:11:09 +02:00
parent 1dc31f5b98
commit 07f0046203
9 changed files with 51 additions and 34 deletions

View File

@@ -2790,7 +2790,7 @@ static void editbmesh_calc_modifiers(
} }
else { else {
struct Mesh *mesh = ob->data; struct Mesh *mesh = ob->data;
if (mesh->id.tag & LIB_TAG_COPY_ON_WRITE) { if (mesh->id.tag & LIB_TAG_COPIED_ON_WRITE) {
BKE_mesh_runtime_ensure_edit_data(mesh); BKE_mesh_runtime_ensure_edit_data(mesh);
mesh->runtime.edit_data->vertexCos = MEM_dupallocN(deformedVerts); mesh->runtime.edit_data->vertexCos = MEM_dupallocN(deformedVerts);
} }
@@ -2832,7 +2832,7 @@ static void editbmesh_calc_modifiers(
else { else {
/* this is just a copy of the editmesh, no need to calc normals */ /* this is just a copy of the editmesh, no need to calc normals */
struct Mesh *mesh = ob->data; struct Mesh *mesh = ob->data;
if (mesh->id.tag & LIB_TAG_COPY_ON_WRITE) { if (mesh->id.tag & LIB_TAG_COPIED_ON_WRITE) {
BKE_mesh_runtime_ensure_edit_data(mesh); BKE_mesh_runtime_ensure_edit_data(mesh);
if (mesh->runtime.edit_data->vertexCos != NULL) if (mesh->runtime.edit_data->vertexCos != NULL)
MEM_freeN((void *)mesh->runtime.edit_data->vertexCos); MEM_freeN((void *)mesh->runtime.edit_data->vertexCos);
@@ -2943,18 +2943,14 @@ static void mesh_finalize_eval(Object *object)
if (mesh_eval->mat != NULL) { if (mesh_eval->mat != NULL) {
MEM_freeN(mesh_eval->mat); MEM_freeN(mesh_eval->mat);
} }
/* Set flag which makes it easier to see what's going on in a debugger. */
mesh_eval->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
mesh_eval->mat = MEM_dupallocN(mesh->mat); mesh_eval->mat = MEM_dupallocN(mesh->mat);
mesh_eval->totcol = mesh->totcol; mesh_eval->totcol = mesh->totcol;
/* Make evaluated mesh to share same edit mesh pointer as original /* Make evaluated mesh to share same edit mesh pointer as original
* and copied meshes. * and copied meshes.
*/ */
mesh_eval->edit_btmesh = mesh->edit_btmesh; mesh_eval->edit_btmesh = mesh->edit_btmesh;
/* Special flags to help debugging and also to allow copy-on-write core
* to understand that on re-evaluation this mesh is to be preserved and
* to be remapped back to copied original mesh when used as object data.
*/
mesh_eval->id.tag |= LIB_TAG_COPY_ON_WRITE_EVAL;
mesh_eval->id.orig_id = &mesh->id;
/* Copy autosmooth settings from original mesh. /* Copy autosmooth settings from original mesh.
* This is not done by BKE_mesh_new_nomain_from_template(), so need to take * This is not done by BKE_mesh_new_nomain_from_template(), so need to take
* extra care here. * extra care here.
@@ -2970,7 +2966,7 @@ static void mesh_finalize_eval(Object *object)
/* Object is sometimes not evaluated! /* Object is sometimes not evaluated!
* TODO(sergey): BAD TEMPORARY HACK FOR UNTIL WE ARE SMARTER */ * TODO(sergey): BAD TEMPORARY HACK FOR UNTIL WE ARE SMARTER */
if (object->id.tag & LIB_TAG_COPY_ON_WRITE) { if (object->id.tag & LIB_TAG_COPIED_ON_WRITE) {
object->data = mesh_eval; object->data = mesh_eval;
} }
else { else {

View File

@@ -1057,7 +1057,7 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Objec
modifier_deformVerts_DM_deprecated(md, &mectx, NULL, vertexCos, numVerts); modifier_deformVerts_DM_deprecated(md, &mectx, NULL, vertexCos, numVerts);
} }
if (ob->id.tag & LIB_TAG_COPY_ON_WRITE) { if (ob->id.tag & LIB_TAG_COPIED_ON_WRITE) {
if (vertexCos) { if (vertexCos) {
BKE_lattice_vertexcos_apply(ob, vertexCos); BKE_lattice_vertexcos_apply(ob, vertexCos);
MEM_freeN(vertexCos); MEM_freeN(vertexCos);

View File

@@ -344,7 +344,9 @@ void BKE_object_free_derived_caches(Object *ob)
if (ob->runtime.mesh_eval != NULL) { if (ob->runtime.mesh_eval != NULL) {
Mesh *mesh_eval = ob->runtime.mesh_eval; Mesh *mesh_eval = ob->runtime.mesh_eval;
/* Restore initial pointer. */ /* Restore initial pointer. */
ob->data = mesh_eval->id.orig_id; if (ob->data == mesh_eval) {
ob->data = ob->runtime.mesh_orig;
}
/* Evaluated mesh points to edit mesh, but does not own it. */ /* Evaluated mesh points to edit mesh, but does not own it. */
mesh_eval->edit_btmesh = NULL; mesh_eval->edit_btmesh = NULL;
BKE_mesh_free(mesh_eval); BKE_mesh_free(mesh_eval);

View File

@@ -311,7 +311,7 @@ ID *DepsgraphNodeBuilder::get_cow_id(const ID *id_orig) const
ID *DepsgraphNodeBuilder::ensure_cow_id(ID *id_orig) ID *DepsgraphNodeBuilder::ensure_cow_id(ID *id_orig)
{ {
if (id_orig->tag & LIB_TAG_COPY_ON_WRITE) { if (id_orig->tag & LIB_TAG_COPIED_ON_WRITE) {
/* ID is already remapped to copy-on-write. */ /* ID is already remapped to copy-on-write. */
return id_orig; return id_orig;
} }

View File

@@ -310,7 +310,7 @@ IDDepsNode *Depsgraph::find_id_node(const ID *id) const
IDDepsNode *Depsgraph::add_id_node(ID *id, ID *id_cow_hint) IDDepsNode *Depsgraph::add_id_node(ID *id, ID *id_cow_hint)
{ {
BLI_assert((id->tag & LIB_TAG_COPY_ON_WRITE) == 0); BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) == 0);
IDDepsNode *id_node = find_id_node(id); IDDepsNode *id_node = find_id_node(id);
if (!id_node) { if (!id_node) {
DepsNodeFactory *factory = deg_type_get_factory(DEG_NODE_TYPE_ID_REF); DepsNodeFactory *factory = deg_type_get_factory(DEG_NODE_TYPE_ID_REF);
@@ -500,7 +500,7 @@ ID *Depsgraph::get_cow_id(const ID *id_orig) const
* We try to enforce that in debug builds, for for release we play a bit * We try to enforce that in debug builds, for for release we play a bit
* safer game here. * safer game here.
*/ */
if ((id_orig->tag & LIB_TAG_COPY_ON_WRITE) == 0) { if ((id_orig->tag & LIB_TAG_COPIED_ON_WRITE) == 0) {
/* TODO(sergey): This is nice sanity check to have, but it fails /* TODO(sergey): This is nice sanity check to have, but it fails
* in following situations: * in following situations:
* *

View File

@@ -240,6 +240,6 @@ ID *DEG_get_original_id(ID *id)
if (id->orig_id == NULL) { if (id->orig_id == NULL) {
return id; return id;
} }
BLI_assert((id->tag & LIB_TAG_COPY_ON_WRITE) != 0); BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0);
return (ID *)id->orig_id; return (ID *)id->orig_id;
} }

View File

@@ -542,6 +542,7 @@ void update_special_pointers(const Depsgraph *depsgraph,
BLI_assert(object_cow->derivedDeform == NULL); BLI_assert(object_cow->derivedDeform == NULL);
object_cow->mode = object_orig->mode; object_cow->mode = object_orig->mode;
object_cow->sculpt = object_orig->sculpt; object_cow->sculpt = object_orig->sculpt;
object_cow->runtime.mesh_orig = (Mesh *)object_cow->data;
if (object_cow->type == OB_ARMATURE) { if (object_cow->type == OB_ARMATURE) {
BKE_pose_remap_bone_pointers((bArmature *)object_cow->data, BKE_pose_remap_bone_pointers((bArmature *)object_cow->data,
object_cow->pose); object_cow->pose);
@@ -724,12 +725,12 @@ static void deg_backup_object_runtime(
Mesh *mesh_eval = object->runtime.mesh_eval; Mesh *mesh_eval = object->runtime.mesh_eval;
object_runtime_backup->runtime = object->runtime; object_runtime_backup->runtime = object->runtime;
BKE_object_runtime_reset(object); BKE_object_runtime_reset(object);
/* Currently object update will override actual object->data /* Object update will override actual object->data to an evaluated version.
* to an evaluated version. Need to make sure we don't have * Need to make sure we don't have data set to evaluated one before free
* data set to evaluated one before free anything. * anything.
*/ */
if (mesh_eval != NULL && object->data == mesh_eval) { if (mesh_eval != NULL && object->data == mesh_eval) {
object->data = mesh_eval->id.orig_id; object->data = object->runtime.mesh_orig;
} }
/* Store curve cache and make sure we don't free it. */ /* Store curve cache and make sure we don't free it. */
object_runtime_backup->curve_cache = object->curve_cache; object_runtime_backup->curve_cache = object->curve_cache;
@@ -742,8 +743,21 @@ static void deg_restore_object_runtime(
Object *object, Object *object,
const ObjectRuntimeBackup *object_runtime_backup) const ObjectRuntimeBackup *object_runtime_backup)
{ {
Mesh *mesh_orig = object->runtime.mesh_orig;
object->runtime = object_runtime_backup->runtime; object->runtime = object_runtime_backup->runtime;
object->runtime.mesh_orig = mesh_orig;
if (object->runtime.mesh_eval != NULL) { if (object->runtime.mesh_eval != NULL) {
if (object->id.recalc & ID_RECALC_GEOMETRY) {
/* If geometry is tagged for update it means, that part of
* evaluated mesh are not valid anymore. In this case we can not
* have any "persistent" pointers to point to an invalid data.
*
* We restore object's data datablock to an original copy of
* that datablock.
*/
object->data = mesh_orig;
}
else {
Mesh *mesh_eval = object->runtime.mesh_eval; Mesh *mesh_eval = object->runtime.mesh_eval;
/* Do same thing as object update: override actual object data /* Do same thing as object update: override actual object data
* pointer with evaluated datablock. * pointer with evaluated datablock.
@@ -754,8 +768,8 @@ static void deg_restore_object_runtime(
* original mesh during update, need to make sure no dead * original mesh during update, need to make sure no dead
* pointers are left behind. * pointers are left behind.
*/ */
Mesh *mesh = ((Mesh *)mesh_eval->id.orig_id); mesh_eval->edit_btmesh = mesh_orig->edit_btmesh;
mesh_eval->edit_btmesh = mesh->edit_btmesh; }
} }
} }
if (object_runtime_backup->curve_cache != NULL) { if (object_runtime_backup->curve_cache != NULL) {
@@ -995,8 +1009,8 @@ bool deg_validate_copy_on_write_datablock(ID *id_cow)
void deg_tag_copy_on_write_id(ID *id_cow, const ID *id_orig) void deg_tag_copy_on_write_id(ID *id_cow, const ID *id_orig)
{ {
BLI_assert(id_cow != id_orig); BLI_assert(id_cow != id_orig);
BLI_assert((id_orig->tag & LIB_TAG_COPY_ON_WRITE) == 0); BLI_assert((id_orig->tag & LIB_TAG_COPIED_ON_WRITE) == 0);
id_cow->tag |= LIB_TAG_COPY_ON_WRITE; id_cow->tag |= LIB_TAG_COPIED_ON_WRITE;
id_cow->orig_id = (ID *)id_orig; id_cow->orig_id = (ID *)id_orig;
} }

View File

@@ -467,8 +467,8 @@ enum {
LIB_TAG_PRE_EXISTING = 1 << 11, LIB_TAG_PRE_EXISTING = 1 << 11,
/* The datablock is a copy-on-write/localized version. */ /* The datablock is a copy-on-write/localized version. */
LIB_TAG_COPY_ON_WRITE = 1 << 12, LIB_TAG_COPIED_ON_WRITE = 1 << 12,
LIB_TAG_COPY_ON_WRITE_EVAL = 1 << 13, LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT = 1 << 13,
LIB_TAG_LOCALIZED = 1 << 14, LIB_TAG_LOCALIZED = 1 << 14,
/* RESET_NEVER tag datablock for freeing etc. behavior (usually set when copying real one into temp/runtime one). */ /* RESET_NEVER tag datablock for freeing etc. behavior (usually set when copying real one into temp/runtime one). */

View File

@@ -145,6 +145,11 @@ typedef struct ObjectDisplay {
/* Not saved in file! */ /* Not saved in file! */
typedef struct Object_Runtime { typedef struct Object_Runtime {
/* Original mesh pointer, before object->data was changed to point
* to mesh_eval.
* Is assigned by dependency craph's copy-on-write evaluation.
*/
struct Mesh *mesh_orig;
/* Mesh structure created during object evaluation. /* Mesh structure created during object evaluation.
* It has all modifiers applied. * It has all modifiers applied.
*/ */