this should improve v'syncing, maybe v'synced "smoothness"
remaining and exposed issue are "dirty textures" w/o damage events (see the requst description)
can be diminished by increasing MaxFPS above the fastest update (or shadowed below the slowest one)
CCBUG: 258971

svn path=/trunk/KDE/kdebase/workspace/; revision=1207577
This commit is contained in:
Thomas Lübking 2010-12-18 16:51:43 +00:00
parent 587a3cdb2b
commit e2dcfff601
7 changed files with 72 additions and 50 deletions

View file

@ -57,6 +57,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <stdio.h> #include <stdio.h>
#include <QMenu> #include <QMenu>
#include <QTimerEvent>
#include <kaction.h> #include <kaction.h>
#include <kactioncollection.h> #include <kactioncollection.h>
#include <klocale.h> #include <klocale.h>
@ -197,12 +198,9 @@ void Workspace::setupCompositing()
delete cm_selection; delete cm_selection;
return; return;
} }
int rate = xrrRefreshRate = KWin::currentRefreshRate(); xrrRefreshRate = KWin::currentRefreshRate();
compositeRate = 1000 / rate; // invalidate timer -> bounds delay to 0 and the update happens instantly
lastCompositePaint.start(); nextPaintReference = QTime::currentTime().addMSecs( -1000 );
// fake a previous paint, so that the next starts right now
nextPaintReference = QTime::currentTime().addMSecs( -compositeRate );
compositeTimer.setSingleShot( true );
checkCompositeTimer(); checkCompositeTimer();
composite_paint_times.clear(); composite_paint_times.clear();
XCompositeRedirectSubwindows( display(), rootWindow(), CompositeRedirectManual ); XCompositeRedirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
@ -253,7 +251,9 @@ void Workspace::finishCompositing()
effects = NULL; effects = NULL;
delete scene; delete scene;
scene = NULL; scene = NULL;
compositeTimer.stop(); if (compositeTimer)
killTimer(compositeTimer);
compositeTimer = 0;
mousePollingTimer.stop(); mousePollingTimer.stop();
repaints_region = QRegion(); repaints_region = QRegion();
for( ClientList::ConstIterator it = clients.constBegin(); for( ClientList::ConstIterator it = clients.constBegin();
@ -370,24 +370,27 @@ void Workspace::addRepaintFull()
checkCompositeTimer(); checkCompositeTimer();
} }
void Workspace::timerEvent( QTimerEvent *te )
{
if ( te->timerId() == compositeTimer )
{
killTimer( compositeTimer );
compositeTimer = 0;
performCompositing();
}
else
QObject::timerEvent( te );
}
void Workspace::performCompositing() void Workspace::performCompositing()
{ {
#ifdef KWIN_HAVE_COMPOSITING #ifdef KWIN_HAVE_COMPOSITING
// The event loop apparently tries to fire a QTimer as often as possible, even
// at the expense of not processing many X events. This means that the composite
// repaints can seriously impact performance of everything else, therefore throttle
// them - leave at least 1msec time after one repaint is finished and next one
// is started.
if( lastCompositePaint.elapsed() < 1 )
{
compositeTimer.start( 1 );
return;
}
if( !scene->waitSyncAvailable())
nextPaintReference = QTime::currentTime();
if((( repaints_region.isEmpty() && !windowRepaintsPending()) // no damage if((( repaints_region.isEmpty() && !windowRepaintsPending()) // no damage
|| !overlay_visible )) // nothing is visible anyway || !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 );
scene->idle(); scene->idle();
// Note: It would seem here we should undo suspended unredirect, but when scenes need // 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 // it for some reason, e.g. transformations or translucency, the next pass that does not
@ -395,6 +398,8 @@ void Workspace::performCompositing()
// Otherwise the window would not be painted normally anyway. // Otherwise the window would not be painted normally anyway.
return; 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 // create a list of all windows in the stacking order
ToplevelList windows = xStackingOrder(); ToplevelList windows = xStackingOrder();
foreach( EffectWindow* c, static_cast< EffectsHandlerImpl* >( effects )->elevatedWindows()) foreach( EffectWindow* c, static_cast< EffectsHandlerImpl* >( effects )->elevatedWindows())
@ -403,19 +408,16 @@ void Workspace::performCompositing()
windows.removeAll( t ); windows.removeAll( t );
windows.append( t ); windows.append( t );
} }
#if 0
// skip windows that are not yet ready for being painted // skip windows that are not yet ready for being painted
ToplevelList tmp = windows; ToplevelList tmp = windows;
windows.clear(); windows.clear();
#if 0
// There is a bug somewhere that prevents this from working properly (#160393), but additionally // There is a bug somewhere that prevents this from working properly (#160393), but additionally
// this cannot be used so carelessly - needs protections against broken clients, the window // this cannot be used so carelessly - needs protections against broken clients, the window
// should not get focus before it's displayed, handle unredirected windows properly and so on. // should not get focus before it's displayed, handle unredirected windows properly and so on.
foreach( Toplevel* c, tmp ) foreach( Toplevel* c, tmp )
if( c->readyForPainting()) if( c->readyForPainting())
windows.append( c ); windows.append( c );
#else
foreach( Toplevel* c, tmp )
windows.append( c );
#endif #endif
foreach( Toplevel* c, windows ) foreach( Toplevel* c, windows )
{ // This could be possibly optimized WRT obscuring, but that'd need being already { // This could be possibly optimized WRT obscuring, but that'd need being already
@ -423,7 +425,7 @@ void Workspace::performCompositing()
// TODO I think effects->transformWindowDamage() doesn't need to be called here, // TODO I think effects->transformWindowDamage() doesn't need to be called here,
// pre-paint will extend painted window areas as necessary. // pre-paint will extend painted window areas as necessary.
repaints_region |= c->repaints().translated( c->pos()); repaints_region |= c->repaints().translated( c->pos());
repaints_region |= c->decorationPendingRegion(); repaints_region |= c->decorationPendingRegion();
c->resetRepaints( c->decorationRect()); c->resetRepaints( c->decorationRect());
} }
QRegion repaints = repaints_region; QRegion repaints = repaints_region;
@ -431,23 +433,12 @@ void Workspace::performCompositing()
repaints_region = QRegion(); repaints_region = QRegion();
QTime t = QTime::currentTime(); QTime t = QTime::currentTime();
scene->paint( repaints, windows ); scene->paint( repaints, windows );
if( scene->waitSyncAvailable())
{
// If vsync is used, schedule the next repaint slightly in advance of the next sync,
// so that there is still time for the drawing to take place. We have just synced, and
// nextPaintReference is time from which multiples of compositeRate should be added,
// so set it 10ms back (meaning next paint will be in 'compositeRate - 10').
// However, make sure the reserve is smaller than the composite rate.
int reserve = compositeRate <= 10 ? compositeRate - 1 : 10;
nextPaintReference = QTime::currentTime().addMSecs( -reserve );
}
// Trigger at least one more pass even if there would be nothing to paint, so that scene->idle() // 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 // 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 // checkCompositeTime() would restart it again somewhen later, called from functions that
// would again add something pending. // would again add something pending.
checkCompositeTimer(); checkCompositeTimer();
checkCompositePaintTime( t.elapsed()); checkCompositePaintTime( t.elapsed());
lastCompositePaint.start();
#endif #endif
} }
@ -477,9 +468,11 @@ void Workspace::setCompositeTimer()
{ {
if( !compositing()) // should not really happen, but there may be e.g. some damage events still pending if( !compositing()) // should not really happen, but there may be e.g. some damage events still pending
return; return;
// The last paint set nextPaintReference as a reference time to which multiples of compositeRate
// should be added for the next paint. qBound() for protection; system time can change without notice. // should be added for the next paint. qBound() for protection; system time can change without notice.
compositeTimer.start( qBound( 0, nextPaintReference.msecsTo( QTime::currentTime() ), 250 ) % compositeRate ); if ( compositeTimer )
killTimer( compositeTimer );
int delay = options->maxFpsInterval - (qBound( 0, nextPaintReference.msecsTo( QTime::currentTime() ), 250 ) % options->maxFpsInterval);
compositeTimer = startTimer( delay );
} }
void Workspace::startMousePolling() void Workspace::startMousePolling()
@ -567,7 +560,9 @@ void Workspace::checkCompositePaintTime( int msec )
"If this was only a temporary problem, you can resume using the '%1' shortcut.\n" "If this was only a temporary problem, you can resume using the '%1' shortcut.\n"
"You can disable functionality checks in System Settings (on the Advanced tab in Desktop Effects).", shortcut ); "You can disable functionality checks in System Settings (on the Advanced tab in Desktop Effects).", shortcut );
Notify::raise( Notify::CompositingSlow, message ); Notify::raise( Notify::CompositingSlow, message );
compositeTimer.start( 1000 ); // so that it doesn't trigger sooner than suspendCompositing() if ( compositeTimer )
killTimer( compositeTimer );
compositeTimer = startTimer( 1000 ); // so that it doesn't trigger sooner than suspendCompositing()
} }
} }

