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

DX11(SharpDX)计算着色器结果读取异常排查求助

DirectX 11计算着色器结果读取异常排查

我用SharpDX开发DirectX 11计算着色器,预期输出应为{2,2,2}, {5,5,5}, {8,8,8},但实际得到{2,5,8}, {0,0,0}, {0,0,0};将HLSL输出改为xyz时,结果变成{1,4,7}, {0,0,0}, {0,0,0}。确认计算着色器输入和逻辑没问题,问题应该出在GPU结果读取环节,求排查。

C#代码实现

float[] buffer = new float[]{1,2,3,4,5,6,7,8,9};
var vertexArray = MemoryMarshal.Cast<float, Vector3>(buffer).ToArray();
var device = new Device(DriverType.Hardware, DeviceCreationFlags.Debug);
var context = device.ImmediateContext;
// 编译计算着色器
var faceCsFile = Path.Combine(AppContext.BaseDirectory, "HLSL/Face_CS.hlsl");
var shaderBytecode = ShaderBytecode.CompileFromFile(faceCsFile, "CS", "cs_5_0");
var computeShader = new ComputeShader(device, shaderBytecode);
shaderBytecode.Dispose();
var inputDesc = new BufferDescription()
{
    SizeInBytes = SharpDX.Utilities.SizeOf<Vector3>() * vertexArray.Length,
    Usage = ResourceUsage.Default,
    BindFlags = BindFlags.ShaderResource | BindFlags.UnorderedAccess,
    OptionFlags = ResourceOptionFlags.BufferStructured,
    StructureByteStride = SharpDX.Utilities.SizeOf<Vector3>(),
};
var verticeBuffer = Buffer.Create(device, vertexArray, inputDesc);
device.ImmediateContext.UnmapSubresource(verticeBuffer, 0);
var srvDesc = new ShaderResourceViewDescription()
{
    Format = SharpDX.DXGI.Format.Unknown,
    Dimension = ShaderResourceViewDimension.Buffer,
    BufferEx = new ShaderResourceViewDescription.ExtendedBufferResource()
    {
        FirstElement = 0,
        ElementCount = vertexArray.Length,
        Flags = ShaderResourceViewExtendedBufferFlags.None
    } 
};
var vertexBufferView = new ShaderResourceView(device, verticeBuffer, srvDesc);
context.ComputeShader.SetShaderResource(0, vertexBufferView);
// 将输入和输出缓冲区绑定到计算着色器上
context.ComputeShader.Set(computeShader);
var dataOut = new Vector3[vertexArray.Length];
var outDes = new BufferDescription()
{
    SizeInBytes = SharpDX.Utilities.SizeOf<Vector3>() * vertexArray.Length,
    Usage = ResourceUsage.Default,
    BindFlags = BindFlags.UnorderedAccess,
    OptionFlags = ResourceOptionFlags.BufferStructured,
    StructureByteStride = SharpDX.Utilities.SizeOf<Vector3>(),
};
var bufferOut = Buffer.Create(device, dataOut, outDes);
// 创建用于指定着色器输出的UAV
var uavDesc = new UnorderedAccessViewDescription()
{
    Format = SharpDX.DXGI.Format.Unknown,
    Dimension = UnorderedAccessViewDimension.Buffer,
    Buffer = new UnorderedAccessViewDescription.BufferResource()
    {
        ElementCount = vertexArray.Length,
    }
};
var bufferOutView = new UnorderedAccessView(device, bufferOut, uavDesc);
context.ComputeShader.SetUnorderedAccessView(0, bufferOutView);
int count = (vertexArray.Length + 1023) / 1024;
context.Dispatch(count, 1, 1);// 启动计算着色器
var stagingBuffer = new Buffer(device, new BufferDescription
{
    BindFlags = BindFlags.None,
    CpuAccessFlags = CpuAccessFlags.Read,
    OptionFlags = ResourceOptionFlags.None,
    SizeInBytes = dataOut.Length * SharpDX.Utilities.SizeOf<Vector3>(),
    Usage = ResourceUsage.Staging
});// 创建一个暂存缓冲区
context.CopyResource(bufferOut, stagingBuffer);// 将输出缓冲区的内容复制到暂存缓冲区中
// 将暂存缓冲区映射到内存中
var dataBox = context.MapSubresource(stagingBuffer, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
Span<Vector3> result;
unsafe{result = new Span<Vector3>((void*)dataBox.DataPointer, dataOut.Length);}// 读取数据
context.UnmapSubresource(stagingBuffer, 0);// 取消映射
foreach (var vec in result.ToArray())
    Console.WriteLine(vec);

HLSL计算着色器代码

StructuredBuffer<float3> Vertex : register(t0);
RWBuffer<float3> Out : register(u0);

[numthreads(1024, 1, 1)]
void CS(uint3 DTid : SV_DispatchThreadID)
{
    uint id = DTid.x;
    Out[id]=Vertex[id].yyy;
}

问题根源分析

  1. 暂存缓冲区属性不匹配
    输出缓冲区是结构化缓冲区(ResourceOptionFlags.BufferStructured),但创建的暂存缓冲区未指定结构化属性,导致CPU映射时无法正确解析内存布局,把连续的float数据错误地按Vector3结构读取。

  2. GPU计算与CPU复制不同步
    Dispatch调用后GPU可能还未完成计算就执行CopyResource,导致复制的是未初始化或不完整的数据。

修正方案

  1. 修正暂存缓冲区创建
    让暂存缓冲区和输出缓冲区保持一致的结构化属性:

    var stagingBuffer = new Buffer(device, new BufferDescription
    {
        BindFlags = BindFlags.None,
        CpuAccessFlags = CpuAccessFlags.Read,
        OptionFlags = ResourceOptionFlags.BufferStructured,
        StructureByteStride = SharpDX.Utilities.SizeOf<Vector3>(),
        SizeInBytes = dataOut.Length * SharpDX.Utilities.SizeOf<Vector3>(),
        Usage = ResourceUsage.Staging
    });
    
  2. 添加资源同步屏障
    在Dispatch后、CopyResource前插入屏障,确保计算完成后再复制数据:

    context.Dispatch(count, 1, 1);
    // 添加资源屏障同步GPU计算
    var barrier = new ResourceBarrier(
        ResourceBarrierType.Transition,
        ResourceBarrierFlags.None,
        bufferOut,
        ResourceState.UnorderedAccess,
        ResourceState.CopySource
    );
    context.ResourceBarrier(barrier);
    // 执行复制操作
    context.CopyResource(bufferOut, stagingBuffer);
    
  3. 移除多余代码
    删除device.ImmediateContext.UnmapSubresource(verticeBuffer, 0);,Buffer.Create已经完成资源初始化,无需手动Unmap。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:24:56