display: mesa: eglapi接口:getdisplay&initialize_maze的专栏-CSDN博客
最近项目上遇到一个问题:某一帧eglswapbuffer事件的时间格外长,达到300ms以上的情况
基于这个问题。因为是wayland/weston的体系。所以mesa支持的eglswapbuffer有两个部分:
1.client端的eglswapbuffer,这部分是有相关的wayland协议支持的。
2.weston内部gl-renderer模块的eglswapbuffer。
opensource/mesa/src/egl/drivers/dri2$ grep -rn "swap_buffers ="
platform_x11.c:1172: .swap_buffers = dri2_x11_swap_buffers,
platform_x11.c:1186: .swap_buffers = dri2_x11_swap_buffers,
platform_wayland.c:1379: .swap_buffers = dri2_wl_swap_buffers,
platform_wayland.c:1995: .swap_buffers = dri2_wl_swrast_swap_buffers,
platform_drm.c:675: .swap_buffers = dri2_drm_swap_buffers,
platform_x11_dri3.c:507: .swap_buffers = dri3_swap_buffers,
platform_android.c:1284: .swap_buffers = droid_swap_buffers,
weston的client是platform_wayland实现;weston本身内部是platform_drm的实现。
需要一些概念性的东西:display:weston:weston-simple-egl_maze的专栏-CSDN博客
Display(EGLDisplay) 是对实际显示设备的抽象
Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer
Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息
————————————————
Display
Display 是 OpenGL ES 应用运行平台的物理显示器的抽象
Rendering Context
OpenGL ES 状态机,由EGL创建、并与Surface关联;Rendering Context含有OpenGL ES的客户端和服务器端状态;Rendering Context存在于客户端的地址空间。每个线程在同一时刻只能使用1个Rendering Context,每个Rendering Context在同一时刻只能被1个线程使用
Surface
OpenGL ES绘图的“画布”。EGL/OpenGL ES有3种Surface:
window - 用于屏上(onscreen)渲染
pbuffer - 用于离屏(offscreen)渲染
pixmap - 离屏渲染,但本地渲染API也可以访问
OpenGL支持2种渲染模式:
back buffered - 绘图的color buffer由EGL创建和管理,绘图完成后,EGL将后台color buffer交换/拷贝到窗口上
single buffered - 其color buffer为本地Pixmap,EGL能使用但不维护。OpenGL ES绘图后,像素直接在Surface上呈现
window和pbuffer Surface是back buffered模式,pixmap Surface是single buffered模式
Config
用于创建Surface,包含了Surface的各个buffer的创建参数
color buffer
depth buffer
multisample buffer
stencil buffer
https://www.pianshen.com/article/6484561270/
先从simple-egl里面的eglSwapBuffers:
mesa/src/egl/main/eglapi.c:
EGLBoolean EGLAPIENTRY
eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
{
_EGLContext *ctx = _eglGetCurrentContext();
_EGLDisplay *disp = _eglLockDisplay(dpy);
_EGLSurface *surf = _eglLookupSurface(surface, disp);
//先获取三大项:eglcontext, egldisplay, eglsurface
...
if (surf->Type != EGL_WINDOW_BIT)
RETURN_EGL_EVAL(disp, EGL_TRUE);
//不是window类型的surface直接退出,上面提到的屏上渲染,(没有双buffer,你调这个函数干啥)
...
ret = disp->Driver->SwapBuffers(disp, surf);
//回调
}
这个disp->Driver->SwapBuffers(disp, surf);
对应的driver为_EGLDriver:
mesa/src/egl/drviers/dri2/egl_dri2.c
static EGLBoolean
dri2_swap_buffers(_EGLDisplay *disp, _EGLSurface *surf)
{
struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
__DRIdrawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf);
_EGLContext *ctx = _eglGetCurrentContext();
//换成了dri2_egl_display,__DRIdrawable,_EGLContext。
if (ctx && surf)
dri2_surf_update_fence_fd(ctx, disp, surf);//这一部分是fence相关,后续要细挖,学习一下。
ret = dri2_dpy->vtbl->swap_buffers(disp, surf);
/* SwapBuffers marks the end of the frame; reset the damage region for
* use again next time.
*/
if (ret && dri2_dpy->buffer_damage && dri2_dpy->buffer_damage->set_damage_region)
dri2_dpy->buffer_damage->set_damage_region(dri_drawable, 0, NULL);
//重设damage区域。·
return ret;
}
关键在于ret = dri2_dpy->vtbl->swap_buffers(disp, surf);
不同的平台实现不一样,这里先说支持wayland这一种:src/egl/drivers/dri2/platform_wayland.c
static EGLBoolean
dri2_wl_swap_buffers(_EGLDisplay *disp, _EGLSurface *draw)
{
return dri2_wl_swap_buffers_with_damage(disp, draw, NULL, 0);
}
//下面就涉及了很多wayland的知识点。
static EGLBoolean
dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp,
_EGLSurface *draw,
const EGLint *rects,
EGLint n_rects)
//先获取dri2_egl_display 和 dri2_egl_surface.
...
while (dri2_surf->throttle_callback != NULL)
if (wl_display_dispatch_queue(dri2_dpy->wl_dpy,
dri2_surf->wl_queue) == -1)
return -1;
//这一段的意思是,如果callback不是空的,也就是上一帧还没有收到weston这边传来的callback done事件,那就继续从socket上读event给到client,也就是这一帧在这里忙等weston发event过来。
...
for (int i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++)
if (dri2_surf->color_buffers[i].age > 0)
dri2_surf->color_buffers[i].age++;
//age++目前不清楚干啥的。
...
/* Make sure we have a back buffer in case we're swapping without ever
* rendering. */
if (update_buffers_if_needed(dri2_surf) < 0)
return _eglError(EGL_BAD_ALLOC, "dri2_swap_buffers");
//没有back-buffer的话,就不会sw