This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/depsgraph/intern/builder/deg_builder.cc
Sergey Sharybin 523de7ae9b Fix T66325: Animation Keyframe Undo/Redo Bug
The issue was caused by dependency graph always ignoring animation
update when it is first time constructed. This was a way to make it
preserve unkeyed changes on undo/redo. This, however, made it so
changes of animation data itself (such as deleting/moving keyframes)
did not trigger animation update by the dependency graph.

This worked prior to copy-on-write because animation recalc flags
were stored in the DNA and never re-set on file/undo load. This was
giving dependency graph a clue that animation is to be re-evaluated
when operator explicitly asked to (more precisely, when such operator
was undone/redone).

This change makes it so original ID's recalc flags are storing
recalc flags when ID is tagged for update as an response to user
input. This way re-building dependency graph can force animation
to be updated on redo.

Tricky part here is that ID's recalc flag is no longer to be zeroed
when loading undo step (which is the same as reading .blend file).
This is something what works differently comparing to legacy
dependency graph, which was zeroing object's recalc flags there but
not animation data's recalc flags.
Shouldn't be causing issues, since unkeyed changes are not preserved
upon opening a file anyway, at least to my knowledge.

Related reports which are to be taken into account and verified
they are not re-introduced when making changes in the area:

- T63111: Auto-Bake stuck at constant re-rendering
- T54296: Cycles viewport render stuck on constant re-render

Reviewers: campbellbarton, brecht

Reviewed By: campbellbarton, brecht

Maniphest Tasks: T66325

Differential Revision: https://developer.blender.org/D5316
2019-07-26 14:33:51 +02:00

236 lines
7.8 KiB
C++

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder.h"
#include <cstring>
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_layer_types.h"
#include "DNA_ID.h"
#include "DNA_object_types.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BLI_stack.h"
#include "BKE_action.h"
extern "C" {
#include "BKE_animsys.h"
}
#include "intern/depsgraph.h"
#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/builder/deg_builder_cache.h"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_operation.h"
#include "DEG_depsgraph.h"
namespace DEG {
bool deg_check_base_in_depsgraph(const Depsgraph *graph, Base *base)
{
Object *object_orig = base->base_orig->object;
IDNode *id_node = graph->find_id_node(&object_orig->id);
if (id_node == NULL) {
return false;
}
return id_node->has_base;
}
/*******************************************************************************
* Base class for builders.
*/
DepsgraphBuilder::DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache)
: bmain_(bmain), graph_(graph), cache_(cache)
{
}
bool DepsgraphBuilder::need_pull_base_into_graph(Base *base)
{
/* Simple check: enabled bases are always part of dependency graph. */
const int base_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT :
BASE_ENABLED_RENDER;
if (base->flag & base_flag) {
return true;
}
/* More involved check: since we don't support dynamic changes in dependency graph topology and
* all visible objects are to be part of dependency graph, we pull all objects which has animated
* visibility. */
Object *object = base->object;
AnimatedPropertyID property_id;
if (graph_->mode == DAG_EVAL_VIEWPORT) {
property_id = AnimatedPropertyID(&object->id, &RNA_Object, "hide_viewport");
}
else if (graph_->mode == DAG_EVAL_RENDER) {
property_id = AnimatedPropertyID(&object->id, &RNA_Object, "hide_render");
}
else {
BLI_assert(!"Unknown evaluation mode.");
return false;
}
return cache_->isPropertyAnimated(&object->id, property_id);
}
bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel *pchan)
{
BLI_assert(object->type == OB_ARMATURE);
if (pchan == NULL || pchan->bone == NULL) {
return false;
}
/* We don't really care whether segments are higher than 1 due to static user input (as in,
* rigger entered value like 3 manually), or due to animation. In either way we need to create
* special evaluation. */
if (pchan->bone->segments > 1) {
return true;
}
bArmature *armature = static_cast<bArmature *>(object->data);
AnimatedPropertyID property_id(&armature->id, &RNA_Bone, pchan->bone, "bbone_segments");
/* Check both Object and Armature animation data, because drivers modifying Armature
* state could easily be created in the Object AnimData. */
return cache_->isPropertyAnimated(&object->id, property_id) ||
cache_->isPropertyAnimated(&armature->id, property_id);
}
bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan)
{
/* Proxies don't have BONE_SEGMENTS */
if (ID_IS_LINKED(object) && object->proxy_from != NULL) {
return false;
}
return check_pchan_has_bbone(object, pchan);
}
bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const char *bone_name)
{
const bPoseChannel *pchan = BKE_pose_channel_find_name(object->pose, bone_name);
return check_pchan_has_bbone_segments(object, pchan);
}
/*******************************************************************************
* Builder finalizer.
*/
namespace {
void deg_graph_build_flush_visibility(Depsgraph *graph)
{
enum {
DEG_NODE_VISITED = (1 << 0),
};
BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack");
for (IDNode *id_node : graph->id_nodes) {
GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, id_node->components) {
comp_node->affects_directly_visible |= id_node->is_directly_visible;
}
GHASH_FOREACH_END();
}
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->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
++op_node->num_links_pending;
}
}
if (op_node->num_links_pending == 0) {
BLI_stack_push(stack, &op_node);
op_node->custom_flags |= DEG_NODE_VISITED;
}
}
while (!BLI_stack_is_empty(stack)) {
OperationNode *op_node;
BLI_stack_pop(stack, &op_node);
/* Flush layers to parents. */
for (Relation *rel : op_node->inlinks) {
if (rel->from->type == NodeType::OPERATION) {
OperationNode *op_from = (OperationNode *)rel->from;
op_from->owner->affects_directly_visible |= op_node->owner->affects_directly_visible;
}
}
/* Schedule parent nodes. */
for (Relation *rel : op_node->inlinks) {
if (rel->from->type == NodeType::OPERATION) {
OperationNode *op_from = (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) {
BLI_stack_push(stack, &op_from);
op_from->custom_flags |= DEG_NODE_VISITED;
}
}
}
}
BLI_stack_free(stack);
}
} // namespace
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
{
/* Make sure dependencies of visible ID datablocks are visible. */
deg_graph_build_flush_visibility(graph);
/* Re-tag IDs for update if it was tagged before the relations
* update tag. */
for (IDNode *id_node : graph->id_nodes) {
ID *id_orig = id_node->id_orig;
id_node->finalize_build(graph);
int flag = 0;
/* Tag rebuild if special evaluation flags changed. */
if (id_node->eval_flags != id_node->previous_eval_flags) {
flag |= ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY;
}
/* Tag rebuild if the custom data mask changed. */
if (id_node->customdata_masks != id_node->previous_customdata_masks) {
flag |= ID_RECALC_GEOMETRY;
}
if (!deg_copy_on_write_is_expanded(id_node->id_cow)) {
flag |= ID_RECALC_COPY_ON_WRITE;
/* This means ID is being added to the dependency graph first
* time, which is similar to "ob-visible-change" */
if (GS(id_orig->name) == ID_OB) {
flag |= ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY;
}
}
/* Restore recalc flags from original ID, which could possibly contain recalc flags set by
* an operator and then were carried on by the undo system. */
flag |= id_orig->recalc;
if (flag != 0) {
graph_id_tag_update(bmain, graph, id_node->id_orig, flag, DEG_UPDATE_SOURCE_RELATIONS);
}
}
}
} // namespace DEG