not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / scene_xrender.cpp
blob33b827de13104671f3e789ab49c18a5c617d33dc
1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
22 This is the XRender-based compositing code. The primary compositing
23 backend is the OpenGL-based one, which should be more powerful
24 and also possibly better documented. This backend is mostly for cases
25 when the OpenGL backend cannot be used for some reason (insufficient
26 performance, no usable OpenGL support at all, etc.)
27 The plan is to keep it around as long as needed/possible, but if it
28 proves to be too much hassle it will be dropped in the future.
30 Docs:
32 XRender (the protocol, but the function calls map to it):
33 http://gitweb.freedesktop.org/?p=xorg/proto/renderproto.git;a=blob_plain;hb=HEAD;f=renderproto.txt
35 XFixes (again, the protocol):
36 http://gitweb.freedesktop.org/?p=xorg/proto/fixesproto.git;a=blob_plain;hb=HEAD;f=fixesproto.txt
40 #include "scene_xrender.h"
42 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
44 #include "toplevel.h"
45 #include "client.h"
46 #include "deleted.h"
47 #include "effects.h"
48 #include "kwinxrenderutils.h"
50 #include <X11/extensions/Xcomposite.h>
52 #include <kxerrorhandler.h>
54 namespace KWin
57 //****************************************
58 // SceneXrender
59 //****************************************
61 // kDebug() support for the XserverRegion type
62 struct RegionDebug
64 RegionDebug( XserverRegion r ) : rr( r ) {}
65 XserverRegion rr;
66 };
68 kdbgstream& operator<<( kdbgstream& stream, RegionDebug r )
70 if( r.rr == None )
71 return stream << "EMPTY";
72 int num;
73 XRectangle* rects = XFixesFetchRegion( display(), r.rr, &num );
74 if( rects == NULL || num == 0 )
75 return stream << "EMPTY";
76 for( int i = 0;
77 i < num;
78 ++i )
79 stream << "[" << rects[ i ].x << "+" << rects[ i ].y << " " << rects[ i ].width << "x" << rects[ i ].height << "]";
80 return stream;
83 Picture SceneXrender::buffer = None;
84 ScreenPaintData SceneXrender::screen_paint;
86 SceneXrender::SceneXrender( Workspace* ws )
87 : Scene( ws )
88 , front( None )
89 , init_ok( false )
91 if( !Extensions::renderAvailable())
93 kError( 1212 ) << "No XRender extension available";
94 return;
96 if( !Extensions::fixesRegionAvailable())
98 kError( 1212 ) << "No XFixes v3+ extension available";
99 return;
101 KXErrorHandler xerr;
102 if( wspace->createOverlay())
104 wspace->setupOverlay( None );
105 XWindowAttributes attrs;
106 XGetWindowAttributes( display(), wspace->overlayWindow(), &attrs );
107 format = XRenderFindVisualFormat( display(), attrs.visual );
108 if( format == NULL )
110 kError( 1212 ) << "Failed to find XRender format for overlay window";
111 return;
113 front = XRenderCreatePicture( display(), wspace->overlayWindow(), format, 0, NULL );
115 else
117 // create XRender picture for the root window
118 format = XRenderFindVisualFormat( display(), DefaultVisual( display(), DefaultScreen( display())));
119 if( format == NULL )
121 kError( 1212 ) << "Failed to find XRender format for root window";
122 return; // error
124 XRenderPictureAttributes pa;
125 pa.subwindow_mode = IncludeInferiors;
126 front = XRenderCreatePicture( display(), rootWindow(), format, CPSubwindowMode, &pa );
128 createBuffer();
129 if( xerr.error( true ))
131 kError( 1212 ) << "XRender compositing setup failed";
132 return;
134 if( !selfCheck())
135 return;
136 init_ok = true;
139 SceneXrender::~SceneXrender()
141 if( !init_ok )
143 // TODO this probably needs to clean up whatever has been created until the failure
144 wspace->destroyOverlay();
145 return;
147 XRenderFreePicture( display(), front );
148 XRenderFreePicture( display(), buffer );
149 buffer = None;
150 wspace->destroyOverlay();
151 foreach( Window* w, windows )
152 delete w;
155 bool SceneXrender::initFailed() const
157 return !init_ok;
160 // Create the compositing buffer. The root window is not double-buffered,
161 // so it is done manually using this buffer,
162 void SceneXrender::createBuffer()
164 Pixmap pixmap = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(), DefaultDepth( display(), DefaultScreen( display())));
165 buffer = XRenderCreatePicture( display(), pixmap, format, 0, 0 );
166 XFreePixmap( display(), pixmap ); // The picture owns the pixmap now
169 // Just like SceneOpenGL::selfCheck()
170 bool SceneXrender::selfCheck()
172 QImage img( 3, 2, QImage::Format_RGB32 );
173 img.setPixel( 0, 0, QColor( Qt::red ).rgb());
174 img.setPixel( 1, 0, QColor( Qt::green ).rgb());
175 img.setPixel( 2, 0, QColor( Qt::blue ).rgb());
176 img.setPixel( 0, 1, QColor( Qt::white ).rgb());
177 img.setPixel( 1, 1, QColor( Qt::black ).rgb());
178 img.setPixel( 2, 1, QColor( Qt::white ).rgb());
179 QPixmap pix = QPixmap::fromImage( img );
180 QList< QPoint > points = selfCheckPoints();
181 QRegion reg;
182 foreach( const QPoint& p, points )
183 reg |= QRect( p, pix.size());
184 if( wspace->overlayWindow())
185 { // avoid covering the whole screen too soon
186 wspace->setOverlayShape( reg );
187 wspace->showOverlay();
189 foreach( const QPoint& p, points )
191 XSetWindowAttributes wa;
192 wa.override_redirect = True;
193 ::Window window = XCreateWindow( display(), rootWindow(), 0, 0, 3, 2, 0, QX11Info::appDepth(),
194 CopyFromParent, CopyFromParent, CWOverrideRedirect, &wa );
195 XSetWindowBackgroundPixmap( display(), window, pix.handle());
196 XClearWindow( display(), window );
197 XMapWindow( display(), window );
198 // move the window one down to where the result will be rendered too, just in case
199 // the render would fail completely and eventual check would try to read this window's contents
200 XMoveWindow( display(), window, p.x() + 1, p.y());
201 XCompositeRedirectWindow( display(), window, CompositeRedirectManual );
202 Pixmap wpix = XCompositeNameWindowPixmap( display(), window );
203 XWindowAttributes attrs;
204 XGetWindowAttributes( display(), window, &attrs );
205 XRenderPictFormat* format = XRenderFindVisualFormat( display(), attrs.visual );
206 Picture pic = XRenderCreatePicture( display(), wpix, format, 0, 0 );
207 QRect rect( p.x(), p.y(), 3, 2 );
208 XRenderComposite( display(), PictOpSrc, pic, None, buffer, 0, 0, 0, 0,
209 rect.x(), rect.y(), rect.width(), rect.height());
210 XFreePixmap( display(), wpix );
211 XDestroyWindow( display(), window );
213 flushBuffer( PAINT_SCREEN_REGION, reg );
214 bool ok = true;
215 foreach( const QPoint& p, points )
217 QPixmap pix = QPixmap::grabWindow( rootWindow(), p.x(), p.y(), 3, 2 );
218 QImage img = pix.toImage();
219 // kDebug(1212) << "P:" << QColor( img.pixel( 0, 0 )).name();
220 // kDebug(1212) << "P:" << QColor( img.pixel( 1, 0 )).name();
221 // kDebug(1212) << "P:" << QColor( img.pixel( 2, 0 )).name();
222 // kDebug(1212) << "P:" << QColor( img.pixel( 0, 1 )).name();
223 // kDebug(1212) << "P:" << QColor( img.pixel( 1, 1 )).name();
224 // kDebug(1212) << "P:" << QColor( img.pixel( 2, 1 )).name();
225 if( img.pixel( 0, 0 ) != QColor( Qt::red ).rgb()
226 || img.pixel( 1, 0 ) != QColor( Qt::green ).rgb()
227 || img.pixel( 2, 0 ) != QColor( Qt::blue ).rgb()
228 || img.pixel( 0, 1 ) != QColor( Qt::white ).rgb()
229 || img.pixel( 1, 1 ) != QColor( Qt::black ).rgb()
230 || img.pixel( 2, 1 ) != QColor( Qt::white ).rgb())
232 kError( 1212 ) << "Compositing self-check failed, disabling compositing.";
233 ok = false;
234 break;
237 if( wspace->overlayWindow())
238 wspace->hideOverlay();
239 if( ok )
240 kDebug( 1212 ) << "Compositing self-check passed.";
241 if( !ok && options->disableCompositingChecks )
243 kWarning( 1212 ) << "Compositing checks disabled, proceeding regardless of self-check failure.";
244 return true;
246 return ok;
250 // the entry point for painting
251 void SceneXrender::paint( QRegion damage, ToplevelList toplevels )
253 foreach( Toplevel* c, toplevels )
255 assert( windows.contains( c ));
256 stacking_order.append( windows[ c ] );
258 int mask = 0;
259 paintScreen( &mask, &damage );
260 if( wspace->overlayWindow()) // show the window only after the first pass, since
261 wspace->showOverlay(); // that pass may take long
262 flushBuffer( mask, damage );
263 // do cleanup
264 stacking_order.clear();
267 void SceneXrender::flushBuffer( int mask, QRegion damage )
269 if( mask & PAINT_SCREEN_REGION )
271 // Use the damage region as the clip region for the root window
272 XserverRegion front_region = toXserverRegion( damage );
273 XFixesSetPictureClipRegion( display(), front, 0, 0, front_region );
274 XFixesDestroyRegion( display(), front_region );
275 // copy composed buffer to the root window
276 XFixesSetPictureClipRegion( display(), buffer, 0, 0, None );
277 XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
278 XFixesSetPictureClipRegion( display(), front, 0, 0, None );
279 XFlush( display());
281 else
283 // copy composed buffer to the root window
284 XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
285 XFlush( display());
289 void SceneXrender::paintGenericScreen( int mask, ScreenPaintData data )
291 screen_paint = data; // save, transformations will be done when painting windows
292 if( true ) // as long as paintTransformedScreen() doesn't work properly
293 Scene::paintGenericScreen( mask, data );
294 else
295 paintTransformedScreen( mask );
299 TODO currently broken
300 Try to do optimized painting even with transformations. Since only scaling
301 and translation are supported by the painting code, clipping can be done
302 manually to avoid having to paint everything in every pass. Whole screen
303 still need to be painted but e.g. obscured windows don't. So this below
304 is basically paintSimpleScreen() with extra code to compute clipping correctly.
306 This code assumes that the only transformations possible with XRender are those
307 provided by Window/ScreenPaintData, In the (very unlikely?) case more is needed
308 then paintGenericScreen() needs to be used.
310 void SceneXrender::paintTransformedScreen( int orig_mask )
312 QRegion region( 0, 0, displayWidth(), displayHeight());
313 QList< Phase2Data > phase2;
314 QRegion allclips;
315 // Draw each opaque window top to bottom, subtracting the bounding rect of
316 // each window from the clip region after it's been drawn.
317 for( int i = stacking_order.count() - 1; // top to bottom
318 i >= 0;
319 --i )
321 Window* w = static_cast< Window* >( stacking_order[ i ] );
322 WindowPrePaintData data;
323 data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT );
324 w->resetPaintingEnabled();
325 data.paint = region;
326 // TODO this is wrong, transformedShape() should be used here, but is not known yet
327 data.clip = w->isOpaque() ? region : QRegion();
328 data.quads = w->buildQuads();
329 // preparation step
330 effects->prePaintWindow( effectWindow( w ), data, time_diff );
331 #ifndef NDEBUG
332 foreach( const WindowQuad &q, data.quads )
333 if( q.isTransformed())
334 kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
335 #endif
336 if( !w->isPaintingEnabled())
337 continue;
338 data.paint -= allclips; // make sure to avoid already clipped areas
339 if( data.paint.isEmpty()) // completely clipped
340 continue;
341 if( data.paint != region ) // prepaint added area to draw
343 region |= data.paint; // make sure other windows in that area get painted too
344 painted_region |= data.paint; // make sure it makes it to the screen
346 // If the window is transparent, the transparent part will be done
347 // in the 2nd pass.
348 if( data.mask & PAINT_WINDOW_TRANSLUCENT )
349 phase2.prepend( Phase2Data( w, data.paint, data.clip, data.mask, data.quads ));
350 if( data.mask & PAINT_WINDOW_OPAQUE )
352 w->setTransformedShape( QRegion());
353 paintWindow( w, data.mask, data.paint, data.quads );
354 // The window can clip by its opaque parts the windows below.
355 region -= w->transformedShape();
357 // translucency or window transformed require window pixmap
358 w->suspendUnredirect( data.mask & ( PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED ));
360 if( !( orig_mask & PAINT_SCREEN_BACKGROUND_FIRST ))
361 paintBackground( region ); // Fill any areas of the root window not covered by windows
362 // Now walk the list bottom to top, drawing translucent windows.
363 // That we draw bottom to top is important now since we're drawing translucent objects
364 // and also are clipping only by opaque windows.
365 QRegion add_paint;
366 foreach( const Phase2Data &d, phase2 )
368 Scene::Window* w = d.window;
369 paintWindow( w, d.mask, d.region | add_paint, d.quads );
370 // It is necessary to also add paint regions of windows below, because their
371 // pre-paint's might have extended the paint area, so those areas need to be painted too.
372 add_paint |= d.region;
376 // fill the screen background
377 void SceneXrender::paintBackground( QRegion region )
379 PaintClipper pc( region );
380 for( PaintClipper::Iterator iterator;
381 !iterator.isDone();
382 iterator.next())
384 XRenderColor col = { 0, 0, 0, 0xffff }; // black
385 XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight());
389 void SceneXrender::windowGeometryShapeChanged( Toplevel* c )
391 if( !windows.contains( c )) // this is ok, shape is not valid by default
392 return;
393 Window* w = windows[ c ];
394 w->discardPicture();
395 w->discardShape();
396 w->discardAlpha();
399 void SceneXrender::windowOpacityChanged( Toplevel* c )
401 if( !windows.contains( c )) // this is ok, alpha is created on demand
402 return;
403 Window* w = windows[ c ];
404 w->discardAlpha();
407 void SceneXrender::windowClosed( Toplevel* c, Deleted* deleted )
409 assert( windows.contains( c ));
410 if( deleted != NULL )
411 { // replace c with deleted
412 Window* w = windows.take( c );
413 w->updateToplevel( deleted );
414 windows[ deleted ] = w;
416 else
418 delete windows.take( c );
419 c->effectWindow()->setSceneWindow( NULL );
423 void SceneXrender::windowDeleted( Deleted* c )
425 assert( windows.contains( c ));
426 delete windows.take( c );
427 c->effectWindow()->setSceneWindow( NULL );
430 void SceneXrender::windowAdded( Toplevel* c )
432 assert( !windows.contains( c ));
433 windows[ c ] = new Window( c );
434 c->effectWindow()->setSceneWindow( windows[ c ]);
437 //****************************************
438 // SceneXrender::Window
439 //****************************************
441 SceneXrender::Window::Window( Toplevel* c )
442 : Scene::Window( c )
443 , _picture( None )
444 , format( XRenderFindVisualFormat( display(), c->visual()))
445 , alpha( None )
449 SceneXrender::Window::~Window()
451 discardPicture();
452 discardAlpha();
453 discardShape();
456 // Create XRender picture for the pixmap with the window contents.
457 Picture SceneXrender::Window::picture()
459 if( !toplevel->damage().isEmpty() && _picture != None )
461 XRenderFreePicture( display(), _picture );
462 _picture = None;
464 if( _picture == None && format != NULL )
466 // Get the pixmap with the window contents.
467 Pixmap pix = toplevel->windowPixmap();
468 if( pix == None )
469 return None;
470 _picture = XRenderCreatePicture( display(), pix, format, 0, 0 );
471 toplevel->resetDamage( toplevel->rect());
473 return _picture;
477 void SceneXrender::Window::discardPicture()
479 if( _picture != None )
480 XRenderFreePicture( display(), _picture );
481 _picture = None;
484 void SceneXrender::Window::discardAlpha()
486 if( alpha != None )
487 XRenderFreePicture( display(), alpha );
488 alpha = None;
491 // Create XRender picture for the alpha mask.
492 Picture SceneXrender::Window::alphaMask( double opacity )
494 if( isOpaque() && opacity == 1.0 )
495 return None;
496 if( alpha != None && alpha_cached_opacity != opacity )
498 if( alpha != None )
499 XRenderFreePicture( display(), alpha );
500 alpha = None;
502 if( alpha != None )
503 return alpha;
504 if( opacity == 1.0 )
505 { // no need to create alpha mask
506 alpha_cached_opacity = 1.0;
507 return None;
509 // Create a 1x1 8bpp pixmap containing the given opacity in the alpha channel.
510 Pixmap pixmap = XCreatePixmap( display(), rootWindow(), 1, 1, 8 );
511 XRenderPictFormat* format = XRenderFindStandardFormat( display(), PictStandardA8 );
512 XRenderPictureAttributes pa;
513 pa.repeat = True;
514 alpha = XRenderCreatePicture( display(), pixmap, format, CPRepeat, &pa );
515 XFreePixmap( display(), pixmap );
516 XRenderColor col;
517 col.alpha = int( opacity * 0xffff );
518 alpha_cached_opacity = opacity;
519 XRenderFillRectangle( display(), PictOpSrc, alpha, &col, 0, 0, 1, 1 );
520 return alpha;
523 // paint the window
524 void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintData data )
526 setTransformedShape( QRegion()); // maybe nothing will be painted
527 // check if there is something to paint
528 bool opaque = isOpaque() && data.opacity == 1.0;
529 /* HACK: It seems this causes painting glitches, disable temporarily
530 if(( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT ))
531 { // We are only painting either opaque OR translucent windows, not both
532 if( mask & PAINT_WINDOW_OPAQUE && !opaque )
533 return; // Only painting opaque and window is translucent
534 if( mask & PAINT_WINDOW_TRANSLUCENT && opaque )
535 return; // Only painting translucent and window is opaque
537 Picture pic = picture(); // get XRender picture
538 if( pic == None ) // The render format can be null for GL and/or Xv visuals
539 return;
540 // set picture filter
541 if( options->xrenderSmoothScale ) // only when forced, it's slow
543 if( mask & PAINT_WINDOW_TRANSFORMED )
544 filter = ImageFilterGood;
545 else if( mask & PAINT_SCREEN_TRANSFORMED )
546 filter = ImageFilterGood;
547 else
548 filter = ImageFilterFast;
550 else
551 filter = ImageFilterFast;
552 // do required transformations
553 int x = toplevel->x();
554 int y = toplevel->y();
555 int width = toplevel->width();
556 int height = toplevel->height();
557 double xscale = 1;
558 double yscale = 1;
559 transformed_shape = shape();
560 if( mask & PAINT_WINDOW_TRANSFORMED )
562 xscale *= data.xScale;
563 yscale *= data.yScale;
564 x += data.xTranslate;
565 y += data.yTranslate;
567 if( mask & PAINT_SCREEN_TRANSFORMED )
569 xscale *= screen_paint.xScale;
570 yscale *= screen_paint.yScale;
571 x = int( x * screen_paint.xScale );
572 y = int( y * screen_paint.yScale );
573 x += screen_paint.xTranslate;
574 y += screen_paint.yTranslate;
576 if( yscale != 1 || xscale != 1 )
578 XTransform xform = {{
579 { XDoubleToFixed( 1 / xscale ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
580 { XDoubleToFixed( 0 ), XDoubleToFixed( 1 / yscale ), XDoubleToFixed( 0 ) },
581 { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
583 XRenderSetPictureTransform( display(), pic, &xform );
584 width = (int)(width * xscale);
585 height = (int)(height * yscale);
586 if( filter == ImageFilterGood )
587 XRenderSetPictureFilter( display(), pic, const_cast< char* >( "good" ), NULL, 0 );
588 // transform the shape for clipping in paintTransformedScreen()
589 QVector< QRect > rects = transformed_shape.rects();
590 for( int i = 0;
591 i < rects.count();
592 ++i )
594 QRect& r = rects[ i ];
595 r = QRect( int( r.x() * xscale ), int( r.y() * yscale ),
596 int( r.width() * xscale ), int( r.height() * xscale ));
598 transformed_shape.setRects( rects.constData(), rects.count());
600 transformed_shape.translate( x, y );
601 PaintClipper pcreg( region ); // clip by the region to paint
602 PaintClipper pc( transformed_shape ); // clip by window's shape
603 for( PaintClipper::Iterator iterator;
604 !iterator.isDone();
605 iterator.next())
607 if( opaque )
609 XRenderComposite( display(), PictOpSrc, pic, None, buffer, 0, 0, 0, 0,
610 x, y, width, height);
611 // fake brightness change by overlaying black
612 XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - data.brightness ) };
613 XRenderFillRectangle( display(), PictOpOver, buffer, &col, x, y, width, height );
615 else
617 Picture alpha = alphaMask( data.opacity );
618 XRenderComposite( display(), PictOpOver, pic, alpha, buffer, 0, 0, 0, 0,
619 x, y, width, height);
620 // fake brightness change by overlaying black
621 XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - data.brightness ) * data.opacity };
622 XRenderFillRectangle( display(), PictOpOver, buffer, &col, x, y, width, height );
623 transformed_shape = QRegion();
626 if( xscale != 1 || yscale != 1 )
628 XTransform xform = {{
629 { XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
630 { XDoubleToFixed( 0 ), XDoubleToFixed( 1 ), XDoubleToFixed( 0 ) },
631 { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
633 XRenderSetPictureTransform( display(), pic, &xform );
634 if( filter == ImageFilterGood )
635 XRenderSetPictureFilter( display(), pic, const_cast< char* >( "fast" ), NULL, 0 );
639 } // namespace
640 #endif