support a permanent glSwapBuffer

either by
- forcing fullrepaints unconditionally
- turning a repaint to a full one beyond a threshhold
- completing the the backbuffer from the frontbuffer after the paint

BUG: 307965
FIXED-IN: 4.10
REVIEW: 107198
icc-effect-5.14.5
Thomas Lübking 2013-02-18 23:17:46 +01:00
parent 499e34736b
commit 9aef5b85a0
9 changed files with 224 additions and 84 deletions

View File

@ -501,8 +501,6 @@ void Compositor::timerEvent(QTimerEvent *te)
QObject::timerEvent(te);
}
static int s_pendingFlushes = 0;
void Compositor::performCompositing()
{
if (!isOverlayWindowVisible())
@ -543,15 +541,7 @@ void Compositor::performCompositing()
win->getDamageRegionReply();
}
bool pending = !repaints_region.isEmpty() || windowRepaintsPending();
if (pending)
s_pendingFlushes = 3;
else if (m_scene->hasPendingFlush())
--s_pendingFlushes;
else
s_pendingFlushes = 0;
if (s_pendingFlushes < 1) {
s_pendingFlushes = 0;
if (repaints_region.isEmpty() && !windowRepaintsPending()) {
m_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

View File

@ -194,12 +194,13 @@ bool EglOnXBackend::initBufferConfigs()
void EglOnXBackend::present()
{
if (lastMask() & Scene::PAINT_SCREEN_REGION && surfaceHasSubPost && eglPostSubBufferNV) {
const QRect damageRect = lastDamage().boundingRect();
eglPostSubBufferNV(dpy, surface, damageRect.left(), displayHeight() - damageRect.bottom() - 1, damageRect.width(), damageRect.height());
} else {
const bool swap = (options->glPreferBufferSwap() && options->glPreferBufferSwap() != Options::ExtendDamage) ||
!(lastMask() & Scene::PAINT_SCREEN_REGION && surfaceHasSubPost && eglPostSubBufferNV);
if (swap) {
eglSwapBuffers(dpy, surface);
} else {
const QRect damageRect = lastDamage().boundingRect();
eglPostSubBufferNV(dpy, surface, damageRect.left(), displayHeight() - damageRect.bottom() - 1, damageRect.width(), damageRect.height());
}
eglWaitGL();

View File

@ -115,7 +115,7 @@ void GlxBackend::init()
// However mesa/dri will return a range error (6) because deactivating the
// swapinterval (as of today) seems completely unsupported
setHasWaitSync(true);
setSwapInterval(0);
setSwapInterval(1);
}
else
qWarning() << "NO VSYNC! glXWaitVideoSync(1,0,&uint) isn't 0 but" << glXWaitVideoSync(1, 0, &sync);
@ -473,77 +473,80 @@ void GlxBackend::waitSync()
void GlxBackend::present()
{
QRegion displayRegion(0, 0, displayWidth(), displayHeight());
const bool fullRepaint = (lastDamage() == displayRegion);
if (isDoubleBuffer()) {
if (lastMask() & Scene::PAINT_SCREEN_REGION) {
waitSync();
if (glXCopySubBuffer) {
foreach (const QRect & r, lastDamage().rects()) {
// convert to OpenGL coordinates
int y = displayHeight() - r.y() - r.height();
glXCopySubBuffer(display(), glxbuffer, r.x(), y, r.width(), r.height());
}
} else {
// if a shader is bound or the texture unit is enabled, copy pixels results in a black screen
// therefore unbind the shader and restore after copying the pixels
GLint shader = 0;
if (ShaderManager::instance()->isShaderBound()) {
glGetIntegerv(GL_CURRENT_PROGRAM, &shader);
glUseProgram(0);
}
bool reenableTexUnit = false;
if (glIsEnabled(GL_TEXTURE_2D)) {
glDisable(GL_TEXTURE_2D);
reenableTexUnit = true;
}
// no idea why glScissor() is used, but Compiz has it and it doesn't seem to hurt
glEnable(GL_SCISSOR_TEST);
glDrawBuffer(GL_FRONT);
int xpos = 0;
int ypos = 0;
foreach (const QRect & r, lastDamage().rects()) {
// convert to OpenGL coordinates
int y = displayHeight() - r.y() - r.height();
// Move raster position relatively using glBitmap() rather
// than using glRasterPos2f() - the latter causes drawing
// artefacts at the bottom screen edge with some gfx cards
// glRasterPos2f( r.x(), r.y() + r.height());
glBitmap(0, 0, 0, 0, r.x() - xpos, y - ypos, NULL);
xpos = r.x();
ypos = y;
glScissor(r.x(), y, r.width(), r.height());
glCopyPixels(r.x(), y, r.width(), r.height(), GL_COLOR);
}
glBitmap(0, 0, 0, 0, -xpos, -ypos, NULL); // move position back to 0,0
glDrawBuffer(GL_BACK);
glDisable(GL_SCISSOR_TEST);
if (reenableTexUnit) {
glEnable(GL_TEXTURE_2D);
}
// rebind previously bound shader
if (ShaderManager::instance()->isShaderBound()) {
glUseProgram(shader);
}
}
} else {
if (fullRepaint) {
if (haveSwapInterval) {
setSwapInterval(options->isGlVSync() ? 1 : 0);
glXSwapBuffers(display(), glxbuffer);
setSwapInterval(0);
startRenderTimer(); // this is important so we don't assume to be loosing frames in the compositor timing calculation
startRenderTimer();
} else {
waitSync();
waitSync(); // calls startRenderTimer();
glXSwapBuffers(display(), glxbuffer);
}
} else if (glXCopySubBuffer) {
waitSync();
foreach (const QRect & r, lastDamage().rects()) {
// convert to OpenGL coordinates
int y = displayHeight() - r.y() - r.height();
glXCopySubBuffer(display(), glxbuffer, r.x(), y, r.width(), r.height());
}
} else { // Copy Pixels
// if a shader is bound or the texture unit is enabled, copy pixels results in a black screen
// therefore unbind the shader and restore after copying the pixels
GLint shader = 0;
if (ShaderManager::instance()->isShaderBound()) {
glGetIntegerv(GL_CURRENT_PROGRAM, &shader);
glUseProgram(0);
}
bool reenableTexUnit = false;
if (glIsEnabled(GL_TEXTURE_2D)) {
glDisable(GL_TEXTURE_2D);
reenableTexUnit = true;
}
// no idea why glScissor() is used, but Compiz has it and it doesn't seem to hurt
glEnable(GL_SCISSOR_TEST);
glDrawBuffer(GL_FRONT);
waitSync();
int xpos = 0;
int ypos = 0;
foreach (const QRect & r, lastDamage().rects()) {
// convert to OpenGL coordinates
int y = displayHeight() - r.y() - r.height();
// Move raster position relatively using glBitmap() rather
// than using glRasterPos2f() - the latter causes drawing
// artefacts at the bottom screen edge with some gfx cards
// glRasterPos2f( r.x(), r.y() + r.height());
glBitmap(0, 0, 0, 0, r.x() - xpos, y - ypos, NULL);
xpos = r.x();
ypos = y;
glScissor(r.x(), y, r.width(), r.height());
glCopyPixels(r.x(), y, r.width(), r.height(), GL_COLOR);
}
glBitmap(0, 0, 0, 0, -xpos, -ypos, NULL); // move position back to 0,0
glDrawBuffer(GL_BACK);
glDisable(GL_SCISSOR_TEST);
if (reenableTexUnit) {
glEnable(GL_TEXTURE_2D);
}
// rebind previously bound shader
if (ShaderManager::instance()->isShaderBound()) {
glUseProgram(shader);
}
}
glXWaitGL();
} else {
glXWaitGL();
if (lastMask() & Scene::PAINT_SCREEN_REGION)
if (!fullRepaint)
foreach (const QRect & r, lastDamage().rects())
XCopyArea(display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y());
else
XCopyArea(display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0);
}
setLastDamage(QRegion());
XFlush(display());
}

View File

@ -161,6 +161,7 @@ Options::Options(QObject *parent)
, m_glStrictBinding(Options::defaultGlStrictBinding())
, m_glStrictBindingFollowsDriver(Options::defaultGlStrictBindingFollowsDriver())
, m_glLegacy(Options::defaultGlLegacy())
, m_glPreferBufferSwap(Options::defaultGlPreferBufferSwap())
, OpTitlebarDblClick(Options::defaultOperationTitlebarDblClick())
, CmdActiveTitlebar1(Options::defaultCommandActiveTitlebar1())
, CmdActiveTitlebar2(Options::defaultCommandActiveTitlebar2())
@ -768,6 +769,24 @@ void Options::setGlLegacy(bool glLegacy)
emit glLegacyChanged();
}
void Options::setGlPreferBufferSwap(char glPreferBufferSwap)
{
if (glPreferBufferSwap == 'a') {
// buffer cpying is very fast with the nvidia blob
// but due to restrictions in DRI2 *incredibly* slow for all MESA drivers
// see http://www.x.org/releases/X11R7.7/doc/dri2proto/dri2proto.txt, item 2.5
if (GLPlatform::instance()->driver() == Driver_NVidia)
glPreferBufferSwap = CopyFrontBuffer;
else
glPreferBufferSwap = ExtendDamage;
}
if (m_glPreferBufferSwap == (GlSwapStrategy)glPreferBufferSwap) {
return;
}
m_glPreferBufferSwap = (GlSwapStrategy)glPreferBufferSwap;
emit glPreferBufferSwapChanged();
}
void Options::reparseConfiguration()
{
KGlobal::config()->reparseConfiguration();
@ -957,6 +976,16 @@ void Options::reloadCompositingSettings(bool force)
}
setGlLegacy(config.readEntry("GLLegacy", Options::defaultGlLegacy()));
char c = 0;
if (isGlVSync()) { // buffer swap enforcement makes little sense without
const QString s = config.readEntry("GLPreferBufferSwap", QString(Options::defaultGlPreferBufferSwap()));
if (!s.isEmpty())
c = s.at(0).toAscii();
if (c != 'a' && c != 'c' && c != 'p' && c != 'e')
c = 0;
}
setGlPreferBufferSwap(c);
setColorCorrected(config.readEntry("GLColorCorrection", Options::defaultColorCorrected()));
m_xrenderSmoothScale = config.readEntry("XRenderSmoothScale", false);

View File

@ -42,6 +42,7 @@ class Options : public QObject, public KDecorationOptions
{
Q_OBJECT
Q_ENUMS(FocusPolicy)
Q_ENUMS(GlSwapStrategy)
Q_ENUMS(MouseCommand)
Q_ENUMS(MouseWheelCommand)
@ -187,6 +188,7 @@ class Options : public QObject, public KDecorationOptions
* Whether legacy OpenGL should be used or OpenGL (ES) 2
**/
Q_PROPERTY(bool glLegacy READ isGlLegacy WRITE setGlLegacy NOTIFY glLegacyChanged)
Q_PROPERTY(char glPreferBufferSwap READ glPreferBufferSwap WRITE setGlPreferBufferSwap NOTIFY glPreferBufferSwapChanged)
public:
explicit Options(QObject *parent = NULL);
@ -549,6 +551,11 @@ public:
return m_glLegacy;
}
enum GlSwapStrategy { NoSwapEncourage = 0, CopyFrontBuffer = 'c', PaintFullScreen = 'p', ExtendDamage = 'e', AutoSwapStrategy = 'a' };
GlSwapStrategy glPreferBufferSwap() const {
return m_glPreferBufferSwap;
}
// setters
void setFocusPolicy(FocusPolicy focusPolicy);
void setNextFocusPrefersMouse(bool nextFocusPrefersMouse);
@ -610,6 +617,7 @@ public:
void setGlStrictBinding(bool glStrictBinding);
void setGlStrictBindingFollowsDriver(bool glStrictBindingFollowsDriver);
void setGlLegacy(bool glLegacy);
void setGlPreferBufferSwap(char glPreferBufferSwap);
// default values
static WindowOperation defaultOperationTitlebarDblClick() {
@ -717,6 +725,9 @@ public:
static bool defaultGlLegacy() {
return false;
}
static GlSwapStrategy defaultGlPreferBufferSwap() {
return AutoSwapStrategy;
}
static int defaultAnimationSpeed() {
return 3;
}
@ -797,6 +808,7 @@ Q_SIGNALS:
void glStrictBindingChanged();
void glStrictBindingFollowsDriverChanged();
void glLegacyChanged();
void glPreferBufferSwapChanged();
public Q_SLOTS:
void setColorCorrected(bool colorCorrected = false);
@ -847,6 +859,7 @@ private:
bool m_glStrictBinding;
bool m_glStrictBindingFollowsDriver;
bool m_glLegacy;
GlSwapStrategy m_glPreferBufferSwap;
WindowOperation OpTitlebarDblClick;

View File

@ -103,8 +103,9 @@ Scene::~Scene()
// returns mask and possibly modified region
void Scene::paintScreen(int* mask, QRegion* region)
{
*mask = (*region == QRegion(0, 0, displayWidth(), displayHeight()))
? 0 : PAINT_SCREEN_REGION;
const QRegion displayRegion(0, 0, displayWidth(), displayHeight());
*mask = (*region == displayRegion) ? 0 : PAINT_SCREEN_REGION;
updateTimeDiff();
// preparation step
static_cast<EffectsHandlerImpl*>(effects)->startPaint();
@ -124,10 +125,10 @@ void Scene::paintScreen(int* mask, QRegion* region)
*region = infiniteRegion();
} else if (*mask & PAINT_SCREEN_REGION) {
// make sure not to go outside visible screen
*region &= QRegion(0, 0, displayWidth(), displayHeight());
*region &= displayRegion;
} else {
// whole screen, not transformed, force region to be full
*region = QRegion(0, 0, displayWidth(), displayHeight());
*region = displayRegion;
}
painted_region = *region;
if (*mask & PAINT_SCREEN_BACKGROUND_FIRST) {
@ -141,7 +142,7 @@ void Scene::paintScreen(int* mask, QRegion* region)
effects->postPaintScreen();
*region |= painted_region;
// make sure not to go outside of the screen area
*region &= QRegion(0, 0, displayWidth(), displayHeight());
*region &= displayRegion;
// make sure all clipping is restored
Q_ASSERT(!PaintClipper::clip());
}
@ -236,6 +237,7 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region)
QList< QPair< Window*, Phase2Data > > phase2data;
QRegion dirtyArea = region;
bool opaqueFullscreen(false);
for (int i = 0; // do prePaintWindow bottom to top
i < stacking_order.count();
++i) {
@ -254,10 +256,12 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region)
topw->resetRepaints();
// Clip out the decoration for opaque windows; the decoration is drawn in the second pass
opaqueFullscreen = false; // TODO: do we care about unmanged windows here (maybe input windows?)
if (w->isOpaque()) {
Client *c = NULL;
if (topw->isClient()) {
c = static_cast<Client*>(topw);
opaqueFullscreen = c->isFullScreen();
}
// the window is fully opaque
if (c && c->decorationHasAlpha()) {
@ -297,13 +301,24 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region)
w->suspendUnredirect(data.mask & PAINT_WINDOW_TRANSLUCENT);
}
// This is the occlusion culling pass
const QRegion displayRegion(0, 0, displayWidth(), displayHeight());
bool fullRepaint(dirtyArea == displayRegion); // spare some expensive region operations
if (!fullRepaint) {
extendPaintRegion(dirtyArea, opaqueFullscreen);
fullRepaint = (dirtyArea == displayRegion);
}
QRegion allclips, upperTranslucentDamage;
// This is the occlusion culling pass
for (int i = phase2data.count() - 1; i >= 0; --i) {
QPair< Window*, Phase2Data > *entry = &phase2data[i];
Phase2Data *data = &entry->second;
data->region |= upperTranslucentDamage;
if (fullRepaint)
data->region = displayRegion;
else
data->region |= upperTranslucentDamage;
// subtract the parts which will possibly been drawn as part of
// a higher opaque window
@ -315,8 +330,9 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region)
// clip away the opaque regions for all windows below this one
allclips |= data->clip;
// extend the translucent damage for windows below this by remaining (translucent) regions
upperTranslucentDamage |= data->region - data->clip;
} else {
if (!fullRepaint)
upperTranslucentDamage |= data->region - data->clip;
} else if (!fullRepaint) {
upperTranslucentDamage |= data->region;
}
}
@ -338,7 +354,10 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region)
paintWindow(data->window, data->mask, data->region, data->quads);
}
painted_region |= paintedArea;
if (fullRepaint)
painted_region = displayRegion;
else
painted_region |= paintedArea;
}
void Scene::paintWindow(Window* w, int mask, QRegion region, WindowQuadList quads)
@ -452,6 +471,12 @@ void Scene::finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, Windo
w->sceneWindow()->performPaint(mask, region, data);
}
void Scene::extendPaintRegion(QRegion &region, bool opaqueFullscreen)
{
Q_UNUSED(region);
Q_UNUSED(opaqueFullscreen);
}
bool Scene::waitSyncAvailable() const
{
return false;

View File

@ -125,6 +125,9 @@ protected:
virtual void paintWindow(Window* w, int mask, QRegion region, WindowQuadList quads);
// called after all effects had their drawWindow() called
virtual void finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data);
// let the scene decide whether it's better to paint more of the screen, eg. in order to allow a buffer swap
// the default is NOOP
virtual void extendPaintRegion(QRegion &region, bool opaqueFullscreen);
// compute time since the last repaint
void updateTimeDiff();
// saved data for 2nd pass of optimized screen painting

