diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/platformsupport/scenes/opengl/abstract_egl_backend.cpp --- a/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -152,6 +152,9 @@ if (useBufferAge != "0") setSupportsBufferAge(true); } + + setSupportsPartialUpdate(hasExtension(QByteArrayLiteral("EGL_KHR_partial_update"))); + setSupportsSwapBuffersWithDamage(hasExtension(QByteArrayLiteral("EGL_EXT_swap_buffers_with_damage"))); } void AbstractEglBackend::initWayland() diff --git a/platformsupport/scenes/opengl/backend.h b/platformsupport/scenes/opengl/backend.h --- a/platformsupport/scenes/opengl/backend.h +++ b/platformsupport/scenes/opengl/backend.h @@ -159,6 +159,13 @@ return m_haveBufferAge; } + bool supportsPartialUpdate() const { + return m_havePartialUpdate; + } + bool supportsSwapBuffersWithDamage() const { + return m_haveSwapBuffersWithDamage; + } + /** * @returns whether the context is surfaceless */ @@ -248,6 +255,14 @@ m_haveBufferAge = value; } + void setSupportsPartialUpdate(bool value) { + m_havePartialUpdate = value; + } + + void setSupportsSwapBuffersWithDamage(bool value) { + m_haveSwapBuffersWithDamage = value; + } + /** * @return const QRegion& Damage of previously rendered frame */ @@ -299,6 +314,11 @@ * @brief Whether the backend supports GLX_EXT_buffer_age / EGL_EXT_buffer_age. */ bool m_haveBufferAge; + /** + * @brief Whether the backend supports EGL_KHR_partial_update + */ + bool m_havePartialUpdate; + bool m_haveSwapBuffersWithDamage = false; /** * @brief Whether the initialization failed, of course default to @c false. */ diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -438,9 +438,29 @@ // Not in use. This backend does per-screen rendering. } +static QVector regionToRects(const QRegion &lastRegion, int height) +{ + QVector rects; + rects.reserve(lastRegion.rectCount() * 4); + for (const auto &rect : lastRegion) { + rects << rect.left(); + rects << height - rect.bottom() - 1; + rects << rect.width(); + rects << rect.height(); + } + return rects; +} + void EglGbmBackend::presentOnOutput(Output &output) { - eglSwapBuffers(eglDisplay(), output.eglSurface); + if (supportsSwapBuffersWithDamage()) { + QVector rects = regionToRects(output.damageHistory.constFirst(), + output.output->geometry().height()); + eglSwapBuffersWithDamageEXT(eglDisplay(), output.eglSurface, + rects.data(), rects.count()/4); + } else { + eglSwapBuffers(eglDisplay(), output.eglSurface); + } output.buffer = m_backend->createBuffer(output.gbmSurface); if(m_remoteaccessManager && gbm_surface_has_free_buffers(output.gbmSurface->surface())) { @@ -493,15 +513,32 @@ if (supportsBufferAge()) { QRegion region; - // Note: An age of zero means the buffer contents are undefined if (output.bufferAge > 0 && output.bufferAge <= output.damageHistory.count()) { for (int i = 0; i < output.bufferAge - 1; i++) region |= output.damageHistory[i]; } else { region = output.output->geometry(); } + if (output.bufferAge > 0 + && !output.damageHistory.isEmpty() + && supportsPartialUpdate()) + { +// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_partial_update.txt + + QVector rects = regionToRects(output.damageHistory.constFirst(), + output.output->geometry().height()); + + bool correct = rects.isEmpty() + || eglSetDamageRegionKHR(eglDisplay(), output.eglSurface, + rects.data(), rects.count()/4); + if (!correct) { + qCritical() << "failed eglSetDamageRegionKHR" << eglGetError(); + } + } + + return region; } return QRegion();