第38章 OpenGL ES 互操作测试

38.1 概述

OpenCL 与 OpenGL ES 的互操作(cl_khr_gl_event 扩展)允许 OpenCL 在移动和嵌入式平台上与 OpenGL ES 共享图形资源。OpenGL ES 是 OpenGL 的子集,专为资源受限的设备设计,广泛应用于移动设备、游戏机和嵌入式系统。

38.1.1 与 OpenGL 互操作的区别

特性 OpenGL OpenGL ES
目标平台 桌面系统 移动/嵌入式设备
API 复杂度 完整 精简子集
扩展名称 cl_khr_gl_sharing cl_khr_gl_sharing
上下文创建 GLX/WGL/CGL EGL
支持的对象 更全面 核心对象

38.1.2 支持的 GLES 版本

  • OpenGL ES 2.0:基本的着色器支持
  • OpenGL ES 3.0:纹理数组、3D 纹理、变换反馈
  • OpenGL ES 3.1:计算着色器、原子计数器
  • OpenGL ES 3.2:几何着色器、细分着色器

38.2 EGL 上下文创建

38.2.1 EGL 初始化

c 复制代码
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl3.h>
#include <CL/cl.h>
#include <CL/cl_gl.h>

// EGL 上下文结构
typedef struct {
    EGLDisplay display;
    EGLContext context;
    EGLSurface surface;
    EGLConfig config;
} EGLContext_t;

// 初始化 EGL 并创建 GLES 上下文
EGLContext_t* create_egl_context(int width, int height) {
    EGLContext_t *egl_ctx = (EGLContext_t*)malloc(sizeof(EGLContext_t));
    
    // 获取默认显示
    egl_ctx->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (egl_ctx->display == EGL_NO_DISPLAY) {
        printf("Failed to get EGL display\n");
        return NULL;
    }
    
    // 初始化 EGL
    EGLint major, minor;
    if (!eglInitialize(egl_ctx->display, &major, &minor)) {
        printf("Failed to initialize EGL\n");
        return NULL;
    }
    
    printf("EGL version: %d.%d\n", major, minor);
    
    // 选择配置
    EGLint config_attribs[] = {
        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_DEPTH_SIZE, 24,
        EGL_NONE
    };
    
    EGLint num_configs;
    if (!eglChooseConfig(egl_ctx->display, config_attribs,
                        &egl_ctx->config, 1, &num_configs)) {
        printf("Failed to choose EGL config\n");
        return NULL;
    }
    
    // 创建 Pbuffer 表面
    EGLint pbuffer_attribs[] = {
        EGL_WIDTH, width,
        EGL_HEIGHT, height,
        EGL_NONE
    };
    
    egl_ctx->surface = eglCreatePbufferSurface(egl_ctx->display,
                                               egl_ctx->config,
                                               pbuffer_attribs);
    if (egl_ctx->surface == EGL_NO_SURFACE) {
        printf("Failed to create EGL surface\n");
        return NULL;
    }
    
    // 创建 GLES 3.0 上下文
    EGLint context_attribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 3,
        EGL_NONE
    };
    
    egl_ctx->context = eglCreateContext(egl_ctx->display,
                                        egl_ctx->config,
                                        EGL_NO_CONTEXT,
                                        context_attribs);
    if (egl_ctx->context == EGL_NO_CONTEXT) {
        printf("Failed to create EGL context\n");
        return NULL;
    }
    
    // 激活上下文
    eglMakeCurrent(egl_ctx->display, egl_ctx->surface,
                   egl_ctx->surface, egl_ctx->context);
    
    return egl_ctx;
}

38.2.2 从 EGL 上下文创建 CL 上下文

