From 7de0f1f2ad022241fb5644ab6798b60b5a94d90f Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Tue, 4 May 2021 12:19:31 +0200 Subject: [PATCH] platforms/drm: CPU copy for multi-gpu Not all GPUs can scan out linear buffers, so if import with a dmabuf fails manually copy the data into a non-linear gbm buffer instead. BUG: 432707 --- src/plugins/platforms/drm/CMakeLists.txt | 1 + .../platforms/drm/abstract_egl_drm_backend.h | 15 +- src/plugins/platforms/drm/drm_buffer.h | 4 +- src/plugins/platforms/drm/drm_buffer_gbm.cpp | 28 +- src/plugins/platforms/drm/drm_buffer_gbm.h | 12 + src/plugins/platforms/drm/dumb_swapchain.cpp | 54 ++++ src/plugins/platforms/drm/dumb_swapchain.h | 45 ++++ src/plugins/platforms/drm/egl_gbm_backend.cpp | 250 ++++++++++++------ src/plugins/platforms/drm/egl_gbm_backend.h | 16 +- .../platforms/drm/egl_multi_backend.cpp | 10 +- .../drm/scene_qpainter_drm_backend.cpp | 30 +-- .../drm/scene_qpainter_drm_backend.h | 5 +- 12 files changed, 346 insertions(+), 124 deletions(-) create mode 100644 src/plugins/platforms/drm/dumb_swapchain.cpp create mode 100644 src/plugins/platforms/drm/dumb_swapchain.h diff --git a/src/plugins/platforms/drm/CMakeLists.txt b/src/plugins/platforms/drm/CMakeLists.txt index b10ae4b551..079902cf32 100644 --- a/src/plugins/platforms/drm/CMakeLists.txt +++ b/src/plugins/platforms/drm/CMakeLists.txt @@ -13,6 +13,7 @@ set(DRM_SOURCES drm_gpu.cpp egl_multi_backend.cpp abstract_egl_drm_backend.cpp + dumb_swapchain.cpp ) if (HAVE_GBM) diff --git a/src/plugins/platforms/drm/abstract_egl_drm_backend.h b/src/plugins/platforms/drm/abstract_egl_drm_backend.h index 996314cdbe..39f976b903 100644 --- a/src/plugins/platforms/drm/abstract_egl_drm_backend.h +++ b/src/plugins/platforms/drm/abstract_egl_drm_backend.h @@ -27,13 +27,24 @@ public: virtual int screenCount() const = 0; virtual void addOutput(DrmOutput *output) = 0; virtual void removeOutput(DrmOutput *output) = 0; - virtual int getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32_t *format, uint32_t *stride) { + virtual bool swapBuffers(DrmOutput *output) { + Q_UNUSED(output) + return false; + } + virtual bool exportFramebuffer(DrmOutput *output, void *data, const QSize &size, uint32_t stride) { + Q_UNUSED(output) + Q_UNUSED(data) + Q_UNUSED(size) + Q_UNUSED(stride) + return false; + } + virtual int exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride) { Q_UNUSED(output) Q_UNUSED(format) Q_UNUSED(stride) return 0; } - virtual QRegion beginFrameForSecondaryGpu(AbstractOutput *output) { + virtual QRegion beginFrameForSecondaryGpu(DrmOutput *output) { Q_UNUSED(output) return QRegion(); } diff --git a/src/plugins/platforms/drm/drm_buffer.h b/src/plugins/platforms/drm/drm_buffer.h index 09dd8a08f3..30b03e618c 100644 --- a/src/plugins/platforms/drm/drm_buffer.h +++ b/src/plugins/platforms/drm/drm_buffer.h @@ -60,7 +60,9 @@ public: QImage *image() const { return m_image; } - + void *data() const { + return m_memory; + } quint32 stride() const { return m_stride; } diff --git a/src/plugins/platforms/drm/drm_buffer_gbm.cpp b/src/plugins/platforms/drm/drm_buffer_gbm.cpp index 73fb8a11e6..8cd891010d 100644 --- a/src/plugins/platforms/drm/drm_buffer_gbm.cpp +++ b/src/plugins/platforms/drm/drm_buffer_gbm.cpp @@ -32,11 +32,17 @@ GbmBuffer::GbmBuffer(const QSharedPointer &surface) : m_surface(surface) { m_bo = m_surface->lockFrontBuffer(); + if (m_bo) { + m_stride = gbm_bo_get_stride(m_bo); + } else { + qCWarning(KWIN_DRM) << "failed to lock front buffer!" << strerror(errno); + } } GbmBuffer::GbmBuffer(gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface) : m_bo(buffer) , m_bufferInterface(bufferInterface) + , m_stride(gbm_bo_get_stride(m_bo)) { if (m_bufferInterface) { m_bufferInterface->ref(); @@ -54,14 +60,32 @@ void GbmBuffer::releaseBuffer() if (m_bufferInterface) { clearBufferInterface(); } - if (m_surface && m_bo) { + if (!m_bo) { + return; + } + if (m_mapping) { + gbm_bo_unmap(m_bo, m_mapping); + } + if (m_surface) { m_surface->releaseBuffer(m_bo); - } else if (m_bo) { + } else { gbm_bo_destroy(m_bo); } m_bo = nullptr; } +bool GbmBuffer::map(uint32_t flags) +{ + if (m_data) { + return true; + } + if (!m_bo) { + return false; + } + m_data = gbm_bo_map(m_bo, 0, 0, gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo), flags, &m_stride, &m_mapping); + return m_data; +} + void GbmBuffer::clearBufferInterface() { disconnect(m_bufferInterface, &KWaylandServer::BufferInterface::aboutToBeDestroyed, this, &DrmGbmBuffer::clearBufferInterface); diff --git a/src/plugins/platforms/drm/drm_buffer_gbm.h b/src/plugins/platforms/drm/drm_buffer_gbm.h index bbe227d477..64934f0295 100644 --- a/src/plugins/platforms/drm/drm_buffer_gbm.h +++ b/src/plugins/platforms/drm/drm_buffer_gbm.h @@ -40,11 +40,23 @@ public: void releaseBuffer(); + bool map(uint32_t flags); + void *mappedData() const { + return m_data; + } + uint32_t stride() const { + return m_stride; + } + protected: QSharedPointer m_surface; gbm_bo *m_bo = nullptr; KWaylandServer::BufferInterface *m_bufferInterface = nullptr; + void *m_data = nullptr; + void *m_mapping = nullptr; + uint32_t m_stride = 0; + void clearBufferInterface(); }; diff --git a/src/plugins/platforms/drm/dumb_swapchain.cpp b/src/plugins/platforms/drm/dumb_swapchain.cpp new file mode 100644 index 0000000000..ca2992b654 --- /dev/null +++ b/src/plugins/platforms/drm/dumb_swapchain.cpp @@ -0,0 +1,54 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "dumb_swapchain.h" + +#include "gbm.h" + +#include "drm_gpu.h" +#include "logging.h" + +namespace KWin +{ + +DumbSwapchain::DumbSwapchain(DrmGpu *gpu, const QSize &size) + : m_size(size) +{ + for (int i = 0; i < 3; i++) { + auto buffer = QSharedPointer::create(gpu, size); + if (!buffer->bufferId()) { + break; + } + if (!buffer->map()) { + break; + } + buffer->image()->fill(Qt::black); + m_buffers << buffer; + } + if (m_buffers.count() < 3) { + qCWarning(KWIN_DRM) << "Failed to create gbm buffers for swapchain!"; + m_buffers.clear(); + } +} + +QSharedPointer DumbSwapchain::acquireBuffer() +{ + if (m_buffers.isEmpty()) { + return nullptr; + } + index = (index + 1) % m_buffers.count(); + return m_buffers[index]; +} + +QSharedPointer DumbSwapchain::currentBuffer() const +{ + return m_buffers[index]; +} + +} diff --git a/src/plugins/platforms/drm/dumb_swapchain.h b/src/plugins/platforms/drm/dumb_swapchain.h new file mode 100644 index 0000000000..c374d46e68 --- /dev/null +++ b/src/plugins/platforms/drm/dumb_swapchain.h @@ -0,0 +1,45 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include +#include +#include + +namespace KWin +{ + +class DrmDumbBuffer; +class DrmGpu; + +class DumbSwapchain +{ +public: + DumbSwapchain(DrmGpu *gpu, const QSize &size); + + QSharedPointer acquireBuffer(); + QSharedPointer currentBuffer() const; + + QSize size() const { + return m_size; + } + + bool isEmpty() const { + return m_buffers.isEmpty(); + } + +private: + QSize m_size; + + int index = 0; + QVector> m_buffers; +}; + +} diff --git a/src/plugins/platforms/drm/egl_gbm_backend.cpp b/src/plugins/platforms/drm/egl_gbm_backend.cpp index 81734d01eb..dcac2ba6ff 100644 --- a/src/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/src/plugins/platforms/drm/egl_gbm_backend.cpp @@ -22,6 +22,7 @@ #include "surfaceitem_wayland.h" #include "drm_gpu.h" #include "linux_dmabuf.h" +#include "dumb_swapchain.h" // kwin libs #include #include @@ -76,6 +77,7 @@ void EglGbmBackend::cleanupOutput(Output &output) if (output.eglSurface != EGL_NO_SURFACE) { // gbm buffers have to be released before destroying the egl surface output.output->releaseGbm(); + output.secondaryBuffer = nullptr; eglDestroySurface(eglDisplay(), output.eglSurface); } } @@ -204,6 +206,7 @@ bool EglGbmBackend::resetOutput(Output &output, DrmOutput *drmOutput) if (output.eglSurface != EGL_NO_SURFACE) { // gbm buffers have to be released before destroying the egl surface output.output->releaseGbm(); + output.secondaryBuffer = nullptr; eglDestroySurface(eglDisplay(), output.eglSurface); } output.eglSurface = eglSurface; @@ -261,7 +264,54 @@ void EglGbmBackend::removeOutput(DrmOutput *drmOutput) outputs.erase(it); } -int EglGbmBackend::getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32_t *format, uint32_t *stride) +bool EglGbmBackend::swapBuffers(DrmOutput *drmOutput) +{ + auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(), + [drmOutput] (const Output &output) { + return output.output == drmOutput; + } + ); + if (it == m_secondaryGpuOutputs.end()) { + return false; + } + renderFramebufferToSurface(*it); + auto error = eglSwapBuffers(eglDisplay(), it->eglSurface); + if (error != EGL_TRUE) { + qCDebug(KWIN_DRM) << "an error occurred while swapping buffers" << error; + it->secondaryBuffer = nullptr; + return false; + } + it->secondaryBuffer = QSharedPointer::create(it->gbmSurface); + if (it->secondaryBuffer->getBo()) { + return true; + } else { + it->secondaryBuffer = nullptr; + return false; + } +} + +bool EglGbmBackend::exportFramebuffer(DrmOutput *drmOutput, void *data, const QSize &size, uint32_t stride) +{ + auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(), + [drmOutput] (const Output &output) { + return output.output == drmOutput; + } + ); + if (it == m_secondaryGpuOutputs.end()) { + return false; + } + + if (!it->secondaryBuffer || !it->secondaryBuffer->map(GBM_BO_TRANSFER_READ)) { + return false; + } + if (stride != it->secondaryBuffer->stride()) { + // shouldn't happen if formats are the same + return false; + } + return memcpy(data, it->secondaryBuffer->mappedData(), size.height() * stride); +} + +int EglGbmBackend::exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride) { DrmOutput *drmOutput = static_cast(output); auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(), @@ -272,14 +322,6 @@ int EglGbmBackend::getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32 if (it == m_secondaryGpuOutputs.end()) { return -1; } - renderFramebufferToSurface(*it); - auto error = eglSwapBuffers(eglDisplay(), it->eglSurface); - if (error != EGL_TRUE) { - qCDebug(KWIN_DRM) << "an error occurred while swapping buffers" << error; - it->secondaryBuffer = nullptr; - return -1; - } - it->secondaryBuffer = QSharedPointer::create(it->gbmSurface); int fd = gbm_bo_get_fd(it->secondaryBuffer->getBo()); if (fd == -1) { qCDebug(KWIN_DRM) << "failed to export gbm_bo as dma-buf!"; @@ -290,9 +332,8 @@ int EglGbmBackend::getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32 return fd; } -QRegion EglGbmBackend::beginFrameForSecondaryGpu(AbstractOutput *output) +QRegion EglGbmBackend::beginFrameForSecondaryGpu(DrmOutput *drmOutput) { - DrmOutput *drmOutput = static_cast(output); auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(), [drmOutput] (const Output &output) { return output.output == drmOutput; @@ -304,6 +345,56 @@ QRegion EglGbmBackend::beginFrameForSecondaryGpu(AbstractOutput *output) return prepareRenderingForOutput(*it); } +void EglGbmBackend::importFramebuffer(Output &output) const +{ + if (!renderingBackend()->swapBuffers(output.output)) { + qCWarning(KWIN_DRM) << "swapping buffers failed on output" << output.output; + return; + } + output.buffer = nullptr; + const auto size = output.output->modeSize(); + if (output.importMode == ImportMode::Dmabuf) { + uint32_t stride = 0; + uint32_t format = 0; + int fd = renderingBackend()->exportFramebufferAsDmabuf(output.output, &format, &stride); + if (fd != -1) { + struct gbm_import_fd_data data = {}; + data.fd = fd; + data.width = (uint32_t) size.width(); + data.height = (uint32_t) size.height(); + data.stride = stride; + data.format = format; + gbm_bo *importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR); + close(fd); + if (importedBuffer) { + auto buffer = QSharedPointer::create(m_gpu, importedBuffer, nullptr); + if (buffer->bufferId() > 0) { + output.buffer = buffer; + return; + } + } + } + qCDebug(KWIN_DRM) << "import with dmabuf failed! Switching to CPU import on output" << output.output; + output.importMode = ImportMode::DumbBuffer; + } + // ImportMode::DumbBuffer + if (!output.importSwapchain || output.importSwapchain->size() != size) { + output.importSwapchain = QSharedPointer::create(m_gpu, size); + if (output.importSwapchain->isEmpty()) { + output.importSwapchain = nullptr; + } + } + if (output.importSwapchain) { + auto buffer = output.importSwapchain->acquireBuffer(); + if (renderingBackend()->exportFramebuffer(output.output, buffer->data(), size, buffer->stride())) { + output.buffer = buffer; + return; + } + } + qCWarning(KWIN_DRM) << "all imports failed on output" << output.output; + // TODO turn off output? +} + const float vertices[] = { -1.0f, 1.0f, -1.0f, -1.0f, @@ -377,81 +468,58 @@ void EglGbmBackend::initRenderTarget(Output &output) void EglGbmBackend::renderFramebufferToSurface(Output &output) { - if (!output.render.framebuffer && isPrimary()) { + if (!output.render.framebuffer) { // No additional render target. return; } + makeContextCurrent(output); + const auto size = output.output->modeSize(); - if (isPrimary()) { - // primary GPU - makeContextCurrent(output); + glViewport(0, 0, size.width(), size.height()); - glViewport(0, 0, size.width(), size.height()); + auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture); - auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture); + QMatrix4x4 mvpMatrix; - QMatrix4x4 mvpMatrix; - - const DrmOutput *drmOutput = output.output; - switch (drmOutput->transform()) { - case DrmOutput::Transform::Normal: - case DrmOutput::Transform::Flipped: - break; - case DrmOutput::Transform::Rotated90: - case DrmOutput::Transform::Flipped90: - mvpMatrix.rotate(90, 0, 0, 1); - break; - case DrmOutput::Transform::Rotated180: - case DrmOutput::Transform::Flipped180: - mvpMatrix.rotate(180, 0, 0, 1); - break; - case DrmOutput::Transform::Rotated270: - case DrmOutput::Transform::Flipped270: - mvpMatrix.rotate(270, 0, 0, 1); - break; - } - switch (drmOutput->transform()) { - case DrmOutput::Transform::Flipped: - case DrmOutput::Transform::Flipped90: - case DrmOutput::Transform::Flipped180: - case DrmOutput::Transform::Flipped270: - mvpMatrix.scale(-1, 1); - break; - default: - break; - } - - shader->setUniform(GLShader::ModelViewProjectionMatrix, mvpMatrix); - - initRenderTarget(output); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - GLRenderTarget::setKWinFramebuffer(0); - glBindTexture(GL_TEXTURE_2D, output.render.texture); - output.render.vbo->render(GL_TRIANGLES); - ShaderManager::instance()->popShader(); - glBindTexture(GL_TEXTURE_2D, 0); - } else { - // secondary GPU: render on primary and import framebuffer - uint32_t stride = 0; - uint32_t format = 0; - int fd = renderingBackend()->getDmabufForSecondaryGpuOutput(output.output, &format, &stride); - if (fd != -1) { - struct gbm_import_fd_data data = {}; - data.fd = fd; - data.width = (uint32_t) size.width(); - data.height = (uint32_t) size.height(); - data.stride = stride; - data.format = format; - if (gbm_bo *importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR)) { - output.buffer = QSharedPointer::create(m_gpu, importedBuffer, nullptr); - } else { - qCDebug(KWIN_DRM) << "failed to import dma-buf!" << strerror(errno); - output.buffer = nullptr; - } - close(fd); - } + const DrmOutput *drmOutput = output.output; + switch (drmOutput->transform()) { + case DrmOutput::Transform::Normal: + case DrmOutput::Transform::Flipped: + break; + case DrmOutput::Transform::Rotated90: + case DrmOutput::Transform::Flipped90: + mvpMatrix.rotate(90, 0, 0, 1); + break; + case DrmOutput::Transform::Rotated180: + case DrmOutput::Transform::Flipped180: + mvpMatrix.rotate(180, 0, 0, 1); + break; + case DrmOutput::Transform::Rotated270: + case DrmOutput::Transform::Flipped270: + mvpMatrix.rotate(270, 0, 0, 1); + break; } + switch (drmOutput->transform()) { + case DrmOutput::Transform::Flipped: + case DrmOutput::Transform::Flipped90: + case DrmOutput::Transform::Flipped180: + case DrmOutput::Transform::Flipped270: + mvpMatrix.scale(-1, 1); + break; + default: + break; + } + + shader->setUniform(GLShader::ModelViewProjectionMatrix, mvpMatrix); + + initRenderTarget(output); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + GLRenderTarget::setKWinFramebuffer(0); + glBindTexture(GL_TEXTURE_2D, output.render.texture); + output.render.vbo->render(GL_TRIANGLES); + ShaderManager::instance()->popShader(); + glBindTexture(GL_TEXTURE_2D, 0); } void EglGbmBackend::prepareRenderFramebuffer(const Output &output) const @@ -587,7 +655,7 @@ bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion } output.buffer = QSharedPointer::create(m_gpu, output.gbmSurface); } else if (!output.buffer) { - qCDebug(KWIN_DRM) << "imported gbm_bo does not exist!"; + qCDebug(KWIN_DRM) << "imported buffer does not exist!"; return false; } @@ -668,7 +736,11 @@ void EglGbmBackend::endFrame(int screenId, const QRegion &renderedRegion, Output &output = m_outputs[screenId]; DrmOutput *drmOutput = output.output; - renderFramebufferToSurface(output); + if (isPrimary()) { + renderFramebufferToSurface(output); + } else { + importFramebuffer(output); + } const QRegion dirty = damagedRegion.intersected(output.output->geometry()); if (!presentOnOutput(output, dirty)) { @@ -760,13 +832,20 @@ bool EglGbmBackend::scanout(int screenId, SurfaceItem *surfaceItem) QSharedPointer EglGbmBackend::textureForOutput(AbstractOutput *abstractOutput) const { - const QVector::const_iterator itOutput = std::find_if(m_outputs.begin(), m_outputs.end(), + auto itOutput = std::find_if(m_outputs.begin(), m_outputs.end(), [abstractOutput] (const auto &output) { return output.output == abstractOutput; } ); if (itOutput == m_outputs.end()) { - return {}; + itOutput = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(), + [abstractOutput] (const auto &output) { + return output.output == abstractOutput; + } + ); + if (itOutput == m_secondaryGpuOutputs.end()) { + return {}; + } } DrmOutput *drmOutput = itOutput->output; @@ -776,7 +855,12 @@ QSharedPointer EglGbmBackend::textureForOutput(AbstractOutput *abstra return glTexture; } - EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR, itOutput->buffer->getBo(), nullptr); + auto gbmBuffer = dynamic_cast(itOutput->buffer.data()); + if (!gbmBuffer) { + qCWarning(KWIN_DRM) << "Failed to record frame: Dumb buffer used for presentation!"; + return {}; + } + EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR, gbmBuffer->getBo(), nullptr); if (image == EGL_NO_IMAGE_KHR) { qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << glGetError(); return {}; diff --git a/src/plugins/platforms/drm/egl_gbm_backend.h b/src/plugins/platforms/drm/egl_gbm_backend.h index c853005199..af7ca5f6e1 100644 --- a/src/plugins/platforms/drm/egl_gbm_backend.h +++ b/src/plugins/platforms/drm/egl_gbm_backend.h @@ -31,6 +31,7 @@ class DrmGbmBuffer; class DrmOutput; class GbmSurface; class GbmBuffer; +class DumbSwapchain; /** * @brief OpenGL Backend using Egl on a GBM surface. @@ -58,8 +59,10 @@ public: void addOutput(DrmOutput *output) override; void removeOutput(DrmOutput *output) override; - int getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32_t *format, uint32_t *stride) override; - QRegion beginFrameForSecondaryGpu(AbstractOutput *output) override; + bool swapBuffers(DrmOutput *output) override; + bool exportFramebuffer(DrmOutput *output, void *data, const QSize &size, uint32_t stride) override; + int exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride) override; + QRegion beginFrameForSecondaryGpu(DrmOutput *output) override; bool directScanoutAllowed(int screen) const override; @@ -72,9 +75,13 @@ private: bool initBufferConfigs(); bool initRenderingContext(); + enum class ImportMode { + Dmabuf, + DumbBuffer + }; struct Output { DrmOutput *output = nullptr; - QSharedPointer buffer; + QSharedPointer buffer; QSharedPointer secondaryBuffer; QSharedPointer gbmSurface; EGLSurface eglSurface = EGL_NO_SURFACE; @@ -91,6 +98,8 @@ private: } render; KWaylandServer::SurfaceInterface *surfaceInterface = nullptr; + ImportMode importMode = ImportMode::Dmabuf; + QSharedPointer importSwapchain; }; bool resetOutput(Output &output, DrmOutput *drmOutput); @@ -105,6 +114,7 @@ private: void prepareRenderFramebuffer(const Output &output) const; void renderFramebufferToSurface(Output &output); QRegion prepareRenderingForOutput(Output &output) const; + void importFramebuffer(Output &output) const; bool presentOnOutput(Output &output, const QRegion &damagedRegion); bool directScanoutActive(const Output &output); diff --git a/src/plugins/platforms/drm/egl_multi_backend.cpp b/src/plugins/platforms/drm/egl_multi_backend.cpp index 705dd26f9d..221391394d 100644 --- a/src/plugins/platforms/drm/egl_multi_backend.cpp +++ b/src/plugins/platforms/drm/egl_multi_backend.cpp @@ -97,14 +97,8 @@ PlatformSurfaceTexture *EglMultiBackend::createPlatformSurfaceTextureWayland(Sur QSharedPointer EglMultiBackend::textureForOutput(AbstractOutput *requestedOutput) const { - // this assumes that the wrong backends return {} - for (auto backend : qAsConst(m_backends)) { - auto texture = backend->textureForOutput(requestedOutput); - if (!texture.isNull()) { - return texture; - } - } - return {}; + // this assumes that all outputs are rendered on backend 0 + return m_backends[0]->textureForOutput(requestedOutput); } void EglMultiBackend::screenGeometryChanged(const QSize &size) diff --git a/src/plugins/platforms/drm/scene_qpainter_drm_backend.cpp b/src/plugins/platforms/drm/scene_qpainter_drm_backend.cpp index a4f702fbc3..ee8a9eab70 100644 --- a/src/plugins/platforms/drm/scene_qpainter_drm_backend.cpp +++ b/src/plugins/platforms/drm/scene_qpainter_drm_backend.cpp @@ -44,12 +44,9 @@ DrmQPainterBackend::DrmQPainterBackend(DrmBackend *backend, DrmGpu *gpu) void DrmQPainterBackend::initOutput(DrmOutput *output) { Output o; - auto initBuffer = [&o, output, this] (int index) { - o.buffer[index] = QSharedPointer::create(m_gpu, output->pixelSize()); - if (o.buffer[index]->map()) { - o.buffer[index]->image()->fill(Qt::black); - } - }; + o.swapchain = QSharedPointer::create(m_gpu, output->pixelSize()); + o.output = output; + m_outputs << o; connect(output, &DrmOutput::modeChanged, this, [output, this] { auto it = std::find_if(m_outputs.begin(), m_outputs.end(), @@ -60,26 +57,14 @@ void DrmQPainterBackend::initOutput(DrmOutput *output) if (it == m_outputs.end()) { return; } - auto initBuffer = [it, output, this] (int index) { - it->buffer[index] = QSharedPointer::create(m_gpu, output->pixelSize()); - if (it->buffer[index]->map()) { - it->buffer[index]->image()->fill(Qt::black); - } - }; - initBuffer(0); - initBuffer(1); + it->swapchain = QSharedPointer::create(m_gpu, output->pixelSize()); } ); - initBuffer(0); - initBuffer(1); - o.output = output; - m_outputs << o; } QImage *DrmQPainterBackend::bufferForScreen(int screenId) { - const Output &o = m_outputs.at(screenId); - return o.buffer[o.index]->image(); + return m_outputs[screenId].swapchain->currentBuffer()->image(); } bool DrmQPainterBackend::needsFullRepaint(int screenId) const @@ -90,8 +75,7 @@ bool DrmQPainterBackend::needsFullRepaint(int screenId) const void DrmQPainterBackend::beginFrame(int screenId) { - Output &rendererOutput = m_outputs[screenId]; - rendererOutput.index = (rendererOutput.index + 1) % 2; + m_outputs[screenId].swapchain->acquireBuffer(); } void DrmQPainterBackend::endFrame(int screenId, int mask, const QRegion &damage) @@ -102,7 +86,7 @@ void DrmQPainterBackend::endFrame(int screenId, int mask, const QRegion &damage) const Output &rendererOutput = m_outputs[screenId]; DrmOutput *drmOutput = rendererOutput.output; - if (!drmOutput->present(rendererOutput.buffer[rendererOutput.index])) { + if (!drmOutput->present(rendererOutput.swapchain->currentBuffer())) { RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(drmOutput->renderLoop()); renderLoopPrivate->notifyFrameFailed(); } diff --git a/src/plugins/platforms/drm/scene_qpainter_drm_backend.h b/src/plugins/platforms/drm/scene_qpainter_drm_backend.h index f39c75f9f5..c95c36de70 100644 --- a/src/plugins/platforms/drm/scene_qpainter_drm_backend.h +++ b/src/plugins/platforms/drm/scene_qpainter_drm_backend.h @@ -14,6 +14,8 @@ #include #include +#include "dumb_swapchain.h" + namespace KWin { @@ -36,9 +38,8 @@ public: private: void initOutput(DrmOutput *output); struct Output { - QSharedPointer buffer[2]; DrmOutput *output; - int index = 0; + QSharedPointer swapchain; }; QVector m_outputs; DrmBackend *m_backend;