[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