Skip to content

Low Level Virtualization Details

CoffeeVampir3 edited this page Apr 11, 2021 · 14 revisions

What is Virtualization about?

Lets take a look at a very basic graph: Basic Graph Example

When you use a Graph Blueprint to construct a Virtual Graph, the blueprint will initialize a new set of virtual ports which are then indexed by a lookup key. Every virtual graph actually shares the same set of concrete objects, it uses this lookup key generated by the blueprint to access the data, and it does not instantiate new objects. This means each copy of a blueprint is nearly free, the vitualization process also means that creating a copy is also virtually free. You can imagine that each Virtual Graph is just an index into an actual graph (because it is.) virtualized graph This is a good way to imagine how the virtualization proces works, you're creating a new "slice" where the only unique thing about each slice is the link values. The nodes themselves do not change from one to another.

There's a note here, the first Virtual Graph created by a blueprint is more expensive, you can call GraphBlueprint.Precache() to run the expensive operations at a more opportune moment (For example, when your game is loading.)

More on Links

Okay, so we know links are virtualized at runtime, when you create a new Virtual Graph using the Graph Blueprint's CreateVirtualGraph the Graph Blueprint automatically creates a virtualized set of links and values. This is why the blueprint returns a "Virtual Graph" and not a graph blueprint, because you're actually only getting an ID allocated into a lookup table. This allows for very little allocations and there's essentially no cost to each new Virtual Graph as a result. These links are indexed using the same index provided to the Virtual Graph when it was created, which you can find using VirtualGraph.virtualId. When you call something like ValuePort.TryGetValue there's some magic going on related to the VirtualGraph id:

return valuePort.TryGetValue(CurrentGraphIndex, link, out value);

This is the code that actually returns the value of a link, you can see it's using a dictionary lookup into what is the current Virtual Graph ID index. That index is set when a node starts being evaluated:

internal virtual RuntimeNode Evaluate(Context evContext)
{
    int graphId = evContext.virtGraph.virtualId;
    //Make sure we're looking up the right graph index when we lookup our port values.
    BasePort.CurrentGraphIndex = graphId;
    Blackboards.virtGraph = evContext.virtGraph;
    return OnEvaluate(evContext);
}

Here you can see BasePort.CurrentGraphIndex and Blackboards.virtGraph are set based on the current context, which contains information about the current virtual graph being executed. (Incidentally, this is why accessing blackboards outside OnEvaluate or the evaluator's Initialization are undefined behaviour.) The principle benefit this allows for is a much simpler API.

link.Reset(contextId);

This would reset the value of that link -- for the virtual graph with that contextId -- to the value initial value (pulled from the actual Graph Controller)

The lifespan of a Virtual Graph is tied to the object, hence why the object has the ID. Once a Virtual Graph is collected that ID goes back into the pool, and a new Virtual Graph can then be allocated with that ID.