[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kwin
Subject:    Better vsync (was Re: Basic RandR support)
From:       Philip Falkner <philip.falkner () gmail ! com>
Date:       2006-11-28 18:00:55
Message-ID: 200611281300.56047.philip.falkner () gmail ! com
[Download RAW message or body]

On Friday 24 November 2006 18:03, Lubos Lunak wrote:
> On Friday 24 November 2006 20:16, Philip Falkner wrote:
> > Hm.  I'm seeing a sawtooth here too.  Even with 50Hz, it's pretty jagged,
> > although in a different way.  Without vsync it's all happy, of course, no
> > matter what refresh rate used.  Setting the refresh to 200Hz with vsync
> > makes it look a lot better here, but I think that might be just papering
> > over the problem.
>
>  Actually this may be a bug in the code. If I manually set refresh to 100Hz
> and have vblank syncing enabled, then showfps shows the frame rate to
> slightly vary slightly below 60Hz, i.e. the detected refresh rate.
> Specifically the throttling code in Workspace::performCompositing() could
> cause it, as it basically makes kwin miss one refresh because of the timer
> being set exactly to it, so the repaint should be attempted again in a
> shorter time. However I fail to see how this should affect even the normal
> almost-idle case when the painting should not cause overloading. I think
> this needs some more experimenting.

Here's my understanding of things.

We want to paint RefreshRate times per second.  In the non-vsync case, this is 
easy to do by setting the timer to fire that many times per second.  In the 
vsync case, we want the same, but the paint should also be done long enough 
before the sync signal that we don't miss it, but close enough to it that we 
don't sit in waitSync() all the time.

When vsync is on and RefreshRate matches the monitor's current refresh rate, 
then we just need to make each painting pass happen soon before each sync 
signal.  If RefreshRate is greater, then vsync means we're limited to the 
monitor's refresh rate.  If RefreshRate is less, then we should be limited to 
RefreshRate, NOT the monitor's refresh rate.  Of course, if RefreshRate and 
the monitor's refresh rate don't divide evenly into each other, then we may 
not be able to maintain an absolutely constant fps.


I've put together a patch which, when vsync is in use, alters compositeTimer's 
interval at the end of performCompositing() based on roughly how long it 
takes to paint, and a doubled refresh rate.  It seems to work here, whether 
RefreshRate is equal to the monitor's current refresh rate or not.  It does 
make vsync slightly more expensive than non-vsync, but achieves painting that 
usually hits the desired refresh rate (except when lots of paints are 
happening, of course).

I'm not proposing this for commit so much as asking for comments.

-- 
Philip Falkner

["kwin_fix-vsync.patch" (text/x-diff)]

Index: workspace.h
===================================================================
--- workspace.h	(revision 608825)
+++ workspace.h	(working copy)
@@ -682,6 +682,7 @@
         KSelectionOwner* cm_selection;
         QTimer compositeTimer;
         QTime lastCompositePaint;
+        int compositeRate;
         QRegion damage_region;
         Window overlay; // XComposite overlay window
         QSlider *transSlider;
Index: workspace.cpp
===================================================================
--- workspace.cpp	(revision 608825)
+++ workspace.cpp	(working copy)
@@ -128,6 +128,7 @@
     block_stacking_updates( 0 ),
     forced_global_mouse_grab( false ),
     cm_selection( NULL ),
+    compositeRate( 0 ),
     damage_region( None ),
     overlay( None ),
     transSlider( NULL ),
Index: scene.h
===================================================================
--- scene.h	(revision 608825)
+++ scene.h	(working copy)
@@ -62,6 +62,7 @@
             };
         // there's nothing to paint (adjust time_diff later)
         void idle();
+        bool waitSyncAvailable() { return has_waitSync; }
     protected:
         // shared implementation, starts painting the screen
         void paintScreen( int* mask, QRegion* region );
@@ -97,6 +98,7 @@
         int time_diff;
         QTime last_time;
         Workspace* wspace;
+        bool has_waitSync;
     };
 
 // The base class for windows representations in composite backends
Index: scene.cpp
===================================================================
--- scene.cpp	(revision 608825)
+++ scene.cpp	(working copy)
@@ -84,7 +84,8 @@
 Scene* scene;
 
 Scene::Scene( Workspace* ws )
-    : wspace( ws )
+    : wspace( ws ),
+    has_waitSync( false )
     {
     }
     
Index: scene_opengl.cpp
===================================================================
--- scene_opengl.cpp	(revision 608825)
+++ scene_opengl.cpp	(working copy)
@@ -175,6 +175,7 @@
     initGL();
     if( db )
         glDrawBuffer( GL_BACK );
+    has_waitSync = glXGetVideoSync ? true : false;
 
     // Check whether certain features are supported
     supports_saturation = ((hasGLExtension("GL_ARB_texture_env_crossbar")
@@ -456,13 +457,10 @@
 // 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 )
+    if( waitSyncAvailable() && options->glVSync )
         {
+        unsigned int sync;
+
         glFlush();
         glXGetVideoSync( &sync );
         glXWaitVideoSync( 2, ( sync + 1 ) % 2, &sync );
Index: composite.cpp
===================================================================
--- composite.cpp	(revision 608825)
+++ composite.cpp	(working copy)
@@ -107,7 +107,8 @@
     else if( rate > 1000 )
         rate = 1000;
     kDebug( 1212 ) << "Refresh rate " << rate << "Hz" << endl;
-    compositeTimer.start( 1000 / rate );
+    compositeRate = 1000 / rate;
+    compositeTimer.start( compositeRate );
     lastCompositePaint.start();
     XCompositeRedirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
     if( dynamic_cast< SceneOpenGL* >( scene ))
@@ -228,6 +229,16 @@
     // clear all damage, so that post-pass can add damage for the next repaint
     damage_region = QRegion();
     scene->paint( damage, windows );
+    if( scene->waitSyncAvailable() && options->glVSync )
+        { // if we're using vsync, then time the next paint pass to
+          // before the next available sync
+        int paintTime = ( lastCompositePaint.elapsed() % compositeRate ) +
+                        ( compositeRate / 2 );
+        if( paintTime >= compositeRate )
+            compositeTimer.start( paintTime );
+        else if( paintTime < compositeRate )
+            compositeTimer.start( compositeRate - paintTime );
+        }
     lastCompositePaint.start();
     }
 


_______________________________________________
Kwin mailing list
Kwin@kde.org
https://mail.kde.org/mailman/listinfo/kwin


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic