diff --git a/src/backends/drm/drm_abstract_output.cpp b/src/backends/drm/drm_abstract_output.cpp index 5d25cd29cb..1c3e8f0f71 100644 --- a/src/backends/drm/drm_abstract_output.cpp +++ b/src/backends/drm/drm_abstract_output.cpp @@ -9,6 +9,7 @@ #include "drm_abstract_output.h" #include "drm_gpu.h" #include "drm_backend.h" +#include "renderloop_p.h" namespace KWin { @@ -30,4 +31,14 @@ QRect DrmAbstractOutput::renderGeometry() const return geometry(); } +void DrmAbstractOutput::frameFailed() const +{ + RenderLoopPrivate::get(m_renderLoop)->notifyFrameFailed(); +} + +void DrmAbstractOutput::pageFlipped(std::chrono::nanoseconds timestamp) const +{ + RenderLoopPrivate::get(m_renderLoop)->notifyFrameCompleted(timestamp); +} + } diff --git a/src/backends/drm/drm_abstract_output.h b/src/backends/drm/drm_abstract_output.h index 3de990c619..f4b8b4c5c5 100644 --- a/src/backends/drm/drm_abstract_output.h +++ b/src/backends/drm/drm_abstract_output.h @@ -24,6 +24,8 @@ public: RenderLoop *renderLoop() const override; QRect renderGeometry() const override; + void frameFailed() const override; + void pageFlipped(std::chrono::nanoseconds timestamp) const override; protected: friend class DrmGpu; diff --git a/src/backends/drm/drm_display_device.h b/src/backends/drm/drm_display_device.h index 29a8d160b4..a974012312 100644 --- a/src/backends/drm/drm_display_device.h +++ b/src/backends/drm/drm_display_device.h @@ -17,6 +17,7 @@ namespace KWin class DrmBuffer; class DrmGpu; +class DrmLayer; class DrmDisplayDevice { @@ -26,7 +27,11 @@ public: DrmGpu *gpu() const; - virtual bool present(const QSharedPointer &buffer, QRegion damagedRegion) = 0; + 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; @@ -34,6 +39,7 @@ public: virtual QVector supportedModifiers(uint32_t drmFormat) const = 0; virtual int maxBpc() const = 0; virtual QRect renderGeometry() const = 0; + virtual DrmLayer *outputLayer() const = 0; protected: DrmGpu *const m_gpu; diff --git a/src/backends/drm/drm_gpu.cpp b/src/backends/drm/drm_gpu.cpp index 01f9e88453..183b700986 100644 --- a/src/backends/drm/drm_gpu.cpp +++ b/src/backends/drm/drm_gpu.cpp @@ -309,9 +309,9 @@ bool DrmGpu::updateOutputs() if (testPendingConfiguration()) { for (const auto &pipeline : qAsConst(m_pipelines)) { pipeline->applyPendingChanges(); - if (!pipeline->pending.crtc && pipeline->output()) { + if (const auto drmOutput = dynamic_cast(pipeline->displayDevice()); drmOutput && !pipeline->pending.crtc) { pipeline->pending.enabled = false; - pipeline->output()->setEnabled(false); + drmOutput->setEnabled(false); } } } else { @@ -421,6 +421,9 @@ bool DrmGpu::testPipelines() // pipelines that are enabled but not active need to be activated for the test QVector inactivePipelines; for (const auto &pipeline : qAsConst(m_pipelines)) { + if (!pipeline->pending.layer) { + pipeline->pending.layer = m_renderBackend->createLayer(pipeline->displayDevice()); + } if (!pipeline->pending.active) { pipeline->pending.active = true; inactivePipelines << pipeline; @@ -553,6 +556,7 @@ void DrmGpu::removeOutput(DrmOutput *output) qCDebug(KWIN_DRM) << "Removing output" << output; m_drmOutputs.removeOne(output); m_pipelines.removeOne(output->pipeline()); + output->pipeline()->pending.layer.reset(); m_outputs.removeOne(output); Q_EMIT outputRemoved(output); delete output; @@ -560,12 +564,17 @@ void DrmGpu::removeOutput(DrmOutput *output) EglGbmBackend *DrmGpu::eglBackend() const { - return m_eglBackend; + return dynamic_cast(m_renderBackend); } -void DrmGpu::setEglBackend(EglGbmBackend *eglBackend) +DrmRenderBackend *DrmGpu::renderBackend() const { - m_eglBackend = eglBackend; + return m_renderBackend; +} + +void DrmGpu::setRenderBackend(DrmRenderBackend *backend) +{ + m_renderBackend = backend; } DrmBackend *DrmGpu::platform() const { @@ -661,6 +670,7 @@ void DrmGpu::removeLeaseOutput(DrmLeaseOutput *output) qCDebug(KWIN_DRM) << "Removing leased output" << output; m_leaseOutputs.removeOne(output); m_pipelines.removeOne(output->pipeline()); + output->pipeline()->pending.layer.reset(); delete output; } @@ -740,10 +750,10 @@ bool DrmGpu::maybeModeset() } const bool ok = DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::CommitModeset, unusedObjects()); for (DrmPipeline *pipeline : qAsConst(pipelines)) { - if (pipeline->modesetPresentPending() && pipeline->output()) { + if (pipeline->modesetPresentPending()) { pipeline->resetModesetPresentPending(); if (!ok) { - pipeline->output()->presentFailed(); + pipeline->displayDevice()->frameFailed(); } } } diff --git a/src/backends/drm/drm_gpu.h b/src/backends/drm/drm_gpu.h index 9a065d7699..48f06993e0 100644 --- a/src/backends/drm/drm_gpu.h +++ b/src/backends/drm/drm_gpu.h @@ -41,6 +41,7 @@ class DrmPipeline; class DrmAbstractOutput; class DrmVirtualOutput; class DrmLeaseOutput; +class DrmRenderBackend; class DrmGpu : public QObject { @@ -59,6 +60,7 @@ public: gbm_device *gbmDevice() const; EGLDisplay eglDisplay() const; EglGbmBackend *eglBackend() const; + DrmRenderBackend *renderBackend() const; DrmBackend *platform() const; /** * Returns the clock from which presentation timestamps are sourced. The returned value @@ -71,7 +73,7 @@ public: const QVector pipelines() const; void setEglDisplay(EGLDisplay display); - void setEglBackend(EglGbmBackend *eglBackend); + void setRenderBackend(DrmRenderBackend *backend); bool updateOutputs(); @@ -120,7 +122,7 @@ private: clockid_t m_presentationClock; gbm_device* m_gbmDevice; EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; - QPointer m_eglBackend; + DrmRenderBackend *m_renderBackend; DrmBackend* const m_platform; QVector m_planes; diff --git a/src/backends/drm/drm_layer.h b/src/backends/drm/drm_layer.h index f1f7c69333..5dd0e0c68e 100644 --- a/src/backends/drm/drm_layer.h +++ b/src/backends/drm/drm_layer.h @@ -41,6 +41,9 @@ public: virtual QSharedPointer testBuffer() = 0; virtual QSharedPointer currentBuffer() const = 0; + virtual QRegion currentDamage() const = 0; + virtual bool hasDirectScanoutBuffer() const = 0; + virtual DrmDisplayDevice *displayDevice() const = 0; }; diff --git a/src/backends/drm/drm_lease_output.cpp b/src/backends/drm/drm_lease_output.cpp index 455f562211..ed7c80125b 100644 --- a/src/backends/drm/drm_lease_output.cpp +++ b/src/backends/drm/drm_lease_output.cpp @@ -32,11 +32,13 @@ DrmLeaseOutput::DrmLeaseOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDe , DrmDisplayDevice(pipeline->gpu()) , m_pipeline(pipeline) { + m_pipeline->setDisplayDevice(this); qCDebug(KWIN_DRM) << "offering connector" << m_pipeline->connector()->id() << "for lease"; } DrmLeaseOutput::~DrmLeaseOutput() { + m_pipeline->setDisplayDevice(nullptr); qCDebug(KWIN_DRM) << "revoking lease offer for connector" << m_pipeline->connector()->id(); } @@ -76,10 +78,13 @@ DrmPipeline *DrmLeaseOutput::pipeline() const return m_pipeline; } -bool DrmLeaseOutput::present(const QSharedPointer &buffer, QRegion damagedRegion) +bool DrmLeaseOutput::present() +{ + return false; +} + +bool DrmLeaseOutput::testScanout() { - Q_UNUSED(buffer) - Q_UNUSED(damagedRegion) return false; } @@ -122,4 +127,18 @@ QRect DrmLeaseOutput::renderGeometry() const return QRect(QPoint(), m_pipeline->sourceSize()); } +DrmLayer *DrmLeaseOutput::outputLayer() const +{ + return m_pipeline->pending.layer.data(); +} + +void DrmLeaseOutput::frameFailed() const +{ +} + +void DrmLeaseOutput::pageFlipped(std::chrono::nanoseconds timestamp) const +{ + Q_UNUSED(timestamp) +} + } diff --git a/src/backends/drm/drm_lease_output.h b/src/backends/drm/drm_lease_output.h index bd8d1cc847..90a6099d02 100644 --- a/src/backends/drm/drm_lease_output.h +++ b/src/backends/drm/drm_lease_output.h @@ -40,7 +40,7 @@ public: KWaylandServer::DrmLeaseV1Interface *lease() const; DrmPipeline *pipeline() const; - bool present(const QSharedPointer &buffer, QRegion damagedRegion) override; + bool present() override; DrmPlane::Transformations softwareTransforms() const override; QSize bufferSize() const override; QSize sourceSize() const override; @@ -48,6 +48,10 @@ public: QVector supportedModifiers(uint32_t drmFormat) const override; int maxBpc() const override; QRect renderGeometry() const override; + DrmLayer *outputLayer() const override; + bool testScanout() override; + void frameFailed() const override; + void pageFlipped(std::chrono::nanoseconds timestamp) const override; private: DrmPipeline *m_pipeline; diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index e8b9458b41..662e25ab88 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -26,6 +26,7 @@ #include "waylandoutputconfig.h" #include "dumb_swapchain.h" #include "cursor.h" +#include "drm_layer.h" // Qt #include #include @@ -45,7 +46,7 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline) , m_pipeline(pipeline) , m_connector(pipeline->connector()) { - m_pipeline->setOutput(this); + m_pipeline->setDisplayDevice(this); const auto conn = m_pipeline->connector(); m_renderLoop->setRefreshRate(m_pipeline->pending.mode->refreshRate()); setSubPixelInternal(conn->subpixel()); @@ -78,7 +79,7 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline) DrmOutput::~DrmOutput() { - m_pipeline->setOutput(nullptr); + m_pipeline->setDisplayDevice(nullptr); } static bool isCursorSpriteCompatible(const QImage *buffer, const QImage *sprite) @@ -318,12 +319,8 @@ void DrmOutput::updateModes() } } -bool DrmOutput::present(const QSharedPointer &buffer, QRegion damagedRegion) +bool DrmOutput::present() { - if (!buffer || buffer->bufferId() == 0) { - presentFailed(); - return false; - } RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop); if (m_pipeline->pending.syncMode != renderLoopPrivate->presentMode) { m_pipeline->pending.syncMode = renderLoopPrivate->presentMode; @@ -334,14 +331,21 @@ bool DrmOutput::present(const QSharedPointer &buffer, QRegion damaged setVrrPolicy(RenderLoop::VrrPolicy::Never); } } - if (m_pipeline->present(buffer)) { - Q_EMIT outputChange(damagedRegion); + if (m_pipeline->present()) { + Q_EMIT outputChange(m_pipeline->pending.layer->currentDamage()); return true; } else { + qCWarning(KWIN_DRM) << "Presentation failed!" << strerror(errno); + frameFailed(); return false; } } +bool DrmOutput::testScanout() +{ + return m_pipeline->testScanout(); +} + int DrmOutput::gammaRampSize() const { return m_pipeline->pending.crtc ? m_pipeline->pending.crtc->gammaRampSize() : 256; @@ -446,16 +450,6 @@ void DrmOutput::revertQueuedChanges() m_pipeline->revertPendingChanges(); } -void DrmOutput::pageFlipped(std::chrono::nanoseconds timestamp) -{ - RenderLoopPrivate::get(m_renderLoop)->notifyFrameCompleted(timestamp); -} - -void DrmOutput::presentFailed() -{ - RenderLoopPrivate::get(m_renderLoop)->notifyFrameFailed(); -} - int DrmOutput::maxBpc() const { auto prop = m_connector->getProp(DrmConnector::PropertyIndex::MaxBpc); @@ -477,4 +471,9 @@ DrmPlane::Transformations DrmOutput::softwareTransforms() const } } +DrmLayer *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 cf30ec2afd..46bc9fab36 100644 --- a/src/backends/drm/drm_output.h +++ b/src/backends/drm/drm_output.h @@ -43,25 +43,24 @@ public: DrmOutput(DrmPipeline *pipeline); ~DrmOutput() override; - bool present(const QSharedPointer &buffer, QRegion damagedRegion) override; - DrmConnector *connector() const; 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; bool queueChanges(const WaylandOutputConfig &config); void applyQueuedChanges(const WaylandOutputConfig &config); void revertQueuedChanges(); void updateModes(); - void pageFlipped(std::chrono::nanoseconds timestamp); - void presentFailed(); bool usesSoftwareCursor() const override; private: diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp index 115f424017..d40d6556de 100644 --- a/src/backends/drm/drm_pipeline.cpp +++ b/src/backends/drm/drm_pipeline.cpp @@ -23,6 +23,7 @@ #include "drm_backend.h" #include "egl_gbm_backend.h" #include "drm_buffer_gbm.h" +#include "drm_layer.h" #include #include @@ -31,30 +32,37 @@ namespace KWin { DrmPipeline::DrmPipeline(DrmConnector *conn) - : m_output(nullptr) + : m_displayDevice(nullptr) , m_connector(conn) { } DrmPipeline::~DrmPipeline() { - m_output = nullptr; if (m_pageflipPending && m_current.crtc) { pageFlipped({}); } } -bool DrmPipeline::present(const QSharedPointer &buffer) +bool DrmPipeline::testScanout() +{ + // TODO make the modeset check only be tested at most once per scanout cycle + if (gpu()->needsModeset()) { + return false; + } + if (gpu()->atomicModeSetting()) { + return commitPipelines({this}, CommitMode::Test); + } else { + // no other way to test than to do it. + // As we only have a maximum of one test per scanout cycle, this is fine + return presentLegacy(); + } +} + +bool DrmPipeline::present() { Q_ASSERT(pending.crtc); - Q_ASSERT(buffer); - m_primaryBuffer = buffer; - // with direct scanout disallow modesets, calling presentFailed() and logging warnings - const bool directScanout = isBufferForDirectScanout(); if (gpu()->needsModeset()) { - if (directScanout) { - return false; - } m_modesetPresentPending = true; return gpu()->maybeModeset(); } @@ -72,35 +80,22 @@ bool DrmPipeline::present(const QSharedPointer &buffer) } } if (!commitPipelines({this}, CommitMode::Commit)) { - if (directScanout) { - return false; - } - qCWarning(KWIN_DRM) << "Atomic present failed!" << strerror(errno); printDebugInfo(); - if (m_output) { - m_output->presentFailed(); - } return false; } } } else { + if (pending.layer->hasDirectScanoutBuffer()) { + // already presented + return true; + } if (!presentLegacy()) { - qCWarning(KWIN_DRM) << "Present failed!" << strerror(errno); - if (m_output) { - m_output->presentFailed(); - } return false; } } return true; } -bool DrmPipeline::isBufferForDirectScanout() const -{ - const auto buf = dynamic_cast(m_primaryBuffer.data()); - return buf && buf->clientBuffer(); -} - bool DrmPipeline::commitPipelines(const QVector &pipelines, CommitMode mode, const QVector &unusedObjects) { Q_ASSERT(!pipelines.isEmpty()); @@ -133,7 +128,7 @@ bool DrmPipeline::commitPipelinesAtomic(const QVector &pipelines, return false; }; for (const auto &pipeline : pipelines) { - if (!pipeline->checkTestBuffer()) { + if (!pipeline->pending.layer->testBuffer()) { qCWarning(KWIN_DRM) << "Checking test buffer failed for" << mode; return failed(); } @@ -197,8 +192,9 @@ bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags) pending.crtc->setPending(DrmCrtc::PropertyIndex::VrrEnabled, pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive); pending.crtc->setPending(DrmCrtc::PropertyIndex::Gamma_LUT, pending.gamma ? pending.gamma->blobId() : 0); const auto modeSize = pending.mode->size(); - pending.crtc->primaryPlane()->set(QPoint(0, 0), m_primaryBuffer ? m_primaryBuffer->size() : bufferSize(), QPoint(0, 0), modeSize); - pending.crtc->primaryPlane()->setBuffer(activePending() ? m_primaryBuffer.get() : nullptr); + const auto buffer = pending.layer->currentBuffer().data(); + pending.crtc->primaryPlane()->set(QPoint(0, 0), buffer ? buffer->size() : bufferSize(), QPoint(0, 0), modeSize); + pending.crtc->primaryPlane()->setBuffer(activePending() ? buffer : nullptr); if (pending.crtc->cursorPlane()) { pending.crtc->cursorPlane()->set(QPoint(0, 0), gpu()->cursorSize(), pending.cursorPos, gpu()->cursorSize()); @@ -271,10 +267,6 @@ uint32_t DrmPipeline::calculateUnderscan() void DrmPipeline::atomicCommitFailed() { - if (m_oldTestBuffer) { - m_primaryBuffer = m_oldTestBuffer; - m_oldTestBuffer = nullptr; - } m_connector->rollbackPending(); if (pending.crtc) { pending.crtc->rollbackPending(); @@ -287,7 +279,6 @@ void DrmPipeline::atomicCommitFailed() void DrmPipeline::atomicCommitSuccessful(CommitMode mode) { - m_oldTestBuffer = nullptr; m_connector->commitPending(); if (pending.crtc) { pending.crtc->commitPending(); @@ -303,7 +294,7 @@ void DrmPipeline::atomicCommitSuccessful(CommitMode mode) m_connector->commit(); if (pending.crtc) { pending.crtc->commit(); - pending.crtc->primaryPlane()->setNext(m_primaryBuffer); + pending.crtc->primaryPlane()->setNext(pending.layer->currentBuffer()); pending.crtc->primaryPlane()->commit(); if (pending.crtc->cursorPlane()) { pending.crtc->cursorPlane()->setNext(pending.cursorBo); @@ -317,32 +308,6 @@ void DrmPipeline::atomicCommitSuccessful(CommitMode mode) } } -bool DrmPipeline::checkTestBuffer() -{ - const auto backend = gpu()->eglBackend(); - if (!pending.crtc || (!(backend && m_output) && m_primaryBuffer && m_primaryBuffer->size() == bufferSize()) || isBufferForDirectScanout()) { - return true; - } - QSharedPointer buffer; - if (backend && m_output) { - buffer = backend->testBuffer(m_output); - } else if (backend && gpu()->gbmDevice()) { - gbm_bo *bo = gbm_bo_create(gpu()->gbmDevice(), bufferSize().width(), bufferSize().height(), DRM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!bo) { - return false; - } - buffer = QSharedPointer::create(gpu(), bo, nullptr); - } else { - buffer = QSharedPointer::create(gpu(), bufferSize(), DRM_FORMAT_XRGB8888); - } - if (buffer && buffer->bufferId()) { - m_oldTestBuffer = m_primaryBuffer; - m_primaryBuffer = buffer; - return true; - } - return false; -} - bool DrmPipeline::setCursor(const QSharedPointer &buffer, const QPoint &hotspot) { if (pending.cursorBo == buffer && pending.cursorHotspot == hotspot) { @@ -360,8 +325,8 @@ bool DrmPipeline::setCursor(const QSharedPointer &buffer, const Q } if (result) { m_next = pending; - if (m_output && (visibleBefore || isCursorVisible())) { - m_output->renderLoop()->scheduleRepaint(); + if (const auto drmOutput = dynamic_cast(m_displayDevice); drmOutput && (visibleBefore || isCursorVisible())) { + drmOutput->renderLoop()->scheduleRepaint(); } } else { pending = m_next; @@ -385,8 +350,8 @@ bool DrmPipeline::moveCursor(QPoint pos) } if (result) { m_next = pending; - if (m_output && (visibleBefore || isCursorVisible())) { - m_output->renderLoop()->scheduleRepaint(); + if (const auto drmOutput = dynamic_cast(m_displayDevice); drmOutput && (visibleBefore || isCursorVisible())) { + drmOutput->renderLoop()->scheduleRepaint(); } } else { pending = m_next; @@ -446,19 +411,12 @@ void DrmPipeline::pageFlipped(std::chrono::nanoseconds timestamp) m_current.crtc->cursorPlane()->flipBuffer(); } m_pageflipPending = false; - if (m_output) { - m_output->pageFlipped(timestamp); - } + m_displayDevice->pageFlipped(timestamp); } -void DrmPipeline::setOutput(DrmOutput *output) +void DrmPipeline::setDisplayDevice(DrmDisplayDevice *device) { - m_output = output; -} - -DrmOutput *DrmPipeline::output() const -{ - return m_output; + m_displayDevice = device; } static const QMap> legacyFormats = { @@ -541,6 +499,11 @@ DrmCrtc *DrmPipeline::currentCrtc() const return m_current.crtc; } +DrmDisplayDevice *DrmPipeline::displayDevice() const +{ + return m_displayDevice; +} + DrmGammaRamp::DrmGammaRamp(DrmGpu *gpu, const GammaRamp &lut) : m_gpu(gpu) , m_lut(lut) diff --git a/src/backends/drm/drm_pipeline.h b/src/backends/drm/drm_pipeline.h index ecc30e8164..11fda35849 100644 --- a/src/backends/drm/drm_pipeline.h +++ b/src/backends/drm/drm_pipeline.h @@ -31,6 +31,8 @@ class DrmBuffer; class DrmDumbBuffer; class GammaRamp; class DrmConnectorMode; +class DrmLayer; +class DrmDisplayDevice; class DrmGammaRamp { @@ -60,7 +62,8 @@ public: * tests the pending commit first and commits it if the test passes * if the test fails, there is a guarantee for no lasting changes */ - bool present(const QSharedPointer &buffer); + bool present(); + bool testScanout(); bool needsModeset() const; void applyPendingChanges(); @@ -91,8 +94,8 @@ public: QVector supportedModifiers(uint32_t drmFormat) const; QMap> supportedFormats() const; - void setOutput(DrmOutput *output); - DrmOutput *output() const; + void setDisplayDevice(DrmDisplayDevice *device); + DrmDisplayDevice *displayDevice() const; struct State { DrmCrtc *crtc = nullptr; @@ -104,6 +107,8 @@ public: RenderLoopPrivate::SyncMode syncMode = RenderLoopPrivate::SyncMode::Fixed; QSharedPointer gamma; + QSharedPointer layer; + QPoint cursorPos; QPoint cursorHotspot; QSharedPointer cursorBo; @@ -124,7 +129,6 @@ public: static bool commitPipelines(const QVector &pipelines, CommitMode mode, const QVector &unusedObjects = {}); private: - bool checkTestBuffer(); bool activePending() const; bool isCursorVisible() const; bool isBufferForDirectScanout() const; @@ -150,11 +154,9 @@ private: static void printFlags(uint32_t flags); static void printProps(DrmObject *object, PrintMode mode); - DrmOutput *m_output = nullptr; + DrmDisplayDevice *m_displayDevice = nullptr; DrmConnector *m_connector = nullptr; - QSharedPointer m_primaryBuffer; - QSharedPointer m_oldTestBuffer; bool m_pageflipPending = false; bool m_modesetPresentPending = false; diff --git a/src/backends/drm/drm_pipeline_legacy.cpp b/src/backends/drm/drm_pipeline_legacy.cpp index d77d13ca49..2b5f1c19eb 100644 --- a/src/backends/drm/drm_pipeline_legacy.cpp +++ b/src/backends/drm/drm_pipeline_legacy.cpp @@ -13,6 +13,7 @@ #include "drm_object_crtc.h" #include "drm_object_connector.h" #include "logging.h" +#include "drm_layer.h" #include @@ -21,33 +22,32 @@ namespace KWin bool DrmPipeline::presentLegacy() { - if ((!pending.crtc->current() || pending.crtc->current()->needsModeChange(m_primaryBuffer.get())) && !legacyModeset()) { + const auto buffer = pending.layer->currentBuffer(); + if ((!pending.crtc->current() || pending.crtc->current()->needsModeChange(buffer.get())) && !legacyModeset()) { return false; } - if (drmModePageFlip(gpu()->fd(), pending.crtc->id(), m_primaryBuffer ? m_primaryBuffer->bufferId() : 0, DRM_MODE_PAGE_FLIP_EVENT, nullptr) != 0) { - qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno) << m_primaryBuffer; + if (drmModePageFlip(gpu()->fd(), pending.crtc->id(), buffer ? buffer->bufferId() : 0, DRM_MODE_PAGE_FLIP_EVENT, nullptr) != 0) { + qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno) << buffer; return false; } m_pageflipPending = true; - pending.crtc->setNext(m_primaryBuffer); + pending.crtc->setNext(buffer); return true; } bool DrmPipeline::legacyModeset() { uint32_t connId = m_connector->id(); - if (!checkTestBuffer() || drmModeSetCrtc(gpu()->fd(), pending.crtc->id(), m_primaryBuffer->bufferId(), 0, 0, &connId, 1, pending.mode->nativeMode()) != 0) { + if (!pending.layer->testBuffer() || drmModeSetCrtc(gpu()->fd(), pending.crtc->id(), pending.layer->currentBuffer()->bufferId(), 0, 0, &connId, 1, pending.mode->nativeMode()) != 0) { qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno); pending = m_next; - m_primaryBuffer = m_oldTestBuffer; return false; } - m_oldTestBuffer = nullptr; // make sure the buffer gets kept alive, or the modeset gets reverted by the kernel if (pending.crtc->current()) { - pending.crtc->setNext(m_primaryBuffer); + pending.crtc->setNext(pending.layer->currentBuffer()); } else { - pending.crtc->setCurrent(m_primaryBuffer); + pending.crtc->setCurrent(pending.layer->currentBuffer()); } return true; } diff --git a/src/backends/drm/drm_qpainter_layer.cpp b/src/backends/drm/drm_qpainter_layer.cpp index 64f6231f80..40a7e198e3 100644 --- a/src/backends/drm/drm_qpainter_layer.cpp +++ b/src/backends/drm/drm_qpainter_layer.cpp @@ -35,6 +35,7 @@ std::optional DrmQPainterLayer::startRendering() bool DrmQPainterLayer::endRendering(const QRegion &damagedRegion) { + m_currentDamage = damagedRegion; m_swapchain->releaseBuffer(m_swapchain->currentBuffer(), damagedRegion); return true; } @@ -63,9 +64,19 @@ QSharedPointer DrmQPainterLayer::currentBuffer() const return m_swapchain ? m_swapchain->currentBuffer() : nullptr; } +QRegion DrmQPainterLayer::currentDamage() const +{ + return m_currentDamage; +} + DrmDisplayDevice *DrmQPainterLayer::displayDevice() const { return m_displayDevice; } +bool DrmQPainterLayer::hasDirectScanoutBuffer() const +{ + return false; +} + } diff --git a/src/backends/drm/drm_qpainter_layer.h b/src/backends/drm/drm_qpainter_layer.h index e308bf70b9..ef3f0c8250 100644 --- a/src/backends/drm/drm_qpainter_layer.h +++ b/src/backends/drm/drm_qpainter_layer.h @@ -23,12 +23,15 @@ public: bool scanout(SurfaceItem *surfaceItem) override; QSharedPointer testBuffer() override; QSharedPointer currentBuffer() const override; + QRegion currentDamage() const override; + bool hasDirectScanoutBuffer() const override; DrmDisplayDevice *displayDevice() const override; private: bool doesSwapchainFit() const; QSharedPointer m_swapchain; + QRegion m_currentDamage; DrmDisplayDevice *const m_displayDevice; }; diff --git a/src/backends/drm/drm_render_backend.h b/src/backends/drm/drm_render_backend.h new file mode 100644 index 0000000000..b04b40cee3 --- /dev/null +++ b/src/backends/drm/drm_render_backend.h @@ -0,0 +1,28 @@ +/* + 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 + +namespace KWin +{ + +class DrmLayer; +class DrmDisplayDevice; + +class DrmRenderBackend +{ +public: + virtual ~DrmRenderBackend() = default; + + virtual QSharedPointer createLayer(DrmDisplayDevice *displayDevice) const = 0; + +}; + +} diff --git a/src/backends/drm/drm_virtual_output.cpp b/src/backends/drm/drm_virtual_output.cpp index 87ee88dadd..191c2ecdea 100644 --- a/src/backends/drm/drm_virtual_output.cpp +++ b/src/backends/drm/drm_virtual_output.cpp @@ -14,9 +14,12 @@ #include "drm_gpu.h" #include "drm_backend.h" #include "logging.h" +#include "drm_layer.h" +#include "drm_render_backend.h" namespace KWin { + static int s_serial = 0; DrmVirtualOutput::DrmVirtualOutput(DrmGpu *gpu, const QSize &size) : DrmVirtualOutput(QString::number(s_serial++), gpu, size) @@ -40,20 +43,19 @@ DrmVirtualOutput::DrmVirtualOutput(const QString &name, DrmGpu *gpu, const QSize modes, QByteArray("EDID_") + name.toUtf8()); m_renderLoop->setRefreshRate(modes[m_modeIndex].refreshRate); + + m_layer = gpu->renderBackend()->createLayer(this); } DrmVirtualOutput::~DrmVirtualOutput() { } -bool DrmVirtualOutput::present(const QSharedPointer &buffer, QRegion damagedRegion) +bool DrmVirtualOutput::present() { - Q_UNUSED(damagedRegion) - - m_currentBuffer = buffer; m_vsyncMonitor->arm(); m_pageFlipPending = true; - Q_EMIT outputChange(damagedRegion); + Q_EMIT outputChange(m_layer->currentDamage()); return true; } @@ -119,4 +121,14 @@ DrmPlane::Transformations DrmVirtualOutput::softwareTransforms() const return DrmPlane::Transformation::Rotate0; } +DrmLayer *DrmVirtualOutput::outputLayer() const +{ + return m_layer.data(); +} + +bool DrmVirtualOutput::testScanout() +{ + return true; +} + } diff --git a/src/backends/drm/drm_virtual_output.h b/src/backends/drm/drm_virtual_output.h index 68cdd57ba6..6de8ed27e2 100644 --- a/src/backends/drm/drm_virtual_output.h +++ b/src/backends/drm/drm_virtual_output.h @@ -19,6 +19,7 @@ namespace KWin class SoftwareVsyncMonitor; class VirtualBackend; +class DrmLayer; class DrmVirtualOutput : public DrmAbstractOutput { @@ -28,7 +29,7 @@ public: DrmVirtualOutput(DrmGpu *gpu, const QSize &size); ~DrmVirtualOutput() override; - bool present(const QSharedPointer &buffer, QRegion damagedRegion) override; + bool present() override; QSize bufferSize() const override; QSize sourceSize() const override; @@ -39,13 +40,15 @@ public: int gammaRampSize() const override; bool setGammaRamp(const GammaRamp &gamma) override; DrmPlane::Transformations softwareTransforms() const override; + DrmLayer *outputLayer() const override; + bool testScanout() override; private: void vblank(std::chrono::nanoseconds timestamp); void setDpmsMode(DpmsMode mode) override; void updateEnablement(bool enable) override; - QSharedPointer m_currentBuffer; + 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 e73b56eacc..7d3764794f 100644 --- a/src/backends/drm/egl_gbm_backend.cpp +++ b/src/backends/drm/egl_gbm_backend.cpp @@ -49,20 +49,14 @@ EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend) : AbstractEglBackend(drmBackend->primaryGpu()->deviceId()) , m_backend(drmBackend) { - drmBackend->primaryGpu()->setEglBackend(this); - connect(m_backend, &DrmBackend::outputAdded, this, &EglGbmBackend::addOutput); - connect(m_backend, &DrmBackend::outputRemoved, this, &EglGbmBackend::removeOutput); + drmBackend->primaryGpu()->setRenderBackend(this); setIsDirectRendering(true); } EglGbmBackend::~EglGbmBackend() { cleanup(); -} - -void EglGbmBackend::cleanupSurfaces() -{ - m_surfaces.clear(); + m_backend->primaryGpu()->setRenderBackend(nullptr); } bool EglGbmBackend::initializeEgl() @@ -124,23 +118,9 @@ bool EglGbmBackend::initRenderingContext() if (!createContext() || !makeCurrent()) { return false; } - const auto outputs = m_backend->outputs(); - for (const auto &output : outputs) { - addOutput(output); - } return true; } -void EglGbmBackend::addOutput(AbstractOutput *output) { - auto drmOutput = static_cast(output); - m_surfaces.insert(drmOutput, QSharedPointer::create(m_backend->primaryGpu(), drmOutput)); -} - -void EglGbmBackend::removeOutput(AbstractOutput *output) -{ - m_surfaces.remove(output); -} - bool EglGbmBackend::initBufferConfigs() { const EGLint config_attribs[] = { @@ -250,9 +230,8 @@ static QVector regionToRects(const QRegion ®ion, DrmAbstractOutput *o void EglGbmBackend::aboutToStartPainting(AbstractOutput *output, const QRegion &damagedRegion) { - Q_ASSERT_X(output, "aboutToStartPainting", "not using per screen rendering"); - Q_ASSERT(m_surfaces.contains(output)); - const auto &surface = m_surfaces[output]; + 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); @@ -274,34 +253,34 @@ SurfaceTexture *EglGbmBackend::createSurfaceTextureWayland(SurfacePixmapWayland QRegion EglGbmBackend::beginFrame(AbstractOutput *output) { - Q_ASSERT(m_surfaces.contains(output)); - return m_surfaces[output]->startRendering().value_or(QRegion()); + return static_cast(output)->outputLayer()->startRendering().value_or(QRegion()); } void EglGbmBackend::endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damagedRegion) { - Q_ASSERT(m_surfaces.contains(output)); Q_UNUSED(renderedRegion) - m_surfaces[output]->endRendering(damagedRegion); - static_cast(output)->present(m_surfaces[output]->currentBuffer(), damagedRegion); + const auto drmOutput = static_cast(output); + drmOutput->outputLayer()->endRendering(damagedRegion); + drmOutput->present(); } bool EglGbmBackend::scanout(AbstractOutput *output, SurfaceItem *surfaceItem) { - return m_surfaces[output]->scanout(surfaceItem); -} - -QSharedPointer EglGbmBackend::testBuffer(DrmAbstractOutput *output) -{ - return m_surfaces[output]->testBuffer(); + const auto drmOutput = static_cast(output); + if (drmOutput->outputLayer()->scanout(surfaceItem)) { + drmOutput->present(); + return true; + } else { + return false; + } } QSharedPointer EglGbmBackend::textureForOutput(AbstractOutput *output) const { - Q_ASSERT(m_surfaces.contains(output)); - return m_surfaces[output]->texture(); + const auto drmOutput = static_cast(output); + return static_cast(drmOutput->outputLayer())->texture(); } GbmFormat EglGbmBackend::gbmFormatForDrmFormat(uint32_t format) const @@ -352,6 +331,11 @@ EGLConfig EglGbmBackend::config(uint32_t format) const return m_configs[format]; } +QSharedPointer EglGbmBackend::createLayer(DrmDisplayDevice *displayDevice) const +{ + return QSharedPointer::create(m_backend->primaryGpu(), displayDevice); +} + bool operator==(const GbmFormat &lhs, const GbmFormat &rhs) { return lhs.drmFormat == rhs.drmFormat; diff --git a/src/backends/drm/egl_gbm_backend.h b/src/backends/drm/egl_gbm_backend.h index ac83e7c1db..7bea7ac0e0 100644 --- a/src/backends/drm/egl_gbm_backend.h +++ b/src/backends/drm/egl_gbm_backend.h @@ -6,9 +6,9 @@ SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef KWIN_EGL_GBM_BACKEND_H -#define KWIN_EGL_GBM_BACKEND_H +#pragma once #include "abstract_egl_backend.h" +#include "drm_render_backend.h" #include @@ -52,7 +52,7 @@ bool operator==(const GbmFormat &lhs, const GbmFormat &rhs); /** * @brief OpenGL Backend using Egl on a GBM surface. */ -class EglGbmBackend : public AbstractEglBackend +class EglGbmBackend : public AbstractEglBackend, public DrmRenderBackend { Q_OBJECT public: @@ -67,6 +67,7 @@ public: void init() override; bool scanout(AbstractOutput *output, SurfaceItem *surfaceItem) override; bool prefer10bpc() const override; + QSharedPointer createLayer(DrmDisplayDevice *displayDevice) const override; QSharedPointer textureForOutput(AbstractOutput *requestedOutput) const override; @@ -76,17 +77,13 @@ public: std::optional chooseFormat(DrmDisplayDevice *displyDevice) const; protected: - void cleanupSurfaces() override; void aboutToStartPainting(AbstractOutput *output, const QRegion &damage) override; private: bool initializeEgl(); bool initBufferConfigs(); bool initRenderingContext(); - void addOutput(AbstractOutput *output); - void removeOutput(AbstractOutput *output); - QMap> m_surfaces; DrmBackend *m_backend; QVector m_formats; QMap m_configs; @@ -95,5 +92,3 @@ private: }; } // namespace - -#endif diff --git a/src/backends/drm/egl_gbm_layer.cpp b/src/backends/drm/egl_gbm_layer.cpp index e3eefb2b11..aa4e7cc864 100644 --- a/src/backends/drm/egl_gbm_layer.cpp +++ b/src/backends/drm/egl_gbm_layer.cpp @@ -50,6 +50,7 @@ EglGbmLayer::~EglGbmLayer() std::optional EglGbmLayer::startRendering() { + m_scanoutBuffer.reset(); // dmabuf feedback if (!m_scanoutCandidate.attemptedThisFrame && m_scanoutCandidate.surface) { if (const auto feedback = m_scanoutCandidate.surface->dmabufFeedbackV1()) { @@ -117,13 +118,27 @@ bool EglGbmLayer::endRendering(const QRegion &damagedRegion) const auto buffer = m_gbmSurface->swapBuffersForDrm(damagedRegion); if (buffer) { m_currentBuffer = buffer; + m_currentDamage = damagedRegion; } return buffer; } +QRegion EglGbmLayer::currentDamage() const +{ + return m_currentDamage; +} + QSharedPointer EglGbmLayer::testBuffer() { if (!m_currentBuffer || !doesGbmSurfaceFit(m_gbmSurface.data())) { + if (doesGbmSurfaceFit(m_oldGbmSurface.data())) { + // re-use old surface and buffer without rendering + m_gbmSurface = m_oldGbmSurface; + if (m_gbmSurface->currentBuffer()) { + m_currentBuffer = m_gbmSurface->currentDrmBuffer(); + return m_currentBuffer; + } + } if (!renderTestBuffer() && m_importMode == MultiGpuImportMode::DumbBufferXrgb8888) { // try multi-gpu import again, this time with DRM_FORMAT_XRGB8888 renderTestBuffer(); @@ -401,10 +416,11 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem) } return false; } - const auto bo = QSharedPointer::create(m_displayDevice->gpu(), importedBuffer, buffer); - if (!bo->bufferId()) { + 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; } @@ -421,10 +437,12 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem) } else { damage = m_displayDevice->renderGeometry(); } - if (m_displayDevice->present(bo, damage)) { - m_currentBuffer = bo; + if (m_displayDevice->testScanout()) { + m_currentBuffer = m_scanoutBuffer; + m_currentDamage = damage; return true; } else { + m_scanoutBuffer.reset(); return false; } } @@ -462,7 +480,7 @@ void EglGbmLayer::sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer * QSharedPointer EglGbmLayer::currentBuffer() const { - return m_currentBuffer; + return m_scanoutBuffer ? m_scanoutBuffer : m_currentBuffer; } DrmDisplayDevice *EglGbmLayer::displayDevice() const @@ -480,4 +498,9 @@ 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 fe31ae2032..26b02a2de0 100644 --- a/src/backends/drm/egl_gbm_layer.h +++ b/src/backends/drm/egl_gbm_layer.h @@ -44,6 +44,8 @@ public: bool scanout(SurfaceItem *surfaceItem) override; QSharedPointer testBuffer() override; QSharedPointer currentBuffer() const override; + bool hasDirectScanoutBuffer() const override; + QRegion currentDamage() const override; QSharedPointer texture() const; DrmDisplayDevice *displayDevice() const override; @@ -76,7 +78,9 @@ private: bool attemptedThisFrame = false; } m_scanoutCandidate; + QSharedPointer m_scanoutBuffer; QSharedPointer m_currentBuffer; + QRegion m_currentDamage; QSharedPointer m_gbmSurface; QSharedPointer m_oldGbmSurface; QSharedPointer m_shadowBuffer; diff --git a/src/backends/drm/scene_qpainter_drm_backend.cpp b/src/backends/drm/scene_qpainter_drm_backend.cpp index f645f822ed..10f1cb4164 100644 --- a/src/backends/drm/scene_qpainter_drm_backend.cpp +++ b/src/backends/drm/scene_qpainter_drm_backend.cpp @@ -23,29 +23,37 @@ DrmQPainterBackend::DrmQPainterBackend(DrmBackend *backend) : QPainterBackend() , m_backend(backend) { - connect(m_backend, &DrmBackend::outputEnabled, this, [this] (const auto output) { - m_swapchains[output] = QSharedPointer::create(static_cast(output)); - }); - connect(m_backend, &DrmBackend::outputDisabled, this, [this] (const auto output) { - m_swapchains.remove(output); - }); + m_backend->primaryGpu()->setRenderBackend(this); +} + +DrmQPainterBackend::~DrmQPainterBackend() +{ + m_backend->primaryGpu()->setRenderBackend(nullptr); } QImage *DrmQPainterBackend::bufferForScreen(AbstractOutput *output) { - return static_cast(m_swapchains[output]->currentBuffer().data())->image(); + const auto drmOutput = static_cast(output); + return static_cast(drmOutput->outputLayer()->currentBuffer().data())->image(); } QRegion DrmQPainterBackend::beginFrame(AbstractOutput *output) { - return m_swapchains[output]->startRendering().value_or(QRegion()); + const auto drmOutput = static_cast(output); + return drmOutput->outputLayer()->startRendering().value_or(QRegion()); } void DrmQPainterBackend::endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damage) { Q_UNUSED(renderedRegion) - m_swapchains[output]->endRendering(damage); - static_cast(output)->present(m_swapchains[output]->currentBuffer(), output->geometry()); + const auto drmOutput = static_cast(output); + drmOutput->outputLayer()->endRendering(damage); + static_cast(output)->present(); +} + +QSharedPointer DrmQPainterBackend::createLayer(DrmDisplayDevice *displayDevice) const +{ + return QSharedPointer::create(displayDevice); } } diff --git a/src/backends/drm/scene_qpainter_drm_backend.h b/src/backends/drm/scene_qpainter_drm_backend.h index 71c6b6b34d..d32ef13d90 100644 --- a/src/backends/drm/scene_qpainter_drm_backend.h +++ b/src/backends/drm/scene_qpainter_drm_backend.h @@ -6,16 +6,15 @@ SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef KWIN_SCENE_QPAINTER_DRM_BACKEND_H -#define KWIN_SCENE_QPAINTER_DRM_BACKEND_H +#pragma once #include "qpainterbackend.h" +#include "drm_render_backend.h" +#include "dumb_swapchain.h" #include #include #include -#include "dumb_swapchain.h" - namespace KWin { @@ -23,20 +22,19 @@ class DrmBackend; class DrmAbstractOutput; class DrmQPainterLayer; -class DrmQPainterBackend : public QPainterBackend +class DrmQPainterBackend : public QPainterBackend, public DrmRenderBackend { Q_OBJECT public: DrmQPainterBackend(DrmBackend *backend); + ~DrmQPainterBackend(); 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) const override; private: - QMap> m_swapchains; DrmBackend *m_backend; }; } - -#endif