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

Unity中向Compute Shader收发double数组遇blittable类型错误求助

Unity Compute Shader Double数组传输错误解决方案

问题详情

收到错误提示:

ArgumentException: Array passed to ComputeBuffer.SetData(array) must be blittable.
Data.i is not blittable because it is not of value type (System.Double[])
Data.w is not blittable because it is not of value type (System.Double[])

调用栈信息:

UnityEngine.ComputeBuffer.SetData (System.Array data) (at <3be1a7ff939c43f181c0a10b5a0189ac>:0)
Script1.Compute () (at Assets/Script1.cs:46)
Script1.Start () (at Assets/Script1.cs:37)

用户尝试传输double[]数组到Compute Shader,相关代码如下:

C# 原代码

public class Script1 : MonoBehaviour
{
    public double[] inputs;
    public double[] weights = { 3.1, 2.1, 8.7 };
    float bias = 3;
    float output = 0;

    public ComputeShader compute1;
    ComputeBuffer buffer;
    public struct Data
    {
        public double[] i;
        public double[] w;
        public float b;
        public float o;
    }
    Data[] d;
    
    // Start is called before the first frame update
    void Start()
    {
        d = new Data[1];
        d[0] = new Data
        {
            i = new double[] { 1.2, 5.1, 2.1 },
            w = weights,
            b = bias,
            o = output
        };
        buffer = new ComputeBuffer(d.Length, 56);

        Compute();
    }


    void Compute()
    {
        float[] result = new float[2];
        int kernelHandle = compute1.FindKernel("CSMain");
        compute1.SetBuffer(kernelHandle, "buffer", buffer);
        buffer.SetData(d);
        buffer.SetData(result);
        compute1.Dispatch(kernelHandle, 24, 1, 1);
        buffer.GetData(result);
    }
    private void OnDestroy()
    {
        buffer.Dispose();
    }
}

Compute Shader 原代码

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain

// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> Result;
struct d 
{
    double i[3], w[3];
    float b, o;
};
RWStructuredBuffer<d> buffer;


[numthreads(24,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // TODO: insert actual code here!

    float result[] = { buffer[0].i[0] * buffer[0].w[0] + buffer[0].i[1] * buffer[0].w[1] + buffer[0].i[2] * buffer[0].w[2] + buffer[0].b, 0 };

}

用户尝试过(System.Double)强制转换但无效,询问正确的Double数组收发方法。


核心问题原因

C#中的double[]是引用类型,而ComputeBuffer要求传输的结构体必须是blittable(可 blittable 值类型)——引用类型无法直接映射到GPU内存的连续结构,因此报错。而Compute Shader中定义的是固定长度的double[3]值类型数组,与C#端的引用类型数组内存布局不匹配。

修正方案

1. 修改C#端结构体为固定长度值类型数组

将C#的Data结构体中的double[]改为固定长度的fixed double数组,同时添加[StructLayout(LayoutKind.Sequential)]标记确保内存布局与Compute Shader一致:

using System.Runtime.InteropServices;

public class Script1 : MonoBehaviour
{
    public double[] inputs;
    public double[] weights = { 3.1, 2.1, 8.7 };
    float bias = 3;
    float output = 0;

    public ComputeShader compute1;
    ComputeBuffer dataBuffer;

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct Data
    {
        public fixed double i[3]; // 固定长度值类型数组
        public fixed double w[3];
        public float b;
        public float o;
    }
    Data d;
    
    void Start()
    {
        // 初始化固定数组
        unsafe
        {
            var inputVals = new double[] { 1.2, 5.1, 2.1 };
            for (int i = 0; i < 3; i++)
            {
                d.i[i] = inputVals[i];
                d.w[i] = weights[i];
            }
        }
        d.b = bias;
        d.o = output;

        // 计算结构体大小:3*8 + 3*8 + 4 +4 = 56
        dataBuffer = new ComputeBuffer(1, sizeof(Data));

        Compute();
    }


    void Compute()
    {
        // 单独创建结果缓冲区,避免内存冲突
        ComputeBuffer resultBuffer = new ComputeBuffer(1, sizeof(float)*2);
        float[] result = new float[2];
        
        int kernelHandle = compute1.FindKernel("CSMain");
        compute1.SetBuffer(kernelHandle, "dataBuffer", dataBuffer);
        compute1.SetBuffer(kernelHandle, "resultBuffer", resultBuffer);
        
        dataBuffer.SetData(new Data[]{d});
        resultBuffer.SetData(result);
        
        compute1.Dispatch(kernelHandle, 1, 1, 1); // 仅需1个线程处理单组数据
        
        resultBuffer.GetData(result);
        Debug.Log($"计算结果:{result[0]}");
        
        resultBuffer.Dispose();
    }
    private void OnDestroy()
    {
        dataBuffer.Dispose();
    }
}

2. 修正Compute Shader代码

新增结果缓冲区,将计算结果写入缓冲区,确保结果能正确回传:

#pragma kernel CSMain

struct d 
{
    double i[3], w[3];
    float b, o;
};
RWStructuredBuffer<d> dataBuffer;
RWStructuredBuffer<float2> resultBuffer; // 新增结果缓冲区

[numthreads(1,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    double sum = dataBuffer[0].i[0] * dataBuffer[0].w[0] + 
                 dataBuffer[0].i[1] * dataBuffer[0].w[1] + 
                 dataBuffer[0].i[2] * dataBuffer[0].w[2];
    resultBuffer[0] = float2(float(sum) + dataBuffer[0].b, 0);
}

3. 关键注意事项

  • blittable类型要求:GPU仅识别连续内存布局的数值类型,C#引用类型(如数组、类)无法直接传输,必须使用固定长度值类型数组或连续结构体。
  • 内存布局一致性:C#结构体必须添加[StructLayout(LayoutKind.Sequential)],确保与Compute Shader的结构体内存顺序完全匹配。
  • 缓冲区分离:避免同一缓冲区既存数据又存结果,防止内存冲突,建议分开创建数据缓冲区和结果缓冲区。
  • 线程调度合理性:根据实际处理的数据量调度线程,无需过度分配线程资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 15:35:35