Macos 如何用金属制作模板计算内核

Macos 如何用金属制作模板计算内核,macos,gpu,gpgpu,metal,Macos,Gpu,Gpgpu,Metal,我一直在写一些金属计算内核。因此,我用以下声明编写了一个内核: kernel void myKernel(const device uint32_t *inData [[buffer(MyKernelIn)]], device uint32_t *outData [[buffer(MyKernelOut)]], uint2 gid [[thread_position_in_grid]],

我一直在写一些金属计算内核。因此,我用以下声明编写了一个内核:

kernel void
myKernel(const device uint32_t *inData [[buffer(MyKernelIn)]],
        device uint32_t *outData [[buffer(MyKernelOut)]],
        uint2                          gid       [[thread_position_in_grid]],
        uint2 thread_position_in_threadgroup         [[thread_position_in_threadgroup]],
        uint2       threads_per_threadgroup      [[threads_per_threadgroup]],
        uint2 threadgroup_position_in_grid       [[threadgroup_position_in_grid]]) 
{ }
现在,我想写一个变量,它接受
uint8\t
类型的
inData
float
,我该怎么做呢

我可以想到的可能方法:

  • 用不同的名字复制我的内核。(不可扩展)
  • 传递一些标志,根据这些标志,我可以向内核添加开关盒,我可以随时使用这些标志,在
    inData
    outData
    中读取/写入任何内存位置。这意味着我创建的任何临时数据也将使用这种逻辑进行铸造。(这将再次在内核代码中引发大量间接操作,不确定它将如何影响我的性能)
  • 有没有更好的办法?我看到金属性能着色器在
    MTLTexture
    上工作,该着色器指定
    pixelFormat
    ,并且基于该
    pixelFormat
    ,MPS可以在大范围的数据类型上工作。对如何做到这一点有什么见解吗


    谢谢

    一种可行的方法是:

    • inData
      声明为
      void*
    • 在内核着色器的主体中,调用模板函数,传递参数。模板函数将按所需类型进行模板化,并将接收
      inData
      作为指向该类型的指针
    您可以使用输入参数动态选择要调用的模板函数的变体。但更好的方法可能是使用函数常量进行拾取。这样,选项就被编译成了

    比如说:

    constant int variant [[function_constant(0)]];
    
    template<typename T> void
    work(const device void *inData,
         device uint32_t *outData,
         uint2 gid,
         uint2 thread_position_in_threadgroup,
         uint2 threads_per_threadgroup,
         uint2 threadgroup_position_in_grid) 
    {
        const device T *data = static_cast<const device T*>(inData);
        // ...
    }
    
    kernel void
    myKernel(const device void *inData              [[buffer(MyKernelIn)]],
             device uint32_t *outData               [[buffer(MyKernelOut)]],
             uint2 gid                              [[thread_position_in_grid]],
             uint2 thread_position_in_threadgroup   [[thread_position_in_threadgroup]],
             uint2 threads_per_threadgroup          [[threads_per_threadgroup]],
             uint2 threadgroup_position_in_grid     [[threadgroup_position_in_grid]]) 
    {
        if (variant == 0)
            work<uint32_t>(inData, outData, gid, thread_position_in_threadgroup,
                           threads_per_threadgroup, threadgroup_position_in_grid);
        else if (variant == 1)
            work<uint8_t>(inData, outData, gid, thread_position_in_threadgroup,
                          threads_per_threadgroup, threadgroup_position_in_grid);
        else
            work<float>(inData, outData, gid, thread_position_in_threadgroup,
                        threads_per_threadgroup, threadgroup_position_in_grid);
    }
    
    常量int变量[[函数_常量(0)];
    模板空隙
    工作(const device void*inData,
    设备uint32_t*输出数据,
    uint2 gid,
    uint2螺纹在螺纹组中的位置,
    每个螺纹组的uint2螺纹,
    uint2螺纹组(网格中的位置)
    {
    常量设备T*数据=静态广播(inData);
    // ...
    }
    内核空洞
    myKernel(const device void*inData[[buffer(MyKernelIn)]],
    设备uint32_t*outData[[buffer(MyKernelOut)],
    uint2 gid[[螺纹位置在网格中],
    uint2螺纹组中的螺纹位置[[螺纹组中的螺纹位置],
    uint2线程每线程组[[线程每线程组]],
    uint2线程组在网格中的位置[[线程组在网格中的位置])
    {
    如果(变量==0)
    工作(inData、outData、gid、螺纹组中的螺纹位置、,
    每个线程组的线程数,线程组在网格中的位置);
    else if(变量==1)
    工作(inData、outData、gid、螺纹组中的螺纹位置、,
    每个线程组的线程数,线程组在网格中的位置);
    其他的
    工作(inData、outData、gid、螺纹组中的螺纹位置、,
    每个线程组的线程数,线程组在网格中的位置);
    }
    
    谢谢!只是一个简单的问题,因为
    function\u常量将在
    MTLFunction
    上设置,这是否意味着,我必须为
    变量的每个新值创建一个新的
    MTLComputePipelineState
    ?是的。如果您不想这样做,您可以将
    variant
    作为制服传入。这可能会影响性能。我不知道,但我怀疑这会很轻微。