revert vsync strategy, fix timeouts

differecens to patch atteched to 258971:
- removed debug statements
- fixed indention...
- NON vsync strategy does not rely on the estimation, but on the time passed since the last repaint trigger, allowing a precise framerate

CCBUG: 258971


svn path=/trunk/KDE/kdebase/workspace/; revision=1210445
This commit is contained in:
Thomas Lübking 2010-12-31 13:14:11 +00:00
parent c81051a46f
commit 337e1eac18
9 changed files with 59 additions and 40 deletions

View file

@ -199,8 +199,17 @@ void Workspace::setupCompositing()
return;
}
xrrRefreshRate = KWin::currentRefreshRate();
// invalidate timer -> bounds delay to 0 and the update happens instantly
nextPaintReference = QTime::currentTime().addMSecs( -1000 );
fpsInterval = (options->maxFpsInterval<<10);
if ( scene->waitSyncAvailable() ) // if we do vsync, set the fps to the next multiple of the vblank rate
{
vBlankInterval = (1000<<10)/xrrRefreshRate;
fpsInterval -= (fpsInterval % vBlankInterval);
fpsInterval = qMax(fpsInterval, vBlankInterval);
}
else
vBlankInterval = 1<<10; // no sync - DO NOT set "0", would cause div-by-zero segfaults.
vBlankPadding = 3; // vblank rounding errors... :-(
nextPaintReference = QTime::currentTime();
checkCompositeTimer();
composite_paint_times.clear();
XCompositeRedirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
@ -388,9 +397,7 @@ void Workspace::performCompositing()
if((( repaints_region.isEmpty() && !windowRepaintsPending()) // no damage
|| !overlay_visible )) // nothing is visible anyway
{
// invalidate timer, we're idle, thus have already waited maxFps don't want to
// wait more when we woke up
nextPaintReference = QTime::currentTime().addMSecs( -1000 );
vBlankPadding += 3;
scene->idle();
// Note: It would seem here we should undo suspended unredirect, but when scenes need
// it for some reason, e.g. transformations or translucency, the next pass that does not
@ -398,8 +405,6 @@ void Workspace::performCompositing()
// Otherwise the window would not be painted normally anyway.
return;
}
// we paint now, how much time ever it takes, we wanna show up in maxfps from now
nextPaintReference = QTime::currentTime();
// create a list of all windows in the stacking order
ToplevelList windows = xStackingOrder();
foreach( EffectWindow* c, static_cast< EffectsHandlerImpl* >( effects )->elevatedWindows())
@ -432,7 +437,18 @@ void Workspace::performCompositing()
// clear all repaints, so that post-pass can add repaints for the next repaint
repaints_region = QRegion();
QTime t = QTime::currentTime();
scene->paint( repaints, windows );
if ( scene->waitSyncAvailable() )
{ // vsync: paint the scene, than rebase the timer and use the duration for next timeout estimation
scene->paint( repaints, windows );
nextPaintReference = QTime::currentTime();
}
else
{ // no vsyc -> inversion: reset the timer, then paint the scene, this way we can provide a constant framerate
nextPaintReference = QTime::currentTime();
scene->paint( repaints, windows );
}
// reset the roundin error corrective... :-(
vBlankPadding = 3;
// Trigger at least one more pass even if there would be nothing to paint, so that scene->idle()
// is called the next time. If there would be nothing pending, it will not restart the timer and
// checkCompositeTime() would restart it again somewhen later, called from functions that
@ -468,11 +484,25 @@ void Workspace::setCompositeTimer()
{
if( !compositing()) // should not really happen, but there may be e.g. some damage events still pending
return;
// should be added for the next paint. qBound() for protection; system time can change without notice.
if ( compositeTimer )
killTimer( compositeTimer );
int delay = options->maxFpsInterval - (qBound( 0, nextPaintReference.msecsTo( QTime::currentTime() ), 250 ) % options->maxFpsInterval);
compositeTimer = startTimer( delay );
// interval - "time since last paint completion" - "time we need to paint"
uint passed = nextPaintReference.msecsTo( QTime::currentTime() ) << 10;
uint delay = fpsInterval;
if ( scene->waitSyncAvailable() )
{
if ( passed > fpsInterval )
{
delay = vBlankInterval;
passed %= vBlankInterval;
}
delay -= ( (passed + ((scene->estimatedRenderTime() + vBlankPadding)<<10) ) % vBlankInterval );
}
else
delay = qBound( 0, int(delay - passed), 250<<10 );
compositeTimer = startTimer( delay>>10 );
}
void Workspace::startMousePolling()

