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:
2017-07-28 12:27:34 +02:00
parent 9323182e73
commit 50cc0aa0d1
9 changed files with 89 additions and 29 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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;
}

View File

@@ -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. */

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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));