OpenGL & DIR

 Mesa简介-CSDN博客

Mesa, also called  Mesa3D and   The Mesa 3D Graphics Library, is an   open source software implementation of   OpenGL,  Vulkan, and other   graphics   API specifications. Mesa translates these specifications to vendor-specific graphics hardware drivers.

Mesa 实际上是一个库,它实现了多种图形 API 规范,这其中就包括了最著名的 OpenGL。Mesa 底层直接使用图形硬件驱动。

Mesa implements a translation layer between a graphics API such as OpenGL and the graphics hardware drivers in the operating system kernel.

上面这句话基本上点到了 Mesa 的实质。Mesa 实际上就是一个转换层,它提供了图形 API(比如:OpenGL)到图形硬件(比如:显卡 GPU)驱动之间的一个转换。

它诞生之时,是在 CPU 上进行所有渲染的,后续又发展为通过 display server 非直接的渲染。但其内部架构在设计上支持使用显卡硬件 3D 渲染的。

可以说,在适配 DRI 架构时,正式奠定了 Mesa 的位置:

With adapting to DRI, the Mesa library finally took over the role of the front end component of a full scale OpenGL framework with varying backend components that could offer different degrees of 3D hardware support while not dropping the full software rendering capability.

 Mesa & Gullium3D 介绍 - 掘金

Mesa (或Mesa3D)是一个 OpenGL/Vulkan 的实现,以及为所有开源图形驱动提供各种 GL 的入口点, 它是一个项目的名字。由它编译出来的库是下面这些:

OpenGL & DIR_第1张图片

或者这些文件:

OpenGL & DIR_第2张图片

其中需要注意的是这几个文件:libGL.so,libEGL.so,libGLESv2.so,根据名字很显然,他们分别实现了对应的API。

Mesa有两个作用:

  1. 对接各种GPU硬件,将应用层对GL API的调用转换到对硬件GPU的调用上;
  2. 各种 GL API 的纯软实现,当没有可用的硬件时,它可以提供传软件的 GL API 的实现;

作者:keyou
链接:https://juejin.cn/post/6844903841096269837
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

mesa解析1 线程本地存储 libGL.so原理_mesa libglapi.so-CSDN博客

TLS机制:

openGL维护了一个状态机,或者叫上下文;gl开头的api函数,如glUniform4fv,会修改这个上下文

系统中,如果有多个基于openGL的程序在运行,则每个程序有一个独立的上下文。每个程序调用gl api都作用于自己的上下文。

但是,libGL.so是一个共享库,每个基于openGL的程序都将其映射到自己的地址空间,共享库的代码段在系统运行期间是不变的。要同一份二进制程序,在不同程序中访问到各程序自己的gl上下文,线程本地存储(TLS / thread local storage)机制就是为了解决这个问题

每个线程有独立的地址空间,并且其中有一块用作线程本地存储(TLS)。操作系统在上下文切换时,会将TLS指针放进一个特殊的寄存器;共享库读取这个寄存器,就可以得到该线程的TLS指针,而上下文在TLS段中具体什么位置,是编译器决定的。

也就是说,约定所有程序的上下文指针都存在TLS段中特定位置,则libGL.so中读取TLS指针,加上特定偏移,就可以得到gl上下文


libGL.so基本原理

应用程序调用gl开头的函数,这些函数的定义都在libGL.so中

每个api函数的入口实现是统一的:读取TLS指针,从TLS段中读取分发表指针,从分发表中特定偏移处读取实际的实现函数的指针,通过这个函数指针进行调用

也就是说,libGL.so这个共享库是一个空壳,一个中介。在使用这个库提供的gl api函数之前,应用程序要先创建上下文和分发表,并将上下文和分发表的指针写进TLS中

上下文和分发表的定义在驱动中,如r600_dri.so,api函数的实际实现,也都在这个驱动中。

应用程序通过egl的接口,来创建gl上下文,设定上下文(将上下文和分发表注册到TLS中),之后,应用程序就可以调用libGL.so中的gl api函数了


