• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

scene_opengl.cpp

Go to the documentation of this file.
00001 /********************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
00006 
00007 Based on glcompmgr code by Felix Bellaby.
00008 Using code from Compiz and Beryl.
00009 
00010 This program is free software; you can redistribute it and/or modify
00011 it under the terms of the GNU General Public License as published by
00012 the Free Software Foundation; either version 2 of the License, or
00013 (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022 *********************************************************************/
00023 
00024 
00025 /*
00026  This is the OpenGL-based compositing code. It is the primary and most powerful
00027  compositing backend.
00028  
00029 Sources and other compositing managers:
00030 =======================================
00031 
00032 - http://opengl.org
00033     - documentation
00034         - OpenGL Redbook (http://opengl.org/documentation/red_book/ - note it's only version 1.1)
00035         - GLX docs (http://opengl.org/documentation/specs/glx/glx1.4.pdf)
00036         - extensions docs (http://www.opengl.org/registry/)
00037 
00038 - glcompmgr
00039     - http://lists.freedesktop.org/archives/xorg/2006-July/017006.html ,
00040     - http://www.mail-archive.com/compiz%40lists.freedesktop.org/msg00023.html
00041     - simple and easy to understand
00042     - works even without texture_from_pixmap extension
00043     - claims to support several different gfx cards
00044     - compile with something like
00045       "gcc -Wall glcompmgr-0.5.c `pkg-config --cflags --libs glib-2.0` -lGL -lXcomposite -lXdamage -L/usr/X11R6/lib"
00046 
00047 - compiz
00048     - git clone git://anongit.freedesktop.org/git/xorg/app/compiz
00049     - the ultimate <whatever>
00050     - glxcompmgr
00051         - git clone git://anongit.freedesktop.org/git/xorg/app/glxcompgr
00052         - a rather old version of compiz, but also simpler and as such simpler
00053             to understand
00054 
00055 - beryl
00056     - a fork of Compiz
00057     - http://beryl-project.org
00058     - git clone git://anongit.beryl-project.org/beryl/beryl-core (or beryl-plugins etc. ,
00059         the full list should be at git://anongit.beryl-project.org/beryl/)
00060 
00061 - libcm (metacity)
00062     - cvs -d :pserver:anonymous@anoncvs.gnome.org:/cvs/gnome co libcm
00063     - not much idea about it, the model differs a lot from KWin/Compiz/Beryl
00064     - does not seem to be very powerful or with that much development going on
00065 
00066 */
00067 
00068 #include "scene_opengl.h"
00069 
00070 #include <kxerrorhandler.h>
00071 
00072 #include "utils.h"
00073 #include "client.h"
00074 #include "deleted.h"
00075 #include "effects.h"
00076 
00077 #include <sys/ipc.h>
00078 #include <sys/shm.h>
00079 #include <math.h>
00080 
00081 // turns on checks for opengl errors in various places (for easier finding of them)
00082 // normally only few of them are enabled
00083 //#define CHECK_GL_ERROR
00084 
00085 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
00086 
00087 namespace KWin
00088 {
00089 
00090 //****************************************
00091 // SceneOpenGL
00092 //****************************************
00093 
00094 // the configs used for the destination
00095 GLXFBConfig SceneOpenGL::fbcbuffer_db;
00096 GLXFBConfig SceneOpenGL::fbcbuffer_nondb;
00097 // the configs used for windows
00098 SceneOpenGL::FBConfigInfo SceneOpenGL::fbcdrawableinfo[ 32 + 1 ];
00099 // GLX content
00100 GLXContext SceneOpenGL::ctxbuffer;
00101 GLXContext SceneOpenGL::ctxdrawable;
00102 // the destination drawable where the compositing is done
00103 GLXDrawable SceneOpenGL::glxbuffer = None;
00104 GLXDrawable SceneOpenGL::last_pixmap = None;
00105 bool SceneOpenGL::tfp_mode; // using glXBindTexImageEXT (texture_from_pixmap)
00106 bool SceneOpenGL::db; // destination drawable is double-buffered
00107 bool SceneOpenGL::shm_mode;
00108 #ifdef HAVE_XSHM
00109 XShmSegmentInfo SceneOpenGL::shm;
00110 #endif
00111 
00112 
00113 SceneOpenGL::SceneOpenGL( Workspace* ws )
00114     : Scene( ws )
00115     , init_ok( false )
00116     {
00117     if( !Extensions::glxAvailable())
00118         {
00119         kDebug( 1212 ) << "No glx extensions available";
00120         return; // error
00121         }
00122     initGLX();
00123     // check for FBConfig support
00124     if( !hasGLExtension( "GLX_SGIX_fbconfig" ) || !glXGetFBConfigAttrib || !glXGetFBConfigs ||
00125             !glXGetVisualFromFBConfig || !glXCreatePixmap || !glXDestroyPixmap ||
00126             !glXCreateWindow || !glXDestroyWindow )
00127         {
00128         kError( 1212 ) << "GLX_SGIX_fbconfig or required GLX functions missing";
00129         return; // error
00130         }
00131     if( !selectMode())
00132         return; // error
00133     if( !initBuffer()) // create destination buffer
00134         return; // error
00135     if( !initRenderingContext())
00136         return; // error
00137     // Initialize OpenGL
00138     initGL();
00139     if( !hasGLExtension( "GL_ARB_texture_non_power_of_two" )
00140         && !hasGLExtension( "GL_ARB_texture_rectangle" ))
00141         {
00142         kError( 1212 ) << "GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle missing";
00143         return; // error
00144         }
00145     if( db )
00146         glDrawBuffer( GL_BACK );
00147     // Check whether certain features are supported
00148     has_waitSync = false;
00149     if( glXGetVideoSync && glXIsDirect( display(), ctxbuffer ) && options->glVSync )
00150         {
00151         unsigned int sync;
00152         if( glXGetVideoSync( &sync ) == 0 )
00153             {
00154             if( glXWaitVideoSync( 1, 0, &sync ) == 0 )
00155                 has_waitSync = true;
00156             }
00157         }
00158 
00159     // OpenGL scene setup
00160     glMatrixMode( GL_PROJECTION );
00161     glLoadIdentity();
00162     // swap top and bottom to have OpenGL coordinate system match X system
00163     glOrtho( 0, displayWidth(), displayHeight(), 0, 0, 65535 );
00164     glMatrixMode( GL_MODELVIEW );
00165     glLoadIdentity();
00166     if( checkGLError( "Init" ))
00167         {
00168         kError( 1212 ) << "OpenGL compositing setup failed";
00169         return; // error
00170         }
00171     kDebug( 1212 ) << "DB:" << db << ", TFP:" << tfp_mode << ", SHM:" << shm_mode
00172         << ", Direct:" << bool( glXIsDirect( display(), ctxbuffer )) << endl;
00173     init_ok = true;
00174     }
00175 
00176 SceneOpenGL::~SceneOpenGL()
00177     {
00178     if( !init_ok )
00179         {
00180         // TODO this probably needs to clean up whatever has been created until the failure
00181         wspace->destroyOverlay();
00182         return;
00183         }
00184     foreach( Window* w, windows )
00185         delete w;
00186     // do cleanup after initBuffer()
00187     if( wspace->overlayWindow())
00188         {
00189         if( hasGLXVersion( 1, 3 ))
00190             glXDestroyWindow( display(), glxbuffer );
00191         XDestroyWindow( display(), buffer );
00192         wspace->destroyOverlay();
00193         }
00194     else
00195         {
00196         glXDestroyPixmap( display(), glxbuffer );
00197         XFreeGC( display(), gcroot );
00198         XFreePixmap( display(), buffer );
00199         }
00200     if( shm_mode )
00201         cleanupShm();
00202     if( !tfp_mode && !shm_mode )
00203         {
00204         if( last_pixmap != None )
00205             glXDestroyPixmap( display(), last_pixmap );
00206         glXDestroyContext( display(), ctxdrawable );
00207         }
00208     glXMakeCurrent( display(), None, NULL );
00209     glXDestroyContext( display(), ctxbuffer );
00210     checkGLError( "Cleanup" );
00211     }
00212 
00213 bool SceneOpenGL::initFailed() const
00214     {
00215     return !init_ok;
00216     }
00217 
00218 bool SceneOpenGL::selectMode()
00219     {
00220     // select mode - try TFP first, then SHM, otherwise fallback mode
00221     shm_mode = false;
00222     tfp_mode = false;
00223     if( options->glMode == Options::GLTFP )
00224         {
00225         if( initTfp())
00226             tfp_mode = true;
00227         else if( initShm())
00228             shm_mode = true;
00229         }
00230     else if( options->glMode == Options::GLSHM )
00231         {
00232         if( initShm())
00233             shm_mode = true;
00234         else if( initTfp())
00235             tfp_mode = true;
00236         }
00237     if( !initDrawableConfigs())
00238         return false;
00239     return true;
00240     }
00241 
00242 bool SceneOpenGL::initTfp()
00243     {
00244     if( glXBindTexImageEXT == NULL || glXReleaseTexImageEXT == NULL )
00245         return false;
00246     return true;
00247     }
00248 
00249 bool SceneOpenGL::initShm()
00250     {
00251 #ifdef HAVE_XSHM
00252     int major, minor;
00253     Bool pixmaps;
00254     if( !XShmQueryVersion( display(), &major, &minor, &pixmaps ) || !pixmaps )
00255         return false;
00256     if( XShmPixmapFormat( display()) != ZPixmap )
00257         return false;
00258     const int MAXSIZE = 4096 * 2048 * 4; // TODO check there are not larger windows
00259     // TODO check that bytes_per_line doesn't involve padding?
00260     shm.readOnly = False;
00261     shm.shmid = shmget( IPC_PRIVATE, MAXSIZE, IPC_CREAT | 0600 );
00262     if( shm.shmid < 0 )
00263         return false;
00264     shm.shmaddr = ( char* ) shmat( shm.shmid, NULL, 0 );
00265     if( shm.shmaddr == ( void * ) -1 )
00266         {
00267         shmctl( shm.shmid, IPC_RMID, 0 );
00268         return false;
00269         }
00270 #ifdef __linux__
00271     // mark as deleted to automatically free the memory in case
00272     // of a crash (but this doesn't work e.g. on Solaris ... oh well)
00273     shmctl( shm.shmid, IPC_RMID, 0 );
00274 #endif
00275     KXErrorHandler errs;
00276     XShmAttach( display(), &shm );
00277     if( errs.error( true ))
00278         {
00279 #ifndef __linux__
00280         shmctl( shm.shmid, IPC_RMID, 0 );
00281 #endif
00282         shmdt( shm.shmaddr );
00283         return false;
00284         }
00285     return true;
00286 #else
00287     return false;
00288 #endif
00289     }
00290 
00291 void SceneOpenGL::cleanupShm()
00292     {
00293 #ifdef HAVE_XSHM
00294     shmdt( shm.shmaddr );
00295 #ifndef __linux__
00296     shmctl( shm.shmid, IPC_RMID, 0 );
00297 #endif
00298 #endif
00299     }
00300 
00301 bool SceneOpenGL::initRenderingContext()
00302     {
00303     bool direct_rendering = options->glDirect;
00304     if( !tfp_mode && !shm_mode )
00305         direct_rendering = false; // fallback doesn't seem to work with direct rendering
00306     KXErrorHandler errs1;
00307     ctxbuffer = glXCreateNewContext( display(), fbcbuffer, GLX_RGBA_TYPE, NULL,
00308         direct_rendering ? GL_TRUE : GL_FALSE );
00309     bool failed = ( ctxbuffer == NULL || !glXMakeCurrent( display(), glxbuffer, ctxbuffer ));
00310     if( errs1.error( true )) // always check for error( having it all in one if() could skip
00311         failed = true;       // it due to evaluation short-circuiting
00312     if( failed )
00313         {
00314         if( !direct_rendering )
00315             {
00316             kDebug( 1212 ).nospace() << "Couldn't initialize rendering context ("
00317                 << KXErrorHandler::errorMessage( errs1.errorEvent()) << ")";
00318             return false;
00319             }
00320     glXMakeCurrent( display(), None, NULL );
00321         if( ctxbuffer != NULL )
00322             glXDestroyContext( display(), ctxbuffer );
00323         direct_rendering = false; // try again
00324         KXErrorHandler errs2;
00325         ctxbuffer = glXCreateNewContext( display(), fbcbuffer, GLX_RGBA_TYPE, NULL, GL_FALSE );
00326         bool failed = ( ctxbuffer == NULL || !glXMakeCurrent( display(), glxbuffer, ctxbuffer ));
00327         if( errs2.error( true ))
00328             failed = true;
00329         if( failed )
00330             {
00331             kDebug( 1212 ).nospace() << "Couldn't initialize rendering context ("
00332                 << KXErrorHandler::errorMessage( errs2.errorEvent()) << ")";
00333             return false;
00334             }
00335         }
00336     if( !tfp_mode && !shm_mode )
00337         {
00338         ctxdrawable = glXCreateNewContext( display(), fbcdrawableinfo[ QX11Info::appDepth() ].fbconfig, GLX_RGBA_TYPE, ctxbuffer,
00339             direct_rendering ? GL_TRUE : GL_FALSE );
00340         }
00341     return true;
00342     }
00343 
00344 // create destination buffer
00345 bool SceneOpenGL::initBuffer()
00346     {
00347     if( !initBufferConfigs())
00348         return false;
00349     if( fbcbuffer_db != NULL && wspace->createOverlay())
00350         { // we have overlay, try to create double-buffered window in it
00351         fbcbuffer = fbcbuffer_db;
00352         XVisualInfo* visual = glXGetVisualFromFBConfig( display(), fbcbuffer );
00353         XSetWindowAttributes attrs;
00354         attrs.colormap = XCreateColormap( display(), rootWindow(), visual->visual, AllocNone );
00355         buffer = XCreateWindow( display(), wspace->overlayWindow(), 0, 0, displayWidth(), displayHeight(),
00356             0, visual->depth, InputOutput, visual->visual, CWColormap, &attrs );
00357         if( hasGLXVersion( 1, 3 ))
00358             glxbuffer = glXCreateWindow( display(), fbcbuffer, buffer, NULL );
00359         else
00360             glxbuffer = buffer;
00361         wspace->setupOverlay( buffer );
00362         db = true;
00363         XFree( visual );
00364         }
00365     else if( fbcbuffer_nondb != NULL )
00366         { // cannot get any double-buffered drawable, will double-buffer using a pixmap
00367         fbcbuffer = fbcbuffer_nondb;
00368         XVisualInfo* visual = glXGetVisualFromFBConfig( display(), fbcbuffer );
00369         XGCValues gcattr;
00370         gcattr.subwindow_mode = IncludeInferiors;
00371         gcroot = XCreateGC( display(), rootWindow(), GCSubwindowMode, &gcattr );
00372         buffer = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(),
00373             visual->depth );
00374         glxbuffer = glXCreatePixmap( display(), fbcbuffer, buffer, NULL );
00375         db = false;
00376         XFree( visual );
00377         }
00378     else
00379         {
00380         kError( 1212 ) << "Couldn't create output buffer (failed to create overlay window?) !";
00381         return false; // error
00382         }
00383     int vis_buffer;
00384     glXGetFBConfigAttrib( display(), fbcbuffer, GLX_VISUAL_ID, &vis_buffer );
00385     XVisualInfo* visinfo_buffer = glXGetVisualFromFBConfig( display(), fbcbuffer );
00386     kDebug( 1212 ) << "Buffer visual (depth " << visinfo_buffer->depth << "): 0x" << QString::number( vis_buffer, 16 );
00387     XFree( visinfo_buffer );
00388     return true;
00389     }
00390 
00391 // choose the best configs for the destination buffer
00392 bool SceneOpenGL::initBufferConfigs()
00393     {
00394     int cnt;
00395     GLXFBConfig *fbconfigs = glXGetFBConfigs( display(), DefaultScreen( display() ), &cnt );
00396     fbcbuffer_db = NULL;
00397     fbcbuffer_nondb = NULL;
00398 
00399     for( int i = 0; i < 2; i++ )
00400         {
00401         int back, stencil, depth, caveat, alpha;
00402         back = i > 0 ? INT_MAX : 1;
00403         stencil = INT_MAX;
00404         depth = INT_MAX;
00405         caveat = INT_MAX;
00406         alpha = 0;
00407         for( int j = 0; j < cnt; j++ )
00408             {
00409             XVisualInfo *vi;
00410             int visual_depth;
00411             vi = glXGetVisualFromFBConfig( display(), fbconfigs[ j ] );
00412             if( vi == NULL )
00413                 continue;
00414             visual_depth = vi->depth;
00415             XFree( vi );
00416             if( visual_depth != DefaultDepth( display(), DefaultScreen( display())))
00417                 continue;
00418             int value;
00419             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00420                                   GLX_ALPHA_SIZE, &alpha );
00421             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00422                                   GLX_BUFFER_SIZE, &value );
00423             if( value != visual_depth && ( value - alpha ) != visual_depth )
00424                 continue;
00425             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00426                                   GLX_RENDER_TYPE, &value );
00427             if( !( value & GLX_RGBA_BIT ))
00428                 continue;
00429             int back_value;
00430             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00431                                   GLX_DOUBLEBUFFER, &back_value );
00432             if( i > 0 )
00433                 {
00434                 if( back_value > back )
00435                     continue;
00436                 }
00437             else
00438                 {
00439                 if( back_value < back )
00440                     continue;
00441                 }
00442             int stencil_value;
00443             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00444                                   GLX_STENCIL_SIZE, &stencil_value );
00445             if( stencil_value > stencil )
00446                 continue;
00447             int depth_value;
00448             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00449                                   GLX_DEPTH_SIZE, &depth_value );
00450             if( depth_value > depth )
00451                 continue;
00452             int caveat_value;
00453             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00454                                   GLX_CONFIG_CAVEAT, &caveat_value );
00455             if( caveat_value > caveat )
00456                 continue;
00457             back = back_value;
00458             stencil = stencil_value;
00459             depth = depth_value;
00460             caveat = caveat_value;
00461             if( i > 0 )
00462                 fbcbuffer_nondb = fbconfigs[ j ];
00463             else
00464                 fbcbuffer_db = fbconfigs[ j ];
00465             }
00466         }
00467     if( cnt )
00468         XFree( fbconfigs );
00469     if( fbcbuffer_db == NULL && fbcbuffer_nondb == NULL )
00470         {
00471         kError( 1212 ) << "Couldn't find framebuffer configuration for buffer!";
00472         return false;
00473         }
00474     for( int i = 0; i <= 32; i++ )
00475         {
00476         if( fbcdrawableinfo[ i ].fbconfig == NULL )
00477             continue;
00478         int vis_drawable = 0;
00479         glXGetFBConfigAttrib( display(), fbcdrawableinfo[ i ].fbconfig, GLX_VISUAL_ID, &vis_drawable );
00480         kDebug( 1212 ) << "Drawable visual (depth " << i << "): 0x" << QString::number( vis_drawable, 16 );
00481         }
00482     return true;
00483     }
00484 
00485 // make a list of the best configs for windows by depth
00486 bool SceneOpenGL::initDrawableConfigs()
00487     {
00488     int cnt;
00489     GLXFBConfig *fbconfigs = glXGetFBConfigs( display(), DefaultScreen( display() ), &cnt );
00490 
00491     for( int i = 0; i <= 32; i++ )
00492         {
00493         int back, stencil, depth, caveat, alpha, mipmap, rgba;
00494         back = INT_MAX;
00495         stencil = INT_MAX;
00496         depth = INT_MAX;
00497         caveat = INT_MAX;
00498         mipmap = 0;
00499         rgba = 0;
00500         fbcdrawableinfo[ i ].fbconfig = NULL;
00501         fbcdrawableinfo[ i ].bind_texture_format = 0;
00502         fbcdrawableinfo[ i ].y_inverted = 0;
00503         fbcdrawableinfo[ i ].mipmap = 0;
00504         for( int j = 0; j < cnt; j++ )
00505             {
00506             XVisualInfo *vi;
00507             int visual_depth;
00508             vi = glXGetVisualFromFBConfig( display(), fbconfigs[ j ] );
00509             if( vi == NULL )
00510                 continue;
00511             visual_depth = vi->depth;
00512             XFree( vi );
00513             if( visual_depth != i )
00514                 continue;
00515             int value;
00516             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00517                                   GLX_ALPHA_SIZE, &alpha );
00518             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00519                                   GLX_BUFFER_SIZE, &value );
00520             if( value != i && ( value - alpha ) != i )
00521                 continue;
00522             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00523                                   GLX_RENDER_TYPE, &value );
00524             if( !( value & GLX_RGBA_BIT ))
00525                 continue;
00526             if( tfp_mode )
00527                 {
00528                 value = 0;
00529                 if( i == 32 )
00530                     {
00531                     glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00532                                           GLX_BIND_TO_TEXTURE_RGBA_EXT, &value );
00533                     if( value )
00534                         {
00535                         // TODO I think this should be set only after the config passes all tests
00536                         rgba = 1;
00537                         fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGBA_EXT;
00538                         }
00539                     }
00540                 if( !value )
00541                     {
00542                     if( rgba )
00543                         continue;
00544                     glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00545                                           GLX_BIND_TO_TEXTURE_RGB_EXT, &value );
00546                     if( !value )
00547                         continue;
00548                     fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGB_EXT;
00549                     }
00550                 }
00551             int back_value;
00552             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00553                                   GLX_DOUBLEBUFFER, &back_value );
00554             if( back_value > back )
00555                 continue;
00556             int stencil_value;
00557             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00558                                   GLX_STENCIL_SIZE, &stencil_value );
00559             if( stencil_value > stencil )
00560                 continue;
00561             int depth_value;
00562             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00563                                   GLX_DEPTH_SIZE, &depth_value );
00564             if( depth_value > depth )
00565                 continue;
00566             int mipmap_value = -1;
00567             if( tfp_mode && GLTexture::framebufferObjectSupported())
00568                 {
00569                 glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00570                                       GLX_BIND_TO_MIPMAP_TEXTURE_EXT, &mipmap_value );
00571                 if( mipmap_value < mipmap )
00572                     continue;
00573                 }
00574             int caveat_value;
00575             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00576                                   GLX_CONFIG_CAVEAT, &caveat_value );
00577             if( caveat_value > caveat )
00578                 continue;
00579             // ok, config passed all tests, it's the best one so far
00580             fbcdrawableinfo[ i ].fbconfig = fbconfigs[ j ];
00581             caveat = caveat_value;
00582             back = back_value;
00583             stencil = stencil_value;
00584             depth = depth_value;
00585             mipmap = mipmap_value;
00586             glXGetFBConfigAttrib( display(), fbconfigs[ j ],
00587                                   GLX_Y_INVERTED_EXT, &value );
00588             fbcdrawableinfo[ i ].y_inverted = value;
00589             fbcdrawableinfo[ i ].mipmap = mipmap;
00590             }
00591         }
00592     if( cnt )
00593         XFree( fbconfigs );
00594     if( fbcdrawableinfo[ DefaultDepth( display(), DefaultScreen( display())) ].fbconfig == NULL )
00595         {
00596         kError( 1212 ) << "Couldn't find framebuffer configuration for default depth!";
00597         return false;
00598         }
00599     if( fbcdrawableinfo[ 32 ].fbconfig == NULL )
00600         {
00601         kError( 1212 ) << "Couldn't find framebuffer configuration for depth 32 (no ARGB GLX visual)!";
00602         return false;
00603         }
00604     return true;
00605     }
00606 
00607 // the entry function for painting
00608 void SceneOpenGL::paint( QRegion damage, ToplevelList toplevels )
00609     {
00610     foreach( Toplevel* c, toplevels )
00611         {
00612         assert( windows.contains( c ));
00613         stacking_order.append( windows[ c ] );
00614         }
00615     grabXServer();
00616     glXWaitX();
00617     glPushMatrix();
00618     int mask = 0;
00619 #ifdef CHECK_GL_ERROR
00620     checkGLError( "Paint1" );
00621 #endif
00622     paintScreen( &mask, &damage ); // call generic implementation
00623 #ifdef CHECK_GL_ERROR
00624     checkGLError( "Paint2" );
00625 #endif
00626     glPopMatrix();
00627     ungrabXServer(); // ungrab before flushBuffer(), it may wait for vsync
00628     flushBuffer( mask, damage );
00629     // do cleanup
00630     stacking_order.clear();
00631     checkGLError( "PostPaint" );
00632     }
00633 
00634 // wait for vblank signal before painting
00635 void SceneOpenGL::waitSync()
00636     { // NOTE that vsync has no effect with indirect rendering
00637     if( waitSyncAvailable())
00638         {
00639         unsigned int sync;
00640 
00641         glFlush();
00642         glXGetVideoSync( &sync );
00643         glXWaitVideoSync( 2, ( sync + 1 ) % 2, &sync );
00644         }
00645     }
00646 
00647 // actually paint to the screen (double-buffer swap or copy from pixmap buffer)
00648 void SceneOpenGL::flushBuffer( int mask, QRegion damage )
00649     {
00650     if( wspace->overlayWindow()) // show the window only after the first pass, since
00651         wspace->showOverlay();   // that pass may take long
00652     if( db )
00653         {
00654         if( mask & PAINT_SCREEN_REGION )
00655             {
00656             waitSync();
00657             if( glXCopySubBuffer )
00658                 {
00659                 foreach( const QRect &r, damage.rects())
00660                     {
00661                     // convert to OpenGL coordinates
00662                     int y = displayHeight() - r.y() - r.height();
00663                     glXCopySubBuffer( display(), glxbuffer, r.x(), y, r.width(), r.height());
00664                     }
00665                 }
00666             else
00667                 { // no idea why glScissor() is used, but Compiz has it and it doesn't seem to hurt
00668                 glEnable( GL_SCISSOR_TEST );
00669                 glDrawBuffer( GL_FRONT );
00670                 int xpos = 0;
00671                 int ypos = 0;
00672                 foreach( const QRect &r, damage.rects())
00673                     {
00674                     // convert to OpenGL coordinates
00675                     int y = displayHeight() - r.y() - r.height();
00676                     // Move raster position relatively using glBitmap() rather
00677                     // than using glRasterPos2f() - the latter causes drawing
00678                     // artefacts at the bottom screen edge with some gfx cards
00679 //                    glRasterPos2f( r.x(), r.y() + r.height());
00680                     glBitmap( 0, 0, 0, 0, r.x() - xpos, y - ypos, NULL );
00681                     xpos = r.x();
00682                     ypos = y;
00683                     glScissor( r.x(), y, r.width(), r.height());
00684                     glCopyPixels( r.x(), y, r.width(), r.height(), GL_COLOR );
00685                     }
00686                 glBitmap( 0, 0, 0, 0, -xpos, -ypos, NULL ); // move position back to 0,0
00687                 glDrawBuffer( GL_BACK );
00688                 glDisable( GL_SCISSOR_TEST );
00689                 }
00690             }
00691         else
00692             {
00693             waitSync();
00694             glXSwapBuffers( display(), glxbuffer );
00695             }
00696         glXWaitGL();
00697         XFlush( display());
00698         }
00699     else
00700         {
00701         glFlush();
00702         glXWaitGL();
00703         waitSync();
00704         if( mask & PAINT_SCREEN_REGION )
00705             foreach( const QRect &r, damage.rects())
00706                 XCopyArea( display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y());
00707         else
00708             XCopyArea( display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0 );
00709         XFlush( display());
00710         }
00711     }
00712 
00713 void SceneOpenGL::paintGenericScreen( int mask, ScreenPaintData data )
00714     {
00715     if( mask & PAINT_SCREEN_TRANSFORMED )
00716         { // apply screen transformations
00717         glPushMatrix();
00718         glTranslatef( data.xTranslate, data.yTranslate, 0 );
00719         glScalef( data.xScale, data.yScale, 1 );
00720         }
00721     Scene::paintGenericScreen( mask, data );
00722     if( mask & PAINT_SCREEN_TRANSFORMED )
00723         glPopMatrix();
00724     }
00725 
00726 void SceneOpenGL::paintBackground( QRegion region )
00727     {
00728     PaintClipper pc( region );
00729     if( !PaintClipper::clip())
00730         {
00731         glPushAttrib( GL_COLOR_BUFFER_BIT );
00732         glClearColor( 0, 0, 0, 1 ); // black
00733         glClear( GL_COLOR_BUFFER_BIT );
00734         glPopAttrib();
00735         return;
00736         }
00737     if( pc.clip() && pc.paintArea().isEmpty())
00738         return; // no background to paint
00739     glPushAttrib( GL_CURRENT_BIT );
00740     glColor4f( 0, 0, 0, 1 ); // black
00741     for( PaintClipper::Iterator iterator;
00742          !iterator.isDone();
00743          iterator.next())
00744         {
00745         glBegin( GL_QUADS );
00746         QRect r = iterator.boundingRect();
00747         glVertex2i( r.x(), r.y());
00748         glVertex2i( r.x() + r.width(), r.y());
00749         glVertex2i( r.x() + r.width(), r.y() + r.height());
00750         glVertex2i( r.x(), r.y() + r.height());
00751         glEnd();
00752         }
00753     glPopAttrib();
00754     }
00755 
00756 void SceneOpenGL::windowAdded( Toplevel* c )
00757     {
00758     assert( !windows.contains( c ));
00759     windows[ c ] = new Window( c );
00760     c->effectWindow()->setSceneWindow( windows[ c ]);
00761     }
00762 
00763 void SceneOpenGL::windowClosed( Toplevel* c, Deleted* deleted )
00764     {
00765     assert( windows.contains( c ));
00766     if( deleted != NULL )
00767         { // replace c with deleted
00768         Window* w = windows.take( c );
00769         w->updateToplevel( deleted );
00770         windows[ deleted ] = w;
00771         }
00772     else
00773         {
00774         delete windows.take( c );
00775         c->effectWindow()->setSceneWindow( NULL );
00776         }
00777     }
00778 
00779 void SceneOpenGL::windowDeleted( Deleted* c )
00780     {
00781     assert( windows.contains( c ));
00782     delete windows.take( c );
00783     c->effectWindow()->setSceneWindow( NULL );
00784     }
00785 
00786 void SceneOpenGL::windowGeometryShapeChanged( Toplevel* c )
00787     {
00788     if( !windows.contains( c )) // this is ok, shape is not valid
00789         return;                 // by default
00790     Window* w = windows[ c ];
00791     w->discardShape();
00792     w->checkTextureSize();
00793     }
00794 
00795 void SceneOpenGL::windowOpacityChanged( Toplevel* )
00796     {
00797 #if 0 // not really needed, windows are painted on every repaint
00798       // and opacity is used when applying texture, not when
00799       // creating it
00800     if( !windows.contains( c )) // this is ok, texture is created
00801         return;                 // on demand
00802     Window* w = windows[ c ];
00803     w->discardTexture();
00804 #endif
00805     }
00806 
00807 //****************************************
00808 // SceneOpenGL::Texture
00809 //****************************************
00810 
00811 SceneOpenGL::Texture::Texture() : GLTexture()
00812     {
00813     init();
00814     }
00815 
00816 SceneOpenGL::Texture::Texture( const Pixmap& pix, const QSize& size, int depth ) : GLTexture()
00817     {
00818     init();
00819     load( pix, size, depth );
00820     }
00821 
00822 SceneOpenGL::Texture::~Texture()
00823     {
00824     }
00825 
00826 void SceneOpenGL::Texture::init()
00827     {
00828     bound_glxpixmap = None;
00829     }
00830 
00831 void SceneOpenGL::Texture::createTexture()
00832     {
00833     glGenTextures( 1, &mTexture );
00834     }
00835 
00836 void SceneOpenGL::Texture::discard()
00837     {
00838     if( mTexture != None )
00839         release();
00840     GLTexture::discard();
00841     }
00842 
00843 void SceneOpenGL::Texture::release()
00844     {
00845     if( tfp_mode && bound_glxpixmap != None )
00846         {
00847         if( !options->glStrictBinding )
00848             glXReleaseTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT );
00849         glXDestroyGLXPixmap( display(), bound_glxpixmap );
00850         bound_glxpixmap = None;
00851         }
00852     }
00853 
00854 void SceneOpenGL::Texture::findTarget()
00855     {
00856     unsigned int new_target = 0;
00857     if( tfp_mode && glXQueryDrawable && bound_glxpixmap != None )
00858         glXQueryDrawable( display(), bound_glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target );
00859     // Hack for XGL - this should not be a fallback for glXQueryDrawable() but instead the case
00860     // when glXQueryDrawable is not available. However this call fails with XGL, unless KWin
00861     // is compiled statically with the libGL that Compiz is built against (without which neither
00862     // Compiz works with XGL). Falling back to doing this manually makes this work.
00863     if( new_target == 0 )
00864         {
00865         if( NPOTTextureSupported() ||
00866             ( isPowerOfTwo( mSize.width()) && isPowerOfTwo( mSize.height())))
00867             new_target = GLX_TEXTURE_2D_EXT;
00868         else
00869             new_target = GLX_TEXTURE_RECTANGLE_EXT;
00870         }
00871     switch( new_target )
00872         {
00873         case GLX_TEXTURE_2D_EXT:
00874             mTarget = GL_TEXTURE_2D;
00875             mScale.setWidth( 1.0f / mSize.width());
00876             mScale.setHeight( 1.0f / mSize.height());
00877           break;
00878         case GLX_TEXTURE_RECTANGLE_EXT:
00879             mTarget = GL_TEXTURE_RECTANGLE_ARB;
00880             mScale.setWidth( 1.0f );
00881             mScale.setHeight( 1.0f );
00882           break;
00883         default:
00884             assert( false );
00885         }
00886     }
00887 
00888 QRegion SceneOpenGL::Texture::optimizeBindDamage( const QRegion& reg, int limit )
00889     {
00890     if( reg.rects().count() <= 1 )
00891         return reg;
00892     // try to reduce the number of rects, as especially with SHM mode every rect
00893     // causes X roundtrip, even for very small areas - so, when the size difference
00894     // between all the areas and the bounding rectangle is small, simply use
00895     // only the bounding rectangle
00896     int size = 0;
00897     foreach( const QRect &r, reg.rects())
00898         size += r.width() * r.height();
00899     if( reg.boundingRect().width() * reg.boundingRect().height() - size < limit )
00900         return reg.boundingRect();
00901     return reg;
00902     }
00903 
00904 bool SceneOpenGL::Texture::load( const Pixmap& pix, const QSize& size,
00905     int depth, QRegion region )
00906     {
00907 #ifdef CHECK_GL_ERROR
00908     checkGLError( "TextureLoad1" );
00909 #endif
00910     if( pix == None || size.isEmpty() || depth < 1 )
00911         return false;
00912     if( tfp_mode )
00913         {
00914         if( fbcdrawableinfo[ depth ].fbconfig == NULL )
00915             {
00916             kDebug( 1212 ) << "No framebuffer configuration for depth " << depth
00917                            << "; not binding pixmap" << endl;
00918             return false;
00919             }
00920         }
00921 
00922     mSize = size;
00923     if( mTexture == None || !region.isEmpty())
00924         { // new texture, or texture contents changed; mipmaps now invalid
00925         setDirty();
00926         }
00927 
00928 #ifdef CHECK_GL_ERROR
00929     checkGLError( "TextureLoad2" );
00930 #endif
00931     if( tfp_mode )
00932         { // tfp mode, simply bind the pixmap to texture
00933         if( mTexture == None )
00934             createTexture();
00935         // when the pixmap is bound to the texture, they share the same data, so the texture
00936         // updates automatically - no need to do anything in such case
00937         if( bound_glxpixmap != None )
00938             glBindTexture( mTarget, mTexture );
00939         else
00940             {
00941             int attrs[] =
00942                 {
00943                 GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format,
00944                 GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap,
00945                 None
00946                 };
00947             // the GLXPixmap will reference the X pixmap, so it will be freed automatically
00948             // when no longer needed
00949             bound_glxpixmap = glXCreatePixmap( display(), fbcdrawableinfo[ depth ].fbconfig, pix, attrs );
00950 #ifdef CHECK_GL_ERROR
00951             checkGLError( "TextureLoadTFP1" );
00952 #endif
00953             findTarget();
00954             y_inverted = fbcdrawableinfo[ depth ].y_inverted ? true : false;
00955             can_use_mipmaps = fbcdrawableinfo[ depth ].mipmap ? true : false;
00956             glBindTexture( mTarget, mTexture );
00957 #ifdef CHECK_GL_ERROR
00958             checkGLError( "TextureLoadTFP2" );
00959 #endif
00960             if( !options->glStrictBinding )
00961                 glXBindTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT, NULL );
00962             }
00963         }
00964     else if( shm_mode )
00965         { // copy pixmap contents to a texture via shared memory
00966 #ifdef HAVE_XSHM
00967         GLenum pixfmt, type;
00968         if( depth >= 24 )
00969             {
00970             pixfmt = GL_BGRA;
00971             type = GL_UNSIGNED_BYTE;
00972             }
00973         else
00974             { // depth 16
00975             pixfmt = GL_RGB;
00976             type = GL_UNSIGNED_SHORT_5_6_5;
00977             }
00978         findTarget();
00979 #ifdef CHECK_GL_ERROR
00980         checkGLError( "TextureLoadSHM1" );
00981 #endif
00982         if( mTexture == None )
00983             {
00984             createTexture();
00985             glBindTexture( mTarget, mTexture );
00986             y_inverted = false;
00987             glTexImage2D( mTarget, 0, depth == 32 ? GL_RGBA : GL_RGB,
00988                 mSize.width(), mSize.height(), 0,
00989                 pixfmt, type, NULL );
00990             }
00991         else
00992             glBindTexture( mTarget, mTexture );
00993         if( !region.isEmpty())
00994             {
00995             XGCValues xgcv;
00996             xgcv.graphics_exposures = False;
00997             xgcv.subwindow_mode = IncludeInferiors;
00998             GC gc = XCreateGC( display(), pix, GCGraphicsExposures | GCSubwindowMode, &xgcv );
00999             Pixmap p = XShmCreatePixmap( display(), rootWindow(), shm.shmaddr, &shm,
01000                 mSize.width(), mSize.height(), depth );
01001             QRegion damage = optimizeBindDamage( region, 100 * 100 );
01002             glPixelStorei( GL_UNPACK_ROW_LENGTH, mSize.width());
01003             foreach( const QRect &r, damage.rects())
01004                 { // TODO for small areas it might be faster to not use SHM to avoid the XSync()
01005                 XCopyArea( display(), pix, p, gc, r.x(), r.y(), r.width(), r.height(), 0, 0 );
01006                 glXWaitX();
01007                 glTexSubImage2D( mTarget, 0,
01008                     r.x(), r.y(), r.width(), r.height(),
01009                     pixfmt, type, shm.shmaddr );
01010                 glXWaitGL();
01011                 }
01012             glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
01013             XFreePixmap( display(), p );
01014             XFreeGC( display(), gc );
01015             }
01016 #ifdef CHECK_GL_ERROR
01017         checkGLError( "TextureLoadSHM2" );
01018 #endif
01019         y_inverted = true;
01020         can_use_mipmaps = true;
01021 #endif
01022         }
01023     else
01024         { // fallback, copy pixmap contents to a texture
01025         // note that if depth is not QX11Info::appDepth(), this may
01026         // not work (however, it does seem to work with nvidia)
01027         findTarget();
01028         GLXDrawable pixmap = glXCreatePixmap( display(), fbcdrawableinfo[ QX11Info::appDepth() ].fbconfig, pix, NULL );
01029         glXMakeCurrent( display(), pixmap, ctxdrawable );
01030         if( last_pixmap != None )
01031             glXDestroyPixmap( display(), last_pixmap );
01032         // workaround for ATI - it leaks/crashes when the pixmap is destroyed immediately
01033         // here (http://lists.kde.org/?l=kwin&m=116353772208535&w=2)
01034         last_pixmap = pixmap;
01035         glReadBuffer( GL_FRONT );
01036         glDrawBuffer( GL_FRONT );
01037         if( mTexture == None )
01038             {
01039             createTexture();
01040             glBindTexture( mTarget, mTexture );
01041             y_inverted = false;
01042             glCopyTexImage2D( mTarget, 0,
01043                 depth == 32 ? GL_RGBA : GL_RGB,
01044                 0, 0, mSize.width(), mSize.height(), 0 );
01045             }
01046         else
01047             {
01048             glBindTexture( mTarget, mTexture );
01049             QRegion damage = optimizeBindDamage( region, 30 * 30 );
01050             foreach( const QRect &r, damage.rects())
01051                 {
01052                 // convert to OpenGL coordinates (this is mapping
01053                 // the pixmap to a texture, this is not affected
01054                 // by using glOrtho() for the OpenGL scene)
01055                 int gly = mSize.height() - r.y() - r.height();
01056                 glCopyTexSubImage2D( mTarget, 0,
01057                     r.x(), gly, r.x(), gly, r.width(), r.height());
01058                 }
01059             }
01060         glXWaitGL();
01061         if( db )
01062             glDrawBuffer( GL_BACK );
01063         glXMakeCurrent( display(), glxbuffer, ctxbuffer );
01064         glBindTexture( mTarget, mTexture );
01065         y_inverted = false;
01066         can_use_mipmaps = true;
01067         }
01068 #ifdef CHECK_GL_ERROR
01069     checkGLError( "TextureLoad0" );
01070 #endif
01071     return true;
01072     }
01073 
01074 bool SceneOpenGL::Texture::load( const Pixmap& pix, const QSize& size,
01075     int depth )
01076     {
01077     return load( pix, size, depth,
01078         QRegion( 0, 0, size.width(), size.height()));
01079     }
01080 
01081 bool SceneOpenGL::Texture::load( const QImage& image, GLenum target )
01082     {
01083     if( image.isNull())
01084         return false;
01085     return load( QPixmap::fromImage( image ), target );
01086     }
01087 
01088 bool SceneOpenGL::Texture::load( const QPixmap& pixmap, GLenum target )
01089     {
01090     Q_UNUSED( target ); // SceneOpenGL::Texture::findTarget() detects the target
01091     if( pixmap.isNull())
01092         return false;
01093     return load( pixmap.handle(), pixmap.size(), pixmap.depth());
01094     }
01095 
01096 void SceneOpenGL::Texture::bind()
01097     {
01098     glEnable( mTarget );
01099     glBindTexture( mTarget, mTexture );
01100     if( tfp_mode && options->glStrictBinding )
01101         {
01102         assert( bound_glxpixmap != None );
01103         glXBindTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT, NULL );
01104         }
01105     enableFilter();
01106     if( hasGLVersion( 1, 4, 0 ))
01107         {
01108         // Lod bias makes the trilinear-filtered texture look a bit sharper
01109         glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0f );
01110         }
01111     }
01112 
01113 void SceneOpenGL::Texture::unbind()
01114     {
01115     if( hasGLVersion( 1, 4, 0 ))
01116         {
01117         glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f );
01118         }
01119     if( tfp_mode && options->glStrictBinding )
01120         {
01121         assert( bound_glxpixmap != None );
01122         glBindTexture( mTarget, mTexture );
01123         glXReleaseTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT );
01124         }
01125     GLTexture::unbind();
01126     }
01127 
01128 //****************************************
01129 // SceneOpenGL::Window
01130 //****************************************
01131 
01132 SceneOpenGL::Window::Window( Toplevel* c )
01133     : Scene::Window( c )
01134     , texture()
01135     {
01136     }
01137 
01138 SceneOpenGL::Window::~Window()
01139     {
01140     discardTexture();
01141     }
01142 
01143 // Bind the window pixmap to an OpenGL texture.
01144 bool SceneOpenGL::Window::bindTexture()
01145     {
01146     if( texture.texture() != None && toplevel->damage().isEmpty())
01147         {
01148         // texture doesn't need updating, just bind it
01149         glBindTexture( texture.target(), texture.texture());
01150         return true;
01151         }
01152     // Get the pixmap with the window contents
01153     Pixmap pix = toplevel->windowPixmap();
01154     if( pix == None )
01155         return false;
01156     bool success = texture.load( pix, toplevel->size(), toplevel->depth(),
01157         toplevel->damage());
01158     if( success )
01159         toplevel->resetDamage( toplevel->rect());
01160     else
01161         kDebug( 1212 ) << "Failed to bind window";
01162     return success;
01163     }
01164 
01165 void SceneOpenGL::Window::discardTexture()
01166     {
01167     texture.discard();
01168     }
01169 
01170 // This call is used in SceneOpenGL::windowGeometryShapeChanged(),
01171 // which originally called discardTexture(), however this was causing performance
01172 // problems with the launch feedback icon - large number of texture rebinds.
01173 // Since the launch feedback icon does not resize, only changes shape, it
01174 // is not necessary to rebind the texture (with no strict binding), therefore
01175 // discard the texture only if size changes.
01176 void SceneOpenGL::Window::checkTextureSize()
01177     {
01178     if( texture.size() != size())
01179         discardTexture();
01180     }
01181 
01182 // when the window's composite pixmap is discarded, undo binding it to the texture
01183 void SceneOpenGL::Window::pixmapDiscarded()
01184     {
01185     texture.release();
01186     }
01187 
01188 // paint the window
01189 void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintData data )
01190     {
01191     // check if there is something to paint (e.g. don't paint if the window
01192     // is only opaque and only PAINT_WINDOW_TRANSLUCENT is requested)
01193     bool opaque = isOpaque() && data.opacity == 1.0;
01194     if( mask & ( PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSLUCENT ))
01195         {}
01196     else if( mask & PAINT_WINDOW_OPAQUE )
01197         {
01198         if( !opaque )
01199             return;
01200         }
01201     else if( mask & PAINT_WINDOW_TRANSLUCENT )
01202         {
01203         if( opaque )
01204             return;
01205         }
01206     // paint only requested areas
01207     if( region != infiniteRegion()) // avoid integer overflow
01208         region.translate( -x(), -y());
01209     if(( mask & ( PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED )) == 0 )
01210         region &= shape();
01211     if( region.isEmpty())
01212         return;
01213     if( !bindTexture())
01214         return;
01215     glPushMatrix();
01216     // set texture filter
01217     if( options->smoothScale != 0 ) // default to yes
01218         {
01219         if( mask & PAINT_WINDOW_TRANSFORMED )
01220             filter = ImageFilterGood;
01221         else if( mask & PAINT_SCREEN_TRANSFORMED )
01222             filter = ImageFilterGood;
01223         else
01224             filter = ImageFilterFast;
01225         }
01226     else
01227         filter = ImageFilterFast;
01228     if( filter == ImageFilterGood )
01229         {
01230         // avoid unneeded mipmap generation by only using trilinear
01231         // filtering when it actually makes a difference, that is with
01232         // minification or changed vertices
01233         if( options->smoothScale == 2
01234             && ( data.quads.smoothNeeded() || data.xScale < 1 || data.yScale < 1 ))
01235             {
01236             texture.setFilter( GL_LINEAR_MIPMAP_LINEAR );
01237             }
01238         else
01239             texture.setFilter( GL_LINEAR );
01240         }
01241     else
01242         texture.setFilter( GL_NEAREST );
01243     // do required transformations
01244     int x = toplevel->x();
01245     int y = toplevel->y();
01246     if( mask & PAINT_WINDOW_TRANSFORMED )
01247         {
01248         x += data.xTranslate;
01249         y += data.yTranslate;
01250         }
01251     glTranslatef( x, y, 0 );
01252     if(( mask & PAINT_WINDOW_TRANSFORMED ) && ( data.xScale != 1 || data.yScale != 1 ))
01253         glScalef( data.xScale, data.yScale, 1 );
01254     region.translate( toplevel->x(), toplevel->y() );  // Back to screen coords
01255 
01256     texture.bind();
01257     texture.enableUnnormalizedTexCoords();
01258 
01259     WindowQuadList decoration = data.quads.select( WindowQuadDecoration );    
01260     if( data.contents_opacity != data.decoration_opacity && !decoration.isEmpty())
01261         {
01262         prepareStates( data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader );
01263         renderQuads( mask, region, data.quads.select( WindowQuadContents ));
01264         restoreStates( data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader );
01265         prepareStates( data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader );
01266         renderQuads( mask, region, decoration );
01267         restoreStates( data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader );
01268         }
01269     else
01270         {
01271         prepareStates( data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader );
01272         renderQuads( mask, region, data.quads.select( WindowQuadContents ));
01273         renderQuads( mask, region, data.quads.select( WindowQuadDecoration ));
01274         restoreStates( data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader );
01275         }
01276 
01277     texture.disableUnnormalizedTexCoords();
01278     texture.unbind();
01279     glPopMatrix();
01280     }
01281 
01282 void SceneOpenGL::Window::renderQuads( int, const QRegion& region, const WindowQuadList& quads )
01283     {
01284     if( quads.isEmpty())
01285         return;
01286     // Render geometry
01287     float* vertices;
01288     float* texcoords;
01289     quads.makeArrays( &vertices, &texcoords );
01290     renderGLGeometry( region, quads.count() * 4,
01291             vertices, texcoords, NULL, 2, 0 );
01292     delete[] vertices;
01293     delete[] texcoords;
01294     }
01295 
01296 void SceneOpenGL::Window::prepareStates( double opacity, double brightness, double saturation, GLShader* shader )
01297     {
01298     if(shader)
01299         prepareShaderRenderStates( opacity, brightness, saturation, shader );
01300     else
01301         prepareRenderStates( opacity, brightness, saturation );
01302     }
01303 
01304 void SceneOpenGL::Window::prepareShaderRenderStates( double opacity, double brightness, double saturation, GLShader* shader )
01305     {
01306     // setup blending of transparent windows
01307     glPushAttrib( GL_ENABLE_BIT );
01308     bool opaque = isOpaque() && opacity == 1.0;
01309     if( !opaque )
01310         {
01311         glEnable( GL_BLEND );
01312         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
01313         }
01314     shader->setUniform("opacity", (float)opacity);
01315     shader->setUniform("saturation", (float)saturation);
01316     shader->setUniform("brightness", (float)brightness);
01317     }
01318 
01319 void SceneOpenGL::Window::prepareRenderStates( double opacity, double brightness, double saturation )
01320     {
01321     // setup blending of transparent windows
01322     glPushAttrib( GL_ENABLE_BIT );
01323     bool opaque = isOpaque() && opacity == 1.0;
01324     if( !opaque )
01325         {
01326         glEnable( GL_BLEND );
01327         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
01328         }
01329     if( saturation != 1.0 && texture.saturationSupported())
01330         {
01331         // First we need to get the color from [0; 1] range to [0.5; 1] range
01332         glActiveTexture( GL_TEXTURE0 );
01333         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
01334         glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
01335         glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE );
01336         glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
01337         glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
01338         glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
01339         glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT );
01340         glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );
01341         const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5};
01342         glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant );
01343         texture.bind();
01344 
01345         // Then we take dot product of the result of previous pass and
01346         //  saturation_constant. This gives us completely unsaturated
01347         //  (greyscale) image
01348         // Note that both operands have to be in range [0.5; 1] since opengl
01349         //  automatically substracts 0.5 from them
01350         glActiveTexture( GL_TEXTURE1 );
01351         float saturation_constant[] = { 0.5 + 0.5*0.30, 0.5 + 0.5*0.59, 0.5 + 0.5*0.11, saturation };
01352         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
01353         glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB );
01354         glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS );
01355         glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
01356         glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
01357         glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
01358         glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant );
01359         texture.bind();
01360 
01361         // Finally we need to interpolate between the original image and the
01362         //  greyscale image to get wanted level of saturation
01363         glActiveTexture( GL_TEXTURE2 );
01364         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
01365         glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
01366         glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 );
01367         glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
01368         glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS );
01369         glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
01370         glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT );
01371         glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );
01372         glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant );
01373         // Also replace alpha by primary color's alpha here
01374         glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
01375         glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR );
01376         glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
01377         // And make primary color contain the wanted opacity
01378         glColor4f( opacity, opacity, opacity, opacity );
01379         texture.bind();
01380 
01381         if( toplevel->hasAlpha() || brightness != 1.0f )
01382             {
01383             glActiveTexture( GL_TEXTURE3 );
01384             glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
01385             glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE );
01386             glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS );
01387             glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
01388             glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR );
01389             glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
01390             if( toplevel->hasAlpha() )
01391                 {
01392                 // The color has to be multiplied by both opacity and brightness
01393                 float opacityByBrightness = opacity * brightness;
01394                 glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity );
01395                 // Also multiply original texture's alpha by our opacity
01396                 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
01397                 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0 );
01398                 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
01399                 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR );
01400                 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );
01401                 }
01402             else
01403                 {
01404                 // Color has to be multiplied only by brightness
01405                 glColor4f( brightness, brightness, brightness, opacity );
01406                 // Alpha will be taken from previous stage
01407                 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
01408                 glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS );
01409                 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
01410                 }
01411             texture.bind();
01412             }
01413 
01414         glActiveTexture(GL_TEXTURE0 );
01415         }
01416     else if( opacity != 1.0 || brightness != 1.0 )
01417         {
01418         // the window is additionally configured to have its opacity adjusted,
01419         // do it
01420         if( toplevel->hasAlpha())
01421             {
01422             float opacityByBrightness = opacity * brightness;
01423             glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
01424             glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness,
01425                 opacity);
01426             }
01427         else
01428             {
01429             // Multiply color by brightness and replace alpha by opacity
01430             float constant[] = { brightness, brightness, brightness, opacity };
01431             glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
01432             glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE );
01433             glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE );
01434             glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
01435             glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
01436             glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
01437             glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
01438             glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT );
01439             glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant );
01440             }
01441         }
01442     }
01443 
01444 void SceneOpenGL::Window::restoreStates( double opacity, double brightness, double saturation, GLShader* shader )
01445     {
01446     if(shader)
01447         restoreShaderRenderStates( opacity, brightness, saturation, shader );
01448     else
01449         restoreRenderStates( opacity, brightness, saturation );
01450     }
01451 
01452 void SceneOpenGL::Window::restoreShaderRenderStates( double opacity, double brightness, double saturation, GLShader* shader )
01453     {
01454     Q_UNUSED( opacity );
01455     Q_UNUSED( brightness );
01456     Q_UNUSED( saturation );
01457     Q_UNUSED( shader );
01458     glPopAttrib();  // ENABLE_BIT
01459     }
01460 
01461 void SceneOpenGL::Window::restoreRenderStates( double opacity, double brightness, double saturation )
01462     {
01463     if( opacity != 1.0 || saturation != 1.0 || brightness != 1.0f )
01464         {
01465         if( saturation != 1.0 && texture.saturationSupported())
01466             {
01467             glActiveTexture(GL_TEXTURE3);
01468             glDisable( texture.target());
01469             glActiveTexture(GL_TEXTURE2);
01470             glDisable( texture.target());
01471             glActiveTexture(GL_TEXTURE1);
01472             glDisable( texture.target());
01473             glActiveTexture(GL_TEXTURE0);
01474             }
01475         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
01476         glColor4f( 0, 0, 0, 0 );
01477         }
01478 
01479     glPopAttrib();  // ENABLE_BIT
01480     }
01481 
01482 } // namespace
01483 
01484 #endif

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal