技术关键词:mesa 调试、mesa调用栈
目录
前言
一、Linux图形软件栈的再讨论
二、glClear的调用栈分析
2.1 _mesa_开头的函数
2.2 st_开头的函数
2.3 pipe层字样的函数
2.4 GPU-specific层的函数
三、gl api 典型调用栈
总结
前文总结了mesa的主要模块和模块间的静态关系,本文从一个OpenGL应用程序的典型api入手,分析gl api 调用到用户态驱动后端的过程,进而总结出一个典型的调用栈。理解了这个典型调用栈,对后续任何一个API的调用过程分析,都是a piece of cake。
前文给出了一个顶层的linux图形栈的关系图。这里把原图中的xserver拿掉,把mesa剖开,把linux系统和硬件层加入,形成了如下linux的mesa图形软件栈。
下图的左侧调用栈是在gallium 架构之前采用的模式,右侧是gallium架构下的模式。可以看出gallium架构有比较明显的三个分层(夹心饼干):
1、state_tracker层:负责收集OpenGL状态;
2、GPU-specific层:厂商的用户态驱动核心部分;
3、OS WinSys层:操作系统对接层。
这里的图形调用栈的不妥之处是把libdrm放入了mesa中,实际上libdrm是linux系统提供的使用drm内核功能的一个用户态库,可以单独作为一层来看待(有时间再修改下该图,该图出处见图下的文字链接)。
图形栈中的mesa位置图
接下来以glClear的函数实现,来分析从应用程序到GPU-specific的调用栈。先把关键调用栈组织如下(省略了不相关的调用)。
glClear(GL_COLOR_BUFFER_BIT); _mesa_Clear(GL_COLOR_BUFFER_BIT); st_Clear(ctx, GL_COLOR_BUFFER_BIT); st->pipe->Clear(...); gpu_specific_Clear(...);
gl_打头的函数是OpenGL API,首先这些函数都是函数指针,这些函数挂载的函数就是mesa里以_mesa_打头的对应函数。这里有个调试技巧,把函数名gl_前缀换成_mesa_前缀即可。例如glGenBuffers的mesa入口函数是_mesa_GenBuffers。 _mesa_打头函数在前文的libmesa.a静态库中,而libmesa.a又被libgallium_dri.so包含,因此这些代码都是后端驱动的一部分,可以随意修改。
_mesa_打头的函数会调用st_开头的函数。这些st函数就是上图中的state tracker层。
pipe层,是接近于硬件的模块,GPU-specific 层是从pipe层继承的。可以把pipe层看成接口层,各家驱动实现该接口。
把pipe层的OpenGL状态转换为厂商支持的状态字段。该层一般会封装和处理硬件相关的状态,即硬件命令字。这些封装数据一般要由kmd交给硬件配置相关寄存器。
通过以上分析,这里给出一个典型的gl函数的调用栈。即:mesa层、st层、pipe层、gpu-spec层。
mesa典型调用栈
这里并没有给出winsys层,因为不同的OS有不同实现,这里关注linux。就是上图中的与libdrm交互的那一层,这层会使用ioctl将gpu-spec层组织的数据交给kmd,再由kmd把数据下发到gpu硬件。
本文从glClear的函数的调用栈分析了mesa实现OpenGL的一个典型流程。对于任何一个gl api可以套用该流程,熟悉mesa代码的调用脉络。