You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取指向原生DirectX11结构体的IntPtr?C++与C#互操作场景

实现C++ D3D11Buffer到C# SlimDX的零拷贝互操作

我来帮你梳理下如何实现零拷贝的跨语言D3D11Buffer互操作——你的核心需求是绕开CPU拷贝,所以关键是用共享GPU资源的方式让C++和C#直接访问同一块显存。下面是具体的调整和落地步骤:

一、C++端的关键修正与实现

1. 按共享资源要求创建缓冲区

要让缓冲区能跨语言/进程共享,创建ID3D11Buffer时必须添加D3D11_RESOURCE_MISC_SHARED标记(Windows 8+也可以用更安全的D3D11_RESOURCE_MISC_SHARED_NTHANDLE),同时资源用法要匹配GPU访问需求(零拷贝场景优先用D3D11_USAGE_DEFAULT)。

示例创建代码:

// 确保顶点结构和C#端内存布局完全一致
#pragma pack(push, 1)
struct Vertex {
    XMFLOAT3 pos;
    XMFLOAT4 color;
};
#pragma pack(pop)

// 初始化D3D设备、上下文的代码这里省略,假设已经完成初始化
ID3D11Device* d3d11Device;
ID3D11DeviceContext* d3d11DevCon;
ID3D11Buffer* triangleVertBuffer;

// 创建共享缓冲区的核心代码
HRESULT createSharedVertexBuffer() {
    D3D11_BUFFER_DESC bufferDesc = {};
    bufferDesc.Usage = D3D11_USAGE_DEFAULT;
    bufferDesc.ByteWidth = sizeof(Vertex) * 3; // 示例3个三角形顶点
    bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    // 关键:添加共享资源标记
    bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;

    // 初始化顶点数据(动态填充可后续用UpdateSubresource)
    Vertex vertices[] = {
        { XMFLOAT3(0.0f, 0.5f, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
        { XMFLOAT3(0.5f, -0.5f, 0.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
        { XMFLOAT3(-0.5f, -0.5f, 0.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) }
    };
    D3D11_SUBRESOURCE_DATA initData = {};
    initData.pSysMem = vertices;

    return d3d11Device->CreateBuffer(&bufferDesc, &initData, &triangleVertBuffer);
}

2. 修正导出函数:传递共享句柄而非原始指针

直接传递ID3D11Buffer*给C#是行不通的,COM对象跨语言传递需要用共享句柄。修改你的导出函数:

extern "C" {
    __declspec(dllexport) HRESULT getValues(int* debug, HANDLE* outSharedHandle) {
        if (!triangleVertBuffer || !outSharedHandle) {
            *debug = E_INVALIDARG;
            return E_INVALIDARG;
        }

        // 创建共享资源句柄
        HRESULT hr = d3d11Device->CreateSharedHandle(triangleVertBuffer, nullptr, GENERIC_ALL, nullptr, outSharedHandle);
        if (FAILED(hr)) {
            *debug = hr;
            return hr;
        }

        *debug = S_OK;
        return S_OK;
    }
}

二、C#(SlimDX)端的对接实现

1. 定义匹配的顶点结构

确保和C++端的内存布局、字节对齐完全一致:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Vertex
{
    public Vector3 Position;
    public Color4 Color;

    public Vertex(Vector3 pos, Color4 color)
    {
        Position = pos;
        Color = color;
    }
}

2. 导入C++导出函数

[DllImport("你的C++动态库名称.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int getValues(out int debugCode, out IntPtr sharedHandle);

3. 打开共享缓冲区并直接使用

通过共享句柄打开缓冲区后,可直接绑定为顶点缓冲区使用,全程无CPU拷贝:

// 假设你已经初始化了SlimDX的D3D11Device和DeviceContext
int debugCode;
IntPtr sharedHandle;
int hr = getValues(out debugCode, out sharedHandle);
if (hr != 0) // HRESULT的S_OK对应0
{
    throw new Exception($"获取共享句柄失败,错误码:{debugCode:X8}");
}

// 通过共享句柄打开D3D11缓冲区
using (var sharedBuffer = SlimDX.Direct3D11.Buffer.FromSharedHandle(device, sharedHandle))
{
    // 直接绑定为顶点缓冲区使用
    deviceContext.InputAssembler.SetVertexBuffers(
        0, 
        new VertexBufferBinding(sharedBuffer, Marshal.SizeOf(typeof(Vertex)), 0)
    );
    // 后续即可正常执行绘制逻辑
}

// 释放共享句柄(C++端创建的句柄,C#侧需负责释放)
CloseHandle(sharedHandle);

// 导入kernel32的CloseHandle函数
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hObject);

三、核心注意事项

  • 内存布局一致性:C++和C#的顶点结构必须严格匹配,建议两端都用pack=1强制对齐,避免因编译器默认对齐导致数据错乱。
  • 资源生命周期:要确保C++端的缓冲区在C#使用期间不会被释放,或者通过COM引用计数做好生命周期管理。
  • 权限与系统版本:D3D11_RESOURCE_MISC_SHARED支持Windows 7及以上,Windows 8+推荐用D3D11_RESOURCE_MISC_SHARED_NTHANDLE并指定更精细的读写权限。

内容的提问来源于stack exchange,提问作者digitalWannabe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:26:53