We hashed out a new DRI design at XDS2007 and this page describes the design. It's very simple actually. The elevator pitch is:
- Always private back buffers
- No clip rects in DRI driver
- No DDX driver part
- Minimal X server part
- Swap buffer and clip rects in kernel
- No SAREA
The following breaks it down into how it affects the different modules in the stack.
The DRM module will take over some of the bookkeeping previously done by the X server DRI module. To implement sync-to-vblank buffer swaps, we need to submit the blits from the vblank tasklet, and thus, DRM needs to know about the clip rectangles for the drawable. Given this and private back buffers, there is no need to have the clip rects in the DRI driver. The current ioctl for setting clip rects lets the X server set clip rects for one drm_drawable_t at a time, which is fine if we're holding a lock. We're not, so we'll need to be able to post updates for all drm_drawable_t's affected by the clip rect change. For example, a window moves, which may change clip rects for all the windows it obscures. Communication these changes to the kernel must be one atomic operation.
The DRM module will also need to track the buffer objects currently associated with a drm_drawable_t. The X server allocates the front buffer (this is the buffer object for the window pixmap for windows and the buffer object for the pixmap for pixmaps). When a GLX drawable is created, the buffer object is attached to the drm_drawable_t, but also if the backing pixmap changes (screen resizing, redirected window resizing, or a window transitioning to or from redirected). Whenever a new front buffer is attached, the ancillary buffers are unreffed and invalidated.
The drm_drawable_t has an associated serial number, which is increased every time this happens. Several clients may be rendering to the same drm_drawable_t and the serial number provides a mechanism to ensure that clients can allocate the necessary ancillary buffers in a race free way. After detecting the serial number has increased, a client allocates the buffers it needs and calls the ioctl to attach those buffers, passing in the serial number, buffer object handles and attachment points. The kernel replies with the current serial number and the list of currently attached buffers. If this client was the first to allocate the new ancillary buffers, the buffer in the reply will be the ones the client passed in in the ioctl. Otherwise, the buffers previously attached by another client for this serial number, will take precedence and the client must destroy those unused buffers.
Moving cliprects and buffer tracking into the kernel eliminates the need for an SAREA. The clip rects are only needed on swap buffers; this is done by the kernel, which always has the latest clip rects.
Issue1: When and how does a client detect that a new front buffer has been attached? The Gallium Design frowns on the SAREA timestamp mechanism because it greatly complicates the DRI driver implementation, but only checking on glXSwapBuffers() isn't sufficient. Consider an application that (unlike, say, glxgears) doesn't render as many frames per second as possible, but uses OpenGL as a way to redraw it's interface in response to X events (mouse clicks, window resizing). When resizing the window of such an application in a composited environment, the front buffer is reallocated and attached and the application is sent an expose event. The application will re-layout the interface and re-render it, however, the DRI driver won't update its back buffers until glXSwapBuffer() is called at which point it's too late. This application will always be a frame behind.
KW: This is somewhat of an implementation problem for the 3d client and/or gallium architecture design -- it looks like there will be times when it is necessary to check window dimensions apart from swapbuffers, and in the case of the above scenario the application will give us a very good hint by adjusting the viewport parameters. More generally we can say we want to check at two places -- SwapBuffers, and immediately before the very first piece of rendering after swapbuffers. This will catch the application-redraw case, and won't hurt performance as long as the mechanism for checking window size continues to be fast.
- KW: Also, gallium doesn't dislike the SAREA mechanism, it just avoids using it at random moments in the middle of a frame.
Issue2: If several clients render to a shared GLX drawable, once one of them post a glXSwapBuffer() for that drawable, it is specified to have the same effect for all clients rendering to the drawable. This gives a problem similar to the above issue, where the other clients will need to update the buffers, but may never issue a glXSwapBuffer().
- KW: I guess it depends what the GLX spec really means by this, and how separated all these clients can be... And maybe on just how closely we want to follow the spec. Maybe Brian can comment...
X Server DRI Module
This module reduces to just initializing the DRM file descriptor and making it available for DDX drivers (for talking to the memory manager and possibly mode setting), pushing SetWindowPixmap and clip rect changes into the kernel and mapping from X drawables to the corresponding drm_drawable_t. The drm_drawable_t is updated from the X server DRI module with the offset of the drawable into the buffer object (for child windows, e.g. top-level windows are child windows of the root window) and the clip rects.
The new DRI module will be a module on its own and the existing DRI module will stay around for compatibility with old DDX drivers. This allows one X server to load both old and new DDX drivers.
X Server GLX Module
The changes to the GLX module fall in two parts: a new DRI loader that can load the new DRI driver interface. This part is easy and can co-exist with the loader for the old DRI driver interface. This will let the X server load both the old and new interface at run-time, so both old and new DRI drivers can be used. The other part is to implement protocol support for the GLX 1.3 entry points and figure out how to fail nicely for those entry points when the old loader is used.
The DRI driver interface changes are mostly unchanged from what is currently sitting on the dri2 branch in my mesa git repo. What is different is the screen initialization: we no longer pass in SAREA info or framebuffer maps. DRI2 doesn't use an SAREA and the framebuffer buffers are allocated on demand, jointly by the X server (the front buffer) and the DRI driver.
Internally, the CreateBuffer DriverAPI function is now responsible for allocating the ancillary buffers that the FBConfig in question asks for. This function will receive a drm_drawable_t and can query the kernel for the buffers currently attached to that handle. The X server will create the drm_drawable_t whenever a GLX drawable is created (either explicitly through glXCreateWindow, glXCreatePixmap, etc, or implicitly when an X window is bound to a GLX context). The X server will attach the pixmap backing the drawable to the drm_drawable_t as the front buffer. If the DRI driver sees that it needs ancillary buffers, it allocates those from the DRM memory manager and uses an ioctl to attach the buffers to the drm_drawable_t as described above.
The DDX driver part is greatly simplified. As the GLX visual initialization now is done entirely in the AIGLX loader, the DDX driver doesn't need to do all that gross stuff with miInitVisual hook and storing the GLX visuals in a global. The EXA TTM changes adds a CreatePixmap hook in for the DDX driver to implement. If the driver supports using an X drawable as a rendering target or texture source, this hook is responsible for allocating the pixmap so it satisfies whatever stride/alignment requirements the 3D hardware might have. Also, all the static buffer allocation is no longer necessary, so essentially, all of i830_dri.c goes away.