Commit 0ceff5fd authored by Vlad Zahorodnii's avatar Vlad Zahorodnii

Swap buffers after finishing a compositing cycle

The compositing timing algorithm assumes that glXSwapBuffers() and
eglSwapBuffers() block. While this was true long time ago with NVIDIA
drivers, nowadays, it's not the case. The NVIDIA driver queues
several buffers in advance and if the application runs out of them,
it will block. With Mesa driver, swapping buffer was never blocking.

This change makes the render backends swap buffers right after ending
a compositing cycle. This may potentially block, but it shouldn't be
an issue with modern drivers. In case it gets proven, we can move
glXSwapBuffers() and eglSwapBuffers() in a separate thread.

Note that this change breaks the compositing timing algorithm, but
it's already sort of broken with Mesa drivers.
parent ed88cbfb
......@@ -37,14 +37,6 @@ void OpenGLBackend::setFailed(const QString &reason)
m_failed = true;
}
void OpenGLBackend::idle()
{
if (hasPendingFlush()) {
effects->makeOpenGLContextCurrent();
present();
}
}
void OpenGLBackend::addToDamageHistory(const QRegion &region)
{
if (m_damageHistory.count() > 10)
......
......@@ -61,17 +61,6 @@ public:
virtual bool usesOverlayWindow() const = 0;
virtual QRegion beginFrame(int screenId) = 0;
virtual void endFrame(int screenId, const QRegion &damage, const QRegion &damagedRegion) = 0;
/**
* @brief Compositor is going into idle mode, flushes any pending paints.
*/
void idle();
/**
* @return bool Whether the scene needs to flush a frame.
*/
bool hasPendingFlush() const {
return !m_lastDamage.isEmpty();
}
/**
* @brief Returns the OverlayWindow used by the backend.
......@@ -181,10 +170,6 @@ public:
virtual QSharedPointer<GLTexture> textureForOutput(AbstractOutput *output) const;
protected:
/**
* @brief Backend specific flushing of frame to screen.
*/
virtual void present() = 0;
/**
* @brief Sets the backend initialization to failed.
*
......@@ -251,16 +236,6 @@ protected:
m_haveNativeFence = value;
}
/**
* @return const QRegion& Damage of previously rendered frame
*/
const QRegion &lastDamage() const {
return m_lastDamage;
}
void setLastDamage(const QRegion &damage) {
m_lastDamage = damage;
}
/**
* Sets the platform-specific @p extensions.
*
......@@ -304,10 +279,6 @@ private:
* @brief Whether the initialization failed, of course default to @c false.
*/
bool m_failed;
/**
* @brief Damaged region of previously rendered frame.
*/
QRegion m_lastDamage;
/**
* @brief The damage history for the past 10 frames.
*/
......
......@@ -552,12 +552,6 @@ bool EglGbmBackend::initBufferConfigs()
return false;
}
void EglGbmBackend::present()
{
Q_UNREACHABLE();
// Not in use. This backend does per-screen rendering.
}
static QVector<EGLint> regionToRects(const QRegion &region, AbstractWaylandOutput *output)
{
const int height = output->modeSize().height();
......
......@@ -50,7 +50,6 @@ public:
QRegion beginFrameForSecondaryGpu(AbstractOutput *output) override;
protected:
void present() override;
void cleanupSurfaces() override;
void aboutToStartPainting(int screenId, const QRegion &damage) override;
......
......@@ -116,11 +116,6 @@ void EglMultiBackend::screenGeometryChanged(const QSize &size)
Q_UNUSED(size)
}
void EglMultiBackend::present()
{
Q_UNREACHABLE();
}
AbstractEglDrmBackend *EglMultiBackend::findBackend(int screenId, int& internalScreenId)
{
int screens = 0;
......
......@@ -38,9 +38,6 @@ public:
void addBackend(AbstractEglDrmBackend *backend);
protected:
void present() override;
private:
QVector<AbstractEglDrmBackend*> m_backends;
......
......@@ -438,14 +438,6 @@ bool EglStreamBackend::initBufferConfigs()
return true;
}
void EglStreamBackend::present()
{
for (auto &o : m_outputs) {
makeContextCurrent(o);
presentOnOutput(o);
}
}
void EglStreamBackend::presentOnOutput(EglStreamBackend::Output &o)
{
eglSwapBuffers(eglDisplay(), o.eglSurface);
......
......@@ -40,7 +40,6 @@ public:
void removeOutput(DrmOutput *output) override;
protected:
void present() override;
void cleanupSurfaces() override;
private:
......
......@@ -141,16 +141,6 @@ bool EglGbmBackend::initBufferConfigs()
return true;
}
void EglGbmBackend::present()
{
Compositor::self()->aboutToSwapBuffers();
eglSwapBuffers(eglDisplay(), surface());
setLastDamage(QRegion());
Compositor::self()->bufferSwapComplete();
}
void EglGbmBackend::screenGeometryChanged(const QSize &size)
{
Q_UNUSED(size)
......@@ -165,9 +155,6 @@ SceneOpenGLTexturePrivate *EglGbmBackend::createBackendTexture(SceneOpenGLTextur
QRegion EglGbmBackend::beginFrame(int screenId)
{
Q_UNUSED(screenId)
if (!lastDamage().isEmpty()) {
present();
}
if (!GLRenderTarget::isRenderTargetBound()) {
GLRenderTarget::pushRenderTarget(m_fbo);
}
......@@ -208,6 +195,7 @@ static void convertFromGLImage(QImage &img, int w, int h)
void EglGbmBackend::endFrame(int screenId, const QRegion &renderedRegion, const QRegion &damagedRegion)
{
Q_UNUSED(screenId)
Q_UNUSED(renderedRegion)
Q_UNUSED(damagedRegion)
glFlush();
if (m_backend->saveFrames()) {
......@@ -217,7 +205,10 @@ void EglGbmBackend::endFrame(int screenId, const QRegion &renderedRegion, const
img.save(QStringLiteral("%1/%2.png").arg(m_backend->saveFrames()).arg(QString::number(m_frameCounter++)));
}
GLRenderTarget::popRenderTarget();
setLastDamage(renderedRegion);
Compositor::self()->aboutToSwapBuffers();
eglSwapBuffers(eglDisplay(), surface());
Compositor::self()->bufferSwapComplete();
}
bool EglGbmBackend::usesOverlayWindow() const
......
......@@ -31,9 +31,6 @@ public:
bool usesOverlayWindow() const override;
void init() override;
protected:
void present() override;
private:
bool initializeEgl();
bool initBufferConfigs();
......
......@@ -272,14 +272,6 @@ bool EglWaylandBackend::initBufferConfigs()
return true;
}
void EglWaylandBackend::present()
{
for (auto *output: qAsConst(m_outputs)) {
makeContextCurrent(output);
presentOnSurface(output, output->m_waylandOutput->geometry());
}
}
static QVector<EGLint> regionToRects(const QRegion &region, AbstractWaylandOutput *output)
{
const int height = output->modeSize().height();
......
......@@ -91,7 +91,6 @@ private:
void cleanupOutput(EglWaylandOutput *output);
bool makeContextCurrent(EglWaylandOutput *output);
void present() override;
void presentOnSurface(EglWaylandOutput *output, const QRegion &damagedRegion);
WaylandBackend *m_backend;
......
......@@ -310,20 +310,6 @@ bool EglOnXBackend::initBufferConfigs()
return true;
}
void EglOnXBackend::present()
{
if (lastDamage().isEmpty())
return;
presentSurface(surface(), lastDamage(), screens()->geometry());
setLastDamage(QRegion());
if (!supportsBufferAge()) {
eglWaitGL();
xcb_flush(m_connection);
}
}
void EglOnXBackend::presentSurface(EGLSurface surface, const QRegion &damage, const QRect &screenGeometry)
{
if (damage.isEmpty()) {
......@@ -398,8 +384,6 @@ QRegion EglOnXBackend::beginFrame(int screenId)
usleep(1000);
}
present();
if (supportsBufferAge())
repaint = accumulatedDamageHistory(m_bufferAge);
......@@ -413,7 +397,6 @@ void EglOnXBackend::endFrame(int screenId, const QRegion &renderedRegion, const
Q_UNUSED(screenId)
if (damagedRegion.isEmpty()) {
setLastDamage(QRegion());
// If the damaged region of a window is fully occluded, the only
// rendering done, if any, will have been to repair a reused back
......@@ -429,17 +412,7 @@ void EglOnXBackend::endFrame(int screenId, const QRegion &renderedRegion, const
return;
}
setLastDamage(renderedRegion);
if (!blocksForRetrace()) {
// This also sets lastDamage to empty which prevents the frame from
// being posted again when prepareRenderingFrame() is called.
present();
} else {
// Make sure that the GPU begins processing the command stream
// now and not the next time prepareRenderingFrame() is called.
glFlush();
}
presentSurface(surface(), renderedRegion, screens()->geometry());
if (m_overlayWindow && overlayWindow()->window()) // show the window only after the first pass,
overlayWindow()->show(); // since that pass may take long
......
......@@ -41,7 +41,6 @@ public:
}
protected:
void present() override;
void presentSurface(EGLSurface surface, const QRegion &damage, const QRect &screenGeometry);
virtual bool createSurfaces();
EGLSurface createSurface(xcb_window_t window);
......
......@@ -103,11 +103,6 @@ GlxBackend::GlxBackend(Display *display)
, haveSwapInterval(false)
, m_x11Display(display)
{
// Ensures calls to glXSwapBuffers will always block until the next
// retrace when using the proprietary NVIDIA driver. This must be
// set before libGL.so is loaded.
setenv("__GL_MaxFramesAllowed", "1", true);
// Force initialization of GLX integration in the Qt's xcb backend
// to make it call XESetWireToEvent callbacks, which is required
// by Mesa when using DRI2.
......@@ -709,14 +704,15 @@ void GlxBackend::waitSync()
}
}
void GlxBackend::present()
void GlxBackend::present(const QRegion &damage)
{
if (lastDamage().isEmpty())
if (damage.isEmpty()) {
return;
}
const QSize &screenSize = screens()->size();
const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
const bool fullRepaint = supportsBufferAge() || (lastDamage() == displayRegion);
const bool fullRepaint = supportsBufferAge() || (damage == displayRegion);
if (fullRepaint) {
if (m_haveINTELSwapEvent)
......@@ -743,18 +739,17 @@ void GlxBackend::present()
glXQueryDrawable(display(), glxWindow, GLX_BACK_BUFFER_AGE_EXT, (GLuint *) &m_bufferAge);
}
} else if (m_haveMESACopySubBuffer) {
for (const QRect &r : lastDamage()) {
for (const QRect &r : damage) {
// convert to OpenGL coordinates
int y = screenSize.height() - r.y() - r.height();
glXCopySubBufferMESA(display(), glxWindow, r.x(), y, r.width(), r.height());
}
} else { // Copy Pixels (horribly slow on Mesa)
glDrawBuffer(GL_FRONT);
copyPixels(lastDamage());
copyPixels(damage);
glDrawBuffer(GL_BACK);
}
setLastDamage(QRegion());
if (!supportsBufferAge()) {
glXWaitGL();
XFlush(display());
......@@ -796,7 +791,6 @@ QRegion GlxBackend::beginFrame(int screenId)
}
makeCurrent();
present();
if (supportsBufferAge())
repaint = accumulatedDamageHistory(m_bufferAge);
......@@ -811,8 +805,6 @@ void GlxBackend::endFrame(int screenId, const QRegion &renderedRegion, const QRe
Q_UNUSED(screenId)
if (damagedRegion.isEmpty()) {
setLastDamage(QRegion());
// If the damaged region of a window is fully occluded, the only
// rendering done, if any, will have been to repair a reused back
// buffer, making it identical to the front buffer.
......@@ -827,17 +819,7 @@ void GlxBackend::endFrame(int screenId, const QRegion &renderedRegion, const QRe
return;
}
setLastDamage(renderedRegion);
if (!blocksForRetrace()) {
// This also sets lastDamage to empty which prevents the frame from
// being posted again when prepareRenderingFrame() is called.
present();
} else {
// Make sure that the GPU begins processing the command stream
// now and not the next time prepareRenderingFrame() is called.
glFlush();
}
present(renderedRegion);
if (overlayWindow()->window()) // show the window only after the first pass,
overlayWindow()->show(); // since that pass may take long
......
......@@ -69,10 +69,8 @@ public:
bool usesOverlayWindow() const override;
void init() override;
protected:
void present() override;
private:
void present(const QRegion &damage);
bool initBuffer();
bool checkVersion();
void initExtensions();
......
......@@ -48,18 +48,6 @@ bool EglX11Backend::createSurfaces()
return true;
}
void EglX11Backend::present()
{
for (int i = 0; i < screens()->count(); ++i) {
EGLSurface s = m_surfaces.at(i);
makeContextCurrent(s);
setupViewport(i);
presentSurface(s, screens()->geometry(i), screens()->geometry(i));
}
eglWaitGL();
xcb_flush(m_backend->connection());
}
bool EglX11Backend::usesOverlayWindow() const
{
return false;
......
......@@ -28,7 +28,6 @@ public:
void endFrame(int screenId, const QRegion &damage, const QRegion &damagedRegion) override;
protected:
void present() override;
void cleanupSurfaces() override;
bool createSurfaces() override;
......
......@@ -492,12 +492,6 @@ bool SceneOpenGL::blocksForRetrace() const
return m_backend->blocksForRetrace();
}
void SceneOpenGL::idle()
{
m_backend->idle();
Scene::idle();
}
bool SceneOpenGL::initFailed() const
{
return !init_ok;
......
......@@ -34,7 +34,6 @@ public:
class EffectFrame;
~SceneOpenGL() override;
bool initFailed() const override;
bool hasPendingFlush() const override;
void paint(int screenId, const QRegion &damage, const QList<Toplevel *> &windows,
std::chrono::milliseconds presentTime) override;
Scene::EffectFrame *createEffectFrame(EffectFrameImpl *frame) override;
......@@ -55,8 +54,6 @@ public:
void insertWait();
void idle() override;
bool debug() const { return m_debug; }
void initDebugOutput();
......@@ -309,11 +306,6 @@ private:
QScopedPointer<GLTexture> m_texture;
};
inline bool SceneOpenGL::hasPendingFlush() const
{
return m_backend->hasPendingFlush();
}
inline bool SceneOpenGL::usesOverlayWindow() const
{
return m_backend->usesOverlayWindow();
......
......@@ -59,8 +59,6 @@ public:
virtual bool initFailed() const = 0;
virtual CompositingType compositingType() const = 0;
virtual bool hasPendingFlush() const { return false; }
// Repaints the given screen areas, windows provides the stacking order.
// The entry point for the main part of the painting pass.
// returns the time since the last vblank signal - if there's one
......@@ -134,7 +132,7 @@ public:
// types of filtering available
enum ImageFilterType { ImageFilterFast, ImageFilterGood };
// there's nothing to paint (adjust time_diff later)
virtual void idle();
void idle();
virtual bool blocksForRetrace() const;
virtual bool syncsToVBlank() const;
virtual OverlayWindow* overlayWindow() const = 0;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment