diff --git a/COMPOSITE_TODO b/COMPOSITE_TODO index d14fbde4f4..2b550fde53 100644 --- a/COMPOSITE_TODO +++ b/COMPOSITE_TODO @@ -96,10 +96,9 @@ 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 -+ sync to vblank +/ sync to vblank - currently the compositing code is run with 20ms timer, i.e. constant 50fps - the GLX_SGI_video_sync extension should be used - - compiz uses this, no idea about it though + GL_ARB_texture_rectangle vs GL_ARB_texture_non_power_of_two - code currently uses GL_ARB_texture_rectangle (GL_TEXTURE_RECTANGLE_ARB), using diff --git a/glutils.cpp b/glutils.cpp index ba62071e46..10775505b1 100644 --- a/glutils.cpp +++ b/glutils.cpp @@ -33,6 +33,9 @@ glXBindTexImageEXT_func glXBindTexImageEXT; glActiveTexture_func glActiveTexture; // glXCopySubBufferMESA glXCopySubBuffer_func glXCopySubBuffer; +// video_sync extension functions +glXGetVideoSync_func glXGetVideoSync; +glXWaitVideoSync_func glXWaitVideoSync; // Functions @@ -74,6 +77,16 @@ void initGLX() glXCopySubBuffer = (glXCopySubBuffer_func) getProcAddress( "glXCopySubBufferMESA" ); else glXCopySubBuffer = NULL; + if( hasGLExtension( "GLX_SGI_video_sync" )) + { + glXGetVideoSync = (glXGetVideoSync_func) getProcAddress( "glXGetVideoSyncSGI" ); + glXWaitVideoSync = (glXWaitVideoSync_func) getProcAddress( "glXWaitVideoSyncSGI" ); + } + else + { + glXGetVideoSync = NULL; + glXWaitVideoSync = NULL; + } } void initGL() diff --git a/glutils.h b/glutils.h index cc537f1170..33ef88ddb1 100644 --- a/glutils.h +++ b/glutils.h @@ -85,6 +85,11 @@ extern glActiveTexture_func glActiveTexture; // glXCopySubBufferMESA typedef void (*glXCopySubBuffer_func) ( Display* , GLXDrawable, int, int, int, int ); extern glXCopySubBuffer_func glXCopySubBuffer; +// video_sync extension functions +typedef void (*glXGetVideoSync_func)( unsigned int *count ); +typedef void (*glXWaitVideoSync_func)( int divisor, int remainder, unsigned int *count ); +extern glXGetVideoSync_func glXGetVideoSync; +extern glXWaitVideoSync_func glXWaitVideoSync; } // namespace diff --git a/options.cpp b/options.cpp index c3e9e45cf0..f0d202ed13 100644 --- a/options.cpp +++ b/options.cpp @@ -203,6 +203,7 @@ unsigned long Options::updateSettings() glMode = GLFallback; glAlwaysRebind = config->readEntry("GLAlwaysRebind", false ); glDirect = config->readEntry("GLDirect", true ); + glVSync = config->readEntry("GLVSync", true ); config->setGroup( "EffectShowFps" ); effectShowFpsAlpha = config->readEntry( "Alpha", 0.5 ); diff --git a/options.h b/options.h index e87184ff8c..24893f8579 100644 --- a/options.h +++ b/options.h @@ -304,6 +304,7 @@ class Options : public KDecorationOptions GLMode glMode; bool glAlwaysRebind; bool glDirect; + bool glVSync; double effectShowFpsAlpha; int effectShowFpsX; diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 482ef8b5d6..8850f85ac5 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -450,6 +450,22 @@ void SceneOpenGL::paint( QRegion damage, ToplevelList toplevels ) ungrabXServer(); } +// wait for vblank signal before painting +void SceneOpenGL::waitSync() + { // NOTE that vsync has no effect with indirect rendering + bool vsync = options->glVSync; + unsigned int sync; + + if( !vsync ) + return; + if( glXGetVideoSync ) + { + glFlush(); + glXGetVideoSync( &sync ); + glXWaitVideoSync( 2, ( sync + 1 ) % 2, &sync ); + } + } + // actually paint to the screen (double-buffer swap or copy from pixmap buffer) void SceneOpenGL::flushBuffer( int mask, const QRegion& damage ) { @@ -457,6 +473,7 @@ void SceneOpenGL::flushBuffer( int mask, const QRegion& damage ) { if( mask & PAINT_SCREEN_REGION ) { + waitSync(); if( glXCopySubBuffer ) { foreach( QRect r, damage.rects()) @@ -483,7 +500,10 @@ void SceneOpenGL::flushBuffer( int mask, const QRegion& damage ) } } else + { + waitSync(); glXSwapBuffers( display(), glxbuffer ); + } glXWaitGL(); XFlush( display()); } @@ -491,6 +511,7 @@ void SceneOpenGL::flushBuffer( int mask, const QRegion& damage ) { glFlush(); glXWaitGL(); + waitSync(); if( mask & PAINT_SCREEN_REGION ) foreach( QRect r, damage.rects()) XCopyArea( display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y()); diff --git a/scene_opengl.h b/scene_opengl.h index b55a58fe10..e21b9352e5 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -44,6 +44,7 @@ class SceneOpenGL void initBuffer(); void initRenderingContext(); bool findConfig( const int* attrs, GLXFBConfig* config, VisualID visual = None ); + void waitSync(); void flushBuffer( int mask, const QRegion& damage ); typedef GLuint Texture; GC gcroot;