• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

ios - 使用 GPU 进行像素格式转换?

[复制链接]
菜鸟教程小白 发表于 2022-12-13 14:11:26 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题

我正在尝试非常有效地将压缩的 24bpp RGB 图像转换为压缩的 32bpp RGBA。我尝试使用 Accelerate.framework 中的 vImageConvert_RGB888toRGBA8888,但想知道在 Metal 中使用计算内核是否有更快的方法。我在 Metal 中尝试了几种不同的方法,但结果总是比使用 Accelerate.framework 慢得多,即使对于具有 >1M 像素的大图像也是如此。

这是我的计算内核的样子:

kernel void rgb24_to_rgba32(texture2d<half, access::read> inTexture [[texture(0)]],
                     texture2d<half, access::write> outTexture [[texture(1)]],
                     uint2 id [[ thread_position_in_grid ]])
{   
    uint2 srcAddr1 = uint2(id.x * 3, id.y);
    uint2 srcAddr2 = uint2(id.x * 3 + 1, id.y);
    uint2 srcAddr3 = uint2(id.x * 3 + 2, id.y);

    outTexture.write(half4(inTexture.read(srcAddr1).r, inTexture.read(srcAddr2).r, inTexture.read(srcAddr3).r, 1), id);

    return;
}

我将 inTexture 定义为 r8Unorm,将 outTexture 定义为 bgra8Unorm。两种纹理都是使用 .storageModeShared 加载的,因此不应该发生任何内存复制。

代码工作正常,转换正确执行,但性能不高。我尝试了不同的 threadgroupsPerGridthreadsPerThreadgroup 设置,但没有一个能达到与 Accelerate.framework 相当的性能。

例如,在 A7(第一代 iPad Air)上,一张 1024x1024 的图像大约需要 32 毫秒,而使用 Accelerate.framework 则需要 6 毫秒。有趣的是,对于更快的设备,例如基于 A9 的 iPhone 6s,差异要小得多(在 GPU 上为 1.5 毫秒,而使用 Accelerate 为 1.1 毫秒),但 Metal 实现总是更慢。

这只是对 GPU 不友好的操作吗(可能是由于无数未对齐的内存访问?)在最大化我的计算内核的性能方面,我可能遗漏了一些基本的东西吗?

更新:使用以下实现,我最终能够获得比上述更好的性能:

这种方法利用 packed_uint3 的 96 位读取和使用 packed_uint4 的 128 位写入来显着提高性能。

#define RGB24_TO_RGBA32_PIXEL1(myUint) (myUint | 0xff000000)

#define RGB24_TO_RGBA32_PIXEL2(myUint1, myUint2) (myUint1 >> 24 | \
                                                ((myUint2) << 8) | 0xff000000)


#define RGB24_TO_RGBA32_PIXEL3(myUint2, myUint3) (myUint2 >> 16 | \
                                                ((myUint3) << 16) | 0xff000000)

#define RGB24_TO_RGBA32_PIXEL4(myUint3) ((myUint3 >> 8) | 0xff000000)

inline packed_uint4 packed_rgb24_to_packed_rgba32(packed_uint3 src) {
    return uint4(RGB24_TO_RGBA32_PIXEL1(src[0]),
                 RGB24_TO_RGBA32_PIXEL2(src[0], src[1]),
                 RGB24_TO_RGBA32_PIXEL3(src[1], src[2]),
                 RGB24_TO_RGBA32_PIXEL4(src[2]));
}

kernel void rgb24_to_rgba32_textures(
                         constant packed_uint3 *src [[ buffer(0) ]],
                         device packed_uint4 *dest [[ buffer(1) ]],
                         uint2 id [[ thread_position_in_grid ]])
{
    // Process 8 pixels per thread (two packed_uint3s, each containing 4 pixels):
    uint index = id.x  * 2;
    dest[index] = packed_rgb24_to_packed_rgba32(src[index]);
    dest[index + 1] = packed_rgb24_to_packed_rgba32(src[index + 1]);
    return;
}

使用这种方法,旧设备上的性能差异会变得更小(Accelerate 大约比 GPU 快 2 倍),而在更现代 (A9) 设备上,Metal 实际上最终会快 40-50%

我尝试过为每个线程处理一个、两个或多个 packed_uint3 向量,结论是两个向量是性能的最佳点。



Best Answer-推荐答案


为了结束,这里是 Apple 的开发者关系对这个问题的回应。 最重要的是,GPU 在这种情况下并没有提供任何真正的优势,因为这种转换不是计算量很大的操作。

After discussions with engineering, and evaluating more sample implementations, the verdict is out on Metal v.s. Accelerate performance for converting packed 24bpp RGB images to packed 32bpp RGBA images: on newer devices you can get close to the same performance using Metal but Accelerate will be faster for this operation. “vImage is an extremely well-tuned implementation and since this conversion operation is not compute heavy the best we can do is to be at parity.”

The proposed reasoning behind this is data locality and efficiently operating on multiple pixels at a time (something you’ve mentioned). The fastest Metal implementation tested processed two pixels per thread and still lagged behind vImageConvert_RGB888toRGBA8888.

There was an “optimized” implementation using Metal buffers rather than textures (something else that you’d mentioned exploring) and surprisingly this approach was slightly less performant.

Lastly, adjustment of thread groups came into discussion as well as tuning by adding code to the kernel to handle the case where the thread position in grid is outside the destination image. Again, despite these considerations Accelerate remained as the fastest implementation.

我应该补充一点,使用 Metal 的一个真正优势是 CPU 使用率,虽然它没有更快,但它确实显着减少了 CPU 的工作量。对于 CPU 负载很重的应用程序,Metal 方法实际上可能有意义。

关于ios - 使用 GPU 进行像素格式转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39781719/

回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注0

粉丝2

帖子830918

发布主题
阅读排行 更多
广告位

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap