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, NULL, 0);
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,
®istry_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, ¶ms_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, 0, 0);
wl_surface_damage(window->surface, 0, 0, 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] = {
{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 },
{ 1, 1, 0 }
};//RGBA
//控制shader中的gl position,产生渲染效果的移动
offset = (time_ms % iteration_ms) / (float) iteration_ms - 0.5;
glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo);
glViewport(0, 0, 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, 0, 4);
//一定要释放顶点资源
glDisableVertexAttribArray(window->gl.pos);
glDisableVertexAttribArray(window->gl.color);