金属内核着色器——淡入淡出实现
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);
}
我还没有写过很多 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);
}