OpenCL - Writing data to device

492 views Asked by At

I am having difficulty in sending data of type CL_HALF_FLOAT to an AMD HD 7990 GPU.

Currently I am reading a .exr file using OpenEXR and storing the data in a buffer named 'pixels'.

// http://www.openexr.com/ReadingAndWritingImageFiles.pdf

Imf::Array2D<Imf::Rgba> pixels; // Input image buffer

try{
    std::string fileName = resourcesDirectory + "Input/tunnel/00000.exr"; // Read in test file
    std::cout << "Reading " << fileName << std::endl;
    Imf::RgbaInputFile file(fileName.c_str()); // Constructor opens the file and reads the files header - dataWindow
    Imath::Box2i dataWindow = file.dataWindow(); // File's data window
    imageWidth = dataWindow.max.x - dataWindow.min.x + 1; // Width of image
    imageHeight = dataWindow.max.y - dataWindow.min.y + 1; // Height of image
    pixels.resizeErase(imageHeight, imageWidth); // Performs allocation
    // Tell the RgbaInputFile object how to access individual pixels in the buffer
    file.setFrameBuffer(&pixels[0][0] - dataWindow.min.x - dataWindow.min.y * imageWidth, 1, imageWidth);
    // Copy the pixel data from the file into the buffer
    file.readPixels(dataWindow.min.y, dataWindow.max.y);
    // How many channels does the image have?
    switch (file.channels()){
        case Imf::WRITE_RGBA:
            numChannels = 4;
            break;
        case Imf::WRITE_RGB:
            numChannels = 3;
            break;
        default:
            throw std::runtime_error("Unable to load EXR files that are not RGBA or RGB");
    }
    std::cout << "Image has " << numChannels << " channels\n";
}catch (Iex::BaseExc & e){
    std::cout << e.what() << std::endl;
}

I am confident that the image is being correctly read in because if I use a built in OpenEXR function to write the file using 'pixels' it produces the same output image.

Creating the buffer object 'inputImageBuffer' does not produce any errors.

// Set Persistent memory only for AMD platform
cl_mem_flags inMemFlags = CL_MEM_READ_ONLY;
if (args->isAmdPlatform()){
    inMemFlags |= CL_MEM_USE_PERSISTENT_MEM_AMD; // Faster transfer speed under windows 7
}

cl::Buffer inputImageBuffer;
// Create memory object for input image on the device
inputImageBuffer = cl::Buffer(
    context, // Context
    inMemFlags, // Flags
    imageWidth * imageHeight * numChannels * sizeof(CL_HALF_FLOAT), // Size
    NULL, // Host pointer 
    &status); // Status check
statusCheck(status, "Buffer::Buffer() failed. (inputImageBuffer)");

However when I try to send the data using the function enqueueWriteBuffer the program crashes and no helpful debug information is returned.

// Copy pixels to inputBufferImage
status = commandQueue.enqueueWriteBuffer(
    inputImageBuffer, 
    CL_TRUE, 
    0,
    imageWidth * imageHeight * numChannels * sizeof(CL_HALF_FLOAT),
    &pixels); 
statusCheck(status, "Copying failed");

I think the error might lay in how I am declaring the size of each buffer but I am unsure and any help would be much appreciated.

Thank you

2

There are 2 answers

1
sharpneli On BEST ANSWER

Your problem stems mostly from C++ and over eager abstraction from the library maker. Array2D is not a raw flat memory which the OpenCL enqueueWriteBuffer will need, and it does not provide any clear way of actually accessing the memory.

You are basically giving it a pointer to a class instance and it tries to read from that on instead of from the _data that actually contains the pixels. See http://www.sidefx.com/docs/hdk12.1/_imf_array_8h_source.html on how it's implemented.

You need to get access to the actual memory where the pixels are held. You can either fiddle with the template and change it or you can perhaps try pixels[0]. It could work if the implementation is as on that page I linked.

1
Jonas Bötel On

The OpenExr documentation is horrible in this regard but I'm pretty sure that the problem is &pixels:

status = commandQueue.enqueueWriteBuffer( 
             inputImageBuffer, 
             CL_TRUE, 
             0,
             imageWidth * imageHeight * numChannels * sizeof(CL_HALF_FLOAT),
             &pixels // <---- error here!
         );

As sharpneli already pointed out, &pixels is a pointer to an instance of a non-trivial class. There is a very strong chance, that this is not the location of the actual raw pixel data. In fact I don't know where the raw data is, but every example I found in there documentation uses the following pattern:

&pixels[0][0] - dataWindow.min.x - dataWindow.min.y * imageWidth

So I guess that is where the raw data is.