From aac7f50d8bc4f2bf13434f3e7a9143abd677dff2 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Fri, 18 Feb 2022 01:26:29 +0100 Subject: [PATCH] backends/drm: remove some unnecessary redirection --- src/backends/drm/CMakeLists.txt | 1 + src/backends/drm/drm_abstract_output.cpp | 16 ++ src/backends/drm/drm_abstract_output.h | 1 + src/backends/drm/drm_buffer_gbm.cpp | 57 ++++-- src/backends/drm/drm_buffer_gbm.h | 5 +- src/backends/drm/drm_display_device.h | 13 +- src/backends/drm/drm_gpu.cpp | 4 +- src/backends/drm/drm_layer.cpp | 16 +- src/backends/drm/drm_layer.h | 24 ++- src/backends/drm/drm_lease_output.cpp | 48 +---- src/backends/drm/drm_lease_output.h | 10 +- src/backends/drm/drm_output.cpp | 43 +--- src/backends/drm/drm_output.h | 9 +- src/backends/drm/drm_pipeline.h | 4 +- src/backends/drm/drm_qpainter_layer.cpp | 67 +++++- src/backends/drm/drm_qpainter_layer.h | 37 +++- src/backends/drm/drm_render_backend.h | 8 +- src/backends/drm/drm_virtual_output.cpp | 40 +--- src/backends/drm/drm_virtual_output.h | 16 +- src/backends/drm/egl_gbm_backend.cpp | 58 +----- src/backends/drm/egl_gbm_backend.h | 6 +- src/backends/drm/egl_gbm_layer.cpp | 166 +++++++-------- src/backends/drm/egl_gbm_layer.h | 12 +- .../drm/scene_qpainter_drm_backend.cpp | 12 +- src/backends/drm/scene_qpainter_drm_backend.h | 4 +- src/backends/drm/shadowbuffer.cpp | 3 +- src/backends/drm/shadowbuffer.h | 3 +- src/backends/drm/virtual_egl_gbm_layer.cpp | 190 ++++++++++++++++++ src/backends/drm/virtual_egl_gbm_layer.h | 61 ++++++ 29 files changed, 572 insertions(+), 362 deletions(-) create mode 100644 src/backends/drm/virtual_egl_gbm_layer.cpp create mode 100644 src/backends/drm/virtual_egl_gbm_layer.h diff --git a/src/backends/drm/CMakeLists.txt b/src/backends/drm/CMakeLists.txt index 0a788ebad0..94d8160f61 100644 --- a/src/backends/drm/CMakeLists.txt +++ b/src/backends/drm/CMakeLists.txt @@ -27,6 +27,7 @@ set(DRM_SOURCES gbm_surface.cpp gbm_dmabuf.cpp placeholderinputeventfilter.cpp + virtual_egl_gbm_layer.cpp ) add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES}) diff --git a/src/backends/drm/drm_abstract_output.cpp b/src/backends/drm/drm_abstract_output.cpp index 1c3e8f0f71..5a9048a6fe 100644 --- a/src/backends/drm/drm_abstract_output.cpp +++ b/src/backends/drm/drm_abstract_output.cpp @@ -41,4 +41,20 @@ void DrmAbstractOutput::pageFlipped(std::chrono::nanoseconds timestamp) const RenderLoopPrivate::get(m_renderLoop)->notifyFrameCompleted(timestamp); } +QVector DrmAbstractOutput::regionToRects(const QRegion ®ion) const +{ + const int height = pixelSize().height(); + const QMatrix4x4 matrix = AbstractWaylandOutput::logicalToNativeMatrix(geometry(), scale(), transform()); + QVector rects; + rects.reserve(region.rectCount() * 4); + for (const QRect &_rect : region) { + const QRect rect = matrix.mapRect(_rect); + rects << rect.left(); + rects << height - (rect.y() + rect.height()); + rects << rect.width(); + rects << rect.height(); + } + return rects; +} + } diff --git a/src/backends/drm/drm_abstract_output.h b/src/backends/drm/drm_abstract_output.h index f4b8b4c5c5..e3f1f00348 100644 --- a/src/backends/drm/drm_abstract_output.h +++ b/src/backends/drm/drm_abstract_output.h @@ -26,6 +26,7 @@ public: QRect renderGeometry() const override; void frameFailed() const override; void pageFlipped(std::chrono::nanoseconds timestamp) const override; + QVector regionToRects(const QRegion ®ion) const override; protected: friend class DrmGpu; diff --git a/src/backends/drm/drm_buffer_gbm.cpp b/src/backends/drm/drm_buffer_gbm.cpp index eb6b88f5a0..775b658ee6 100644 --- a/src/backends/drm/drm_buffer_gbm.cpp +++ b/src/backends/drm/drm_buffer_gbm.cpp @@ -21,9 +21,10 @@ #include #include #include +#include // KWaylandServer #include "KWaylandServer/clientbuffer.h" -#include +#include "KWaylandServer/linuxdmabufv1clientbuffer.h" namespace KWin { @@ -31,17 +32,42 @@ namespace KWin GbmBuffer::GbmBuffer(GbmSurface *surface, gbm_bo *bo) : m_surface(surface) , m_bo(bo) -{ - m_stride = gbm_bo_get_stride(m_bo); -} - -GbmBuffer::GbmBuffer(gbm_bo *buffer, KWaylandServer::ClientBuffer *clientBuffer) - : m_bo(buffer) - , m_clientBuffer(clientBuffer) , m_stride(gbm_bo_get_stride(m_bo)) { - if (m_clientBuffer) { - m_clientBuffer->ref(); +} + +GbmBuffer::GbmBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer) + : m_clientBuffer(clientBuffer) +{ + clientBuffer->ref(); + const auto planes = clientBuffer->planes(); + if (planes.first().modifier != DRM_FORMAT_MOD_INVALID || planes.first().offset > 0 || planes.count() > 1) { + gbm_import_fd_modifier_data data = {}; + data.format = clientBuffer->format(); + data.width = (uint32_t) clientBuffer->size().width(); + data.height = (uint32_t) clientBuffer->size().height(); + data.num_fds = planes.count(); + data.modifier = planes.first().modifier; + for (int i = 0; i < planes.count(); i++) { + data.fds[i] = planes[i].fd; + data.offsets[i] = planes[i].offset; + data.strides[i] = planes[i].stride; + } + m_bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT); + } else { + const auto &plane = planes.first(); + gbm_import_fd_data data = {}; + data.fd = plane.fd; + data.width = (uint32_t) clientBuffer->size().width(); + data.height = (uint32_t) clientBuffer->size().height(); + data.stride = plane.stride; + data.format = clientBuffer->format(); + m_bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT); + } + if (m_bo) { + m_stride = gbm_bo_get_stride(m_bo); + } else if (errno != EINVAL) { + qCWarning(KWIN_DRM) << "Importing buffer for direct scanout failed:" << strerror(errno); } } @@ -105,13 +131,15 @@ uint32_t GbmBuffer::stride() const DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, GbmSurface *surface, gbm_bo *bo) - : DrmBuffer(gpu, gbm_bo_get_format(bo), gbm_bo_get_modifier(bo)), GbmBuffer(surface, bo) + : DrmBuffer(gpu, gbm_bo_get_format(bo), gbm_bo_get_modifier(bo)) + , GbmBuffer(surface, bo) { initialize(); } -DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, gbm_bo *buffer, KWaylandServer::ClientBuffer *clientBuffer) - : DrmBuffer(gpu, gbm_bo_get_format(buffer), gbm_bo_get_modifier(buffer)), GbmBuffer(buffer, clientBuffer) +DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer) + : DrmBuffer(gpu, clientBuffer->format(), clientBuffer->planes().constFirst().modifier) + , GbmBuffer(gpu, clientBuffer) { initialize(); } @@ -127,6 +155,9 @@ DrmGbmBuffer::~DrmGbmBuffer() void DrmGbmBuffer::initialize() { + if (!m_bo) { + return; + } m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo)); uint32_t handles[4] = { }; uint32_t strides[4] = { }; diff --git a/src/backends/drm/drm_buffer_gbm.h b/src/backends/drm/drm_buffer_gbm.h index ee7c0149e1..c58302d70c 100644 --- a/src/backends/drm/drm_buffer_gbm.h +++ b/src/backends/drm/drm_buffer_gbm.h @@ -19,6 +19,7 @@ struct gbm_bo; namespace KWaylandServer { class ClientBuffer; +class LinuxDmaBufV1ClientBuffer; } namespace KWin @@ -31,7 +32,7 @@ class GbmBuffer : public QObject Q_OBJECT public: GbmBuffer(GbmSurface *surface, gbm_bo *bo); - GbmBuffer(gbm_bo *buffer, KWaylandServer::ClientBuffer *clientBuffer); + GbmBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer); virtual ~GbmBuffer(); void releaseBuffer(); @@ -56,7 +57,7 @@ class DrmGbmBuffer : public DrmBuffer, public GbmBuffer { public: DrmGbmBuffer(DrmGpu *gpu, GbmSurface *surface, gbm_bo *bo); - DrmGbmBuffer(DrmGpu *gpu, gbm_bo *buffer, KWaylandServer::ClientBuffer *clientBuffer); + DrmGbmBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer); ~DrmGbmBuffer() override; bool needsModeChange(DrmBuffer *b) const override; diff --git a/src/backends/drm/drm_display_device.h b/src/backends/drm/drm_display_device.h index a974012312..ca73b9a892 100644 --- a/src/backends/drm/drm_display_device.h +++ b/src/backends/drm/drm_display_device.h @@ -17,7 +17,7 @@ namespace KWin class DrmBuffer; class DrmGpu; -class DrmLayer; +class DrmOutputLayer; class DrmDisplayDevice { @@ -28,18 +28,11 @@ public: DrmGpu *gpu() const; virtual bool present() = 0; - virtual bool testScanout() = 0; virtual void frameFailed() const = 0; virtual void pageFlipped(std::chrono::nanoseconds timestamp) const = 0; - - virtual DrmPlane::Transformations softwareTransforms() const = 0; - virtual QSize bufferSize() const = 0; - virtual QSize sourceSize() const = 0; - virtual bool isFormatSupported(uint32_t drmFormat) const = 0; - virtual QVector supportedModifiers(uint32_t drmFormat) const = 0; - virtual int maxBpc() const = 0; virtual QRect renderGeometry() const = 0; - virtual DrmLayer *outputLayer() const = 0; + virtual DrmOutputLayer *outputLayer() const = 0; + virtual QVector regionToRects(const QRegion ®ion) const = 0; protected: DrmGpu *const m_gpu; diff --git a/src/backends/drm/drm_gpu.cpp b/src/backends/drm/drm_gpu.cpp index e316334a0b..7dbf51a02d 100644 --- a/src/backends/drm/drm_gpu.cpp +++ b/src/backends/drm/drm_gpu.cpp @@ -422,7 +422,7 @@ bool DrmGpu::testPipelines() QVector inactivePipelines; for (const auto &pipeline : qAsConst(m_pipelines)) { if (!pipeline->pending.layer) { - pipeline->pending.layer = m_platform->renderBackend()->createLayer(pipeline->displayDevice()); + pipeline->pending.layer = m_platform->renderBackend()->createDrmPipelineLayer(pipeline); } if (!pipeline->pending.active) { pipeline->pending.active = true; @@ -772,7 +772,7 @@ QSize DrmGpu::cursorSize() const void DrmGpu::recreateSurfaces() { for (const auto &pipeline : qAsConst(m_pipelines)) { - pipeline->pending.layer = m_platform->renderBackend()->createLayer(pipeline->displayDevice()); + pipeline->pending.layer = m_platform->renderBackend()->createDrmPipelineLayer(pipeline); } for (const auto &output : qAsConst(m_outputs)) { if (const auto virtualOutput = qobject_cast(output)) { diff --git a/src/backends/drm/drm_layer.cpp b/src/backends/drm/drm_layer.cpp index 611b801c57..2d34e2963d 100644 --- a/src/backends/drm/drm_layer.cpp +++ b/src/backends/drm/drm_layer.cpp @@ -7,20 +7,24 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "drm_layer.h" +#include "drm_pipeline.h" +#include "drm_display_device.h" + +#include namespace KWin { -DrmLayer::DrmLayer(KWin::DrmDisplayDevice* device) - : m_displayDevice(device) +DrmOutputLayer::~DrmOutputLayer() = default; + +void DrmOutputLayer::aboutToStartPainting(const QRegion &damagedRegion) { + Q_UNUSED(damagedRegion) } -DrmLayer::~DrmLayer() = default; - -DrmDisplayDevice *DrmLayer::displayDevice() const +DrmPipelineLayer::DrmPipelineLayer(DrmPipeline *pipeline) + : m_pipeline(pipeline) { - return m_displayDevice; } } diff --git a/src/backends/drm/drm_layer.h b/src/backends/drm/drm_layer.h index 06ce00c62e..d844c89326 100644 --- a/src/backends/drm/drm_layer.h +++ b/src/backends/drm/drm_layer.h @@ -18,15 +18,16 @@ namespace KWin class SurfaceItem; class DrmBuffer; -class DrmDisplayDevice; +class GLTexture; +class DrmPipeline; -class DrmLayer : public QObject +class DrmOutputLayer : public QObject { Q_OBJECT public: - DrmLayer(DrmDisplayDevice *device); - virtual ~DrmLayer(); + virtual ~DrmOutputLayer(); + virtual void aboutToStartPainting(const QRegion &damagedRegion); virtual std::optional startRendering() = 0; virtual bool endRendering(const QRegion &damagedRegion) = 0; @@ -37,6 +38,16 @@ public: */ virtual bool scanout(SurfaceItem *surfaceItem) = 0; + virtual QSharedPointer texture() const = 0; + + virtual QRegion currentDamage() const = 0; +}; + +class DrmPipelineLayer : public DrmOutputLayer +{ +public: + DrmPipelineLayer(DrmPipeline *pipeline); + /** * @returns a buffer for atomic test commits * If no fitting buffer is available, a new current buffer is created @@ -44,13 +55,10 @@ public: virtual QSharedPointer testBuffer() = 0; virtual QSharedPointer currentBuffer() const = 0; - virtual QRegion currentDamage() const = 0; virtual bool hasDirectScanoutBuffer() const = 0; - DrmDisplayDevice *displayDevice() const; - protected: - DrmDisplayDevice *const m_displayDevice; + DrmPipeline *const m_pipeline; }; } diff --git a/src/backends/drm/drm_lease_output.cpp b/src/backends/drm/drm_lease_output.cpp index ed7c80125b..dc357f8e51 100644 --- a/src/backends/drm/drm_lease_output.cpp +++ b/src/backends/drm/drm_lease_output.cpp @@ -14,6 +14,7 @@ #include "drm_object_crtc.h" #include "drm_object_plane.h" #include "drm_pipeline.h" +#include "drm_layer.h" #include "logging.h" @@ -83,51 +84,12 @@ bool DrmLeaseOutput::present() return false; } -bool DrmLeaseOutput::testScanout() -{ - return false; -} - -DrmPlane::Transformations DrmLeaseOutput::softwareTransforms() const -{ - return DrmPlane::Transformation::Rotate0; -} - -QSize DrmLeaseOutput::bufferSize() const -{ - return m_pipeline->bufferSize(); -} - -QSize DrmLeaseOutput::sourceSize() const -{ - return m_pipeline->sourceSize(); -} - -bool DrmLeaseOutput::isFormatSupported(uint32_t drmFormat) const -{ - return m_pipeline->isFormatSupported(drmFormat); -} - -QVector DrmLeaseOutput::supportedModifiers(uint32_t drmFormat) const -{ - return m_pipeline->supportedModifiers(drmFormat); -} - -int DrmLeaseOutput::maxBpc() const -{ - if (const auto prop = m_pipeline->connector()->getProp(DrmConnector::PropertyIndex::MaxBpc)) { - return prop->maxValue(); - } else { - return 8; - } -} - QRect DrmLeaseOutput::renderGeometry() const { return QRect(QPoint(), m_pipeline->sourceSize()); } -DrmLayer *DrmLeaseOutput::outputLayer() const +DrmOutputLayer *DrmLeaseOutput::outputLayer() const { return m_pipeline->pending.layer.data(); } @@ -141,4 +103,10 @@ void DrmLeaseOutput::pageFlipped(std::chrono::nanoseconds timestamp) const Q_UNUSED(timestamp) } +QVector DrmLeaseOutput::regionToRects(const QRegion ®ion) const +{ + Q_UNUSED(region) + return {}; +} + } diff --git a/src/backends/drm/drm_lease_output.h b/src/backends/drm/drm_lease_output.h index 90a6099d02..f4e5c897ed 100644 --- a/src/backends/drm/drm_lease_output.h +++ b/src/backends/drm/drm_lease_output.h @@ -41,17 +41,11 @@ public: DrmPipeline *pipeline() const; bool present() override; - DrmPlane::Transformations softwareTransforms() const override; - QSize bufferSize() const override; - QSize sourceSize() const override; - bool isFormatSupported(uint32_t drmFormat) const override; - QVector supportedModifiers(uint32_t drmFormat) const override; - int maxBpc() const override; QRect renderGeometry() const override; - DrmLayer *outputLayer() const override; - bool testScanout() override; + DrmOutputLayer *outputLayer() const override; void frameFailed() const override; void pageFlipped(std::chrono::nanoseconds timestamp) const override; + QVector regionToRects(const QRegion ®ion) const override; private: DrmPipeline *m_pipeline; diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index f2bdad3b2a..dc4563918c 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -340,11 +340,6 @@ bool DrmOutput::present() } } -bool DrmOutput::testScanout() -{ - return m_pipeline->testScanout(); -} - int DrmOutput::gammaRampSize() const { return m_pipeline->pending.crtc ? m_pipeline->pending.crtc->gammaRampSize() : 256; @@ -373,26 +368,6 @@ DrmPipeline *DrmOutput::pipeline() const return m_pipeline; } -QSize DrmOutput::bufferSize() const -{ - return m_pipeline->bufferSize(); -} - -QSize DrmOutput::sourceSize() const -{ - return m_pipeline->sourceSize(); -} - -bool DrmOutput::isFormatSupported(uint32_t drmFormat) const -{ - return m_pipeline->isFormatSupported(drmFormat); -} - -QVector DrmOutput::supportedModifiers(uint32_t drmFormat) const -{ - return m_pipeline->supportedModifiers(drmFormat); -} - bool DrmOutput::queueChanges(const WaylandOutputConfig &config) { static bool valid; @@ -449,28 +424,12 @@ void DrmOutput::revertQueuedChanges() m_pipeline->revertPendingChanges(); } -int DrmOutput::maxBpc() const -{ - auto prop = m_connector->getProp(DrmConnector::PropertyIndex::MaxBpc); - return prop ? prop->maxValue() : 8; -} - bool DrmOutput::usesSoftwareCursor() const { return !m_setCursorSuccessful || !m_moveCursorSuccessful; } -DrmPlane::Transformations DrmOutput::softwareTransforms() const -{ - if (m_pipeline->pending.bufferTransformation == m_pipeline->pending.sourceTransformation) { - return DrmPlane::Transformation::Rotate0; - } else { - // TODO handle sourceTransformation != Rotate0 - return m_pipeline->pending.sourceTransformation; - } -} - -DrmLayer *DrmOutput::outputLayer() const +DrmOutputLayer *DrmOutput::outputLayer() const { return m_pipeline->pending.layer.data(); } diff --git a/src/backends/drm/drm_output.h b/src/backends/drm/drm_output.h index 46bc9fab36..c19ce5ae46 100644 --- a/src/backends/drm/drm_output.h +++ b/src/backends/drm/drm_output.h @@ -47,14 +47,7 @@ public: DrmPipeline *pipeline() const; bool present() override; - bool testScanout() override; - QSize bufferSize() const override; - QSize sourceSize() const override; - bool isFormatSupported(uint32_t drmFormat) const override; - QVector supportedModifiers(uint32_t drmFormat) const override; - DrmPlane::Transformations softwareTransforms() const override; - int maxBpc() const override; - DrmLayer *outputLayer() const override; + DrmOutputLayer *outputLayer() const override; bool queueChanges(const WaylandOutputConfig &config); void applyQueuedChanges(const WaylandOutputConfig &config); diff --git a/src/backends/drm/drm_pipeline.h b/src/backends/drm/drm_pipeline.h index 11fda35849..0be97cd778 100644 --- a/src/backends/drm/drm_pipeline.h +++ b/src/backends/drm/drm_pipeline.h @@ -31,7 +31,7 @@ class DrmBuffer; class DrmDumbBuffer; class GammaRamp; class DrmConnectorMode; -class DrmLayer; +class DrmPipelineLayer; class DrmDisplayDevice; class DrmGammaRamp @@ -107,7 +107,7 @@ public: RenderLoopPrivate::SyncMode syncMode = RenderLoopPrivate::SyncMode::Fixed; QSharedPointer gamma; - QSharedPointer layer; + QSharedPointer layer; QPoint cursorPos; QPoint cursorHotspot; diff --git a/src/backends/drm/drm_qpainter_layer.cpp b/src/backends/drm/drm_qpainter_layer.cpp index c66df420cf..b3a97b5d4d 100644 --- a/src/backends/drm/drm_qpainter_layer.cpp +++ b/src/backends/drm/drm_qpainter_layer.cpp @@ -13,16 +13,18 @@ #include "scene_qpainter_drm_backend.h" #include "drm_gpu.h" #include "drm_backend.h" +#include "drm_pipeline.h" +#include "drm_virtual_output.h" #include namespace KWin { -DrmQPainterLayer::DrmQPainterLayer(DrmDisplayDevice *displayDevice) - : DrmLayer(displayDevice) +DrmQPainterLayer::DrmQPainterLayer(DrmPipeline *pipeline) + : DrmPipelineLayer(pipeline) { - connect(static_cast(displayDevice->gpu()->platform()->renderBackend()), &DrmQPainterBackend::aboutToBeDestroyed, this, [this]() { + connect(static_cast(pipeline->gpu()->platform()->renderBackend()), &DrmQPainterBackend::aboutToBeDestroyed, this, [this]() { m_swapchain.reset(); }); } @@ -30,10 +32,10 @@ DrmQPainterLayer::DrmQPainterLayer(DrmDisplayDevice *displayDevice) std::optional DrmQPainterLayer::startRendering() { if (!doesSwapchainFit()) { - m_swapchain = QSharedPointer::create(m_displayDevice->gpu(), m_displayDevice->sourceSize(), DRM_FORMAT_XRGB8888); + m_swapchain = QSharedPointer::create(m_pipeline->gpu(), m_pipeline->sourceSize(), DRM_FORMAT_XRGB8888); } QRegion needsRepaint; - if (!m_swapchain->acquireBuffer(m_displayDevice->renderGeometry(), &needsRepaint)) { + if (!m_swapchain->acquireBuffer(m_pipeline->displayDevice()->renderGeometry(), &needsRepaint)) { return std::optional(); } return needsRepaint; @@ -55,14 +57,14 @@ bool DrmQPainterLayer::scanout(SurfaceItem *surfaceItem) QSharedPointer DrmQPainterLayer::testBuffer() { if (!doesSwapchainFit()) { - m_swapchain = QSharedPointer::create(m_displayDevice->gpu(), m_displayDevice->sourceSize(), DRM_FORMAT_XRGB8888); + m_swapchain = QSharedPointer::create(m_pipeline->gpu(), m_pipeline->sourceSize(), DRM_FORMAT_XRGB8888); } return m_swapchain->currentBuffer(); } bool DrmQPainterLayer::doesSwapchainFit() const { - return m_swapchain && m_swapchain->size() == m_displayDevice->sourceSize(); + return m_swapchain && m_swapchain->size() == m_pipeline->sourceSize(); } QSharedPointer DrmQPainterLayer::currentBuffer() const @@ -80,4 +82,55 @@ bool DrmQPainterLayer::hasDirectScanoutBuffer() const return false; } +QSharedPointer DrmQPainterLayer::texture() const +{ + return nullptr; +} + +QImage *DrmQPainterLayer::image() +{ + return m_swapchain ? m_swapchain->currentBuffer()->image() : nullptr; +} + + +DrmVirtualQPainterLayer::DrmVirtualQPainterLayer(DrmVirtualOutput *output) + : m_output(output) +{ +} + +std::optional DrmVirtualQPainterLayer::startRendering() +{ + if (m_image.isNull() || m_image.size() != m_output->pixelSize()) { + m_image = QImage(m_output->pixelSize(), QImage::Format_RGB32); + } + return QRegion(); +} + +bool DrmVirtualQPainterLayer::endRendering(const QRegion &damagedRegion) +{ + m_currentDamage = damagedRegion; + return true; +} + +bool DrmVirtualQPainterLayer::scanout(SurfaceItem *surfaceItem) +{ + Q_UNUSED(surfaceItem); + return false; +} + +QSharedPointer DrmVirtualQPainterLayer::texture() const +{ + return nullptr; +} + +QRegion DrmVirtualQPainterLayer::currentDamage() const +{ + return m_currentDamage; +} + +QImage *DrmVirtualQPainterLayer::image() +{ + return &m_image; +} + } diff --git a/src/backends/drm/drm_qpainter_layer.h b/src/backends/drm/drm_qpainter_layer.h index edfebe050e..49960dbd1d 100644 --- a/src/backends/drm/drm_qpainter_layer.h +++ b/src/backends/drm/drm_qpainter_layer.h @@ -9,23 +9,37 @@ #pragma once #include "drm_layer.h" +#include + namespace KWin { class DumbSwapchain; +class DrmPipeline; +class DrmVirtualOutput; -class DrmQPainterLayer : public DrmLayer +class QPainterLayer { public: - DrmQPainterLayer(DrmDisplayDevice *displayDevice); + virtual ~QPainterLayer() = default; + + virtual QImage *image() = 0; +}; + +class DrmQPainterLayer : public DrmPipelineLayer, QPainterLayer +{ +public: + DrmQPainterLayer(DrmPipeline *pipeline); std::optional startRendering() override; bool endRendering(const QRegion &damagedRegion) override; bool scanout(SurfaceItem *surfaceItem) override; QSharedPointer testBuffer() override; + QSharedPointer texture() const override; QSharedPointer currentBuffer() const override; QRegion currentDamage() const override; bool hasDirectScanoutBuffer() const override; + QImage *image() override; private: bool doesSwapchainFit() const; @@ -34,4 +48,23 @@ private: QRegion m_currentDamage; }; +class DrmVirtualQPainterLayer : public DrmOutputLayer, QPainterLayer +{ +public: + DrmVirtualQPainterLayer(DrmVirtualOutput *output); + + std::optional startRendering() override; + bool endRendering(const QRegion &damagedRegion) override; + bool scanout(SurfaceItem *surfaceItem) override; + + QSharedPointer texture() const override; + QRegion currentDamage() const override; + QImage *image() override; + +private: + QImage m_image; + QRegion m_currentDamage; + DrmVirtualOutput *const m_output; +}; + } diff --git a/src/backends/drm/drm_render_backend.h b/src/backends/drm/drm_render_backend.h index 243f664ba4..36067d9fbb 100644 --- a/src/backends/drm/drm_render_backend.h +++ b/src/backends/drm/drm_render_backend.h @@ -13,15 +13,19 @@ namespace KWin { -class DrmLayer; +class DrmPipelineLayer; class DrmDisplayDevice; +class DrmVirtualOutput; +class DrmPipeline; +class DrmOutputLayer; class DrmRenderBackend { public: virtual ~DrmRenderBackend() = default; - virtual QSharedPointer createLayer(DrmDisplayDevice *displayDevice) = 0; + virtual QSharedPointer createDrmPipelineLayer(DrmPipeline *pipeline) = 0; + virtual QSharedPointer createLayer(DrmVirtualOutput *output) = 0; }; diff --git a/src/backends/drm/drm_virtual_output.cpp b/src/backends/drm/drm_virtual_output.cpp index b198f7c441..ca5126a348 100644 --- a/src/backends/drm/drm_virtual_output.cpp +++ b/src/backends/drm/drm_virtual_output.cpp @@ -77,29 +77,6 @@ void DrmVirtualOutput::updateEnablement(bool enable) gpu()->platform()->enableOutput(this, enable); } -QSize DrmVirtualOutput::bufferSize() const -{ - return pixelSize(); -} - -QSize DrmVirtualOutput::sourceSize() const -{ - return pixelSize(); -} - -bool DrmVirtualOutput::isFormatSupported(uint32_t drmFormat) const -{ - Q_UNUSED(drmFormat); - return true; -} - -QVector DrmVirtualOutput::supportedModifiers(uint32_t drmFormat) const -{ - Q_UNUSED(drmFormat); - // empty list -> implicit modifiers are used / modifier is freely chosen by gbm - return {}; -} - int DrmVirtualOutput::gammaRampSize() const { return 200; @@ -111,26 +88,11 @@ bool DrmVirtualOutput::setGammaRamp(const GammaRamp &gamma) return true; } -int DrmVirtualOutput::maxBpc() const -{ - return 8; -} - -DrmPlane::Transformations DrmVirtualOutput::softwareTransforms() const -{ - return DrmPlane::Transformation::Rotate0; -} - -DrmLayer *DrmVirtualOutput::outputLayer() const +DrmOutputLayer *DrmVirtualOutput::outputLayer() const { return m_layer.data(); } -bool DrmVirtualOutput::testScanout() -{ - return true; -} - void DrmVirtualOutput::recreateSurface() { m_layer = m_gpu->platform()->renderBackend()->createLayer(this); diff --git a/src/backends/drm/drm_virtual_output.h b/src/backends/drm/drm_virtual_output.h index 24ab437872..7cac653eb6 100644 --- a/src/backends/drm/drm_virtual_output.h +++ b/src/backends/drm/drm_virtual_output.h @@ -19,7 +19,7 @@ namespace KWin class SoftwareVsyncMonitor; class VirtualBackend; -class DrmLayer; +class DrmPipelineLayer; class DrmVirtualOutput : public DrmAbstractOutput { @@ -30,19 +30,9 @@ public: ~DrmVirtualOutput() override; bool present() override; - QSize bufferSize() const override; - QSize sourceSize() const override; - - bool isFormatSupported(uint32_t drmFormat) const override; - QVector supportedModifiers(uint32_t drmFormat) const override; - int maxBpc() const override; - int gammaRampSize() const override; bool setGammaRamp(const GammaRamp &gamma) override; - DrmPlane::Transformations softwareTransforms() const override; - DrmLayer *outputLayer() const override; - bool testScanout() override; - + DrmOutputLayer *outputLayer() const override; void recreateSurface(); private: @@ -50,7 +40,7 @@ private: void setDpmsMode(DpmsMode mode) override; void updateEnablement(bool enable) override; - QSharedPointer m_layer; + QSharedPointer m_layer; bool m_pageFlipPending = true; int m_modeIndex = 0; diff --git a/src/backends/drm/egl_gbm_backend.cpp b/src/backends/drm/egl_gbm_backend.cpp index a480012264..19bf1a08a2 100644 --- a/src/backends/drm/egl_gbm_backend.cpp +++ b/src/backends/drm/egl_gbm_backend.cpp @@ -29,6 +29,7 @@ #include "drm_abstract_output.h" #include "egl_dmabuf.h" #include "egl_gbm_layer.h" +#include "virtual_egl_gbm_layer.h" // kwin libs #include #include @@ -208,38 +209,9 @@ bool EglGbmBackend::initBufferConfigs() return false; } -static QVector regionToRects(const QRegion ®ion, DrmAbstractOutput *output) -{ - const int height = output->sourceSize().height(); - - const QMatrix4x4 matrix = DrmOutput::logicalToNativeMatrix(output->geometry(), - output->scale(), - output->transform()); - - QVector rects; - rects.reserve(region.rectCount() * 4); - for (const QRect &_rect : region) { - const QRect rect = matrix.mapRect(_rect); - - rects << rect.left(); - rects << height - (rect.y() + rect.height()); - rects << rect.width(); - rects << rect.height(); - } - return rects; -} - void EglGbmBackend::aboutToStartPainting(AbstractOutput *output, const QRegion &damagedRegion) { - const auto drmOutput = static_cast(output); - const auto &surface = static_cast(drmOutput->outputLayer()); - if (surface->bufferAge() > 0 && !damagedRegion.isEmpty() && supportsPartialUpdate()) { - QVector rects = regionToRects(damagedRegion, static_cast(output)); - const bool correct = eglSetDamageRegionKHR(eglDisplay(), surface->eglSurface(), rects.data(), rects.count()/4); - if (!correct) { - qCWarning(KWIN_DRM) << "eglSetDamageRegionKHR failed:" << getEglErrorString(); - } - } + static_cast(output)->outputLayer()->aboutToStartPainting(damagedRegion); } SurfaceTexture *EglGbmBackend::createSurfaceTextureInternal(SurfacePixmapInternal *pixmap) @@ -303,23 +275,6 @@ GbmFormat EglGbmBackend::gbmFormatForDrmFormat(uint32_t format) const } } -std::optional EglGbmBackend::chooseFormat(DrmDisplayDevice *device) const -{ - // formats are already sorted by order of preference - std::optional fallback; - for (const auto &format : qAsConst(m_formats)) { - if (device->isFormatSupported(format.drmFormat)) { - int bpc = std::max(format.redSize, std::max(format.greenSize, format.blueSize)); - if (bpc <= device->maxBpc() && !fallback.has_value()) { - fallback = format.drmFormat; - } else { - return format.drmFormat; - } - } - } - return fallback; -} - bool EglGbmBackend::prefer10bpc() const { static bool ok = false; @@ -332,9 +287,14 @@ EGLConfig EglGbmBackend::config(uint32_t format) const return m_configs[format]; } -QSharedPointer EglGbmBackend::createLayer(DrmDisplayDevice *displayDevice) +QSharedPointer EglGbmBackend::createDrmPipelineLayer(DrmPipeline *pipeline) { - return QSharedPointer::create(this, displayDevice); + return QSharedPointer::create(this, pipeline); +} + +QSharedPointer EglGbmBackend::createLayer(DrmVirtualOutput *output) +{ + return QSharedPointer::create(this, output); } DrmGpu *EglGbmBackend::gpu() const diff --git a/src/backends/drm/egl_gbm_backend.h b/src/backends/drm/egl_gbm_backend.h index e454e2640e..0c7b4ea50f 100644 --- a/src/backends/drm/egl_gbm_backend.h +++ b/src/backends/drm/egl_gbm_backend.h @@ -39,6 +39,8 @@ class DrmBackend; class DrmGpu; class EglGbmLayer; class DrmDisplayDevice; +class DrmOutputLayer; +class DrmPipeline; struct GbmFormat { uint32_t drmFormat = 0; @@ -67,14 +69,14 @@ public: void init() override; bool scanout(AbstractOutput *output, SurfaceItem *surfaceItem) override; bool prefer10bpc() const override; - QSharedPointer createLayer(DrmDisplayDevice *displayDevice) override; + QSharedPointer createDrmPipelineLayer(DrmPipeline *pipeline) override; + QSharedPointer createLayer(DrmVirtualOutput *output) override; QSharedPointer textureForOutput(AbstractOutput *requestedOutput) const override; QSharedPointer testBuffer(DrmAbstractOutput *output); EGLConfig config(uint32_t format) const; GbmFormat gbmFormatForDrmFormat(uint32_t format) const; - std::optional chooseFormat(DrmDisplayDevice *displyDevice) const; DrmGpu *gpu() const; Q_SIGNALS: diff --git a/src/backends/drm/egl_gbm_layer.cpp b/src/backends/drm/egl_gbm_layer.cpp index a992ae15bd..421f110eae 100644 --- a/src/backends/drm/egl_gbm_layer.cpp +++ b/src/backends/drm/egl_gbm_layer.cpp @@ -34,8 +34,8 @@ namespace KWin { -EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmDisplayDevice *displayDevice) - : DrmLayer(displayDevice) +EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline) + : DrmPipelineLayer(pipeline) , m_eglBackend(eglBackend) { connect(eglBackend, &EglGbmBackend::aboutToBeDestroyed, this, &EglGbmLayer::destroyResources); @@ -90,7 +90,7 @@ std::optional EglGbmLayer::startRendering() if (!m_gbmSurface->makeContextCurrent()) { return std::optional(); } - auto repaintRegion = m_gbmSurface->repaintRegion(m_displayDevice->renderGeometry()); + auto repaintRegion = m_gbmSurface->repaintRegion(m_pipeline->displayDevice()->renderGeometry()); // shadow buffer if (doesShadowBufferFit(m_shadowBuffer.data())) { @@ -99,9 +99,9 @@ std::optional EglGbmLayer::startRendering() if (doesShadowBufferFit(m_oldShadowBuffer.data())) { m_shadowBuffer = m_oldShadowBuffer; } else { - if (m_displayDevice->softwareTransforms() != DrmPlane::Transformations(DrmPlane::Transformation::Rotate0)) { + if (m_pipeline->pending.bufferTransformation != m_pipeline->pending.sourceTransformation) { const auto format = m_eglBackend->gbmFormatForDrmFormat(m_gbmSurface->format()); - m_shadowBuffer = QSharedPointer::create(m_displayDevice->sourceSize(), format); + m_shadowBuffer = QSharedPointer::create(m_pipeline->sourceSize(), format); if (!m_shadowBuffer->isComplete()) { return std::optional(); } @@ -121,11 +121,23 @@ std::optional EglGbmLayer::startRendering() return repaintRegion; } +void EglGbmLayer::aboutToStartPainting(const QRegion &damagedRegion) +{ + if (m_gbmSurface->bufferAge() > 0 && !damagedRegion.isEmpty() && m_eglBackend->supportsPartialUpdate()) { + QVector rects = m_pipeline->displayDevice()->regionToRects(damagedRegion); + const bool correct = eglSetDamageRegionKHR(m_eglBackend->eglDisplay(), m_gbmSurface->eglSurface(), rects.data(), rects.count() / 4); + if (!correct) { + qCWarning(KWIN_DRM) << "eglSetDamageRegionKHR failed:" << getEglErrorString(); + } + } +} + bool EglGbmLayer::endRendering(const QRegion &damagedRegion) { if (m_shadowBuffer) { GLRenderTarget::popRenderTarget(); - m_shadowBuffer->render(m_displayDevice); + // TODO handle m_pipeline->pending.bufferTransformation != Rotate0 + m_shadowBuffer->render(m_pipeline->pending.sourceTransformation); } GLRenderTarget::popRenderTarget(); const auto buffer = m_gbmSurface->swapBuffersForDrm(damagedRegion); @@ -166,70 +178,83 @@ bool EglGbmLayer::renderTestBuffer() return false; } glClear(GL_COLOR_BUFFER_BIT); - if (!endRendering(m_displayDevice->renderGeometry())) { + if (!endRendering(m_pipeline->displayDevice()->renderGeometry())) { return false; } return true; } -bool EglGbmLayer::createGbmSurface() +bool EglGbmLayer::createGbmSurface(uint32_t format, const QVector &modifiers) { static bool modifiersEnvSet = false; static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0; - - auto format = m_eglBackend->chooseFormat(m_displayDevice); - if (!format || m_importMode == MultiGpuImportMode::DumbBufferXrgb8888) { - format = DRM_FORMAT_XRGB8888; - } - const auto modifiers = m_displayDevice->supportedModifiers(format.value()); - const auto size = m_displayDevice->bufferSize(); - const auto config = m_eglBackend->config(format.value()); - const bool allowModifiers = m_eglBackend->gpu()->addFB2ModifiersSupported() && m_displayDevice->gpu()->addFB2ModifiersSupported() + const bool allowModifiers = m_eglBackend->gpu()->addFB2ModifiersSupported() && m_pipeline->gpu()->addFB2ModifiersSupported() && ((m_eglBackend->gpu()->isNVidia() && !modifiersEnvSet) || (modifiersEnvSet && modifiersEnv)); + const auto size = m_pipeline->bufferSize(); + const auto config = m_eglBackend->config(format); + QSharedPointer gbmSurface; #if HAVE_GBM_BO_GET_FD_FOR_PLANE if (!allowModifiers) { #else // modifiers have to be disabled with multi-gpu if gbm_bo_get_fd_for_plane is not available - if (!allowModifiers || m_displayDevice->gpu() != m_eglBackend->gpu()) { + if (!allowModifiers || m_pipeline->gpu() != m_eglBackend->gpu()) { #endif int flags = GBM_BO_USE_RENDERING; - if (m_displayDevice->gpu() == m_eglBackend->gpu()) { + if (m_pipeline->gpu() == m_eglBackend->gpu()) { flags |= GBM_BO_USE_SCANOUT; } else { flags |= GBM_BO_USE_LINEAR; } - gbmSurface = QSharedPointer::create(m_eglBackend->gpu(), size, format.value(), flags, config); + gbmSurface = QSharedPointer::create(m_eglBackend->gpu(), size, format, flags, config); } else { - gbmSurface = QSharedPointer::create(m_eglBackend->gpu(), size, format.value(), modifiers, config); + gbmSurface = QSharedPointer::create(m_eglBackend->gpu(), size, format, modifiers, config); if (!gbmSurface->isValid()) { // the egl / gbm implementation may reject the modifier list from another gpu // as a fallback use linear, to at least make CPU copy more efficient const QVector linear = {DRM_FORMAT_MOD_LINEAR}; - gbmSurface = QSharedPointer::create(m_eglBackend->gpu(), size, format.value(), linear, config); + gbmSurface = QSharedPointer::create(m_eglBackend->gpu(), size, format, linear, config); } } - if (!gbmSurface->isValid()) { + if (gbmSurface->isValid()) { + m_oldGbmSurface = m_gbmSurface; + m_gbmSurface = gbmSurface; + return true; + } else { return false; } - m_oldGbmSurface = m_gbmSurface; - m_gbmSurface = gbmSurface; - return true; +} + +bool EglGbmLayer::createGbmSurface() +{ + const auto tranches = m_eglBackend->dmabuf()->tranches(); + for (const auto &tranche : tranches) { + for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) { + const uint32_t &format = it.key(); + if (m_importMode == MultiGpuImportMode::DumbBufferXrgb8888 && format != DRM_FORMAT_XRGB8888) { + continue; + } + if (m_pipeline->isFormatSupported(format) && createGbmSurface(format, m_pipeline->supportedModifiers(format))) { + return true; + } + } + } + return false; } bool EglGbmLayer::doesGbmSurfaceFit(GbmSurface *surf) const { - return surf && surf->size() == m_displayDevice->bufferSize() - && m_displayDevice->isFormatSupported(surf->format()) + return surf && surf->size() == m_pipeline->bufferSize() + && m_pipeline->isFormatSupported(surf->format()) && (m_importMode != MultiGpuImportMode::DumbBufferXrgb8888 || surf->format() == DRM_FORMAT_XRGB8888) - && (surf->modifiers().isEmpty() || m_displayDevice->supportedModifiers(surf->format()) == surf->modifiers()); + && (surf->modifiers().isEmpty() || m_pipeline->supportedModifiers(surf->format()) == surf->modifiers()); } bool EglGbmLayer::doesShadowBufferFit(ShadowBuffer *buffer) const { - if (m_displayDevice->softwareTransforms() != DrmPlane::Transformations(DrmPlane::Transformation::Rotate0)) { - return buffer && buffer->texture()->size() == m_displayDevice->sourceSize() && buffer->drmFormat() == m_gbmSurface->format(); + if (m_pipeline->pending.bufferTransformation != m_pipeline->pending.sourceTransformation) { + return buffer && buffer->texture()->size() == m_pipeline->sourceSize() && buffer->drmFormat() == m_gbmSurface->format(); } else { return buffer == nullptr; } @@ -237,7 +262,7 @@ bool EglGbmLayer::doesShadowBufferFit(ShadowBuffer *buffer) const bool EglGbmLayer::doesSwapchainFit(DumbSwapchain *swapchain) const { - return swapchain && swapchain->size() == m_displayDevice->sourceSize() && swapchain->drmFormat() == m_gbmSurface->format(); + return swapchain && swapchain->size() == m_pipeline->sourceSize() && swapchain->drmFormat() == m_gbmSurface->format(); } QSharedPointer EglGbmLayer::texture() const @@ -248,7 +273,7 @@ QSharedPointer EglGbmLayer::texture() const qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString(); return QSharedPointer(nullptr); } - return QSharedPointer::create(m_eglBackend->eglDisplay(), image, GL_RGBA8, m_displayDevice->sourceSize()); + return QSharedPointer::create(m_eglBackend->eglDisplay(), image, GL_RGBA8, m_pipeline->sourceSize()); }; if (m_scanoutBuffer) { return createImage(dynamic_cast(m_scanoutBuffer.data())); @@ -315,7 +340,7 @@ QSharedPointer EglGbmLayer::importDmabuf() data.strides[i] = gbm_bo_get_stride_for_plane(bo, i); data.offsets[i] = gbm_bo_get_offset(bo, i); } - importedBuffer = gbm_bo_import(m_displayDevice->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT); + importedBuffer = gbm_bo_import(m_pipeline->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT); } else { #endif gbm_import_fd_data data = { @@ -329,7 +354,7 @@ QSharedPointer EglGbmLayer::importDmabuf() qCWarning(KWIN_DRM, "failed to export gbm_bo as dma-buf: %s", strerror(errno)); return nullptr; } - importedBuffer = gbm_bo_import(m_displayDevice->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT); + importedBuffer = gbm_bo_import(m_pipeline->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT); #if HAVE_GBM_BO_GET_FD_FOR_PLANE } #endif @@ -337,7 +362,7 @@ QSharedPointer EglGbmLayer::importDmabuf() qCWarning(KWIN_DRM, "failed to import gbm_bo for multi-gpu usage: %s", strerror(errno)); return nullptr; } - const auto buffer = QSharedPointer::create(m_displayDevice->gpu(), importedBuffer, nullptr); + const auto buffer = QSharedPointer::create(m_pipeline->gpu(), nullptr, importedBuffer); return buffer->bufferId() ? buffer : nullptr; } @@ -349,7 +374,7 @@ QSharedPointer EglGbmLayer::importWithCpu() if (doesSwapchainFit(m_oldImportSwapchain.data())) { m_importSwapchain = m_oldImportSwapchain; } else { - const auto swapchain = QSharedPointer::create(m_displayDevice->gpu(), m_displayDevice->sourceSize(), m_gbmSurface->format()); + const auto swapchain = QSharedPointer::create(m_pipeline->gpu(), m_pipeline->sourceSize(), m_gbmSurface->format()); if (swapchain->isEmpty()) { return nullptr; } @@ -386,7 +411,7 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem) return false; } const auto buffer = qobject_cast(item->surface()->buffer()); - if (!buffer || buffer->planes().isEmpty() || buffer->size() != m_displayDevice->sourceSize()) { + if (!buffer || buffer->planes().isEmpty() || buffer->size() != m_pipeline->sourceSize()) { return false; } @@ -396,51 +421,14 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem) m_scanoutCandidate.surface = item->surface(); m_scanoutCandidate.attemptedThisFrame = true; - if (!m_displayDevice->isFormatSupported(buffer->format())) { + if (!m_pipeline->isFormatSupported(buffer->format())) { sendDmabufFeedback(buffer); return false; } - const auto planes = buffer->planes(); - gbm_bo *importedBuffer; - if (planes.first().modifier != DRM_FORMAT_MOD_INVALID || planes.first().offset > 0 || planes.count() > 1) { - if (!m_displayDevice->gpu()->addFB2ModifiersSupported() || !m_displayDevice->supportedModifiers(buffer->format()).contains(planes.first().modifier)) { - return false; - } - gbm_import_fd_modifier_data data = {}; - data.format = buffer->format(); - data.width = (uint32_t) buffer->size().width(); - data.height = (uint32_t) buffer->size().height(); - data.num_fds = planes.count(); - data.modifier = planes.first().modifier; - for (int i = 0; i < planes.count(); i++) { - data.fds[i] = planes[i].fd; - data.offsets[i] = planes[i].offset; - data.strides[i] = planes[i].stride; - } - importedBuffer = gbm_bo_import(m_displayDevice->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT); - } else { - const auto &plane = planes.first(); - gbm_import_fd_data data = {}; - data.fd = plane.fd; - data.width = (uint32_t) buffer->size().width(); - data.height = (uint32_t) buffer->size().height(); - data.stride = plane.stride; - data.format = buffer->format(); - importedBuffer = gbm_bo_import(m_displayDevice->gpu()->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT); - } - if (!importedBuffer) { + m_scanoutBuffer = QSharedPointer::create(m_pipeline->gpu(), buffer); + if (!m_scanoutBuffer || !m_scanoutBuffer->bufferId()) { sendDmabufFeedback(buffer); - if (errno != EINVAL) { - qCWarning(KWIN_DRM) << "Importing buffer for direct scanout failed:" << strerror(errno); - } - return false; - } - m_scanoutBuffer = QSharedPointer::create(m_displayDevice->gpu(), importedBuffer, buffer); - if (!m_scanoutBuffer->bufferId()) { - // buffer can't actually be scanned out. Mesa is supposed to prevent this from happening - // in gbm_bo_import but apparently that doesn't always work m_scanoutBuffer.reset(); - sendDmabufFeedback(buffer); return false; } // damage tracking for screen casting @@ -450,13 +438,13 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem) surfaceItem->resetDamage(); for (const auto &rect : trackedDamage) { auto damageRect = QRect(rect); - damageRect.translate(m_displayDevice->renderGeometry().topLeft()); + damageRect.translate(m_pipeline->displayDevice()->renderGeometry().topLeft()); damage |= damageRect; } } else { - damage = m_displayDevice->renderGeometry(); + damage = m_pipeline->displayDevice()->renderGeometry(); } - if (m_displayDevice->testScanout()) { + if (m_pipeline->testScanout()) { m_currentBuffer = m_scanoutBuffer; m_currentDamage = damage; return true; @@ -471,9 +459,9 @@ void EglGbmLayer::sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer * if (!m_scanoutCandidate.attemptedFormats[failedBuffer->format()].contains(failedBuffer->planes().first().modifier)) { m_scanoutCandidate.attemptedFormats[failedBuffer->format()] << failedBuffer->planes().first().modifier; } - if (const auto &drmOutput = dynamic_cast(m_displayDevice); drmOutput && m_scanoutCandidate.surface->dmabufFeedbackV1()) { + if (m_scanoutCandidate.surface->dmabufFeedbackV1()) { QVector scanoutTranches; - const auto &drmFormats = drmOutput->pipeline()->supportedFormats(); + const auto &drmFormats = m_pipeline->supportedFormats(); const auto tranches = m_eglBackend->dmabuf()->tranches(); for (const auto &tranche : tranches) { KWaylandServer::LinuxDmaBufV1Feedback::Tranche scanoutTranche; @@ -488,7 +476,7 @@ void EglGbmLayer::sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer * } } if (!scanoutTranche.formatTable.isEmpty()) { - scanoutTranche.device = m_displayDevice->gpu()->deviceId(); + scanoutTranche.device = m_pipeline->gpu()->deviceId(); scanoutTranche.flags = KWaylandServer::LinuxDmaBufV1Feedback::TrancheFlag::Scanout; scanoutTranches << scanoutTranche; } @@ -502,16 +490,6 @@ QSharedPointer EglGbmLayer::currentBuffer() const return m_scanoutBuffer ? m_scanoutBuffer : m_currentBuffer; } -int EglGbmLayer::bufferAge() const -{ - return m_gbmSurface ? m_gbmSurface->bufferAge() : 0; -} - -EGLSurface EglGbmLayer::eglSurface() const -{ - return m_gbmSurface ? m_gbmSurface->eglSurface() : EGL_NO_SURFACE; -} - bool EglGbmLayer::hasDirectScanoutBuffer() const { return m_scanoutBuffer != nullptr; diff --git a/src/backends/drm/egl_gbm_layer.h b/src/backends/drm/egl_gbm_layer.h index 9ae3dacab2..d4c9643667 100644 --- a/src/backends/drm/egl_gbm_layer.h +++ b/src/backends/drm/egl_gbm_layer.h @@ -33,26 +33,26 @@ class DrmGpu; class SurfaceItem; class GLTexture; class EglGbmBackend; +class DrmPipeline; -class EglGbmLayer : public DrmLayer +class EglGbmLayer : public DrmPipelineLayer { public: - EglGbmLayer(EglGbmBackend *eglBackend, DrmDisplayDevice *displayDevice); + EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline); ~EglGbmLayer(); std::optional startRendering() override; + void aboutToStartPainting(const QRegion &damagedRegion) override; bool endRendering(const QRegion &damagedRegion) override; bool scanout(SurfaceItem *surfaceItem) override; QSharedPointer testBuffer() override; QSharedPointer currentBuffer() const override; bool hasDirectScanoutBuffer() const override; QRegion currentDamage() const override; - QSharedPointer texture() const; - - int bufferAge() const; - EGLSurface eglSurface() const; + QSharedPointer texture() const override; private: + bool createGbmSurface(uint32_t format, const QVector &modifiers); bool createGbmSurface(); bool doesGbmSurfaceFit(GbmSurface *surf) const; bool doesShadowBufferFit(ShadowBuffer *buffer) const; diff --git a/src/backends/drm/scene_qpainter_drm_backend.cpp b/src/backends/drm/scene_qpainter_drm_backend.cpp index a85566e345..229781e213 100644 --- a/src/backends/drm/scene_qpainter_drm_backend.cpp +++ b/src/backends/drm/scene_qpainter_drm_backend.cpp @@ -13,6 +13,7 @@ #include "drm_buffer.h" #include "renderloop_p.h" #include "drm_qpainter_layer.h" +#include "drm_virtual_output.h" #include @@ -34,7 +35,7 @@ DrmQPainterBackend::~DrmQPainterBackend() QImage *DrmQPainterBackend::bufferForScreen(AbstractOutput *output) { const auto drmOutput = static_cast(output); - return static_cast(drmOutput->outputLayer()->currentBuffer().data())->image(); + return dynamic_cast(drmOutput->outputLayer())->image(); } QRegion DrmQPainterBackend::beginFrame(AbstractOutput *output) @@ -51,9 +52,14 @@ void DrmQPainterBackend::endFrame(AbstractOutput *output, const QRegion &rendere static_cast(output)->present(); } -QSharedPointer DrmQPainterBackend::createLayer(DrmDisplayDevice *displayDevice) +QSharedPointer DrmQPainterBackend::createDrmPipelineLayer(DrmPipeline *pipeline) { - return QSharedPointer::create(displayDevice); + return QSharedPointer::create(pipeline); +} + +QSharedPointer DrmQPainterBackend::createLayer(DrmVirtualOutput *output) +{ + return QSharedPointer::create(output); } } diff --git a/src/backends/drm/scene_qpainter_drm_backend.h b/src/backends/drm/scene_qpainter_drm_backend.h index 17ab602d75..7ba129ddae 100644 --- a/src/backends/drm/scene_qpainter_drm_backend.h +++ b/src/backends/drm/scene_qpainter_drm_backend.h @@ -21,6 +21,7 @@ namespace KWin class DrmBackend; class DrmAbstractOutput; class DrmQPainterLayer; +class DrmPipeline; class DrmQPainterBackend : public QPainterBackend, public DrmRenderBackend { @@ -32,7 +33,8 @@ public: QImage *bufferForScreen(AbstractOutput *output) override; QRegion beginFrame(AbstractOutput *output) override; void endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damagedRegion) override; - QSharedPointer createLayer(DrmDisplayDevice *displayDevice) override; + QSharedPointer createDrmPipelineLayer(DrmPipeline *pipeline) override; + QSharedPointer createLayer(DrmVirtualOutput *output) override; Q_SIGNALS: void aboutToBeDestroyed(); diff --git a/src/backends/drm/shadowbuffer.cpp b/src/backends/drm/shadowbuffer.cpp index e1c9fb0062..0b5f34478c 100644 --- a/src/backends/drm/shadowbuffer.cpp +++ b/src/backends/drm/shadowbuffer.cpp @@ -56,10 +56,9 @@ ShadowBuffer::~ShadowBuffer() { } -void ShadowBuffer::render(DrmDisplayDevice *displayDevice) +void ShadowBuffer::render(DrmPlane::Transformations transform) { QMatrix4x4 mvpMatrix; - const auto transform = displayDevice->softwareTransforms(); if (transform & DrmPlane::Transformation::Rotate90) { mvpMatrix.rotate(90, 0, 0, 1); } else if (transform & DrmPlane::Transformation::Rotate180) { diff --git a/src/backends/drm/shadowbuffer.h b/src/backends/drm/shadowbuffer.h index ac047dd299..2f70813e2e 100644 --- a/src/backends/drm/shadowbuffer.h +++ b/src/backends/drm/shadowbuffer.h @@ -12,6 +12,7 @@ #include #include "egl_gbm_backend.h" +#include "drm_object_plane.h" namespace KWin { @@ -25,7 +26,7 @@ public: ~ShadowBuffer(); bool isComplete() const; - void render(DrmDisplayDevice *displayDevice); + void render(DrmPlane::Transformations transform); GLRenderTarget *renderTarget() const; QSharedPointer texture() const; diff --git a/src/backends/drm/virtual_egl_gbm_layer.cpp b/src/backends/drm/virtual_egl_gbm_layer.cpp new file mode 100644 index 0000000000..57ee379f97 --- /dev/null +++ b/src/backends/drm/virtual_egl_gbm_layer.cpp @@ -0,0 +1,190 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2022 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#include "virtual_egl_gbm_layer.h" +#include "gbm_surface.h" +#include "drm_abstract_output.h" +#include "drm_gpu.h" +#include "egl_gbm_backend.h" +#include "shadowbuffer.h" +#include "drm_output.h" +#include "drm_pipeline.h" +#include "dumb_swapchain.h" +#include "logging.h" +#include "egl_dmabuf.h" +#include "surfaceitem_wayland.h" +#include "kwineglimagetexture.h" +#include "drm_backend.h" +#include "drm_virtual_output.h" +#include "kwineglutils_p.h" + +#include "KWaylandServer/surface_interface.h" +#include "KWaylandServer/linuxdmabufv1clientbuffer.h" + +#include +#include +#include +#include +#include + +namespace KWin +{ + +VirtualEglGbmLayer::VirtualEglGbmLayer(EglGbmBackend *eglBackend, DrmVirtualOutput *output) + : m_output(output) + , m_eglBackend(eglBackend) +{ + connect(eglBackend, &EglGbmBackend::aboutToBeDestroyed, this, &VirtualEglGbmLayer::destroyResources); +} + +void VirtualEglGbmLayer::destroyResources() +{ + m_gbmSurface.reset(); + m_oldGbmSurface.reset(); +} + +void VirtualEglGbmLayer::aboutToStartPainting(const QRegion &damagedRegion) +{ + if (m_gbmSurface->bufferAge() > 0 && !damagedRegion.isEmpty() && m_eglBackend->supportsPartialUpdate()) { + const QRegion region = damagedRegion & m_output->geometry(); + + QVector rects = m_output->regionToRects(region); + const bool correct = eglSetDamageRegionKHR(m_eglBackend->eglDisplay(), m_gbmSurface->eglSurface(), rects.data(), rects.count() / 4); + if (!correct) { + qCWarning(KWIN_DRM) << "eglSetDamageRegionKHR failed:" << getEglErrorString(); + } + } +} + +std::optional VirtualEglGbmLayer::startRendering() +{ + // gbm surface + if (doesGbmSurfaceFit(m_gbmSurface.data())) { + m_oldGbmSurface.reset(); + } else { + if (doesGbmSurfaceFit(m_oldGbmSurface.data())) { + m_gbmSurface = m_oldGbmSurface; + } else { + if (!createGbmSurface()) { + return std::optional(); + } + } + } + if (!m_gbmSurface->makeContextCurrent()) { + return std::optional(); + } + GLRenderTarget::pushRenderTarget(m_gbmSurface->renderTarget()); + return m_gbmSurface->repaintRegion(m_output->geometry()); +} + +bool VirtualEglGbmLayer::endRendering(const QRegion &damagedRegion) +{ + GLRenderTarget::popRenderTarget(); + const auto buffer = m_gbmSurface->swapBuffers(damagedRegion.intersected(m_output->geometry())); + if (buffer) { + m_currentBuffer = buffer; + m_currentDamage = damagedRegion; + } + return buffer; +} + +QRegion VirtualEglGbmLayer::currentDamage() const +{ + return m_currentDamage; +} + +bool VirtualEglGbmLayer::createGbmSurface() +{ + static bool modifiersEnvSet = false; + static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0; + + const auto tranches = m_eglBackend->dmabuf()->tranches(); + for (const auto &tranche : tranches) { + for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) { + const auto size = m_output->pixelSize(); + const auto config = m_eglBackend->config(it.key()); + const auto modifiers = it.value(); + const bool allowModifiers = m_eglBackend->gpu()->addFB2ModifiersSupported() && ((m_eglBackend->gpu()->isNVidia() && !modifiersEnvSet) || (modifiersEnvSet && modifiersEnv)); + + QSharedPointer gbmSurface; + if (!allowModifiers) { + gbmSurface = QSharedPointer::create(m_eglBackend->gpu(), size, it.key(), GBM_BO_USE_RENDERING, config); + } else { + gbmSurface = QSharedPointer::create(m_eglBackend->gpu(), size, it.key(), it.value(), config); + } + if (!gbmSurface->isValid()) { + continue; + } + m_oldGbmSurface = m_gbmSurface; + m_gbmSurface = gbmSurface; + return true; + } + } + return false; +} + +bool VirtualEglGbmLayer::doesGbmSurfaceFit(GbmSurface *surf) const +{ + return surf && surf->size() == m_output->pixelSize(); +} + +QSharedPointer VirtualEglGbmLayer::texture() const +{ + GbmBuffer *gbmBuffer = m_gbmSurface->currentBuffer().get(); + if (!gbmBuffer) { + qCWarning(KWIN_DRM) << "Failed to record frame: No gbm buffer!"; + return nullptr; + } + EGLImageKHR image = eglCreateImageKHR(m_eglBackend->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 nullptr; + } + return QSharedPointer::create(m_eglBackend->eglDisplay(), image, GL_RGBA8, m_gbmSurface->size()); +} + +bool VirtualEglGbmLayer::scanout(SurfaceItem *surfaceItem) +{ + static bool valid; + static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid; + if (directScanoutDisabled) { + return false; + } + + SurfaceItemWayland *item = qobject_cast(surfaceItem); + if (!item || !item->surface()) { + return false; + } + const auto buffer = qobject_cast(item->surface()->buffer()); + if (!buffer || buffer->planes().isEmpty() || buffer->size() != m_output->pixelSize()) { + return false; + } + const auto scanoutBuffer = QSharedPointer::create(m_output->gpu(), buffer); + if (!scanoutBuffer->getBo()) { + return false; + } + // damage tracking for screen casting + QRegion damage; + if (m_scanoutSurface == item->surface()) { + QRegion trackedDamage = surfaceItem->damage(); + surfaceItem->resetDamage(); + for (const auto &rect : trackedDamage) { + auto damageRect = QRect(rect); + damageRect.translate(m_output->geometry().topLeft()); + damage |= damageRect; + } + } else { + damage = m_output->geometry(); + } + m_scanoutSurface = item->surface(); + m_currentBuffer = scanoutBuffer; + m_currentDamage = damage; + return true; +} + +} diff --git a/src/backends/drm/virtual_egl_gbm_layer.h b/src/backends/drm/virtual_egl_gbm_layer.h new file mode 100644 index 0000000000..6ef2aa549d --- /dev/null +++ b/src/backends/drm/virtual_egl_gbm_layer.h @@ -0,0 +1,61 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2022 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#pragma once +#include "drm_layer.h" + +#include +#include +#include +#include +#include +#include + +namespace KWaylandServer +{ +class SurfaceInterface; +} + +namespace KWin +{ + +class GbmSurface; +class GLTexture; +class EglGbmBackend; +class GbmBuffer; +class DrmVirtualOutput; + +class VirtualEglGbmLayer : public DrmOutputLayer +{ +public: + VirtualEglGbmLayer(EglGbmBackend *eglBackend, DrmVirtualOutput *output); + + void aboutToStartPainting(const QRegion &damagedRegion) override; + std::optional startRendering() override; + bool endRendering(const QRegion &damagedRegion) override; + bool scanout(SurfaceItem *surfaceItem) override; + + QRegion currentDamage() const override; + QSharedPointer texture() const override; + +private: + bool createGbmSurface(); + bool doesGbmSurfaceFit(GbmSurface *surf) const; + void destroyResources(); + + QPointer m_scanoutSurface; + QSharedPointer m_currentBuffer; + QRegion m_currentDamage; + QSharedPointer m_gbmSurface; + QSharedPointer m_oldGbmSurface; + + DrmVirtualOutput *const m_output; + EglGbmBackend *const m_eglBackend; +}; + +}