c 复制代码
// 从 EGL 上下文创建 CL 上下文
cl_context create_cl_context_from_egl(cl_platform_id platform,
                                      cl_device_id device,
                                      EGLContext_t *egl_ctx) {
    cl_int err;
    
    // 设置上下文属性
    cl_context_properties properties[] = {
        CL_GL_CONTEXT_KHR, (cl_context_properties)egl_ctx->context,
        CL_EGL_DISPLAY_KHR, (cl_context_properties)egl_ctx->display,
        CL_CONTEXT_PLATFORM, (cl_context_properties)platform,
        0
    };
    
    // 创建 CL 上下文
    cl_context context = clCreateContext(properties, 1, &device,
                                        NULL, NULL, &err);
    if (err != CL_SUCCESS) {
        printf("Failed to create CL context: %d\n", err);
        return NULL;
    }
    
    return context;
}

// 从 EGL 获取合适的 CL 设备
cl_device_id get_device_from_egl(cl_platform_id platform,
                                 EGLContext_t *egl_ctx) {
    cl_device_id device;
    cl_int err;
    
    cl_context_properties properties[] = {
        CL_GL_CONTEXT_KHR, (cl_context_properties)egl_ctx->context,
        CL_EGL_DISPLAY_KHR, (cl_context_properties)egl_ctx->display,
        CL_CONTEXT_PLATFORM, (cl_context_properties)platform,
        0
    };
    
    // 获取 clGetGLContextInfoKHR 函数指针
    clGetGLContextInfoKHR_fn clGetGLContextInfoKHR =
        (clGetGLContextInfoKHR_fn)clGetExtensionFunctionAddressForPlatform(
            platform, "clGetGLContextInfoKHR");
    
    if (!clGetGLContextInfoKHR) {
        printf("clGetGLContextInfoKHR not available\n");
        return NULL;
    }
    
    // 获取当前 GL 上下文的设备
    err = clGetGLContextInfoKHR(properties,
                                CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR,
                                sizeof(cl_device_id), &device, NULL);
    if (err != CL_SUCCESS) {
        printf("Failed to get device from GL context: %d\n", err);
        return NULL;
    }
    
    return device;
}

38.3 GLES 缓冲区互操作

38.3.1 创建和共享 GLES 缓冲区

c 复制代码
// 创建 GLES 缓冲区
GLuint create_gles_buffer(size_t size, void *data) {
    GLuint buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, size, data, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    // 检查 GL 错误
    GLenum err = glGetError();
    if (err != GL_NO_ERROR) {
        printf("GL error in buffer creation: 0x%x\n", err);
        return 0;
    }
    
    return buffer;
}

// 测试 GLES 缓冲区互操作
int test_gles_buffer(cl_context context, cl_command_queue queue,
                    size_t buffer_size) {
    cl_int err;
    GLuint gl_buffer;
    cl_mem cl_buffer;
    
    // 创建测试数据
    float *data = (float*)malloc(buffer_size);
    for (size_t i = 0; i < buffer_size / sizeof(float); i++) {
        data[i] = (float)i;
    }
    
    // 创建 GLES 缓冲区
    gl_buffer = create_gles_buffer(buffer_size, data);
    if (gl_buffer == 0) {
        free(data);
        return TEST_FAIL;
    }
    
    // 从 GL 缓冲区创建 CL 对象
    cl_buffer = clCreateFromGLBuffer(context, CL_MEM_READ_WRITE,
                                    gl_buffer, &err);
    if (err != CL_SUCCESS) {
        printf("clCreateFromGLBuffer failed: %d\n", err);
        glDeleteBuffers(1, &gl_buffer);
        free(data);
        return TEST_FAIL;
    }
    
    // 创建处理内核
    const char *kernel_source =
        "__kernel void scale_buffer(__global float *buf, float factor) {\n"
        "    int gid = get_global_id(0);\n"
        "    buf[gid] *= factor;\n"
        "}\n";
    
    cl_program program = clCreateProgramWithSource(context, 1,
                                                   &kernel_source, NULL, &err);
    clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
    cl_kernel kernel = clCreateKernel(program, "scale_buffer", &err);
    
    // 获取 GL 对象
    err = clEnqueueAcquireGLObjects(queue, 1, &cl_buffer, 0, NULL, NULL);
    if (err != CL_SUCCESS) {
        printf("clEnqueueAcquireGLObjects failed: %d\n", err);
        return TEST_FAIL;
    }
    
    // 执行内核
    float factor = 2.0f;
    clSetKernelArg(kernel, 0, sizeof(cl_mem), &cl_buffer);
    clSetKernelArg(kernel, 1, sizeof(float), &factor);
    
    size_t global_size = buffer_size / sizeof(float);
    err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size,
                                NULL, 0, NULL, NULL);
    
    // 释放 GL 对象
    err = clEnqueueReleaseGLObjects(queue, 1, &cl_buffer, 0, NULL, NULL);
    clFinish(queue);
    
    // 验证结果
    float *result = (float*)malloc(buffer_size);
    glBindBuffer(GL_ARRAY_BUFFER, gl_buffer);
    glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer_size, result);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    int pass = 1;
    for (size_t i = 0; i < buffer_size / sizeof(float); i++) {
        if (fabs(result[i] - data[i] * factor) > 0.001f) {
            printf("Mismatch at index %zu: expected %f, got %f\n",
                   i, data[i] * factor, result[i]);
            pass = 0;
            break;
        }
    }
    
    // 清理
    free(data);
    free(result);
    clReleaseMemObject(cl_buffer);
    clReleaseKernel(kernel);
    clReleaseProgram(program);
    glDeleteBuffers(1, &gl_buffer);
    
    return pass ? TEST_PASS : TEST_FAIL;
}

