Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,518 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Evaluation engine entry-points for Depsgraph Engine.
*/
#include <atomic>
#include <cstdint>
#include "intern/eval/deg_eval.h"
#include "BLI_function_ref.hh"
#include "BLI_gsqueue.h"
#include "BLI_task.h"
#include "BLI_time.h"
#include "BKE_global.hh"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#ifdef WITH_PYTHON
# include "BPY_extern.hh"
#endif
#include "atomic_ops.h"
#include "intern/depsgraph.hh"
#include "intern/depsgraph_relation.hh"
#include "intern/depsgraph_tag.hh"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/eval/deg_eval_flush.h"
#include "intern/eval/deg_eval_stats.h"
#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_operation.hh"
namespace blender::deg {
namespace {
struct DepsgraphEvalState;
void deg_task_run_func(TaskPool *pool, void *taskdata);
void schedule_children(DepsgraphEvalState *state,
OperationNode *node,
FunctionRef<void(OperationNode *node)> schedule_fn);
/* Denotes which part of dependency graph is being evaluated. */
enum class EvaluationStage {
/* Stage 1: Only Copy-on-Write operations are to be evaluated, prior to anything else.
* This allows other operations to access its dependencies when there is a dependency cycle
* involved. */
COPY_ON_EVAL,
/* Evaluate actual ID nodes visibility based on the current state of animation and drivers. */
DYNAMIC_VISIBILITY,
/* Threaded evaluation of all possible operations. */
THREADED_EVALUATION,
/* Workaround for areas which can not be evaluated in threads.
*
* For example, meta-balls, which are iterating over all bases and are requesting dupli-lists
* to see whether there are meta-balls inside. */
SINGLE_THREADED_WORKAROUND,
};
struct DepsgraphEvalState {
Depsgraph *graph;
bool do_stats;
EvaluationStage stage;
bool need_update_pending_parents = true;
bool need_single_thread_pass = false;
};
void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_node)
{
blender::Depsgraph *depsgraph = reinterpret_cast<blender::Depsgraph *>(state->graph);
/* Sanity checks. */
BLI_assert_msg(!operation_node->is_noop(), "NOOP nodes should not actually be scheduled");
/* Perform operation. */
if (state->do_stats) {
const double start_time = BLI_time_now_seconds();
operation_node->evaluate(depsgraph);
operation_node->stats.current_time += BLI_time_now_seconds() - start_time;
}
else {
operation_node->evaluate(depsgraph);
}
/* Clear the flag early on, allowing partial updates without re-evaluating the same node multiple
* times.
* This is a thread-safe modification as the node's flags are only read for a non-scheduled nodes
* and this node has been scheduled. */
operation_node->flag &= ~DEPSOP_FLAG_CLEAR_ON_EVAL;
}
void deg_task_run_func(TaskPool *pool, void *taskdata)
{
void *userdata_v = BLI_task_pool_user_data(pool);
DepsgraphEvalState *state = static_cast<DepsgraphEvalState *>(userdata_v);
/* Evaluate node. */
OperationNode *operation_node = reinterpret_cast<OperationNode *>(taskdata);
evaluate_node(state, operation_node);
/* Schedule children. */
schedule_children(state, operation_node, [&](OperationNode *node) {
BLI_task_pool_push(pool, deg_task_run_func, node, false, nullptr);
});
}
bool check_operation_node_visible(const DepsgraphEvalState *state, OperationNode *op_node)
{
const ComponentNode *comp_node = op_node->owner;
/* Special case for copy-on-eval component: it is to be always evaluated, to keep copied
* "database" in a consistent state. */
if (comp_node->type == NodeType::COPY_ON_EVAL) {
return true;
}
/* Special case for dynamic visibility pass: the actual visibility is not yet known, so limit to
* only operations which affects visibility. */
if (state->stage == EvaluationStage::DYNAMIC_VISIBILITY) {
return op_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
}
return comp_node->affects_visible_id;
}
void calculate_pending_parents_for_node(const DepsgraphEvalState *state, OperationNode *node)
{
/* Update counters, applies for both visible and invisible IDs. */
node->num_links_pending = 0;
node->scheduled = false;
/* Invisible IDs requires no pending operations. */
if (!check_operation_node_visible(state, node)) {
return;
}
/* No need to bother with anything if node is not tagged for update. */
if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) {
return;
}
for (Relation *rel : node->inlinks) {
if (rel->from->type == NodeType::OPERATION && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
OperationNode *from = static_cast<OperationNode *>(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(state, from)) {
continue;
}
/* No need to wait for operation which is up to date. */
if ((from->flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) {
continue;
}
++node->num_links_pending;
}
}
}
void calculate_pending_parents_if_needed(DepsgraphEvalState *state)
{
if (!state->need_update_pending_parents) {
return;
}
for (OperationNode *node : state->graph->operations) {
calculate_pending_parents_for_node(state, node);
}
state->need_update_pending_parents = false;
}
void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph)
{
/* Clear tags and other things which needs to be clear. */
if (state->do_stats) {
for (OperationNode *node : graph->operations) {
node->stats.reset_current();
}
}
}
bool is_metaball_object_operation(const OperationNode *operation_node)
{
const ComponentNode *component_node = operation_node->owner;
const IDNode *id_node = component_node->owner;
/* This runs after the COPY_ON_EVAL stage which creates id_cow. */
BLI_assert(id_node->id_cow);
if (GS(id_node->id_cow->name) != ID_OB) {
return false;
}
const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
return object->type == OB_MBALL;
}
/* Simulation modifiers with sub-frames (fluid domain, dynamic paint canvas) perform direct updates
* of other objects, which can cause race conditions over certain data (#115636). Unless and until
* sub-steps are fully supported in depsgraph evaluation such objects must use single-threaded
* evaluation. */
bool is_modifier_subframe_operation(const OperationNode *operation_node)
{
const ComponentNode *component_node = operation_node->owner;
const IDNode *id_node = component_node->owner;
/* This runs after the COPY_ON_EVAL stage which creates id_cow. */
BLI_assert(id_node->id_cow);
if (GS(id_node->id_cow->name) != ID_OB) {
return false;
}
const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
for (const ModifierData &md : object->modifiers) {
if (md.type == eModifierType_Fluid) {
const auto &fmd = reinterpret_cast<const FluidModifierData &>(md);
if (fmd.type == MOD_FLUID_TYPE_DOMAIN) {
return true;
}
}
if (md.type == eModifierType_DynamicPaint) {
const auto &dmd = reinterpret_cast<const DynamicPaintModifierData &>(md);
if (dmd.type == MOD_DYNAMICPAINT_TYPE_CANVAS) {
return true;
}
}
}
return false;
}
bool need_evaluate_operation_at_stage(DepsgraphEvalState *state,
const OperationNode *operation_node)
{
const ComponentNode *component_node = operation_node->owner;
switch (state->stage) {
case EvaluationStage::COPY_ON_EVAL:
return (component_node->type == NodeType::COPY_ON_EVAL);
case EvaluationStage::DYNAMIC_VISIBILITY:
return operation_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
case EvaluationStage::THREADED_EVALUATION:
if (is_metaball_object_operation(operation_node)) {
state->need_single_thread_pass = true;
return false;
}
if (is_modifier_subframe_operation(operation_node)) {
state->need_single_thread_pass = true;
return false;
}
return true;
case EvaluationStage::SINGLE_THREADED_WORKAROUND:
return true;
}
BLI_assert_msg(0, "Unhandled evaluation stage, should never happen.");
return false;
}
/* Schedule a node if it needs evaluation.
* dec_parents: Decrement pending parents count, true when child nodes are
* scheduled after a task has been completed.
*/
void schedule_node(DepsgraphEvalState *state,
OperationNode *node,
bool dec_parents,
const FunctionRef<void(OperationNode *node)> schedule_fn)
{
/* No need to schedule nodes of invisible ID. */
if (!check_operation_node_visible(state, 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;
}
/* During the copy-on-eval stage only schedule copy-on-eval nodes. */
if (!need_evaluate_operation_at_stage(state, node)) {
return;
}
/* Actually schedule the node. */
bool is_scheduled = atomic_fetch_and_or_uint8(reinterpret_cast<uint8_t *>(&node->scheduled),
uint8_t(true));
if (!is_scheduled) {
if (node->is_noop()) {
/* Clear flags to avoid affecting subsequent update propagation.
* For normal nodes these are cleared when it is evaluated. */
node->flag &= ~DEPSOP_FLAG_CLEAR_ON_EVAL;
/* skip NOOP node, schedule children right away */
schedule_children(state, node, schedule_fn);
}
else {
/* children are scheduled once this task is completed */
schedule_fn(node);
}
}
}
void schedule_graph(DepsgraphEvalState *state,
const FunctionRef<void(OperationNode *node)> schedule_fn)
{
for (OperationNode *node : state->graph->operations) {
schedule_node(state, node, false, schedule_fn);
}
}
void schedule_children(DepsgraphEvalState *state,
OperationNode *node,
const FunctionRef<void(OperationNode *node)> schedule_fn)
{
for (Relation *rel : node->outlinks) {
OperationNode *child = static_cast<OperationNode *>(rel->to);
BLI_assert(child->type == NodeType::OPERATION);
if (child->scheduled) {
/* Happens when having cyclic dependencies. */
continue;
}
schedule_node(state, child, (rel->flag & RELATION_FLAG_CYCLIC) == 0, schedule_fn);
}
}
/* Evaluate given stage of the dependency graph evaluation using multiple threads.
*
* NOTE: Will assign the `state->stage` to the given stage. */
void evaluate_graph_threaded_stage(DepsgraphEvalState *state,
TaskPool *task_pool,
const EvaluationStage stage)
{
state->stage = stage;
calculate_pending_parents_if_needed(state);
schedule_graph(state, [&](OperationNode *node) {
BLI_task_pool_push(task_pool, deg_task_run_func, node, false, nullptr);
});
BLI_task_pool_work_and_wait(task_pool);
}
/* Evaluate remaining operations of the dependency graph in a single threaded manner. */
void evaluate_graph_single_threaded_if_needed(DepsgraphEvalState *state)
{
if (!state->need_single_thread_pass) {
return;
}
BLI_assert(!state->need_update_pending_parents);
state->stage = EvaluationStage::SINGLE_THREADED_WORKAROUND;
GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *));
auto schedule_node_to_queue = [&](OperationNode *node) {
BLI_gsqueue_push(evaluation_queue, &node);
};
schedule_graph(state, schedule_node_to_queue);
while (!BLI_gsqueue_is_empty(evaluation_queue)) {
OperationNode *operation_node;
BLI_gsqueue_pop(evaluation_queue, &operation_node);
evaluate_node(state, operation_node);
schedule_children(state, operation_node, schedule_node_to_queue);
}
BLI_gsqueue_free(evaluation_queue);
}
void depsgraph_ensure_view_layer(Depsgraph *graph)
{
/* We update evaluated scene in the following cases:
* - It was not expanded yet.
* - It was tagged for update of evaluated component.
* This allows us to have proper view layer pointer. */
Scene *scene_cow = graph->scene_cow;
if (deg_eval_copy_is_expanded(&scene_cow->id) &&
(scene_cow->id.recalc & ID_RECALC_SYNC_TO_EVAL) == 0)
{
return;
}
const IDNode *scene_id_node = graph->find_id_node(&graph->scene->id);
deg_update_eval_copy_datablock(graph, scene_id_node);
}
TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state)
{
if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) {
return BLI_task_pool_create_no_threads(state);
}
return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH);
}
} // namespace
void deg_evaluate_on_refresh(Depsgraph *graph)
{
/* Nothing to update, early out. */
if (graph->entry_tags.is_empty()) {
return;
}
/* The update counts can be used to check if the Depsgraph was changed since the last time it was
* cached by comparing its current update count with the one stored at the moment the Depsgraph
* data were cached.
*
* A global atomic is used as opposed to incrementing the update count per Depsgraph to protect
* against the case where the Depsgraph is being recreated for each update and used to feed the
* same running engine instances. This can happen when using a brute force update pattern (see
* #135635). */
static std::atomic<uint64_t> global_update_count = 0;
graph->update_count = global_update_count.fetch_add(1) + 1;
graph->debug.begin_graph_evaluation();
#ifdef WITH_PYTHON
/* Release the GIL so that Python drivers can be evaluated. See #91046. */
BPy_BEGIN_ALLOW_THREADS;
#endif
graph->is_evaluating = true;
depsgraph_ensure_view_layer(graph);
/* Set up evaluation state. */
DepsgraphEvalState state;
state.graph = graph;
state.do_stats = graph->debug.do_time_debug();
/* Prepare all nodes for evaluation. */
initialize_execution(&state, graph);
/* Evaluation happens in several incremental steps:
*
* - Start with the copy-on-evaluation operations which never form dependency cycles. This will
* ensure that if a dependency graph has a cycle evaluation functions will always "see" valid
* expanded datablock. It might not be evaluated yet, but at least the datablock will be valid.
*
* - If there is potentially dynamically changing visibility in the graph update the actual
* nodes visibilities, so that actual heavy data evaluation can benefit from knowledge that
* something heavy is not currently visible.
*
* - Multi-threaded evaluation of all possible nodes.
* Certain operations (and their subtrees) could be ignored. For example, meta-balls are not
* safe from threading point of view, so the threaded evaluation will stop at the metaball
* operation node.
*
* - Single-threaded pass of all remaining operations. */
TaskPool *task_pool = deg_evaluate_task_pool_create(&state);
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_EVAL);
if (graph->has_animated_visibility || graph->need_update_nodes_visibility) {
/* Update pending parents including only the ones which are affecting operations which are
* affecting visibility. */
state.need_update_pending_parents = true;
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::DYNAMIC_VISIBILITY);
deg_graph_flush_visibility_flags_if_needed(graph);
/* Update parents to an updated visibility and evaluation stage.
*
* Need to do it regardless of whether visibility is actually changed or not: current state of
* the pending parents are all zeroes because it was previously calculated for only visibility
* related nodes and those are fully evaluated by now. */
state.need_update_pending_parents = true;
}
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::THREADED_EVALUATION);
BLI_task_pool_free(task_pool);
evaluate_graph_single_threaded_if_needed(&state);
/* Finalize statistics gathering. This is because we only gather single
* operation timing here, without aggregating anything to avoid any extra
* synchronization. */
if (state.do_stats) {
deg_eval_stats_aggregate(graph);
}
/* Clear any uncleared tags. */
deg_graph_clear_tags(graph);
graph->is_evaluating = false;
#ifdef WITH_PYTHON
BPy_END_ALLOW_THREADS;
#endif
graph->debug.end_graph_evaluation();
}
} // namespace blender::deg

