QNX Screen-Hardware Rendering

简介

上一篇博客,介绍了QNX Screen的software rendering# QNX SCREEN-Software Rendering。本文主要介绍,QNX Screen侧的硬件(GPU)渲染。这一部分渲染主要是使用GPU来执行渲染操作;通常,在QNX侧使用通过使用Khronos API来使用EGL(例如OpenGL ES或OpenVG)的缓冲区,或使用QNX的native Screen API实现Blitting, 来进行渲染操作。

Khronos rendering API

Screen支持以下Khronos渲染API:OpenGL ES、OpenVG。它们为图形硬件提供通用接口,来辅助生成和操作高质量的二维、三维图像。 在QNX侧,使用Screen的EGL library(libEGL)来访问Khronos EGL,主要的流程类似于前一篇介绍的software rendering流程:

  1. 创建render target
  2. 创建render 上下文(Context)
  3. render
  4. 上屏 本文已OpenGL为例,详解QNX 侧通过EGL渲染的方案

1、创建render target

该步骤同screen的software render类似,关键区别点在于,创建的windows属性SCREEN_PROPERTY_USAGE。software render时,SCREEN_PROPERTY_USAGE通常设置为SCREEN_USAGE_READ | SCREEN_USAGE_WRITE | SCREEN_USAGE_NATIVE,当使用EGL进行渲染时,需要设置为SCREEN_USAGE_OPENGL_ES2。本文依旧以Windows为Render Target

C++ 复制代码
int usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_OVERLAY;
int format = SCREEN_FORMAT_RGBA8888;
ret = screen_set_window_property_iv(pstCtx->screen_win, SCREEN_PROPERTY_USAGE, &usage);
ret = screen_set_window_property_iv(pstCtx->screen_win, SCREEN_PROPERTY_FORMAT, &format);
ret = screen_set_window_property_iv(pstCtx->screen_win, SCREEN_PROPERTY_SWAP_INTERVAL, &pstCtx->interval);
ret = screen_set_window_property_iv(pstCtx->screen_win, SCREEN_PROPERTY_SIZE, pstCtx->size);
ret = screen_set_window_property_iv(pstCtx->screen_win, SCREEN_PROPERTY_POSITION, pstCtx->pos);
ret = screen_set_window_property_iv(pstCtx->screen_win, SCREEN_PROPERTY_ZORDER, &pstCtx->zorder);
ret = screen_set_window_property_cv(pstCtx->screen_win, SCREEN_PROPERTY_ID_STRING, strlen(id_string), id_string);
ret = screen_create_window_buffers(pstCtx->screen_win, pstCtx->nbuffers);

2、创建render context

该Context可以理解为EGL的Context,创建流程也和创建openGL Context一致,如下:

  1. 同EGL的Display建立连接
  2. 初始化EGL的Display
  3. 配置EGL config
  4. 创建Context
  5. 将该Context绑定至步骤一创建的Render Target 具体代码如下:

初始化EGL display

C++ 复制代码
ret = eglInitialize(pstCtx->egl_disp, NULL, NULL);
if (ret != EGL_TRUE) {
     LOGE( "eglInitialize error, %s\n", egl_strerror());
     goto fail1;
}

配置EGL config

C++ 复制代码
ret = eglChooseConfig(pstCtx->egl_disp, attribute_list, &pstCtx->egl_conf, 1, &pstCtx->num_config);
if (ret != EGL_TRUE) {
    LOGE( "eglChooseConfig error, %s\n", egl_strerror());
    goto fail2;
}

创建Context

C++ 复制代码
pstCtx->egl_ctx = eglCreateContext(pstCtx->egl_disp, pstCtx->egl_conf, EGL_NO_CONTEXT, context_attributes);
if (pstCtx->egl_ctx == EGL_NO_CONTEXT) {
    LOGE( "eglCreateContext error, %s\n", egl_strerror());
    goto fail3;
}

将该Context绑定至步骤一创建的Render Target

创建windowsurface,绑定步骤一创建的render target

C++ 复制代码
pstCtx->egl_surf = eglCreateWindowSurface(pstCtx->egl_disp, pstCtx->egl_conf, (EGLNativeWindowType)screen_win, (EGLint *)&pstCtx->egl_surf_attr);
if (pstCtx->egl_surf == EGL_NO_SURFACE) {
    LOGE( "eglCreateWindowSurface error, %s\n", egl_strerror());
    goto fail2;
}
ret = eglMakeCurrent(pstCtx->egl_disp, pstCtx->egl_surf, pstCtx->egl_surf, pstCtx->egl_ctx);
if (ret != EGL_TRUE) {
    LOGE( "eglMakeCurrent error, %s\n", egl_strerror());
    goto fail4;
}