细节上,上下文和分发表的注册功能,由libglapi.so完成,所以,libelg是调用libglapi.so中的函数,来设置上下文和分发表,libGL.so是调用libglapi.so中的函数(不过是内联调用),来获取分发表

egl以dlopen的方式,打开驱动,如r600_dri.so,从驱动中以dlsym的方式,获取驱动函数表,调用驱动函数,来创建上下文。


正向的说:egl以dlopen方式打开驱动,获取并调用驱动中的函数,来创建上下文和分发表,用glapi的接口,设置上下文和分发表到TLS;然后用户调用gl的api函数时,libGL.so中api函数会从TLS获取分发表,从分发表读取驱动函数并调用,以实现该api的功能,如glUniform4fv
————————————————
版权声明:本文为CSDN博主「xueshuangbai」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xueshuangbai/article/details/47024505

MESA : GL Dispatch - 知乎

MESA : GL Dispatch

OpenGL函数的分发机制相当复杂,这个文章试图解释其中的一些议题,并且给读者介绍Mesa的分发层实现。

1 GL Dispatch的复杂之处

每一个GL应用至少拥有一个名叫GL context的对象,这个对象是所有GL function隐含的内置参数,储存了所有应用当前状态下的所有GL状态。例如纹理、VBO之类的上下文信息都存放在context对象中。一个应用可以包含多个context,当前时刻哪个context被使用是由一个和窗口相关的函数去指定的(glXMakeContextCurrent)。

例如在使用GLX实现OpenGL With X-Windows的时候,每一个GL的函数指针都是通过glXGetProcAddress函数获得的,获得的函数指针都是contex independent 的,无论是哪个上下文对象被激活,都是相同的函数被调用。

这是GL Dispatch复杂的原因之一!

一个应用包含两个GL contexts。一个context是直接渲染上下文,其中函数调用直接路由到应用程序地址空间中加载的驱动程序(One context is a direct rendering context where function calls are routed directly to a driver loaded within the application’s address space),另外一个context间接渲染上下文,其中函数调用将会被转化为GLX协议并且发送给服务端(The other context is an indirect rendering context where function calls are converted to GLX protocol and sent to a server)。在这两种情况下,类似glVertex3fv这样的gl函数都需要做出相同的正确的事情。

高度优化的驱动或者GLX协议实现会希望更具当前的状态更改GL函数的行为,例如,glFogCoordf函数会根据fog是否enable而产生不同的影响。

在多线程的环境中,每一个线程包含不同的GL context是完全可能的。这个意味着可怜的glVertex3fv函数必须要去知道哪个contex是当前线程的contex,函数在什么时候被调用。

2 Mesa实现的概览

Mesa 每个线程使用两个指针,第一个指针记录了当前线程使用的context地址,第二个指针记录了一个与contex对应的dispatch table地址。dispatch table储存了一个真实实现此功能的函数指针。每个时刻,一个新的context被创建在一个线程中,这两个指针都会被更新。

其实 glVertex3fv这样的函数实现在概念上很简单:

  1. 获取当前的dispatch table指针
  2. 从dispatch table指针中获取确实实现glVertex3fv的函数指针
  3. 调用真实的函数指针

这个函数可以被声明的非常简单,甚至就几行C代码。见src/mesa/glapi/glapitemp.h可以看到类似的代码。

void glVertex3f(GLfloat x, GLfloat y, GLfloat z)
{
    const struct _glapi_table * const dispatch = GET_DISPATCH();

    (*dispatch->Vertex3f)(x, y, z);
}

但是上述这个简单的实现存在问题,他给每次调用此GL函数带来很大的开销。

在多线程的环境里,一个原始的GET_DISPATCH()函数会包含一个调用_glapi_get_dispatch()or_glapi_tls_Dispatch 来优化开销。

3 优化

近一些年产生了很多优化的方法来减少因为GL dispatch带来的性能损失,这个章节将会描述这些优化方法,列出了每种优化的好处,以及在什么时候可以用此优化在什么时候不能。

