Swap vsync order, trade in 1frame lag
REVIEW: 103058
This commit is contained in:
parent
ecb94fb98c
commit
fc665106c9
15 changed files with 213 additions and 85 deletions
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
10
options.cpp
10
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;
|
||||
}
|
||||
|
|
10
options.h
10
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;
|
||||
|
|
|
@ -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
10
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
13
workspace.h
13
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<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) {
|
||||
|
|
Loading…
Reference in a new issue