clients\simple-dmabuf-egl.c的主函数逻辑

uint32_t format = DRM_FORMAT_XRGB8888;
char const *drm_render_node = "/dev/dri/renderD128";
int window_size = 256;
    
display = create_display(drm_render_node, format, opts);
window = create_window(display, window_size, window_size, opts);
    
if (!window->wait_for_configure)
  redraw(window, NULL0);
while (running && ret != -1)
  ret = wl_display_dispatch(display->display);

create_display

create_display的主要逻辑,连接并注册display的监听器registry_listener,registry_listener中的registry_handle_global负责注册display支持的接口(wl_compositor/zwp_linux_dmabuf_v1/zwp_fullscreen_shell_v1/zwp_linux_explicit_synchronization_v1/weston_direct_display_v1)。

display->display = wl_display_connect(NULL);
display->registry = wl_display_get_registry(display->display);
wl_registry_add_listener(display->registry,
     &registry_listener, display);
wl_display_roundtrip(display->display);
wl_display_roundtrip(display->display);
//设置display中gbm相关的成员
display_set_up_gbm(display, drm_render_node);
//设置渲染器
display_set_up_egl(display);
//更新modifiers
display_update_supported_modifiers_for_egl(display);

display_set_up_gbm

1.设置gbm的渲染节点

display->gbm.drm_fd = open(drm_render_node, O_RDWR);

2.创建gbm设备

display->gbm.device = gbm_create_device(display->gbm.drm_fd);

libgbm作为一个用户态的api,它的作用是管理drm设备的GEM内存。gbm_create_device负责从DRM设备中创建gbm_device,gbm_device是DRM设备的抽象结构,管理所有的分配出来的buffer object(bo)。一个gbm_device创建出来的bo生命周期和该gbm_device绑定,gbm_device_destroy的时候才会释放gbm_device中的bo。

display_set_up_egl

display_set_up_egl初始化EGL环境,并且设置了diaplay。

static const EGLint context_attribs[] = {
  EGL_CONTEXT_CLIENT_VERSION, 2,
  EGL_NONE
 };
EGLint config_attribs[] = {
  EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  EGL_RED_SIZE, 1,
  EGL_GREEN_SIZE, 1,
  EGL_BLUE_SIZE, 1,
  EGL_ALPHA_SIZE, 1,
  EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  EGL_NONE
 };

//根据display->gbm.device获取egl.display
//配置egl环境
display->egl.display =
  weston_platform_get_egl_display(EGL_PLATFORM_GBM_KHR,
      display->gbm.device, NULL);
eglInitialize(display->egl.display, &major, &minor)
eglBindAPI(EGL_OPENGL_ES_API)//绑定opengles api
egl_extensions = eglQueryString(display->egl.display, EGL_EXTENSIONS)
//写入egl配置
eglChooseConfig(display->egl.display, config_attribs,
         &display->egl.conf, 1, &count);
//指定Display、Config,创建一个Rendering Context
//多个Rendering Context可以共享数据
display->egl.context = eglCreateContext(display->egl.display,
      display->egl.conf,
      EGL_NO_CONTEXT,
      context_attribs);
//将Rendering Context绑定到当前线程
eglMakeCurrent(display->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
         display->egl.context);

//设置display->egl中的函数
display->egl.query_dma_buf_modifiers =
   (void *) eglGetProcAddress("eglQueryDmaBufModifiersEXT");
display->egl.create_image =
  (void *) eglGetProcAddress("eglCreateImageKHR");
display->egl.destroy_image =
  (void *) eglGetProcAddress("eglDestroyImageKHR");
