QNX Screen-Hardware 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

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

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

配置EGL config

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

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

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、上屏

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纹理

///创建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。根据高通的手册,调试并整理了代码如下:

/ 创建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。

//创建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,操作如下

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大小。具体属性含义:

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)。

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值