【发布时间】:2020-04-19 10:37:41
【问题描述】:
我一直在关注使用 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 数组之一作为输出,但我只需要知道我实际上正在正确地将数据传递给内核。
我正在尝试使用以下代码在 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 数组来做到这一点,但我想不通。任何将数据从主机正确获取到设备的方法都将不胜感激。
【问题讨论】:
标签: python opencl ctypes pyopencl