Невозможно установить аргумент ядра OpenCL с объектом буферной памяти - PullRequest
1 голос
/ 17 апреля 2020

У меня есть следующее простое ядро ​​OpenCL, которое просто копирует все записи, указанные в a, в b

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

В следующем фрагменте кода показаны вызовы функций opencl для создания объекта буферной памяти из четырех чисел с плавающей запятой, и установка первого аргумента в ядре с буферным объектом.

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. похоже, что создание буфера не удалось, но вернулся без ошибки.

spe c также не очень точен, когда нужно более подробно описать ошибку:

для аргумента, объявленного как память объект, когда указанное arg_value не является допустимым объектом памяти.

note : функции и типы OpenCL были созданы с помощью rust-bindgen.

update 1

Чтобы прояснить, как непрозрачные типы представлены в ржавчине, вот представление cl_mem,

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

ffi для 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

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) для представления внешних непрозрачных типов . Таким образом, в основном cl_mem уже является указателем.

update 2

Согласно ответу pmdj правильный путь - передать указатель на 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. В spe c для clSetKernelArg также упоминается указатель на данные

Указатель на данные, которые следует использовать в качестве значения аргумента для аргумента, указанного в arg_index. Данные аргумента, на которые указывает arg_value, копируются, и поэтому указатель arg_value может повторно использоваться приложением после возврата clSetKernelArg. Указанное значение аргумента - это значение, используемое всеми вызовами API, которые ставят ядро ​​в очередь (clEnqueueNDRangeKernel), до тех пор, пока значение аргумента не будет изменено путем вызова clSetKernelArg для ядра [...]

1 Ответ

1 голос
/ 20 апреля 2020

Прежде чем углубиться в это, я укажу, что я относительный новичок в Rust, и я не особо знаком с тем, что производит bindgen, но я хорошо знаю OpenCL. Поэтому, пожалуйста, потерпите меня, если мой синтаксис Rust отключен.

Наиболее очевидная вещь, которая меня выделяет, это то, что передача буфера в clSetKernelArg с использованием buffer as *const c_void выглядит подозрительно. Насколько я понимаю, ваш код примерно эквивалентен этому 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);

Однако последняя строка неверна, она должна быть:

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

Хотя cl_mem определенный как указатель на непрозрачный тип структуры, вам нужно передать указатель на этот указатель в качестве аргумента, как и с любым другим типом аргумента ядра: концептуально, я считаю полезным думать о нем как clSetKernelArg выполняет memcpy(internal_buffer, arg_value, arg_size); внутри - поэтому arg_size всегда должен быть размером объекта, на который указывает arg_value. Я считаю, что это помогает мне определить правильный уровень косвенности.

Так что в Rust это, вероятно, примерно так:

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

, но я не пробежал мимо rustc так что это наверное неправильно. Вы получаете дрейф, хотя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...