38.3.2 顶点缓冲区对象(VBO)

c 复制代码
// 测试 VBO 互操作
int test_gles_vbo(cl_context context, cl_command_queue queue,
                 int num_vertices) {
    cl_int err;
    GLuint vbo;
    cl_mem cl_vbo;
    
    // 创建顶点数据
    typedef struct {
        float x, y, z;
        float nx, ny, nz;
    } Vertex;
    
    size_t buffer_size = num_vertices * sizeof(Vertex);
    Vertex *vertices = (Vertex*)malloc(buffer_size);
    
    // 初始化顶点
    for (int i = 0; i < num_vertices; i++) {
        vertices[i].x = (float)i;
        vertices[i].y = (float)i * 2.0f;
        vertices[i].z = 0.0f;
        vertices[i].nx = 0.0f;
        vertices[i].ny = 0.0f;
        vertices[i].nz = 1.0f;
    }
    
    // 创建 VBO
    vbo = create_gles_buffer(buffer_size, vertices);
    
    // 从 VBO 创建 CL 对象
    cl_vbo = clCreateFromGLBuffer(context, CL_MEM_READ_WRITE, vbo, &err);
    test_error(err, "clCreateFromGLBuffer failed");
    
    // CL 处理顶点(例如:物理模拟)
    const char *kernel_source =
        "typedef struct {\n"
        "    float x, y, z;\n"
        "    float nx, ny, nz;\n"
        "} Vertex;\n"
        "\n"
        "__kernel void update_vertices(__global Vertex *vertices, float time) {\n"
        "    int gid = get_global_id(0);\n"
        "    vertices[gid].y += sin(time + vertices[gid].x * 0.1f) * 0.1f;\n"
        "}\n";
    
    cl_program program = clCreateProgramWithSource(context, 1,
                                                   &kernel_source, NULL, &err);
    clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
    cl_kernel kernel = clCreateKernel(program, "update_vertices", &err);
    
    // 模拟循环
    for (int frame = 0; frame < 10; frame++) {
        float time = frame * 0.1f;
        
        // 获取 GL 对象
        clEnqueueAcquireGLObjects(queue, 1, &cl_vbo, 0, NULL, NULL);
        
        // 更新顶点
        clSetKernelArg(kernel, 0, sizeof(cl_mem), &cl_vbo);
        clSetKernelArg(kernel, 1, sizeof(float), &time);
        
        size_t global_size = num_vertices;
        clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size,
                              NULL, 0, NULL, NULL);
        
        // 释放 GL 对象
        clEnqueueReleaseGLObjects(queue, 1, &cl_vbo, 0, NULL, NULL);
        clFinish(queue);
        
        // GL 渲染顶点
        // ... 渲染代码 ...
    }
    
    // 清理
    free(vertices);
    clReleaseMemObject(cl_vbo);
    clReleaseKernel(kernel);
    clReleaseProgram(program);
    glDeleteBuffers(1, &vbo);
    
    return TEST_PASS;
}