View file

@ -251,7 +251,7 @@ unsigned long Options::updateSettings()
CmdAllWheel = mouseWheelCommand(config.readEntry("CommandAllWheel","Nothing"));
config=KConfigGroup(_config,"Compositing");
maxFpsInterval = qRound(1000.0/config.readEntry( "MaxFPS", 35 ));
maxFpsInterval = qRound(1000.0/config.readEntry( "MaxFPS", 60 ));
refreshRate = config.readEntry( "RefreshRate", 0 );
// Read button tooltip animation effect from kdeglobals

View file

@ -92,6 +92,7 @@ class Scene
};
// types of filtering available
enum ImageFilterType { ImageFilterFast, ImageFilterGood };
inline uint estimatedRenderTime() { return lastRenderTime; }
// there's nothing to paint (adjust time_diff later)
void idle();
bool waitSyncAvailable() { return has_waitSync; }
@ -143,6 +144,7 @@ class Scene
QRegion painted_region;
// time since last repaint
int time_diff;
uint lastRenderTime;
QTime last_time;
Workspace* wspace;
bool has_waitSync;

View file

@ -44,6 +44,7 @@ SceneBasic::~SceneBasic()
void SceneBasic::paint( QRegion, ToplevelList windows )
{
QTime t = QTime::currentTime();
Pixmap composite_pixmap = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(), DefaultDepth( display(), DefaultScreen( display())));
XGCValues val;
val.foreground = WhitePixel( display(), DefaultScreen( display()));
@ -64,6 +65,7 @@ void SceneBasic::paint( QRegion, ToplevelList windows )
qMax( 0, -(*it)->x()), qMax( 0, -(*it)->y()), r.width(), r.height(), r.x(), r.y());
}
}
lastRenderTime = t.elapsed();
XCopyArea( display(), composite_pixmap, rootWindow(), gc, 0, 0, displayWidth(), displayHeight(), 0, 0 );
XFreeGC( display(), gc );
XFreePixmap( display(), composite_pixmap );

View file

