Depsgraph: Bring back visibility checks based on collection restrict flags

The title says it all actually, the idea is to speedup the following case:

- Visible duplicator of a restricted collection (reported as T56512),

One of the questionable change is that none of the view layer bases is
ignored now. This ensures corresponding objects will have copy-on-write
component evaluated, making it possible to access those pointers. The
evaluation of those objects is skipped.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D3641
This commit is contained in:
Sergey Sharybin 2018-08-23 16:17:06 +02:00
parent a9ecfc9653
commit f97d61c4bd
Notes: blender-bot 2023-02-14 10:29:30 +01:00
Referenced by issue #56512, Slow playback speed when duplicating collection is restricted
11 changed files with 196 additions and 116 deletions

View File

@ -34,6 +34,8 @@
#include "DNA_object_types.h"
#include "DNA_ID.h"
#include "BLI_stack.h"
extern "C" {
#include "BKE_animsys.h"
}
@ -43,6 +45,8 @@ extern "C" {
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/nodes/deg_node.h"
#include "intern/nodes/deg_node_id.h"
#include "intern/nodes/deg_node_component.h"
#include "intern/nodes/deg_node_operation.h"
#include "util/deg_util_foreach.h"
@ -50,8 +54,61 @@ extern "C" {
namespace DEG {
namespace {
void deg_graph_build_flush_layers(Depsgraph *graph)
{
BLI_Stack *stack = BLI_stack_new(sizeof(OperationDepsNode*),
"DEG flush layers stack");
foreach (OperationDepsNode *op_node, graph->operations) {
op_node->done = 0;
op_node->num_links_pending = 0;
foreach (DepsRelation *rel, op_node->outlinks) {
if ((rel->from->type == DEG_NODE_TYPE_OPERATION) &&
(rel->flag & DEPSREL_FLAG_CYCLIC) == 0)
{
++op_node->num_links_pending;
}
}
if (op_node->num_links_pending == 0) {
BLI_stack_push(stack, &op_node);
op_node->done = 1;
}
}
while (!BLI_stack_is_empty(stack)) {
OperationDepsNode *op_node;
BLI_stack_pop(stack, &op_node);
/* Flush layers to parents. */
foreach (DepsRelation *rel, op_node->inlinks) {
if (rel->from->type == DEG_NODE_TYPE_OPERATION) {
OperationDepsNode *op_from = (OperationDepsNode *)rel->from;
op_from->owner->owner->is_visible |=
op_node->owner->owner->is_visible;
}
}
/* Schedule parent nodes. */
foreach (DepsRelation *rel, op_node->inlinks) {
if (rel->from->type == DEG_NODE_TYPE_OPERATION) {
OperationDepsNode *op_from = (OperationDepsNode *)rel->from;
if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) {
BLI_assert(op_from->num_links_pending > 0);
--op_from->num_links_pending;
}
if (op_from->num_links_pending == 0 && op_from->done == 0) {
BLI_stack_push(stack, &op_from);
op_from->done = 1;
}
}
}
}
BLI_stack_free(stack);
}
} // namespace
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
{
deg_graph_build_flush_layers(graph);
/* Re-tag IDs for update if it was tagged before the relations
* update tag.
*/

View File

@ -141,6 +141,8 @@ DepsgraphNodeBuilder::DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph)
graph_(graph),
scene_(NULL),
view_layer_(NULL),
view_layer_index_(-1),
collection_(NULL),
cow_id_hash_(NULL)
{
}
@ -394,7 +396,7 @@ void DepsgraphNodeBuilder::build_id(ID *id) {
build_camera((Camera *)id);
break;
case ID_GR:
build_collection(DEG_COLLECTION_OWNER_UNKNOWN, (Collection *)id);
build_collection((Collection *)id);
break;
case ID_OB:
build_object(-1, (Object *)id, DEG_ID_LINKED_INDIRECTLY);
@ -445,46 +447,49 @@ void DepsgraphNodeBuilder::build_id(ID *id) {
}
}
void DepsgraphNodeBuilder::build_collection(
eDepsNode_CollectionOwner owner_type,
Collection *collection)
void DepsgraphNodeBuilder::build_collection(Collection *collection)
{
if (built_map_.checkIsBuiltAndTag(collection)) {
/* NOTE: Currently collections restrict flags only depend on collection
* itself and do not depend on a "context" (like, particle system
* visibility).
*
* If we ever change this, we need to update restrict flag here for an
* already built collection.
*/
return;
}
const bool allow_restrict_flags = (owner_type == DEG_COLLECTION_OWNER_SCENE);
if (allow_restrict_flags) {
const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT)
? COLLECTION_RESTRICT_VIEW
: COLLECTION_RESTRICT_RENDER;
if (collection->flag & restrict_flag) {
return;
}
}
Collection *current_state_collection = collection_;
collection_ = collection;
const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT)
? COLLECTION_RESTRICT_VIEW
: COLLECTION_RESTRICT_RENDER;
const bool is_parent_collection_restricted =
(current_state_collection == NULL)
? false
: (current_state_collection->flag & restrict_flag);
const bool is_collection_restricted = (collection->flag & restrict_flag);
const bool is_collection_visible =
!is_collection_restricted && !is_parent_collection_restricted;
/* Collection itself. */
add_id_node(&collection->id);
IDDepsNode *id_node = add_id_node(&collection->id);
id_node->is_visible = is_collection_visible;
/* Build collection objects. */
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
if (allow_restrict_flags) {
const int restrict_flag = (
(graph_->mode == DAG_EVAL_VIEWPORT) ?
OB_RESTRICT_VIEW :
OB_RESTRICT_RENDER);
if (cob->ob->restrictflag & restrict_flag) {
continue;
}
}
build_object(-1, cob->ob, DEG_ID_LINKED_INDIRECTLY);
build_object(
-1, cob->ob, DEG_ID_LINKED_INDIRECTLY, is_collection_visible);
}
/* Build child collections. */
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
build_collection(owner_type, child->collection);
build_collection(child->collection);
}
collection_ = current_state_collection;
}
void DepsgraphNodeBuilder::build_object(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state)
eDepsNode_LinkedState_Type linked_state,
bool is_visible)
{
const bool has_object = built_map_.checkIsBuiltAndTag(object);
/* Skip rest of components if the ID node was already there. */
@ -497,11 +502,13 @@ void DepsgraphNodeBuilder::build_object(int base_index,
build_object_flags(base_index, object, linked_state);
}
id_node->linked_state = max(id_node->linked_state, linked_state);
id_node->is_visible |= is_visible;
return;
}
/* Create ID node for object and begin init. */
IDDepsNode *id_node = add_id_node(&object->id);
id_node->linked_state = linked_state;
id_node->is_visible = is_visible;
object->customdata_mask = 0;
/* Various flags, flushing from bases/collections. */
build_object_flags(base_index, object, linked_state);
@ -562,7 +569,7 @@ void DepsgraphNodeBuilder::build_object(int base_index,
}
/* Object dupligroup. */
if (object->dup_group != NULL) {
build_collection(DEG_COLLECTION_OWNER_OBJECT, object->dup_group);
build_collection(object->dup_group);
}
}
@ -953,7 +960,7 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene)
/* objects - simulation participants */
if (rbw->group) {
build_collection(DEG_COLLECTION_OWNER_OBJECT, rbw->group);
build_collection(rbw->group);
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(rbw->group, object)
{
@ -1029,7 +1036,7 @@ void DepsgraphNodeBuilder::build_particles(Object *object)
break;
case PART_DRAW_GR:
if (part->dup_group != NULL) {
build_collection(DEG_COLLECTION_OWNER_OBJECT, part->dup_group);
build_collection(part->dup_group);
}
break;
}

View File

@ -163,11 +163,11 @@ struct DepsgraphNodeBuilder {
void build_view_layer(Scene *scene,
ViewLayer *view_layer,
eDepsNode_LinkedState_Type linked_state);
void build_collection(eDepsNode_CollectionOwner owner_type,
Collection *collection);
void build_collection(Collection *collection);
void build_object(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state);
eDepsNode_LinkedState_Type linked_state,
bool is_visible = true);
void build_object_flags(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state);
@ -245,6 +245,10 @@ protected:
Scene *scene_;
ViewLayer *view_layer_;
int view_layer_index_;
/* NOTE: Collection are possibly built recursively, so be careful when
* setting the current state.
*/
Collection *collection_;
GHash *cow_id_hash_;
BuilderMap built_map_;

View File

@ -75,7 +75,7 @@ void DepsgraphNodeBuilder::build_layer_collections(ListBase *lb)
continue;
}
if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) {
build_collection(DEG_COLLECTION_OWNER_SCENE, lc->collection);
build_collection(lc->collection);
}
build_layer_collections(&lc->layer_collections);
}
@ -108,10 +108,9 @@ void DepsgraphNodeBuilder::build_view_layer(
BASE_ENABLED_VIEWPORT : BASE_ENABLED_RENDER;
LISTBASE_FOREACH(Base *, base, &view_layer->object_bases) {
/* object itself */
if (base->flag & base_flag) {
build_object(base_index, base->object, linked_state);
base->object->select_color = select_color++;
}
const bool is_object_visible = (base->flag & base_flag);
build_object(base_index, base->object, linked_state, is_object_visible);
base->object->select_color = select_color++;
++base_index;
}
build_layer_collections(&view_layer->layer_collections);

View File

@ -409,7 +409,7 @@ void DepsgraphRelationBuilder::build_id(ID *id)
build_camera((Camera *)id);
break;
case ID_GR:
build_collection(DEG_COLLECTION_OWNER_UNKNOWN, NULL, (Collection *)id);
build_collection(NULL, (Collection *)id);
break;
case ID_OB:
build_object(NULL, (Object *)id);
@ -458,37 +458,19 @@ void DepsgraphRelationBuilder::build_id(ID *id)
}
void DepsgraphRelationBuilder::build_collection(
eDepsNode_CollectionOwner owner_type,
Object *object,
Collection *collection)
{
const bool allow_restrict_flags = (owner_type == DEG_COLLECTION_OWNER_SCENE);
if (allow_restrict_flags) {
const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT)
? COLLECTION_RESTRICT_VIEW
: COLLECTION_RESTRICT_RENDER;
if (collection->flag & restrict_flag) {
return;
}
}
const bool group_done = built_map_.checkIsBuiltAndTag(collection);
OperationKey object_transform_final_key(object != NULL ? &object->id : NULL,
DEG_NODE_TYPE_TRANSFORM,
DEG_OPCODE_TRANSFORM_FINAL);
if (!group_done) {
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
if (allow_restrict_flags) {
const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT)
? OB_RESTRICT_VIEW
: OB_RESTRICT_RENDER;
if (cob->ob->restrictflag & restrict_flag) {
continue;
}
}
build_object(NULL, cob->ob);
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
build_collection(owner_type, NULL, child->collection);
build_collection(NULL, child->collection);
}
}
if (object != NULL) {
@ -617,7 +599,7 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object)
}
/* Object dupligroup. */
if (object->dup_group != NULL) {
build_collection(DEG_COLLECTION_OWNER_OBJECT, object, object->dup_group);
build_collection(object, object->dup_group);
}
}
@ -1495,7 +1477,7 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
/* objects - simulation participants */
if (rbw->group) {
build_collection(DEG_COLLECTION_OWNER_OBJECT, NULL, rbw->group);
build_collection(NULL, rbw->group);
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(rbw->group, object)
{
@ -1677,7 +1659,7 @@ void DepsgraphRelationBuilder::build_particles(Object *object)
break;
case PART_DRAW_GR:
if (part->dup_group != NULL) {
build_collection(DEG_COLLECTION_OWNER_OBJECT, NULL, part->dup_group);
build_collection(NULL, part->dup_group);
LISTBASE_FOREACH (CollectionObject *, go, &part->dup_group->gobject) {
build_particles_visualization_object(object,
psys,

View File

@ -203,9 +203,7 @@ struct DepsgraphRelationBuilder
void build_id(ID *id);
void build_layer_collections(ListBase *lb);
void build_view_layer(Scene *scene, ViewLayer *view_layer);
void build_collection(eDepsNode_CollectionOwner owner_type,
Object *object,
Collection *collection);
void build_collection(Object *object, Collection *collection);
void build_object(Base *base, Object *object);
void build_object_flags(Base *base, Object *object);
void build_object_data(Object *object);

View File

@ -79,7 +79,7 @@ void DepsgraphRelationBuilder::build_layer_collections(ListBase *lb)
continue;
}
if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) {
build_collection(DEG_COLLECTION_OWNER_SCENE, NULL, lc->collection);
build_collection(NULL, lc->collection);
}
build_layer_collections(&lc->layer_collections);
}
@ -94,12 +94,8 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene, ViewLayer *view_la
* passed to the evaluation functions. During relations builder we only
* do NULL-pointer check of the base, so it's fine to pass original one.
*/
const int base_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ?
BASE_ENABLED_VIEWPORT : BASE_ENABLED_RENDER;
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (base->flag & base_flag) {
build_object(base, base->object);
}
build_object(base, base->object);
}
build_layer_collections(&view_layer->layer_collections);

View File

@ -280,15 +280,4 @@ typedef enum eDepsOperation_Code {
} eDepsOperation_Code;
const char *operationCodeAsString(eDepsOperation_Code opcode);
typedef enum eDepsNode_CollectionOwner {
/* Unknown owner of collection, collection is pulled directly, maybe
* via driver.
*/
DEG_COLLECTION_OWNER_UNKNOWN,
/* Collection belongs to a scene. */
DEG_COLLECTION_OWNER_SCENE,
/* Collection is used by object, as a dupli-system. */
DEG_COLLECTION_OWNER_OBJECT,
} eDepsNode_CollectionOwner;
} // namespace DEG