38.4 GLES 纹理互操作

38.4.1 2D 纹理测试

c 复制代码
// 创建 GLES 2D 纹理
GLuint create_gles_texture_2d(int width, int height, void *data) {
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, data);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    glBindTexture(GL_TEXTURE_2D, 0);
    
    GLenum err = glGetError();
    if (err != GL_NO_ERROR) {
        printf("GL error in texture creation: 0x%x\n", err);
        return 0;
    }
    
    return texture;
}

// 测试 2D 纹理互操作
int test_gles_images_2D(cl_context context, cl_command_queue queue,
                       int width, int height) {
    cl_int err;
    GLuint gl_texture;
    cl_mem cl_image;
    
    // 创建测试纹理
    size_t image_size = width * height * 4;
    unsigned char *data = (unsigned char*)malloc(image_size);
    for (size_t i = 0; i < image_size; i++) {
        data[i] = i % 256;
    }
    
    gl_texture = create_gles_texture_2d(width, height, data);
    if (gl_texture == 0) {
        free(data);
        return TEST_FAIL;
    }
    
    // 从 GL 纹理创建 CL 图像
    cl_image = clCreateFromGLTexture(context, CL_MEM_READ_WRITE,
                                    GL_TEXTURE_2D, 0, gl_texture, &err);
    if (err != CL_SUCCESS) {
        printf("clCreateFromGLTexture failed: %d\n", err);
        glDeleteTextures(1, &gl_texture);
        free(data);
        return TEST_FAIL;
    }
    
    // 图像处理内核
    const char *kernel_source =
        "__kernel void invert_image(\n"
        "    __read_only image2d_t input,\n"
        "    __write_only image2d_t output)\n"
        "{\n"
        "    int x = get_global_id(0);\n"
        "    int y = get_global_id(1);\n"
        "    \n"
        "    sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n"
        "                        CLK_ADDRESS_CLAMP_TO_EDGE |\n"
        "                        CLK_FILTER_NEAREST;\n"
        "    \n"
        "    float4 pixel = read_imagef(input, sampler, (int2)(x, y));\n"
        "    pixel = (float4)(1.0f) - pixel;\n"
        "    pixel.w = 1.0f;  // 保持 alpha 通道\n"
        "    write_imagef(output, (int2)(x, y), pixel);\n"
        "}\n";
    
    cl_program program = clCreateProgramWithSource(context, 1,
                                                   &kernel_source, NULL, &err);
    clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
    cl_kernel kernel = clCreateKernel(program, "invert_image", &err);
    
    // 获取 GL 对象
    clEnqueueAcquireGLObjects(queue, 1, &cl_image, 0, NULL, NULL);
    
    // 执行图像处理
    clSetKernelArg(kernel, 0, sizeof(cl_mem), &cl_image);
    clSetKernelArg(kernel, 1, sizeof(cl_mem), &cl_image);  // 就地处理
    
    size_t global_size[] = { width, height };
    clEnqueueNDRangeKernel(queue, kernel, 2, NULL, global_size,
                          NULL, 0, NULL, NULL);
    
    // 释放 GL 对象
    clEnqueueReleaseGLObjects(queue, 1, &cl_image, 0, NULL, NULL);
    clFinish(queue);
    
    // 清理
    free(data);
    clReleaseMemObject(cl_image);
    clReleaseKernel(kernel);
    clReleaseProgram(program);
    glDeleteTextures(1, &gl_texture);
    
    return TEST_PASS;
}

38.4.2 3D 纹理测试(GLES 3.0+)