@ -111,9 +111,6 @@ GLXDrawable SceneOpenGL::last_pixmap = None;
bool SceneOpenGL::tfp_mode; // using glXBindTexImageEXT (texture_from_pixmap)
bool SceneOpenGL::db; // destination drawable is double-buffered
bool SceneOpenGL::shm_mode;
uint SceneOpenGL::vBlankInterval;
uint SceneOpenGL::estimatedRenderTime = 0xfffffff; // Looooong - to ensure we wait on the first frame
QTime SceneOpenGL::lastVBlank;
#ifdef HAVE_XSHM
XShmSegmentInfo SceneOpenGL::shm;
#endif
@ -129,8 +126,6 @@ SceneOpenGL::SceneOpenGL( Workspace* ws )
kDebug( 1212 ) << "No glx extensions available";
return; // error
}
vBlankInterval = (1000<<10) / KWin::currentRefreshRate();
lastVBlank = QTime::currentTime();
initGLX();
// check for FBConfig support
if( !hasGLExtension( "GLX_SGIX_fbconfig" ) || !glXGetFBConfigAttrib || !glXGetFBConfigs ||
@ -171,7 +166,11 @@ SceneOpenGL::SceneOpenGL( Workspace* ws )
{
if( glXWaitVideoSync( 1, 0, &sync ) == 0 )
has_waitSync = true;
else
qWarning() << "NO VSYNC! glXWaitVideoSync(1,0,&uint) isn't 0 but" << glXWaitVideoSync( 1, 0, &sync );
}
else
qWarning() << "NO VSYNC! glXGetVideoSync(&uint) isn't 0 but" << glXGetVideoSync( &sync );
}
// OpenGL scene setup
@ -756,6 +755,7 @@ bool SceneOpenGL::selfCheckFinish()
// the entry function for painting
void SceneOpenGL::paint( QRegion damage, ToplevelList toplevels )
{
QTime t = QTime::currentTime();
foreach( Toplevel* c, toplevels )
{
assert( windows.contains( c ));
@ -784,6 +784,7 @@ void SceneOpenGL::paint( QRegion damage, ToplevelList toplevels )
damage |= selfCheckRegion();
}
#endif
lastRenderTime = t.elapsed();
flushBuffer( mask, damage );
#if 0
if( !selfCheckDone )
@ -803,30 +804,21 @@ void SceneOpenGL::waitSync()
{ // NOTE that vsync has no effect with indirect rendering
if( waitSyncAvailable())
{
// hackalert - we abuse "sync" as "remaining time before next estimated vblank"
// reason: we do not "just wait" for the next vsync if we estimate that we can paint the
// entire frame before this event, what could effectively mean "usleep(10000)", as it used to
uint sync = ((lastVBlank.msecsTo( QTime::currentTime() )<<10) % vBlankInterval);
if ( sync < (estimatedRenderTime+1)<<10 )
{
glFlush();
glXGetVideoSync( &sync );
glXWaitVideoSync( 2, ( sync + 1 ) % 2, &sync );
lastVBlank = QTime::currentTime();
}
uint sync;
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, QRegion damage )
{
QTime t;
if( db )
{
if( mask & PAINT_SCREEN_REGION )
{
waitSync();
t = QTime::currentTime();
if( glXCopySubBuffer )
{
foreach( const QRect &r, damage.rects())
@ -864,28 +856,22 @@ void SceneOpenGL::flushBuffer( int mask, QRegion damage )
else
{
waitSync();
t = QTime::currentTime();
glXSwapBuffers( display(), glxbuffer );
}
glXWaitGL();
XFlush( display());
estimatedRenderTime = t.elapsed();
}
else
{
t = QTime::currentTime();
glFlush();
glXWaitGL();
estimatedRenderTime = t.elapsed();
waitSync();
t = QTime::currentTime();
if( mask & PAINT_SCREEN_REGION )
foreach( const QRect &r, damage.rects())
XCopyArea( display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y());
else
XCopyArea( display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0 );
XFlush( display());
estimatedRenderTime += t.elapsed();
}
}

View file

@ -90,8 +90,6 @@ class SceneOpenGL
static GLXDrawable last_pixmap; // for a workaround in bindTexture()
static bool tfp_mode;
static bool shm_mode;
static uint vBlankInterval, estimatedRenderTime;
static QTime lastVBlank;
QHash< Toplevel*, Window* > windows;
#ifdef HAVE_XSHM
static XShmSegmentInfo shm;

View file

@ -271,6 +271,7 @@ bool SceneXrender::selfCheckFinish()
// the entry point for painting
void SceneXrender::paint( QRegion damage, ToplevelList toplevels )
{
QTime t = QTime::currentTime();
foreach( Toplevel* c, toplevels )
{
assert( windows.contains( c ));
@ -285,6 +286,7 @@ void SceneXrender::paint( QRegion damage, ToplevelList toplevels )
selfCheckSetup();
damage |= selfCheckRegion();
}
lastRenderTime = t.elapsed();
flushBuffer( mask, damage );
if( !selfCheckDone )
{

View file

@ -152,7 +152,6 @@ Workspace::Workspace( bool restore )
, cm_selection( NULL )
, compositingSuspended( false )
, compositeTimer( 0 )
, vBlankInterval( 0 )
, xrrRefreshRate( 0 )
, overlay( None )
, overlay_visible( true )

View file

@ -1062,7 +1062,7 @@ class Workspace : public QObject, public KDecorationDefines
int compositeTimer;
QTime nextPaintReference;
QTimer mousePollingTimer;
uint vBlankInterval;
uint vBlankInterval, vBlankPadding, fpsInterval, estimatedRenderTime;
int xrrRefreshRate; // used only for compositing
QRegion repaints_region;
Window overlay; // XComposite overlay window