View File

@ -103,6 +103,19 @@ typedef struct CalculatePengindData {
Depsgraph *graph;
} CalculatePengindData;
static bool check_operation_node_visible(OperationDepsNode *op_node)
{
const ComponentDepsNode *comp_node = op_node->owner;
/* Special exception, copy on write component is to be always evaluated,
* to keep copied "database" in a consistent state.
*/
if (comp_node->type == DEG_NODE_TYPE_COPY_ON_WRITE) {
return true;
}
const IDDepsNode *id_node = comp_node->owner;
return id_node->is_visible;
}
static void calculate_pending_func(
void *__restrict data_v,
const int i,
@ -111,21 +124,35 @@ static void calculate_pending_func(
CalculatePengindData *data = (CalculatePengindData *)data_v;
Depsgraph *graph = data->graph;
OperationDepsNode *node = graph->operations[i];
/* Update counters, applies for both visible and invisible IDs. */
node->num_links_pending = 0;
node->scheduled = false;
/* count number of inputs that need updates */
if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) {
foreach (DepsRelation *rel, node->inlinks) {
if (rel->from->type == DEG_NODE_TYPE_OPERATION &&
(rel->flag & DEPSREL_FLAG_CYCLIC) == 0)
{
OperationDepsNode *from = (OperationDepsNode *)rel->from;
if ((from->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) {
++node->num_links_pending;
}
/* Invisible IDs requires no pending operations. */
if (!check_operation_node_visible(node)) {
return;
}
/* No need to bother with anything if node is not tagged for update. */
if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) {
return;
}
foreach (DepsRelation *rel, node->inlinks) {
if (rel->from->type == DEG_NODE_TYPE_OPERATION &&
(rel->flag & DEPSREL_FLAG_CYCLIC) == 0)
{
OperationDepsNode *from = (OperationDepsNode *)rel->from;
/* TODO(sergey): This is how old layer system was checking for the
* calculation, but how is it possible that visible object depends
* on an invisible? This is something what is prohibited after
* deg_graph_build_flush_layers().
*/
if (!check_operation_node_visible(from)) {
continue;
}
/* No need to vait for operation which is up to date. */
if ((from->flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) {
continue;
}
++node->num_links_pending;
}
}
}
@ -166,30 +193,44 @@ static void schedule_node(TaskPool *pool, Depsgraph *graph,
OperationDepsNode *node, bool dec_parents,
const int thread_id)
{
if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) {
if (dec_parents) {
BLI_assert(node->num_links_pending > 0);
atomic_sub_and_fetch_uint32(&node->num_links_pending, 1);
/* No need to schedule nodes of invisible ID. */
if (!check_operation_node_visible(node)) {
return;
}
/* No need to schedule operations which are not tagged for update, they are
* considered to be up to date.
*/
if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) {
return;
}
/* TODO(sergey): This is not strictly speaking safe to read
* num_links_pending.
*/
if (dec_parents) {
BLI_assert(node->num_links_pending > 0);
atomic_sub_and_fetch_uint32(&node->num_links_pending, 1);
}
/* Cal not schedule operation while its dependencies are not yet
* evaluated.
*/
if (node->num_links_pending != 0) {
return;
}
bool is_scheduled = atomic_fetch_and_or_uint8(
(uint8_t *)&node->scheduled, (uint8_t)true);
if (!is_scheduled) {
if (node->is_noop()) {
/* skip NOOP node, schedule children right away */
schedule_children(pool, graph, node, thread_id);
}
if (node->num_links_pending == 0) {
bool is_scheduled = atomic_fetch_and_or_uint8(
(uint8_t *)&node->scheduled, (uint8_t)true);
if (!is_scheduled) {
if (node->is_noop()) {
/* skip NOOP node, schedule children right away */
schedule_children(pool, graph, node, thread_id);
}
else {
/* children are scheduled once this task is completed */
BLI_task_pool_push_from_thread(pool,
deg_task_run_func,
node,
false,
TASK_PRIORITY_HIGH,
thread_id);
}
}
else {
/* children are scheduled once this task is completed */
BLI_task_pool_push_from_thread(pool,
deg_task_run_func,
node,
false,
TASK_PRIORITY_HIGH,
thread_id);
}
}
}

View File

@ -104,6 +104,7 @@ void IDDepsNode::init(const ID *id, const char *UNUSED(subdata))
id_orig = (ID *)id;
eval_flags = 0;
linked_state = DEG_ID_LINKED_INDIRECTLY;
is_visible = true;
components = BLI_ghash_new(id_deps_node_hash_key,
id_deps_node_hash_key_cmp,
@ -171,7 +172,8 @@ string IDDepsNode::identifier() const
BLI_snprintf(orig_ptr, sizeof(orig_ptr), "%p", id_orig);
BLI_snprintf(cow_ptr, sizeof(cow_ptr), "%p", id_cow);
return string(nodeTypeAsString(type)) + " : " + name +
" (orig: " + orig_ptr + ", eval: " + cow_ptr + ")";
" (orig: " + orig_ptr + ", eval: " + cow_ptr +
", is_visible " + (is_visible ? "true" : "false") + ")";
}
ComponentDepsNode *IDDepsNode::find_component(eDepsNode_Type type,

View File

@ -77,6 +77,11 @@ struct IDDepsNode : public DepsNode {
eDepsNode_LinkedState_Type linked_state;
/* Indicates the datablock is visible, meaning, it is to be evaluated by
* the dependency graph.
*/
bool is_visible;
DEG_DEPSNODE_DECLARE;
};