c 复制代码
// 测试 3D 纹理互操作(需要 GLES 3.0+)
int test_gles_images_3D(cl_context context, cl_command_queue queue,
                       int width, int height, int depth) {
    cl_int err;
    GLuint gl_texture;
    cl_mem cl_image;
    
    // 创建 3D 纹理
    glGenTextures(1, &gl_texture);
    glBindTexture(GL_TEXTURE_3D, gl_texture);
    
    glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, width, height, depth, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    
    glBindTexture(GL_TEXTURE_3D, 0);
    
    // 从 GL 纹理创建 CL 图像
    cl_image = clCreateFromGLTexture(context, CL_MEM_READ_WRITE,
                                    GL_TEXTURE_3D, 0, gl_texture, &err);
    if (err != CL_SUCCESS) {
        printf("clCreateFromGLTexture (3D) failed: %d\n", err);
        glDeleteTextures(1, &gl_texture);
        return TEST_FAIL;
    }
    
    // 验证图像维度
    size_t image_width, image_height, image_depth;
    clGetImageInfo(cl_image, CL_IMAGE_WIDTH, sizeof(size_t), &image_width, NULL);
    clGetImageInfo(cl_image, CL_IMAGE_HEIGHT, sizeof(size_t), &image_height, NULL);
    clGetImageInfo(cl_image, CL_IMAGE_DEPTH, sizeof(size_t), &image_depth, NULL);
    
    if (image_width != width || image_height != height || image_depth != depth) {
        printf("3D image dimensions mismatch\n");
        clReleaseMemObject(cl_image);
        glDeleteTextures(1, &gl_texture);
        return TEST_FAIL;
    }
    
    // 获取并释放 GL 对象
    clEnqueueAcquireGLObjects(queue, 1, &cl_image, 0, NULL, NULL);
    // ... 处理 3D 图像 ...
    clEnqueueReleaseGLObjects(queue, 1, &cl_image, 0, NULL, NULL);
    clFinish(queue);
    
    // 清理
    clReleaseMemObject(cl_image);
    glDeleteTextures(1, &gl_texture);
    
    return TEST_PASS;
}

38.5 GLES 渲染缓冲区互操作

38.5.1 渲染缓冲区测试

