Совместное использование данных между OpenCL и OpenGL

OpenGL (Открытая библиотека графических функций) является API для записи приложений, производящих два - и трехмерная компьютерная графика. OpenCL и OpenGL разработаны для взаимодействия. В частности OpenCL и OpenGL могут совместно использовать данные, сокращающие наверху. Например, объекты OpenGL и объекты памяти OpenCL, создаваемые из объектов OpenGL, могут получить доступ к той же памяти. Кроме того, GLSL (Язык Штриховки OpenGL) программы построения теней и ядра OpenCL может получить доступ к совместно используемым данным.

Для проверки OpenCL и OpenGL сотрудничают гладко:

Эта глава показывает, как OpenCL API может использоваться для создания объектов памяти OpenCL из буферных объектов вершины OpenGL (VBOs), объектов текстуры и объектов renderbuffer. Это также показывает, как создать буферные объекты OpenCL из буферных объектов OpenGL и как создать объект изображения OpenCL из текстуры OpenGL или объекта renderbuffer.

Sharegroups

Чтобы позволить приложению OpenCL получать доступ к объекту OpenGL, используйте контекст OpenCL, создающийся из OpenGL sharegroup (CGLShareGroupObj) объект. OpenCL возражает, что Вы создаете в этом контексте, сошлется на эти объекты OpenGL. Когда контекст OpenCL подключен к OpenGL sharegroup объект таким образом, и OpenCL и контексты OpenGL могут сослаться на те же объекты.

Рисунок 10-1 иллюстрирует типичный сценарий, в котором OpenCL генерирует геометрию на GPU, и OpenGL представляет совместно используемую геометрию, также на GPU. Они могут видеть то, на что они смотрят как различные объекты - в этом случае, OpenGL рассматривает данные как VBO, и OpenCL рассматривает его как a cl_mem. Результат состоит в том, что и OpenCL и контексты OpenGL заканчивают тем, что сослались на тот же sharegroup (CGLShareGroupObj) объект, посмотрите те же устройства и получите доступ к этой совместно используемой геометрии.

Рисунок 10-1  OpenGL и OpenCL совместно использует данные с помощью sharegroups

Взаимодействовать между OpenCL и OpenGL:

  1. Установите sharegroup:

    CGLContextObj cgl_context = CGLGetCurrentContext();
    CGLShareGroupObj sharegroup = CGLGetShareGroup(cgl_context);
    gcl_gl_set_sharegroup(sharegroup);
    ...
  2. После того, как sharegroup был установлен, можно создать объекты памяти OpenCL из существующих объектов OpenGL:

    • Для создания буферного объекта OpenCL из буферного объекта OpenGL вызовите:

      void * gcl_gl_create_ptr_from_buffer(GLuint bufobj);
    • Для создания объекта изображения OpenCL из объекта текстуры OpenGL вызовите:

      cl_image gcl_gl_create_image_from_texture(
                  GLenum texture_target,
                  GLint mip_level,
                  GLuint texture);
    • Для создания объекта двухмерного изображения OpenCL из буферного объекта рендеринга OpenGL вызовите:

      cl_image gcl_gl_create_image_from_renderbuffer(GLuint render_buffer);

Синхронизация Доступа К Совместно используемому OpenCL / Объекты OpenGL

Для обеспечения целостности данных приложение должно синхронизировать доступ к объектам, совместно использованным OpenCL и OpenGL. Отказ обеспечить такую синхронизацию может привести к условиям состязания или другому неопределенному поведению включая немобильность между реализациями. Для получения информации о синхронизирующихся событиях OpenCL и OpenGL и пределах, посмотрите Управление OpenCL / Взаимодействие OpenGL С GCD.

Пример: OpenCL и буферы совместного использования OpenGL

Этот пример кода выбирается от OpenCL Процедурный Геометрический Пример Смещения с Интеграцией GCD. Это показывает, как OpenCL может связать с существующими буферами OpenGL во избежание копирования данных назад от вычислить устройства, когда это должно использовать результаты вычисления для рендеринга. В этом примере OpenCL вычисляет ядро, перемещает вершины управляемого OpenGL буферного объекта вершины (VBO). Вычислить ядро вычисляет несколько октав процедурного шума для продвижения получающихся позиций вершины за пределы и вычисляет новые нормальные направления с помощью конечных разностей. Вершины тогда представляются OpenGL.

