zoe vas
zoe vas

Reputation: 391

Base Address of Memory Object OpenCL

I want to traverse a tree at GPU with OpenCL, so i assemble the tree in a contiguous block at host and i change the addresses of all pointers so as to be consistent at device as follows:

TreeAddressDevice = (size_t)BaseAddressDevice + ((size_t)TreeAddressHost - (size_t)BaseAddressHost);

I want the base address of the memory buffer: At host i allocate memory for the buffer, as follows: cl_mem tree_d = clCreateBuffer(...);

The problem is that cl_mems are objects that track an internal representation of the data. Technically they're pointers to an object, but they are not pointers to the data. The only way to access a cl_mem from within a kernel is to pass it in as an argument via setKernelArgs.

Here http://www.proxya.net/browse.php?u=%3A%2F%2Fwww.khronos.org%2Fmessage_boards%2Fviewtopic.php%3Ff%3D37%26amp%3Bt%3D2900&b=28 i found the following solution, but it doesnot work:

__kernel void getPtr( __global void *ptr, __global void *out )

    {
    *out = ptr;
    }

that can be invoked as follows

Code:

...

    cl_mem auxBuf = clCreateBuffer( context, CL_MEM_READ_WRITE, sizeof(void*), NULL, NULL );
    void *gpuPtr;

    clSetKernelArg( getterKernel, 0, sizeof(cl_mem), &myBuf );
    clSetKernelArg( getterKernel, 1, sizeof(cl_mem), &auxBuf );
    clEnqueueTask( commandQueue, getterKernel, 0, NULL, NULL );
    clEnqueueReadBuffer( commandQueue, auxBuf, CL_TRUE, 0, sizeof(void*), &gpuPtr, 0, NULL, NULL );

    clReleaseMemObject(auxBuf);

...

Now "gpuPtr" should contain the address of the beginning of "myBuf" in GPU memory space.

The solution is obvious and i can't find it? How can I get back a pointer to device memory when creating buffers?

Upvotes: 0

Views: 2615

Answers (2)

Gorethox
Gorethox

Reputation: 19

As Eric pointed out, there are two sets of memory to consider: host memory and device memory. Basically, OpenCL tries to hide the gritty details of this interaction by introducing the buffer object for us to interact with in our program on the host side. Now, as you noted, the problem with this methodology is that it hides away the details of our device when we want to do something trickier than the OpenCL developers intended or allowed in their scope. The solution here is to remember that OpenCL kernels use C99 and that the language allows us to access pointers without any issue. With this in mind, we can just demand the pointer be stored in an unsigned integer variable to be referenced later.

Your implementation was on the right track, but it needed a little bit more C syntax to finish up the transfer.

OpenCL Kernel:

// Kernel used to obtain pointer from target buffer
__kernel void mem_ptr(__global char * buffer, __global ulong * ptr)
{
    ptr[0] = &buffer[0];
}

// Kernel to demonstrate how to use that pointer again after we extract it.
__kernel void use_ptr(__global ulong * ptr)
{
    char * print_me = (char *)ptr[0];
    /* Code that uses all of our hard work */
    /* ... */
}

Host Program:

// Create the buffer that we want the device pointer from (target_buffer) 
//  and a place to store it (ptr_buffer).
cl_mem target_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE, 
                                      MEM_SIZE * sizeof(char), NULL, &ret);
cl_mem ptr_buffer    = clCreateBuffer(context, CL_MEM_READ_WRITE,
                                      1 * sizeof(cl_ulong), NULL, &ret);

/* Setup the rest of our OpenCL program */    
/* .... */

// Setup our kernel arguments from the host...
ret = clSetKernelArg(kernel_mem_ptr, 0, sizeof(cl_mem), (void *)&target_buffer);
ret = clSetKernelArg(kernel_mem_ptr, 1, sizeof(cl_mem), (void *)&ptr_buffer);
ret = clEnqueueTask(command_queue, kernel_mem_ptr, 0, NULL, NULL);

// Now it's just a matter of storing the pointer where we want to use it for later.
ret = clEnqueueCopyBuffer(command_queue, ptr_buffer, dst_buffer, 0, 1 * sizeof(cl_ulong),
                          sizeof(cl_ulong), 0, NULL, NULL);
ret = clEnqueueReadBuffer(command_queue, ptr_buffer, CL_TRUE, 0,
                          1 * sizeof(cl_ulong), buffer_ptrs, 0, NULL, NULL);  

There you have it. Now, keep in mind that you don't have to use the char variables I used; it works for any type. However, I'd recommend using cl_ulong for the storing of pointers. This shouldn't matter for devices with less than 4GB of accessible memory. But for devices with a larger address space, you have to use cl_ulong. If you absolutely NEED to save space on your device but have a device whose memory > 4GB, then you might be able to create a struct that can store the lower 32 LSB of the address into a uint type, with the MSB's being stored in a small type.

Upvotes: 1

Eric Bainville
Eric Bainville

Reputation: 9886

It's because in the OpenCL model, host memory and device memory are disjoint. A pointer in device memory will have no meaning on the host.

You can map a device buffer to host memory using clEnqueueMapBuffer. The mapping will synchronize device to host, and unmapping will synchronize back host to device.

Update. As you explain in the comments, you want to send a tree structure to the GPU. One solution would be to store all tree nodes inside an array, replacing pointers to nodes with indices in the array.

Upvotes: 1

Related Questions