View File

@@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Evaluation engine entry-points for Depsgraph Engine.
*/
#pragma once
namespace blender::deg {
struct Depsgraph;
/**
* Evaluate all nodes tagged for updating,
* \warning This is usually done as part of main loop, but may also be
* called from frame-change update.
*
* \note Time sources should be all valid!
*/
void deg_evaluate_on_refresh(Depsgraph *graph);
} // namespace blender::deg

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
/* SPDX-FileCopyrightText: 2017 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include <cstddef>
#include "DNA_ID.h"
namespace blender {
struct Depsgraph;
struct ID;
/* Uncomment this to have verbose log about original and evaluated pointers
* logged, with detailed information when they are allocated, expanded
* and remapped.
*/
// #define DEG_DEBUG_COW_POINTERS
#ifdef DEG_DEBUG_COW_POINTERS
# define DEG_COW_PRINT(format, ...) printf(format, __VA_ARGS__);
#else
# define DEG_COW_PRINT(format, ...)
#endif
namespace deg {
struct Depsgraph;
class DepsgraphNodeBuilder;
struct IDNode;
/**
* Makes sure given evaluated data-block is brought back to state of the original
* data-block.
*/
ID *deg_update_eval_copy_datablock(const Depsgraph *depsgraph, const IDNode *id_node);
ID *deg_update_eval_copy_datablock(const Depsgraph *depsgraph, struct ID *id_orig);
/** Helper function which frees memory used by copy-on-written data-block. */
void deg_free_eval_copy_datablock(struct ID *id_cow);
/**
* Callback function for depsgraph operation node which ensures evaluated
* data-block is ready for use by further evaluation routines.
*/
void deg_create_eval_copy(blender::Depsgraph *depsgraph, const struct IDNode *id_node);
/**
* Check that given ID is properly expanded and does not have any shallow
* copies inside.
*/
bool deg_validate_eval_copy_datablock(ID *id_cow);
/** Tag given ID block as being copy-on-eval. */
void deg_tag_eval_copy_id(Depsgraph &depsgraph, struct ID *id_cow, const struct ID *id_orig);
/**
* Check whether ID data-block is expanded.
*
* TODO(sergey): Make it an inline function or a macro.
*/
bool deg_eval_copy_is_expanded(const struct ID *id_cow);
/**
* Check whether an evaluated data-block copy is needed for given ID.
*
* There are some exceptions on data-blocks which are covered by dependency graph
* but which we don't want to start duplicating.
*
* This includes images.
*/
bool deg_eval_copy_is_needed(const ID *id_orig);
bool deg_eval_copy_is_needed(const ID_Type id_type);
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,361 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Core routines for how the Depsgraph works.
*/
#include "intern/eval/deg_eval_flush.h"
#include <algorithm>
#include <deque>
#include "BLI_listbase.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BKE_global.hh"
#include "BKE_key.hh"
#include "BKE_object.hh"
#include "BKE_scene.hh"
#include "DRW_engine.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_debug.hh"
#include "intern/debug/deg_debug.h"
#include "intern/depsgraph.hh"
#include "intern/depsgraph_relation.hh"
#include "intern/depsgraph_update.hh"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_factory.hh"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_operation.hh"
#include "intern/node/deg_node_time.hh"
#include "intern/eval/deg_eval_copy_on_write.h"
/* Invalidate data-block data when update is flushed on it.
*
* The idea of this is to help catching cases when area is accessing data which
* is not yet evaluated, which could happen due to missing relations. The issue
* is that usually that data will be kept from previous frame, and it looks to
* be plausible.
*
* This ensures that data does not look plausible, making it much easier to
* catch usage of invalid state. */
#undef INVALIDATE_ON_FLUSH
namespace blender::deg {
enum {
ID_STATE_NONE = 0,
ID_STATE_MODIFIED = 1,
};
enum {
COMPONENT_STATE_NONE = 0,
COMPONENT_STATE_SCHEDULED = 1,
COMPONENT_STATE_DONE = 2,
};
using FlushQueue = std::deque<OperationNode *>;
namespace {
void flush_init_id_node_func(void *__restrict data_v,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
{
Depsgraph *graph = static_cast<Depsgraph *>(data_v);
IDNode *id_node = graph->id_nodes[i];
id_node->custom_flags = ID_STATE_NONE;
for (ComponentNode *comp_node : id_node->components.values()) {
comp_node->custom_flags = COMPONENT_STATE_NONE;
}
}
inline void flush_prepare(Depsgraph *graph)
{
for (OperationNode *node : graph->operations) {
node->scheduled = false;
}
{
const int num_id_nodes = graph->id_nodes.size();
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = 1024;
BLI_task_parallel_range(0, num_id_nodes, graph, flush_init_id_node_func, &settings);
}
}
inline void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue)
{
for (OperationNode *op_node : graph->entry_tags) {
queue->push_back(op_node);
op_node->scheduled = true;
DEG_DEBUG_PRINTF((::blender::Depsgraph *)graph,
EVAL,
"Operation is entry point for update: %s\n",
op_node->identifier().c_str());
}
}
inline void flush_handle_id_node(IDNode *id_node)
{
id_node->custom_flags = ID_STATE_MODIFIED;
}
/* TODO(sergey): We can reduce number of arguments here. */
inline void flush_handle_component_node(IDNode *id_node,
ComponentNode *comp_node,
FlushQueue *queue)
{
/* We only handle component once. */
if (comp_node->custom_flags == COMPONENT_STATE_DONE) {
return;
}
comp_node->custom_flags = COMPONENT_STATE_DONE;
/* Tag all required operations in component for update, unless this is a
* special component where we don't want all operations to be tagged.
*
* TODO(sergey): Make this a more generic solution. */
if (!ELEM(comp_node->type, NodeType::PARTICLE_SETTINGS, NodeType::PARTICLE_SYSTEM)) {
const bool is_geometry_component = comp_node->type == NodeType::GEOMETRY;
for (OperationNode *op : comp_node->operations) {
/* Special case for the visibility operation in the geometry component.
*
* This operation is a part of the geometry component so that manual tag for geometry recalc
* ensures that the visibility is re-evaluated. This operation is not to be re-evaluated when
* an update is flushed to the geometry component via a time dependency or a driver targeting
* a modifier. Skipping update in this case avoids CPU time unnecessarily spent looping over
* modifiers and looking up operations by name in the visibility evaluation function. */
if (is_geometry_component && op->opcode == OperationCode::VISIBILITY) {
continue;
}
op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
}
}
/* when some target changes bone, we might need to re-run the
* whole IK solver, otherwise result might be unpredictable. */
if (comp_node->type == NodeType::BONE) {
ComponentNode *pose_comp = id_node->find_component(NodeType::EVAL_POSE);
BLI_assert(pose_comp != nullptr);
if (pose_comp->custom_flags == COMPONENT_STATE_NONE) {
queue->push_front(pose_comp->get_entry_operation());
pose_comp->custom_flags = COMPONENT_STATE_SCHEDULED;
}
}
}
/* Schedule children of the given operation node for traversal.
*
* One of the children will bypass 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.
*/
inline OperationNode *flush_schedule_children(OperationNode *op_node, FlushQueue *queue)
{
if (op_node->flag & DEPSOP_FLAG_USER_MODIFIED) {
IDNode *id_node = op_node->owner->owner;
id_node->is_user_modified = true;
}
OperationNode *result = nullptr;
for (Relation *rel : op_node->outlinks) {
/* Flush is forbidden, completely. */
if (rel->flag & RELATION_FLAG_NO_FLUSH) {
continue;
}
/* Relation only allows flushes on user changes, but the node was not
* affected by user. */
if ((rel->flag & RELATION_FLAG_FLUSH_USER_EDIT_ONLY) &&
(op_node->flag & DEPSOP_FLAG_USER_MODIFIED) == 0)
{
continue;
}
OperationNode *to_node = static_cast<OperationNode *>(rel->to);
/* Always flush flushable flags, so children always know what happened
* to their parents. */
to_node->flag |= (op_node->flag & DEPSOP_FLAG_FLUSH);
/* Flush update over the relation, if it was not flushed yet. */
if (to_node->scheduled) {
continue;
}
if (result != nullptr) {
queue->push_front(to_node);
}
else {
result = to_node;
}
to_node->scheduled = true;
}
return result;
}
/* NOTE: It will also accumulate flags from changed components. */
void flush_editors_id_update(Depsgraph *graph, const DEGEditorUpdateContext *update_ctx)
{
for (IDNode *id_node : graph->id_nodes) {
if (id_node->custom_flags != ID_STATE_MODIFIED) {
continue;
}
DEG_graph_id_type_tag(reinterpret_cast<::blender::Depsgraph *>(graph),
GS(id_node->id_orig->name));
/* TODO(sergey): Do we need to pass original or evaluated ID here? */
ID *id_orig = id_node->id_orig;
ID *id_cow = id_node->id_cow;
/* Gather recalc flags from all changed components. */
for (ComponentNode *comp_node : id_node->components.values()) {
if (comp_node->custom_flags != COMPONENT_STATE_DONE) {
continue;
}
DepsNodeFactory *factory = type_get_factory(comp_node->type);
BLI_assert(factory != nullptr);
id_cow->recalc |= factory->id_recalc_tag();
}
DEG_DEBUG_PRINTF((blender::Depsgraph *)graph,
EVAL,
"Accumulated recalc bits for %s: %u\n",
id_orig->name,
uint(id_cow->recalc));
/* Inform editors. Only if the data-block is being evaluated a second
* time, to distinguish between user edits and initial evaluation when
* the data-block becomes visible.
*
* TODO: image data-blocks do not use copy-on-eval, so might not be detected
* correctly. */
if (deg_eval_copy_is_expanded(id_cow)) {
if (graph->is_active && id_node->is_user_modified) {
deg_editors_id_update(update_ctx, id_orig);
}
}
}
}
#ifdef INVALIDATE_ON_FLUSH
void invalidate_tagged_evaluated_transform(ID *id)
{
const ID_Type id_type = GS(id->name);
switch (id_type) {
case ID_OB: {
Object *object = (Object *)id;
std::fill_n((float *)object->object_to_world().ptr(), 16, NAN);
break;
}
default:
break;
}
}
void invalidate_tagged_evaluated_geometry(ID *id)
{
const ID_Type id_type = GS(id->name);
switch (id_type) {
case ID_OB: {
Object *object = (Object *)id;
BKE_object_free_derived_caches(object);
break;
}
default:
break;
}
}
#endif
void invalidate_tagged_evaluated_data(Depsgraph *graph)
{
#ifdef INVALIDATE_ON_FLUSH
for (IDNode *id_node : graph->id_nodes) {
if (id_node->custom_flags != ID_STATE_MODIFIED) {
continue;
}
ID *id_cow = id_node->id_cow;
if (!deg_eval_copy_is_expanded(id_cow)) {
continue;
}
for (ComponentNode *comp_node : id_node->components.values()) {
if (comp_node->custom_flags != COMPONENT_STATE_DONE) {
continue;
}
switch (comp_node->type) {
case ID_RECALC_TRANSFORM:
invalidate_tagged_evaluated_transform(id_cow);
break;
case ID_RECALC_GEOMETRY:
invalidate_tagged_evaluated_geometry(id_cow);
break;
default:
break;
}
}
}
#else
(void)graph;
#endif
}
} // namespace
void deg_graph_flush_updates(Depsgraph *graph)
{
/* Sanity checks. */
BLI_assert(graph != nullptr);
Main *bmain = graph->bmain;
graph->time_source->flush_update_tag(graph);
/* Nothing to update, early out. */
if (graph->entry_tags.is_empty()) {
return;
}
/* Reset all flags, get ready for the flush. */
flush_prepare(graph);
/* Starting from the tagged "entry" nodes, flush outwards. */
FlushQueue queue;
flush_schedule_entrypoints(graph, &queue);
/* Prepare update context for editors. */
DEGEditorUpdateContext update_ctx;
update_ctx.bmain = bmain;
update_ctx.depsgraph = reinterpret_cast<::blender::Depsgraph *>(graph);
update_ctx.scene = graph->scene;
update_ctx.view_layer = graph->view_layer;
/* Do actual flush. */
while (!queue.empty()) {
OperationNode *op_node = queue.front();
queue.pop_front();
while (op_node != nullptr) {
/* Tag operation as required for update. */
op_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
/* Inform corresponding ID and component nodes about the change. */
ComponentNode *comp_node = op_node->owner;
IDNode *id_node = comp_node->owner;
flush_handle_id_node(id_node);
flush_handle_component_node(id_node, comp_node, &queue);
/* Flush to nodes along links. */
op_node = flush_schedule_children(op_node, &queue);
}
}
/* Inform editors about all changes. */
flush_editors_id_update(graph, &update_ctx);
/* Reset evaluation result tagged which is tagged for update to some state
* which is obvious to catch. */
invalidate_tagged_evaluated_data(graph);
}
void deg_graph_clear_tags(Depsgraph *graph)
{
/* Clear any entry tags which haven't been flushed. */
graph->entry_tags.clear();
graph->time_source->tagged_for_update = false;
}
} // namespace blender::deg

View File

@@ -0,0 +1,27 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Core routines for how the Depsgraph works.
*/
#pragma once
namespace blender::deg {
struct Depsgraph;
/**
* Flush updates from tagged nodes outwards until all affected nodes are tagged.
*/
void deg_graph_flush_updates(struct Depsgraph *graph);
/**
* Clear tags from all operation nodes.
*/
void deg_graph_clear_tags(struct Depsgraph *graph);
} // namespace blender::deg

View File

@@ -0,0 +1,96 @@
/* SPDX-FileCopyrightText: 2017 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup.h"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "DRW_engine.hh"
namespace blender::deg {
RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph)
: have_backup(false),
id_data({nullptr}),
animation_backup(depsgraph),
scene_backup(depsgraph),
sound_backup(depsgraph),
object_backup(depsgraph),
movieclip_backup(depsgraph),
volume_backup(depsgraph)
{
}
void RuntimeBackup::init_from_id(ID *id)
{
if (!deg_eval_copy_is_expanded(id)) {
return;
}
have_backup = true;
/* Clear, so freeing the expanded data doesn't touch this Python reference. */
id_data.py_instance = id->py_instance;
id->py_instance = nullptr;
animation_backup.init_from_id(id);
const ID_Type id_type = GS(id->name);
switch (id_type) {
case ID_OB:
object_backup.init_from_object(reinterpret_cast<Object *>(id));
break;
case ID_SCE:
scene_backup.init_from_scene(reinterpret_cast<Scene *>(id));
break;
case ID_SO:
sound_backup.init_from_sound(reinterpret_cast<bSound *>(id));
break;
case ID_MC:
movieclip_backup.init_from_movieclip(reinterpret_cast<MovieClip *>(id));
break;
case ID_VO:
volume_backup.init_from_volume(reinterpret_cast<Volume *>(id));
break;
default:
break;
}
}
void RuntimeBackup::restore_to_id(ID *id)
{
if (!have_backup) {
return;
}
id->py_instance = id_data.py_instance;
animation_backup.restore_to_id(id);
const ID_Type id_type = GS(id->name);
switch (id_type) {
case ID_OB:
object_backup.restore_to_object(reinterpret_cast<Object *>(id));
break;
case ID_SCE:
scene_backup.restore_to_scene(reinterpret_cast<Scene *>(id));
break;
case ID_SO:
sound_backup.restore_to_sound(reinterpret_cast<bSound *>(id));
break;
case ID_MC:
movieclip_backup.restore_to_movieclip(reinterpret_cast<MovieClip *>(id));
break;
case ID_VO:
volume_backup.restore_to_volume(reinterpret_cast<Volume *>(id));
break;
default:
break;
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,58 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "DNA_ID.h"
#include "intern/eval/deg_eval_runtime_backup_animation.h"
#include "intern/eval/deg_eval_runtime_backup_movieclip.h"
#include "intern/eval/deg_eval_runtime_backup_object.h"
#include "intern/eval/deg_eval_runtime_backup_scene.h"
#include "intern/eval/deg_eval_runtime_backup_sound.h"
#include "intern/eval/deg_eval_runtime_backup_volume.h"
namespace blender::deg {
struct Depsgraph;
class RuntimeBackup {
public:
explicit RuntimeBackup(const Depsgraph *depsgraph);
/* NOTE: Will reset all runtime fields which has been backed up to nullptr. */
void init_from_id(ID *id);
/* Restore fields to the given ID. */
void restore_to_id(ID *id);
/* Denotes whether init_from_id did put anything into the backup storage.
* This will not be the case when init_from_id() is called for an ID which has never been
* copied-on-eval. In this case there is no need to backup or restore anything.
*
* It also allows to have restore() logic to be symmetrical to init() without need to worry
* that init() might not have happened.
*
* In practice this is used by audio system to lock audio while scene is going through
* copy-on-evaluation mechanism. */
bool have_backup;
/* Struct members of the ID pointer. */
struct {
void *py_instance;
} id_data;
AnimationBackup animation_backup;
SceneBackup scene_backup;
SoundBackup sound_backup;
ObjectRuntimeBackup object_backup;
MovieClipBackup movieclip_backup;
VolumeBackup volume_backup;
};
} // namespace blender::deg

View File

@@ -0,0 +1,101 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_animation.h"
#include "DNA_anim_types.h"
#include "BKE_anim_data.hh"
#include "BKE_animsys.h"
#include "RNA_access.hh"
#include "RNA_types.hh"
#include "intern/depsgraph.hh"
namespace blender::deg {
AnimationValueBackup::AnimationValueBackup(const std::string &rna_path,
int array_index,
float value)
: rna_path(rna_path), array_index(array_index), value(value)
{
}
AnimationBackup::AnimationBackup(const Depsgraph *depsgraph)
{
meed_value_backup = !depsgraph->is_active;
reset();
}
void AnimationBackup::reset() {}
void AnimationBackup::init_from_id(ID *id)
{
/* NOTE: This animation backup nicely preserves values which are animated and
* are not touched by frame/depsgraph post_update handler.
*
* But it makes it impossible to have user edits to animated properties: for
* example, translation of object with animated location will not work with
* the current version of backup. */
return;
PointerRNA id_pointer_rna = RNA_id_pointer_create(id);
BKE_fcurves_id_cb(id, [&](ID *cb_id, FCurve *fcurve) {
if (fcurve->rna_path == nullptr || fcurve->rna_path[0] == '\0') {
return;
}
if (id != cb_id) {
return;
}
/* Resolve path to the property. */
PathResolvedRNA resolved_rna;
if (!BKE_animsys_rna_path_resolve(
&id_pointer_rna, fcurve->rna_path, fcurve->array_index, &resolved_rna))
{
return;
}
/* Read property value. */
float value;
if (!BKE_animsys_read_from_rna_path(&resolved_rna, &value)) {
return;
}
this->values_backup.append({fcurve->rna_path, fcurve->array_index, value});
});
}
void AnimationBackup::restore_to_id(ID *id)
{
return;
PointerRNA id_pointer_rna = RNA_id_pointer_create(id);
for (const AnimationValueBackup &value_backup : values_backup) {
/* Resolve path to the property.
*
* NOTE: Do it again (after storing), since the sub-data pointers might be
* changed after copy-on-evaluation. */
PathResolvedRNA resolved_rna;
if (!BKE_animsys_rna_path_resolve(&id_pointer_rna,
value_backup.rna_path.c_str(),
value_backup.array_index,
&resolved_rna))
{
return;
}
/* Write property value. */
if (!BKE_animsys_write_to_rna_path(&resolved_rna, value_backup.value)) {
return;
}
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,54 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include <string>
#include "BLI_vector.hh"
namespace blender {
struct ID;
namespace deg {
struct Depsgraph;
class AnimationValueBackup {
public:
AnimationValueBackup() = default;
AnimationValueBackup(const std::string &rna_path, int array_index, float value);
AnimationValueBackup(const AnimationValueBackup &other) = default;
AnimationValueBackup(AnimationValueBackup &&other) noexcept = default;
AnimationValueBackup &operator=(const AnimationValueBackup &other) = default;
AnimationValueBackup &operator=(AnimationValueBackup &&other) = default;
std::string rna_path;
int array_index;
float value;
};
/* Backup of animated properties values. */
class AnimationBackup {
public:
AnimationBackup(const Depsgraph *depsgraph);
void reset();
void init_from_id(ID *id);
void restore_to_id(ID *id);
bool meed_value_backup;
Vector<AnimationValueBackup> values_backup;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,20 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_modifier.h"
#include "DNA_modifier_types.h"
namespace blender::deg {
ModifierDataBackup::ModifierDataBackup(ModifierData *modifier_data)
: type(modifier_data->type), runtime(modifier_data->runtime)
{
}
} // namespace blender::deg

View File

@@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "DNA_modifier_types.h"
namespace blender {
struct ModifierData;
namespace deg {
class ModifierDataBackup {
public:
explicit ModifierDataBackup(ModifierData *modifier_data);
ModifierType type;
void *runtime;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,44 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_movieclip.h"
#include "DNA_movieclip_types.h"
namespace blender::deg {
MovieClipBackup::MovieClipBackup(const Depsgraph * /*depsgraph*/)
{
reset();
}
void MovieClipBackup::reset()
{
anim = nullptr;
cache = nullptr;
}
void MovieClipBackup::init_from_movieclip(MovieClip *movieclip)
{
anim = movieclip->anim;
cache = movieclip->cache;
/* Clear pointers stored in the movie clip, so they are not freed when copied-on-written
* datablock is freed for re-allocation. */
movieclip->anim = nullptr;
movieclip->cache = nullptr;
}
void MovieClipBackup::restore_to_movieclip(MovieClip *movieclip)
{
movieclip->anim = anim;
movieclip->cache = cache;
reset();
}
} // namespace blender::deg

View File

@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
namespace blender {
struct MovieClip;
struct MovieClipCache;
struct MovieReader;
namespace deg {
struct Depsgraph;
/* Backup of movie clip runtime data. */
class MovieClipBackup {
public:
MovieClipBackup(const Depsgraph *depsgraph);
void reset();
void init_from_movieclip(MovieClip *movieclip);
void restore_to_movieclip(MovieClip *movieclip);
struct MovieReader *anim;
struct MovieClipCache *cache;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,192 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "BLI_session_uid.h"
#include "intern/eval/deg_eval_runtime_backup_object.h"
#include <cstring>
#include "DNA_mesh_types.h"
#include "BLI_listbase.h"
#include "BKE_action.hh"
#include "BKE_light_linking.h"
#include "BKE_mesh_types.hh"
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "BKE_object_types.hh"
namespace blender::deg {
ObjectRuntimeBackup::ObjectRuntimeBackup(const Depsgraph * /*depsgraph*/)
: base_flag(0), base_local_view_bits(0)
{
/* TODO(sergey): Use something like BKE_object_runtime_reset(). */
runtime = {};
}
void ObjectRuntimeBackup::init_from_object(Object *object)
{
/* Store evaluated mesh and curve_cache, and make sure we don't free it. */
runtime = *object->runtime;
if (object->light_linking) {
light_linking_runtime = object->light_linking->runtime;
}
BKE_object_runtime_reset(object);
/* Keep bounding-box (for now at least). */
object->runtime->bounds_eval = runtime.bounds_eval;
/* Object update will override actual object->data to an evaluated version.
* Need to make sure we don't have data set to evaluated one before free
* anything. */
object->data = runtime.data_orig;
/* Make a backup of base flags. */
base_flag = object->base_flag;
base_local_view_bits = object->base_local_view_bits;
/* Backup runtime data of all modifiers. */
backup_modifier_runtime_data(object);
/* Backup runtime data of all pose channels. */
backup_pose_channel_runtime_data(object);
}
void ObjectRuntimeBackup::backup_modifier_runtime_data(Object *object)
{
for (ModifierData &modifier_data : object->modifiers) {
if (modifier_data.runtime == nullptr) {
continue;
}
modifier_runtime_data.add(modifier_data.persistent_uid, ModifierDataBackup(&modifier_data));
modifier_data.runtime = nullptr;
}
}
void ObjectRuntimeBackup::backup_pose_channel_runtime_data(Object *object)
{
if (object->pose != nullptr) {
for (bPoseChannel &pchan : object->pose->chanbase) {
const SessionUID &session_uid = pchan.runtime.session_uid;
BLI_assert(BLI_session_uid_is_generated(&session_uid));
pose_channel_runtime_data.add(session_uid, pchan.runtime);
BKE_pose_channel_runtime_reset(&pchan.runtime);
}
}
}
void ObjectRuntimeBackup::restore_to_object(Object *object)
{
ID *data_orig = object->runtime->data_orig;
ID *data_eval = runtime.data_eval;
std::optional<Bounds<float3>> bounds = object->runtime->bounds_eval;
SculptSession *sculpt_session = object->runtime->sculpt_session;
*object->runtime = runtime;
object->runtime->data_orig = data_orig;
object->runtime->bounds_eval = bounds;
object->runtime->sculpt_session = sculpt_session;
if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY, OB_FONT) && data_eval != nullptr) {
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 = data_orig;
/* After that, immediately free the invalidated caches. */
BKE_object_free_derived_caches(object);
}
else {
/* Do same thing as object update: override actual object data pointer with evaluated
* datablock, but only if the evaluated data has the same type as the original data. */
if (GS(((ID *)object->data)->name) == GS(data_eval->name)) {
object->data = data_eval;
}
/* Evaluated mesh simply copied edit_mesh pointer from
* original mesh during update, need to make sure no dead
* pointers are left behind. */
if (object->type == OB_MESH) {
Mesh *mesh_eval = id_cast<Mesh *>(data_eval);
Mesh *mesh_orig = id_cast<Mesh *>(data_orig);
mesh_eval->runtime->edit_mesh = mesh_orig->runtime->edit_mesh;
}
}
}
else if (ELEM(object->type, OB_CURVES, OB_POINTCLOUD, OB_VOLUME, OB_GREASE_PENCIL)) {
if (object->id.recalc & ID_RECALC_GEOMETRY) {
/* Free evaluated caches. */
object->data = data_orig;
BKE_object_free_derived_caches(object);
}
else {
object->data = object->runtime->data_eval;
}
}
if (light_linking_runtime) {
/* Lazily allocate light linking on the evaluated object for the cases when the object is only
* a receiver or a blocker and does not need its own LightLinking on the original object. */
BKE_light_linking_ensure(object);
object->light_linking->runtime = *light_linking_runtime;
}
object->base_flag = base_flag;
object->base_local_view_bits = base_local_view_bits;
/* Restore modifier's runtime data.
* NOTE: Data of unused modifiers will be freed there. */
restore_modifier_runtime_data(object);
restore_pose_channel_runtime_data(object);
}
void ObjectRuntimeBackup::restore_modifier_runtime_data(Object *object)
{
for (ModifierData &modifier_data : object->modifiers) {
std::optional<ModifierDataBackup> backup = modifier_runtime_data.pop_try(
modifier_data.persistent_uid);
if (backup.has_value()) {
modifier_data.runtime = backup->runtime;
}
}
for (ModifierDataBackup &backup : modifier_runtime_data.values()) {
const ModifierTypeInfo *modifier_type_info = BKE_modifier_get_info(backup.type);
BLI_assert(modifier_type_info != nullptr);
modifier_type_info->free_runtime_data(backup.runtime);
if (backup.type == eModifierType_Subsurf) {
if (object->type == OB_MESH) {
Mesh *mesh = id_cast<Mesh *>(object->data);
if (mesh->runtime->subsurf_runtime_data == backup.runtime) {
mesh->runtime->subsurf_runtime_data = nullptr;
}
}
}
}
}
void ObjectRuntimeBackup::restore_pose_channel_runtime_data(Object *object)
{
if (object->pose != nullptr) {
for (bPoseChannel &pchan : object->pose->chanbase) {
const SessionUID &session_uid = pchan.runtime.session_uid;
std::optional<bPoseChannel_Runtime> runtime = pose_channel_runtime_data.pop_try(session_uid);
if (runtime.has_value()) {
pchan.runtime = *runtime;
}
}
}
for (bPoseChannel_Runtime &runtime : pose_channel_runtime_data.values()) {
BKE_pose_channel_runtime_free(&runtime);
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,56 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include <optional>
#include "DNA_object_types.h"
#include "DNA_session_uid_types.h"
#include "BKE_object_types.hh"
#include "BLI_map.hh"
#include "intern/eval/deg_eval_runtime_backup_modifier.h"
namespace blender {
struct Object;
namespace deg {
struct Depsgraph;
class ObjectRuntimeBackup {
public:
ObjectRuntimeBackup(const Depsgraph *depsgraph);
/* Make a backup of object's evaluation runtime data, additionally
* make object to be safe for free without invalidating backed up
* pointers. */
void init_from_object(Object *object);
void backup_modifier_runtime_data(Object *object);
void backup_pose_channel_runtime_data(Object *object);
/* Restore all fields to the given object. */
void restore_to_object(Object *object);
/* NOTE: Will free all runtime data which has not been restored. */
void restore_modifier_runtime_data(Object *object);
void restore_pose_channel_runtime_data(Object *object);
bke::ObjectRuntime runtime;
std::optional<LightLinkingRuntime> light_linking_runtime;
short base_flag;
unsigned short base_local_view_bits;
Map<int, ModifierDataBackup> modifier_runtime_data;
Map<SessionUID, bPoseChannel_Runtime> pose_channel_runtime_data;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,9 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_pose.h"

View File

@@ -0,0 +1,13 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
namespace blender::deg {
} // namespace blender::deg

View File

@@ -0,0 +1,59 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_scene.h"
#include "BKE_scene_runtime.hh"
#include "BKE_sound.hh"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
namespace blender::deg {
SceneBackup::SceneBackup(const Depsgraph *depsgraph) : sequencer_backup(depsgraph)
{
reset();
}
void SceneBackup::reset()
{
audio_runtime = {};
rigidbody_last_time = -1;
}
void SceneBackup::init_from_scene(Scene *scene)
{
BKE_sound_lock();
if (scene->rigidbody_world != nullptr) {
rigidbody_last_time = scene->rigidbody_world->ltime;
}
audio_runtime = std::move(scene->runtime->audio);
scene->runtime->audio = {};
sequencer_backup.init_from_scene(scene);
}
void SceneBackup::restore_to_scene(Scene *scene)
{
scene->runtime->audio = std::move(audio_runtime);
if (scene->rigidbody_world != nullptr) {
scene->rigidbody_world->ltime = rigidbody_last_time;
}
sequencer_backup.restore_to_scene(scene);
BKE_sound_unlock();
reset();
}
} // namespace blender::deg

View File

@@ -0,0 +1,39 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "BKE_scene_runtime.hh"
#include "intern/eval/deg_eval_runtime_backup_sequencer.h"
namespace blender {
struct Scene;
namespace deg {
struct Depsgraph;
/* Backup of scene runtime data. */
class SceneBackup {
public:
SceneBackup(const Depsgraph *depsgraph);
void reset();
void init_from_scene(Scene *scene);
void restore_to_scene(Scene *scene);
bke::SceneAudioRuntime audio_runtime;
float rigidbody_last_time;
SequencerBackup sequencer_backup;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,124 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_sequence.h"
#include "DNA_sequence_types.h"
#include "SEQ_modifier.hh"
#include "SEQ_sequencer.hh"
#include "BLI_listbase.h"
namespace blender::deg {
StripModifierDataBackup::StripModifierDataBackup()
{
reset();
}
void StripModifierDataBackup::reset()
{
sound_in = nullptr;
sound_out = nullptr;
flag = STRIP_MODIFIER_FLAG_NONE;
params_hash = 0;
}
void StripModifierDataBackup::init_from_modifier(StripModifierData *smd)
{
blender::seq::StripModifierDataRuntime *runtime = smd->runtime;
if (smd->is_type_sound()) {
flag = runtime->flag;
sound_in = runtime->last_sound_in;
sound_out = runtime->last_sound_out;
params_hash = runtime->params_hash;
runtime->last_sound_in = nullptr;
runtime->last_sound_out = nullptr;
}
}
void StripModifierDataBackup::restore_to_modifier(StripModifierData *smd)
{
blender::seq::StripModifierDataRuntime *runtime = smd->runtime;
if (smd->is_type_sound()) {
runtime->flag = flag;
runtime->last_sound_in = sound_in;
runtime->last_sound_out = sound_out;
runtime->params_hash = params_hash;
}
reset();
}
bool StripModifierDataBackup::isEmpty() const
{
return sound_in == nullptr && sound_out == nullptr;
}
StripBackup::StripBackup(const Depsgraph * /*depsgraph*/)
{
reset();
}
void StripBackup::reset()
{
scene_sound = nullptr;
sound_time_stretch = nullptr;
sound_time_stretch_fps = 0.0f;
movie_readers.clear();
modifiers.clear();
}
void StripBackup::init_from_strip(Strip *strip)
{
scene_sound = strip->runtime->scene_sound;
sound_time_stretch = strip->runtime->sound_time_stretch;
sound_time_stretch_fps = strip->runtime->sound_time_stretch_fps;
movie_readers = std::move(strip->runtime->movie_readers);
for (StripModifierData &smd : strip->modifiers) {
StripModifierDataBackup mod_backup;
mod_backup.init_from_modifier(&smd);
if (!mod_backup.isEmpty()) {
modifiers.add(smd.persistent_uid, mod_backup);
}
}
strip->runtime->scene_sound = nullptr;
strip->runtime->sound_time_stretch = nullptr;
strip->runtime->sound_time_stretch_fps = 0.0f;
strip->runtime->movie_readers.clear();
}
void StripBackup::restore_to_strip(Strip *strip)
{
strip->runtime->scene_sound = scene_sound;
strip->runtime->sound_time_stretch = sound_time_stretch;
strip->runtime->sound_time_stretch_fps = sound_time_stretch_fps;
strip->runtime->movie_readers = std::move(movie_readers);
for (StripModifierData &smd : strip->modifiers) {
std::optional<StripModifierDataBackup> backup = modifiers.pop_try(smd.persistent_uid);
if (backup) {
backup->restore_to_modifier(&smd);
}
}
reset();
}
bool StripBackup::isEmpty() const
{
return (scene_sound == nullptr) && (sound_time_stretch == nullptr) && movie_readers.is_empty() &&
modifiers.is_empty();
}
} // namespace blender::deg

View File

@@ -0,0 +1,68 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "DNA_listBase.h"
#include "BKE_sound_types.hh"
#include "BLI_map.hh"
#include "BLI_vector.hh"
#include "SEQ_modifier.hh"
namespace blender {
struct MovieReader;
struct Strip;
struct StripModifierData;
namespace deg {
struct Depsgraph;
class StripModifierDataBackup {
public:
StripModifierDataBackup();
void reset();
void init_from_modifier(StripModifierData *smd);
void restore_to_modifier(StripModifierData *smd);
bool isEmpty() const;
/* For Sound Modifiers. */
AUD_Sound sound_in;
AUD_Sound sound_out;
eStripModifierFlag flag;
uint64_t params_hash;
};
/* Backup of a single strip. */
class StripBackup {
public:
StripBackup(const Depsgraph *depsgraph);
void reset();
void init_from_strip(Strip *strip);
void restore_to_strip(Strip *strip);
bool isEmpty() const;
AUD_SequenceEntry scene_sound;
AUD_Sound sound_time_stretch;
float sound_time_stretch_fps;
Vector<MovieReader *, 1> movie_readers;
Map<int, StripModifierDataBackup> modifiers;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,73 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "BLI_session_uid.h"
#include "intern/eval/deg_eval_runtime_backup_sequencer.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "BLI_assert.h"
#include "BKE_sound.hh"
#include "SEQ_iterator.hh"
#include "SEQ_sequencer.hh"
namespace blender::deg {
SequencerBackup::SequencerBackup(const Depsgraph *depsgraph) : depsgraph(depsgraph) {}
static bool strip_init_cb(Strip *strip, void *user_data)
{
SequencerBackup *sb = static_cast<SequencerBackup *>(user_data);
StripBackup strip_backup(sb->depsgraph);
strip_backup.init_from_strip(strip);
if (!strip_backup.isEmpty()) {
const SessionUID &session_uid = strip->runtime->session_uid;
BLI_assert(BLI_session_uid_is_generated(&session_uid));
sb->strips_backup.add(session_uid, strip_backup);
}
return true;
}
void SequencerBackup::init_from_scene(Scene *scene)
{
if (scene->ed != nullptr) {
seq::foreach_strip(&scene->ed->seqbase, strip_init_cb, this);
}
}
static bool strip_restore_cb(Strip *strip, void *user_data)
{
SequencerBackup *sb = static_cast<SequencerBackup *>(user_data);
const SessionUID &session_uid = strip->runtime->session_uid;
BLI_assert(BLI_session_uid_is_generated(&session_uid));
StripBackup *strip_backup = sb->strips_backup.lookup_ptr(session_uid);
if (strip_backup != nullptr) {
strip_backup->restore_to_strip(strip);
}
return true;
}
void SequencerBackup::restore_to_scene(Scene *scene)
{
if (scene->ed != nullptr) {
seq::foreach_strip(&scene->ed->seqbase, strip_restore_cb, this);
}
/* Cleanup audio while the scene is still known. */
for (StripBackup &strip_backup : strips_backup.values()) {
if (strip_backup.scene_sound != nullptr) {
BKE_sound_remove_scene_sound(scene, strip_backup.scene_sound);
strip_backup.scene_sound.reset();
}
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,39 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "DNA_session_uid_types.h"
#include "BLI_map.hh"
#include "intern/eval/deg_eval_runtime_backup_sequence.h"
namespace blender {
struct Scene;
namespace deg {
struct Depsgraph;
/* Backup of sequencer strips runtime data. */
class SequencerBackup {
public:
SequencerBackup(const Depsgraph *depsgraph);
void init_from_scene(Scene *scene);
void restore_to_scene(Scene *scene);
const Depsgraph *depsgraph;
Map<SessionUID, StripBackup> strips_backup;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,39 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_sound.h"
#include "DNA_sound_types.h"
namespace blender::deg {
SoundBackup::SoundBackup(const Depsgraph * /*depsgraph*/)
{
reset();
}
void SoundBackup::reset()
{
this->cache = nullptr;
this->waveform = nullptr;
this->playback_handle = nullptr;
}
void SoundBackup::init_from_sound(bSound *sound)
{
BKE_sound_runtime_state_get_and_clear(
sound, &this->cache, &this->playback_handle, &this->waveform);
}
void SoundBackup::restore_to_sound(bSound *sound)
{
BKE_sound_runtime_state_set(sound, this->cache, this->playback_handle, this->waveform);
reset();
}
} // namespace blender::deg

View File

@@ -0,0 +1,37 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "BKE_sound.hh"
namespace blender {
struct bSound;
namespace deg {
struct Depsgraph;
/* Backup of sound datablocks runtime data. */
class SoundBackup {
public:
SoundBackup(const Depsgraph *depsgraph);
void reset();
void init_from_sound(bSound *sound);
void restore_to_sound(bSound *sound);
AUD_Sound cache;
AUD_Sound playback_handle;
Vector<float> *waveform;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,40 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_volume.h"
#include "BLI_assert.h"
#include "BLI_string.h"
#include "DNA_volume_types.h"
#include "BKE_volume.hh"
namespace blender::deg {
VolumeBackup::VolumeBackup(const Depsgraph * /*depsgraph*/) : grids(nullptr) {}
void VolumeBackup::init_from_volume(Volume *volume)
{
STRNCPY(filepath, volume->filepath);
BLI_STATIC_ASSERT(sizeof(filepath) == sizeof(volume->filepath),
"VolumeBackup filepath length wrong");
grids = volume->runtime->grids;
volume->runtime->grids = nullptr;
}
void VolumeBackup::restore_to_volume(Volume *volume)
{
if (grids) {
BKE_volume_grids_backup_restore(volume, grids, filepath);
grids = nullptr;
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
namespace blender {
struct Volume;
struct VolumeGridVector;
namespace deg {
struct Depsgraph;
/* Backup of volume datablocks runtime data. */
class VolumeBackup {
public:
VolumeBackup(const Depsgraph *depsgraph);
void init_from_volume(Volume *volume);
void restore_to_volume(Volume *volume);
VolumeGridVector *grids;
char filepath[/*FILE_MAX*/ 1024];
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,40 @@
/* SPDX-FileCopyrightText: 2017 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_stats.h"
#include "intern/depsgraph.hh"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_operation.hh"
namespace blender::deg {
void deg_eval_stats_aggregate(Depsgraph *graph)
{
/* Reset current evaluation stats for ID and component nodes.
* Those are not filled in by the evaluation engine. */
for (Node *node : graph->id_nodes) {
IDNode *id_node = static_cast<IDNode *>(node);
for (ComponentNode *comp_node : id_node->components.values()) {
comp_node->stats.reset_current();
}
id_node->stats.reset_current();
}
/* Now accumulate operation timings to components and IDs. */
for (OperationNode *op_node : graph->operations) {
ComponentNode *comp_node = op_node->owner;
IDNode *id_node = comp_node->owner;
id_node->stats.current_time += op_node->stats.current_time;
comp_node->stats.current_time += op_node->stats.current_time;
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2017 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
namespace blender::deg {
struct Depsgraph;
/* Aggregate operation timings to overall component and ID nodes timing. */
void deg_eval_stats_aggregate(Depsgraph *graph);
} // namespace blender::deg

View File

@@ -0,0 +1,235 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_visibility.h"
#include "DNA_layer_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BLI_assert.h"
#include "BLI_listbase.h"
#include "BLI_stack.hh"
#include "DEG_depsgraph.hh"
#include "intern/depsgraph.hh"
#include "intern/depsgraph_relation.hh"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_operation.hh"
namespace blender::deg {
void deg_evaluate_object_node_visibility(blender::Depsgraph *depsgraph, IDNode *id_node)
{
BLI_assert(GS(id_node->id_cow->name) == ID_OB);
Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph);
const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id);
const int required_flags = (graph->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT :
BASE_ENABLED_RENDER;
const bool is_enabled = !graph->use_visibility_optimization ||
object->base_flag & required_flags;
if (id_node->is_enabled_on_eval != is_enabled) {
id_node->is_enabled_on_eval = is_enabled;
/* Tag dependency graph for changed visibility, so that it is updated on all dependencies prior
* to a pass of an actual evaluation. */
graph->need_update_nodes_visibility = true;
}
}
void deg_evaluate_object_modifiers_mode_node_visibility(blender::Depsgraph *depsgraph,
IDNode *id_node)
{
BLI_assert(GS(id_node->id_cow->name) == ID_OB);
Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph);
const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id);
if (object->modifiers.is_empty()) {
return;
}
const ModifierMode modifier_mode = (graph->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime :
eModifierMode_Render;
const ComponentNode *geometry_component = id_node->find_component(NodeType::GEOMETRY);
for (ModifierData &modifier : object->modifiers) {
OperationNode *modifier_node = geometry_component->find_operation(OperationCode::MODIFIER,
modifier.name);
BLI_assert_msg(modifier_node != nullptr,
"Modifier node in depsgraph is not found. Likely due to missing "
"DEG_relations_tag_update().");
const bool modifier_enabled = !graph->use_visibility_optimization ||
(modifier.mode & modifier_mode);
const int mute_flag = modifier_enabled ? 0 : DEPSOP_FLAG_MUTE;
if ((modifier_node->flag & DEPSOP_FLAG_MUTE) != mute_flag) {
modifier_node->flag &= ~DEPSOP_FLAG_MUTE;
modifier_node->flag |= mute_flag;
graph->need_update_nodes_visibility = true;
}
}
}
void deg_graph_flush_visibility_flags(Depsgraph *graph)
{
enum {
DEG_NODE_VISITED = (1 << 0),
};
for (IDNode *id_node : graph->id_nodes) {
for (ComponentNode *comp_node : id_node->components.values()) {
comp_node->possibly_affects_visible_id = id_node->is_visible_on_build;
comp_node->affects_visible_id = id_node->is_visible_on_build && id_node->is_enabled_on_eval;
/* Visibility component is always to be considered to have the same visibility as the
* `id_node->is_visible_on_build`. This is because the visibility is to be evaluated
* regardless of its current state as it might get changed due to animation. */
if (comp_node->type == NodeType::VISIBILITY) {
comp_node->affects_visible_id = id_node->is_visible_on_build;
}
/* Enforce "visibility" of the synchronization component.
*
* This component is never connected to other ID nodes, and hence can not be handled in the
* same way as other components needed for evaluation. It is only needed for proper
* evaluation of the ID node it belongs to.
*
* The design is such that the synchronization is supposed to happen whenever any part of the
* ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag
* flushing and scheduling will handle the component in a generic manner. */
if (comp_node->type == NodeType::SYNCHRONIZATION) {
comp_node->possibly_affects_visible_id = true;
comp_node->affects_visible_id = true;
}
}
}
Stack<OperationNode *> stack;
for (OperationNode *op_node : graph->operations) {
op_node->custom_flags = 0;
op_node->num_links_pending = 0;
for (Relation *rel : op_node->outlinks) {
if ((rel->to->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
++op_node->num_links_pending;
}
}
if (op_node->num_links_pending == 0) {
stack.push(op_node);
op_node->custom_flags |= DEG_NODE_VISITED;
}
}
while (!stack.is_empty()) {
OperationNode *op_node = stack.pop();
/* Flush flags to parents. */
for (Relation *rel : op_node->inlinks) {
if (rel->from->type == NodeType::OPERATION) {
const OperationNode *op_to = reinterpret_cast<const OperationNode *>(rel->to);
const ComponentNode *comp_to = op_to->owner;
/* Ignore the synchronization target.
* It is always visible and should not affect on other components. */
if (comp_to->type == NodeType::SYNCHRONIZATION) {
continue;
}
OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from);
ComponentNode *comp_from = op_from->owner;
op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
if (rel->flag & RELATION_NO_VISIBILITY_CHANGE) {
continue;
}
const bool target_possibly_affects_visible_id = comp_to->possibly_affects_visible_id;
bool target_affects_visible_id = comp_to->affects_visible_id;
/* This is a bit arbitrary but the idea here is following:
*
* - When another object is used by a disabled modifier we do not want that object to
* be considered needed for evaluation.
*
* - However, we do not want to take mute flag during visibility propagation within the
* same object. Otherwise drivers and transform dependencies of the geometry component
* entry component might not be properly handled.
*
* This code works fine for muting modifiers, but might need tweaks when mute is used for
* something else. */
if (comp_from != comp_to && (op_to->flag & DEPSOP_FLAG_MUTE)) {
target_affects_visible_id = false;
}
/* Visibility component forces all components of the current ID to be considered as
* affecting directly visible. */
if (comp_from->type == NodeType::VISIBILITY) {
const IDNode *id_node_from = comp_from->owner;
if (target_possibly_affects_visible_id) {
for (ComponentNode *comp_node : id_node_from->components.values()) {
comp_node->possibly_affects_visible_id |= target_possibly_affects_visible_id;
}
}
if (target_affects_visible_id) {
for (ComponentNode *comp_node : id_node_from->components.values()) {
comp_node->affects_visible_id |= target_affects_visible_id;
}
}
}
else {
comp_from->possibly_affects_visible_id |= target_possibly_affects_visible_id;
comp_from->affects_visible_id |= target_affects_visible_id;
}
}
}
/* Schedule parent nodes. */
for (Relation *rel : op_node->inlinks) {
if (rel->from->type == NodeType::OPERATION) {
OperationNode *op_from = static_cast<OperationNode *>(rel->from);
if ((rel->flag & RELATION_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->custom_flags & DEG_NODE_VISITED) == 0) {
stack.push(op_from);
op_from->custom_flags |= DEG_NODE_VISITED;
}
}
}
}
graph->need_update_nodes_visibility = false;
}
void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph)
{
if (!graph->need_update_nodes_visibility) {
return;
}
deg_graph_flush_visibility_flags(graph);
}
} // namespace blender::deg

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
namespace blender {
struct Depsgraph;
namespace deg {
struct Depsgraph;
struct IDNode;
/* Evaluate actual node visibility flags based on the current state of object's visibility
* restriction flags. */
void deg_evaluate_object_node_visibility(blender::Depsgraph *depsgraph, IDNode *id_node);
/* Update node visibility flags based on actual modifiers mode flags. */
void deg_evaluate_object_modifiers_mode_node_visibility(blender::Depsgraph *depsgraph,
IDNode *id_node);
/* Flush both static and dynamic visibility flags from leaves up to the roots, making it possible
* to know whether a node has affect on something (potentially) visible. */
void deg_graph_flush_visibility_flags(Depsgraph *graph);
void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph);
} // namespace deg
} // namespace blender