c 复制代码
// 创建 GLES 渲染缓冲区
GLuint create_gles_renderbuffer(int width, int height, GLenum format) {
    GLuint renderbuffer;
    glGenRenderbuffers(1, &renderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    
    GLenum err = glGetError();
    if (err != GL_NO_ERROR) {
        printf("GL error in renderbuffer creation: 0x%x\n", err);
        return 0;
    }
    
    return renderbuffer;
}

// 测试渲染缓冲区互操作
int test_gles_renderbuffer(cl_context context, cl_command_queue queue,
                          int width, int height) {
    cl_int err;
    GLuint gl_rb;
    cl_mem cl_image;
    
    // 创建渲染缓冲区
    gl_rb = create_gles_renderbuffer(width, height, GL_RGBA8);
    if (gl_rb == 0) {
        return TEST_FAIL;
    }
    
    // 从渲染缓冲区创建 CL 图像
    cl_image = clCreateFromGLRenderbuffer(context, CL_MEM_READ_WRITE,
                                         gl_rb, &err);
    if (err != CL_SUCCESS) {
        printf("clCreateFromGLRenderbuffer failed: %d\n", err);
        glDeleteRenderbuffers(1, &gl_rb);
        return TEST_FAIL;
    }
    
    // 查询 GL 对象信息
    cl_GLuint gl_name;
    cl_gl_object_type obj_type;
    
    err = clGetGLObjectInfo(cl_image, &obj_type, &gl_name);
    test_error(err, "clGetGLObjectInfo failed");
    
    if (obj_type != CL_GL_OBJECT_RENDERBUFFER) {
        printf("Object type mismatch\n");
        clReleaseMemObject(cl_image);
        glDeleteRenderbuffers(1, &gl_rb);
        return TEST_FAIL;
    }
    
    if (gl_name != gl_rb) {
        printf("GL name mismatch\n");
        clReleaseMemObject(cl_image);
        glDeleteRenderbuffers(1, &gl_rb);
        return TEST_FAIL;
    }
    
    // 获取并释放 GL 对象
    clEnqueueAcquireGLObjects(queue, 1, &cl_image, 0, NULL, NULL);
    // ... 处理渲染缓冲区 ...
    clEnqueueReleaseGLObjects(queue, 1, &cl_image, 0, NULL, NULL);
    clFinish(queue);
    
    // 清理
    clReleaseMemObject(cl_image);
    glDeleteRenderbuffers(1, &gl_rb);
    
    return TEST_PASS;
}

38.5.2 帧缓冲区对象(FBO)

c 复制代码
// 使用 FBO 和 CL 进行离屏渲染
int test_gles_fbo_cl_pipeline(cl_context context, cl_command_queue queue,
                              int width, int height) {
    cl_int err;
    GLuint fbo, color_rb, depth_rb;
    cl_mem cl_color;
    
    // 创建 FBO
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
    // 创建颜色渲染缓冲区
    color_rb = create_gles_renderbuffer(width, height, GL_RGBA8);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                             GL_RENDERBUFFER, color_rb);
    
    // 创建深度渲染缓冲区
    depth_rb = create_gles_renderbuffer(width, height, GL_DEPTH_COMPONENT24);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                             GL_RENDERBUFFER, depth_rb);
    
    // 检查 FBO 完整性
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        printf("FBO is not complete\n");
        return TEST_FAIL;
    }
    
    // 从颜色缓冲区创建 CL 图像
    cl_color = clCreateFromGLRenderbuffer(context, CL_MEM_READ_WRITE,
                                         color_rb, &err);
    test_error(err, "clCreateFromGLRenderbuffer failed");
    
    // GL 渲染到 FBO
    glViewport(0, 0, width, height);
    glClearColor(0.2f, 0.3f, 0.4f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // ... 渲染场景 ...
    
    // 确保 GL 渲染完成
    glFinish();
    
    // CL 后处理
    clEnqueueAcquireGLObjects(queue, 1, &cl_color, 0, NULL, NULL);
    // ... CL 图像处理 ...
    clEnqueueReleaseGLObjects(queue, 1, &cl_color, 0, NULL, NULL);
    clFinish(queue);
    
    // 读取最终结果
    // ...
    
    // 清理
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    clReleaseMemObject(cl_color);
    glDeleteFramebuffers(1, &fbo);
    glDeleteRenderbuffers(1, &color_rb);
    glDeleteRenderbuffers(1, &depth_rb);
    
    return TEST_PASS;
}

38.6 EGL Sync 同步

38.6.1 EGL Sync 对象

c 复制代码
// 使用 EGL Sync 进行 GL-CL 同步
int test_gles_egl_sync(cl_context context, cl_command_queue queue,
                      EGLDisplay display) {
    cl_int err;
    
    // 在 GL 端插入 sync 对象
    EGLSyncKHR egl_sync = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL);
    if (egl_sync == EGL_NO_SYNC_KHR) {
        printf("Failed to create EGL sync\n");
        return TEST_FAIL;
    }
    
    // 刷新 GL 命令
    glFlush();
    
    // 从 EGL sync 创建 CL 事件(如果支持)
    clCreateEventFromEGLSyncKHR_fn clCreateEventFromEGLSyncKHR =
        (clCreateEventFromEGLSyncKHR_fn)clGetExtensionFunctionAddress(
            "clCreateEventFromEGLSyncKHR");
    
    if (clCreateEventFromEGLSyncKHR) {
        cl_event cl_event = clCreateEventFromEGLSyncKHR(context, egl_sync,
                                                       display, &err);
        if (err == CL_SUCCESS) {
            // CL 命令等待 GL 完成
            clEnqueueMarkerWithWaitList(queue, 1, &cl_event, NULL);
            clReleaseEvent(cl_event);
        }
    } else {
        // 降级到客户端同步
        EGLint result = eglClientWaitSyncKHR(display, egl_sync,
                                            EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                            EGL_FOREVER_KHR);
        if (result == EGL_FALSE) {
            printf("eglClientWaitSyncKHR failed\n");
            return TEST_FAIL;
        }
    }
    
    // 清理
    eglDestroySyncKHR(display, egl_sync);
    
    return TEST_PASS;
}

