Motivation is to disambiguate on the naming level what the matrix actually means. It is very easy to understand the meaning backwards, especially since in Python the name goes the opposite way (it is called `world_matrix` in the Python API). It is important to disambiguate the naming without making developers to look into the comment in the header file (which is also not super clear either). Additionally, more clear naming facilitates the unit verification (or, in this case, space validation) when reading an expression. This patch calls the matrix `object_to_world` which makes it clear from the local code what is it exactly going on. This is only done on DNA level, and a lot of local variables still follow the old naming. A DNA rename is setup in a way that there is no change on the file level, so there should be no regressions at all. The possibility is to add `_matrix` or `_mat` suffix to the name to make it explicit that it is a matrix. Although, not sure if it really helps the readability, or is it something redundant. Differential Revision: https://developer.blender.org/D16328
399 lines
12 KiB
C++
399 lines
12 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2013 Blender Foundation. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup depsgraph
|
|
*
|
|
* Core routines for how the Depsgraph works.
|
|
*/
|
|
|
|
#include "intern/eval/deg_eval_flush.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math_vector.h"
|
|
#include "BLI_task.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BKE_key.h"
|
|
#include "BKE_object.h"
|
|
#include "BKE_scene.h"
|
|
|
|
#include "DNA_key_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "DRW_engine.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "intern/debug/deg_debug.h"
|
|
#include "intern/depsgraph.h"
|
|
#include "intern/depsgraph_relation.h"
|
|
#include "intern/depsgraph_type.h"
|
|
#include "intern/depsgraph_update.h"
|
|
#include "intern/node/deg_node.h"
|
|
#include "intern/node/deg_node_component.h"
|
|
#include "intern/node/deg_node_factory.h"
|
|
#include "intern/node/deg_node_id.h"
|
|
#include "intern/node/deg_node_operation.h"
|
|
#include "intern/node/deg_node_time.h"
|
|
|
|
#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 = deque<OperationNode *>;
|
|
|
|
namespace {
|
|
|
|
void flush_init_id_node_func(void *__restrict data_v,
|
|
const int i,
|
|
const TaskParallelTLS *__restrict /*tls*/)
|
|
{
|
|
Depsgraph *graph = (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((::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 by-pass the queue and will be returned as a function
|
|
* return value, so it can start being handled right away, without building too
|
|
* much of a queue.
|
|
*/
|
|
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 = (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;
|
|
}
|
|
|
|
void flush_engine_data_update(ID *id)
|
|
{
|
|
DrawDataList *draw_data_list = DRW_drawdatalist_from_id(id);
|
|
if (draw_data_list == nullptr) {
|
|
return;
|
|
}
|
|
LISTBASE_FOREACH (DrawData *, draw_data, draw_data_list) {
|
|
draw_data->recalc |= id->recalc;
|
|
}
|
|
}
|
|
|
|
/* 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<::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((::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 COW, so might not be detected
|
|
* correctly. */
|
|
if (deg_copy_on_write_is_expanded(id_cow)) {
|
|
if (graph->is_active && id_node->is_user_modified) {
|
|
deg_editors_id_update(update_ctx, id_orig);
|
|
|
|
/* We only want to tag an ID for lib-override auto-refresh if it was actually tagged as
|
|
* changed. CoW IDs indirectly modified because of changes in other IDs should never
|
|
* require a lib-override diffing. */
|
|
if (ID_IS_OVERRIDE_LIBRARY_REAL(id_orig)) {
|
|
id_orig->tag |= LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH;
|
|
}
|
|
else if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_orig)) {
|
|
switch (GS(id_orig->name)) {
|
|
case ID_KE:
|
|
((Key *)id_orig)->from->tag |= LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH;
|
|
break;
|
|
case ID_GR:
|
|
BLI_assert(id_orig->flag & LIB_EMBEDDED_DATA);
|
|
/* TODO. */
|
|
break;
|
|
case ID_NT:
|
|
BLI_assert(id_orig->flag & LIB_EMBEDDED_DATA);
|
|
/* TODO. */
|
|
break;
|
|
default:
|
|
BLI_assert(0);
|
|
}
|
|
}
|
|
}
|
|
/* Inform draw engines that something was changed. */
|
|
flush_engine_data_update(id_cow);
|
|
}
|
|
}
|
|
}
|
|
|
|
#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;
|
|
copy_vn_fl((float *)object->object_to_world, 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_copy_on_write_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 = (::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
|