QNX SCREEN渲染

简介

QNX屏幕图形子系统基于客户机/服务器模型,其中应用程序是向服务器(屏幕)请求图形服务的客户机。它包括一个复合窗口系统作为这些服务之一,这意味着所有的应用程序呈现都在屏幕外缓冲区上执行,然后可以稍后用于更新显示。

Screen支持硬件渲染加速和合成,也支持软件渲染。兼容显示控制器,2D\3D加速器和输入设备。除了事件之外,所有Screen API组件都必须与上下文关联,通过Context,可以实现对Screen各个模块进行操作,具体模块如下:

本文主要针对QNX侧最为基本的SoftWare上屏的形式做详细介绍

SoftWare Rendering(软件渲染)

按照官方手册,software rendering主要可以分为四个步骤:

  1. 创建rendering的目标
  2. 创建需要rendering的buffer
  3. 在buffer上绘制内容
  4. post上屏

根据自身项目,本文在这里对上述四个流程做一个展开

1、创建Rendering的目标

创建Rendering的目标,即QNX提供的Window、Pixmap、Stream。Rendering可以是Rendering + Display(即on-screen),也可以仅仅Rendering(即 off-screen),可以是多buffer,也可以是单个buffer。Screen API支持的Rendering Target如下:

Rendering Target Output destination Buffers
windows On-screen Multiple
pixmap Off-screen Multiple
stream Off-screen Single

本文以on-screen渲染为例,首先是创建Screen Context和rendering目标Screen Windows

js 复制代码
ret = screen_create_context(&pstCtx->screen_ctx, SCREEN_APPLICATION_CONTEXT | SCREEN_BUFFER_PROVIDER_CONTEXT);
ret = screen_create_window(&pstCtx->screen_win, pstCtx->screen_ctx);

创建Context时,需要设置创建的Context类型,主要表示了Context支持的某些操作,Context类型如下:

js 复制代码
enum { 
    SCREEN_APPLICATION_CONTEXT = 0,               /// 允许进程创建自己的窗口并控制某些窗口属性
    SCREEN_WINDOW_MANAGER_CONTEXT = (1 << 0),     /// 允许进程创建的win拥有修改其他窗口的权力
    SCREEN_INPUT_PROVIDER_CONTEXT = (1 << 1),     /// 允许进程创建的win可以向其他窗口发送event
    SCREEN_POWER_MANAGER_CONTEXT = (1 << 2),      /// 允许进程创建的win可以访问diplay power
    SCREEN_DISPLAY_MANAGER_CONTEXT = (1 << 3),    /// 允许进程创建的win可以修改系统的display属性
    SCREEN_INPUT_MANAGER_CONTEXT = (1 << 4),      /// 允许进程创建的win可以收到所有的session
    SCREEN_BUFFER_PROVIDER_CONTEXT = (1 << 5)     /// 允许进程创建的win可以attach buffer
};

创建Context和Windows之后,对创建的windows窗口设置属性。属性较多,本文不在此详细解释每一个属性的含义,在此列出项目中用到的属性设置

js 复制代码
    SCREEN_PROPERTY_USAGE                           //// 指定该rendering target的预期用途
    SCREEN_PROPERTY_FORMAT                          //// 指定该rendering target的颜色格式
    SCREEN_PROPERTY_SWAP_INTERVAL                   //// 指定该rendering target post的周期
    SCREEN_PROPERTY_SIZE                            //// 指定该rendering target的尺寸
    SCREEN_PROPERTY_POSITION                        //// 指定该rendering target的位置
    SCREEN_PROPERTY_ZORDER                          //// 指定该rendering target的zorder
    SCREEN_PROPERTY_ID_STRING                       //// 指定该rendering target的窗口名
    SCREEN_PROPERTY_PIPELINE                        //// 指定该rendering target的display管道
    SCREEN_PROPERTY_DISPLAY_COUNT                   //// 用于获取系统支持的外部硬件display数量
    SCREEN_PROPERTY_DISPLAYS                        //// 指定该rendering target的display

具体参考代码如下

js 复制代码
    ret = screen_set_window_property_iv(pstCtx->screen_win, SCREEN_PROPERTY_USAGE, &pstCtx->usage);
    ret = screen_set_window_property_iv(pstCtx->screen_win, SCREEN_PROPERTY_FORMAT, &pstCtx->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);
    ret = screen_set_context_property_iv(pstCtx->screen_ctx, SCREEN_PROPERTY_PIPELINE, &pstCtx->pipeline);
    //////首先获取Context下支持的硬件display数量
    ret = screen_get_context_property_iv(pstCtx->screen_ctx, SCREEN_PROPERTY_DISPLAY_COUNT, &pstCtx->ndisplays);
    pstCtx->screen_dpy = (screen_display_t *)calloc(pstCtx->ndisplays, sizeof(screen_display_t));
    ///// 获取所有的display指针
    ret = screen_get_context_property_pv(pstCtx->screen_ctx, SCREEN_PROPERTY_DISPLAYS, (void **)pstCtx->screen_dpy);
    for (i = 0; i < pstCtx->ndisplays; ++i) {
        int display_id = 0;
        int size[2] = {0};
        ///////获取display的下标以及对应的size
        ret = screen_get_display_property_iv(pstCtx->screen_dpy[i], SCREEN_PROPERTY_ID, &display_id);
        ret = screen_get_display_property_iv(pstCtx->screen_dpy[i], SCREEN_PROPERTY_SIZE, size);
    }
    //////// 根据项目需要,设置对应的display指针
    pstCtx->target_disp_idx = pstCtx->ndisplays - 1;
    pstCtx->target_disp = pstCtx->screen_dpy[pstCtx->target_disp_idx]; 
    ret = screen_set_window_property_pv(pstCtx->screen_win, SCREEN_PROPERTY_DISPLAY, (void **)&pstCtx->target_disp);