3、Render

使用openGL对EGL的Framebuffer进行绘制,该步骤便不详细展开

4、上屏

C++ 复制代码
ret = eglSwapBuffers(pstCtx->egl_disp, pstCtx->egl_surf);
if (ret != EGL_TRUE) {
    LOGE( "eglSwapBuffers error, %s\n", egl_strerror());
    return -__LINE__;
}

Blitting

由于图形处理需要大量的内存管理和内存区域的移动,为了更好的支持图形处理,通常使用 bit blitter。不同于CPU的字节复制,blitter实现的是pixel的搬运。因此,QNX Screen提供了一套native API,使用blitter搬运buffer。

使用screen_blit进行渲染,渲染前的绘制流程和Software Rendering一致,创建Render Target、创建buffer、绘制buffer,但在设置buffer的usage时,需要设置为SCREEN_USAGE_NATIVE。本文通过EGLImage来刷新一个native buffer,从而讲解screen_blit的调用

绑定EGL Framebuffer

QNX侧,可以使用两种方式将EGL的framebuffer同screen的native buffer绑定。1、使用pixmap创建EGL OES纹理,绘制纹理;2、pmem方式,创建EGL OES纹理,将screen buffer绑定pmem申请的地址。

1、pixmap创建OES纹理

C++ 复制代码
///////创建native screen buffer
ret = screen_create_buffer(&pstCtx->buffer);
///////设置buffer属性,此处不在赘述
ret = screen_set_buffer_property_iv(pstCtx->buffer, SCREEN_PROPERTY_FORMAT, &fmt);
.......
///////创建pixmap
ret = screen_create_pixmap(&pstCtx->pixmap, pstCtx->pixmap_screen_ctx);
////// 将native screen buffer 绑定到pixmap上
screen_attach_pixmap_buffer(pstCtx->pixmap, pstCtx->buffer);
/////  创建EGLImage
imageKHR = (EGLImageKHR)eglCreateImageKHR(egl_display, NULL, EGL_NATIVE_PIXMAP_KHR, (EGLClientBUffer)pstCtx->pixmap, NULL);
///// 生成并绑定OES纹理
GL_CHECK(glGenTextures(1, &pstCtx->textures));
GL_CHECK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, pstCtx->textures));

GL_CHECK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES), imageKHR));

如此,外部可以同过对该纹理进行渲染,实现对buffer的更新

2、高通QCOM创建OES纹理

高通同时也提供了自己的EGLImage创建方式。该buffer通过绑定pmem申请的pmem handle,来创建EGLImage。根据高通的手册,调试并整理了代码如下:

C++ 复制代码
///// 创建pmem handle
pstCtx->mem_fd = nullptr;
pstCtx->mem_ptr = pmem_malloc_ext_v2(buffer_size,
                                            PMEM_CAMERA_ID,
                                            PMEM_FLAGS_SHMEM | PMEM_FLAGS_PHYS_NON_CONTIG | PMEM_FLAGS_CACHE_NONE,
                                            PMEM_ALIGNMENT_4K,
                                            0,
                                            &pstCtx->mem_fd,
                                            NULL);
////// 设置elg attributes
EGLint attr[] = {
                EGL_WIDTH,buffer_width,
                EGL_HEIGHT,buffer_height,
                EGL_IMAGE_FORMAT_QCOM,buffer_format,
                EGL_IMAGE_EXT_BUFFER_DESCRIPTOR_LOW_QCOM,(((intptr_t)(pstCtx->mem_fd))&0xFFFFFFFF),
                EGL_IMAGE_EXT_BUFFER_DESCRIPTOR_HIGH_QCOM,((intptr_t)(pstCtx->mem_fd)>>32),
                EGL_IMAGE_EXT_BUFFER_SIZE_QCOM,buffer_size,
                EGL_IMAGE_EXT_BUFFER_STRIDE_QCOM,buffer_stride,
                EGL_IMAGE_EXT_BUFFER_MEMORY_TYPE_QCOM,EGL_IMAGE_EXT_BUFFER_MEMORY_TYPE_PMEM_QCOM,
                EGL_IMAGE_EXT_BUFFER_PLANE0_OFFSET_QCOM, 0, 
                EGL_NONE};
///// 创建EGLImage
pstCtx->output_image = eglCreateImageKHR(pstCtx->egl_disp,pstCtx->egl_ctx,EGL_NEW_IMAGE_QCOM,(EGLClientBuffer)0,attr);

GL_CHECK(glGenTextures(1, &pstCtx->frameBufferTexture));
GL_CHECK(glBindTexture(GL_TEXTURE_2D, pstCtx->frameBufferTexture));
GL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL_CHECK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)pstCtx->output_image));

