Создание и управление объектами изображения в OpenCL
OpenCL имеет встроенную поддержку обработки данных изображения. Используя объекты изображения, можно взять данные изображения, находящиеся в памяти хоста, и сделайте его доступным для обработки в ядре, выполняющемся на устройстве OpenCL. Объекты изображения упрощают процесс представления и доступа к данным изображения, так как они предлагают собственную поддержку множества форматов изображения. Если Вы запишете функции ядра, которые должны эффективно выполнить вычисления на данных изображения, то Вы найдете OpenCL для собственной поддержки OS X изображений полезным.
Эта глава иллюстрирует, как взять данные изображения, находящиеся в памяти хоста, и поместить ее в объекты изображения, к которым может получить доступ ядро. Это также обеспечивает обзор того, как пойти об обработке этих данных изображения. Посмотрите Параметры, Описывающие Изображения и Буферы в OS X, который OpenCL для концептуальных описаний видов параметров обычно передавал этим функциям.
Создание и Используя изображения в OpenCL
Для создания объектов изображения использовать gcl_create_image
. Эта функция может использоваться для создания объектов двухмерного изображения и трехмерного изображения. Для указания двухмерного изображения установите image_depth
параметр к 0
. Для создания объекта трехмерного изображения укажите image_depth
в пикселях. Если Вы передаете IOSurfaceRef
как io_surface
параметр, изображение будет создаваться с помощью IOSurface, который Вы передаете. Иначе, установите io_surface
параметр к NULL
.
cl_image gcl_create_image( |
const cl_image_format *image_format, |
size_t image_width, |
size_t image_height, |
size_t image_depth, |
IOSurfaceRef io_surface |
); |
Параметр | Описание |
---|---|
| Дескриптор формата изображения OpenCL. |
| Ширина изображения в пикселях. |
| Высота изображения в пикселях. |
| Глубина изображения в пикселях. |
| Если Вы передаете |
|
Чтение, при записи, и копирование объектов изображения
После создания объекта изображения можно ставить в очередь чтения, записи и копии между ним и памятью хоста. От Вашего хост-приложения можно использовать следующие функции:
Чтобы скопировать данные между двумя изображениями или скопировать данные с одной части изображения к другой части (в том же изображении), вызовите:
void gcl_copy_image(
cl_image dst_image,
cl_image src_image,
const size_t dst_origin[3],
const size_t src_origin[3],
const size_t region[3]);
Копия запускается с
src_origin
(x, y, z значение), и начинает писать вdst_origin
(также x, y, z). Это копирует два - или трехмерная прямоугольная сумма, указаннаяregion
. Поскольку мы копируем между изображениями, все параметры указаны в пикселях.Для копирования данных с изображения на буфер вызовите:
void gcl_copy_image_to_ptr(
void *buffer_ptr,
cl_image src_image,
const size_t src_origin[3],
const size_t region[3]);
buffer_ptr
параметр указывает на целевой буфер, в который будут скопированы пиксели.src_image
изображение, с которого должны быть скопированы пиксели.src_origin
первый пиксель, который будет скопирован.region
область, в которую должны быть скопированы пиксели. Поскольку мы копируем с изображения, источник и область указаны в пикселях.Например, скажите, что у нас есть изображение на 4 пикселя x 4 пикселя. Каждый пиксель требует 4 байтов, одного байта для каждого красного, зеленого, синего, и альфа-канала. В этом примере мы хотим взять часть этих данных изображения — говорит область 3 пикселя шириной и 2 пикселя высотой — запускающийся в пикселе 1,1 из изображения и копирует эту часть изображения к буферу. На рисунке 8-1 мы хотели бы скопировать все светло-зеленые и темно-зеленые пиксели, но не серые пиксели.
Поскольку наше изображение требует четырех байт на пиксель, и мы хотим скопировать 3 x 2 = 6 пикселей, мы требуем, чтобы буфер (по крайней мере) 6 x 4 = 24 байта разместил копию.
Вызов был бы похож на это:
const size_t origin[3] = { 1, 1, 0 };
const size_t region[3] = { 3, 2, 1};
gcl_copy_image_to_ptr(our_buffer, the_image, origin, region);
Для копирования данных от буфера до изображения вызовите:
void gcl_copy_ptr_to_image(
cl_image dst_image,
void *src_buffer_ptr,
const size_t dst_origin[3],
const size_t buffer_region[3]);
Параметры Вы передаете
gcl_copy_ptr_to_image
функция подобна переданнымgcl_copy_image_to_ptr
функция, за исключением того, что место назначения является изображением и пикселями, копируется с буфера.
Доступ к объектам изображения от ядра
gcl_copy
* функции позволяют Вам переместить изображения в и от памяти хоста. Для фактической обработки этих данных изображения на устройстве необходимо сделать это доступными данными к единицам работы, выполняющимся на устройстве. Следующие разделы показывают Вам, как передать Ваши данные ядрам для последующей обработки.
Посмотрите, Как Ядро Взаимодействует С Данными в OS X OpenCL для получения дополнительной информации.
Отображение объектов изображения
Для отображения области в изображении в пространство адреса узла вызовите:
void *gcl_map_image(cl_image image, |
cl_map_flags map_flags, |
const size_t origin[3], |
const size_t region[3]); |
Возвращает указатель на область, которую он отобразил.
Параметр | Описание |
---|---|
| Изображение, которое будет отображено. |
| Указание битового поля |
| (x, y, z) позиция в пикселях в изображении, в котором можно запустить отображение. |
| Область (в пикселях) для чтения. |
Эта функция обеспечивает функциональность, подобную тому из стандарта OpenCL clEnqueueMapImage
функция.
Неотображение объектов изображения
Не отображаться с отображенной памятью gcl_map_ptr
или gcl_map_image
функции, вызовите:
void gcl_unmap(void *ptr); |
Параметр | Описание |
---|---|
| Указатель на память устройства или изображение, для неотображений. |
Сохранение и выпуск объектов изображения
Для предотвращения утечек памяти объекты изображения должны быть освобождены, когда они больше не необходимы.
void gcl_retain_image(cl_image image)
void gcl_release_image(cl_image image)
Пример
В следующем примере узел создает одно изображение для ввода и одно изображение для вывода, вызывает ядро для свопинга красных и зеленых пикселей, затем проверяет результаты.
Демонстрационная функция узла перечисления 8-1 создает изображения, тогда вызывает функцию ядра
#include <stdio.h> |
#include <stdlib.h> |
#include <OpenCL/opencl.h> |
// Include the automatically-generated header which provides the kernel block |
// declaration. |
#include "kernels.cl.h" |
#define COUNT 2048 |
static void display_device(cl_device_id device) |
{ |
char name_buf[128]; |
char vendor_buf[128]; |
clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(char)*128, name_buf, NULL); |
clGetDeviceInfo(device, CL_DEVICE_VENDOR, sizeof(char)*128, vendor_buf, NULL); |
fprintf(stdout, "Using OpenCL device: %s %s\n", vendor_buf, name_buf); |
} |
static void image_test(const dispatch_queue_t dq) |
{ |
// This example uses a dispatch semaphore to achieve synchronization |
// between the host application and the work done for us by the OpenCL device. |
dispatch_semaphore_t dsema = dispatch_semaphore_create(0); |
// This example creates a "fake" RGBA, 8-bit-per channel image, solid red. |
// In a real program, you would use some real raster data. |
// Most OpenCL devices support a wide variety of image formats. |
unsigned int i; |
size_t height = 2048, width = 2048; |
unsigned int *pixels = |
(unsigned int*)malloc( sizeof(unsigned int) * width * height ); |
for (i = 0; i < width*height; i++) |
pixels[i] = 0xFF0000FF; // 0xAABBGGRR: 8bits per channel, all red. |
// This image data is on the host side. |
// You need to create two OpenCL images in order to perform some |
// manipulations: one for the input and one for the ouput. |
// This describes the format of the image data. |
cl_image_format format; |
format.image_channel_order = CL_RGBA; |
format.image_channel_data_type = CL_UNSIGNED_INT8; |
cl_mem input_image = gcl_create_image(&format, width, height, 1, NULL); |
cl_mem output_image = gcl_create_image(&format, width, height, 1, NULL); |
dispatch_async(dq, ^{ |
// This kernel is written such that each work item processes one pixel. |
// Thus, it executes over a two-dimensional range, with the width and |
// height of the image determining the dimensions |
// of execution. |
cl_ndrange range = { |
2, // Using a two-dimensional execution. |
{0}, // Start at the beginning of the range. |
{width, height}, // Execute width * height work items. |
{0} // And let OpenCL decide how to divide |
// the work items into work-groups. |
}; |
// Copy the host-side, initial pixel data to the image memory object on |
// the OpenCL device. Here, we copy the whole image, but you could use |
// the origin and region parameters to specify an offset and sub-region |
// of the image, if you'd like. |
const size_t origin[3] = { 0, 0, 0 }; |
const size_t region[3] = { width, height, 1 }; |
gcl_copy_ptr_to_image(input_image, pixels, origin, region); |
// Do it! |
red_to_green_kernel(&range, input_image, output_image); |
// Read back the results; then reuse the host-side buffer we |
// started with. |
gcl_copy_image_to_ptr(pixels, output_image, origin, region); |
// Let the host know we're done. |
dispatch_semaphore_signal(dsema); |
}); |
// Do other work, if you'd like... |
// ... but eventually, you will want to wait for OpenCL to finish up. |
dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER); |
// We expect '0xFF00FF00' for each pixel. |
// Solid green, all the way. |
int results_ok = 1; |
for (i = 0; i < width*height; i++) { |
if (pixels[i] != 0xFF00FF00) { |
fprintf(stdout, |
"Oh dear. Pixel %d was not correct. |
Expected 0xFF00FF00, saw %x\n", |
i, pixels[i]); |
results_ok = 0; |
break; |
} |
} |
if (results_ok) |
fprintf(stdout, "Image results OK!\n"); |
// Clean up device-size allocations. |
// Note that we use the "standard" OpenCL API here. |
clReleaseMemObject(input_image); |
clReleaseMemObject(output_image); |
// Clean up host-side allocations. |
free(pixels); |
} |
int main (int argc, const char * argv[]) |
{ |
// Grab a CPU-based dispatch queue. |
dispatch_queue_t dq = gcl_create_dispatch_queue(CL_DEVICE_TYPE_CPU, NULL); |
if (!dq) |
{ |
fprintf(stdout, "Unable to create a CPU-based dispatch queue.\n"); |
exit(1); |
} |
// Display the OpenCL device associated with this dispatch queue. |
display_device(gcl_get_device_id_with_dispatch_queue(dq)); |
image_test(dq); |
fprintf(stdout, "\nDone.\n\n"); |
dispatch_release(dq); |
} |
Демонстрационное ядро перечисления 8-2 подкачивает красные и зеленые каналы
// A simple kernel that swaps the red and green channels. |
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST; |
kernel void red_to_green(read_only image2d_t input, write_only image2d_t output) |
{ |
size_t x = get_global_id(0); |
size_t y = get_global_id(1); |
uint4 tap = read_imageui(input, sampler, (int2)(x,y)); |
write_imageui(output, (int2)(x,y), tap.yxzw); |
} |