OpenCL 如何将浮点向量写入 image2d_t

OpenCL How to write vector of floats to image2d_t

对于我编写的 OpenCL 程序,我需要对灰度图像进行计算。这些灰度图像以 .pgm 格式存储,我将它们作为浮点向量读入我的程序。

据我了解 image2d_t 内存对象,每个像素都被编码为具有 4 个分量的 OpenCL 矢量数据类型,例如float4 在我的例子中,组件 1 到 3 在 3 个颜色通道 R、G、B 中编码颜色值,第四个组件编码亮度或 alpha。因此,我尝试炸毁现有图像以将其读入 image2d_t 内存对象,如下所示:

std::vector<float> A(256,0.0);
std::vector<float> A_img(1024, 0.0);

for(int i=0; i<256; i++)
{
    A_img[4*i] = A[i];
    A_img[(4*i)+3] = 1;
}

稍后在将此向量复制到 image2d_t 对象的过程中,我的设备的小字节序可能会导致组件 1 和组件 4 被切换。我不确定。

但是,在将此图像写入我的 GPU 内存、让内核读取它、将其写入输出图像并从 GPU 内存读回时,我无法取回图像。一个最小的工作示例(对于 OpenCL 来说总是有点长)如下:

#define CL_HPP_TARGET_OPENCL_VERSION 120
#define CL_HPP_MINIMUM_OPENCL_VERSION 120
#if defined(__APPLE__) 
#include <OpenCL/cl2.hpp>
#else 
#include <CL/cl2.hpp>
#endif
#include <iostream>
#include <string>
#include <vector>
#include <fstream>

int main(void)
{
    // Set up platform, device and context
    std::vector<cl::Platform> platforms;
    std::vector<cl::Device> devices;
    cl::Device default_device;
    cl::Platform::get(&platforms);
    
    if (platforms.size() == 0)
    {
        std::cout << "No OpenCL platform found, check installation!" << std::endl;
        exit(-1);
    }
    platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
    
    if (devices.size() == 0)
    {
        std::cout << "No devices found in platform, check installation!" << std::endl;
        exit(-1);
    }
    default_device = devices[0];
    cl::Context context(default_device);
    
    std::ifstream program_file("read_write_image.cl");
    std::string program_string(std::istreambuf_iterator<char>(program_file), (std::istreambuf_iterator<char>()));
    cl::Program::Sources source { program_string };
    cl::Program dummy_program(context, source);
    if (dummy_program.build()!=CL_SUCCESS)
    {
        std::cout << "Error building: " << dummy_program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(default_device)<< std::endl;
        exit(-1);
    }
    cl::Kernel kernel(dummy_program, "read_write_image");
    cl::CommandQueue queue(context, default_device);
    
    // Set up dummy grayscale image
    std::vector<float> A(256, 0.0);
    for(int i=0; i<256; i++)
    {
        A[i] = 255.0f - i;
        std::cout << A[i] << "  ";
    }
    std::cout << std::endl;
    
    // Blow up to float4 array
    std::vector<float> A_img(1024, 0.0);
    for(int i=0; i<256; i++)
    {
        A_img[4*i] = A[i];
        A_img[(4*i)+3] = 1;
    }
    std::vector<float> B_img(1024, 0.0);
    
    cl::ImageFormat grayscale(CL_R, CL_FLOAT);
    cl::Image2D Input_Image(context, CL_MEM_READ_ONLY, grayscale, 16, 16);
    cl::Image2D Output_Image(context, CL_MEM_WRITE_ONLY, grayscale, 16, 16);
    
    std::array<cl::size_type, 3> origin {0,0,0};
    std::array<cl::size_type, 3> region {16, 16, 1};

    queue.enqueueWriteImage(Input_Image, CL_TRUE, origin, region, 0, 0, &A_img[0]);

    kernel.setArg(0, Input_Image);
    kernel.setArg(1, Output_Image);
    
    queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(16,16), cl::NullRange, NULL); 
    queue.enqueueReadImage(Output_Image, CL_TRUE, origin, region, 0, 0, &B_img[0]);

    for(int i=0; i<1024; i++)
    {
        std::cout << B_img[i] << "  ";
    }   
    std::cout << std::endl;
    
    return EXIT_SUCCESS;
}

