图像如何在 opencl 内核中工作?

How do images work in opencl kernel?

我正在尝试寻找在 opencl 中将多维数组从主机复制到设备的方法,并认为一种方法是使用图像...可以是 1、2 或 3 维对象。但是我很困惑,因为从数组中读取像素时,它们使用的是矢量数据类型。通常我会想到双指针,但它听起来不像是向量数据类型的意思。无论如何,这是我的问题:

1) 矢量数据类型的实际含义是什么,为什么我们在表示像素坐标时不指定 2 或 3 个索引?看起来像 float2 这样的单个值被用来表示坐标,但这对我来说毫无意义。我正在查看函数 read_imageui 和 read_image.

2)输入图像可以只是整个图像的一个子集,采样器可以是输入图像的子集吗?我不明白这里实际上是如何指定坐标的,因为 read_image() 只能接缝为输入取一个值,为采样器取一个值。

3) 如果做线性代数,我是否应该硬着头皮将缓冲区中的一维数组数据转换为 opencl 中的多维度数组?

4)我对图像还是很感兴趣的,所以即使我想做的不是图像最好的,你还能解释一下问题1和2吗?

谢谢!

编辑 我想细化我的问题并询问,在他们定义的以下 khronos 文档中...

 int4 read_imagei (
    image2d_t image,
    sampler_t sampler,
    int2 coord)

但是我在任何地方都找不到 image2d_t 的定义或结构应该是什么。 sampler_t 和 int2 坐标相同。它们对我来说似乎是结构或指向结构的指针,因为 opencl 应该基于 ansi c,但是这些结构的字段是什么,或者我如何用看起来像 scala 的东西来记录坐标?!我见过符号 (int2)(x,y),但那不是 ansi c,它看起来像 scala,哈哈。事情对我来说似乎是矛盾的。再次感谢!

一般来说,您可以通过三种不同的方式阅读图像:

  1. 直接像素访问,无采样

  2. 采样,归一化坐标

  3. 采样,整数坐标

第一个是你想要的,也就是说,你传递整数像素坐标,如 (10, 43),它将 return 图像在该点的内容,没有任何过滤,如如果它是内存缓冲区。您可以使用不带 sampler_t 参数的 read_image*() 系列函数。

第二个是大多数人想要的图像,您指定 0 到 1 之间的标准化图像坐标,return 值是指定点的插值图像颜色(因此,如果您的坐标指定像素之间的点,颜色根据周围像素颜色进行插值)。插值和越界坐标的处理方式由传递给函数的 sampler_t 参数的配置定义。

第三个与第二个相同,只是纹理坐标未归一化,采样器需要相应配置。在某种意义上,第三种方式更接近第一种,它提供的唯一附加功能是能够处理越界像素坐标(例如,通过环绕或夹紧它们),而不是您手动处理。

最后,每个功能的不同版本,例如根据图像的像素格式使用 read_imagefread_imageiread_imageui。如果它包含浮点数(在每个通道中),使用 read_imagef,如果它包含有符号整数(在每个通道中),使用 read_imagei,等等...

另一方面,写入图像很简单,有 write_image{f,i,ui}() 函数可以获取图像对象、整数像素坐标和像素颜色,都非常简单。

请注意,您无法在同一内核中读取和写入同一图像! (我不知道最近的 OpenCL 版本是否改变了这一点)。一般来说,如果您不打算将图像用作实际图像(即您采样的输入纹理或您在内核末尾仅写入一次的输出纹理),我建议使用缓冲区。


关于 image2d_tsampler_t 类型,它们是 OpenCL "pseudo-objects",您可以将它们从 C 传递到内核(它们是保留类型)。你从 C 端将你的图像或你的采样器发送到 clSetKernelArg,内核在内核的参数列表中返回一个 sampler_t 或一个 image2d_t (就像你传入一个缓冲区对象并且它得到一个指针)。对象本身无法在内核中进行有意义的操作,它们只是您可以发送到 read_image/write_image 函数以及其他一些函数的句柄。


至于 "actual" 图像和缓冲区之间的低级差异,GPU 通常具有针对 "read often, write once" 访问模式高度优化的专门保留的纹理内存,具有特殊的纹理采样硬件和纹理缓存优化分散读取、mipmap 等。

在 CPU 上,图像和缓冲区之间可能没有根本区别,并且您的运行时可能在执行图像语义时将两者都实现为内存数组。