Swap vsync order, trade in 1frame lag

REVIEW: 103058
This commit is contained in:
Thomas Lübking 2012-03-29 22:11:28 +02:00
parent ecb94fb98c
commit fc665106c9
15 changed files with 213 additions and 85 deletions

View file

@ -178,22 +178,20 @@ void Workspace::slotCompositingOptionsInitialized()
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);
fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval);
} else
vBlankInterval = 1 << 10; // no sync - DO NOT set "0", would cause div-by-zero segfaults.
vBlankPadding = 3; // vblank rounding errors... :-(
nextPaintReference.start();
m_timeSinceLastVBlank = fpsInterval - 1; // means "start now" - we dont't have even a slight idea when the first vsync will occur
checkCompositeTimer();
XCompositeRedirectSubwindows(display(), rootWindow(), CompositeRedirectManual);
new EffectsHandlerImpl(scene->compositingType()); // sets also the 'effects' pointer
addRepaintFull();
foreach (Client * c, clients)
c->setupCompositing();
c->setupCompositing();
foreach (Client * c, desktops)
c->setupCompositing();
c->setupCompositing();
foreach (Unmanaged * c, unmanaged)
c->setupCompositing();
c->setupCompositing();
discardPopup(); // force re-creation of the Alt+F3 popup (opacity option)
// render at least once
@ -394,12 +392,15 @@ void Workspace::timerEvent(QTimerEvent *te)
} else
QObject::timerEvent(te);
}
static bool s_pending = false;
QElapsedTimer profiler;
void Workspace::performCompositing()
{
if (((repaints_region.isEmpty() && !windowRepaintsPending()) // no damage
|| !scene->overlayWindow()->isVisible())) { // nothing is visible anyway
vBlankPadding += 3;
if (!scene->overlayWindow()->isVisible())
return; // nothing is visible anyway
bool pending = !repaints_region.isEmpty() || windowRepaintsPending();
if (!(pending || s_pending)) {
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
@ -407,6 +408,8 @@ void Workspace::performCompositing()
// Otherwise the window would not be painted normally anyway.
return;
}
profiler.start();
s_pending = pending;
// create a list of all windows in the stacking order
ToplevelList windows = xStackingOrder();
foreach (EffectWindow *c, static_cast< EffectsHandlerImpl* >(effects)->elevatedWindows()) {
@ -426,17 +429,8 @@ void Workspace::performCompositing()
QRegion repaints = repaints_region;
// clear all repaints, so that post-pass can add repaints for the next repaint
repaints_region = QRegion();
if (scene->waitSyncAvailable()) {
// vsync: paint the scene, than rebase the timer and use the duration for next timeout estimation
scene->paint(repaints, windows);
nextPaintReference.start();
} else {
// no vsyc -> inversion: reset the timer, then paint the scene, this way we can provide a constant framerate
nextPaintReference.start();
scene->paint(repaints, windows);
}
// reset the roundin error corrective... :-(
vBlankPadding = 3;
m_timeSinceLastVBlank = scene->paint(repaints, windows);
// 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
@ -471,19 +465,38 @@ void Workspace::setCompositeTimer()
if (!compositing()) // should not really happen, but there may be e.g. some damage events still pending
return;
// interval - "time since last paint completion" - "time we need to paint"
uint passed = nextPaintReference.elapsed() << 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);
uint padding = m_timeSinceLastVBlank << 10;
compositeTimer.start(delay >> 10, this);
if (scene->waitSyncAvailable()) {
// TODO: make vBlankTime dynamic?!
// It's required because glXWaitVideoSync will *likely* block a full frame if one enters
// a retrace pass which can last a variable amount of time, depending on the actual screen
// Now, my ooold 19" CRT can do such retrace so that 2ms are entirely sufficient,
// while another ooold 15" TFT requires about 6ms
if (padding > fpsInterval) {
// we're at low repaints or spent more time in painting than the user wanted to wait for that frame
padding = vBlankInterval - (padding%vBlankInterval); // -> align to next vblank
} else { // -> align to the next maxFps tick
padding = ((vBlankInterval - padding%vBlankInterval) + (fpsInterval/vBlankInterval-1)*vBlankInterval);
// "remaining time of the first vsync" + "time for the other vsyncs of the frame"
}
if (padding < options->vBlankTime()) { // we'll likely miss this frame
m_nextFrameDelay = (padding + vBlankInterval) >> 10;
padding = (padding + vBlankInterval - options->vBlankTime()) >> 10; // so we add one
// qDebug() << "WE LOST A FRAME";
} else {
m_nextFrameDelay = padding >> 10;
padding = (padding - options->vBlankTime()) >> 10;
}
}
else // w/o vsync we just jump to the next demanded tick
// the "1" will ensure we don't block out the eventloop - the system's just not faster
// "0" would be sufficient, but the compositor isn't the WMs only task
m_nextFrameDelay = padding = (padding > (int)fpsInterval) ? 1 : ((fpsInterval - padding) >> 10);
compositeTimer.start(qMin(padding, 250u), this); // force 4fps minimum
}
void Workspace::startMousePolling()

View file

@ -49,6 +49,7 @@ glXCopySubBuffer_func glXCopySubBuffer;
// video_sync extension functions
glXGetVideoSync_func glXGetVideoSync;
glXWaitVideoSync_func glXWaitVideoSync;
glXSwapInterval_func glXSwapInterval;
// GLX_SGIX_fbconfig
glXGetFBConfigAttrib_func glXGetFBConfigAttrib;
glXGetVisualFromFBConfig_func glXGetVisualFromFBConfig;
@ -162,6 +163,18 @@ void glxResolveFunctions()
glXWaitVideoSync = NULL;
}
if (hasGLExtension("GLX_SGI_swap_control")) {
glXSwapInterval = (glXSwapInterval_func) getProcAddress("glXSwapIntervalSGI");
} else if (hasGLExtension("GLX_EXT_swap_control")) {
glXSwapInterval = (glXSwapInterval_func) getProcAddress("glXSwapIntervalEXT");
} else if (hasGLExtension("GLX_MESA_swap_control")) {
glXSwapInterval = (glXSwapInterval_func) getProcAddress("glXSwapIntervalMESA");
} else if (hasGLExtension("GLX_OML_sync_control")) {
glXSwapInterval = (glXSwapInterval_func) getProcAddress("glXSwapIntervalOML");
} else {
glXSwapInterval = NULL;
}
GL_RESOLVE_WITH_EXT(glXGetFBConfigAttrib, glXGetFBConfigAttribSGIX);
GL_RESOLVE_WITH_EXT(glXGetVisualFromFBConfig, glXGetVisualFromFBConfigSGIX);
GL_RESOLVE(glXGetFBConfigs);

