From 5fd96620ed8a3974f7383e0cb4214819692c7b50 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Fri, 15 Mar 2024 00:58:46 +0100 Subject: [PATCH] backends: use explicit sync for reusing graphics buffers with EGL Otherwise there can be glitches on NVidia, and this also makes some future code changes around multi gpu copies and shadow buffers easier --- src/backends/drm/drm_egl_layer_surface.cpp | 4 ++-- src/backends/drm/drm_virtual_egl_layer.cpp | 5 ++++- src/backends/wayland/wayland_egl_backend.cpp | 13 ++++++------- src/backends/wayland/wayland_egl_backend.h | 1 + .../x11/windowed/x11_windowed_egl_backend.cpp | 4 +++- src/opengl/eglswapchain.cpp | 17 ++++++++++++----- src/opengl/eglswapchain.h | 6 +++++- 7 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/backends/drm/drm_egl_layer_surface.cpp b/src/backends/drm/drm_egl_layer_surface.cpp index dc06c1c603..9c10980c2e 100644 --- a/src/backends/drm/drm_egl_layer_surface.cpp +++ b/src/backends/drm/drm_egl_layer_surface.cpp @@ -177,7 +177,6 @@ bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion) GLFramebuffer::popFramebuffer(); } m_surface->damageJournal.add(damagedRegion); - m_surface->gbmSwapchain->release(m_surface->currentSlot); m_surface->timeQuery->end(); glFlush(); EGLNativeFence sourceFence(m_eglBackend->eglDisplayObject()); @@ -186,6 +185,7 @@ bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion) // and NVidia doesn't support implicit sync glFinish(); } + m_surface->gbmSwapchain->release(m_surface->currentSlot, sourceFence.fileDescriptor().duplicate()); const auto buffer = importBuffer(m_surface.get(), m_surface->currentSlot.get(), sourceFence.fileDescriptor()); m_surface->renderEnd = std::chrono::steady_clock::now(); if (buffer) { @@ -579,7 +579,7 @@ std::shared_ptr EglGbmLayerSurface::importWithEgl(Surface *surfa if (!endFence.isValid()) { glFinish(); } - surface->importGbmSwapchain->release(slot); + surface->importGbmSwapchain->release(slot, endFence.fileDescriptor().duplicate()); surface->importTimeQuery->end(); // restore the old context diff --git a/src/backends/drm/drm_virtual_egl_layer.cpp b/src/backends/drm/drm_virtual_egl_layer.cpp index 67e26342af..c45eb72133 100644 --- a/src/backends/drm/drm_virtual_egl_layer.cpp +++ b/src/backends/drm/drm_virtual_egl_layer.cpp @@ -11,6 +11,7 @@ #include "drm_gpu.h" #include "drm_logging.h" #include "drm_virtual_output.h" +#include "opengl/eglnativefence.h" #include "opengl/eglswapchain.h" #include "opengl/glrendertimequery.h" #include "scene/surfaceitem_wayland.h" @@ -89,7 +90,9 @@ bool VirtualEglGbmLayer::endFrame(const QRegion &renderedRegion, const QRegion & glFlush(); m_currentDamage = damagedRegion; m_damageJournal.add(damagedRegion); - m_gbmSwapchain->release(m_currentSlot); + + EGLNativeFence releaseFence{m_eglBackend->eglDisplayObject()}; + m_gbmSwapchain->release(m_currentSlot, releaseFence.fileDescriptor().duplicate()); return true; } diff --git a/src/backends/wayland/wayland_egl_backend.cpp b/src/backends/wayland/wayland_egl_backend.cpp index b4d7e16996..2e7de81827 100644 --- a/src/backends/wayland/wayland_egl_backend.cpp +++ b/src/backends/wayland/wayland_egl_backend.cpp @@ -106,6 +106,10 @@ bool WaylandEglPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegi m_query->end(); // Flush rendering commands to the dmabuf. glFlush(); + EGLNativeFence releaseFence{m_backend->eglDisplayObject()}; + + m_presentationBuffer = m_backend->backend()->importBuffer(m_buffer->buffer()); + m_swapchain->release(m_buffer, releaseFence.fileDescriptor().duplicate()); m_damageJournal.add(damagedRegion); return true; @@ -127,10 +131,6 @@ bool WaylandEglPrimaryLayer::scanout(SurfaceItem *surfaceItem) void WaylandEglPrimaryLayer::present() { - if (!m_presentationBuffer) { - m_presentationBuffer = m_backend->backend()->importBuffer(m_buffer->buffer()); - Q_ASSERT(m_presentationBuffer); - } KWayland::Client::Surface *surface = m_waylandOutput->surface(); surface->attachBuffer(m_presentationBuffer); @@ -139,8 +139,6 @@ void WaylandEglPrimaryLayer::present() surface->commit(); Q_EMIT m_waylandOutput->outputChange(m_damageJournal.lastDamage()); m_presentationBuffer = nullptr; - - m_swapchain->release(m_buffer); } std::chrono::nanoseconds WaylandEglPrimaryLayer::queryRenderTime() const @@ -217,7 +215,8 @@ bool WaylandEglCursorLayer::endFrame(const QRegion &renderedRegion, const QRegio m_output->cursor()->update(buffer, scale(), hotspot().toPoint()); - m_swapchain->release(m_buffer); + EGLNativeFence releaseFence{m_backend->eglDisplayObject()}; + m_swapchain->release(m_buffer, releaseFence.fileDescriptor().duplicate()); return true; } diff --git a/src/backends/wayland/wayland_egl_backend.h b/src/backends/wayland/wayland_egl_backend.h index 3a7bdac382..4cee304253 100644 --- a/src/backends/wayland/wayland_egl_backend.h +++ b/src/backends/wayland/wayland_egl_backend.h @@ -10,6 +10,7 @@ #pragma once #include "core/outputlayer.h" +#include "opengl/eglnativefence.h" #include "platformsupport/scenes/opengl/abstract_egl_backend.h" #include "utils/damagejournal.h" diff --git a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp index 8b4f33669d..37112d97cd 100644 --- a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp @@ -8,6 +8,7 @@ */ #include "x11_windowed_egl_backend.h" #include "core/gbmgraphicsbufferallocator.h" +#include "opengl/eglnativefence.h" #include "opengl/eglswapchain.h" #include "opengl/glrendertimequery.h" #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h" @@ -104,7 +105,8 @@ void X11WindowedEglPrimaryLayer::present() Q_EMIT m_output->outputChange(infiniteRegion()); - m_swapchain->release(m_buffer); + EGLNativeFence releaseFence{m_backend->eglDisplayObject()}; + m_swapchain->release(m_buffer, releaseFence.fileDescriptor().duplicate()); } std::shared_ptr X11WindowedEglPrimaryLayer::texture() const diff --git a/src/opengl/eglswapchain.cpp b/src/opengl/eglswapchain.cpp index 1ee35f9fef..984f944dd1 100644 --- a/src/opengl/eglswapchain.cpp +++ b/src/opengl/eglswapchain.cpp @@ -54,6 +54,11 @@ int EglSwapchainSlot::age() const return m_age; } +bool EglSwapchainSlot::isBusy() const +{ + return m_buffer->isReferenced() || (m_releaseFd.isValid() && !m_releaseFd.isReadable()); +} + std::shared_ptr EglSwapchainSlot::create(EglContext *context, GraphicsBuffer *buffer) { auto texture = context->importDmaBufAsTexture(*buffer->dmabufAttributes()); @@ -100,10 +105,11 @@ uint64_t EglSwapchain::modifier() const std::shared_ptr EglSwapchain::acquire() { - for (const auto &slot : std::as_const(m_slots)) { - if (!slot->buffer()->isReferenced()) { - return slot; - } + const auto it = std::ranges::find_if(std::as_const(m_slots), [](const auto &slot) { + return !slot->isBusy(); + }); + if (it != m_slots.cend()) { + return *it; } GraphicsBuffer *buffer = m_allocator->allocate(GraphicsBufferOptions{ @@ -124,8 +130,9 @@ std::shared_ptr EglSwapchain::acquire() return slot; } -void EglSwapchain::release(std::shared_ptr slot) +void EglSwapchain::release(std::shared_ptr slot, FileDescriptor &&releaseFence) { + slot->m_releaseFd = std::move(releaseFence); for (qsizetype i = 0; i < m_slots.count(); ++i) { if (m_slots[i] == slot) { m_slots[i]->m_age = 1; diff --git a/src/opengl/eglswapchain.h b/src/opengl/eglswapchain.h index d326c26ccb..c591013844 100644 --- a/src/opengl/eglswapchain.h +++ b/src/opengl/eglswapchain.h @@ -9,6 +9,7 @@ #pragma once #include "kwin_export.h" +#include "utils/filedescriptor.h" #include #include @@ -40,10 +41,13 @@ public: static std::shared_ptr create(EglContext *context, GraphicsBuffer *buffer); private: + bool isBusy() const; + GraphicsBuffer *m_buffer; std::unique_ptr m_framebuffer; std::shared_ptr m_texture; int m_age = 0; + FileDescriptor m_releaseFd; friend class EglSwapchain; }; @@ -58,7 +62,7 @@ public: uint64_t modifier() const; std::shared_ptr acquire(); - void release(std::shared_ptr slot); + void release(std::shared_ptr slot, FileDescriptor &&releaseFence); static std::shared_ptr create(GraphicsBufferAllocator *allocator, EglContext *context, const QSize &size, uint32_t format, const QList &modifiers);