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