Add Chromium-only Blender WebEngine parity work
This commit is contained in:
518
blender-5.2.0/source/blender/depsgraph/intern/eval/deg_eval.cc
Normal file
518
blender-5.2.0/source/blender/depsgraph/intern/eval/deg_eval.cc
Normal 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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user