X协议虽然是间接渲染的代表性协议,但是实际上它也支持直接渲染扩展。和原生的直接渲染协议——wayland相比,x协议和wayland协议的主要差异就是没有把合成器和窗口管理器合二为一了,这也是性能损失最大的地方(合成器和窗口管理器做了很多重复的工作,且多做了很多不需要的正文切换)。
DRI1
由于早期的显卡(video cards)内存大小很小,在最初的dri框架里,DRI client和server共享一个front buffer和back buffer(其实还有其他depth buffer和stencil buffer也只有一个实例)。所有的程序直接将图形渲染到back buffer,然后vb 间隔里和front buffer交换,以完成显示。
和Xserver之间的同步操作是通过信号和一个叫做SAREA的共享内存buffer来实现的。对DRM device的访问是独占的,所以任何DRI client都需要在渲染开始时锁定该设备。这也意味着其他用户,包括Xserver在内,同时刻是被阻塞的。他们只能等到渲染完成后的锁释放时刻。即使两者的操作不会有冲突也必须这样。另外一个缺点就是所有的操作释放锁之后,就不能保留已分配的内存。所以被上传到显存里的纹理数据也会丢失。因此对性能影响很大。
DRI2
由于合成窗口管理器如compiz(特殊的Xclient)越来越受欢迎,DRI需要重新设计来满足X client在使用直接渲染时也可以进行离线渲染功能。一般情况下X client时满足重定向图形到由X server提供的另外一个pixmap(offscreen pixmap)作为渲染目标,但是DRI client如果还是只能渲染到唯一且共享的back buffer,那它实际相当于做了合成管理器的工作,导致compiz这个实际的合成管理器被旁路。终极解决方案时改变DRI处理渲染buffer的方式,这就导致一个完全不同的DRI 扩展——DRI2被设计出来。DRI2不是DRI1的延续,而是另外一个扩展。
在DRI2中,摒弃了只有一个唯一共享的back buffer的方案,取而代之的是每个DRI client都可以拥有自己的back buffer,这样每个client都可以使用硬件加速渲染将窗口内容。然后client将一个假的front buffer和backbuffer进行交换,这个假的front buffer又是下一个阶段——窗口合成管理器的输入之一。合成窗口管理器将所有的输入合成到最后的back buffer里,在vblank间隔内交换到front buffer。
为了管理众多缓存,DRI引入了TTM,后续又被重写为GEM。最新的DRI2 内部buffer 管理器模型解决了两个重大的性能瓶颈:
DRI2 clients 在渲染的时候,不再需要锁住整个DRM device 。因为每个client都有自己独立的渲染缓存。
DRI2 clients 可以自己分配buffer,如纹理,顶点列表等。并且可以保持他们的生命周期,直到自己释放为止。这个极大的减少了内存带宽使用。
DRI2中,所有的内存分配都是由Xserver 完成的。DRI client通过调用诸如DRI2GetBuffers和DRI2GetBuffersWithFormat等方法来获取用于渲染窗口内容的缓存。DRI2使用GEM names 一个由GEM API提供的全局的handle,该handle允许两个不同的进程访问统一个DRM 设备来引用同一个buffer。为什么让Xserver来管理缓存分配,是由于GLX协议允许多个Xclient合作渲染同一个窗口。这样,Xserver 将这个缓存的全生命周期和渲染进程一起进行管理更有利于安全和回收缓存。如果一个窗口调整了大小,Xserver负责分配一块新的buffer以匹配这个窗口的大小,并且通过使用InvalidateBuffer 事件将这个变化通知有关的DRI client的到新缓存的GEM name。
DRI3
尽管DRI2极大改进了DRI1,但是同样也引入了新问题。DRI3被开发出来去解决这几个问题:
DRI3和DRI2的主要区别:
DRI3 clients 自己分配缓存,取代了必须依赖Xserver分配的方式。
DRI3 去除了不安全的GEM 管理机制。因为GEM name是全局的,所以其他进程可以随意遍历所有name,就能访问到共享内容。使用PRIME dma-bufs机制来共享,新机制使用文件描述符来共享。由于文件描述符是独立的,如果不通过unix socket 传递的话,其他进程无法访问到其他进程的fd指向的内容。
但是在client端分配缓存打破了GLX之前关于多个client共同渲染一个窗口的设计。所以从此多个client无法共同渲染同一个窗口。进一步讲,DRI client管理缓存能有效实现窗口和缓存的尺寸匹配需求,解决了DRI2模式下的问题。还有一个优点是窗口合成管理器(也是一个dri3 client)就可以保留历史的buffer,复用于下一帧的显示,以节省时间片。
从技术上讲,DRI有两个不同扩展组成,“Present”和“DRI3”。DRI3扩展主要用于共享缓存,client分配缓存作为渲染目标,而xserver使用x11 的object名称pixmap来表示这些缓存。DRI3提供两个操作,DRI3PixmapFromBuffer 和 DRI3BufferFromPixmap,其中一个是通过GEM buffer object(在DRI client 空间)创建pixmap(x server的空间),另外一个是反向操作。从x pixmap获得gem buffer object。在这些操作中,gembuffer object 都通过dmabuf 文件描述符进行传递。不同于DRI2,DRI3open操作,必须在每个driclient第一步被调用,以获取到底用哪个drm device。 它会返回一个关于设备节点的已打开的文件描述符,而不是一个设备节点名。同时也会由Xserver 在高级模式下执行后续需要的认证流程。
DRI3没有提供机制显示buffer,它依赖另外一个扩展——Present来完成。该扩展不仅能显示dri3的buffer,也能显示其他通过GL方法生成的纹理等。