View file

@ -66,9 +66,11 @@ int currentRefreshRate()
{ {
QString reply = QString::fromLocal8Bit( nvidia_settings.readAllStandardOutput() ); QString reply = QString::fromLocal8Bit( nvidia_settings.readAllStandardOutput() );
bool ok; bool ok;
rate = reply.split(' ').first().split(KGlobal::locale()->decimalSymbol()).first().toUInt( &ok ); const float frate = reply.split(' ').first().toFloat( &ok );
if ( !ok ) if ( !ok )
rate = -1; rate = -1;
else
rate = qRound(frate);
} }
} }
#ifdef HAVE_XRANDR #ifdef HAVE_XRANDR
@ -247,6 +249,7 @@ unsigned long Options::updateSettings()
CmdAllWheel = mouseWheelCommand(config.readEntry("CommandAllWheel","Nothing")); CmdAllWheel = mouseWheelCommand(config.readEntry("CommandAllWheel","Nothing"));
config=KConfigGroup(_config,"Compositing"); config=KConfigGroup(_config,"Compositing");
maxFpsInterval = qRound(1000.0/config.readEntry( "MaxFPS", 35 ));
refreshRate = config.readEntry( "RefreshRate", 0 ); refreshRate = config.readEntry( "RefreshRate", 0 );
// Read button tooltip animation effect from kdeglobals // Read button tooltip animation effect from kdeglobals