38.7 移动端优化

38.7.1 内存带宽优化

c 复制代码
// 针对移动 GPU 优化的图像处理
const char *mobile_optimized_kernel =
"__kernel void mobile_blur(\n"
"    __read_only image2d_t input,\n"
"    __write_only image2d_t output)\n"
"{\n"
"    int x = get_global_id(0);\n"
"    int y = get_global_id(1);\n"
"    \n"
"    sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n"
"                        CLK_ADDRESS_CLAMP_TO_EDGE |\n"
"                        CLK_FILTER_LINEAR;  // 利用硬件线性插值\n"
"    \n"
"    // 使用较小的滤波核以减少内存访问\n"
"    float4 sum = (float4)(0.0f);\n"
"    sum += read_imagef(input, sampler, (float2)(x-1, y-1)) * 0.0625f;\n"
"    sum += read_imagef(input, sampler, (float2)(x,   y-1)) * 0.125f;\n"
"    sum += read_imagef(input, sampler, (float2)(x+1, y-1)) * 0.0625f;\n"
"    sum += read_imagef(input, sampler, (float2)(x-1, y  )) * 0.125f;\n"
"    sum += read_imagef(input, sampler, (float2)(x,   y  )) * 0.25f;\n"
"    sum += read_imagef(input, sampler, (float2)(x+1, y  )) * 0.125f;\n"
"    sum += read_imagef(input, sampler, (float2)(x-1, y+1)) * 0.0625f;\n"
"    sum += read_imagef(input, sampler, (float2)(x,   y+1)) * 0.125f;\n"
"    sum += read_imagef(input, sampler, (float2)(x+1, y+1)) * 0.0625f;\n"
"    \n"
"    write_imagef(output, (int2)(x, y), sum);\n"
"}\n";

38.7.2 功耗优化

c 复制代码
// 功耗优化策略
void optimize_for_mobile(cl_command_queue queue) {
    // 1. 使用异步操作减少CPU等待
    clFlush(queue);  // 而不是 clFinish()
    
    // 2. 批量处理减少命令提交开销
    // 积累多个操作后再flush
    
    // 3. 选择合适的工作组大小
    // 移动GPU通常较小的工作组更高效
    size_t local_size[] = { 8, 8 };  // 而不是 16x16
}

38.8 CTS 测试用例清单

38.8.1 缓冲区测试

测试名称 测试内容
test_buffers GLES 缓冲区基本读写
test_buffers_getinfo GLES 缓冲区信息查询

38.8.2 纹理测试

测试名称 测试内容
test_images_2D 2D 纹理互操作
test_images_2D_info 2D 纹理信息查询
test_images_3D 3D 纹理互操作(GLES 3.0+)
test_images_3D_info 3D 纹理信息查询(GLES 3.0+)

38.8.3 渲染缓冲区测试

测试名称 测试内容
test_renderbuffer 渲染缓冲区读写
test_renderbuffer_info 渲染缓冲区信息查询

38.8.4 同步测试

测试名称 测试内容
test_fence_sync EGL fence sync 同步

38.9 实际应用场景

38.9.1 移动游戏后处理

c 复制代码
// 移动游戏实时后处理流水线
void mobile_game_postprocess(GLuint scene_texture, GLuint output_texture,
                             cl_context context, cl_command_queue queue,
                             int width, int height) {
    cl_mem cl_input, cl_output;
    
    // 创建 CL 图像
    cl_input = clCreateFromGLTexture(context, CL_MEM_READ_ONLY,
                                    GL_TEXTURE_2D, 0, scene_texture, NULL);
    cl_output = clCreateFromGLTexture(context, CL_MEM_WRITE_ONLY,
                                     GL_TEXTURE_2D, 0, output_texture, NULL);
    
    // 每帧处理
    while (game_running) {
        // GLES 渲染场景
        render_game_scene();
        
        // CL 后处理
        cl_mem objects[] = { cl_input, cl_output };
        clEnqueueAcquireGLObjects(queue, 2, objects, 0, NULL, NULL);
        
        // 应用多种效果:HDR、Bloom、Color Grading
        apply_hdr_tonemapping(queue, cl_input, cl_output, width, height);
        apply_bloom(queue, cl_output, width, height);
        apply_color_grading(queue, cl_output, width, height);
        
        clEnqueueReleaseGLObjects(queue, 2, objects, 0, NULL, NULL);
        clFlush(queue);  // 不使用 Finish 以提高性能
        
        // GLES 显示结果
        display_texture(output_texture);
        swap_buffers();
    }
    
    clReleaseMemObject(cl_input);
    clReleaseMemObject(cl_output);
}

