mcpField
V
vvvv-dotnet

vvvv-dotnet

github.com/tebjan开发3492 次安装2 天前更新
代码仓库

关于 vvvv-dotnet

面向 vvvv gamma 插件开发者的 .NET 集成指南,涵盖 .csproj 配置、节点发现、构建与打包,以及资源管理和线程处理。

主要功能

  1. 01.csproj 与 vvvv 插件配置
  2. 02源码引用和二进制引用工作流
  3. 03节点发现所需的程序集设置
  4. 04NuGet 打包与构建命令
  5. 05资源管理、异步与线程模式

适用场景

  1. 01配置 vvvv C# 插件项目
  2. 02选择实时重载或预编译程序集
  3. 03构建并分发插件 NuGet 包
  4. 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.Stride3D 渲染、着色器集成
VL.Stride.RuntimeStride 引擎运行时
VL.Core.ImportImportAsIs 特性
VL.Lib.CollectionsSpread、SpreadBuilder
VL.Skia2D 渲染(Skia 图形引擎)
VL.FuseGPU 可视化编程(着色器图)
VL.IO.OSC开放声音控制协议
VL.IO.MQTTMQTT 消息传递
VL.IO.RedisRedis 键值存储
VL.OpenCV计算机视觉(OpenCV 绑定)
VL.MediaPipeMediaPipe ML 管线(手部、面部、姿态)
VL.Audio音频合成与输入/输出(基于 NAudio)
VL.Devices.AzureKinectAzure 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/包版本、最小复现示例或权威来源,以及观察到的结果。将其他用户的报告视为线索,在验证前不要当作事实。只改进最相关的最小范围内的说明;不要把一个示例推广成通用规则。提交上游前,请对私人数据进行脱敏并获得发布授权。 请参阅贡献政策。 合并后的改进会通过用户的安装器/更新工作流送达,而不是通过本地编辑或自动覆盖所有人的技能。

评论

支持 Markdown