View file

@ -365,6 +365,7 @@ class Options : public KDecorationOptions
// XRender // XRender
bool xrenderSmoothScale; bool xrenderSmoothScale;
uint maxFpsInterval;
// Settings that should be auto-detected // Settings that should be auto-detected
uint refreshRate; uint refreshRate;
bool glDirect; bool glDirect;

View file

@ -91,6 +91,8 @@ Sources and other compositing managers:
namespace KWin namespace KWin
{ {
extern int currentRefreshRate();
//**************************************** //****************************************
// SceneOpenGL // SceneOpenGL
//**************************************** //****************************************
@ -109,6 +111,9 @@ GLXDrawable SceneOpenGL::last_pixmap = None;
bool SceneOpenGL::tfp_mode; // using glXBindTexImageEXT (texture_from_pixmap) bool SceneOpenGL::tfp_mode; // using glXBindTexImageEXT (texture_from_pixmap)
bool SceneOpenGL::db; // destination drawable is double-buffered bool SceneOpenGL::db; // destination drawable is double-buffered
bool SceneOpenGL::shm_mode; 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 #ifdef HAVE_XSHM
XShmSegmentInfo SceneOpenGL::shm; XShmSegmentInfo SceneOpenGL::shm;
#endif #endif
@ -124,6 +129,8 @@ SceneOpenGL::SceneOpenGL( Workspace* ws )
kDebug( 1212 ) << "No glx extensions available"; kDebug( 1212 ) << "No glx extensions available";
return; // error return; // error
} }
vBlankInterval = (1000<<10) / KWin::currentRefreshRate();
lastVBlank = QTime::currentTime();
initGLX(); initGLX();
// check for FBConfig support // check for FBConfig support
if( !hasGLExtension( "GLX_SGIX_fbconfig" ) || !glXGetFBConfigAttrib || !glXGetFBConfigs || if( !hasGLExtension( "GLX_SGIX_fbconfig" ) || !glXGetFBConfigAttrib || !glXGetFBConfigs ||
@ -796,22 +803,30 @@ void SceneOpenGL::waitSync()
{ // NOTE that vsync has no effect with indirect rendering { // NOTE that vsync has no effect with indirect rendering
if( waitSyncAvailable()) if( waitSyncAvailable())
{ {
unsigned int sync; // 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
glFlush(); // entire frame before this event, what could effectively mean "usleep(10000)", as it used to
glXGetVideoSync( &sync ); uint sync = ((lastVBlank.msecsTo( QTime::currentTime() )<<10) % vBlankInterval);
glXWaitVideoSync( 2, ( sync + 1 ) % 2, &sync ); if ( sync < (estimatedRenderTime+1)<<10 )
{
glFlush();
glXGetVideoSync( &sync );
glXWaitVideoSync( 2, ( sync + 1 ) % 2, &sync );
lastVBlank = QTime::currentTime();
}
} }
} }
// actually paint to the screen (double-buffer swap or copy from pixmap buffer) // actually paint to the screen (double-buffer swap or copy from pixmap buffer)
void SceneOpenGL::flushBuffer( int mask, QRegion damage ) void SceneOpenGL::flushBuffer( int mask, QRegion damage )
{ {
QTime t;
if( db ) if( db )
{ {
if( mask & PAINT_SCREEN_REGION ) if( mask & PAINT_SCREEN_REGION )
{ {
waitSync(); waitSync();
t = QTime::currentTime();
if( glXCopySubBuffer ) if( glXCopySubBuffer )
{ {
foreach( const QRect &r, damage.rects()) foreach( const QRect &r, damage.rects())
@ -849,22 +864,28 @@ void SceneOpenGL::flushBuffer( int mask, QRegion damage )
else else
{ {
waitSync(); waitSync();
t = QTime::currentTime();
glXSwapBuffers( display(), glxbuffer ); glXSwapBuffers( display(), glxbuffer );
} }
glXWaitGL(); glXWaitGL();
XFlush( display()); XFlush( display());
estimatedRenderTime = t.elapsed();
} }
else else
{ {
t = QTime::currentTime();
glFlush(); glFlush();
glXWaitGL(); glXWaitGL();
estimatedRenderTime = t.elapsed();
waitSync(); waitSync();
t = QTime::currentTime();
if( mask & PAINT_SCREEN_REGION ) if( mask & PAINT_SCREEN_REGION )
foreach( const QRect &r, damage.rects()) foreach( const QRect &r, damage.rects())
XCopyArea( display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y()); XCopyArea( display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y());
else else
XCopyArea( display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0 ); XCopyArea( display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0 );
XFlush( display()); XFlush( display());
estimatedRenderTime += t.elapsed();
} }
} }

View file

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

View file

@ -151,7 +151,8 @@ Workspace::Workspace( bool restore )
, forced_global_mouse_grab( false ) , forced_global_mouse_grab( false )
, cm_selection( NULL ) , cm_selection( NULL )
, compositingSuspended( false ) , compositingSuspended( false )
, compositeRate( 0 ) , compositeTimer( 0 )
, vBlankInterval( 0 )
, xrrRefreshRate( 0 ) , xrrRefreshRate( 0 )
, overlay( None ) , overlay( None )
, overlay_visible( true ) , overlay_visible( true )
@ -384,7 +385,6 @@ void Workspace::init()
connect( &reconfigureTimer, SIGNAL( timeout() ), this, SLOT( slotReconfigure() )); connect( &reconfigureTimer, SIGNAL( timeout() ), this, SLOT( slotReconfigure() ));
connect( &updateToolWindowsTimer, SIGNAL( timeout() ), this, SLOT( slotUpdateToolWindows() )); connect( &updateToolWindowsTimer, SIGNAL( timeout() ), this, SLOT( slotUpdateToolWindows() ));
connect( &compositeTimer, SIGNAL( timeout() ), SLOT( performCompositing() ));
connect( &mousePollingTimer, SIGNAL( timeout() ), SLOT( performMousePoll() )); connect( &mousePollingTimer, SIGNAL( timeout() ), SLOT( performMousePoll() ));
connect( KGlobalSettings::self(), SIGNAL( appearanceChanged() ), this, SLOT( reconfigure() )); connect( KGlobalSettings::self(), SIGNAL( appearanceChanged() ), this, SLOT( reconfigure() ));

View file

@ -774,6 +774,7 @@ class Workspace : public QObject, public KDecorationDefines
protected: protected:
bool keyPressMouseEmulation( XKeyEvent& ev ); bool keyPressMouseEmulation( XKeyEvent& ev );
void timerEvent( QTimerEvent *te );
Q_SIGNALS: Q_SIGNALS:
Q_SCRIPTABLE void compositingToggled( bool active ); Q_SCRIPTABLE void compositingToggled( bool active );
@ -1058,11 +1059,10 @@ class Workspace : public QObject, public KDecorationDefines
KSelectionOwner* cm_selection; KSelectionOwner* cm_selection;
bool compositingSuspended; bool compositingSuspended;
QTimer compositeTimer; int compositeTimer;
QTime lastCompositePaint;
QTime nextPaintReference; QTime nextPaintReference;
QTimer mousePollingTimer; QTimer mousePollingTimer;
int compositeRate; uint vBlankInterval;
int xrrRefreshRate; // used only for compositing int xrrRefreshRate; // used only for compositing
QRegion repaints_region; QRegion repaints_region;
Window overlay; // XComposite overlay window Window overlay; // XComposite overlay window
@ -1348,7 +1348,7 @@ inline bool Workspace::hasClient( const Client* c )
inline void Workspace::checkCompositeTimer() inline void Workspace::checkCompositeTimer()
{ {
if( !compositeTimer.isActive() ) if( !compositeTimer )
setCompositeTimer(); setCompositeTimer();
} }