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
29 Sources and other compositing managers:
30 =======================================
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/)
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"
48 - git clone git://anongit.freedesktop.org/git/xorg/app/compiz
49 - the ultimate <whatever>
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
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/)
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>
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>
94 //****************************************
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 ];
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
;
113 XShmSegmentInfo
SceneOpenGL::shm
;
117 SceneOpenGL::SceneOpenGL( Workspace
* ws
)
121 if( !Extensions::glxAvailable())
123 kDebug( 1212 ) << "No glx extensions available";
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";
137 if( !initBuffer()) // create destination buffer
139 if( !initRenderingContext())
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";
150 glDrawBuffer( GL_BACK
);
151 // Check whether certain features are supported
152 has_waitSync
= false;
153 if( glXGetVideoSync
&& glXIsDirect( display(), ctxbuffer
) && options
->glVSync
)
156 if( glXGetVideoSync( &sync
) == 0 )
158 if( glXWaitVideoSync( 1, 0, &sync
) == 0 )
163 // OpenGL scene setup
164 glMatrixMode( GL_PROJECTION
);
170 float ymax
= zNear
* tan( fovy
* M_PI
/ 360.0f
);
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
);
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";
188 kDebug( 1212 ) << "DB:" << db
<< ", TFP:" << tfp_mode
<< ", SHM:" << shm_mode
189 << ", Direct:" << bool( glXIsDirect( display(), ctxbuffer
)) << endl
;
193 SceneOpenGL::~SceneOpenGL()
197 // TODO this probably needs to clean up whatever has been created until the failure
198 wspace
->destroyOverlay();
201 foreach( Window
* w
, windows
)
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();
215 glXDestroyPixmap( display(), glxbuffer
);
216 XFreeGC( display(), gcroot
);
217 XFreePixmap( display(), buffer
);
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
235 bool SceneOpenGL::selectMode()
237 // select mode - try TFP first, then SHM, otherwise fallback mode
240 if( options
->glMode
== Options::GLTFP
)
247 else if( options
->glMode
== Options::GLSHM
)
254 if( !initDrawableConfigs())
259 bool SceneOpenGL::initTfp()
261 if( glXBindTexImageEXT
== NULL
|| glXReleaseTexImageEXT
== NULL
)
266 bool SceneOpenGL::initShm()
271 if( !XShmQueryVersion( display(), &major
, &minor
, &pixmaps
) || !pixmaps
)
273 if( XShmPixmapFormat( display()) != ZPixmap
)
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 );
281 shm
.shmaddr
= ( char* ) shmat( shm
.shmid
, NULL
, 0 );
282 if( shm
.shmaddr
== ( void * ) -1 )
284 shmctl( shm
.shmid
, IPC_RMID
, 0 );
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 );
293 XShmAttach( display(), &shm
);
294 if( errs
.error( true ))
297 shmctl( shm
.shmid
, IPC_RMID
, 0 );
299 shmdt( shm
.shmaddr
);
308 void SceneOpenGL::cleanupShm()
311 shmdt( shm
.shmaddr
);
313 shmctl( shm
.shmid
, IPC_RMID
, 0 );
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
331 if( !direct_rendering
)
333 kDebug( 1212 ).nospace() << "Couldn't initialize rendering context ("
334 << KXErrorHandler::errorMessage( errs1
.errorEvent()) << ")";
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 ))
348 kDebug( 1212 ).nospace() << "Couldn't initialize rendering context ("
349 << KXErrorHandler::errorMessage( errs2
.errorEvent()) << ")";
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
);
361 // create destination buffer
362 bool SceneOpenGL::initBuffer()
364 if( !initBufferConfigs())
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
);
378 wspace
->setupOverlay( buffer
);
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
);
387 gcattr
.subwindow_mode
= IncludeInferiors
;
388 gcroot
= XCreateGC( display(), rootWindow(), GCSubwindowMode
, &gcattr
);
389 buffer
= XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(),
391 glxbuffer
= glXCreatePixmap( display(), fbcbuffer
, buffer
, NULL
);
397 kError( 1212 ) << "Couldn't create output buffer (failed to create overlay window?) !";
398 return false; // error
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
);
408 // choose the best configs for the destination buffer
409 bool SceneOpenGL::initBufferConfigs()
412 GLXFBConfig
*fbconfigs
= glXGetFBConfigs( display(), DefaultScreen( display() ), &cnt
);
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;
424 for( int j
= 0; j
< cnt
; j
++ )
428 vi
= glXGetVisualFromFBConfig( display(), fbconfigs
[ j
] );
431 visual_depth
= vi
->depth
;
433 if( visual_depth
!= DefaultDepth( display(), DefaultScreen( display())))
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
)
442 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
443 GLX_RENDER_TYPE
, &value
);
444 if( !( value
& GLX_RGBA_BIT
))
447 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
448 GLX_DOUBLEBUFFER
, &back_value
);
451 if( back_value
> back
)
456 if( back_value
< back
)
460 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
461 GLX_STENCIL_SIZE
, &stencil_value
);
462 if( stencil_value
> stencil
)
465 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
466 GLX_DEPTH_SIZE
, &depth_value
);
467 if( depth_value
> depth
)
470 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
471 GLX_CONFIG_CAVEAT
, &caveat_value
);
472 if( caveat_value
> caveat
)
475 stencil
= stencil_value
;
477 caveat
= caveat_value
;
479 fbcbuffer_nondb
= fbconfigs
[ j
];
481 fbcbuffer_db
= fbconfigs
[ j
];
486 if( fbcbuffer_db
== NULL
&& fbcbuffer_nondb
== NULL
)
488 kError( 1212 ) << "Couldn't find framebuffer configuration for buffer!";
491 for( int i
= 0; i
<= 32; i
++ )
493 if( fbcdrawableinfo
[ i
].fbconfig
== NULL
)
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 );
502 // make a list of the best configs for windows by depth
503 bool SceneOpenGL::initDrawableConfigs()
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
;
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
++ )
525 vi
= glXGetVisualFromFBConfig( display(), fbconfigs
[ j
] );
528 visual_depth
= vi
->depth
;
530 if( visual_depth
!= i
)
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
)
539 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
540 GLX_RENDER_TYPE
, &value
);
541 if( !( value
& GLX_RGBA_BIT
))
548 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
549 GLX_BIND_TO_TEXTURE_RGBA_EXT
, &value
);
552 // TODO I think this should be set only after the config passes all tests
554 fbcdrawableinfo
[ i
].bind_texture_format
= GLX_TEXTURE_FORMAT_RGBA_EXT
;
561 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
562 GLX_BIND_TO_TEXTURE_RGB_EXT
, &value
);
565 fbcdrawableinfo
[ i
].bind_texture_format
= GLX_TEXTURE_FORMAT_RGB_EXT
;
569 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
570 GLX_DOUBLEBUFFER
, &back_value
);
571 if( back_value
> back
)
574 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
575 GLX_STENCIL_SIZE
, &stencil_value
);
576 if( stencil_value
> stencil
)
579 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
580 GLX_DEPTH_SIZE
, &depth_value
);
581 if( depth_value
> depth
)
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
)
592 glXGetFBConfigAttrib( display(), fbconfigs
[ j
],
593 GLX_CONFIG_CAVEAT
, &caveat_value
);
594 if( caveat_value
> caveat
)
596 // ok, config passed all tests, it's the best one so far
597 fbcdrawableinfo
[ i
].fbconfig
= fbconfigs
[ j
];
598 caveat
= caveat_value
;
600 stencil
= stencil_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
;
611 if( fbcdrawableinfo
[ DefaultDepth( display(), DefaultScreen( display())) ].fbconfig
== NULL
)
613 kError( 1212 ) << "Couldn't find framebuffer configuration for default depth!";
616 if( fbcdrawableinfo
[ 32 ].fbconfig
== NULL
)
618 kError( 1212 ) << "Couldn't find framebuffer configuration for depth 32 (no ARGB GLX visual)!";
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();
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
);
663 texture
.load( wpix
, QSize( 3, 2 ), QX11Info::appDepth());
665 QRect
rect( p
.x(), p
.y(), 3, 2 );
666 texture
.render( infiniteRegion(), rect
);
669 XFreePixmap( display(), wpix
);
670 XDestroyWindow( display(), window
);
672 flushBuffer( PAINT_SCREEN_REGION
, reg
);
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.";
697 if( wspace
->overlayWindow())
698 wspace
->hideOverlay();
700 kDebug( 1212 ) << "Compositing self-check passed.";
701 if( !ok
&& options
->disableCompositingChecks
)
703 kWarning( 1212 ) << "Compositing checks disabled, proceeding regardless of self-check failure.";
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
] );
721 #ifdef CHECK_GL_ERROR
722 checkGLError( "Paint1" );
724 paintScreen( &mask
, &damage
); // call generic implementation
725 #ifdef CHECK_GL_ERROR
726 checkGLError( "Paint2" );
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
);
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())
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
)
756 if( mask
& PAINT_SCREEN_REGION
)
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());
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
);
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
);
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
);
796 glXSwapBuffers( display(), glxbuffer
);
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());
810 XCopyArea( display(), buffer
, rootWindow(), gcroot
, 0, 0, displayWidth(), displayHeight(), 0, 0 );
815 void SceneOpenGL::paintGenericScreen( int mask
, ScreenPaintData data
)
817 if( mask
& PAINT_SCREEN_TRANSFORMED
)
818 { // apply screen transformations
820 glTranslatef( data
.xTranslate
, data
.yTranslate
, data
.zTranslate
);
823 // translate to rotation point, rotate, translate back
824 glTranslatef( data
.rotation
->xRotationPoint
, data
.rotation
->yRotationPoint
, data
.rotation
->zRotationPoint
);
828 switch( data
.rotation
->axis
)
830 case RotationData::XAxis
:
833 case RotationData::YAxis
:
836 case RotationData::ZAxis
:
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
)
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
);
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
;
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());
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
;
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
];
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
924 if( !windows
.contains( c
)) // this is ok, texture is created
926 Window
* w
= windows
[ c
];
931 //****************************************
932 // SceneOpenGL::Texture
933 //****************************************
935 SceneOpenGL::Texture::Texture() : GLTexture()
940 SceneOpenGL::Texture::Texture( const Pixmap
& pix
, const QSize
& size
, int depth
) : GLTexture()
943 load( pix
, size
, depth
);
946 SceneOpenGL::Texture::~Texture()
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
)
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
;
994 new_target
= GLX_TEXTURE_RECTANGLE_EXT
;
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());
1003 case GLX_TEXTURE_RECTANGLE_EXT
:
1004 mTarget
= GL_TEXTURE_RECTANGLE_ARB
;
1005 mScale
.setWidth( 1.0f
);
1006 mScale
.setHeight( 1.0f
);
1013 QRegion
SceneOpenGL::Texture::optimizeBindDamage( const QRegion
& reg
, int limit
)
1015 if( reg
.rects().count() <= 1 )
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
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();
1029 bool SceneOpenGL::Texture::load( const Pixmap
& pix
, const QSize
& size
,
1030 int depth
, QRegion region
)
1032 #ifdef CHECK_GL_ERROR
1033 checkGLError( "TextureLoad1" );
1035 if( pix
== None
|| size
.isEmpty() || depth
< 1 )
1039 if( fbcdrawableinfo
[ depth
].fbconfig
== NULL
)
1041 kDebug( 1212 ) << "No framebuffer configuration for depth " << depth
1042 << "; not binding pixmap" << endl
;
1048 if( mTexture
== None
|| !region
.isEmpty())
1049 { // new texture, or texture contents changed; mipmaps now invalid
1053 #ifdef CHECK_GL_ERROR
1054 checkGLError( "TextureLoad2" );
1057 { // tfp mode, simply bind the pixmap to texture
1058 if( mTexture
== None
)
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
);
1068 GLX_TEXTURE_FORMAT_EXT
, fbcdrawableinfo
[ depth
].bind_texture_format
,
1069 GLX_MIPMAP_TEXTURE_EXT
, fbcdrawableinfo
[ depth
].mipmap
,
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" );
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" );
1085 if( !options
->glStrictBinding
)
1086 glXBindTexImageEXT( display(), bound_glxpixmap
, GLX_FRONT_LEFT_EXT
, NULL
);
1090 { // copy pixmap contents to a texture via shared memory
1092 GLenum pixfmt
, type
;
1096 type
= GL_UNSIGNED_BYTE
;
1101 type
= GL_UNSIGNED_SHORT_5_6_5
;
1104 #ifdef CHECK_GL_ERROR
1105 checkGLError( "TextureLoadSHM1" );
1107 if( mTexture
== None
)
1110 glBindTexture( mTarget
, mTexture
);
1112 glTexImage2D( mTarget
, 0, depth
== 32 ? GL_RGBA
: GL_RGB
,
1113 mSize
.width(), mSize
.height(), 0,
1114 pixfmt
, type
, NULL
);
1117 glBindTexture( mTarget
, mTexture
);
1118 if( !region
.isEmpty())
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 );
1132 glTexSubImage2D( mTarget
, 0,
1133 r
.x(), r
.y(), r
.width(), r
.height(),
1134 pixfmt
, type
, shm
.shmaddr
);
1137 glPixelStorei( GL_UNPACK_ROW_LENGTH
, 0 );
1138 XFreePixmap( display(), p
);
1139 XFreeGC( display(), gc
);
1141 #ifdef CHECK_GL_ERROR
1142 checkGLError( "TextureLoadSHM2" );
1145 can_use_mipmaps
= true;
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)
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
)
1165 glBindTexture( mTarget
, mTexture
);
1167 glCopyTexImage2D( mTarget
, 0,
1168 depth
== 32 ? GL_RGBA
: GL_RGB
,
1169 0, 0, mSize
.width(), mSize
.height(), 0 );
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());
1187 glDrawBuffer( GL_BACK
);
1188 glXMakeCurrent( display(), glxbuffer
, ctxbuffer
);
1189 glBindTexture( mTarget
, mTexture
);
1191 can_use_mipmaps
= true;
1193 #ifdef CHECK_GL_ERROR
1194 checkGLError( "TextureLoad0" );
1199 bool SceneOpenGL::Texture::load( const Pixmap
& pix
, const QSize
& size
,
1202 return load( pix
, size
, depth
,
1203 QRegion( 0, 0, size
.width(), size
.height()));
1206 bool SceneOpenGL::Texture::load( const QImage
& image
, GLenum target
)
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())
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
);
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
)
1263 SceneOpenGL::Window::~Window()
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());
1277 // Get the pixmap with the window contents
1278 Pixmap pix
= toplevel
->windowPixmap();
1281 bool success
= texture
.load( pix
, toplevel
->size(), toplevel
->depth(),
1282 toplevel
->damage());
1284 toplevel
->resetDamage( toplevel
->rect());
1286 kDebug( 1212 ) << "Failed to bind window";
1290 void SceneOpenGL::Window::discardTexture()
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())
1307 // when the window's composite pixmap is discarded, undo binding it to the texture
1308 void SceneOpenGL::Window::pixmapDiscarded()
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 )
1332 if( region
.isEmpty())
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
;
1345 filter
= ImageFilterFast
;
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
);
1360 texture
.setFilter( GL_LINEAR
);
1363 texture
.setFilter( GL_NEAREST
);
1364 // do required transformations
1365 int x
= toplevel
->x();
1366 int y
= toplevel
->y();
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
);
1383 switch( data
.rotation
->axis
)
1385 case RotationData::XAxis
:
1388 case RotationData::YAxis
:
1391 case RotationData::ZAxis
:
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
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
);
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();
1426 void SceneOpenGL::Window::renderQuads( int, const QRegion
& region
, const WindowQuadList
& quads
)
1428 if( quads
.isEmpty())
1433 quads
.makeArrays( &vertices
, &texcoords
);
1434 renderGLGeometry( region
, quads
.count() * 4,
1435 vertices
, texcoords
, NULL
, 2, 0 );
1440 void SceneOpenGL::Window::prepareStates( double opacity
, double brightness
, double saturation
, GLShader
* shader
)
1443 prepareShaderRenderStates( opacity
, brightness
, saturation
, shader
);
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;
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;
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
);
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
);
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
);
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
);
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
);
1556 glActiveTexture(GL_TEXTURE0
);
1558 else if( opacity
!= 1.0 || brightness
!= 1.0 )
1560 // the window is additionally configured to have its opacity adjusted,
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
,
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
)
1589 restoreShaderRenderStates( opacity
, brightness
, saturation
, shader
);
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
);
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