通过pmem来生成OES纹理,pmem生成的虚拟地址亦指向了该EGLImage,因此,可以将该虚拟地址绑定于screen的native buffer,如此便可以通过OpenGL渲染Framebuffer来操作该qnx screen buffer。

C++ 复制代码
//////创建screen native buffer,并设置属性
ret = screen_create_buffer(&pstCtx->screen_buf);
///// 其余常见属性此处不在赘述
......
///// 绑定虚拟地址
ret = screen_set_buffer_property_pv(pstCtx->screen_buf, SCREEN_PROPERTY_POINTER, &pstCtx->mem_ptr);
if (ret != 0) {
    LOGE( "screen_set_buffer_property_pv error, %s\n", strerror(errno));
    return -__LINE__;
}

至此,通过oepngl绘制buffer的准备工作完成

screen_blit,上屏

上述EGLImage生成之后,便可通过OpenGL绘制。渲染完成后,我们假如需要对这个buffer做一个类似滑动窗口的效果,可以使用screen_blit,操作如下

C++ 复制代码
screen_get_window_property_pv(screen_pix, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&screen_rbuf)
int hg[] = {SCREEN_BLIT_SOURCE_WIDTH, 100, 
            SCREEN_BLIT_SOURCE_HEIGHT, 100, 
            SCREEN_BLIT_DESTINATION_X, 10, 
            SCREEN_BLIT_DESTINATION_Y, 10, 
            SCREEN_BLIT_DESTINATION_WIDTH, 100, 
            SCREEN_BLIT_DESTINATION_HEIGHT, 100, 
            SCREEN_BLIT_TRANSPARENCY, SCREEN_TRANSPARENCY_SOURCE_OVER, 
            SCREEN_BLIT_END }; 
screen_blit(screen_ctx, screen_rbuf, screen_buf, hg);
/////// 渲染
screen_post_window(screen_win, screen_rbuf, 1, rect, 0);

数组hg即为需要搬运的buffer大小。具体属性含义:

C++ 复制代码
SCREEN_BLIT_SOURCE_WIDTH, 100,    /// 需要搬运的buffer水平位置起始点
SCREEN_BLIT_SOURCE_HEIGHT, 100,   /// 需要搬运buffer的垂直方向起始点
SCREEN_BLIT_DESTINATION_X, 10,    /// 目标buffer的水平方向起始点
SCREEN_BLIT_DESTINATION_Y, 10,     /// 目标buffer的垂直方向起始点
SCREEN_BLIT_DESTINATION_WIDTH, 100, /// 目标buffer的宽
SCREEN_BLIT_DESTINATION_HEIGHT, 100, /// 目标buffer的高
SCREEN_BLIT_TRANSPARENCY, SCREEN_TRANSPARENCY_SOURCE_OVER,   // buffer的aplha参数
SCREEN_BLIT_END           /// 结束标志位

因此,screen_blit的本质,即从source screen buffer中,指定一个窗口,将该窗口中的pixel全都搬运到另一个destination screen buffer中。还可以通过属性值的设置,对该buffer进行操作,如缩放、图像格式转换、透明度混合等。

总结

hardware render总体来说,最常使用的还是通过EGL形式进行渲染。不管是通过Khronos rendering API进行渲染,还是使用Screen Native API Blitting的形式进行渲染,本质上都是pixel的操作。本文主要在项目侧,参考QNX文档对hardware render方案的两种方式进行调试,总结,并做记录。后续将解释QNX侧混合渲染(Hybrid Rendering)。

相关推荐
aircrushin6 分钟前
端到端AI决策架构如何重塑实时协作体验?
前端·javascript·后端
苦瓜小生14 分钟前
【黑马点评学习笔记 | 实战篇 】| 6-Redis消息队列
redis·笔记·后端
yhole42 分钟前
springboot 修复 Spring Framework 特定条件下目录遍历漏洞(CVE-2024-38819)
spring boot·后端·spring
BingoGo1 小时前
Laravel 13 正式发布 使用 Laravel AI 无缝平滑升级
后端·php
l软件定制开发工作室1 小时前
Spring开发系列教程(34)——打包Spring Boot应用
java·spring boot·后端·spring·springboot
随风,奔跑1 小时前
Spring MVC
java·后端·spring
美团技术团队2 小时前
美团 BI 在指标平台和分析引擎上的探索和实践
后端
JimmtButler2 小时前
我用 Claude Code 给 Claude Code 做了一个 DevTools
后端·claude
Java水解2 小时前
Java 中实现多租户架构:数据隔离策略与实践指南
java·后端
Master_Azur2 小时前
Java面向对象之多态与重写
后端