classDiagram
class EffectGraph {
+vector~EffectNode~ nodes
+vector~EffectEdge~ edges
+AddNode(EffectNode) uint32_t
+Connect(srcId, srcPin, dstId, dstPin)
+TopologicalSort() vector~uint32_t~
+Evaluate(ID2D1DeviceContext*) ID2D1Image*
+ToJson() string
+FromJson(string) EffectGraph
}
class EffectNode {
+uint32_t id
+string name
+NodeType type
+float2 position
+map~string,Variant~ properties
+ID2D1Image* cachedOutput
}
class EffectEdge {
+uint32_t sourceNodeId
+uint32_t sourcePin
+uint32_t destNodeId
+uint32_t destPin
}
class NodeType {
<<enumeration>>
Source
BuiltInEffect
PixelShader
ComputeShader
D3D11ComputeShader
Output
}
EffectGraph "1" *-- "*" EffectNode
EffectGraph "1" *-- "*" EffectEdge
EffectNode --> NodeType
NodeType::Output carries no shader — it is a sink that the evaluator draws to. Parameter and clock nodes (Float / Integer / Toggle / Gamut / Clock / Numeric Expression) are stored as ComputeShader-typed nodes whose customEffect.hlslSource is empty; the evaluator special-cases them into a CPU-side data path (see Parameter Nodes and Numeric Expression Node).