3.1 ELF TLS

从2.4.20 Linux Kernel以来,每个线程都会被分配一个全局的储存区域,可以使用GCC的一些扩展(ELF TLS)指令使得某些变量存放在此区域。如果可以把dispatch table指针存放在这个区域,那么调用pthread_getspecific 函数和_glapi_Dispatch函数就可以避免了,这些函数会产生大量的软件开销。

mesa不支持2.4.20以前的linux内核,所以理论上完全支持ELF TLS。

dispatch table 指针被存放在一个新的变量中 名为 _glapi_tls_Dispatch。使用一个新的变量名意味着libGL可以实现两种不同的接口,这使得libGL可以使用任意一个接口的直接渲染驱动程序。一旦正确声明了指针,GET_DISPACH 就变成了一个简单的变量引用。

extern __THREAD_INITIAL_EXEC struct _glapi_table *_glapi_tls_Dispatch;

#define GET_DISPATCH() _glapi_tls_Dispatch

https://blog.csdn.net/qq_30599505/article/details/126494369

DRI是包含现代 Linux 图形堆栈的框架,它允许非特权用户空间程序向图形硬件发出命令,而不会与其他程序发生冲突。DRI 的主要用途是为 OpenGL 实现的 Mesa 提供硬件加速,也适用于为没有图形界面的framebuffer console提供OpenGL加速。
DRI实现分散在X Server及其关联的clients库、Mesa 3D和DRM中。所有的源代码都是开源免费的。

概述

在经典的 X Window 系统架构中,X Server 是唯一能够独占访问图形硬件的进程,因此也是在framebuffer进行实际渲染的进程。X clients所做的就是与 X Server通信以分派渲染命令。这些命令是独立于硬件的,这意味着 X11 协议提供了一个提取图形设备的 API,因此 X clients不需要担心底层硬件的细节。任何依赖硬件的代码都位于设备相关 X 中,X Server 中管理video card或显卡驱动的部分,通常也称为视频或图形驱动程序。
3D渲染的兴起把这种框架的缺点暴露了出来。3D图形应用程序倾向于生成大量的命令和数据,这些命令和数据都必须发送到X Server进行渲染。随着 X client和 X Server 之间的进程间通信 (IPC) 数量的增加,3D 渲染性能受到了影响。X 驱动程序开发人员为了利用最新显卡的 3D 硬件功能,需要新的无 IPC 架构。Xclients应该直接访问图形硬件,而不是依赖第三方进程来执行此操作,从而节省所有 IPC 开销,这种方法称为“direct rendering”。DRI最初是为了允许任何X clients使用这种“direct rendering”方法执行3D渲染。

架构

OpenGL & DIR_第3张图片

DRI基础架构包含3个主要组件:

  • DRI client是一个可以直接执行’direct rendering’的X client - 需要一个硬件特定的驱动程序来管理video card或者显卡驱动以便可以在其上可以渲染。这些 DRI 驱动程序通常作为客户端动态链接共享库提供。
  • X Server提供了一个X11扩展协议(DRI扩展),DRI 客户端使用该扩展与窗口系统和 DDX 驱动程序进行协调。作为 DDX 驱动程序的一部分,X Server进程也同样动态链接到与DRI client相同的DRI驱动程序,但是会使用GLX扩展为X clients提供硬件3D加速渲染以进行间接渲染(如远程X clients不能直接使用渲染)。对于2D渲染,DDX 驱动程序还必须考虑使用相同图形设备的 DRI 客户端。
  • 对video card或显卡驱动的访问由DRM内核模块管理。但是X Server DDX驱动和每个X Client DRI驱动必须使用DRM来访问显卡。 DRM 为显卡的共享资源(例如命令队列、卡寄存器、视频内存、DMA 引擎等资源)提供同步,确保多个用户空间进程的并发访问不会互相干扰。 DRM还有安全保护的作用,它不允许任何X客户端访问执行3D渲染所需的硬件。

DRI1阶段

