无法使用缓冲存储器对象设置OpenCL内核参数 [英] Cannot set OpenCL kernel argument with buffer memory object

查看:105
本文介绍了无法使用缓冲存储器对象设置OpenCL内核参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下简单的OpenCL内核,该内核简单地将指向a的所有条目复制到b

I have the following simple OpenCL kernel, that simply copies all entries pointed at a to b

__kernel void mmcopy(__global float* a, __global float* b) {
    unsigned pos = get_global_id(0);
    b[pos] = a[pos];
}

以下代码片段显示了opencl函数调用,这些调用用于从四个浮点中创建一个缓冲存储器对象,并使用该缓冲对象在内核上设置第一个参数.

The following code snippet shows the opencl function calls for creating a buffer memory object out of four floats, and setting the first argument on the kernel with the buffer object.

let mut v = [1f32, 1f32, 1f32, 1f32];

let size = mem::size_of_val(&v) as size_t;
let mut error_buffer = 0 as i32;
let buffer = unsafe {
    clCreateBuffer(
        context.id.unwrap(),
        (CL_MEM_COPY_HOST_PTR | CL_MEM_READ_WRITE) as u64,
        size,
        v.as_mut_ptr() as *mut c_void,
        &mut error_buffer,
    )
};

let real_size = mem::size_of::<cl_mem>() as size_t;

let error = unsafe {
    clSetKernelArg(
        self.id.unwrap(), // here `self` is a wrapper. `id` is of type `cl_kernel`
        0 as cl_uint,
        real_size,
        buffer as *const c_void,
    )
};

但是,执行代码会导致错误CL_INVALID_MEM_OBJECT. 看起来创建缓冲区没有成功,但是返回时没有错误.

However, executing the code results in an error CL_INVALID_MEM_OBJECT. it looks like creating the buffer didn't succeed, but returned without an error.

规范在更详细地描述错误时也不是很精确:

The spec is also not very precise when it comes to describe the error in more detail:

用于当指定的arg_value不是有效的内存对象时声明为内存对象的参数.

for an argument declared to be a memory object when the specified arg_value is not a valid memory object.

注释:OpenCL函数和类型已由rust-bindgen生成.

note: the OpenCL functions, and types have been generated by rust-bindgen.

为阐明不透明类型如何在锈中表示,下面是cl_mem的表示形式,

To clarify how the opaque types are represented in rust, here is the representation of cl_mem,

pub struct _cl_mem {
    _unused: [u8; 0],
}
pub type cl_mem = *mut _cl_mem;

ffi到clSetKernelArg

the ffi to clSetKernelArg

extern "C" {
    pub fn clSetKernelArg(
        kernel: cl_kernel, 
        arg_index: cl_uint,
        arg_size: size_t,
        arg_value: *const ::std::os::raw::c_void,
    ) -> cl_int;
}

和clCreateBuffer

and clCreateBuffer

extern "C" {
    pub fn clCreateBuffer(
        context: cl_context,
        flags: cl_mem_flags,
        size: size_t,
        host_ptr: *mut ::std::os::raw::c_void,
        errcode_ret: *mut cl_int,
    ) -> cl_mem;
}

据我了解,rust(-bindgen)使用零尺寸类型(ZST)表示

In my understanding rust(-bindgen) uses zero sized types (ZST) to represent external opaque types. So basically cl_mem is already a pointer.

根据 pmdj 的答案,正确的方法是将指针传递给cl_mem缓冲区

According to pmdj's answer the correct way is to pass a pointer to the cl_mem buffer

let error = unsafe {
    clSetKernelArg(
        self.id.unwrap(), // here `self` is a wrapper. `id` is of type `cl_kernel`
        0 as cl_uint,
        real_size,
        &buffer as *const _ as *const c_void,
    )
};

这实际上解决了问题,并将返回值设置为CL_SUCCESS. clSetKernelArg 的规范提到数据的指针

That actually fixes the problem, and set the return value to CL_SUCCESS. The spec for clSetKernelArg also mentions a pointer to data

指向数据的指针,该数据应用作arg_index指定的参数的参数值.复制了由arg_value指向的参数数据,因此在clSetKernelArg返回之后,应用程序可以重新使用arg_value指针.指定的参数值是所有使内核排队的API调用(clEnqueueNDRangeKernel)所使用的值,直到对内核[...]

A pointer to data that should be used as the argument value for argument specified by arg_index. The argument data pointed to by arg_value is copied and the arg_value pointer can therefore be reused by the application after clSetKernelArg returns. The argument value specified is the value used by all API calls that enqueue kernel (clEnqueueNDRangeKernel) until the argument value is changed by a call to clSetKernelArg for kernel [...]

推荐答案

在深入研究之前,我会指出我是Rust的相对初学者,并且我对bindgen的产生并不特别熟悉,但是我非常了解OpenCL.因此,如果我的Rust语法已关闭,请多多包涵.

Before I dig in, I'll point out that I'm a relative beginner with Rust and I'm not particularly familiar with what bindgen produces, but I know OpenCL quite well. So please bear with me if my Rust syntax is off.

对我来说最显而易见的事情是,使用buffer as *const c_void将缓冲区传递给clSetKernelArg看起来很可疑.我的理解是您的代码大致等于此C:

The most obvious thing that sticks out for me is that passing the buffer to clSetKernelArg using buffer as *const c_void looks suspicious. My understanding is that your code is roughly equivalent to this C:

cl_int error_buffer = 0;
cl_mem buffer = clCreateBuffer(
        context.id,
        (CL_MEM_COPY_HOST_PTR | CL_MEM_READ_WRITE),
        size,
        v,
        &error_buffer
    );

size_t real_size = siezof(buffer);
cl_int error = clSetKernelArg(self.id, 0, real_size, buffer);

但是,最后一行不正确,应该是:

However, the last line is incorrect, it should be:

cl_int error = clSetKernelArg(self.id, 0, real_size, &buffer);
// yes, we want a POINTER to the buffer handle-------^

尽管cl_mem被定义为指向不透明结构类型的指针,但您需要将指针传递给该指针作为参数,就像其他任何类型的内核参数一样:从概念上讲,我发现将其视为内部执行memcpy(internal_buffer, arg_value, arg_size);clSetKernelArg很有用-因此arg_size必须始终是arg_value指向的对象的大小.我发现这有助于我确定正确的间接级别.

Although cl_mem is defined as a pointer to an opaque struct type, you need to pass the pointer to that pointer as the argument, just as with any other type of kernel argument: conceptually, I find it useful to think of it as clSetKernelArg performing a memcpy(internal_buffer, arg_value, arg_size); internally - so arg_size must always be the size of the object pointed to by arg_value. I find this helps me work out the correct level of indirection.

因此在Rust中,这可能与以下内容类似:

So in Rust this is probably along the lines of:

let error = unsafe {
    clSetKernelArg(
        self.id.unwrap(),
        0 as cl_uint,
        real_size,
        &buffer as *const c_void,
    )
};

但是我还没有运行过rustc,所以可能是错误的.但是,您会发现漂移.

but I haven't run it past rustc so it's probably wrong. You get the drift though.

这篇关于无法使用缓冲存储器对象设置OpenCL内核参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