Manual Compositing

In my last post, I linked a short screencap of what appeared to be the “wobbly” effect module from Compiz. In this post, I will be both exploring the methodology behind the implementation and covering some of the AWESOME hacks which made it possible.

To start, let’s examine how Compiz operates. There is an OpenGL extension which allows an X11 pixmap to be bound to a texture. Compiz operates based on the principle that this texture can be rendered in any way, at any time by the compositor, and then the underlying X11 input window will quietly be moved to wherever the rendered content ends up. This creates the illusion that the entire window (input and output) is moving in an irregular manner, when really it’s just the case that the window’s output is moving around and then the window’s input region is moved later; an easy comparison would be the shell game–the compositor always knows the state of everything, but the observer is totally fooled.

Compiz has module support by means of a callback interception system. This works by allowing modules to insert callbacks to various points in the compositor’s operations, replacing the default operation function with a module-provided one. As an example, the wobbly window effect occurs when the corresponding module hijacks window movement callbacks to determine velocity and the anchor point, then also takes over the pre-render call for the window in order to manipulate the vertices of the window’s texture before it is rendered. By performing matrix transforms on the vertices, the window appears to wobble around as though the plane it lies upon is constantly shifting.

Rendering within the Compiz compositor is done by a combination of per-screen damage tracking and occlusion detection. There is no concept of “object” damages, and everything renders directly to the screen. In the event of a full screen redraw, a buffer swap occurs; in all other cases, the damaged buffer region is copied onto the front buffer. If the window manager part of Compiz determines that the window (or part of the window) is not visible, then the non-visible region is not drawn.

At the outset of this project, my goal was simple: determine whether it was possible to load unmodified Compiz effect plugins and have them work “as expected” in Enlightenment. The first use case was, of course, the wobbly plugin. After considering a number of ways to NIH a fake Compiz layer, I decided to skip that process (for once) and instead just put all of Compiz-core into a module for Enlightenment. The last Compiz release (0.8.x)  was ideal for this since, amazingly enough, it did not use any external toolkit, just libX11 and OpenGL. After copying all the source files into the module tree, the hacks began in earnest.

The first thing to understand about Enlightenment integration is that, at the module level, all display server events are completely abstracted. This means that, while it’s still possible for a developer to create handlers explicitly for X11 events, Enlightenment translates all display server events into a set of general events. When writing modules which are expected to function in both X11 and Wayland environments this is good, but when trying to interface with Compiz–which only handles X11 events and is both a compositor and a window manager–this didn’t seem like it would be the easiest to work with.

In order to make this work, what I ended up doing was completely sandboxing all of Compiz. It effectively has no knowledge of anything happening on the system, and it is only allowed to have cpu time when various functions are called into. The sandbox works by handling various events and callbacks within Enlightenment and then synthesizing fake X11 events to send to Compiz. As a more concrete example, an XDamageNotifyEvent is created from the “damage” smart callback on a compositor object. All events necessary for rendering are translated within the Enlightenment module integration layer, and then they are sent to Compiz to be processed before the next frame is drawn. The Compiz event processing loop is manually triggered during the canvas pre-render, meaning that time spent in Compiz contexts is kept to a minimum.

Managing the rendering for Compiz ended up being by far the most complex part, and I’m planning to write another post explaining it and the Enlightenment integration in more detail. As a quick summary: when Compiz is loaded, Enlightenment completely stops rendering clients. The module creates and manages a GL FBO for each window and then draws to it during the compositor canvas pre-render. This FBO is then bound to an image object which gets added into the window’s compositor object in order to handle clipping and stacking correctly. This image object covers the window+frame area and stacks above it, and since the Compiz window manager has received the synthesized events, it draws into the correct area, resulting in everything looking mostly normal. A side effect of this is that the window’s frame will never animate; this is drawn directly onto the compositor canvas, and so it is not part of the window’s texture.

I haven’t created any mechanism for handling configuration of Compiz plugins; instead, the module directly loads the “ccp” plugin from libcompizconfig. The user edits settings as normal using the Compiz Settings Manager UI, and then the corresponding plugin will automatically load all the effect plugins that have been enabled.

Also missing is support for any kind of screen effect plugin. The desktop cube plugin, for example, is not able to be used at this time. I think this is definitely doable, and potentially could be fairly easy, but I have been on a somewhat tight time schedule.

All in all, this was an interesting proof of concept project. It took about two weeks of fairly casual hacking, though I successfully rendered my first wobbly window after the first week.

I don’t plan to actively develop this module. While it was fun to work on something new and extend the Enlightenment compositor API in order to support external compositing, it’s hard to continue working on a project that I personally have no use for–especially after I succeeded in my initial goal.

My hope is that this will inspire other developers to try writing cool effects for Enlightenment, whether by continuing work on this Compiz module or by creating something entirely new.

Next time: Desktop Compositing: How much overdraw is too much?