在最初的 DRI 架构中,由于当时video card的内存大小,屏幕back-buffer和front-buffer的单例被DRI client和X Server所共享。渲染的数据会被先写入back-buffer,然后在v-blank间隔时间会与front-buffer进行交换。为了把数据渲染到back-buffer,DRI进程应该确保裁剪渲染数据只保留可视窗口大小。
与 X Server的同步是通过信号和称为 SAREA 的共享内存缓冲区完成的。对 DRM 设备的访问通道是唯一的,因此任何 DRI clients都必须在渲染操作开始时锁定它,其他client期间无法访问,必须等到当前渲染操作结束时释放锁。另一个缺点是在当前 DRI 进程在设备上释放锁后,操作涉及的内存不会常驻,因此上传到显存的任何数据(如纹理)都会丢失,以供即将进行的操作使用,从而对图形性能产生重大影响。

DRI2阶段

由于像 Compiz 这样的合成窗口管理器越来越受欢迎,DRI 必须重新设计以便X client在进行直接渲染时也同样支持重定向到"offscreen-pixmaps"。常规的 X client已经将 X 服务器提供的单独像素图作为渲染目标(所谓的"offscreen-pixmaps"),但 DRI client继续直接在共享的back-buffer中进行渲染,有效地绕过了合成窗口管理器。最终的解决方案是改变DRI处理渲染buffer的方式,这会导致具有一组完全不同的DRI扩展接口,并且DRM也发生了巨大变化。新的扩展名为“DRI2”,虽然它不是更高版本,而是一个不同的扩展,甚至与原始 DRI 不兼容——事实上,两者在 X Server 中共存了很长时间。
在DRI2中,每个DRI client都拥有自己私有的 back-buffer来通过硬件加速渲染器窗口内容,而不是共享back-buffer。然而,DRI client将其与一个假的"front-buffer"进行交换。假的"front-buffer"是合成窗口管理器最终合成屏幕缓冲区的buffer之一,,在 VBLANK 间隔与实际的front-buffer进行交换。
为了处理所有这些新的buffer,DRM必须必须要有新功能,特别是显存管理器。DRI2最初使用的是还不成熟的TTM内存管理器,但是DRM内存管理器还是最终选择了GEM。新的DRI2内部buffer管理模型还解决了最初 DRI 中存在的两个主要性能瓶颈:

  • DRI2 clinet在使用它进行渲染的时候不再锁住整个DRM设备,因为现在每个client都获得了一个独立于其他进程的单独渲染buffer.
  • DRI2 client可以在显存中分配自己的buffer(带有纹理、顶点列表等),并且根据需要可以常驻,从而减少显存带宽消耗。

在DRI2中,为窗口分配私有offscreen-buffers(back-buffer、fake-front-buffer、 depth-buffer、stencil-buffer等)由X Server自己完成。DRI client通过调用 DRI2 扩展中 DRI2GetBuffers 和 DRI2GetBuffersWithFormat 等接口来检索这些buffers以便渲染到窗口中。在内部,DRI2 使用 GEM 名称——一种由 GEM API 提供的全局句柄,允许访问 DRM 设备的两个进程引用同一个缓冲区。
X Server 负责窗口渲染buffer分配的原因是 GLX 扩展允许多个 X client在同一个窗口中协同进行 OpenGL 渲染。 这样,X Server在整个渲染过程中管理渲染buffer的整个生命周期,并知道何时可以安全地回收它们。当调整窗口大小时,X server负责分配新的渲染buffer来匹配新的窗口大小,并使用 InvalidateBuffers 事件通知DRI clinet渲染到窗口中的更改,以便他们检索 GEM新缓冲区的名称。
DRI2 扩展为 DRI client提供了其他核心操作,如DRI2Connect接口可以找出他们应该使用哪个 DRM 设备和驱动程序 或DRI2Authenticate接口通过 X Server进行身份验证以便能够使用 DRM 设备的渲染和缓冲设施。使用 DRI2CopyRegion 和 DRI2SwapBuffers 请求在屏幕中呈现渲染buffer。DRI2CopyRegion 可用于在fake-front-buffer和real-front-buffer之间进行复制,但它不提供与v-black间隔的任何同步,因此会导致撕裂。 另一方面,DRI2SwapBuffers在back-buffer和front-buffer之间执行v-blank同步交换。

