diff --git a/composite.cpp b/composite.cpp index c2ad976ace..00e088ab62 100644 --- a/composite.cpp +++ b/composite.cpp @@ -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() diff --git a/libkwineffects/kwinglutils_funcs.cpp b/libkwineffects/kwinglutils_funcs.cpp index 6d79507b49..a67c7203b3 100644 --- a/libkwineffects/kwinglutils_funcs.cpp +++ b/libkwineffects/kwinglutils_funcs.cpp @@ -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); diff --git a/libkwineffects/kwinglutils_funcs.h b/libkwineffects/kwinglutils_funcs.h index a2c047b092..4f8125ab16 100644 --- a/libkwineffects/kwinglutils_funcs.h +++ b/libkwineffects/kwinglutils_funcs.h @@ -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); diff --git a/options.cpp b/options.cpp index 6188299039..ce9e3719a8 100644 --- a/options.cpp +++ b/options.cpp @@ -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; } diff --git a/options.h b/options.h index e89d02b556..fc44b729ca 100644 --- a/options.h +++ b/options.h @@ -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; diff --git a/scene.cpp b/scene.cpp index ff6aec7273..6dfc0b1432 100644 --- a/scene.cpp +++ b/scene.cpp @@ -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; diff --git a/scene.h b/scene.h index c7e462f202..209d145682 100644 --- a/scene.h +++ b/scene.h @@ -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; diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 2ff293847f..6ca15ddb40 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -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; diff --git a/scene_opengl.h b/scene_opengl.h index 7cf3b6c8fb..de33ce4d00 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -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 diff --git a/scene_opengl_egl.cpp b/scene_opengl_egl.cpp index 87690bff8d..33e8960413 100644 --- a/scene_opengl_egl.cpp +++ b/scene_opengl_egl.cpp @@ -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(); diff --git a/scene_opengl_glx.cpp b/scene_opengl_glx.cpp index 1ec57631d3..4cca4cc4c4 100644 --- a/scene_opengl_glx.cpp +++ b/scene_opengl_glx.cpp @@ -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) diff --git a/scene_xrender.cpp b/scene_xrender.cpp index 265825cb82..142d29f11d 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -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) diff --git a/scene_xrender.h b/scene_xrender.h index 98c4b3b94a..f312759bfc 100644 --- a/scene_xrender.h +++ b/scene_xrender.h @@ -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); diff --git a/workspace.cpp b/workspace.cpp index 466aace13c..12bcdcf9ef 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -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 diff --git a/workspace.h b/workspace.h index 367477e5df..ea275d3f81 100644 --- a/workspace.h +++ b/workspace.h @@ -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 > 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) {