not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / scene_opengl.cpp
blob6fb8f9cf65e98cd6f11bd887a1b377a0e024e2cc
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 Based on glcompmgr code by Felix Bellaby.
8 Using code from Compiz and Beryl.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *********************************************************************/
26 This is the OpenGL-based compositing code. It is the primary and most powerful
27 compositing backend.
29 Sources and other compositing managers:
30 =======================================
32 - http://opengl.org
33 - documentation
34 - OpenGL Redbook (http://opengl.org/documentation/red_book/ - note it's only version 1.1)
35 - GLX docs (http://opengl.org/documentation/specs/glx/glx1.4.pdf)
36 - extensions docs (http://www.opengl.org/registry/)
38 - glcompmgr
39 - http://lists.freedesktop.org/archives/xorg/2006-July/017006.html ,
40 - http://www.mail-archive.com/compiz%40lists.freedesktop.org/msg00023.html
41 - simple and easy to understand
42 - works even without texture_from_pixmap extension
43 - claims to support several different gfx cards
44 - compile with something like
45 "gcc -Wall glcompmgr-0.5.c `pkg-config --cflags --libs glib-2.0` -lGL -lXcomposite -lXdamage -L/usr/X11R6/lib"
47 - compiz
48 - git clone git://anongit.freedesktop.org/git/xorg/app/compiz
49 - the ultimate <whatever>
50 - glxcompmgr
51 - git clone git://anongit.freedesktop.org/git/xorg/app/glxcompgr
52 - a rather old version of compiz, but also simpler and as such simpler
53 to understand
55 - beryl
56 - a fork of Compiz
57 - http://beryl-project.org
58 - git clone git://anongit.beryl-project.org/beryl/beryl-core (or beryl-plugins etc. ,
59 the full list should be at git://anongit.beryl-project.org/beryl/)
61 - libcm (metacity)
62 - cvs -d :pserver:anonymous@anoncvs.gnome.org:/cvs/gnome co libcm
63 - not much idea about it, the model differs a lot from KWin/Compiz/Beryl
64 - does not seem to be very powerful or with that much development going on
68 #include "scene_opengl.h"
70 #include <kxerrorhandler.h>
72 #include "utils.h"
73 #include "client.h"
74 #include "deleted.h"
75 #include "effects.h"
77 #include <sys/ipc.h>
78 #include <sys/shm.h>
79 #include <math.h>
81 // turns on checks for opengl errors in various places (for easier finding of them)
82 // normally only few of them are enabled
83 //#define CHECK_GL_ERROR
85 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
87 #include <X11/extensions/Xcomposite.h>
89 #include <qpainter.h>
91 namespace KWin
94 //****************************************
95 // SceneOpenGL
96 //****************************************
98 // the configs used for the destination
99 GLXFBConfig SceneOpenGL::fbcbuffer_db;
100 GLXFBConfig SceneOpenGL::fbcbuffer_nondb;
101 // the configs used for windows
102 SceneOpenGL::FBConfigInfo SceneOpenGL::fbcdrawableinfo[ 32 + 1 ];
103 // GLX content
104 GLXContext SceneOpenGL::ctxbuffer;
105 GLXContext SceneOpenGL::ctxdrawable;
106 // the destination drawable where the compositing is done
107 GLXDrawable SceneOpenGL::glxbuffer = None;
108 GLXDrawable SceneOpenGL::last_pixmap = None;
109 bool SceneOpenGL::tfp_mode; // using glXBindTexImageEXT (texture_from_pixmap)
110 bool SceneOpenGL::db; // destination drawable is double-buffered
111 bool SceneOpenGL::shm_mode;
112 #ifdef HAVE_XSHM
113 XShmSegmentInfo SceneOpenGL::shm;
114 #endif
117 SceneOpenGL::SceneOpenGL( Workspace* ws )
118 : Scene( ws )
119 , init_ok( false )
121 if( !Extensions::glxAvailable())
123 kDebug( 1212 ) << "No glx extensions available";
124 return; // error
126 initGLX();
127 // check for FBConfig support
128 if( !hasGLExtension( "GLX_SGIX_fbconfig" ) || !glXGetFBConfigAttrib || !glXGetFBConfigs ||
129 !glXGetVisualFromFBConfig || !glXCreatePixmap || !glXDestroyPixmap ||
130 !glXCreateWindow || !glXDestroyWindow )
132 kError( 1212 ) << "GLX_SGIX_fbconfig or required GLX functions missing";
133 return; // error
135 if( !selectMode())
136 return; // error
137 if( !initBuffer()) // create destination buffer
138 return; // error
139 if( !initRenderingContext())
140 return; // error
141 // Initialize OpenGL
142 initGL();
143 if( !hasGLExtension( "GL_ARB_texture_non_power_of_two" )
144 && !hasGLExtension( "GL_ARB_texture_rectangle" ))
146 kError( 1212 ) << "GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle missing";
147 return; // error
149 if( db )
150 glDrawBuffer( GL_BACK );
151 // Check whether certain features are supported
152 has_waitSync = false;
153 if( glXGetVideoSync && glXIsDirect( display(), ctxbuffer ) && options->glVSync )
155 unsigned int sync;
156 if( glXGetVideoSync( &sync ) == 0 )
158 if( glXWaitVideoSync( 1, 0, &sync ) == 0 )
159 has_waitSync = true;
163 // OpenGL scene setup
164 glMatrixMode( GL_PROJECTION );
165 glLoadIdentity();
166 float fovy = 60.0f;
167 float aspect = 1.0f;
168 float zNear = 0.1f;
169 float zFar = 100.0f;
170 float ymax = zNear * tan( fovy * M_PI / 360.0f );
171 float ymin = -ymax;
172 float xmin = ymin * aspect;
173 float xmax = ymax * aspect;
174 // swap top and bottom to have OpenGL coordinate system match X system
175 glFrustum( xmin, xmax, ymin, ymax, zNear, zFar );
176 glMatrixMode( GL_MODELVIEW );
177 glLoadIdentity();
178 float scaleFactor = 1.1 * tan( fovy * M_PI / 360.0f )/ymax;
179 glTranslatef( xmin*scaleFactor, ymax*scaleFactor, -1.1 );
180 glScalef( (xmax-xmin)*scaleFactor/displayWidth(), -(ymax-ymin)*scaleFactor/displayHeight(), 0.001 );
181 if( checkGLError( "Init" ))
183 kError( 1212 ) << "OpenGL compositing setup failed";
184 return; // error
186 if( !selfCheck())
187 return;
188 kDebug( 1212 ) << "DB:" << db << ", TFP:" << tfp_mode << ", SHM:" << shm_mode
189 << ", Direct:" << bool( glXIsDirect( display(), ctxbuffer )) << endl;
190 init_ok = true;
193 SceneOpenGL::~SceneOpenGL()
195 if( !init_ok )
197 // TODO this probably needs to clean up whatever has been created until the failure
198 wspace->destroyOverlay();
199 return;
201 foreach( Window* w, windows )
202 delete w;
203 // do cleanup after initBuffer()
204 glXMakeCurrent( display(), None, NULL );
205 glXDestroyContext( display(), ctxbuffer );
206 if( wspace->overlayWindow())
208 if( hasGLXVersion( 1, 3 ))
209 glXDestroyWindow( display(), glxbuffer );
210 XDestroyWindow( display(), buffer );
211 wspace->destroyOverlay();
213 else
215 glXDestroyPixmap( display(), glxbuffer );
216 XFreeGC( display(), gcroot );
217 XFreePixmap( display(), buffer );
219 if( shm_mode )
220 cleanupShm();
221 if( !tfp_mode && !shm_mode )
223 if( last_pixmap != None )
224 glXDestroyPixmap( display(), last_pixmap );
225 glXDestroyContext( display(), ctxdrawable );
227 checkGLError( "Cleanup" );
230 bool SceneOpenGL::initFailed() const
232 return !init_ok;
235 bool SceneOpenGL::selectMode()
237 // select mode - try TFP first, then SHM, otherwise fallback mode
238 shm_mode = false;
239 tfp_mode = false;
240 if( options->glMode == Options::GLTFP )
242 if( initTfp())
243 tfp_mode = true;
244 else if( initShm())
245 shm_mode = true;
247 else if( options->glMode == Options::GLSHM )
249 if( initShm())
250 shm_mode = true;
251 else if( initTfp())
252 tfp_mode = true;
254 if( !initDrawableConfigs())
255 return false;
256 return true;
259 bool SceneOpenGL::initTfp()
261 if( glXBindTexImageEXT == NULL || glXReleaseTexImageEXT == NULL )
262 return false;
263 return true;
266 bool SceneOpenGL::initShm()
268 #ifdef HAVE_XSHM
269 int major, minor;
270 Bool pixmaps;
271 if( !XShmQueryVersion( display(), &major, &minor, &pixmaps ) || !pixmaps )
272 return false;
273 if( XShmPixmapFormat( display()) != ZPixmap )
274 return false;
275 const int MAXSIZE = 4096 * 2048 * 4; // TODO check there are not larger windows
276 // TODO check that bytes_per_line doesn't involve padding?
277 shm.readOnly = False;
278 shm.shmid = shmget( IPC_PRIVATE, MAXSIZE, IPC_CREAT | 0600 );
279 if( shm.shmid < 0 )
280 return false;
281 shm.shmaddr = ( char* ) shmat( shm.shmid, NULL, 0 );
282 if( shm.shmaddr == ( void * ) -1 )
284 shmctl( shm.shmid, IPC_RMID, 0 );
285 return false;
287 #ifdef __linux__
288 // mark as deleted to automatically free the memory in case
289 // of a crash (but this doesn't work e.g. on Solaris ... oh well)
290 shmctl( shm.shmid, IPC_RMID, 0 );
291 #endif
292 KXErrorHandler errs;
293 XShmAttach( display(), &shm );
294 if( errs.error( true ))
296 #ifndef __linux__
297 shmctl( shm.shmid, IPC_RMID, 0 );
298 #endif
299 shmdt( shm.shmaddr );
300 return false;
302 return true;
303 #else
304 return false;
305 #endif
308 void SceneOpenGL::cleanupShm()
310 #ifdef HAVE_XSHM
311 shmdt( shm.shmaddr );
312 #ifndef __linux__
313 shmctl( shm.shmid, IPC_RMID, 0 );
314 #endif
315 #endif
318 bool SceneOpenGL::initRenderingContext()
320 bool direct_rendering = options->glDirect;
321 if( !tfp_mode && !shm_mode )
322 direct_rendering = false; // fallback doesn't seem to work with direct rendering
323 KXErrorHandler errs1;
324 ctxbuffer = glXCreateNewContext( display(), fbcbuffer, GLX_RGBA_TYPE, NULL,
325 direct_rendering ? GL_TRUE : GL_FALSE );
326 bool failed = ( ctxbuffer == NULL || !glXMakeCurrent( display(), glxbuffer, ctxbuffer ));
327 if( errs1.error( true )) // always check for error( having it all in one if() could skip
328 failed = true; // it due to evaluation short-circuiting
329 if( failed )
331 if( !direct_rendering )
333 kDebug( 1212 ).nospace() << "Couldn't initialize rendering context ("
334 << KXErrorHandler::errorMessage( errs1.errorEvent()) << ")";
335 return false;
337 glXMakeCurrent( display(), None, NULL );
338 if( ctxbuffer != NULL )
339 glXDestroyContext( display(), ctxbuffer );
340 direct_rendering = false; // try again
341 KXErrorHandler errs2;
342 ctxbuffer = glXCreateNewContext( display(), fbcbuffer, GLX_RGBA_TYPE, NULL, GL_FALSE );
343 bool failed = ( ctxbuffer == NULL || !glXMakeCurrent( display(), glxbuffer, ctxbuffer ));
344 if( errs2.error( true ))
345 failed = true;
346 if( failed )
348 kDebug( 1212 ).nospace() << "Couldn't initialize rendering context ("
349 << KXErrorHandler::errorMessage( errs2.errorEvent()) << ")";
350 return false;
353 if( !tfp_mode && !shm_mode )
355 ctxdrawable = glXCreateNewContext( display(), fbcdrawableinfo[ QX11Info::appDepth() ].fbconfig, GLX_RGBA_TYPE, ctxbuffer,
356 direct_rendering ? GL_TRUE : GL_FALSE );
358 return true;
361 // create destination buffer
362 bool SceneOpenGL::initBuffer()
364 if( !initBufferConfigs())
365 return false;
366 if( fbcbuffer_db != NULL && wspace->createOverlay())
367 { // we have overlay, try to create double-buffered window in it
368 fbcbuffer = fbcbuffer_db;
369 XVisualInfo* visual = glXGetVisualFromFBConfig( display(), fbcbuffer );
370 XSetWindowAttributes attrs;
371 attrs.colormap = XCreateColormap( display(), rootWindow(), visual->visual, AllocNone );
372 buffer = XCreateWindow( display(), wspace->overlayWindow(), 0, 0, displayWidth(), displayHeight(),
373 0, visual->depth, InputOutput, visual->visual, CWColormap, &attrs );
374 if( hasGLXVersion( 1, 3 ))
375 glxbuffer = glXCreateWindow( display(), fbcbuffer, buffer, NULL );
376 else
377 glxbuffer = buffer;
378 wspace->setupOverlay( buffer );
379 db = true;
380 XFree( visual );
382 else if( fbcbuffer_nondb != NULL )
383 { // cannot get any double-buffered drawable, will double-buffer using a pixmap
384 fbcbuffer = fbcbuffer_nondb;
385 XVisualInfo* visual = glXGetVisualFromFBConfig( display(), fbcbuffer );
386 XGCValues gcattr;
387 gcattr.subwindow_mode = IncludeInferiors;
388 gcroot = XCreateGC( display(), rootWindow(), GCSubwindowMode, &gcattr );
389 buffer = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(),
390 visual->depth );
391 glxbuffer = glXCreatePixmap( display(), fbcbuffer, buffer, NULL );
392 db = false;
393 XFree( visual );
395 else
397 kError( 1212 ) << "Couldn't create output buffer (failed to create overlay window?) !";
398 return false; // error
400 int vis_buffer;
401 glXGetFBConfigAttrib( display(), fbcbuffer, GLX_VISUAL_ID, &vis_buffer );
402 XVisualInfo* visinfo_buffer = glXGetVisualFromFBConfig( display(), fbcbuffer );
403 kDebug( 1212 ) << "Buffer visual (depth " << visinfo_buffer->depth << "): 0x" << QString::number( vis_buffer, 16 );
404 XFree( visinfo_buffer );
405 return true;
408 // choose the best configs for the destination buffer
409 bool SceneOpenGL::initBufferConfigs()
411 int cnt;
412 GLXFBConfig *fbconfigs = glXGetFBConfigs( display(), DefaultScreen( display() ), &cnt );
413 fbcbuffer_db = NULL;
414 fbcbuffer_nondb = NULL;
416 for( int i = 0; i < 2; i++ )
418 int back, stencil, depth, caveat, alpha;
419 back = i > 0 ? INT_MAX : 1;
420 stencil = INT_MAX;
421 depth = INT_MAX;
422 caveat = INT_MAX;
423 alpha = 0;
424 for( int j = 0; j < cnt; j++ )
426 XVisualInfo *vi;
427 int visual_depth;
428 vi = glXGetVisualFromFBConfig( display(), fbconfigs[ j ] );
429 if( vi == NULL )
430 continue;
431 visual_depth = vi->depth;
432 XFree( vi );
433 if( visual_depth != DefaultDepth( display(), DefaultScreen( display())))
434 continue;
435 int value;
436 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
437 GLX_ALPHA_SIZE, &alpha );
438 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
439 GLX_BUFFER_SIZE, &value );
440 if( value != visual_depth && ( value - alpha ) != visual_depth )
441 continue;
442 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
443 GLX_RENDER_TYPE, &value );
444 if( !( value & GLX_RGBA_BIT ))
445 continue;
446 int back_value;
447 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
448 GLX_DOUBLEBUFFER, &back_value );
449 if( i > 0 )
451 if( back_value > back )
452 continue;
454 else
456 if( back_value < back )
457 continue;
459 int stencil_value;
460 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
461 GLX_STENCIL_SIZE, &stencil_value );
462 if( stencil_value > stencil )
463 continue;
464 int depth_value;
465 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
466 GLX_DEPTH_SIZE, &depth_value );
467 if( depth_value > depth )
468 continue;
469 int caveat_value;
470 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
471 GLX_CONFIG_CAVEAT, &caveat_value );
472 if( caveat_value > caveat )
473 continue;
474 back = back_value;
475 stencil = stencil_value;
476 depth = depth_value;
477 caveat = caveat_value;
478 if( i > 0 )
479 fbcbuffer_nondb = fbconfigs[ j ];
480 else
481 fbcbuffer_db = fbconfigs[ j ];
484 if( cnt )
485 XFree( fbconfigs );
486 if( fbcbuffer_db == NULL && fbcbuffer_nondb == NULL )
488 kError( 1212 ) << "Couldn't find framebuffer configuration for buffer!";
489 return false;
491 for( int i = 0; i <= 32; i++ )
493 if( fbcdrawableinfo[ i ].fbconfig == NULL )
494 continue;
495 int vis_drawable = 0;
496 glXGetFBConfigAttrib( display(), fbcdrawableinfo[ i ].fbconfig, GLX_VISUAL_ID, &vis_drawable );
497 kDebug( 1212 ) << "Drawable visual (depth " << i << "): 0x" << QString::number( vis_drawable, 16 );
499 return true;
502 // make a list of the best configs for windows by depth
503 bool SceneOpenGL::initDrawableConfigs()
505 int cnt;
506 GLXFBConfig *fbconfigs = glXGetFBConfigs( display(), DefaultScreen( display() ), &cnt );
508 for( int i = 0; i <= 32; i++ )
510 int back, stencil, depth, caveat, alpha, mipmap, rgba;
511 back = INT_MAX;
512 stencil = INT_MAX;
513 depth = INT_MAX;
514 caveat = INT_MAX;
515 mipmap = 0;
516 rgba = 0;
517 fbcdrawableinfo[ i ].fbconfig = NULL;
518 fbcdrawableinfo[ i ].bind_texture_format = 0;
519 fbcdrawableinfo[ i ].y_inverted = 0;
520 fbcdrawableinfo[ i ].mipmap = 0;
521 for( int j = 0; j < cnt; j++ )
523 XVisualInfo *vi;
524 int visual_depth;
525 vi = glXGetVisualFromFBConfig( display(), fbconfigs[ j ] );
526 if( vi == NULL )
527 continue;
528 visual_depth = vi->depth;
529 XFree( vi );
530 if( visual_depth != i )
531 continue;
532 int value;
533 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
534 GLX_ALPHA_SIZE, &alpha );
535 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
536 GLX_BUFFER_SIZE, &value );
537 if( value != i && ( value - alpha ) != i )
538 continue;
539 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
540 GLX_RENDER_TYPE, &value );
541 if( !( value & GLX_RGBA_BIT ))
542 continue;
543 if( tfp_mode )
545 value = 0;
546 if( i == 32 )
548 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
549 GLX_BIND_TO_TEXTURE_RGBA_EXT, &value );
550 if( value )
552 // TODO I think this should be set only after the config passes all tests
553 rgba = 1;
554 fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGBA_EXT;
557 if( !value )
559 if( rgba )
560 continue;
561 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
562 GLX_BIND_TO_TEXTURE_RGB_EXT, &value );
563 if( !value )
564 continue;
565 fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGB_EXT;
568 int back_value;
569 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
570 GLX_DOUBLEBUFFER, &back_value );
571 if( back_value > back )
572 continue;
573 int stencil_value;
574 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
575 GLX_STENCIL_SIZE, &stencil_value );
576 if( stencil_value > stencil )
577 continue;
578 int depth_value;
579 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
580 GLX_DEPTH_SIZE, &depth_value );
581 if( depth_value > depth )
582 continue;
583 int mipmap_value = -1;
584 if( tfp_mode && GLTexture::framebufferObjectSupported())
586 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
587 GLX_BIND_TO_MIPMAP_TEXTURE_EXT, &mipmap_value );
588 if( mipmap_value < mipmap )
589 continue;
591 int caveat_value;
592 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
593 GLX_CONFIG_CAVEAT, &caveat_value );
594 if( caveat_value > caveat )
595 continue;
596 // ok, config passed all tests, it's the best one so far
597 fbcdrawableinfo[ i ].fbconfig = fbconfigs[ j ];
598 caveat = caveat_value;
599 back = back_value;
600 stencil = stencil_value;
601 depth = depth_value;
602 mipmap = mipmap_value;
603 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
604 GLX_Y_INVERTED_EXT, &value );
605 fbcdrawableinfo[ i ].y_inverted = value;
606 fbcdrawableinfo[ i ].mipmap = mipmap;
609 if( cnt )
610 XFree( fbconfigs );
611 if( fbcdrawableinfo[ DefaultDepth( display(), DefaultScreen( display())) ].fbconfig == NULL )
613 kError( 1212 ) << "Couldn't find framebuffer configuration for default depth!";
614 return false;
616 if( fbcdrawableinfo[ 32 ].fbconfig == NULL )
618 kError( 1212 ) << "Couldn't find framebuffer configuration for depth 32 (no ARGB GLX visual)!";
619 return false;
621 return true;
624 // Test if compositing actually _really_ works, by creating a texture from a testing
625 // window, drawing it on the screen, reading the contents back and comparing. This
626 // should test whether compositing really works.
627 // It would be still nice to check somehow if compositing is not awfully slow.
628 bool SceneOpenGL::selfCheck()
630 QImage img( 3, 2, QImage::Format_RGB32 );
631 img.setPixel( 0, 0, QColor( Qt::red ).rgb());
632 img.setPixel( 1, 0, QColor( Qt::green ).rgb());
633 img.setPixel( 2, 0, QColor( Qt::blue ).rgb());
634 img.setPixel( 0, 1, QColor( Qt::white ).rgb());
635 img.setPixel( 1, 1, QColor( Qt::black ).rgb());
636 img.setPixel( 2, 1, QColor( Qt::white ).rgb());
637 QPixmap pix = QPixmap::fromImage( img );
638 QList< QPoint > points = selfCheckPoints();
639 QRegion reg;
640 foreach( const QPoint& p, points )
641 reg |= QRect( p, pix.size());
642 if( wspace->overlayWindow())
643 { // avoid covering the whole screen too soon
644 wspace->setOverlayShape( reg );
645 wspace->showOverlay();
647 foreach( const QPoint& p, points )
649 XSetWindowAttributes wa;
650 wa.override_redirect = True;
651 ::Window window = XCreateWindow( display(), rootWindow(), 0, 0, 3, 2, 0, QX11Info::appDepth(),
652 CopyFromParent, CopyFromParent, CWOverrideRedirect, &wa );
653 XSetWindowBackgroundPixmap( display(), window, pix.handle());
654 XClearWindow( display(), window );
655 XMapWindow( display(), window );
656 // move the window one down to where the result will be rendered too, just in case
657 // the render would fail completely and eventual check would try to read this window's contents
658 XMoveWindow( display(), window, p.x() + 1, p.y());
659 XCompositeRedirectWindow( display(), window, CompositeRedirectManual );
660 Pixmap wpix = XCompositeNameWindowPixmap( display(), window );
661 glXWaitX();
662 Texture texture;
663 texture.load( wpix, QSize( 3, 2 ), QX11Info::appDepth());
664 texture.bind();
665 QRect rect( p.x(), p.y(), 3, 2 );
666 texture.render( infiniteRegion(), rect );
667 texture.unbind();
668 glXWaitGL();
669 XFreePixmap( display(), wpix );
670 XDestroyWindow( display(), window );
672 flushBuffer( PAINT_SCREEN_REGION, reg );
673 glXWaitGL();
674 bool ok = true;
675 foreach( const QPoint& p, points )
677 QPixmap pix = QPixmap::grabWindow( rootWindow(), p.x(), p.y(), 3, 2 );
678 QImage img = pix.toImage();
679 // kDebug(1212) << "P:" << QColor( img.pixel( 0, 0 )).name();
680 // kDebug(1212) << "P:" << QColor( img.pixel( 1, 0 )).name();
681 // kDebug(1212) << "P:" << QColor( img.pixel( 2, 0 )).name();
682 // kDebug(1212) << "P:" << QColor( img.pixel( 0, 1 )).name();
683 // kDebug(1212) << "P:" << QColor( img.pixel( 1, 1 )).name();
684 // kDebug(1212) << "P:" << QColor( img.pixel( 2, 1 )).name();
685 if( img.pixel( 0, 0 ) != QColor( Qt::red ).rgb()
686 || img.pixel( 1, 0 ) != QColor( Qt::green ).rgb()
687 || img.pixel( 2, 0 ) != QColor( Qt::blue ).rgb()
688 || img.pixel( 0, 1 ) != QColor( Qt::white ).rgb()
689 || img.pixel( 1, 1 ) != QColor( Qt::black ).rgb()
690 || img.pixel( 2, 1 ) != QColor( Qt::white ).rgb())
692 kError( 1212 ) << "Compositing self-check failed, disabling compositing.";
693 ok = false;
694 break;
697 if( wspace->overlayWindow())
698 wspace->hideOverlay();
699 if( ok )
700 kDebug( 1212 ) << "Compositing self-check passed.";
701 if( !ok && options->disableCompositingChecks )
703 kWarning( 1212 ) << "Compositing checks disabled, proceeding regardless of self-check failure.";
704 return true;
706 return ok;
709 // the entry function for painting
710 void SceneOpenGL::paint( QRegion damage, ToplevelList toplevels )
712 foreach( Toplevel* c, toplevels )
714 assert( windows.contains( c ));
715 stacking_order.append( windows[ c ] );
717 grabXServer();
718 glXWaitX();
719 glPushMatrix();
720 int mask = 0;
721 #ifdef CHECK_GL_ERROR
722 checkGLError( "Paint1" );
723 #endif
724 paintScreen( &mask, &damage ); // call generic implementation
725 #ifdef CHECK_GL_ERROR
726 checkGLError( "Paint2" );
727 #endif
728 glPopMatrix();
729 ungrabXServer(); // ungrab before flushBuffer(), it may wait for vsync
730 if( wspace->overlayWindow()) // show the window only after the first pass, since
731 wspace->showOverlay(); // that pass may take long
732 flushBuffer( mask, damage );
733 // do cleanup
734 stacking_order.clear();
735 checkGLError( "PostPaint" );
738 // wait for vblank signal before painting
739 void SceneOpenGL::waitSync()
740 { // NOTE that vsync has no effect with indirect rendering
741 if( waitSyncAvailable())
743 unsigned int sync;
745 glFlush();
746 glXGetVideoSync( &sync );
747 glXWaitVideoSync( 2, ( sync + 1 ) % 2, &sync );
751 // actually paint to the screen (double-buffer swap or copy from pixmap buffer)
752 void SceneOpenGL::flushBuffer( int mask, QRegion damage )
754 if( db )
756 if( mask & PAINT_SCREEN_REGION )
758 waitSync();
759 if( glXCopySubBuffer )
761 foreach( const QRect &r, damage.rects())
763 // convert to OpenGL coordinates
764 int y = displayHeight() - r.y() - r.height();
765 glXCopySubBuffer( display(), glxbuffer, r.x(), y, r.width(), r.height());
768 else
769 { // no idea why glScissor() is used, but Compiz has it and it doesn't seem to hurt
770 glEnable( GL_SCISSOR_TEST );
771 glDrawBuffer( GL_FRONT );
772 int xpos = 0;
773 int ypos = 0;
774 foreach( const QRect &r, damage.rects())
776 // convert to OpenGL coordinates
777 int y = displayHeight() - r.y() - r.height();
778 // Move raster position relatively using glBitmap() rather
779 // than using glRasterPos2f() - the latter causes drawing
780 // artefacts at the bottom screen edge with some gfx cards
781 // glRasterPos2f( r.x(), r.y() + r.height());
782 glBitmap( 0, 0, 0, 0, r.x() - xpos, y - ypos, NULL );
783 xpos = r.x();
784 ypos = y;
785 glScissor( r.x(), y, r.width(), r.height());
786 glCopyPixels( r.x(), y, r.width(), r.height(), GL_COLOR );
788 glBitmap( 0, 0, 0, 0, -xpos, -ypos, NULL ); // move position back to 0,0
789 glDrawBuffer( GL_BACK );
790 glDisable( GL_SCISSOR_TEST );
793 else
795 waitSync();
796 glXSwapBuffers( display(), glxbuffer );
798 glXWaitGL();
799 XFlush( display());
801 else
803 glFlush();
804 glXWaitGL();
805 waitSync();
806 if( mask & PAINT_SCREEN_REGION )
807 foreach( const QRect &r, damage.rects())
808 XCopyArea( display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y());
809 else
810 XCopyArea( display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0 );
811 XFlush( display());
815 void SceneOpenGL::paintGenericScreen( int mask, ScreenPaintData data )
817 if( mask & PAINT_SCREEN_TRANSFORMED )
818 { // apply screen transformations
819 glPushMatrix();
820 glTranslatef( data.xTranslate, data.yTranslate, data.zTranslate );
821 if( data.rotation )
823 // translate to rotation point, rotate, translate back
824 glTranslatef( data.rotation->xRotationPoint, data.rotation->yRotationPoint, data.rotation->zRotationPoint );
825 float xAxis = 0.0;
826 float yAxis = 0.0;
827 float zAxis = 0.0;
828 switch( data.rotation->axis )
830 case RotationData::XAxis:
831 xAxis = 1.0;
832 break;
833 case RotationData::YAxis:
834 yAxis = 1.0;
835 break;
836 case RotationData::ZAxis:
837 zAxis = 1.0;
838 break;
840 glRotatef( data.rotation->angle, xAxis, yAxis, zAxis );
841 glTranslatef( -data.rotation->xRotationPoint, -data.rotation->yRotationPoint, -data.rotation->zRotationPoint );
843 glScalef( data.xScale, data.yScale, data.zScale );
845 Scene::paintGenericScreen( mask, data );
846 if( mask & PAINT_SCREEN_TRANSFORMED )
847 glPopMatrix();
850 void SceneOpenGL::paintBackground( QRegion region )
852 PaintClipper pc( region );
853 if( !PaintClipper::clip())
855 glPushAttrib( GL_COLOR_BUFFER_BIT );
856 glClearColor( 0, 0, 0, 1 ); // black
857 glClear( GL_COLOR_BUFFER_BIT );
858 glPopAttrib();
859 return;
861 if( pc.clip() && pc.paintArea().isEmpty())
862 return; // no background to paint
863 glPushAttrib( GL_CURRENT_BIT );
864 glColor4f( 0, 0, 0, 1 ); // black
865 for( PaintClipper::Iterator iterator;
866 !iterator.isDone();
867 iterator.next())
869 glBegin( GL_QUADS );
870 QRect r = iterator.boundingRect();
871 glVertex2i( r.x(), r.y());
872 glVertex2i( r.x() + r.width(), r.y());
873 glVertex2i( r.x() + r.width(), r.y() + r.height());
874 glVertex2i( r.x(), r.y() + r.height());
875 glEnd();
877 glPopAttrib();
880 void SceneOpenGL::windowAdded( Toplevel* c )
882 assert( !windows.contains( c ));
883 windows[ c ] = new Window( c );
884 c->effectWindow()->setSceneWindow( windows[ c ]);
887 void SceneOpenGL::windowClosed( Toplevel* c, Deleted* deleted )
889 assert( windows.contains( c ));
890 if( deleted != NULL )
891 { // replace c with deleted
892 Window* w = windows.take( c );
893 w->updateToplevel( deleted );
894 windows[ deleted ] = w;
896 else
898 delete windows.take( c );
899 c->effectWindow()->setSceneWindow( NULL );
903 void SceneOpenGL::windowDeleted( Deleted* c )
905 assert( windows.contains( c ));
906 delete windows.take( c );
907 c->effectWindow()->setSceneWindow( NULL );
910 void SceneOpenGL::windowGeometryShapeChanged( Toplevel* c )
912 if( !windows.contains( c )) // this is ok, shape is not valid
913 return; // by default
914 Window* w = windows[ c ];
915 w->discardShape();
916 w->checkTextureSize();
919 void SceneOpenGL::windowOpacityChanged( Toplevel* )
921 #if 0 // not really needed, windows are painted on every repaint
922 // and opacity is used when applying texture, not when
923 // creating it
924 if( !windows.contains( c )) // this is ok, texture is created
925 return; // on demand
926 Window* w = windows[ c ];
927 w->discardTexture();
928 #endif
931 //****************************************
932 // SceneOpenGL::Texture
933 //****************************************
935 SceneOpenGL::Texture::Texture() : GLTexture()
937 init();
940 SceneOpenGL::Texture::Texture( const Pixmap& pix, const QSize& size, int depth ) : GLTexture()
942 init();
943 load( pix, size, depth );
946 SceneOpenGL::Texture::~Texture()
948 discard();
951 void SceneOpenGL::Texture::init()
953 bound_glxpixmap = None;
956 void SceneOpenGL::Texture::createTexture()
958 glGenTextures( 1, &mTexture );
961 void SceneOpenGL::Texture::discard()
963 if( mTexture != None )
964 release();
965 GLTexture::discard();
968 void SceneOpenGL::Texture::release()
970 if( tfp_mode && bound_glxpixmap != None )
972 if( !options->glStrictBinding )
973 glXReleaseTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT );
974 glXDestroyGLXPixmap( display(), bound_glxpixmap );
975 bound_glxpixmap = None;
979 void SceneOpenGL::Texture::findTarget()
981 unsigned int new_target = 0;
982 if( tfp_mode && glXQueryDrawable && bound_glxpixmap != None )
983 glXQueryDrawable( display(), bound_glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target );
984 // Hack for XGL - this should not be a fallback for glXQueryDrawable() but instead the case
985 // when glXQueryDrawable is not available. However this call fails with XGL, unless KWin
986 // is compiled statically with the libGL that Compiz is built against (without which neither
987 // Compiz works with XGL). Falling back to doing this manually makes this work.
988 if( new_target == 0 )
990 if( NPOTTextureSupported() ||
991 ( isPowerOfTwo( mSize.width()) && isPowerOfTwo( mSize.height())))
992 new_target = GLX_TEXTURE_2D_EXT;
993 else
994 new_target = GLX_TEXTURE_RECTANGLE_EXT;
996 switch( new_target )
998 case GLX_TEXTURE_2D_EXT:
999 mTarget = GL_TEXTURE_2D;
1000 mScale.setWidth( 1.0f / mSize.width());
1001 mScale.setHeight( 1.0f / mSize.height());
1002 break;
1003 case GLX_TEXTURE_RECTANGLE_EXT:
1004 mTarget = GL_TEXTURE_RECTANGLE_ARB;
1005 mScale.setWidth( 1.0f );
1006 mScale.setHeight( 1.0f );
1007 break;
1008 default:
1009 abort();
1013 QRegion SceneOpenGL::Texture::optimizeBindDamage( const QRegion& reg, int limit )
1015 if( reg.rects().count() <= 1 )
1016 return reg;
1017 // try to reduce the number of rects, as especially with SHM mode every rect
1018 // causes X roundtrip, even for very small areas - so, when the size difference
1019 // between all the areas and the bounding rectangle is small, simply use
1020 // only the bounding rectangle
1021 int size = 0;
1022 foreach( const QRect &r, reg.rects())
1023 size += r.width() * r.height();
1024 if( reg.boundingRect().width() * reg.boundingRect().height() - size < limit )
1025 return reg.boundingRect();
1026 return reg;
1029 bool SceneOpenGL::Texture::load( const Pixmap& pix, const QSize& size,
1030 int depth, QRegion region )
1032 #ifdef CHECK_GL_ERROR
1033 checkGLError( "TextureLoad1" );
1034 #endif
1035 if( pix == None || size.isEmpty() || depth < 1 )
1036 return false;
1037 if( tfp_mode )
1039 if( fbcdrawableinfo[ depth ].fbconfig == NULL )
1041 kDebug( 1212 ) << "No framebuffer configuration for depth " << depth
1042 << "; not binding pixmap" << endl;
1043 return false;
1047 mSize = size;
1048 if( mTexture == None || !region.isEmpty())
1049 { // new texture, or texture contents changed; mipmaps now invalid
1050 setDirty();
1053 #ifdef CHECK_GL_ERROR
1054 checkGLError( "TextureLoad2" );
1055 #endif
1056 if( tfp_mode )
1057 { // tfp mode, simply bind the pixmap to texture
1058 if( mTexture == None )
1059 createTexture();
1060 // when the pixmap is bound to the texture, they share the same data, so the texture
1061 // updates automatically - no need to do anything in such case
1062 if( bound_glxpixmap != None )
1063 glBindTexture( mTarget, mTexture );
1064 else
1066 int attrs[] =
1068 GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format,
1069 GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap,
1070 None
1072 // the GLXPixmap will reference the X pixmap, so it will be freed automatically
1073 // when no longer needed
1074 bound_glxpixmap = glXCreatePixmap( display(), fbcdrawableinfo[ depth ].fbconfig, pix, attrs );
1075 #ifdef CHECK_GL_ERROR
1076 checkGLError( "TextureLoadTFP1" );
1077 #endif
1078 findTarget();
1079 y_inverted = fbcdrawableinfo[ depth ].y_inverted ? true : false;
1080 can_use_mipmaps = fbcdrawableinfo[ depth ].mipmap ? true : false;
1081 glBindTexture( mTarget, mTexture );
1082 #ifdef CHECK_GL_ERROR
1083 checkGLError( "TextureLoadTFP2" );
1084 #endif
1085 if( !options->glStrictBinding )
1086 glXBindTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT, NULL );
1089 else if( shm_mode )
1090 { // copy pixmap contents to a texture via shared memory
1091 #ifdef HAVE_XSHM
1092 GLenum pixfmt, type;
1093 if( depth >= 24 )
1095 pixfmt = GL_BGRA;
1096 type = GL_UNSIGNED_BYTE;
1098 else
1099 { // depth 16
1100 pixfmt = GL_RGB;
1101 type = GL_UNSIGNED_SHORT_5_6_5;
1103 findTarget();
1104 #ifdef CHECK_GL_ERROR
1105 checkGLError( "TextureLoadSHM1" );
1106 #endif
1107 if( mTexture == None )
1109 createTexture();
1110 glBindTexture( mTarget, mTexture );
1111 y_inverted = false;
1112 glTexImage2D( mTarget, 0, depth == 32 ? GL_RGBA : GL_RGB,
1113 mSize.width(), mSize.height(), 0,
1114 pixfmt, type, NULL );
1116 else
1117 glBindTexture( mTarget, mTexture );
1118 if( !region.isEmpty())
1120 XGCValues xgcv;
1121 xgcv.graphics_exposures = False;
1122 xgcv.subwindow_mode = IncludeInferiors;
1123 GC gc = XCreateGC( display(), pix, GCGraphicsExposures | GCSubwindowMode, &xgcv );
1124 Pixmap p = XShmCreatePixmap( display(), rootWindow(), shm.shmaddr, &shm,
1125 mSize.width(), mSize.height(), depth );
1126 QRegion damage = optimizeBindDamage( region, 100 * 100 );
1127 glPixelStorei( GL_UNPACK_ROW_LENGTH, mSize.width());
1128 foreach( const QRect &r, damage.rects())
1129 { // TODO for small areas it might be faster to not use SHM to avoid the XSync()
1130 XCopyArea( display(), pix, p, gc, r.x(), r.y(), r.width(), r.height(), 0, 0 );
1131 glXWaitX();
1132 glTexSubImage2D( mTarget, 0,
1133 r.x(), r.y(), r.width(), r.height(),
1134 pixfmt, type, shm.shmaddr );
1135 glXWaitGL();
1137 glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
1138 XFreePixmap( display(), p );
1139 XFreeGC( display(), gc );
1141 #ifdef CHECK_GL_ERROR
1142 checkGLError( "TextureLoadSHM2" );
1143 #endif
1144 y_inverted = true;
1145 can_use_mipmaps = true;
1146 #endif
1148 else
1149 { // fallback, copy pixmap contents to a texture
1150 // note that if depth is not QX11Info::appDepth(), this may
1151 // not work (however, it does seem to work with nvidia)
1152 findTarget();
1153 GLXDrawable pixmap = glXCreatePixmap( display(), fbcdrawableinfo[ QX11Info::appDepth() ].fbconfig, pix, NULL );
1154 glXMakeCurrent( display(), pixmap, ctxdrawable );
1155 if( last_pixmap != None )
1156 glXDestroyPixmap( display(), last_pixmap );
1157 // workaround for ATI - it leaks/crashes when the pixmap is destroyed immediately
1158 // here (http://lists.kde.org/?l=kwin&m=116353772208535&w=2)
1159 last_pixmap = pixmap;
1160 glReadBuffer( GL_FRONT );
1161 glDrawBuffer( GL_FRONT );
1162 if( mTexture == None )
1164 createTexture();
1165 glBindTexture( mTarget, mTexture );
1166 y_inverted = false;
1167 glCopyTexImage2D( mTarget, 0,
1168 depth == 32 ? GL_RGBA : GL_RGB,
1169 0, 0, mSize.width(), mSize.height(), 0 );
1171 else
1173 glBindTexture( mTarget, mTexture );
1174 QRegion damage = optimizeBindDamage( region, 30 * 30 );
1175 foreach( const QRect &r, damage.rects())
1177 // convert to OpenGL coordinates (this is mapping
1178 // the pixmap to a texture, this is not affected
1179 // by using glOrtho() for the OpenGL scene)
1180 int gly = mSize.height() - r.y() - r.height();
1181 glCopyTexSubImage2D( mTarget, 0,
1182 r.x(), gly, r.x(), gly, r.width(), r.height());
1185 glXWaitGL();
1186 if( db )
1187 glDrawBuffer( GL_BACK );
1188 glXMakeCurrent( display(), glxbuffer, ctxbuffer );
1189 glBindTexture( mTarget, mTexture );
1190 y_inverted = false;
1191 can_use_mipmaps = true;
1193 #ifdef CHECK_GL_ERROR
1194 checkGLError( "TextureLoad0" );
1195 #endif
1196 return true;
1199 bool SceneOpenGL::Texture::load( const Pixmap& pix, const QSize& size,
1200 int depth )
1202 return load( pix, size, depth,
1203 QRegion( 0, 0, size.width(), size.height()));
1206 bool SceneOpenGL::Texture::load( const QImage& image, GLenum target )
1208 if( image.isNull())
1209 return false;
1210 return load( QPixmap::fromImage( image ), target );
1213 bool SceneOpenGL::Texture::load( const QPixmap& pixmap, GLenum target )
1215 Q_UNUSED( target ); // SceneOpenGL::Texture::findTarget() detects the target
1216 if( pixmap.isNull())
1217 return false;
1218 return load( pixmap.handle(), pixmap.size(), pixmap.depth());
1221 void SceneOpenGL::Texture::bind()
1223 glEnable( mTarget );
1224 glBindTexture( mTarget, mTexture );
1225 if( tfp_mode && options->glStrictBinding )
1227 assert( bound_glxpixmap != None );
1228 glXBindTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT, NULL );
1230 enableFilter();
1231 if( hasGLVersion( 1, 4, 0 ))
1233 // Lod bias makes the trilinear-filtered texture look a bit sharper
1234 glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0f );
1238 void SceneOpenGL::Texture::unbind()
1240 if( hasGLVersion( 1, 4, 0 ))
1242 glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f );
1244 if( tfp_mode && options->glStrictBinding )
1246 assert( bound_glxpixmap != None );
1247 glBindTexture( mTarget, mTexture );
1248 glXReleaseTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT );
1250 GLTexture::unbind();
1253 //****************************************
1254 // SceneOpenGL::Window
1255 //****************************************
1257 SceneOpenGL::Window::Window( Toplevel* c )
1258 : Scene::Window( c )
1259 , texture()
1263 SceneOpenGL::Window::~Window()
1265 discardTexture();
1268 // Bind the window pixmap to an OpenGL texture.
1269 bool SceneOpenGL::Window::bindTexture()
1271 if( texture.texture() != None && toplevel->damage().isEmpty())
1273 // texture doesn't need updating, just bind it
1274 glBindTexture( texture.target(), texture.texture());
1275 return true;
1277 // Get the pixmap with the window contents
1278 Pixmap pix = toplevel->windowPixmap();
1279 if( pix == None )
1280 return false;
1281 bool success = texture.load( pix, toplevel->size(), toplevel->depth(),
1282 toplevel->damage());
1283 if( success )
1284 toplevel->resetDamage( toplevel->rect());
1285 else
1286 kDebug( 1212 ) << "Failed to bind window";
1287 return success;
1290 void SceneOpenGL::Window::discardTexture()
1292 texture.discard();
1295 // This call is used in SceneOpenGL::windowGeometryShapeChanged(),
1296 // which originally called discardTexture(), however this was causing performance
1297 // problems with the launch feedback icon - large number of texture rebinds.
1298 // Since the launch feedback icon does not resize, only changes shape, it
1299 // is not necessary to rebind the texture (with no strict binding), therefore
1300 // discard the texture only if size changes.
1301 void SceneOpenGL::Window::checkTextureSize()
1303 if( texture.size() != size())
1304 discardTexture();
1307 // when the window's composite pixmap is discarded, undo binding it to the texture
1308 void SceneOpenGL::Window::pixmapDiscarded()
1310 texture.release();
1313 // paint the window
1314 void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintData data )
1316 // check if there is something to paint (e.g. don't paint if the window
1317 // is only opaque and only PAINT_WINDOW_TRANSLUCENT is requested)
1318 bool opaque = isOpaque() && data.opacity == 1.0;
1319 /* HACK: It seems this causes painting glitches, disable temporarily
1320 if(( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT ))
1321 { // We are only painting either opaque OR translucent windows, not both
1322 if( mask & PAINT_WINDOW_OPAQUE && !opaque )
1323 return; // Only painting opaque and window is translucent
1324 if( mask & PAINT_WINDOW_TRANSLUCENT && opaque )
1325 return; // Only painting translucent and window is opaque
1327 // paint only requested areas
1328 if( region != infiniteRegion()) // avoid integer overflow
1329 region.translate( -x(), -y());
1330 if(( mask & ( PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED )) == 0 )
1331 region &= shape();
1332 if( region.isEmpty())
1333 return;
1334 if( !bindTexture())
1335 return;
1336 glPushMatrix();
1337 // set texture filter
1338 if( options->smoothScale != 0 ) // default to yes
1340 if( mask & PAINT_WINDOW_TRANSFORMED )
1341 filter = ImageFilterGood;
1342 else if( mask & PAINT_SCREEN_TRANSFORMED )
1343 filter = ImageFilterGood;
1344 else
1345 filter = ImageFilterFast;
1347 else
1348 filter = ImageFilterFast;
1349 if( filter == ImageFilterGood )
1351 // avoid unneeded mipmap generation by only using trilinear
1352 // filtering when it actually makes a difference, that is with
1353 // minification or changed vertices
1354 if( options->smoothScale == 2
1355 && ( data.quads.smoothNeeded() || data.xScale < 1 || data.yScale < 1 ))
1357 texture.setFilter( GL_LINEAR_MIPMAP_LINEAR );
1359 else
1360 texture.setFilter( GL_LINEAR );
1362 else
1363 texture.setFilter( GL_NEAREST );
1364 // do required transformations
1365 int x = toplevel->x();
1366 int y = toplevel->y();
1367 double z = 0.0;
1368 if( mask & PAINT_WINDOW_TRANSFORMED )
1370 x += data.xTranslate;
1371 y += data.yTranslate;
1372 z += data.zTranslate;
1374 glTranslatef( x, y, z );
1375 if(( mask & PAINT_WINDOW_TRANSFORMED ) && ( data.xScale != 1 || data.yScale != 1 || data.zScale != 1 ))
1376 glScalef( data.xScale, data.yScale, data.zScale );
1377 if(( mask & PAINT_WINDOW_TRANSFORMED ) && data.rotation )
1379 glTranslatef( data.rotation->xRotationPoint, data.rotation->yRotationPoint, data.rotation->zRotationPoint );
1380 float xAxis = 0.0;
1381 float yAxis = 0.0;
1382 float zAxis = 0.0;
1383 switch( data.rotation->axis )
1385 case RotationData::XAxis:
1386 xAxis = 1.0;
1387 break;
1388 case RotationData::YAxis:
1389 yAxis = 1.0;
1390 break;
1391 case RotationData::ZAxis:
1392 zAxis = 1.0;
1393 break;
1395 glRotatef( data.rotation->angle, xAxis, yAxis, zAxis );
1396 glTranslatef( -data.rotation->xRotationPoint, -data.rotation->yRotationPoint, -data.rotation->zRotationPoint );
1398 region.translate( toplevel->x(), toplevel->y() ); // Back to screen coords
1400 texture.bind();
1401 texture.enableUnnormalizedTexCoords();
1403 WindowQuadList decoration = data.quads.select( WindowQuadDecoration );
1404 if( data.contents_opacity != data.decoration_opacity && !decoration.isEmpty())
1406 prepareStates( data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader );
1407 renderQuads( mask, region, data.quads.select( WindowQuadContents ));
1408 restoreStates( data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader );
1409 prepareStates( data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader );
1410 renderQuads( mask, region, decoration );
1411 restoreStates( data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader );
1413 else
1415 prepareStates( data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader );
1416 renderQuads( mask, region, data.quads.select( WindowQuadContents ));
1417 renderQuads( mask, region, data.quads.select( WindowQuadDecoration ));
1418 restoreStates( data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader );
1421 texture.disableUnnormalizedTexCoords();
1422 texture.unbind();
1423 glPopMatrix();
1426 void SceneOpenGL::Window::renderQuads( int, const QRegion& region, const WindowQuadList& quads )
1428 if( quads.isEmpty())
1429 return;
1430 // Render geometry
1431 float* vertices;
1432 float* texcoords;
1433 quads.makeArrays( &vertices, &texcoords );
1434 renderGLGeometry( region, quads.count() * 4,
1435 vertices, texcoords, NULL, 2, 0 );
1436 delete[] vertices;
1437 delete[] texcoords;
1440 void SceneOpenGL::Window::prepareStates( double opacity, double brightness, double saturation, GLShader* shader )
1442 if(shader)
1443 prepareShaderRenderStates( opacity, brightness, saturation, shader );
1444 else
1445 prepareRenderStates( opacity, brightness, saturation );
1448 void SceneOpenGL::Window::prepareShaderRenderStates( double opacity, double brightness, double saturation, GLShader* shader )
1450 // setup blending of transparent windows
1451 glPushAttrib( GL_ENABLE_BIT );
1452 bool opaque = isOpaque() && opacity == 1.0;
1453 if( !opaque )
1455 glEnable( GL_BLEND );
1456 glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
1458 shader->setUniform("opacity", (float)opacity);
1459 shader->setUniform("saturation", (float)saturation);
1460 shader->setUniform("brightness", (float)brightness);
1463 void SceneOpenGL::Window::prepareRenderStates( double opacity, double brightness, double saturation )
1465 // setup blending of transparent windows
1466 glPushAttrib( GL_ENABLE_BIT );
1467 bool opaque = isOpaque() && opacity == 1.0;
1468 if( !opaque )
1470 glEnable( GL_BLEND );
1471 glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
1473 if( saturation != 1.0 && texture.saturationSupported())
1475 // First we need to get the color from [0; 1] range to [0.5; 1] range
1476 glActiveTexture( GL_TEXTURE0 );
1477 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
1478 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
1479 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE );
1480 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
1481 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
1482 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
1483 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT );
1484 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );
1485 const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5};
1486 glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant );
1487 texture.bind();
1489 // Then we take dot product of the result of previous pass and
1490 // saturation_constant. This gives us completely unsaturated
1491 // (greyscale) image
1492 // Note that both operands have to be in range [0.5; 1] since opengl
1493 // automatically substracts 0.5 from them
1494 glActiveTexture( GL_TEXTURE1 );
1495 float saturation_constant[] = { 0.5 + 0.5*0.30, 0.5 + 0.5*0.59, 0.5 + 0.5*0.11, saturation };
1496 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
1497 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB );
1498 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS );
1499 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
1500 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
1501 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
1502 glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant );
1503 texture.bind();
1505 // Finally we need to interpolate between the original image and the
1506 // greyscale image to get wanted level of saturation
1507 glActiveTexture( GL_TEXTURE2 );
1508 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
1509 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
1510 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 );
1511 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
1512 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS );
1513 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
1514 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT );
1515 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );
1516 glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant );
1517 // Also replace alpha by primary color's alpha here
1518 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
1519 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR );
1520 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
1521 // And make primary color contain the wanted opacity
1522 glColor4f( opacity, opacity, opacity, opacity );
1523 texture.bind();
1525 if( toplevel->hasAlpha() || brightness != 1.0f )
1527 glActiveTexture( GL_TEXTURE3 );
1528 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
1529 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE );
1530 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS );
1531 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
1532 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR );
1533 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
1534 // The color has to be multiplied by both opacity and brightness
1535 float opacityByBrightness = opacity * brightness;
1536 glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity );
1537 if( toplevel->hasAlpha() )
1539 // Multiply original texture's alpha by our opacity
1540 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
1541 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0 );
1542 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
1543 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR );
1544 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );
1546 else
1548 // Alpha will be taken from previous stage
1549 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
1550 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS );
1551 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
1553 texture.bind();
1556 glActiveTexture(GL_TEXTURE0 );
1558 else if( opacity != 1.0 || brightness != 1.0 )
1560 // the window is additionally configured to have its opacity adjusted,
1561 // do it
1562 float opacityByBrightness = opacity * brightness;
1563 if( toplevel->hasAlpha())
1565 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
1566 glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness,
1567 opacity);
1569 else
1571 // Multiply color by brightness and replace alpha by opacity
1572 float constant[] = { opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity };
1573 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
1574 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE );
1575 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE );
1576 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
1577 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
1578 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
1579 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
1580 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT );
1581 glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant );
1586 void SceneOpenGL::Window::restoreStates( double opacity, double brightness, double saturation, GLShader* shader )
1588 if(shader)
1589 restoreShaderRenderStates( opacity, brightness, saturation, shader );
1590 else
1591 restoreRenderStates( opacity, brightness, saturation );
1594 void SceneOpenGL::Window::restoreShaderRenderStates( double opacity, double brightness, double saturation, GLShader* shader )
1596 Q_UNUSED( opacity );
1597 Q_UNUSED( brightness );
1598 Q_UNUSED( saturation );
1599 Q_UNUSED( shader );
1600 glPopAttrib(); // ENABLE_BIT
1603 void SceneOpenGL::Window::restoreRenderStates( double opacity, double brightness, double saturation )
1605 if( opacity != 1.0 || saturation != 1.0 || brightness != 1.0f )
1607 if( saturation != 1.0 && texture.saturationSupported())
1609 glActiveTexture(GL_TEXTURE3);
1610 glDisable( texture.target());
1611 glActiveTexture(GL_TEXTURE2);
1612 glDisable( texture.target());
1613 glActiveTexture(GL_TEXTURE1);
1614 glDisable( texture.target());
1615 glActiveTexture(GL_TEXTURE0);
1617 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1618 glColor4f( 0, 0, 0, 0 );
1621 glPopAttrib(); // ENABLE_BIT
1624 } // namespace
1626 #endif