This document provides essential context for AI assistants (Claude, GPT, Copilot, etc.) working with the VL.Fuse codebase.
VL.Fuse is a visual GPU programming library for vvvv gamma. It compiles node graphs into SDSL (Stride Shading Language) shaders at runtime.
Tech Stack:
- C# 13 / .NET 8.0 - Core engine
- SDSL - GPU shaders (Stride's HLSL-based language)
- VL - Visual programming patches
- Stride 3D Engine - Rendering backend
AbstractShaderNode (base class, manages graph connections)
└── ShaderNode<T> (generic typed wrapper)
└── ResultNode<T> (nodes that produce a value)
└── ComputeNode<T>(compute shader nodes)
ShaderNode.SourceTemplate()returns SDSL template with${placeholders}CreateTemplateMap()provides values for placeholdersShaderNodesUtil.Evaluate(template, map)performs substitutionBuildSource()traverses graph and concatenates all code
// Template with placeholders
"${resultType} ${resultName} = ${a} + ${b};"
// After evaluation
"float3 Add_12345 = position_111 + offset_222;"Common placeholders:
${resultType}- GPU type (float, float3, etc.)${resultName}- Unique variable name (ID)${arguments}- Comma-separated input IDs${implementation}- Expression for ResultNode
Nodes declare dependencies via string-keyed properties:
Mixins- Required shader includesInputs- GPU parameters (uniforms, buffers)Declarations- Field declarationsStructs- Custom struct definitionsStreams- Shader stream definitions
Always pass NodeContext as the first constructor parameter:
// Correct
public MyNode(NodeContext nodeContext, ShaderNode<float> input)
: base(nodeContext, "MyNode") { }
// Wrong - will break node identification
public MyNode(ShaderNode<float> input)// Correct
SetInputs(new AbstractShaderNode[] { input1, input2 });
// Wrong - bypasses null handling
Ins.Add(input1);Node IDs are Name_HashCode. Never hardcode IDs:
// Correct
return ID; // "MyNode_12345678"
// Wrong - causes collisions
return "myVar";Methods called via reflection from VL should be marked:
// ReSharper disable once UnusedMember.Global
// accessed from vl
public static void MyMethod() { }
// USED BY VL - do not remove
public string TypeOverride { get; set; }src/Fuse/ # C# shader generation engine
├── ShaderNode.cs # Core classes: AbstractShaderNode, ShaderNode<T>
├── ShaderNodesUtil.cs # Evaluate(), BuildArguments(), utilities
├── compute/ # Compute shader support
├── function/ # Function abstractions
└── ShaderFX/ # ShaderFX integration
vl/
├── shaders/ # SDSL shader files (FuseCommon*.sdsl, etc.)
└── *.vl # Visual patches
help/ # Documentation patches by category
docs/decisions/ # Architecture Decision Records
| C# Type | GPU Type | Notes |
|---|---|---|
float |
float |
|
Vector2 |
float2 |
|
Vector3 |
float3 |
|
Vector4 |
float4 |
|
int |
int |
|
uint |
uint |
|
bool |
bool |
|
Matrix |
float4x4 |
|
GpuStruct |
custom | Uses TypeOverride |
namespace Fuse;
public class MyOperation<T> : ResultNode<T> where T : struct
{
public MyOperation(NodeContext nodeContext, ShaderNode<T> a, ShaderNode<T> b)
: base(nodeContext, "MyOperation")
{
SetInputs(new AbstractShaderNode[] { a, b });
}
protected override string ImplementationTemplate()
{
return "myFunc(${arguments})";
}
}// In vl/shaders/FuseCommon*.sdsl
float myFunc(float a, float b)
{
return a * b;
}// In constructor
AddProperty(Mixins, "FuseCommonBuffer");
AddProperty(Inputs, new BufferInput(buffer));- Don't skip
NodeContextparameter - Don't hardcode variable names (use
ID) - Don't modify
Inslist directly - Don't ignore null input handling
- Don't add breaking changes without updating help patches
- Don't remove methods marked
// USED BY VL
ShaderNode.cs:245-AbstractShaderNodeclass definitionShaderNode.cs:609-ShaderNode<T>generic wrapperShaderNodesUtil.cs:125-Evaluate()template functionShaderNodesUtil.cs:254-RegisterComputeShader()ShaderNodesUtil.cs:267-RegisterDrawShader()
- ARCHITECTURE.md - Full system architecture
- PATTERNS.md - Code patterns and conventions
- CONTRIBUTING.md - Contribution guidelines
- docs/decisions/ - Architecture Decision Records