< Dependency graph Copy-on-Write >
--------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
This is an initial commit of Copy-on-write support added to dependency graph.
Main priority for now: get playback (Alt-A) and all operators (selection,
transform etc) to work with the new concept of clear separation between
evaluated data coming from dependency graph and original data coming from
.blend file (and stored in bmain).
= How does this work? =
The idea is to support Copy-on-Write on the ID level. This means, we duplicate
the whole ID before we cann it's evaluaiton function. This is currently done
in the following way:
- At the depsgraph construction time we create "shallow" copy of the ID
datablock, just so we know it's pointer in memory and can use for function
bindings.
- At the evaluaiton time, the copy of ID get's "expanded" (needs a better
name internally, so it does not conflict with expanding datablocks during
library linking), which means the content of the datablock is being
copied over and all IDs are getting remapped to the copied ones.
Currently we do the whole copy, in the future we will support some tricks
here to prevent duplicating geometry arrays (verts, edges, loops, faces
and polys) when we don't need that.
- Evaluation functions are operating on copied datablocks and never touching
original datablock.
- There are some cases when we need to know non-ID pointers for function
bindings. This mainly applies to scene collections and armatures. The
idea of dealing with this is to "expand" copy-on-write datablock at
the dependency graph build time. This might introduce some slowdown to the
dependency graph construction time, but allows us to have minimal changes
in the code and avoid any hash look-up from evaluation function (one of
the ideas to avoid using pointers as function bindings is to pass name
of layer or a bone to the evaluation function and look up actual data based
on that name).
Currently there is a special function in depsgraph which does such a
synchronization, in the future we might want to make it more generic.
At some point we need to synchronize copy-on-write version of datablock with
the original version. This happens, i.e., when we change active object or
change selection. We don't want any actual evaluation of update flush happening
for such thins, so now we have a special update tag:
DEG_id_tag_update((id, DEG_TAG_COPY_ON_WRITE)
- For the render engines we now have special call for the dependency graph to
give evaluated datablock for the given original one. This isn't fully ideal
but allows to have Cycles viewport render.
This is definitely a subject for further investigation / improvement.
This call will tag copy-on-write component tagged for update without causing
updates to be flushed to any other objects, causing chain reaction of updates.
This tag is handy when selection in the scene changes.
This basically summarizes ideas underneath this commit. The code should be
reasonably documented.
Here is a demo of dependency graph with all copy-on-write stuff in it:
https://developer.blender.org/F635468
= What to expect to (not) work? =
- Only meshes are properly-ish aware of copy-on-write currently, Non-mesh
geometry will probably crash or will not work at all.
- Armatures will need similar depsgraph built-time expansion of the copied
datablock.
- There are some extra tags / relations added, to keep things demo-able but
which are slowing things down for evaluation.
- Edit mode works for until click selection is used (due to the selection
code using EditDerivedMesh created ad-hoc).
- Lots of tools will lack tagging synchronization of copied datablock for
sync with original ID.
= How to move forward? =
There is some tedious work related on going over all the tools, checking
whether they need to work with original or final evaluated object and make
the required changes.
Additionally, there need synchronization tag done in fair amount of tools
and operators as well. For example, currently it's not possible to change
render engine without re-opening the file or forcing dependency graph for
re-build via python console.
There is also now some thoughts required about copying evaluated properties
between objects or from collection to a new object. Perhaps easiest way
would be to move base flag flush to Object ID node and tag new objects for
update instead of doing manual copy.
here is some WIP patch which moves such evaluaiton / flush:
https://developer.blender.org/F635479
Lots of TODOs in the code, with possible optimization.
= How to test? =
This is a feature under heavy development, so obviously it is disabled by
default. The only reason it goes to 2.8 branch is to avoid possible merge
hell.
In order to enable this feature use WITH_DEPSGRAPH_COPY_ON_WRITE CMake
configuration option.
219 lines
6.1 KiB
C++
219 lines
6.1 KiB
C++
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* 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) 2013 Blender Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* Original Author: Joshua Leung
|
|
* Contributor(s): None Yet
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/depsgraph/intern/nodes/deg_node_component.h
|
|
* \ingroup depsgraph
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "intern/nodes/deg_node.h"
|
|
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_string.h"
|
|
|
|
struct ID;
|
|
struct bPoseChannel;
|
|
struct GHash;
|
|
|
|
struct EvaluationContext;
|
|
|
|
namespace DEG {
|
|
|
|
struct Depsgraph;
|
|
struct OperationDepsNode;
|
|
struct BoneComponentDepsNode;
|
|
|
|
/* ID Component - Base type for all components */
|
|
struct ComponentDepsNode : public DepsNode {
|
|
/* Key used to look up operations within a component */
|
|
struct OperationIDKey
|
|
{
|
|
eDepsOperation_Code opcode;
|
|
const char *name;
|
|
int name_tag;
|
|
|
|
OperationIDKey();
|
|
OperationIDKey(eDepsOperation_Code opcode);
|
|
OperationIDKey(eDepsOperation_Code opcode,
|
|
const char *name,
|
|
int name_tag);
|
|
|
|
string identifier() const;
|
|
bool operator==(const OperationIDKey &other) const;
|
|
};
|
|
|
|
/* Typedef for container of operations */
|
|
ComponentDepsNode();
|
|
~ComponentDepsNode();
|
|
|
|
void init(const ID *id, const char *subdata);
|
|
|
|
string identifier() const;
|
|
|
|
/* Find an existing operation, will throw an assert() if it does not exist. */
|
|
OperationDepsNode *find_operation(OperationIDKey key) const;
|
|
OperationDepsNode *find_operation(eDepsOperation_Code opcode,
|
|
const char *name,
|
|
int name_tag) const;
|
|
|
|
/* Check operation exists and return it. */
|
|
OperationDepsNode *has_operation(OperationIDKey key) const;
|
|
OperationDepsNode *has_operation(eDepsOperation_Code opcode,
|
|
const char *name,
|
|
int name_tag) const;
|
|
|
|
/**
|
|
* Create a new node for representing an operation and add this to graph
|
|
* \warning If an existing node is found, it will be modified. This helps
|
|
* when node may have been partially created earlier (e.g. parent ref before
|
|
* parent item is added)
|
|
*
|
|
* \param type: Operation node type (corresponding to context/component that
|
|
* it operates in)
|
|
* \param optype: Role that operation plays within component
|
|
* (i.e. where in eval process)
|
|
* \param op: The operation to perform
|
|
* \param name: Identifier for operation - used to find/locate it again
|
|
*/
|
|
OperationDepsNode *add_operation(const DepsEvalOperationCb& op,
|
|
eDepsOperation_Code opcode,
|
|
const char *name,
|
|
int name_tag);
|
|
|
|
/* Entry/exit operations management.
|
|
*
|
|
* Use those instead of direct set since this will perform sanity checks.
|
|
*/
|
|
void set_entry_operation(OperationDepsNode *op_node);
|
|
void set_exit_operation(OperationDepsNode *op_node);
|
|
|
|
void clear_operations();
|
|
|
|
void tag_update(Depsgraph *graph);
|
|
|
|
/* Evaluation Context Management .................. */
|
|
|
|
/* Initialize component's evaluation context used for the specified
|
|
* purpose.
|
|
*/
|
|
virtual bool eval_context_init(EvaluationContext * /*eval_ctx*/) { return false; }
|
|
/* Free data in component's evaluation context which is used for
|
|
* the specified purpose
|
|
*
|
|
* NOTE: this does not free the actual context in question
|
|
*/
|
|
virtual void eval_context_free(EvaluationContext * /*eval_ctx*/) {}
|
|
|
|
OperationDepsNode *get_entry_operation();
|
|
OperationDepsNode *get_exit_operation();
|
|
|
|
void finalize_build(Depsgraph *graph);
|
|
|
|
IDDepsNode *owner;
|
|
|
|
/* ** Inner nodes for this component ** */
|
|
|
|
/* Operations stored as a hash map, for faster build.
|
|
* This hash map will be freed when graph is fully built.
|
|
*/
|
|
GHash *operations_map;
|
|
|
|
/* This is a "normal" list of operations, used by evaluation
|
|
* and other routines after construction.
|
|
*/
|
|
vector<OperationDepsNode *> operations;
|
|
|
|
OperationDepsNode *entry_operation;
|
|
OperationDepsNode *exit_operation;
|
|
|
|
// XXX: a poll() callback to check if component's first node can be started?
|
|
};
|
|
|
|
/* ---------------------------------------- */
|
|
|
|
struct ParametersComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct AnimationComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct TransformComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct ProxyComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct GeometryComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct SequencerComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct PoseComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
/* Bone Component */
|
|
struct BoneComponentDepsNode : public ComponentDepsNode {
|
|
void init(const ID *id, const char *subdata);
|
|
|
|
struct bPoseChannel *pchan; /* the bone that this component represents */
|
|
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct ParticlesComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct ShadingComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct CacheComponentDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct LayerCollectionsDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
struct CopyOnWriteDepsNode : public ComponentDepsNode {
|
|
DEG_DEPSNODE_DECLARE;
|
|
};
|
|
|
|
|
|
void deg_register_component_depsnodes();
|
|
|
|
} // namespace DEG
|