关于 vvvv-dotnet
面向 vvvv gamma 插件开发者的 .NET 集成指南,涵盖 .csproj 配置、节点发现、构建与打包,以及资源管理和线程处理。
主要功能
- 01.csproj 与 vvvv 插件配置
- 02源码引用和二进制引用工作流
- 03节点发现所需的程序集设置
- 04NuGet 打包与构建命令
- 05资源管理、异步与线程模式
适用场景
- 01配置 vvvv C# 插件项目
- 02选择实时重载或预编译程序集
- 03构建并分发插件 NuGet 包
- 04为节点处理原生资源或异步任务
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tebjan/ vvvv-skills --skill vvvv-dotnet安装后在 Claude Code 中输入/vvvv-dotnet确认已加载。
附带文件forwarding.md
详细介绍
vvvv gamma 中的 .NET 集成
vvvv 插件的 .csproj 配置
vvvv gamma C# 插件的最简 .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputPath Condition="'$(Configuration)'=='Release'">..\..\lib\net8.0\</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="VL.Core" Version="2025.7.*" />
<PackageReference Include="VL.Stride" Version="2025.7.*" />
</ItemGroup>
</Project>
关键设置:
- 目标框架:
net8.0(vvvv gamma 6+ 必需) - 输出路径:指向相对于
.vl文档的lib/net8.0/ - AppendTargetFrameworkToOutputPath:设为
false,使 DLL 直接输出到目标文件夹
vvvv 如何使用 C# 代码
将 C# 集成到 .vl 文档有两种工作流:
源项目引用(实时重新加载):.vl 文档引用一个 .csproj。vvvv 会通过 Roslyn 自行编译 .cs 源文件,并生成内存程序集——无需运行 dotnet build。每次保存 .cs 文件时,vvvv 都会检测到更改并自动重新编译。实时开发期间不会用到 .csproj 中的输出路径;该路径用于 NuGet 打包和部署。
二进制引用(不支持实时重新加载):.vl 文档引用预编译的 DLL 或 NuGet 包。要应用 C# 更改,需在外部重新构建(dotnet build)并重启 vvvv。这是较大型项目和稳定库的标准工作流。
无论使用哪种工作流,着色器(.sdsl 文件)都会实时重新加载。
对于 AI Agent:无论使用哪种工作流,都应运行 dotnet build 验证代码能否编译——你无法查看 vvvv 的编译器输出。对于源项目引用,vvvv 会在保存文件时自动获取更改(无需重启)。对于二进制引用,必须运行 dotnet build,且用户必须重启 vvvv。
必需的全局 using
global using VL.Core;
global using VL.Core.Import;
global using VL.Lib.Collections;
必需的程序集特性
要让 vvvv 发现你的 ProcessNodes 和静态方法:
[assembly: ImportAsIs]
如果没有此项,你的节点将不会出现在 vvvv 节点浏览器中。
NuGet 包源
将以下内容添加到你的 NuGet.config,以获取 vvvv 包:
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="vvvv" value="https://teamcity.vvvv.org/guestAuth/app/nuget/v1/FeedService.svc/" />
</packageSources>
</configuration>
NuGet 打包
要将你的插件分发为 NuGet 包:
nuget pack MyPlugin/deployment/MyPlugin.nuspec
.nuspec 应引用你的 .vl 文档、已编译的 DLL、着色器文件和帮助补丁。
常见 VL 包
| 包 | 用途 |
|---|---|
VL.Core | 核心类型、ProcessNode 特性、Spread |
VL.Stride | 3D 渲染、着色器集成 |
VL.Stride.Runtime | Stride 引擎运行时 |
VL.Core.Import | ImportAsIs 特性 |
VL.Lib.Collections | Spread、SpreadBuilder |
VL.Skia | 2D 渲染(Skia 图形引擎) |
VL.Fuse | GPU 可视化编程(着色器图) |
VL.IO.OSC | 开放声音控制协议 |
VL.IO.MQTT | MQTT 消息传递 |
VL.IO.Redis | Redis 键值存储 |
VL.OpenCV | 计算机视觉(OpenCV 绑定) |
VL.MediaPipe | MediaPipe ML 管线(手部、面部、姿态) |
VL.Audio | 音频合成与输入/输出(基于 NAudio) |
VL.Devices.AzureKinect | Azure Kinect / Orbbec 深度相机 |
完整目录请参阅 vvvv.org/packs。
向量类型与 SIMD 策略
- 内部热点路径:使用
System.Numerics.Vector3/Vector4/Quaternion(通过 AVX/SSE 实现 SIMD) - 外部 API(Update 方法参数):使用
Stride.Core.Mathematics类型(VL 必需) - 两者之间可实现零开销转换:
using System.Runtime.CompilerServices;
// Stride → System.Numerics (zero-cost reinterpret)
ref var numericsVec = ref Unsafe.As<Stride.Core.Mathematics.Vector3, System.Numerics.Vector3>(ref strideVec);
// System.Numerics → Stride (zero-cost reinterpret)
ref var strideVec = ref Unsafe.As<System.Numerics.Vector3, Stride.Core.Mathematics.Vector3>(ref numericsVec);
这些类型具有相同的内存布局,因此 Unsafe.As 操作无需额外开销。
IDisposable 与资源管理
任何持有本机/非托管资源的节点都必须实现 IDisposable:
[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 会调用 Dispose()。
vvvv 中的异步模式
由于 Update() 在主线程上以 60 FPS 运行,长时间运行的操作必须采用异步方式:
[ProcessNode]
public class AsyncLoader
{
private Task<string>? _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;
}
}
用于 GPU/网络的可直接封送结构体
对于需要跨越 GPU 或网络边界的数据,请使用可直接封送的结构体:
[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
}
规则:不得包含引用类型字段,不得使用 bool(请使用 int),并使用显式布局。这样即可通过 Span<T> 访问数据,并通过 MemoryMarshal 实现零拷贝序列化。
引用由 vvvv 加载的 DLL
引用 vvvv 已加载的 DLL(例如 VL.Fuse)时,请使用 <Private>false</Private> 以避免复制:
<Reference Include="Fuse">
<HintPath>..\..\path\to\Fuse.dll</HintPath>
<Private>false</Private>
</Reference>
构建命令
构建 vvvv 插件项目:
dotnet build src/MyPlugin.csproj -c Release
构建整个解决方案:
dotnet build src/MyPlugin.sln -c Release
C++/CLI 互操作
用于封装原生 C/C++ 库:
msbuild MyCLIWrapper/MyCLIWrapper.vcxproj /p:Configuration=Release /p:Platform=x64
C++/CLI 项目必须使用 Visual Studio(而不是 dotnet CLI)进行构建。
常见的包版本范围
引用 vvvv 包时,使用通配符版本以保持兼容:
<PackageReference Include="VL.Core" Version="2025.7.*" />
这样可确保你的插件适用于目标 vvvv 版本的任意补丁版本。
Directory.Build.props
对于多项目解决方案,请集中管理设置:
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
</Project>
COM 互操作注意事项(DX11/DX12)
使用 COM 对象(Direct3D、DXGI)时:
ComPtr<T>是一个没有终结器的结构体——如果它离开作用域时未调用Dispose(),COM 引用就会泄漏- 始终将 ComPtr 返回到池中,或显式调用 Dispose
- 如果队列上的任何命令列表处于录制状态,
IDXGISwapChain::ResizeBuffers就会失败
有关将 .NET 库转发到 VL(封装而不创建新类型、固定修改、封装事件),请参阅 forwarding.md。
线程注意事项
Update()始终在 VL 主线程上调用- 使用
SynchronizationContext从后台任务切回 VL 线程:
private SynchronizationContext _vlSyncContext;
public MyNode()
{
_vlSyncContext = SynchronizationContext.Current!;
}
// From background thread:
_vlSyncContext.Post(_ => { /* runs on VL thread */ }, null);
不断积累知识
让下一位修补者少一个疑问。首次使用本合集中的任何技能,且过去七天内没有成功检查记录时,请对照 tebjan/vvvv-skills。如果没有记录,则检查一次。 在这些技能之间共享一个本地检查时间戳/修订版本;不要在每次调用时都轮询。 保留本地扩展和固定的项目版本;检查并不代表有权安装更新。如果处于离线状态,请记录延期检查并继续任务。
当实际工作暴露出知识缺口时,请记录经过测试的 vvvv/包版本、最小复现示例或权威来源,以及观察到的结果。将其他用户的报告视为线索,在验证前不要当作事实。只改进最相关的最小范围内的说明;不要把一个示例推广成通用规则。提交上游前,请对私人数据进行脱敏并获得发布授权。 请参阅贡献政策。 合并后的改进会通过用户的安装器/更新工作流送达,而不是通过本地编辑或自动覆盖所有人的技能。