在设备和主机之间来回复制全局设备上的指针地址

时间:2016-03-27 10:18:17

标签: opencl

我在OpenCL设备(GPU)上创建了一个缓冲区,从主机我需要知道全局设备上的指针地址,以便我可以将该设备上的地址放在另一个缓冲区中,这样内核就可以了从包含第一个缓冲区地址的缓冲区中读取,然后它可以访问该缓冲区的内容。

如果这让我感到困惑,那就是我想要做的事情:我创建一个包含代表2D图像的通用浮动缓冲区,然后从主机创建一个所有的todo列表我的内核需要绘制的东西,哪些行,哪些圈子,哪些图像......所以从该列表中内核必须知道在哪里找到该图像,但是对该图像的引用不能作为内核参数传递,因为该内核根据列表所说的内容,可能不会绘制任何图像或一千个不同的图像,因此必须在该缓冲区中引用它作为内核的待办事项列表。

到目前为止,我已经完成了这个尴尬的方式:

为此,我尝试在创建获取缓冲区的图像缓冲区后创建一个调用内核的函数,并在另一个缓冲区中将全局设备上的地址作为ulong返回,然后主机将该值存储在64中位整数,如下所示:

uint64_t get_clmem_device_address(clctx_t *clctx, cl_mem buf)
{
    const char kernel_source[] =
"kernel void get_global_ptr_address(global void *ptr, global ulong *devaddr)        \n"
"{                                          \n"
"   *devaddr = (ulong) ptr;                             \n"
"}                                          \n";

    int32_t i;
    cl_int ret;
    static int init=1;
    static cl_program program;
    static cl_kernel kernel;
    size_t global_work_size[1];
    static cl_mem ret_buffer;
    uint64_t devaddr;

    if (init)
    {
        init=0;
        ret = build_cl_program(clctx, &program, kernel_source);
        ret = create_cl_kernel(clctx, program, &kernel, "get_global_ptr_address");
        ret_buffer = clCreateBuffer(clctx->context, CL_MEM_WRITE_ONLY, 1*sizeof(uint64_t), NULL, &ret);
    }
    if (kernel==NULL)
        return ;

    // Run the kernel
    ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), &buf);
    ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), &ret_buffer);

    global_work_size[0] = 1;
    ret = clEnqueueNDRangeKernel(clctx->command_queue, kernel, 1, NULL, global_work_size, NULL, 0, NULL, NULL);         // enqueue the kernel
    ret = clEnqueueReadBuffer(clctx->command_queue, ret_buffer, CL_FALSE, 0, 1*sizeof(uint64_t), &devaddr, 0, NULL, NULL);      // copy the value
    ret = clFlush(clctx->command_queue);
    clFinish(clctx->command_queue);

    return devaddr;
}

显然这是有效的(它确实会返回一个数字,虽然它很难知道它是否正确)但是我把这个devaddr(主机上的64位整数) )在内核用来知道该做什么的todo列表缓冲区中,然后在必要时(根据列表)内核调用下面的函数,le这里是指向todo列表中相关条目的指针,64位地址是第一个元素:

float4 blit_sprite(global uint *le, float4 pv)
{
    const int2 p = (int2) (get_global_id(0), get_global_id(1));
    ulong devaddr;
    global float4 *im;
    int2 im_dim;

    devaddr = ((global ulong *) le)[0];     // global address for the start of the image as a ulong
    im_dim.x = le[2];
    im_dim.y = le[3];

    im = (global float4 *) devaddr;     // ulong is turned into a proper global pointer

    if (p.x < im_dim.x)
        if (p.y < im_dim.y)
            pv += im[p.y * im_dim.x + p.x];     // this gives me a CL_OUT_OF_RESOURCES error, even when changing it to im[0]

    return pv;
}

但是很大的意外,这不起作用,它给了我一个CL_OUT_OF_RESOURCES错误,我认为这意味着我的im指针不是有效的。实际上它有效,当我使用两种不同的上下文时,它没有工作。但它仍然非常笨拙。

有什么不那么奇怪的方式来做我想做的事情吗?

1 个答案:

答案 0 :(得分:1)

OpenCL标准并不保证内核对象不会在内核调用之间进行物理重新分配。因此,原始设备端地址仅在单个内核NDRange中有效。这就是为什么OpenCL内存对象在主机端表示为透明结构指针的原因之一。

但是,您可以将偏移量保存到内存对象的第一个内核中的第一个字节,并将其传递给第二个内核。每次启动内核时,您都将获得内核中的实际设备端地址。通过保存的移位值增加它。那将是完美的&#34;合法&#34;。