Создавать это приложение:

  1. Включайте заголовочный файл, сгенерированный XCode. Этот заголовочный файл содержит блочное объявление ядра.

    #include "displacement_kernel.cl.h"

  2. Объявите очередь отгрузки и семафор отгрузки, используемый для синхронизации между OpenCL и OpenGL. Для получения дополнительной информации о семафорах отгрузки, посмотрите Синхронизацию Узла С OpenCL Используя Семафор Отгрузки.

    static dispatch_queue_t queue;
    static dispatch_semaphore_t cl_gl_semaphore;
  3. Объявите объекты памяти, которые будут содержать данные ввода и вывода.

    static void *InputVertexBuffer;
    static void *OutputVertexBuffer;
    static void *OutputNormalBuffer;
  4. Установите sharegroup, создайте очередь отгрузки и семафор отгрузки, и получите вычислить устройство.

    static int setup_compute_devices(int gpu)
    {
        int err;
        size_t returned_size;
        ComputeDeviceType =
          gpu ? CL_DEVICE_TYPE_GPU : CL_DEVICE_TYPE_CPU;
     
        printf(SEPARATOR);
        printf("Using active OpenGL context...\n");
     
        // Set the sharegroup.
        CGLContextObj kCGLContext =
                      CGLGetCurrentContext();
        CGLShareGroupObj kCGLShareGroup =
                      CGLGetShareGroup(kCGLContext);
     
        gcl_gl_set_sharegroup(kCGLShareGroup);
     
        // Create a dispatch queue.
        queue = gcl_create_dispatch_queue(
                      ComputeDeviceType, NULL);
        if (!queue)
        {
            printf("Error: Failed to create a dispatch queue!\n");
            return EXIT_FAILURE;
        }
     
        // Create a dispatch semaphore.
        cl_gl_semaphore = dispatch_semaphore_create(0);
        if (!cl_gl_semaphore)
        {
            printf("Error:
               Failed to create a dispatch semaphore!\n");
            return EXIT_FAILURE;
        }
     
        // Get the device ID.
        ComputeDeviceId =
             gcl_get_device_id_with_dispatch_queue(queue);
     
        // Report the device vendor and device name.
        cl_char vendor_name[1024] = {0};
        cl_char device_name[1024] = {0};
        err = clGetDeviceInfo(
                ComputeDeviceId, CL_DEVICE_VENDOR,
                sizeof(vendor_name), vendor_name,
                &returned_size);
        err|= clGetDeviceInfo(
                ComputeDeviceId, CL_DEVICE_NAME,
                sizeof(device_name), device_name,
                &returned_size);
        if (err != CL_SUCCESS)
        {
            printf(
                "Error: Failed to retrieve device info!\n");
            return EXIT_FAILURE;
        }
     
        printf(SEPARATOR);
        printf(
                "Connecting to %s %s...\n",
                 vendor_name, device_name);
        return CL_SUCCESS;
    }
  5. Создайте объекты памяти, которые будут содержать данные ввода и вывода и инициализировать входной объект памяти с входными данными.

    Этот пример создает новый объект памяти, InputVertexBuffer, использование gcl_malloc функция. Это тогда использует gcl_gl_create_ptr_from_buffer функция для создания объектов OpenCL (OutputVertexBuffer и OutputNormalBuffer) от существующего OpenGL VBOs (VertexBufferId и NormalBufferId). InputVertexBuffer объект памяти инициализируется с входными данными, хранившими в VertexBuffer, использование gcl_memcpy функция.

    static int setup_compute_memory()
    {
        size_t bytes =
            sizeof(float) * VertexComponents * VertexElements;
     
        printf(SEPARATOR); 
        printf("Allocating buffers on compute device...\n");
     
        InputVertexBuffer = gcl_malloc(bytes, NULL, 0);
        if (!InputVertexBuffer)
        {
            printf("Failed to create InputVertexBuffer!\n");
            return EXIT_FAILURE;
        }
        OutputVertexBuffer =
            gcl_gl_create_ptr_from_buffer(VertexBufferId);
        if (!OutputVertexBuffer)
        {
            printf("Failed to create OutputVertexBuffer!\n");
            return EXIT_FAILURE;
        }
     
        OutputNormalBuffer =
            gcl_gl_create_ptr_from_buffer(NormalBufferId);
        if (!OutputNormalBuffer)
        {
            printf("Failed to create OutputNormalBuffer!\n");
            return EXIT_FAILURE;
        }
     
        dispatch_async(queue,
            ^{gcl_memcpy(
               InputVertexBuffer,
               VertexBuffer, VertexBytes);});
     
        return CL_SUCCESS;
    }
  6. Создайте вычислить ядро из блока ядра, сгенерированного XCode.

    static int setup_compute_kernels(void)
    {
        int err = 0;
     
        ComputeKernel =
           gcl_create_kernel_from_block(displace_kernel);
     
        // Get the maximum work group size for executing
        // the kernel on the device.
        size_t max = 1;
        err = clGetKernelWorkGroupInfo(
            ComputeKernel, ComputeDeviceId,
            CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t),
            &max, NULL);
        if (err != CL_SUCCESS)
        {
            printf("Error:
              Failed to retrieve kernel work group
              info! %d\n", err);
            return EXIT_FAILURE;
        }
     
        MaxWorkGroupSize = max;
        printf("Maximum Workgroup Size '%d'\n",
             MaxWorkGroupSize);
     
        return CL_SUCCESS;
    }
  7. Используйте cl_ndrange структура для указания параллели данных передвигается, по которому можно выполнить ядро. Диспетчеризируйте блок ядра асинхронно с помощью dispatch_async функция. После того, как Вы будете диспетчеризировать ядро, будете сигнализировать семафор отгрузки, чтобы указать, что OpenGL может теперь получить доступ к совместно используемым ресурсам.

    static int recompute(void)
    {
        size_t global[2];
        size_t local[2];
        cl_ndrange ndrange;
     
        uint uiSplitCount = ceilf(sqrtf(VertexElements));
        uint uiActive = (MaxWorkGroupSize / GroupSize);
        uiActive = uiActive < 1 ? 1 : uiActive;
     
        uint uiQueued = MaxWorkGroupSize / uiActive;
     
        local[0] = uiActive;
        local[1] = uiQueued;
     
        global[0] = divide_up(uiSplitCount, uiActive) * uiActive;
        global[1] = divide_up(uiSplitCount, uiQueued) * uiQueued;
     
        ndrange.work_dim = 2;
        ndrange.global_work_offset[0] = 0;
        ndrange.global_work_offset[1] = 0;
        ndrange.global_work_size[0] = global[0];
        ndrange.global_work_size[1] = global[1];
        ndrange.local_work_size[0] = local[0];
        ndrange.local_work_size[1] = local[1];
     
        dispatch_async(queue,
                       ^{
                         displace_kernel(&ndrange,
                         InputVertexBuffer,
                         OutputNormalBuffer,
                         OutputVertexBuffer,
                         ActualDimX, ActualDimY,
                         Frequency, Amplitude, Phase,
                         Lacunarity, Increment, Octaves,
                         Roughness, VertexElements);
                         dispatch_semaphore_signal(
                           cl_gl_semaphore);
                        });
        return 0;
    }
  8. Освободите объекты памяти OpenCL, выпустите вычислить ядро, очередь отгрузки и семафор.

    static void shutdown_opencl(void)
    {
        gcl_free(InputVertexBuffer);
        gcl_free(OutputVertexBuffer);
        gcl_free(OutputNormalBuffer);
     
        clReleaseKernel(ComputeKernel);
     
        if(VertexBuffer)
            free(VertexBuffer);
        if(NormalBuffer)
            free(NormalBuffer);
        dispatch_release(cl_gl_semaphore);
        dispatch_release(queue);
    }