KWin supports implementing a special compositor. As of version 4.11 KWin provides three compositors:
- OpenGL 1
- OpenGL (ES) 2
with OpenGL 1 and 2 sharing a lot of code. The specific compositors are implemented as a sub class of the abstract base class KWin::Scene and a few more classes. The exact steps are described in this document. As an example for implementing a new Compositor this commit can be used.
Introducing a new Compositing Type
KWin references the various compositors through an enum KWin::CompositingType defined in kwinglobals.h. To add a new compositing type a new enum value has to be added. The enum also operates as a flag type, which allows to have compositor sub-types (c.f. OpenGL1Compositing and OpenGL2Compositing as a sub-type of OpenGLCompositing). Each Compositor should have it's own value which has to be returned by the specific implementation of Scene::compositingType().
The CompositingType is necessary for deciding which Compositor to start when compositing gets initialized. For this the method Options::loadCompositingConfig in options.cpp needs to adjusted. The to be used Compositor is read from the config key "Backend" which has the values "OpenGL" or "XRender". To have another Compositor this needs to be extended and checked for. In addition the Compositor can also be selected by the environment varible "KWIN_COMPOSE" which expects a one character value, e.g. "X" for XRender.
The CompositingType is transformed into a human readable string in two locations. Workspace::supportInformation() in workspace.cpp and Compositor::compositingType() in composite.cpp need to be adjusted to know about the new Compositing Type otherwise those methods provide the output that no compositor is running.
Creating the Compositor
The Compositor is created in Compositor::slotCompositingOptionsInitialized() in composite.cpp. Depending on the CompositingType previously set in the Options a specific sub class is created. The code can either do the complete initialization code of the Scene sub class or just call a static factory method in the Scene sub class. It is recommended to use the factory method pattern as this way the error handling can be kept inside the Scene sub class. It is allowed to return NULL in error case and the Compositor can then fall back to another Compositor or even restart KWin to apply adjusted settings for a different Compositor. In addition the Scene provides a pure virtual method initFailed(). This method is checked if a non-null Scene is provided. In case of the factory pattern the implementing class can just return false, otherwise it should have proper error handling. If this method returns true the Scene will be destroyed again.
Scene is an abstract class providing the bas interface towards the compositing framework. The main functionality of this class is to setup the state to render a frame and to be a factory for other Scene related classes. As the class provides many pure virtual methods it's rather obvious which methods need to be implemented.
Rendering a frame
Rendering a frame is controlled by the virtual method Scene::paint(QRegion damage, ToplevelList toplevels). This method has to perform some setup functionality like creating the stacking order for this frame. It is allowed to extend the damage region before the method calls the parent class's method paintScreen(). This method starts the effect chain for rendering. Once the method returns the frame has been rendered and the Scene should blit it to the screen. Last but not least the method needs to cleanup e.g. clear the stacking order and return the time it took to render this frame. This is required for the Compositor to properly schedule the next frame.
The Scene can limit the frame repainting through the method isLastFrameRendered(). If this method returns false the Compositor will not start the rendering of a new frame. If an implementing class of Scene provides a custom implementation of this method, the Scene should call Compositor::self()->lastFrameRendered() once the frame finished so that a new frame can be immediatelly scheduled if it was blocked before.
The method Scene::paintBackground(QRegion region) is responsible for rendering the background. This normally happens at the begin of the frame rendering. KWin expects the background to be rendered in black fully opaque. The rendering should be restricted to the passed in region - this is true for all methods taking a region argument.
Transformed desktop rendering
If the desktop is rendered with trasnformations the method Scene::paintGenericScreen() will be invoked. The implementing class can use the passed in information to adjust the rendering of this frame (e.g. scaling, translating). KWin expects that translation is performed before scaling is performed! After a frame is rendered the adjustments should be reverted so that there is no state overlap to the next frame.
On X11 compositing is normally performed on the X11 Overlay window. KWin has a class wrapping the functionality and the Scene can make use of it, but does not have to. To make use of the Overlay Window it needs to implement Scene::usesOverlayWindow() and Scene::overlayWindow().
KWin has the ability to have custom shadows rendered together with a window. For this there is the abstract class KWin::Shadow (see shadow.h. Each compositor backend needs to provide a specific implementation. The task of this implementation is to map between the generic shadow data and the data needed by the compositor (e.g. a texture). The specialization needs to be provided by implementing the pure virtual method createShadow(Toplevel *toplevel).
The implementation of Shadow does not have a method to render it. The shadow is rendered together with a Window.
Another class which might need to be utilized is the PaintRedirector (see paintredirector.h). This abstract class is responsible for redirecting the rendering of the window decoration into a Compositor specific data structure. It might be needed to implement an additional inheriting class or to reuse an existing one. In both cases the method PaintRedirector::create(Client *c, QWidget *widget) needs to be adjusted to create a PaintRedirector for the new compositing backend. In difference to the other classes which need to be implemented, all inheriting classes are in the same source file and not in the scene files.
The PaintRedirector can be used when rendering the window to get access to the window decoration. It is only available to windows referencing a Client or a Deleted from a Client.
An EffectFrame is an overlay to render text or icons from the effects. They are normally rendered atop all windows or a specific window. There are multiple types of effect frames, like one using a Plasma::FrameSvg or an unstyled using white text on black, half transparent background. An EffectFrame has a special implementation in the Scene which is responsible for rendering to the composited image.
Therefore the backend needs to implement the pure virtual method Scene::createEffectFrame(EffectFrameImpl *frame) which returns a pointer to an instance of the Scene specific implementation.
The most important method is render() which needs to be implemented for all the different types.
The Scene::Window references a Toplevel and is responsible for rendering this window to the composited frame. The backend needs to implement the pure virtual method createWindow(Toplevel *toplevel) to create a backend specific Window. The Window itself does not need many adjustments. Most important is the method performPaint() which needs to be implemented to render the window to the screen. This method needs to do transformations as requested by the WindowPaintData. It also needs to render the Shadow (if present) and the window decorations (if present).
The window content is provided in a WindowPixmap. The backend needs to provide an implementation of this class and Scene::Window needs to provide an instance through the factory method createWindowPixmap(). The WindowPixmap holds the references XPixmap (and in future the wl_buffer) and the backend specific data structure mapped to the pixmap (e.g. a texture).
KWin's effects can have compositor specific code or only available for a specific compositor. It is important to know that a compositor does not need to provide all features available in other compositors. So a 2D compositor should not try to emulate the functionality of the OpenGL compositor. If a feature cannot be provided without large performance overhead it is better to not provide it. In such cases the effects can test for it.
Effects can disable themselves for a compositor by providing a static supported method. This is normally only used for effects which need a specific OpenGL version. Other effects are mostly assumed to work on all compositor. If your new compositor does not support an effect which used to be supported by all other compositors, it is required to add the supported method.
Also there are effects supporting all compositors because they have specific implementations. In such a case specific code might need to be added.