Depsgraph: Re-use evaluated mesh across frames and remove time dependency from all CoW components
This commit is a work forward having less updates during playback, which speeds things up a lot here. The idea is simple: stop update all copy-on-write datablocks (which implies full re-evaluation actually) on frame change and re-use existing evaluated meshes as much as possible. This brings playback speed to 24 fps on the dino test scene here. Performance drops down a lot when armature is animated tho, but that's because of need of tangent space calculation which we can't do much about from just a dependency graph. Hopefully this doesn't make copy-on-write too unstable, quick tests here are surviving fine.
This commit is contained in:
@@ -144,6 +144,12 @@ void constraint_walk(bConstraint * /*con*/,
|
||||
}
|
||||
}
|
||||
|
||||
void free_copy_on_write_datablock(void *id_v)
|
||||
{
|
||||
ID *id = (ID *)id_v;
|
||||
deg_free_copy_on_write_datablock(id);
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
/* ************ */
|
||||
@@ -153,18 +159,30 @@ void constraint_walk(bConstraint * /*con*/,
|
||||
|
||||
DepsgraphNodeBuilder::DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph) :
|
||||
m_bmain(bmain),
|
||||
m_graph(graph)
|
||||
m_graph(graph),
|
||||
m_cow_id_hash(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
DepsgraphNodeBuilder::~DepsgraphNodeBuilder()
|
||||
{
|
||||
if (m_cow_id_hash != NULL) {
|
||||
BLI_ghash_free(m_cow_id_hash, NULL, free_copy_on_write_datablock);
|
||||
}
|
||||
}
|
||||
|
||||
IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id)
|
||||
IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id, bool do_tag)
|
||||
{
|
||||
IDDepsNode *id_node = m_graph->add_id_node(id);
|
||||
#ifdef WITH_COPY_ON_WRITE
|
||||
IDDepsNode *id_node = NULL;
|
||||
ID *id_cow = (ID *)BLI_ghash_lookup(m_cow_id_hash, id);
|
||||
if (id_cow != NULL) {
|
||||
/* TODO(sergey): Is it possible to lookup and pop element from GHash
|
||||
* at the same time?
|
||||
*/
|
||||
BLI_ghash_remove(m_cow_id_hash, id, NULL, NULL);
|
||||
}
|
||||
id_node = m_graph->add_id_node(id, do_tag, id_cow);
|
||||
/* Currently all ID nodes are supposed to have copy-on-write logic.
|
||||
*
|
||||
* NOTE: Zero number of components indicates that ID node was just created.
|
||||
@@ -178,6 +196,8 @@ IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id)
|
||||
"", -1);
|
||||
m_graph->operations.push_back(op_cow);
|
||||
}
|
||||
#else
|
||||
IDDepsNode *id_node = m_graph->add_id_node(id);
|
||||
#endif
|
||||
return id_node;
|
||||
}
|
||||
@@ -301,7 +321,7 @@ ID *DepsgraphNodeBuilder::ensure_cow_id(ID *id_orig)
|
||||
/* ID is already remapped to copy-on-write. */
|
||||
return id_orig;
|
||||
}
|
||||
IDDepsNode *id_node = m_graph->add_id_node(id_orig, false);
|
||||
IDDepsNode *id_node = add_id_node(id_orig, false);
|
||||
return id_node->id_cow;
|
||||
}
|
||||
|
||||
@@ -328,11 +348,36 @@ void DepsgraphNodeBuilder::begin_build(Main *bmain) {
|
||||
/* XXX nested node trees are not included in tag-clearing above,
|
||||
* so we need to do this manually.
|
||||
*/
|
||||
FOREACH_NODETREE(bmain, nodetree, id) {
|
||||
FOREACH_NODETREE(bmain, nodetree, id)
|
||||
{
|
||||
if (id != (ID *)nodetree) {
|
||||
nodetree->id.tag &= ~LIB_TAG_DOIT;
|
||||
}
|
||||
} FOREACH_NODETREE_END
|
||||
}
|
||||
FOREACH_NODETREE_END;
|
||||
|
||||
#ifdef WITH_COPY_ON_WRITE
|
||||
/* Store existing copy-on-write versions of datablock, so we can re-use
|
||||
* them for new ID nodes.
|
||||
*/
|
||||
m_cow_id_hash = BLI_ghash_ptr_new("Depsgraph id hash");
|
||||
GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, m_graph->id_hash)
|
||||
{
|
||||
if (GS(id_node->id_orig->name) != ID_SCE) {
|
||||
continue;
|
||||
}
|
||||
if (deg_copy_on_write_is_expanded(id_node->id_cow)) {
|
||||
BLI_ghash_insert(m_cow_id_hash, id_node->id_orig, id_node->id_cow);
|
||||
id_node->id_cow = NULL;
|
||||
}
|
||||
}
|
||||
GHASH_FOREACH_END();
|
||||
#endif
|
||||
|
||||
/* Make sure graph has no nodes left from previous state. */
|
||||
m_graph->clear_all_nodes();
|
||||
m_graph->operations.clear();
|
||||
BLI_gset_clear(m_graph->entry_tags, NULL);
|
||||
}
|
||||
|
||||
void DepsgraphNodeBuilder::build_group(Scene *scene, Group *group)
|
||||
|
||||
@@ -107,7 +107,7 @@ struct DepsgraphNodeBuilder {
|
||||
|
||||
void begin_build(Main *bmain);
|
||||
|
||||
IDDepsNode *add_id_node(ID *id);
|
||||
IDDepsNode *add_id_node(ID *id, bool do_tag = true);
|
||||
TimeSourceDepsNode *add_time_source();
|
||||
|
||||
ComponentDepsNode *add_component_node(ID *id,
|
||||
@@ -206,6 +206,7 @@ struct DepsgraphNodeBuilder {
|
||||
protected:
|
||||
Main *m_bmain;
|
||||
Depsgraph *m_graph;
|
||||
GHash *m_cow_id_hash;
|
||||
};
|
||||
|
||||
} // namespace DEG
|
||||
|
||||
@@ -1917,7 +1917,7 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDDepsNode *id_node
|
||||
DEG_NODE_TYPE_COPY_ON_WRITE,
|
||||
DEG_OPCODE_COPY_ON_WRITE);
|
||||
/* XXX: This is a quick hack to make Alt-A to work. */
|
||||
add_relation(time_source_key, copy_on_write_key, "Fluxgate capacitor hack");
|
||||
// add_relation(time_source_key, copy_on_write_key, "Fluxgate capacitor hack");
|
||||
/* Resat of code is using rather low level trickery, so need to get some
|
||||
* explicit pointers.
|
||||
*/
|
||||
|
||||
@@ -279,14 +279,14 @@ IDDepsNode *Depsgraph::find_id_node(const ID *id) const
|
||||
return reinterpret_cast<IDDepsNode *>(BLI_ghash_lookup(id_hash, id));
|
||||
}
|
||||
|
||||
IDDepsNode *Depsgraph::add_id_node(ID *id, bool do_tag)
|
||||
IDDepsNode *Depsgraph::add_id_node(ID *id, bool do_tag, ID *id_cow_hint)
|
||||
{
|
||||
BLI_assert((id->tag & LIB_TAG_COPY_ON_WRITE) == 0);
|
||||
IDDepsNode *id_node = find_id_node(id);
|
||||
if (!id_node) {
|
||||
DepsNodeFactory *factory = deg_get_node_factory(DEG_NODE_TYPE_ID_REF);
|
||||
id_node = (IDDepsNode *)factory->create_node(id, "", id->name);
|
||||
id_node->init_copy_on_write();
|
||||
id_node->init_copy_on_write(id_cow_hint);
|
||||
if (do_tag) {
|
||||
id->tag |= LIB_TAG_DOIT;
|
||||
}
|
||||
@@ -310,6 +310,12 @@ void Depsgraph::clear_id_nodes()
|
||||
/* Stupid workaround to ensure we free IDs in a proper order. */
|
||||
GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, id_hash)
|
||||
{
|
||||
if (id_node->id_cow == NULL) {
|
||||
/* This means builder "stole" ownership of the copy-on-written
|
||||
* datablock for her own dirty needs.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
if (!deg_copy_on_write_is_expanded(id_node->id_cow)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ struct Depsgraph {
|
||||
TimeSourceDepsNode *find_time_source() const;
|
||||
|
||||
IDDepsNode *find_id_node(const ID *id) const;
|
||||
IDDepsNode *add_id_node(ID *id, bool do_tag = true);
|
||||
IDDepsNode *add_id_node(ID *id, bool do_tag = true, ID *id_cow_hint = NULL);
|
||||
void clear_id_nodes();
|
||||
|
||||
/* Add new relationship between two nodes. */
|
||||
|
||||
@@ -257,6 +257,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
|
||||
/* Tag all relations for update. */
|
||||
void DEG_relations_tag_update(Main *bmain)
|
||||
{
|
||||
DEG_DEBUG_PRINTF("%s: Tagging relations for update.\n", __func__);
|
||||
for (Scene *scene = (Scene *)bmain->scene.first;
|
||||
scene != NULL;
|
||||
scene = (Scene *)scene->id.next)
|
||||
@@ -285,11 +286,6 @@ void DEG_scene_relations_update(Main *bmain, Scene *scene)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear all previous nodes and operations. */
|
||||
graph->clear_all_nodes();
|
||||
graph->operations.clear();
|
||||
BLI_gset_clear(graph->entry_tags, NULL);
|
||||
|
||||
/* Build new nodes and relations. */
|
||||
DEG_graph_build_from_scene(reinterpret_cast< ::Depsgraph * >(graph),
|
||||
bmain,
|
||||
|
||||
@@ -288,6 +288,16 @@ void deg_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id, int flag)
|
||||
}
|
||||
if (flag & OB_RECALC_DATA) {
|
||||
id_tag_update_object_data(graph, id_node);
|
||||
#ifdef WITH_COPY_ON_WRITE
|
||||
if (flag & DEG_TAG_COPY_ON_WRITE) {
|
||||
const short id_type = GS(id_node->id_orig->name);
|
||||
if (id_type == ID_OB) {
|
||||
Object *object = (Object *)id_node->id_orig;
|
||||
ID *ob_data = (ID *)object->data;
|
||||
DEG_id_tag_update_ex(bmain, ob_data, flag);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (flag & OB_RECALC_TIME) {
|
||||
id_tag_update_object_time(graph, id_node);
|
||||
@@ -337,16 +347,7 @@ void deg_graph_on_visible_update(Main *bmain, Scene *scene, Depsgraph *graph)
|
||||
* TODO(sergey): Need to generalize this somehow.
|
||||
*/
|
||||
if (id_type == ID_OB) {
|
||||
Object *object = (Object *)id_node->id_orig;
|
||||
flag |= OB_RECALC_OB;
|
||||
if (ELEM(object->type, OB_MESH,
|
||||
OB_CURVE,
|
||||
OB_SURF,
|
||||
OB_FONT,
|
||||
OB_MBALL))
|
||||
{
|
||||
flag |= OB_RECALC_DATA;
|
||||
}
|
||||
flag |= OB_RECALC_OB | OB_RECALC_DATA | DEG_TAG_COPY_ON_WRITE;
|
||||
}
|
||||
deg_graph_id_tag_update(bmain, graph, id_node->id_orig, flag);
|
||||
}
|
||||
|
||||
@@ -661,7 +661,13 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph,
|
||||
DEG_COW_PRINT("Expanding datablock for %s: id_orig=%p id_cow=%p\n",
|
||||
id_orig->name, id_orig, id_cow);
|
||||
/* Sanity checks. */
|
||||
BLI_assert(check_datablock_expanded(id_cow) == false);
|
||||
/* NOTE: Disabled for now, conflicts when re-using evaluated datablock when
|
||||
* rebuilding dependencies.
|
||||
*/
|
||||
if (check_datablock_expanded(id_cow) && create_placeholders) {
|
||||
deg_free_copy_on_write_datablock(id_cow);
|
||||
}
|
||||
// BLI_assert(check_datablock_expanded(id_cow) == false);
|
||||
/* Copy data from original ID to a copied version. */
|
||||
/* TODO(sergey): Avoid doing full ID copy somehow, make Mesh to reference
|
||||
* original geometry arrays for until those are modified.
|
||||
|
||||
@@ -183,8 +183,13 @@ void IDDepsNode::init_copy_on_write(ID *id_cow_hint)
|
||||
* it is actually needed.
|
||||
*/
|
||||
if (id_cow_hint != NULL) {
|
||||
BLI_assert(deg_copy_on_write_is_needed(id_orig));
|
||||
id_cow = id_cow_hint;
|
||||
// BLI_assert(deg_copy_on_write_is_needed(id_orig));
|
||||
if (deg_copy_on_write_is_needed(id_orig)) {
|
||||
id_cow = id_cow_hint;
|
||||
}
|
||||
else {
|
||||
id_cow = id_orig;
|
||||
}
|
||||
}
|
||||
else if (deg_copy_on_write_is_needed(id_orig)) {
|
||||
id_cow = (ID *)BKE_libblock_alloc_notest(GS(id_orig->name));
|
||||
|
||||
Reference in New Issue
Block a user