display->egl.image_target_texture_2d =
  (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
display->egl.create_sync =
   (void *) eglGetProcAddress("eglCreateSyncKHR");
display->egl.destroy_sync =
   (void *) eglGetProcAddress("eglDestroySyncKHR");
display->egl.client_wait_sync =
   (void *) eglGetProcAddress("eglClientWaitSyncKHR");
display->egl.dup_native_fence_fd =
   (void *) eglGetProcAddress("eglDupNativeFenceFDANDROID");
display->egl.wait_sync =
   (void *) eglGetProcAddress("eglWaitSyncKHR");

create_window

创建指定数量的dmabuf

//合成器创建surface供渲染器渲染
window->surface = wl_compositor_create_surface(display->compositor);
//设置xdg_surface的listener
window->xdg_surface =
   xdg_wm_base_get_xdg_surface(display->wm_base,
          window->surface);
xdg_surface_add_listener(window->xdg_surface,
      &xdg_surface_listener, window);
//设置xdg_toplevel的listener
window->xdg_toplevel =
   xdg_surface_get_toplevel(window->xdg_surface);
xdg_toplevel_add_listener(window->xdg_toplevel,
       &xdg_toplevel_listener, window);
xdg_toplevel_set_title(window->xdg_toplevel, "simple-dmabuf-egl");
window->wait_for_configure = true;
//commit surface
wl_surface_commit(window->surface);
window->surface_sync =
   zwp_linux_explicit_synchronization_v1_get_synchronization(
     display->explicit_sync, window->surface);
//3个dmabuf fd
for (i = 0; i < NUM_BUFFERS; ++i) {
  int j;
  for (j = 0; j < MAX_BUFFER_PLANES; ++j)
   window->buffers[i].dmabuf_fds[j] = -1;
 }
//创建dmabuf buffer
for (i = 0; i < NUM_BUFFERS; ++i) {
 ret = create_dmabuf_buffer(display, &window->buffers[i],
                            width, height, opts);   
window_set_up_gl(window)
//设置gl program shaders
//获取shader中的变量,以便在程序中修改着色器中变量实现不同的效果
//window->gl.pos = glGetAttribLocation(window->gl.program, "pos");
//window->gl.color = glGetAttribLocation(window->gl.program, "color");
//window->gl.offset_uniform =glGetUniformLocation(window->gl.program, "offset");

create_dmabuf_buffer

GBM(图形缓冲区管理)API 是 DRM KMS之上一个轻量化的API,它提供了一种为图形渲染分配缓冲区的机制。 GBM 旨在用作本机平台DRM 上的 EGL。 它创建的句柄可用于初始化 EGL 和创建渲染目标缓冲区。 GBM 也可以分配一个支持modifier的surface的方法,供 Wayland 合成器和 X11渲染。

可用的modifiers(DRM_FORMAT_MOD_*)位于drm_fourcc.h中。(Arm Framebuffer Compression (AFBC) modifiers/Vivante framebuffer modifiers/Format Modifiers/)。每个GPU厂商都有自己对应的modifiers(修饰符)用于修改专有驱动中的属性。对于跨进程、API和设备,修饰符可以为每个vendor枚举一组合理大小的平铺格式(tile format),不包括vendor内部使用的平铺格式。修饰符的作用是命名内存布局,它们命名了一小组vendor首选的图形共享布局。

Linux 中的许多 API 使用修饰符来协商和指定共享图像的内存布局。例如,Wayland 合成器和 Wayland 客户端可以通过 Wayland 协议 zwp_linux_dmabuf_v1 ,来协商共享 wl_buffer 的vendor特定平铺格式。客户端可以通过GBM为 wl_buffer 分配底层内存,为 gbm_bo_create_with_modifiers 提供选择的修饰符。

第一步,gbm_bo_create_with_modifiers创建一个支持modifiers的buffer object。第二步,获取buffer的plane个数、获取每个plane的handler、根据handler拿到这个plane的dmabuf_fd、获取buffer的stride和offset。第三步,将buffer信息填充进params参数集合。第四步,zwp_linux_buffer_params_v1_create根据dmabuf的params创建wl_buffer。

//创建一个支持modifiers类型的gbm buffer
buffer->bo = gbm_bo_create_with_modifiers(display->gbm.device,
         buffer->width,
         buffer->height,
         buffer->format,
         display->modifiers,
         display->modifiers_count);
if (buffer->bo)
 buffer->modifier = gbm_bo_get_modifier(buffer->bo);
//获取buffer的plane count
buffer->plane_count = gbm_bo_get_plane_count(buffer->bo);
 for (i = 0; i < buffer->plane_count; ++i) {
  int ret;
  union gbm_bo_handle handle;
  //获取特定plane的gbm_bo handle
  handle = gbm_bo_get_handle_for_plane(buffer->bo, i);
  //拿到这个plane对应的dmabuf_fd
  ret = drmPrimeHandleToFD(display->gbm.drm_fd, handle.u32, 0,
      &buffer->dmabuf_fds[i]);
    //获取stride和offsets
    //stride每行像素所占的空间,并不是图形的宽度,而是内存中所占的空间
  buffer->strides[i] = gbm_bo_get_stride_for_plane(buffer->bo, i);
  buffer->offsets[i] = gbm_bo_get_offset(buffer->bo, i);
 }
//此临时对象用于将多个 dmabuf 句柄收集到单个批处理中以创建 wl_buffer。 
//它只能使用一次,并且应该在收到“创建”或“失败”事件后销毁。
params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
for (i = 0; i < buffer->plane_count; ++i) {
     //填充dmabuf的参数
     //此请求向此params的集合中添加一个dmabuf信息
  zwp_linux_buffer_params_v1_add(params,
            buffer->dmabuf_fds[i],
            i,
            buffer->offsets[i],
            buffer->strides[i],
            buffer->modifier >> 32,
            buffer->modifier & 0xffffffff);
 }

//添加创建成功/失败的监听
zwp_linux_buffer_params_v1_add_listener(params, &params_listener, buffer);
//从添加的dmabuf缓冲区创建wl_buffer。 
//如果dmabuf共享成功,wl_buffer不会立即创建,而是通过 'created' 事件返回。
zwp_linux_buffer_params_v1_create(params,
        buffer->width,
        buffer->height,
        buffer->format,
        flags);
//从buffer创建fbo帧缓冲对象
create_fbo_for_buffer(display, buffer)

create_fbo_for_buffer

根据wl_buffer的属性创建buffer->egl_image,然后渲染2d纹理,生成帧缓冲

//添加属性
attribs[atti++] = EGL_WIDTH;
attribs[atti++] = buffer->width;
attribs[atti++] = EGL_HEIGHT;
attribs[atti++] = buffer->height;
attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
attribs[atti++] = buffer->format;
//宏定义,用于每个plane属性的添加
#define ADD_PLANE_ATTRIBS(plane_idx) { \
 attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _FD_EXT; \
 attribs[atti++] = buffer->dmabuf_fds[plane_idx]; \
 attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _OFFSET_EXT; \
 attribs[atti++] = (int) buffer->offsets[plane_idx]; \
 attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _PITCH_EXT; \
 attribs[atti++] = (int) buffer->strides[plane_idx]; \
 if (display->egl.has_dma_buf_import_modifiers) { \
  attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_LO_EXT; \
  attribs[atti++] = buffer->modifier & 0xFFFFFFFF; \
  attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_HI_EXT; \
  attribs[atti++] = buffer->modifier >> 32; \
 } \
 }

 if (buffer->plane_count > 0)
  ADD_PLANE_ATTRIBS(0);
 if (buffer->plane_count > 1)
  ADD_PLANE_ATTRIBS(1);
//eglCreateImageKHR根据attribs属性集合创建EGLImageKHR对象
buffer->egl_image = display->egl.create_image(display->egl.display,
            EGL_NO_CONTEXT,
            EGL_LINUX_DMA_BUF_EXT,
            NULL, attribs);
eglMakeCurrent(display->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
   display->egl.context);
//生成一个空白纹理buffer->gl_texture
glGenTextures(1, &buffer->gl_texture);
//buffer->gl_texture绑定为2D纹理
glBindTexture(GL_TEXTURE_2D, buffer->gl_texture);
//设置纹理属性
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//从EGLImageKHR图像对象生成2D纹理
display->egl.image_target_texture_2d(GL_TEXTURE_2D, buffer->egl_image);
//生成绑定帧缓冲
glGenFramebuffers(1, &buffer->gl_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo);
//从EGLImageKHR图像对象生成2D纹理,此时buffer->gl_texture不再是空白。
//将buffer->gl_texture链接到帧缓冲
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
          GL_TEXTURE_2D, buffer->gl_texture, 0);