DRI3阶段

虽然 DRI2 比DRI 有了显着的改进,不过扩展也引入了一些新的问题。2013年,为了解决新的问题,DRI3作为DRI的第3次迭代被开发了。
DRI3 与 DRI2 相比的主要区别在于:

  • DRI3 client为自己分配渲染缓冲区,而不是依赖 X Server进行分配(这是DRI2 支持的方法)。
  • DRI3 摆脱了旧的基于 GEM 名称(全局 GEM 句柄)的不安全 GEM buffer共享机制(用于在 DRI client和 X Server之间传递buffer对象),转而采用基于 PRIME DMA-BUF(使用文件描述符) 的更安全和通用的机制。

client的缓冲区分配打破了 GLX 的假设,因为多个 GLX 应用程序不再可能在同一个窗口中协同渲染。 从好的方面来说,DRI client在其生命周期内负责自己的缓冲区这一事实带来了许多优势,例如DRI3 client很容易确保渲染buffer的大小始终与窗口的当前大小相匹配,从而消除由于client和server之间缺乏缓冲区大小同步而困扰窗口大小调整。还实现了更好的性能,因为 DRI3 client节省了等待 X server发送渲染buffer的额外耗时。DRI3 client(尤其是合成器窗口管理器)可以利用保留前一帧的buffer区并重用它们仅对渲染窗口中变化的部分进行重绘。DRI3 扩展不再需要修改以支持新的特定缓冲区格式,因为现在直接在 DRI client驱动程序和 DRM kernel驱动程序之间处理。另一方面,文件描述符的使用允许kernel对任何未使用的 GEM buffer对象执行安全清理。
从技术上将,DRI3包含2个扩展:“DRI3”扩展和“Present”扩展。DRI3 扩展的主要目的是实现在 DRI client和 X Server之间共享直接渲染buffer的机制。DRI client分配和使用 GEM buffer对象作为渲染目标,而 X server使用一种称为“pixmap”的 X11 对象表示这些渲染缓冲区。DRI3 提供了两种接口:DRI3PixmapFromBuffer 和 DRI3BufferFromPixmap,前者是从 GEM buffer对象(在“DRI client空间”中)创建pixmap (在“X server空间”中),后者是反向操作。在这些 DRI3 接口中,GEM buffer对象作为 DMA-BUF 文件描述符而不是 GEM 名称传递。DRI3 还提供了一种在 DRI client和 X server之间共享同步对象的方法,允许对共享buffer进行序列化访问。和 DRI2不同,DRI3Open(每个 DRI 客户端必须请求知道要使用哪个 DRM 设备的操作)将已打开的文件描述符返回到设备节点而不是设备节点文件名,并且任何所需的身份验证过程已经提前执行 X sever。
DRI3 没有提供在屏幕上显示渲染缓冲区的机制,而是依赖另一个扩展,即 Present 扩展来做到这一点。Present 之所以如此命名,是因为它的主要任务是在屏幕上“Present ”缓冲区,这意味着它使用client应用程序提供的渲染缓冲区的内容来处理帧缓冲区的更新。屏幕更新必须在适当的时间完成,通常是在 VBLANK 间隔期间,以避免出现撕裂等显示问题。Present 还处理屏幕更新到 VBLANK 间隔的同步。它还使用事件让 X client知道每个缓冲区真正显示在屏幕上的时刻,因此client可以将其渲染过程与当前屏幕刷新率同步。

