Quantcast
Channel: CodeSection,代码区,数据库(综合) - CodeSec
Viewing all articles
Browse latest Browse all 6262

Dependency graph proposal

$
0
0

This postis all about dependency graph design for the Blender 2.8 project.

Brief overview

Before going too much into technical details let’s first look into some top-level design overview about what’s this project is all about, what would that all mean for users and how it’ll affect on everyone working in 2.8 branch.

Dependency graph is owned by layer. Dependency graph holds both relations AND evaluation data (aka state of the scene). Dependency graph allows Render Engine to store its specific data in the evaluation context. Dependency graph evaluation data is aware of copy-on-write and data de-duplication. Dependency graph stays flexible to support data compression in the future.

Now let’s look into all the technical details, digging into what benefits each decision brings.

Ownership

Here and in the rest of the document layers will be referencing to the new layers which are being worked on by Dalai currently. Those will have almost nothing to do with the layers existing in Blender so far.

Each layer will have it’s own dependency graph which will contain both relations and scene state. This gives the following benefits:

Each layer gets dependency graph which is highly optimized for that particular use.
There is no objects in the dependency graph which does not affect this particular layer, meaning there’s no way to have any possible overhead on depsgraph reconstruction and evaluation. In practice this would mean that modelling layer where artists adds new environment objects will not trigger all the character’s rigs dependencies update, keeping interaction as responsive as possible. There is a clear separation in evaluation data (also known as scene state), so this is impossible to have threading conflicts between render and viewport threads. This leads to really clear ownership model and makes it clear what is the time life of the evaluation data.
Dependency graph proposal

In order to keep memory usage as low as possible, dependency graph and its evaluation context gets freed when layer becomes invisible.

Such approach does not allow to have same layer to be in multiple states in different windows (for example, it’s not possible to have layer at frame 16 in one window and at frame 32 in another window). But it is possible to set up your workflow in a way that does not require this. For example, you can have animation layer with only character you’re currently animating and show it in one window and have another preview layer which includes everything required for the preview (which could include more objects from environment or other character).

As a possible downside (or at least something to be aware of) in such model is that showing two different layers with same objects in different windows will slow down performance (objects would need to be update for both windows). This is probably possible to solve in the future with some smart de-duplication of evaluation.

Evaluation Data

Previously, scene state was fully decoupled from the dependency graph, which had advantages of simplicity. Now we need to store multiple states of the same scene. Dependency graph seems to be the most appropriate place for that.

Proposal is to store actual data in the ID nodes (or other “outer” node types). This will make it simple to map original objects to evaluated ones and vice versa (just to state the obvious: it’ll cover any datablock which dependency graph is keeping track of, including node trees, collections, …).

Evaluated data for objects includes applied:

Drivers Animation Overrides Modifiers

All the render engines will only access evaluated objects and will treat them as fully final (no need from render-engine side to worry about overrides i.e.). This will happen via DEG_OBJECT_ITER() macro (with similar semantics to GHASH_ITER).

Tools will work on original scene data, using objects from active layer.

NOTE:Still not fully clear whether we’ll need to keep bmain in depsgraph or not (as some container of all evaluated objects). This is a topic for discussion.

The data flow here would be:

Original DNA is feeding to the input of the dependency graph. Dependency graph copies that data, and runs all operations on this copied data. Dependency graph stores this modified (or evaluated) data in the corresponding outer node (ID node).
Dependency graph proposal

In order to make node-based everything to work we’ll need to extend this idea deeper and allow operation nodes to have copies of the original data as well. This could be nicely hidden behind the API calls to make such specific to be fully transparent for all related code such as modifiers.

NOTE:We would need to have some API to get final state of the object for modifiers anyway, so extending the scope where the data is stored is not that much of an affect on API.

Data stored in operation nodes gets freed once all users of that data are evaluated, which will ensure lowest possible memory footprint. This is possible for as long as we don’t allow other objects to be dependent on intermediate evaluation result (otherwise we wouldn’t be able to re-evaluate other objects after we dropped all intermediate data).

Interaction with Render Engine

Just to re-cap things which were mentioned above:

Render engine only deals with final evaluated objects Render engine does not worry about overrides and consider that all data blocks it interacts with has overrides applied on them.

One thing which was not covered here yet is the requirement to store some renderer-specific data in objects. Example of such data could be VBOs for OpenGL renderer.

Easiest way to deal with this seems to be to allow render engines to store their specific data in the evaluated datablocks. This will grant persistency of this data across redraws for as long as objects do no change. Once object changes and gets re-evaluated it’s renderer-specific data gets freed and recreated by the render engine later on.

If needed, we can go more granular here and allow renderers to have per-evaluation-channel storage, so moving object around will not invalidate it’s VBOs.

Copy-on-write and de-duplication

The goal here is to minimize memory footprint of dependency graph storage by duplicating only data which is getting changed. Roughly speaking, when dependency graph creates local copy of the datablock for the evaluation data storage it only duplicates datablock, but keeps all CustomData referencing the original object’s CustomData. If some modifier changes any CustomData layer it gets decoupled from the original storage and being re-allocated. This is actually quite close to how CustomData currently works in the DerivedMesh.

More tricky scenario here is data de-duplication across multiple dependency graphs, which will help keeping memory usage low when multiple layers are visible and are on the same state.

Proposal here is to go the following route:

Have a global storage of evaluated objects, where dependency graph stores evaluation result of all outer nodes (objects, node trees, …) Objects in this storage are reference-counted so we know when we can remove data from the storage. Objects from this storage gets removed when they are tagged for update. Dependency graph evaluation checks whether object at a given state exists in that storage and if so, references to it instead of doing full object evaluation.
Dependency graph proposal
This sounds a bit

Viewing all articles
Browse latest Browse all 6262

Trending Articles