Как передать массив структур ctypes в pyOpenCL? - PullRequest
0 голосов
/ 30 марта 2020

Я следовал вместе с учебником онлайн, используя OpenCL, где я делаю все, используя python и pyOpenCL. В качестве упрощенного примера моей проблемы мне нужно передать массив структур C в качестве аргумента ядру OpenCL.

Вот искусственный пример кода OpenCL:

typedef struct Test{
    float a;
    float3 b;
} Test;

__kernel void render_kernel(__constant Test *tests, const int width, const int height, const int num_structs, __global float3* output)
{
    unsigned int work_item_id = get_global_id(0);
    unsigned int x_coord = work_item_id % width;
    unsigned int y_coord = work_item_id / width;

    Test test = tests[0];
    output[work_item_id] = test.b;
}

Это сделает глупость, которая просто выдаст один из float3 массивов в качестве выходных данных, но мне просто нужно знать, что я правильно передаю данные ядру.

Я пытаюсь mimi c массив этих структур на стороне python со следующим кодом:

class Test(ctypes.Structure):
    _fields_ = [
        ("a", ctypes.c_float),
        ("b", (ctypes.c_float * 4))
    ]

class Test_Array(ctypes.Structure):
    _fields_ = [("TEST_ARRAY", ctypes.POINTER(Test))]

    def __init__(self, num_structs):
        elems = (Test * num_structs)()
        self.TEST_ARRAY = ctypes.cast(elems, ctypes.POINTER(Test))
        self.elements = num_structs
        for num in range(0, num_structs):
            self.TEST_ARRAY[num].a = 1.0
            self.TEST_ARRAY[num].b = (1.0, 0.0, 0.0, 1.0)

num_structs = 2

test_arr = Test_Array(num_structs)

#host buffer
color_out = np.empty((win.width * win.height, 4), dtype=np.float32)

cl_prog = CL()
cl_prog.load_program("shaders/struct.cl")

#device buffers
cl_structs = cl_prog.create_input_buffer(num_structs * ctypes.sizeof(Test))
cl_output = cl_prog.create_output_buffer(color_out.nbytes)

cl.enqueue_fill_buffer(cl_prog.queue, cl_structs, test_arr.TEST_ARRAY,
 0, num_structs * ctypes.sizeof(Test))

global_work_size = (win.width * win.height,)

cl_prog.program.render_kernel(cl_prog.queue, global_work_size, None,
                              cl_structs, np.int32(win.width), np.int32(win.height), 
                              np.int32(num_structs), cl_output)

cl_prog.retrieve_data(color_out, cl_output)
print(color_out)

Это на самом деле не актуально, поскольку функции в этом классе являются просто обертками вокруг функций pyOpenCL, но здесь это класс CL, который создается.

class CL:
    def __init__(self):
        self.platform = cl.get_platforms()[0]
        self.device = self.platform.get_devices()[0]
        self.ctx = cl.Context([self.device])
        self.queue = cl.CommandQueue(self.ctx)

    def load_program(self, file_path):
        with open(file_path) as f:
            src = f.read()
            self.program = cl.Program(self.ctx, src).build()

    def create_output_buffer(self, size):
        """
        creates and returns a write only cl.Buffer of size bytes.
        """
        mf = cl.mem_flags
        return cl.Buffer(self.ctx, mf.WRITE_ONLY, size)

    def create_input_buffer(self, size):
        """
        returns a read only cl.Buffer of size bytes.
        """
        mf = cl.mem_flags
        return cl.Buffer(self.ctx, mf.READ_ONLY, size)

    def retrieve_data(self, host_buffer, device_buffer):
        """
        retrieves data from a buffer on the device, device_buffer, and copies it
        over to host_buffer
        """
        cl.enqueue_copy(self.queue, host_buffer, device_buffer)

    def fill_buffer(self, memory, pattern, offset, size, wait_for=None):
        """
        A wrapper around cl.enqueue_fill_buffer which uses self.queue
        """
        cl.enqueue_fill_buffer(self.queue, memory, pattern, offset, size, wait_for)

    def enqueue_copy(self, device_buffer, host_buffer):
        cl.enqueue_copy(self.queue, device_buffer, host_buffer)

Когда я запускаю приведенный выше код, он компилируется и работает нормально, но информация, которую я получаю из буфера, является просто мусором, который уже был в памяти. Я не могу сказать, связана ли моя проблема с выравниванием данных, как я создаю массив структур ctypes или как?

Я не привязан к использованию C массива C Структуры. Я подозреваю, что есть способ сделать это с numpy массивами, но я не могу понять это. Будем весьма благодарны за любой способ правильной передачи данных с хоста на устройство.

1 Ответ

0 голосов
/ 19 апреля 2020

Некоторые предполагают, что еще в 2014 году это можно было сделать следующим образом:

__kernel void render_kernel(struct Params Test, ...){

}

Вы можете увидеть эту запись.

В противном случае, что-то называется Атрибуты переменных может быть вариант?

Надеюсь, вы разобрались с этим и поделитесь опытом. Мне бы очень хотелось посмотреть, как это делается, поскольку я могу попробовать передать запрос SQL ядру для обработки.

...