在主机端程序中,可以使用memcpy来实现在两个内存地址中进行数据拷贝。在OpenCL中我们也有类似类型的函数用来实现在同一设备内不同存储器对象间数据拷贝、不同设备间不同存储器对象间数据拷贝。
缓冲区对象间数据传输
缓冲区对象可以使用如下函数来实现缓冲区对象间数据传输:
cl_int clEnqueueCopyBuffer (cl_command_queue command_queue,
cl_mem src_buffer,
cl_mem dst_buffer,
size_t src_offset,
size_t dst_offset,
size_t size,
cl_uint num_events_in_wait_list,
const cl_event *event_wait_list,
cl_event *event)
cl_int clEnqueueCopyBufferRect (cl_command_queue command_queue,
cl_mem src_buffer,
cl_mem dst_buffer,
const size_t *src_origin,
const size_t *dst_origin,
const size_t *region,
size_t src_row_pitch,
size_t src_slice_pitch,
size_t dst_row_pitch,
size_t dst_slice_pitch,
cl_uint num_events_in_wait_list,
const cl_event *event_wait_list,
cl_event *event)
- 参数command_queue为拷贝命令所要入队的命令队列。command_queue、src_buffer和dst_buffer必须位于同一OpenCL上下文中。
-
参数src_buffer为源缓冲区对象。
-
参数dst_buffer为目的缓冲区对象。
-
参数src_offset为源缓冲区对象的偏移量,确定在src_buffer什么位置开始读取数据。
-
参数dst_offset为目的缓冲区对象的偏移量,确定在dst_buffer什么位置开始写入数据。
-
参数size为所要拷贝的数据的字节数。
-
参数src_origin定义了src_buffer中内存区域的偏移量(x,y,z)。对于2维矩形区域,z的值src_origin[2]为0。计算偏移量:src_origin[2]*src_slice_pitch+src_origin[1]*src_row_pitch+src_origin[0]。
[插图]参数dst_origin定义了dst_buffer中内存区域的偏移量(x,y,z)。对于2维矩形区域,z的值dst_origin[2]为0。计算偏移量:dst_origin[2]*dst_slice_pitch+dst_origin[1]* dst_row_pitch+dst_origin[0]。 -
参数region定义了要拷贝的2维或3维区域(width,height,depth),其单位分别是byte、row、slice。对于2维区域,depth的值region[2]为1。参数src_row_pitch是src_buffer中每一行数据的字节数。如果src_row_pitch为0,则在计算偏移量时用region[0]替代。
- 参数src_slice_pitch是src_buffer中每个2维平面(slice)的字节数。如果src_slice_pitch为0,则在计算偏移量时用region[1]*src_row_pitch替代。
- 参数dst_row_pitch是dst_buffer中每一行数据的字节数。如果src_row_pitch为0,则在计算偏移量时用region[0]替代。
- 参数dst_slice_pitch是dst_buffer中每个2维平面的字节数。如果dst_slice_pitch为0,则在计算偏移量时用region[1]*dst_row_pitch替代。
其余参数与之前讲述内容中同名参数意义一样,在此就不赘述。
函数clEnqueueCopyBuffer实现的是把src_buffer缓冲区对象中的内容拷贝到dst_buffer缓冲区对象中。而函数clEnqueueCopyBufferRect实现的是把src_buffer缓冲区对象中的2维或者3维矩形区域内容拷贝到dst_buffer中。
如下代码实现了从buffer_one缓冲区对象开始位置起拷贝100个数据到buffer_two的缓冲区对象第101个位置开始的地方:
……
cl_mem buffer_one, buffer_two;
buffer_one = clCreateBuffer(context, CL_MEM_READ_WRITE,
sizeof(int) * 200, NULL, &err);
buffer_two = clCreateBuffer(context, CL_MEM_READ_WRITE,
sizeof(int) * 200, NULL, &err);
int data = 2;
clEnqueueFillBuffer(cmdqueue, buffer_one, &aa, sizeof(data), 0,
sizeof(int) * 200, 0, NULL, NULL);
……
clEnqueueCopyBuffer(cmdQueue, buffer_one, buffer_two, 0,
sizeof(int) * 100, sizeof(int) * 100, 0,
NULL, NULL);
对于上述代码,可以用下图演示。
如下例子展示了如何从一个缓冲区读取一个4×4的区域到另一个缓冲区对象中:
int data_in[80];
int data_out[16];
for(int i = 0; i 〈 80; i++)
{
data_in[i] = i;
}
cl_mem src_data = clCreateBuffer(context, CL_MEM_READ_WRITE |
CL_MEM_COPY_HOST_PTR,
sizeof(int) * 80, data_in, &err);
cl_mem dst_data = clCreateBuffer(context, CL_MEM_READ_WRITE,
sizeof(int) * 25, NULL, NULL);
const size_t src_origin[3] = {5 * sizeof(int), 3, 0};
const size_t dst_origin[3] = {1 * sizeof(int), 1, 0};
const size_t copy_origin[3] = {4 * sizeof(int), 4, 1};
clEnqueueCopyBufferRect(cmdqueue, src_data, dst_data, src_origin,
dst_origin,
copy_origin, 10 * sizeof(int), 0,
4 * sizeof(int), 0, 0, NULL,
NULL);
clEnqueueReadBuffer(cmdqueue, dst_data, CL_TRUE, 0,
sizeof(int) * 16,
data_out, 0, NULL, NULL);
如上代码,操作如下图所示。clEnqueueCopyBufferRect函数中的7个参数10sizeof(int)使得buffer中的80个数值以10个为一行,一共有8行数据。第9个参数4sizeof(int)使得out_out中的16个数值,以4个为一行,一共有4行。src_origin[3]={5*sizeof(int),3,0}表示矩形数据开始位置在源缓冲区中第5列第3行开始的位置。dst_origin[3]={0,0,0}表示拷贝后的数据从4×4的区域中目的缓冲区对象的起始位置存放。copy_origin[3]={4*sizeof(int),4,1}表示每行拷贝4个数值,拷贝4行,一共16个数据。读者可以自行验证data_out的输出结果。