View File

@ -98,7 +98,8 @@ void OpenGLBackend::setFailed(const QString &reason)
void OpenGLBackend::idle()
{
present();
if (hasPendingFlush())
present();
}
/************************************************
@ -269,7 +270,52 @@ int SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
#ifdef CHECK_GL_ERROR
checkGLError("Paint1");
#endif
const QRegion displayRegion(0, 0, displayWidth(), displayHeight());
paintScreen(&mask, &damage); // call generic implementation
#ifndef KWIN_HAVE_OPENGLES
// copy dirty parts from front to backbuffer
if (options->glPreferBufferSwap() == Options::CopyFrontBuffer && damage != displayRegion) {
GLint shader = 0;
if (ShaderManager::instance()->isShaderBound()) {
glGetIntegerv(GL_CURRENT_PROGRAM, &shader);
glUseProgram(0);
}
bool reenableTexUnit = false;
if (glIsEnabled(GL_TEXTURE_2D)) {
glDisable(GL_TEXTURE_2D);
reenableTexUnit = true;
}
// no idea why glScissor() is used, but Compiz has it and it doesn't seem to hurt
glEnable(GL_SCISSOR_TEST);
glReadBuffer(GL_FRONT);
int xpos = 0;
int ypos = 0;
const QRegion dirty = displayRegion - damage;
foreach (const QRect &r, dirty.rects()) {
// convert to OpenGL coordinates
int y = displayHeight() - r.y() - r.height();
glBitmap(0, 0, 0, 0, r.x() - xpos, y - ypos, NULL); // not glRasterPos2f, see glxbackend.cpp
xpos = r.x();
ypos = y;
glScissor(r.x(), y, r.width(), r.height());
glCopyPixels(r.x(), y, r.width(), r.height(), GL_COLOR);
}
glBitmap(0, 0, 0, 0, -xpos, -ypos, NULL); // move position back to 0,0
glReadBuffer(GL_BACK);
glDisable(GL_SCISSOR_TEST);
if (reenableTexUnit) {
glEnable(GL_TEXTURE_2D);
}
// rebind previously bound shader
if (ShaderManager::instance()->isShaderBound()) {
glUseProgram(shader);
}
damage = displayRegion;
}
#endif
#ifdef CHECK_GL_ERROR
checkGLError("Paint2");
#endif
@ -328,6 +374,35 @@ void SceneOpenGL::paintBackground(QRegion region)
doPaintBackground(verts);
}
void SceneOpenGL::extendPaintRegion(QRegion &region, bool opaqueFullscreen)
{
#ifndef KWIN_HAVE_OPENGLES
if (options->glPreferBufferSwap() == Options::ExtendDamage) { // only Extend "large" repaints
const QRegion displayRegion(0, 0, displayWidth(), displayHeight());
uint damagedPixels = 0;
const uint fullRepaintLimit = (opaqueFullscreen?0.49f:0.748f)*displayWidth()*displayHeight();
// 16:9 is 75% of 4:3 and 2.55:1 is 49.01% of 5:4
// (5:4 is the most square format and 2.55:1 is Cinemascope55 - the widest ever shot
// movie aspect - two times ;-) It's a Fox format, though, so maybe we want to restrict
// to 2.20:1 - Panavision - which has actually been used for interesting movies ...)
// would be 57% of 5/4
foreach (const QRect &r, region.rects()) {
// damagedPixels += r.width() * r.height(); // combined window damage test
damagedPixels = r.width() * r.height(); // experimental single window damage testing
if (damagedPixels > fullRepaintLimit) {
region = displayRegion;
return;
}
}
} else if (options->glPreferBufferSwap() == Options::PaintFullScreen) { // forced full rePaint
region = QRegion(0, 0, displayWidth(), displayHeight());
}
#else
Q_UNUSED(region);
Q_UNUSED(opaqueFullscreen);
#endif
}
void SceneOpenGL::windowAdded(Toplevel* c)
{
assert(!windows.contains(c));

View File

@ -68,6 +68,7 @@ public:
protected:
SceneOpenGL(Workspace* ws, OpenGLBackend *backend);
virtual void paintBackground(QRegion region);
virtual void extendPaintRegion(QRegion &region, bool opaqueFullscreen);
QMatrix4x4 transformation(int mask, const ScreenPaintData &data) const;
virtual void doPaintBackground(const QVector<float> &vertices) = 0;