redraw

//切换到window的下一个buffer
buffer = window_next_buffer(window);
//渲染
render(window, buffer);
//四步提交surface
wl_surface_attach(window->surface, buffer->buffer, 00);
wl_surface_damage(window->surface, 00, window->width, window->height);
window->callback = wl_surface_frame(window->surface);
wl_surface_commit(window->surface);

渲染效果控制

着色器和绘制函数。

static const char *vert_shader_text =
	"uniform float offset;\n"
	"attribute vec4 pos;\n"
	"attribute vec4 color;\n"
	"varying vec4 v_color;\n"
	"void main() {\n"
	"  gl_Position = pos + vec4(offset, offset, 0.0, 0.0);\n"
	"  v_color = color;\n"
	"}\n";

static const char *frag_shader_text =
“precision mediump float;\n”
“varying vec4 v_color;\n”
“void main() {\n”
" gl_FragColor = v_color;\n"
“}\n”;

static const GLfloat verts[4][2] = {
  { -0.5-0.5 },
  { -0.5,  0.5 },
  {  0.5-0.5 },
  {  0.5,  0.5 }
 };
static const GLfloat colors[4][3] = {
  { 100 },
  { 010 },
  { 001 },
  { 110 }
 };//RGBA
//控制shader中的gl position,产生渲染效果的移动
offset = (time_ms % iteration_ms) / (float) iteration_ms - 0.5;
glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo);
glViewport(00, window->width, window->height);//渲染窗口的大小
//传顶点位置给着色器
glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
//传color给着色器
glVertexAttribPointer(window->gl.color, 3, GL_FLOAT, GL_FALSE, 0, colors);
glEnableVertexAttribArray(window->gl.pos);
glEnableVertexAttribArray(window->gl.color);
//绘制FBO的四个顶点
glDrawArrays(GL_TRIANGLE_STRIP, 04);
//一定要释放顶点资源
glDisableVertexAttribArray(window->gl.pos);
glDisableVertexAttribArray(window->gl.color);