2、创建Rendering buffer

设置完Rendering Target之后,便是创建需要渲染的Rendering buffer,attach到对应Rendering Target。亦可直接索引screen windows 中的renderbuffer。 buffer的创建同上述context和screen win的创建类似,screen_create_buffer后,设置对应的buffer属性,并将该buffer attach到对应的rendering target上,即可实现自定义buffer。

js 复制代码
/////////////////////// 直接获取索引的方式///////////////////////////
ret = screen_get_window_property_pv(pstCtx->screen_win, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&pstCtx->screen_buf);
/////////////////////自己创建buffer的方式///////////////////////////
ret = screen_create_buffer(&pstCtx->screen_buf);
ret = screen_set_buffer_property_iv(pstCtx->screen_buf, SCREEN_PROPERTY_FORMAT, &fmt);
ret = screen_set_buffer_property_iv(pstCtx->screen_buf, SCREEN_PROPERTY_BUFFER_SIZE, size);
ret = screen_set_buffer_property_iv(pstCtx->screen_buf, SCREEN_PROPERTY_SIZE, &msz);
ret = screen_set_buffer_property_iv(pstCtx->screen_buf, SCREEN_PROPERTY_STRIDE, &stride);
ret = screen_set_buffer_property_iv(pstCtx->screen_buf, SCREEN_PROPERTY_PLANAR_OFFSETS, offset);

项目中为了实现OpenGL的OES纹理直接绘制到screen buffer上,还设置了buffer的虚拟地址

js 复制代码
ret = screen_set_buffer_property_pv(pstCtx->screen_buf, SCREEN_PROPERTY_POINTER, &pstCtx->mem_ptr);

设置完buffer的属性后,attach到对应的rendering buffer上

js 复制代码
ret = screen_attach_window_buffers(pstCtx->screen_win, 1, &pstCtx->screen_buf);

3、绘制buffer

绘制buffer的方式,可以是对索引到的renderbuffer地址进行赋值,也可以使用OpenGL的形式进行绘制。高通针对OES纹理,提供了一种pmem绑定的形式,可以通过pmem创建的mem_ptr,创建ImageKHR,实现buffer的绘制。代码如下:

js 复制代码
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);
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};
pstCtx->output_image = eglCreateImageKHR(pstCtx->egl_disp,pstCtx->egl_ctx,EGL_NEW_IMAGE_QCOM,(EGLClientBuffer)0,attr);

也可以通过对索引到的render buffer进行辅助操作,如下:

js 复制代码
ret = screen_get_buffer_property_pv(screen_buf, SCREEN_PROPERTY_POINTER, (void **)&&pstCtx->ptr);
ret = screen_get_buffer_property_iv(screen_buf, SCREEN_PROPERTY_STRIDE, &pstCtx->stride);
memset(ptr, 0x00, buffer[0]*buffer[1]*4);//字节
// line 0 to 1/4:   0xFFFF0000    
// line 1/4 to 2/4: 0xFF00FF00    
// line 2/4 to 3/4: 0x000000FF    
// line 3/4 to 1:   0x660000FF    
int one_quarter = buffer[1]/4;   
for(int i = 0; i < buffer[1]; ++i, ptr+=(stride/4))
{        
    for(int j = 0; j < buffer[0]; j++ )
    {           
        if (i < one_quarter)                
            ptr[j] = 0xFFFF0000;            
        else if (i < one_quarter*2)                
            ptr[j] = 0xFF00FF00;            
        else if (i < one_quarter*3)               
            ptr[j] = 0x000000FF;            
        else               
            ptr[j] = 0x660000FF;        
    }    
}

4、post上屏

最终,将绘制后的render buffer上屏。

js 复制代码
ret = screen_post_window(pstCtx->screen_win, pstCtx->screen_buf, 0, NULL, 0);

若使用OpenGL形式绘制的buffer,需要在post之前,bindframebuffer

js 复制代码
/// 先绑定framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, pstCtx->frameBuffer);
/// 获取windows的renderbuffer
ret = screen_get_window_property_pv(pstCtx->screen_win, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&pstCtx->screen_renderbuf);
    if(ret != 0){
        LOGE( "screen_get_window_property_pv SCREEN_PROPERTY_RENDER_BUFFERS error:%s\n", strerror(errno));
        return 0;
    }
/// post上屏
ret = screen_post_window(pstCtx->screen_win, pstCtx->screen_renderbuf, 0, nullptr, 0);

小结

software 渲染的形式是QNX Screen最为基础的渲染方式,但由于文档提供的代码提供的可参考性较差,因此特意撰写此文,为自己在qnx侧调试做记录。后续还会更新QNX侧EGL渲染、Hybrid渲染。

相关推荐
2401_857622661 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589361 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没3 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch3 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
杨哥带你写代码4 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
AskHarries5 小时前
读《show your work》的一点感悟
后端
A尘埃5 小时前
SpringBoot的数据访问
java·spring boot·后端
yang-23075 小时前
端口冲突的解决方案以及SpringBoot自动检测可用端口demo
java·spring boot·后端
Marst Code5 小时前
(Django)初步使用
后端·python·django
代码之光_19805 小时前
SpringBoot校园资料分享平台:设计与实现
java·spring boot·后端