Present 接受任何 X pixmap 作为屏幕更新的来源.由于pixmap 是标准的 X 对象,Present 不仅可以由执行直接渲染的 DRI3 客户端使用,而且可以由任何 X 客户端以任何方式在pixmap 上渲染。例如大多数基于非 GL 的 GTK+ 和 Qt 应用程序过去使用 XRender 进行双缓冲pixmap 渲染。这些应用程序也可以使用 Present 扩展来实现高效且无撕裂的屏幕更新。这就是 Present 作为独立扩展而不是 DRI3 的一部分的原因。
除了允许非 GL X client与 VBLANK 同步之外,Present 还有其他优势。DRI3 图形性能更好,因为 Present 在交换缓冲区方面比​​ DRI2 更有效。基于 Present 提供的新功能, DRI2 中许多不可用的 OpenGL 扩展被支持。
Present 为 X client提供了两个主要接口:PresentPixmap:使用pixmap 的部分或全部内容更新窗口的区域;PresentSelectInput :设置与client想要通知的窗口的Present事件的类型 。窗口可以通知 X client的三个Present事件:1、当PresentPixmap 的调用完成时(PresentCompleteNotify); 2、当 PresentPixmap 使用的pixmap 准备好被重用时(PresentIdleNotify ); 3、当窗口配置(主要是窗口大小)发生变化时 (PresentConfigureNotify)。PresentPixmap 执行直接复制 (blit) 到front-buffer还是整个back-buffer与front-buffer的交换是 Present 扩展实现的内部细节,而不是 像DRI2那样X cleint的显式选择。

采用

几个开源 DRI 驱动程序,包括用于 ATI Mach64、ATI Rage128、ATI Radeon、3dfx Voodoo3 到 Voodoo5、Matrox G200 到 G400、SiS 300 系列、Intel i810 到 i965、S3 Savage、VIA UniChrome 图形芯片组和 Nvidia的nouveau。一些图形供应商使用的闭源 DRI 驱动程序,包括 ATI 和 PowerVR Kyro。
各种版本的 DRI 已由各种操作系统实现,其中包括 Linux kernel、FreeBSD、NetBSD、OpenBSD 和 OpenSolaris。

历史

该项目由 Precision Insight(由 Silicon Graphics 和 Red Hat 资助)的 Jens Owen 和 Kevin E. Martin 发起,它最初是作为 XFree86 4.0的一部分广泛使用的,现在是 X.Org Server 的一部分。它目前由自由软件社区维护。
DRI2 的工作始于 2007 年 X 开发者峰会,由 Kristian Høgsberg 提出。Høgsberg 自己编写了新的 DRI2 扩展以及对 Mesa 和 GLX 的修改。2008 年 3 月,DRI2 基本完成,但它无法进入 X.Org Server 版本 1.5,不得不从 2009 年 2 月开始等到版本 1.6。DRI2 扩展正式包含在 2009 年 10 月的 X11R7.5 版本中。 DRI2 协议 (2.0) 的第一个公共版本于 2009 年 4 月发布。从那以后进行了多次修订,最近的版本是 2012 年 7 月的 2.8 版。
由于 DRI2 的一些限制,Keith Packard 和 Emma Anholt 在 2012 年 X.Org 开发者大会上提出了一个名为 DRI-Next 的新扩展,该扩展在 Linux.conf.au 2013 上再次被提议为 DRI3000,DRI3 和 Present 扩展是在 2013 年开发的,并于 2013 年 12 月合并到 X.Org Server 1.15 版本中。DRI3 协议 (1.0) 的第一个也是唯一一个版本于 2013 年 11 月发布。下面5个图是DRI框架变化过程:
OpenGL & DIR_第4张图片

图1:2D drivers在X Server

OpenGL & DIR_第5张图片

图2:通过GLX间接渲染

OpenGL & DIR_第6张图片

图3:早期DRI:由X-display-server以root身份执行mode-setting

OpenGL & DIR_第7张图片

图4:所有访问都通过DRM进行

OpenGL & DIR_第8张图片

图5:在Linux内核3.12中引入了渲染节点;DRM 和 KMS 驱动程序被拆分。Wayland 通过 EGL 实现直接渲染

你可能感兴趣的:(unity,游戏引擎)