金属内核着色器——淡入淡出实现

Metal kernel shader -- fade implementation

我还没有写过很多 Metal 内核着色器;这是两个 RGBX-32 图像之间的初出茅庐的 "fade" 着色器,在 inBuffer1 (0.0) 到 inBuffer2 (1.0) 之间使用 0.0 到 1.0 的补间值。

我在这里遗漏了什么吗?让我吃惊的是,这可能非常低效。

我的第一个想法是尝试使用向量数据类型(例如 char4)进行减法和乘法运算,认为这可能会更好,但这样做的结果肯定是不确定的(因为某些组件将是否定)。

此外,使用 MTLTexture 与我所做的 MTLBuffer 对象相比是否有一些优势?

kernel void fade_Kernel(device const uchar4  *inBuffer1  [[ buffer(0) ]],
                        device const uchar4  *inBuffer2  [[ buffer(1) ]],
                        device const float   *tween      [[ buffer(2) ]],
                        device uchar4        *outBuffer  [[ buffer(3) ]],
                        uint gid [[ thread_position_in_grid ]])
{
    const float t = tween[0];
    uchar4 pixel1 = inBuffer1[gid];
    uchar4 pixel2 = inBuffer2[gid];

    // these values will be negative
    short r=(pixel2.r-pixel1.r)*t;  
    short g=(pixel2.g-pixel1.g)*t;
    short b=(pixel2.b-pixel1.b)*t;

    outBuffer[gid]=uchar4(pixel1.r+r,pixel1.g+g,pixel1.b+b,0xff);
}

首先,您可能应该将 tween 参数声明为:

constant float &tween [[ buffer(2) ]],

使用 constant 地址 space 更适合像这样的值,它对函数的所有调用都是相同的(而不是通过网格位置等索引)。此外,将其设为引用而不是指针会告诉编译器您不会索引指针可能是 "array" 中的其他元素。

最后,还有一个 mix() 函数可以执行您在此处执行的那种计算。因此,您可以将函数体替换为:

uchar4 pixel1 = inBuffer1[gid];
uchar4 pixel2 = inBuffer2[gid];

outBuffer[gid] = uchar4(uchar3(mix(float3(pixel1.rgb), float3(pixel2.rgb), tween)), 0xff);

至于使用纹理是否更好,这在某种程度上取决于您打算如何处理 运行 这个内核之后的结果。如果你打算用它做类似纹理的事情,最好始终使用纹理。实际上,使用带有混合的绘图操作可能比使用计算内核更好。毕竟,这种混合是 GPU 必须一直做的事情,所以这条路可能很快。您必须测试每种方法的性能。

如果你处理的是图像,使用MTLTexture比使用MTLBuffer效率更高。使用 "half" 也比 "uchar" 更好。我在今年的 WWDC 上直接从一位 Apple 工程师那里了解到这一点。

kernel void alpha(texture2d<half, access::read>  inTexture2  [[texture(0)]],
    texture2d<half, access::read>  inTexture1  [[texture(1)]],
    texture2d<half, access::write> outTexture [[texture(2)]],
    const device float& tween [[ buffer(3) ]],
    uint2 gid [[thread_position_in_grid]]) 
{
    // Check if the pixel is within the bounds of the output texture
    if((gid.x >= outTexture.get_width()) || (gid.y >= outTexture.get_height())) {
        // Return early if the pixel is out of bounds
        return;
    }
    half4 color1  = inTexture1.read(gid);
    half4 color2  = inTexture2.read(gid);
    outTexture.write(half4(mix(color1.rgb, color2.rgb, half(tween)), color1.a), gid);
}