diff --git a/COMPOSITE_TODO b/COMPOSITE_TODO index 1b5c0bc0a3..2ee635d6cb 100644 --- a/COMPOSITE_TODO +++ b/COMPOSITE_TODO @@ -118,10 +118,12 @@ OpenGL TODO one for the root window (or for the window used in the XComposite overlay) and the best one for every depth of drawables -+ GL_ARB_texture_rectangle vs GL_ARB_texture_non_power_of_two - - code currently uses GL_ARB_texture_rectangle (GL_TEXTURE_RECTANGLE_ARB), using - normal textures when GL_ARB_texture_non_power_of_two is available should(?) be - preferred +/ GL_ARB_texture_rectangle vs GL_ARB_texture_non_power_of_two +% - works; bugs in tfp_mode with power_of_two textures + - nvidia: tfp_target needed in bindTexture() else glXCreatePixmap fails; + shouldn't be necessary according to texture_from_pixmap spec + - ati (others?): power_of_two windows are drawn white unless non-tfp_mode + is forced in findTextureTarget() ? in SceneOpenGL::bindTexture() with tfp_mode, with some gfx cards it seems to be faster to not short-circuit the texture binding when there's been diff --git a/glutils.cpp b/glutils.cpp index 10775505b1..1b0b28ed84 100644 --- a/glutils.cpp +++ b/glutils.cpp @@ -26,6 +26,8 @@ int glTextureUnitsCount; // Function pointers glXGetProcAddress_func glXGetProcAddress; +// GLX 1.3 +glXQueryDrawable_func glXQueryDrawable; // texture_from_pixmap extension functions glXReleaseTexImageEXT_func glXReleaseTexImageEXT; glXBindTexImageEXT_func glXBindTexImageEXT; @@ -63,6 +65,7 @@ void initGLX() glXGetProcAddress = (glXGetProcAddress_func) getProcAddress( "glXGetProcAddress" ); if( glXGetProcAddress == NULL ) glXGetProcAddress = (glXGetProcAddress_func) getProcAddress( "glXGetProcAddressARB" ); + glXQueryDrawable = (glXQueryDrawable_func) getProcAddress( "glXQueryDrawable" ); if( hasGLExtension( "GLX_EXT_texture_from_pixmap" )) { glXBindTexImageEXT = (glXBindTexImageEXT_func) getProcAddress( "glXBindTexImageEXT" ); diff --git a/glutils.h b/glutils.h index 33ef88ddb1..1e80e09065 100644 --- a/glutils.h +++ b/glutils.h @@ -47,6 +47,7 @@ bool hasGLXVersion(int major, int minor, int release = 0); // use for both OpenGL and GLX extensions bool hasGLExtension(const QString& extension); +inline bool isPowerOfTwo( int x ) { return (( x & ( x - 1 )) == 0 ); } // Defines /* @@ -73,6 +74,10 @@ bool hasGLExtension(const QString& extension); typedef void (*glXFuncPtr)(); typedef glXFuncPtr (*glXGetProcAddress_func)( const GLubyte* ); extern glXGetProcAddress_func glXGetProcAddress; +// glXQueryDrawable (added in GLX 1.3) +typedef void (*glXQueryDrawable_func)( Display* dpy, GLXDrawable drawable, + int attribute, unsigned int *value ); +extern glXQueryDrawable_func glXQueryDrawable; // texture_from_pixmap extension functions typedef void (*glXBindTexImageEXT_func)( Display* dpy, GLXDrawable drawable, int buffer, const int* attrib_list ); diff --git a/scene_opengl.cpp b/scene_opengl.cpp index ce0d2588d0..f7e10c860d 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -86,6 +86,7 @@ bool SceneOpenGL::tfp_mode; // using glXBindTexImageEXT (texture_from_pixmap) bool SceneOpenGL::strict_binding; // intended for AIGLX bool SceneOpenGL::db; // destination drawable is double-buffered bool SceneOpenGL::copy_buffer_hack; // workaround for nvidia < 1.0-9xxx drivers +bool SceneOpenGL::supports_npot_textures; bool SceneOpenGL::supports_saturation; bool SceneOpenGL::shm_mode; XShmSegmentInfo SceneOpenGL::shm; @@ -180,6 +181,8 @@ SceneOpenGL::SceneOpenGL( Workspace* ws ) has_waitSync = glXGetVideoSync ? true : false; // Check whether certain features are supported + supports_npot_textures = hasGLExtension( "GL_ARB_texture_non_power_of_two" ) + || hasGLVersion(2, 0); supports_saturation = ((hasGLExtension("GL_ARB_texture_env_crossbar") && hasGLExtension("GL_ARB_texture_env_dot3")) || hasGLVersion(1, 4)) && (glTextureUnitsCount >= 4) && glActiveTexture != NULL; @@ -619,6 +622,7 @@ void SceneOpenGL::windowOpacityChanged( Toplevel* ) SceneOpenGL::Window::Window( Toplevel* c ) : Scene::Window( c ) , texture( 0 ) + , texture_target( 0 ) , texture_y_inverted( false ) , bound_glxpixmap( None ) , currentXResolution( -1 ) @@ -711,6 +715,36 @@ void SceneOpenGL::Window::prepareForPainting() // paint pass } +void SceneOpenGL::Window::findTextureTarget() + { + unsigned int target = 0; + if( tfp_mode && glXQueryDrawable && bound_glxpixmap != None ) + glXQueryDrawable( display(), bound_glxpixmap, GLX_TEXTURE_TARGET_EXT, &target ); + else + { + if( supports_npot_textures || + ( isPowerOfTwo( toplevel->width() ) && isPowerOfTwo( toplevel->height() ))) + target = GLX_TEXTURE_2D_EXT; + else + target = GLX_TEXTURE_RECTANGLE_EXT; + } + switch( target ) + { + case GLX_TEXTURE_2D_EXT: + texture_target = GL_TEXTURE_2D; + texture_scale_x = 1.0f / toplevel->width(); + texture_scale_y = 1.0f / toplevel->height(); + break; + case GLX_TEXTURE_RECTANGLE_EXT: + texture_target = GL_TEXTURE_RECTANGLE_ARB; + texture_scale_x = 1.0f; + texture_scale_y = 1.0f; + break; + default: + assert( false ); + } + } + // Bind the window pixmap to an OpenGL texture. void SceneOpenGL::Window::bindTexture() { @@ -718,7 +752,7 @@ void SceneOpenGL::Window::bindTexture() && !options->glAlwaysRebind ) // interestingly with some gfx cards always rebinding is faster { // texture doesn't need updating, just bind it - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture ); + glBindTexture( texture_target, texture ); return; } // Get the pixmap with the window contents @@ -762,20 +796,21 @@ void SceneOpenGL::Window::bindTexture() glXWaitX(); if( shm_mode ) { // non-tfp case, copy pixmap contents to a texture + findTextureTarget(); if( texture == None ) { glGenTextures( 1, &texture ); - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture ); + glBindTexture( texture_target, texture ); texture_y_inverted = false; - glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, toplevel->width(), toplevel->height(), + glTexImage2D( texture_target, 0, GL_RGBA, toplevel->width(), toplevel->height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); // TODO hasAlpha() ? -// glCopyTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, +// glCopyTexImage2D( texture_target, 0, // toplevel->hasAlpha() ? GL_RGBA : GL_RGB, // 0, 0, toplevel->width(), toplevel->height(), 0 ); } else - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture ); + glBindTexture( texture_target, texture ); if( !toplevel->damage().isEmpty()) { XGCValues xgcv; @@ -790,7 +825,7 @@ void SceneOpenGL::Window::bindTexture() XCopyArea( display(), pix, p, gc, r.x(), r.y(), r.width(), r.height(), 0, 0 ); XSync( display(), False ); glXWaitX(); - glTexSubImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, + glTexSubImage2D( texture_target, 0, r.x(), r.y(), r.width(), r.height(), GL_BGRA, GL_UNSIGNED_BYTE, shm.shmaddr ); glXWaitGL(); XFreePixmap( display(), p ); @@ -809,24 +844,34 @@ void SceneOpenGL::Window::bindTexture() glXReleaseTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT ); glXDestroyGLXPixmap( display(), bound_glxpixmap ); } + // TODO works around a crash; check later whether we really need this + unsigned int tfp_target; + if( supports_npot_textures || + ( isPowerOfTwo( toplevel->width() ) && isPowerOfTwo( toplevel->height() ))) + tfp_target = GLX_TEXTURE_2D_EXT; + else + tfp_target = GLX_TEXTURE_RECTANGLE_EXT; static const int attrs[] = { GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, + GLX_TEXTURE_TARGET_EXT, tfp_target, None }; // the GLXPixmap will reference the X pixmap, so it will be freed automatically // when no longer needed bound_glxpixmap = glXCreatePixmap( display(), fbcdrawable, pix, attrs ); + findTextureTarget(); int value; glXGetFBConfigAttrib( display(), fbcdrawable, GLX_Y_INVERTED_EXT, &value ); texture_y_inverted = value ? true : false; - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture ); + glBindTexture( texture_target, texture ); if( !strict_binding ) glXBindTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT, NULL ); toplevel->resetDamage( toplevel->rect()); } else { // non-tfp case, copy pixmap contents to a texture + findTextureTarget(); GLXDrawable pixmap = glXCreatePixmap( display(), fbcdrawable, pix, NULL ); glXMakeContextCurrent( display(), pixmap, pixmap, ctxdrawable ); if( last_pixmap != None ) @@ -839,15 +884,15 @@ void SceneOpenGL::Window::bindTexture() if( texture == None ) { glGenTextures( 1, &texture ); - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture ); + glBindTexture( texture_target, texture ); texture_y_inverted = false; - glCopyTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, + glCopyTexImage2D( texture_target, 0, toplevel->hasAlpha() ? GL_RGBA : GL_RGB, 0, 0, toplevel->width(), toplevel->height(), 0 ); } else { - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture ); + glBindTexture( texture_target, texture ); QRegion damage = optimizeBindDamage( toplevel->damage(), 30 * 30 ); foreach( QRect r, damage.rects()) { @@ -855,7 +900,7 @@ void SceneOpenGL::Window::bindTexture() // the pixmap to a texture, this is not affected // by using glOrtho() for the OpenGL scene) int gly = toplevel->height() - r.y() - r.height(); - glCopyTexSubImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, + glCopyTexSubImage2D( texture_target, 0, r.x(), gly, r.x(), gly, r.width(), r.height()); } } @@ -863,7 +908,7 @@ void SceneOpenGL::Window::bindTexture() if( db ) glDrawBuffer( GL_BACK ); glXMakeContextCurrent( display(), glxbuffer, glxbuffer, ctxbuffer ); - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture ); + glBindTexture( texture_target, texture ); texture_y_inverted = false; toplevel->resetDamage( toplevel->rect()); } @@ -892,8 +937,8 @@ QRegion SceneOpenGL::Window::optimizeBindDamage( const QRegion& reg, int limit ) void SceneOpenGL::Window::enableTexture() { - glEnable( GL_TEXTURE_RECTANGLE_ARB ); - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture ); + glEnable( texture_target ); + glBindTexture( texture_target, texture ); if( tfp_mode && strict_binding ) { assert( bound_glxpixmap != None ); @@ -901,13 +946,13 @@ void SceneOpenGL::Window::enableTexture() } if( options->smoothScale != 0 ) // default to yes { - glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); } else { - glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } } @@ -916,11 +961,11 @@ void SceneOpenGL::Window::disableTexture() if( tfp_mode && strict_binding ) { assert( bound_glxpixmap != None ); - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture ); + glBindTexture( texture_target, texture ); glXReleaseTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT ); } - glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 ); - glDisable( GL_TEXTURE_RECTANGLE_ARB ); + glBindTexture( texture_target, 0 ); + glDisable( texture_target ); } void SceneOpenGL::Window::discardTexture() @@ -1097,6 +1142,18 @@ void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintDat } } enableTexture(); + // update texture matrix to handle GL_TEXTURE_2D and GL_TEXTURE_RECTANGLE + glMatrixMode( GL_TEXTURE ); + glLoadIdentity(); + glScalef( texture_scale_x, texture_scale_y, 1 ); + if( !texture_y_inverted ) + { + // Modify texture matrix so that we could always use non-opengl + // coordinates for textures + glScalef(1, -1, 1); + glTranslatef(0, -height(), 0); + } + glMatrixMode( GL_MODELVIEW ); if(verticeslist.isEmpty()) createVertexGrid(0, 0); // Enable arrays @@ -1104,14 +1161,6 @@ void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintDat glVertexPointer(3, GL_FLOAT, sizeof(Vertex), verticeslist[0].pos); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), verticeslist[0].texcoord); - if( !texture_y_inverted ) - { - // Modify texture matrix so that we could always use non-opengl - // coordinates for textures - glMatrixMode(GL_TEXTURE); - glScalef(1, -1, 1); - glTranslatef(0, -height(), 0); - } // Render if( mask & PAINT_WINDOW_TRANSFORMED ) // Just draw the entire window, no clipping @@ -1132,11 +1181,9 @@ void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintDat glDisable( GL_SCISSOR_TEST ); } // Restore texture matrix - if( !texture_y_inverted ) - { - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - } + glMatrixMode( GL_TEXTURE ); + glLoadIdentity(); + glMatrixMode( GL_MODELVIEW ); // Disable arrays glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); @@ -1146,11 +1193,11 @@ void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintDat if( data.saturation != 1.0 && supports_saturation ) { glActiveTexture(GL_TEXTURE3); - glDisable( GL_TEXTURE_RECTANGLE_ARB ); + glDisable( texture_target ); glActiveTexture(GL_TEXTURE2); - glDisable( GL_TEXTURE_RECTANGLE_ARB ); + glDisable( texture_target ); glActiveTexture(GL_TEXTURE1); - glDisable( GL_TEXTURE_RECTANGLE_ARB ); + glDisable( texture_target ); glActiveTexture(GL_TEXTURE0); } glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); diff --git a/scene_opengl.h b/scene_opengl.h index 2e3b86594c..8fa73106c2 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -48,6 +48,7 @@ class SceneOpenGL void waitSync(); void flushBuffer( int mask, QRegion damage ); typedef GLuint Texture; + typedef GLenum Target; GC gcroot; Drawable buffer; GLXFBConfig fbcbuffer; @@ -61,6 +62,7 @@ class SceneOpenGL static bool shm_mode; static bool strict_binding; static bool copy_buffer_hack; + static bool supports_npot_textures; static bool supports_saturation; QMap< Toplevel*, Window > windows; static XShmSegmentInfo shm; @@ -74,6 +76,7 @@ class SceneOpenGL::Window virtual void free(); virtual void performPaint( int mask, QRegion region, WindowPaintData data ); virtual void prepareForPainting(); + void findTextureTarget(); void bindTexture(); void enableTexture(); void disableTexture(); @@ -118,6 +121,8 @@ class SceneOpenGL::Window private: QRegion optimizeBindDamage( const QRegion& reg, int limit ); Texture texture; + Target texture_target; + float texture_scale_x, texture_scale_y; // to un-normalize GL_TEXTURE_2D bool texture_y_inverted; // texture has y inverted GLXPixmap bound_glxpixmap; // the glx pixmap the texture is bound to, only for tfp_mode