--- name: vvvv-dotnet description: Helps with .NET integration in vvvv gamma — NuGet packages, library references, .csproj project configuration, vector type interop, and async patterns. Use when adding NuGet packages, configuring build settings, referencing external .NET libraries, or working with System.Numerics/Stride type conversions. license: CC-BY-SA-4.0 compatibility: Designed for coding AI agents assisting with vvvv gamma development metadata: author: Tebjan Halm version: "1.0" --- # .NET Integration in vvvv gamma ## .csproj Configuration for vvvv Plugins Minimal `.csproj` for a vvvv gamma C# plugin: ```xml net8.0 ..\..\lib\net8.0\ false ``` Key settings: - **Target framework**: `net8.0` (required for vvvv gamma 6+) - **Output path**: Point to `lib/net8.0/` relative to your `.vl` document - **AppendTargetFrameworkToOutputPath**: Set to `false` so DLLs go directly to the output folder ### How vvvv Uses C# Code There are two workflows for integrating C# with a .vl document: **Source project reference** (live reload): The .vl document references a .csproj. vvvv compiles .cs source files itself via Roslyn into in-memory assemblies — no `dotnet build` needed. On every .cs file save, vvvv detects the change and recompiles automatically. The output path in .csproj is not involved during live development; it is used for NuGet packaging and deployment. **Binary reference** (no live reload): The .vl document references a pre-compiled DLL or NuGet package. To apply C# changes, rebuild externally (`dotnet build`) and restart vvvv. This is the standard workflow for larger projects and stable libraries. Shaders (.sdsl files) always live-reload regardless of workflow. **For AI agents**: regardless of workflow, run `dotnet build` to verify your code compiles — you cannot see vvvv's compiler output. For source project references, vvvv picks up changes on file save automatically (no restart needed). For binary references, `dotnet build` is required and the user must restart vvvv. ## Required Global Usings ```csharp global using VL.Core; global using VL.Core.Import; global using VL.Lib.Collections; ``` ## Required Assembly Attribute For vvvv to discover your ProcessNodes and static methods: ```csharp [assembly: ImportAsIs] ``` Without this, your nodes will not appear in the vvvv node browser. ## NuGet Package Sources Add these to your `NuGet.config` for vvvv packages: ```xml ``` ## NuGet Packaging To distribute your plugin as a NuGet package: ```bash nuget pack MyPlugin/deployment/MyPlugin.nuspec ``` The `.nuspec` should reference your `.vl` document, compiled DLLs, shader files, and help patches. ## Common VL Packages | Package | Purpose | |---|---| | `VL.Core` | Core types, ProcessNode attribute, Spread | | `VL.Stride` | 3D rendering, shader integration | | `VL.Stride.Runtime` | Stride engine runtime | | `VL.Core.Import` | ImportAsIs attribute | | `VL.Lib.Collections` | Spread, SpreadBuilder | | `VL.Skia` | 2D rendering (Skia graphics engine) | | `VL.Fuse` | GPU visual programming (shader graph) | | `VL.IO.OSC` | Open Sound Control protocol | | `VL.IO.MQTT` | MQTT messaging | | `VL.IO.Redis` | Redis key-value store | | `VL.OpenCV` | Computer vision (OpenCV bindings) | | `VL.MediaPipe` | MediaPipe ML pipelines (hand, face, pose) | | `VL.Audio` | Audio synthesis and I/O (NAudio-based) | | `VL.Devices.AzureKinect` | Azure Kinect / Orbbec depth cameras | For a full catalog, see [vvvv.org/packs](https://vvvv.org/packs). ## Vector Types & SIMD Strategy - **Internal hot paths**: Use `System.Numerics.Vector3/Vector4/Quaternion` (SIMD via AVX/SSE) - **External API** (Update method params): Use `Stride.Core.Mathematics` types (required by VL) - **Zero-cost conversion** between them: ```csharp using System.Runtime.CompilerServices; // Stride → System.Numerics (zero-cost reinterpret) ref var numericsVec = ref Unsafe.As(ref strideVec); // System.Numerics → Stride (zero-cost reinterpret) ref var strideVec = ref Unsafe.As(ref numericsVec); ``` These types have identical memory layouts, making `Unsafe.As` a zero-cost operation. ## IDisposable and Resource Management Any node holding native/unmanaged resources must implement `IDisposable`: ```csharp [ProcessNode] public class NativeWrapper : IDisposable { private IntPtr _handle; public NativeWrapper() { _handle = NativeLib.Create(); } public void Update(out int result) { result = NativeLib.Process(_handle); } public void Dispose() { if (_handle != IntPtr.Zero) { NativeLib.Destroy(_handle); _handle = IntPtr.Zero; } } } ``` vvvv calls `Dispose()` when the node is removed or the document closes. ## Async Patterns in vvvv Since `Update()` runs on the main thread at 60 FPS, long-running operations must be async: ```csharp [ProcessNode] public class AsyncLoader { private Task? _loadTask; private string _cachedResult = ""; public void Update( out string result, out bool isLoading, string url = "", bool trigger = false) { if (trigger && (_loadTask == null || _loadTask.IsCompleted)) { _loadTask = Task.Run(() => LoadFromUrl(url)); } isLoading = _loadTask != null && !_loadTask.IsCompleted; if (_loadTask?.IsCompletedSuccessfully == true) _cachedResult = _loadTask.Result; result = _cachedResult; } } ``` ## Blittable Structs for GPU/Network For data that crosses GPU or network boundaries, use blittable structs: ```csharp [StructLayout(LayoutKind.Sequential)] public struct AnimationBlendState { public int ClipIndex1; // 4 bytes public float ClipTime1; // 4 bytes public int ClipIndex2; // 4 bytes public float ClipTime2; // 4 bytes public float BlendWeight; // 4 bytes } ``` Rules: no reference type fields, no `bool` (use `int`), explicit layout. Enables `Span` access and zero-copy serialization via `MemoryMarshal`. ## Referencing vvvv-Loaded DLLs When referencing DLLs already loaded by vvvv (e.g., VL.Fuse), use `false` to prevent copying: ```xml ..\..\path\to\Fuse.dll false ``` ## Build Commands Build a vvvv plugin project: ```bash dotnet build src/MyPlugin.csproj -c Release ``` Build an entire solution: ```bash dotnet build src/MyPlugin.sln -c Release ``` ## C++/CLI Interop For wrapping native C/C++ libraries: ```bash msbuild MyCLIWrapper/MyCLIWrapper.vcxproj /p:Configuration=Release /p:Platform=x64 ``` C++/CLI projects require Visual Studio (not dotnet CLI) for building. ## Common Package Version Ranges When referencing vvvv packages, use wildcard versions to stay compatible: ```xml ``` This ensures your plugin works with any patch release of the target vvvv version. ## Directory.Build.props For multi-project solutions, centralize settings: ```xml net8.0 true ``` ## COM Interop Pitfalls (DX11/DX12) When working with COM objects (Direct3D, DXGI): - `ComPtr` is a struct with no finalizer — if it goes out of scope without `Dispose()`, the COM ref leaks - Always return ComPtrs to pools or explicitly Dispose them - `IDXGISwapChain::ResizeBuffers` fails if any command list on the queue is in recording state For forwarding .NET libraries into VL (wrapping without new types, pin modifications, event wrapping), see [forwarding.md](forwarding.md). ## Threading Considerations - `Update()` is always called on the VL main thread - Use `SynchronizationContext` to post back to the VL thread from background tasks: ```csharp private SynchronizationContext _vlSyncContext; public MyNode() { _vlSyncContext = SynchronizationContext.Current!; } // From background thread: _vlSyncContext.Post(_ => { /* runs on VL thread */ }, null); ```