38.9.2 AR 应用

c 复制代码
// AR 应用中的实时图像分析
void ar_image_processing(GLuint camera_texture,
                        cl_context context, cl_command_queue queue,
                        int width, int height) {
    cl_mem cl_camera;
    
    // 从相机纹理创建 CL 图像
    cl_camera = clCreateFromGLTexture(context, CL_MEM_READ_ONLY,
                                     GL_TEXTURE_2D, 0, camera_texture, NULL);
    
    while (ar_active) {
        // 相机帧更新到纹理
        update_camera_texture(camera_texture);
        
        // CL 图像分析
        clEnqueueAcquireGLObjects(queue, 1, &cl_camera, 0, NULL, NULL);
        
        // 特征检测、跟踪
        detect_markers(queue, cl_camera, width, height);
        track_objects(queue, cl_camera, width, height);
        
        clEnqueueReleaseGLObjects(queue, 1, &cl_camera, 0, NULL, NULL);
        clFlush(queue);
        
        // GLES 渲染 AR 内容
        render_ar_overlays();
        swap_buffers();
    }
    
    clReleaseMemObject(cl_camera);
}

38.10 最佳实践

38.10.1 移动平台特定优化

  1. 优先使用图像对象:移动 GPU 对图像对象优化更好
  2. 减少内存分配:重用 GL/CL 对象
  3. 控制精度:适当使用半精度浮点(half)
  4. 批量操作:减少 API 调用开销

38.10.2 功耗管理

c 复制代码
// 动态调整处理质量以平衡性能和功耗
typedef enum {
    QUALITY_LOW,      // 低功耗模式
    QUALITY_MEDIUM,   // 平衡模式
    QUALITY_HIGH      // 高质量模式
} QualityLevel;

void adjust_processing_quality(QualityLevel level, size_t *global_size) {
    switch (level) {
        case QUALITY_LOW:
            // 降低分辨率
            global_size[0] /= 2;
            global_size[1] /= 2;
            break;
        case QUALITY_MEDIUM:
            // 标准分辨率
            break;
        case QUALITY_HIGH:
            // 全分辨率,可能增加采样
            break;
    }
}

38.11 总结

38.11.1 关键要点

  1. EGL 是移动平台的标准接口用于创建 GL-CL 共享上下文
  2. 功能子集:GLES 相比 OpenGL 功能较少,但核心互操作相同
  3. 性能优化至关重要:移动设备资源受限
  4. 功耗考虑:需要平衡性能和电池寿命

38.11.2 与桌面 GL 的差异

  • 使用 EGL 而非 GLX/WGL/CGL
  • 某些高级 GL 特性不可用
  • 更严格的性能和功耗约束
  • 更小的工作组大小通常更优

参考资源

  • cl_khr_gl_sharing 扩展规范
  • EGL 规范文档
  • test_conformance/gles/ 目录中的测试实现

相关测试目录test_conformance/gles/

依赖章节

  • 第37章:GL - OpenGL 互操作测试
  • 第6章:Images - 图像对象测试
相关推荐
DeeplyMind4 天前
第33章 Extensions - 扩展功能测试
opencl·opencl-cts
DeeplyMind9 天前
第31章 Multiple_device_context - 多设备上下文测试
opencl·opencl-cts
DeeplyMind15 天前
第27章 Device_partition - 设备分区测试
opencl·opencl-cts
DeeplyMind17 天前
第24章 Workgroups - 工作组测试
opencl·workgroup·opencl-cts