Depsgraph: Synchronize flushing with 2.8 branch
Not only this helps merges form master to the branch, but also: - Allows us to production-check changes as soon as possible. - Avoids some unnecessary editors update about ID changes. - Adds small optimization on queue size by always keeping one of the pointers outside of the queue.
This commit is contained in:
@@ -54,12 +54,19 @@ extern "C" {
|
||||
|
||||
namespace DEG {
|
||||
|
||||
enum {
|
||||
ID_STATE_NONE = 0,
|
||||
ID_STATE_MODIFIED = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
COMPONENT_STATE_NONE = 0,
|
||||
COMPONENT_STATE_SCHEDULED = 1,
|
||||
COMPONENT_STATE_DONE = 2,
|
||||
};
|
||||
|
||||
typedef std::deque<OperationDepsNode *> FlushQueue;
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(sergey): De-duplicate with depsgraph_tag,cc
|
||||
@@ -75,176 +82,200 @@ void lib_id_recalc_data_tag(Main *bmain, ID *id)
|
||||
DEG_id_type_tag(bmain, GS(id->name));
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
typedef std::deque<OperationDepsNode *> FlushQueue;
|
||||
|
||||
static void flush_init_func(void *data_v, int i)
|
||||
void flush_init_operation_node_func(void *data_v, int i)
|
||||
{
|
||||
/* ID node's done flag is used to avoid multiple editors update
|
||||
* for the same ID.
|
||||
*/
|
||||
Depsgraph *graph = (Depsgraph *)data_v;
|
||||
OperationDepsNode *node = graph->operations[i];
|
||||
ComponentDepsNode *comp_node = node->owner;
|
||||
IDDepsNode *id_node = comp_node->owner;
|
||||
id_node->done = 0;
|
||||
comp_node->done = COMPONENT_STATE_NONE;
|
||||
node->scheduled = false;
|
||||
}
|
||||
|
||||
void flush_init_id_node_func(void *data_v, int i)
|
||||
{
|
||||
Depsgraph *graph = (Depsgraph *)data_v;
|
||||
IDDepsNode *id_node = graph->id_nodes[i];
|
||||
id_node->done = ID_STATE_NONE;
|
||||
GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components)
|
||||
comp_node->done = COMPONENT_STATE_NONE;
|
||||
GHASH_FOREACH_END();
|
||||
}
|
||||
|
||||
BLI_INLINE void flush_prepare(Depsgraph *graph)
|
||||
{
|
||||
const int num_operations = graph->operations.size();
|
||||
BLI_task_parallel_range(0, num_operations,
|
||||
graph,
|
||||
flush_init_operation_node_func,
|
||||
(num_operations > 256));
|
||||
const int num_id_nodes = graph->id_nodes.size();
|
||||
BLI_task_parallel_range(0, num_id_nodes,
|
||||
graph,
|
||||
flush_init_id_node_func,
|
||||
(num_id_nodes > 256));
|
||||
}
|
||||
|
||||
BLI_INLINE void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue)
|
||||
{
|
||||
GSET_FOREACH_BEGIN(OperationDepsNode *, op_node, graph->entry_tags)
|
||||
{
|
||||
queue->push_back(op_node);
|
||||
op_node->scheduled = true;
|
||||
}
|
||||
GSET_FOREACH_END();
|
||||
}
|
||||
|
||||
BLI_INLINE void flush_handle_id_node(IDDepsNode *id_node)
|
||||
{
|
||||
id_node->done = ID_STATE_MODIFIED;
|
||||
}
|
||||
|
||||
/* TODO(sergey): We can reduce number of arguments here. */
|
||||
BLI_INLINE void flush_handle_component_node(Depsgraph * /*graph*/,
|
||||
IDDepsNode *id_node,
|
||||
ComponentDepsNode *comp_node,
|
||||
FlushQueue *queue)
|
||||
{
|
||||
/* We only handle component once. */
|
||||
if (comp_node->done == COMPONENT_STATE_DONE) {
|
||||
return;
|
||||
}
|
||||
comp_node->done = COMPONENT_STATE_DONE;
|
||||
/* Tag all required operations in component for update. */
|
||||
foreach (OperationDepsNode *op, comp_node->operations) {
|
||||
op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
|
||||
}
|
||||
if (GS(id_node->id->name) == ID_OB) {
|
||||
Object *object = (Object *)id_node->id;
|
||||
/* This code is used to preserve those areas which does
|
||||
* direct object update,
|
||||
*
|
||||
* Plus it ensures visibility changes and relations and
|
||||
* layers visibility update has proper flags to work with.
|
||||
*/
|
||||
switch (comp_node->type) {
|
||||
case DEG_NODE_TYPE_UNDEFINED:
|
||||
case DEG_NODE_TYPE_OPERATION:
|
||||
case DEG_NODE_TYPE_TIMESOURCE:
|
||||
case DEG_NODE_TYPE_ID_REF:
|
||||
case DEG_NODE_TYPE_SEQUENCER:
|
||||
case NUM_DEG_NODE_TYPES:
|
||||
/* Ignore, does not translate to object component. */
|
||||
BLI_assert(!"This should never happen!");
|
||||
break;
|
||||
case DEG_NODE_TYPE_ANIMATION:
|
||||
object->recalc |= OB_RECALC_TIME;
|
||||
break;
|
||||
case DEG_NODE_TYPE_TRANSFORM:
|
||||
object->recalc |= OB_RECALC_OB;
|
||||
break;
|
||||
case DEG_NODE_TYPE_GEOMETRY:
|
||||
case DEG_NODE_TYPE_EVAL_POSE:
|
||||
case DEG_NODE_TYPE_BONE:
|
||||
case DEG_NODE_TYPE_EVAL_PARTICLES:
|
||||
case DEG_NODE_TYPE_SHADING:
|
||||
case DEG_NODE_TYPE_CACHE:
|
||||
case DEG_NODE_TYPE_PROXY:
|
||||
object->recalc |= OB_RECALC_DATA;
|
||||
break;
|
||||
case DEG_NODE_TYPE_PARAMETERS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* When some target changes bone, we might need to re-run the
|
||||
* whole IK solver, otherwise result might be unpredictable.
|
||||
*/
|
||||
if (comp_node->type == DEG_NODE_TYPE_BONE) {
|
||||
ComponentDepsNode *pose_comp =
|
||||
id_node->find_component(DEG_NODE_TYPE_EVAL_POSE);
|
||||
BLI_assert(pose_comp != NULL);
|
||||
if (pose_comp->done == COMPONENT_STATE_NONE) {
|
||||
queue->push_front(pose_comp->get_entry_operation());
|
||||
pose_comp->done = COMPONENT_STATE_SCHEDULED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule children of the given operation node for traversal.
|
||||
*
|
||||
* One of the children will by-pass the queue and will be returned as a function
|
||||
* return value, so it can start being handled right away, without building too
|
||||
* much of a queue.
|
||||
*/
|
||||
BLI_INLINE OperationDepsNode *flush_schedule_children(
|
||||
OperationDepsNode *op_node,
|
||||
FlushQueue *queue)
|
||||
{
|
||||
OperationDepsNode *result = NULL;
|
||||
foreach (DepsRelation *rel, op_node->outlinks) {
|
||||
OperationDepsNode *to_node = (OperationDepsNode *)rel->to;
|
||||
if (to_node->scheduled == false) {
|
||||
if (result != NULL) {
|
||||
queue->push_front(to_node);
|
||||
}
|
||||
else {
|
||||
result = to_node;
|
||||
}
|
||||
to_node->scheduled = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
BLI_INLINE void flush_editors_id_update(Main *bmain,
|
||||
Depsgraph *graph)
|
||||
{
|
||||
foreach (IDDepsNode *id_node, graph->id_nodes) {
|
||||
if (id_node->done != ID_STATE_MODIFIED) {
|
||||
continue;
|
||||
}
|
||||
/* TODO(sergey): Do we need to pass original or evaluated ID here? */
|
||||
ID *id = id_node->id;
|
||||
deg_editors_id_update(bmain, id);
|
||||
lib_id_recalc_tag(bmain, id);
|
||||
/* TODO(sergey): For until we've got proper data nodes in the graph. */
|
||||
lib_id_recalc_data_tag(bmain, id);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/* Flush updates from tagged nodes outwards until all affected nodes
|
||||
* are tagged.
|
||||
*/
|
||||
void deg_graph_flush_updates(Main *bmain, Depsgraph *graph)
|
||||
{
|
||||
/* Sanity check. */
|
||||
if (graph == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Sanity checks. */
|
||||
BLI_assert(bmain != NULL);
|
||||
BLI_assert(graph != NULL);
|
||||
/* Nothing to update, early out. */
|
||||
if (BLI_gset_size(graph->entry_tags) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO(sergey): With a bit of flag magic we can get rid of this
|
||||
* extra loop.
|
||||
*/
|
||||
const int num_operations = graph->operations.size();
|
||||
const bool do_threads = num_operations > 256;
|
||||
BLI_task_parallel_range(0,
|
||||
num_operations,
|
||||
graph,
|
||||
flush_init_func,
|
||||
do_threads);
|
||||
|
||||
/* Reset all flags, get ready for the flush. */
|
||||
flush_prepare(graph);
|
||||
/* Starting from the tagged "entry" nodes, flush outwards. */
|
||||
FlushQueue queue;
|
||||
/* Starting from the tagged "entry" nodes, flush outwards... */
|
||||
/* NOTE: Also need to ensure that for each of these, there is a path back to
|
||||
* root, or else they won't be done.
|
||||
* NOTE: Count how many nodes we need to handle - entry nodes may be
|
||||
* component nodes which don't count for this purpose!
|
||||
*/
|
||||
GSET_FOREACH_BEGIN(OperationDepsNode *, node, graph->entry_tags)
|
||||
{
|
||||
queue.push_back(node);
|
||||
node->scheduled = true;
|
||||
}
|
||||
GSET_FOREACH_END();
|
||||
|
||||
int num_flushed_objects = 0;
|
||||
flush_schedule_entrypoints(graph, &queue);
|
||||
/* Do actual flush. */
|
||||
while (!queue.empty()) {
|
||||
OperationDepsNode *node = queue.front();
|
||||
OperationDepsNode *op_node = queue.front();
|
||||
queue.pop_front();
|
||||
|
||||
for (;;) {
|
||||
node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
|
||||
|
||||
ComponentDepsNode *comp_node = node->owner;
|
||||
while (op_node != NULL) {
|
||||
/* Tag operation as required for update. */
|
||||
op_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
|
||||
/* Inform corresponding ID and component nodes about the change. */
|
||||
ComponentDepsNode *comp_node = op_node->owner;
|
||||
IDDepsNode *id_node = comp_node->owner;
|
||||
|
||||
ID *id = id_node->id;
|
||||
if (id_node->done == 0) {
|
||||
deg_editors_id_update(bmain, id);
|
||||
lib_id_recalc_tag(bmain, id);
|
||||
/* TODO(sergey): For until we've got proper data nodes in the graph. */
|
||||
lib_id_recalc_data_tag(bmain, id);
|
||||
}
|
||||
|
||||
if (comp_node->done != COMPONENT_STATE_DONE) {
|
||||
Object *object = NULL;
|
||||
if (GS(id->name) == ID_OB) {
|
||||
object = (Object *)id;
|
||||
if (id_node->done == 0) {
|
||||
++num_flushed_objects;
|
||||
}
|
||||
}
|
||||
foreach (OperationDepsNode *op, comp_node->operations) {
|
||||
op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
|
||||
}
|
||||
if (object != NULL) {
|
||||
/* This code is used to preserve those areas which does
|
||||
* direct object update,
|
||||
*
|
||||
* Plus it ensures visibility changes and relations and
|
||||
* layers visibility update has proper flags to work with.
|
||||
*/
|
||||
switch (comp_node->type) {
|
||||
case DEG_NODE_TYPE_UNDEFINED:
|
||||
case DEG_NODE_TYPE_OPERATION:
|
||||
case DEG_NODE_TYPE_TIMESOURCE:
|
||||
case DEG_NODE_TYPE_ID_REF:
|
||||
case DEG_NODE_TYPE_SEQUENCER:
|
||||
case NUM_DEG_NODE_TYPES:
|
||||
/* Ignore, does not translate to object component. */
|
||||
BLI_assert(!"This should never happen!");
|
||||
break;
|
||||
case DEG_NODE_TYPE_ANIMATION:
|
||||
object->recalc |= OB_RECALC_TIME;
|
||||
break;
|
||||
case DEG_NODE_TYPE_TRANSFORM:
|
||||
object->recalc |= OB_RECALC_OB;
|
||||
break;
|
||||
case DEG_NODE_TYPE_GEOMETRY:
|
||||
case DEG_NODE_TYPE_EVAL_POSE:
|
||||
case DEG_NODE_TYPE_BONE:
|
||||
case DEG_NODE_TYPE_EVAL_PARTICLES:
|
||||
case DEG_NODE_TYPE_SHADING:
|
||||
case DEG_NODE_TYPE_CACHE:
|
||||
case DEG_NODE_TYPE_PROXY:
|
||||
object->recalc |= OB_RECALC_DATA;
|
||||
break;
|
||||
case DEG_NODE_TYPE_PARAMETERS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* When some target changes bone, we might need to re-run the
|
||||
* whole IK solver, otherwise result might be unpredictable.
|
||||
*/
|
||||
if (comp_node->type == DEG_NODE_TYPE_BONE) {
|
||||
ComponentDepsNode *pose_comp =
|
||||
id_node->find_component(DEG_NODE_TYPE_EVAL_POSE);
|
||||
BLI_assert(pose_comp != NULL);
|
||||
if (pose_comp->done == COMPONENT_STATE_NONE) {
|
||||
queue.push_front(pose_comp->get_entry_operation());
|
||||
pose_comp->done = COMPONENT_STATE_SCHEDULED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
id_node->done = 1;
|
||||
comp_node->done = COMPONENT_STATE_DONE;
|
||||
|
||||
/* Flush to nodes along links... */
|
||||
/* TODO(sergey): This is mainly giving speedup due ot less queue pushes, which
|
||||
* reduces number of memory allocations.
|
||||
*
|
||||
* We should try solve the allocation issue instead of doing crazy things here.
|
||||
*/
|
||||
if (node->outlinks.size() == 1) {
|
||||
OperationDepsNode *to_node = (OperationDepsNode *)node->outlinks[0]->to;
|
||||
if (to_node->scheduled == false) {
|
||||
to_node->scheduled = true;
|
||||
node = to_node;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach (DepsRelation *rel, node->outlinks) {
|
||||
OperationDepsNode *to_node = (OperationDepsNode *)rel->to;
|
||||
if (to_node->scheduled == false) {
|
||||
queue.push_front(to_node);
|
||||
to_node->scheduled = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
flush_handle_id_node(id_node);
|
||||
flush_handle_component_node(graph,
|
||||
id_node,
|
||||
comp_node,
|
||||
&queue);
|
||||
/* Flush to nodes along links. */
|
||||
op_node = flush_schedule_children(op_node, &queue);
|
||||
}
|
||||
}
|
||||
DEG_DEBUG_PRINTF("Update flushed to %d objects\n", num_flushed_objects);
|
||||
/* Inform editors about all changes. */
|
||||
flush_editors_id_update(bmain, graph);
|
||||
}
|
||||
|
||||
static void graph_clear_func(void *data_v, int i)
|
||||
|
||||
Reference in New Issue
Block a user