内核代码(放在文件“read_write_image.cl”中)如下:

__constant sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE|CLK_ADDRESS_CLAMP|CLK_FILTER_LINEAR;

__kernel void read_write_image(read_only image2d_t input_image, write_only image2d_t output_image)
{
    int i = get_global_id(0);
    int j = get_global_id(1);
    float tmp = read_imagef(input_image, sampler, (int2) (i,j)).x;
    write_imagef(output_image, (int2) (i,j), (float4) (tmp,0,0,1));
}

这个最小的工作示例生成一个 16x16 图像 A,它被解释为灰度图像,从左上角的白色像素开始,到右下角的黑色像素结束,以及回读图像B 在我的系统上是完全黑色的,即只有零。我在哪一点上出错了?

要事第一:

  • 在将数组读入图像的过程中,我的系统的小字节序不会反转坐标的顺序
  • 尽管 read_imagef 和 write_imagef 使用 float4s,但并非每个 image_2dt 都是 float4 数组。从代码中删除“放大”部分并仅使用常规缓冲区使该示例成为一个工作示例。但是,支持(对图像格式)似乎取决于平台,在我的 Macbook 上它无法使用这些更改,在我的 AMD 显卡上它可以工作

此处修改主机代码:

#define CL_HPP_TARGET_OPENCL_VERSION 120
#define CL_HPP_MINIMUM_OPENCL_VERSION 120
#if defined(__APPLE__) 
#include <OpenCL/cl2.hpp>
#else 
#include <CL/cl2.hpp>
#endif
#include <iostream>
#include <string>
#include <vector>
#include <fstream>

int main(void)
{
    // Set up platform, device and context
    std::vector<cl::Platform> platforms;
    std::vector<cl::Device> devices;
    cl::Device default_device;
    cl::Platform::get(&platforms);
    
    if (platforms.size() == 0)
    {
        std::cout << "No OpenCL platform found, check installation!" << std::endl;
        exit(-1);
    }
    platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
    
    if (devices.size() == 0)
    {
        std::cout << "No devices found in platform, check installation!" << std::endl;
        exit(-1);
    }
    default_device = devices[0];
    cl::Context context(default_device);
    
    std::ifstream program_file("read_write_image.cl");
    std::string program_string(std::istreambuf_iterator<char>(program_file), (std::istreambuf_iterator<char>()));
    cl::Program::Sources source { program_string };
    cl::Program dummy_program(context, source);
    if (dummy_program.build()!=CL_SUCCESS)
    {
        std::cout << "Error building: " << dummy_program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(default_device)<< std::endl;
        exit(-1);
    }
    cl::Kernel kernel(dummy_program, "read_write_image");
    cl::CommandQueue queue(context, default_device);
    
    // Set up dummy grayscale image
    std::vector<float> A(256, 0.0);
    for(int i=0; i<256; i++)
    {
        A[i] = 255.0f - i;
    }
    // Set up empty target image
    std::vector<float> B(256, 0.0);
    
    cl::ImageFormat grayscale(CL_R, CL_FLOAT);
    cl::Image2D Input_Image(context, CL_MEM_READ_ONLY, grayscale, 16, 16);
    cl::Image2D Output_Image(context, CL_MEM_WRITE_ONLY, grayscale, 16, 16);
    
    std::array<cl::size_type, 3> origin {0,0,0};
    std::array<cl::size_type, 3> region {16, 16, 1};

    queue.enqueueWriteImage(Input_Image, CL_TRUE, origin, region, 0, 0, &A[0]);

    kernel.setArg(0, Input_Image);
    kernel.setArg(1, Output_Image);
    
    queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(16,16), cl::NullRange, NULL); 
    queue.enqueueReadImage(Output_Image, CL_TRUE, origin, region, 0, 0, &B[0]);

    for(int i=0; i<256; i++)
    {
        std::cout << B[i] << "  ";
    }   
    std::cout << std::endl;
    
    return EXIT_SUCCESS;
}

内核没有变化。