View file

@ -197,8 +197,10 @@ extern KWIN_EXPORT glXCopySubBuffer_func glXCopySubBuffer;
// video_sync extension functions
typedef int (*glXGetVideoSync_func)(unsigned int *count);
typedef int (*glXWaitVideoSync_func)(int divisor, int remainder, unsigned int *count);
typedef int (*glXSwapInterval_func)(int ratio);
extern KWIN_EXPORT glXGetVideoSync_func glXGetVideoSync;
extern KWIN_EXPORT glXWaitVideoSync_func glXWaitVideoSync;
extern KWIN_EXPORT glXSwapInterval_func glXSwapInterval;
// GLX_SGIX_fbconfig and misc GLX 1.3 stuff
typedef int (*glXGetFBConfigAttrib_func)(Display *dpy, GLXFBConfig config,
int attribute, int *value);

View file

@ -754,6 +754,15 @@ void Options::setRefreshRate(uint refreshRate)
emit refreshRateChanged();
}
void Options::setVBlankTime(uint vBlankTime)
{
if (m_vBlankTime == vBlankTime) {
return;
}
m_vBlankTime = vBlankTime;
emit vBlankTimeChanged();
}
void Options::setGlDirect(bool glDirect)
{
if (m_glDirect == glDirect) {
@ -929,6 +938,7 @@ unsigned long Options::loadConfig()
config = KConfigGroup(_config, "Compositing");
setMaxFpsInterval(qRound(1000.0 / config.readEntry("MaxFPS", Options::defaultMaxFps())));
setRefreshRate(config.readEntry("RefreshRate", Options::defaultRefreshRate()));
setVBlankTime(config.readEntry("VBlankTime", Options::defaultVBlankTime()));
return changed;
}

View file

@ -192,6 +192,7 @@ class Options : public QObject, public KDecorationOptions
Q_PROPERTY(bool xrenderSmoothScale READ isXrenderSmoothScale WRITE setXrenderSmoothScale NOTIFY xrenderSmoothScaleChanged)
Q_PROPERTY(uint maxFpsInterval READ maxFpsInterval WRITE setMaxFpsInterval NOTIFY maxFpsIntervalChanged)
Q_PROPERTY(uint refreshRate READ refreshRate WRITE setRefreshRate NOTIFY refreshRateChanged)
Q_PROPERTY(uint vBlankTime READ vBlankTime WRITE setVBlankTime NOTIFY vBlankTimeChanged)
Q_PROPERTY(bool glDirect READ isGlDirect WRITE setGlDirect NOTIFY glDirectChanged)
Q_PROPERTY(bool glStrictBinding READ isGlStrictBinding WRITE setGlStrictBinding NOTIFY glStrictBindingChanged)
/**
@ -583,6 +584,9 @@ public:
uint refreshRate() const {
return m_refreshRate;
}
uint vBlankTime() const {
return m_vBlankTime;
}
bool isGlDirect() const {
return m_glDirect;
}
@ -653,6 +657,7 @@ public:
void setXrenderSmoothScale(bool xrenderSmoothScale);
void setMaxFpsInterval(uint maxFpsInterval);
void setRefreshRate(uint refreshRate);
void setVBlankTime(uint vBlankTime);
void setGlDirect(bool glDirect);
void setGlStrictBinding(bool glStrictBinding);
void setGlStrictBindingFollowsDriver(bool glStrictBindingFollowsDriver);
@ -872,6 +877,9 @@ public:
static uint defaultRefreshRate() {
return 0;
}
static uint defaultVBlankTime() {
return 6144;
}
static bool defaultGlDirect() {
return true;
}
@ -960,6 +968,7 @@ Q_SIGNALS:
void xrenderSmoothScaleChanged();
void maxFpsIntervalChanged();
void refreshRateChanged();
void vBlankTimeChanged();
void glDirectChanged();
void glStrictBindingChanged();
void glStrictBindingFollowsDriverChanged();
@ -1005,6 +1014,7 @@ private:
uint m_maxFpsInterval;
// Settings that should be auto-detected
uint m_refreshRate;
uint m_vBlankTime;
bool m_glDirect;
bool m_glStrictBinding;
bool m_glStrictBindingFollowsDriver;

View file

@ -96,7 +96,6 @@ Scene* scene = 0;
Scene::Scene(Workspace* ws)
: QObject(ws)
, lastRenderTime(0)
, wspace(ws)
, has_waitSync(false)
, lanczos_filter(new LanczosFilter())
@ -168,7 +167,11 @@ void Scene::updateTimeDiff()
time_diff = 1;
last_time.start();
} else
time_diff = last_time.restart();
// the extra wspace->nextFrameDelay() basically means that we lie to the effect about the passed
// time - as a result the (animated) effect will run up to a frame shorter but in return stick
// closer to the runtime from the trigger
time_diff = last_time.restart() + wspace->nextFrameDelay();
if (time_diff < 0) // check time rollback
time_diff = 1;

10
scene.h
View file

@ -51,7 +51,9 @@ public:
virtual CompositingType compositingType() const = 0;
// Repaints the given screen areas, windows provides the stacking order.
// The entry point for the main part of the painting pass.
virtual void paint(QRegion damage, ToplevelList windows) = 0;
// returns the time since the last vblank signal - if there's one
// ie. "what of this frame is lost to painting"
virtual int paint(QRegion damage, ToplevelList windows) = 0;
// Notification function - KWin core informs about changes.
// Used to mainly discard cached data.
@ -91,11 +93,8 @@ public:
};
// types of filtering available
enum ImageFilterType { ImageFilterFast, ImageFilterGood };
inline uint estimatedRenderTime() {
return lastRenderTime;
}
// there's nothing to paint (adjust time_diff later)
void idle();
virtual void idle();
bool waitSyncAvailable() {
return has_waitSync;
}
@ -152,7 +151,6 @@ protected:
QRegion painted_region;
// time since last repaint
int time_diff;
uint lastRenderTime;
QElapsedTimer last_time;
Workspace* wspace;
bool has_waitSync;

View file

@ -108,6 +108,12 @@ bool SceneOpenGL::db; // destination drawable is double-buffered
#include "scene_opengl_glx.cpp"
#endif
void SceneOpenGL::idle()
{
flushBuffer(m_lastMask, m_lastDamage);
Scene::idle();
}
bool SceneOpenGL::initFailed() const
{
return !init_ok;

View file

@ -46,11 +46,13 @@ public:
virtual CompositingType compositingType() const {
return OpenGLCompositing;
}
virtual void paint(QRegion damage, ToplevelList windows);
virtual int paint(QRegion damage, ToplevelList windows);
virtual void windowAdded(Toplevel*);
virtual void windowDeleted(Deleted*);
virtual void screenGeometryChanged(const QSize &size);
void idle();
protected:
virtual void paintGenericScreen(int mask, ScreenPaintData data);
virtual void paintBackground(QRegion region);
@ -101,6 +103,9 @@ private:
QHash< Toplevel*, Window* > windows;
bool init_ok;
bool debug;
QElapsedTimer m_renderTimer;
QRegion m_lastDamage;
int m_lastMask;
};
class SceneOpenGL::TexturePrivate

View file

@ -62,6 +62,20 @@ SceneOpenGL::SceneOpenGL(Workspace* ws)
return; // error
}
init_ok = true;
// TODO: activate once this is resolved. currently the explicit invocation seems pointless
#if 0
// - internet rumors say: it doesn't work with TBDR
// - eglSwapInterval has no impact on intel GMA chips
has_waitSync = options->isGlVSync();
if (has_waitSync) {
has_waitSync = (eglSwapInterval(dpy, 1) == EGL_TRUE);
if (!has_waitSync)
kWarning(1212) << "Could not activate EGL v'sync on this system";
}
if (!has_waitSync)
eglSwapInterval(dpy, 0); // deactivate syncing
#endif
}
SceneOpenGL::~SceneOpenGL()
@ -175,10 +189,11 @@ bool SceneOpenGL::initDrawableConfigs()
}
// the entry function for painting
void SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
int SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
{
QElapsedTimer renderTimer;
renderTimer.start();
if (!m_lastDamage.isEmpty())
flushBuffer(m_lastMask, m_lastDamage);
m_renderTimer.start();
foreach (Toplevel * c, toplevels) {
assert(windows.contains(c));
@ -189,16 +204,17 @@ void SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
XSync(display(), false);
int mask = 0;
paintScreen(&mask, &damage); // call generic implementation
ungrabXServer(); // ungrab before flushBuffer(), it may wait for vsync
m_lastMask = mask;
m_lastDamage = damage;
glFlush();
ungrabXServer();
if (m_overlayWindow->window()) // show the window only after the first pass, since
m_overlayWindow->show(); // that pass may take long
lastRenderTime = renderTimer.elapsed();
if (!damage.isEmpty()) {
flushBuffer(mask, damage);
}
// do cleanup
stacking_order.clear();
checkGLError("PostPaint");
return m_renderTimer.elapsed();
}
void SceneOpenGL::waitSync()
@ -208,7 +224,6 @@ void SceneOpenGL::waitSync()
void SceneOpenGL::flushBuffer(int mask, QRegion damage)
{
glFlush();
if (mask & PAINT_SCREEN_REGION && surfaceHasSubPost && eglPostSubBufferNV) {
QRect damageRect = damage.boundingRect();

View file

@ -78,15 +78,26 @@ SceneOpenGL::SceneOpenGL(Workspace* ws)
glDrawBuffer(GL_BACK);
// Check whether certain features are supported
has_waitSync = false;
if (glXGetVideoSync && glXIsDirect(display(), ctxbuffer) && options->isGlVSync()) {
unsigned int sync;
if (glXGetVideoSync(&sync) == 0) {
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);
if (options->isGlVSync()) {
if (glXGetVideoSync && glXSwapInterval && glXIsDirect(display(), ctxbuffer)) {
unsigned int sync;
if (glXGetVideoSync(&sync) == 0) {
if (glXWaitVideoSync(1, 0, &sync) == 0) {
// NOTICE at this time we should actually check whether we can successfully
// deactivate the swapInterval "glXSwapInterval(0) == 0"
// (because we don't actually want it active unless we explicitly run a glXSwapBuffers)
// However mesa/dri will return a range error (6) because deactivating the
// swapinterval (as of today) seems completely unsupported
has_waitSync = true;
glXSwapInterval(0);
}
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);
} else
qWarning() << "NO VSYNC! glXGetVideoSync(&uint) isn't 0 but" << glXGetVideoSync(&sync);
qWarning() << "NO VSYNC! glXGetVideoSync, glXSwapInterval, glXIsDirect" <<
bool(glXGetVideoSync) << bool(glXSwapInterval) << glXIsDirect(display(), ctxbuffer);
}
debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
@ -453,11 +464,12 @@ bool SceneOpenGL::initDrawableConfigs()
}
// the entry function for painting
void SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
int SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
{
QElapsedTimer renderTimer;
renderTimer.start();
if (!m_lastDamage.isEmpty())
flushBuffer(m_lastMask, m_lastDamage);
// actually paint the frame, flushed with the NEXT frame
foreach (Toplevel * c, toplevels) {
assert(windows.contains(c));
stacking_order.append(windows[ c ]);
@ -474,33 +486,61 @@ void SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
checkGLError("Paint1");
#endif
paintScreen(&mask, &damage); // call generic implementation
m_lastMask = mask;
m_lastDamage = damage;
#ifdef CHECK_GL_ERROR
checkGLError("Paint2");
#endif
ungrabXServer(); // ungrab before flushBuffer(), it may wait for vsync
if (m_overlayWindow->window()) // show the window only after the first pass, since
m_overlayWindow->show(); // that pass may take long
lastRenderTime = renderTimer.elapsed();
if (!damage.isEmpty()) {
flushBuffer(mask, damage);
}
glFlush();
ungrabXServer();
if (m_overlayWindow->window()) // show the window only after the first pass,
m_overlayWindow->show(); // since that pass may take long
// do cleanup
stacking_order.clear();
checkGLError("PostPaint");
return m_renderTimer.elapsed();
}
#define VSYNC_DEBUG 0
// wait for vblank signal before painting
void SceneOpenGL::waitSync()
{
// NOTE that vsync has no effect with indirect rendering
if (waitSyncAvailable()) {
#if VSYNC_DEBUG
m_renderTimer.start();
#endif
uint sync;
glFlush();
#if 0
// TODO: why precisely is this important?
// the sync counter /can/ perform multiple steps during glXGetVideoSync & glXWaitVideoSync
// but this only leads to waiting for two frames??!?
glXGetVideoSync(&sync);
glXWaitVideoSync(2, (sync + 1) % 2, &sync);
#endif
glXWaitVideoSync(1, 0, &sync);
#if VSYNC_DEBUG
static int waitTime = 0, waitCounter = 0, crapCounter = 0;
if (m_renderTimer.elapsed() > 11)
++crapCounter;
waitTime += m_renderTimer.elapsed();
++waitCounter;
if (waitCounter > 99)
{
qDebug() << "mean vsync wait time:" << float((float)waitTime / (float)waitCounter) << crapCounter << "/100";
crapCounter = waitTime = waitCounter = 0;
}
#endif
}
m_renderTimer.start(); // yes, the framerate shall be constant anyway.
}
#undef VSYNC_DEBUG
// actually paint to the screen (double-buffer swap or copy from pixmap buffer)
void SceneOpenGL::flushBuffer(int mask, QRegion damage)
{
@ -556,22 +596,26 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage)
}
}
} else {
waitSync();
glXSwapBuffers(display(), glxbuffer);
if (glXSwapInterval) {
glXSwapInterval(1);
glXSwapBuffers(display(), glxbuffer);
glXSwapInterval(0);
m_renderTimer.start(); // this is important so we don't assume to be loosing frames in the compositor timing calculation
} else {
waitSync();
glXSwapBuffers(display(), glxbuffer);
}
}
glXWaitGL();
XFlush(display());
} else {
glFlush();
glXWaitGL();
waitSync();
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());
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());
}
XFlush(display());
}
void SceneOpenGL::screenGeometryChanged(const QSize &size)

View file

@ -170,7 +170,7 @@ void SceneXrender::createBuffer()
}
// the entry point for painting
void SceneXrender::paint(QRegion damage, ToplevelList toplevels)
int SceneXrender::paint(QRegion damage, ToplevelList toplevels)
{
QElapsedTimer renderTimer;
renderTimer.start();
@ -186,11 +186,11 @@ void SceneXrender::paint(QRegion damage, ToplevelList toplevels)
if (m_overlayWindow->window()) // show the window only after the first pass, since
m_overlayWindow->show(); // that pass may take long
lastRenderTime = renderTimer.elapsed();
flushBuffer(mask, damage);
// do cleanup
stacking_order.clear();
return renderTimer.elapsed();
}
void SceneXrender::flushBuffer(int mask, QRegion damage)

View file

@ -44,7 +44,7 @@ public:
virtual CompositingType compositingType() const {
return XRenderCompositing;
}
virtual void paint(QRegion damage, ToplevelList windows);
virtual int paint(QRegion damage, ToplevelList windows);
virtual void windowAdded(Toplevel*);
virtual void windowDeleted(Deleted*);
virtual void screenGeometryChanged(const QSize &size);

View file

@ -111,6 +111,7 @@ Workspace::Workspace(bool restore)
, m_screenEdgeOrientation(0)
#endif
// Unsorted
, m_nextFrameDelay(0)
, active_popup(NULL)
, active_popup_client(NULL)
, temporaryRulesMessages("_KDE_NET_WM_TEMPORARY_RULES", NULL, false)
@ -232,7 +233,6 @@ Workspace::Workspace(bool restore)
tab_box = new TabBox::TabBox(this);
#endif
nextPaintReference.invalidate(); // Initialize the timer
setupCompositing();
// Compatibility

View file

@ -533,6 +533,10 @@ public:
void addRepaint(int x, int y, int w, int h);
void checkUnredirect(bool force = false);
void checkCompositeTimer();
// returns the _estimated_ delay to the next screen update
// good for having a rough idea to calculate transformations, bad to rely on.
// might happen few ms earlier, might be an entire frame to short. This is NOT deterministic.
int nextFrameDelay();
// Mouse polling
void startMousePolling();
@ -766,6 +770,7 @@ private:
bool windowRepaintsPending() const;
void setCompositeTimer();
int m_timeSinceLastVBlank, m_nextFrameDelay;
typedef QHash< QString, QVector<int> > DesktopFocusChains;
DesktopFocusChains::Iterator m_desktopFocusChain;
@ -909,9 +914,8 @@ private:
KSelectionOwner* cm_selection;
bool compositingSuspended, compositingBlocked;
QBasicTimer compositeTimer;
QElapsedTimer nextPaintReference;
QTimer mousePollingTimer;
uint vBlankInterval, vBlankPadding, fpsInterval, estimatedRenderTime;
uint vBlankInterval, fpsInterval;
int xrrRefreshRate; // used only for compositing
QRegion repaints_region;
QSlider* transSlider;
@ -1182,6 +1186,11 @@ inline void Workspace::checkCompositeTimer()
setCompositeTimer();
}
inline int Workspace::nextFrameDelay()
{
return m_nextFrameDelay;
}
inline bool Workspace::hasDecorationPlugin() const
{
if (!mgr) {