diff --git a/src/backends/drm/drm_backend.cpp b/src/backends/drm/drm_backend.cpp index 4ece2296f1..238d4b6587 100644 --- a/src/backends/drm/drm_backend.cpp +++ b/src/backends/drm/drm_backend.cpp @@ -763,6 +763,9 @@ bool DrmBackend::applyOutputChanges(const WaylandOutputConfig &config) } }; updateCursor(); + if (auto compositor = Compositor::self()) { + compositor->addRepaintFull(); + } return true; } diff --git a/src/backends/drm/drm_buffer_gbm.cpp b/src/backends/drm/drm_buffer_gbm.cpp index d0ca248e71..15c832b902 100644 --- a/src/backends/drm/drm_buffer_gbm.cpp +++ b/src/backends/drm/drm_buffer_gbm.cpp @@ -83,6 +83,11 @@ bool GbmBuffer::map(uint32_t flags) return m_data; } +KWaylandServer::ClientBuffer *GbmBuffer::clientBuffer() const +{ + return m_clientBuffer; +} + DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, GbmSurface *surface, gbm_bo *bo) : DrmBuffer(gpu, gbm_bo_get_format(bo), gbm_bo_get_modifier(bo)), GbmBuffer(surface, bo) diff --git a/src/backends/drm/drm_buffer_gbm.h b/src/backends/drm/drm_buffer_gbm.h index d90efaf836..1e3ae8a022 100644 --- a/src/backends/drm/drm_buffer_gbm.h +++ b/src/backends/drm/drm_buffer_gbm.h @@ -47,6 +47,7 @@ public: uint32_t stride() const { return m_stride; } + KWaylandServer::ClientBuffer *clientBuffer() const; protected: GbmSurface *m_surface = nullptr; diff --git a/src/backends/drm/drm_gpu.cpp b/src/backends/drm/drm_gpu.cpp index cc4f141308..2cdf43e42a 100644 --- a/src/backends/drm/drm_gpu.cpp +++ b/src/backends/drm/drm_gpu.cpp @@ -347,7 +347,30 @@ bool DrmGpu::checkCrtcAssignment(QVector connectors, QVectormodelName() << "without a crtc"; conn->pipeline()->pending.crtc = nullptr; } - return DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test); + // non-desktop outputs need to be tested when they would be enabled so that they can be used if leased + QVector leasePipelines; + for (const auto &output : qAsConst(m_leaseOutputs)) { + if (!output->lease()) { + output->pipeline()->pending.active = true; + leasePipelines << output->pipeline(); + } + } + bool test = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test); + if (!leasePipelines.isEmpty() && test) { + // non-desktop outputs should be disabled for normal usage + for (const auto &pipeline : qAsConst(leasePipelines)) { + pipeline->pending.active = false; + } + bool ret = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test); + if (ret) { + for (const auto &pipeline : qAsConst(leasePipelines)) { + pipeline->applyPendingChanges(); + } + } + return ret; + } else { + return test; + } } auto connector = connectors.takeFirst(); auto pipeline = connector->pipeline(); @@ -424,7 +447,7 @@ void DrmGpu::waitIdle() m_socketNotifier->setEnabled(false); while (true) { const bool idle = std::all_of(m_drmOutputs.constBegin(), m_drmOutputs.constEnd(), [](DrmOutput *output){ - return !output->m_pageFlipPending; + return !output->pipeline()->pageflipPending(); }); if (idle) { break; @@ -471,10 +494,10 @@ static std::chrono::nanoseconds convertTimestamp(clockid_t sourceClock, clockid_ return convertTimestamp(targetCurrentTime) - delta; } -static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) +void DrmGpu::pageFlipHandler(int fd, unsigned int sequence, unsigned int sec, unsigned int usec, unsigned int crtc_id, void *user_data) { - Q_UNUSED(fd) - Q_UNUSED(frame) + Q_UNUSED(sequence) + Q_UNUSED(user_data) auto backend = dynamic_cast(kwinApp()->platform()); if (!backend) { return; @@ -483,29 +506,28 @@ static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsign if (!gpu) { return; } - auto output = static_cast(data); - if (!gpu->outputs().contains(output)) { - // output already got deleted - return; - } // The static_cast<> here are for a 32-bit environment where // sizeof(time_t) == sizeof(unsigned int) == 4 . Putting @p sec // into a time_t cuts off the most-significant bit (after the // year 2038), similarly long can't hold all the bits of an // unsigned multiplication. - std::chrono::nanoseconds timestamp = convertTimestamp(output->gpu()->presentationClock(), - CLOCK_MONOTONIC, + std::chrono::nanoseconds timestamp = convertTimestamp(gpu->presentationClock(), CLOCK_MONOTONIC, { static_cast(sec), static_cast(usec * 1000) }); if (timestamp == std::chrono::nanoseconds::zero()) { - qCDebug(KWIN_DRM, "Got invalid timestamp (sec: %u, usec: %u) on output %s", - sec, usec, qPrintable(output->name())); + qCDebug(KWIN_DRM, "Got invalid timestamp (sec: %u, usec: %u) on gpu %s", + sec, usec, qPrintable(gpu->devNode())); timestamp = std::chrono::steady_clock::now().time_since_epoch(); } - - output->pageFlipped(); - RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(output->renderLoop()); - renderLoopPrivate->notifyFrameCompleted(timestamp); + const auto pipelines = gpu->pipelines(); + auto it = std::find_if(pipelines.begin(), pipelines.end(), [crtc_id](const auto &pipeline) { + return pipeline->currentCrtc() && pipeline->currentCrtc()->id() == crtc_id; + }); + if (it == pipelines.end()) { + qCWarning(KWIN_DRM, "received invalid page flip event for crtc %u", crtc_id); + } else { + (*it)->pageFlipped(timestamp); + } } void DrmGpu::dispatchEvents() @@ -514,8 +536,8 @@ void DrmGpu::dispatchEvents() return; } drmEventContext context = {}; - context.version = 2; - context.page_flip_handler = pageFlipHandler; + context.version = 3; + context.page_flip_handler2 = pageFlipHandler; drmHandleEvent(m_fd, &context); } @@ -707,4 +729,30 @@ bool DrmGpu::isNVidia() const return m_isNVidia; } +bool DrmGpu::needsModeset() const +{ + return std::any_of(m_pipelines.constBegin(), m_pipelines.constEnd(), [](const auto &pipeline) { + return pipeline->needsModeset(); + }); +} + +bool DrmGpu::maybeModeset() +{ + auto pipelines = m_pipelines; + for (const auto &output : qAsConst(m_leaseOutputs)) { + if (output->lease()) { + pipelines.removeOne(output->pipeline()); + } + } + bool presentPendingForAll = std::all_of(pipelines.constBegin(), pipelines.constEnd(), [](const auto &pipeline) { + return pipeline->modesetPresentPending() || !pipeline->pending.active; + }); + if (presentPendingForAll) { + return DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::CommitModeset); + } else { + // commit only once all pipelines are ready for presentation + return true; + } +} + } diff --git a/src/backends/drm/drm_gpu.h b/src/backends/drm/drm_gpu.h index 986651e4bb..c15b839efa 100644 --- a/src/backends/drm/drm_gpu.h +++ b/src/backends/drm/drm_gpu.h @@ -83,6 +83,9 @@ public: DrmVirtualOutput *createVirtualOutput(const QString &name, const QSize &size, double scale, VirtualOutputMode mode); void removeVirtualOutput(DrmVirtualOutput *output); + bool needsModeset() const; + bool maybeModeset(); + Q_SIGNALS: void outputAdded(DrmAbstractOutput *output); void outputRemoved(DrmAbstractOutput *output); @@ -102,6 +105,8 @@ private: void handleLeaseRequest(KWaylandServer::DrmLeaseV1Interface *leaseRequest); void handleLeaseRevoked(KWaylandServer::DrmLeaseV1Interface *lease); + static void pageFlipHandler(int fd, unsigned int sequence, unsigned int sec, unsigned int usec, unsigned int crtc_id, void *user_data); + const int m_fd; const dev_t m_deviceId; const QString m_devNode; diff --git a/src/backends/drm/drm_object_connector.cpp b/src/backends/drm/drm_object_connector.cpp index d5336a7ac7..47228013d6 100644 --- a/src/backends/drm/drm_object_connector.cpp +++ b/src/backends/drm/drm_object_connector.cpp @@ -306,6 +306,9 @@ bool DrmConnector::vrrCapable() const bool DrmConnector::needsModeset() const { + if (!gpu()->atomicModeSetting()) { + return false; + } if (getProp(PropertyIndex::CrtcId)->needsCommit()) { return true; } diff --git a/src/backends/drm/drm_object_crtc.cpp b/src/backends/drm/drm_object_crtc.cpp index 3979b0b4f1..c1475b1636 100644 --- a/src/backends/drm/drm_object_crtc.cpp +++ b/src/backends/drm/drm_object_crtc.cpp @@ -54,6 +54,9 @@ drmModeModeInfo DrmCrtc::queryCurrentMode() bool DrmCrtc::needsModeset() const { + if (!gpu()->atomicModeSetting()) { + return false; + } return getProp(PropertyIndex::Active)->needsCommit() || getProp(PropertyIndex::ModeId)->needsCommit(); } diff --git a/src/backends/drm/drm_object_plane.cpp b/src/backends/drm/drm_object_plane.cpp index c432d8b87f..03fbf51b30 100644 --- a/src/backends/drm/drm_object_plane.cpp +++ b/src/backends/drm/drm_object_plane.cpp @@ -168,6 +168,9 @@ void DrmPlane::setBuffer(DrmBuffer *buffer) bool DrmPlane::needsModeset() const { + if (!gpu()->atomicModeSetting()) { + return false; + } auto rotation = getProp(PropertyIndex::Rotation); if (rotation && rotation->needsCommit()) { return true; diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index 23ee8cc9f5..57c92e8725 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -69,9 +69,6 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline) DrmOutput::~DrmOutput() { - if (m_pageFlipPending) { - pageFlipped(); - } m_pipeline->setOutput(nullptr); } @@ -264,7 +261,7 @@ bool DrmOutput::setDrmDpmsMode(DpmsMode mode) return true; } m_pipeline->pending.active = active; - if (DrmPipeline::commitPipelines({m_pipeline}, active ? DrmPipeline::CommitMode::Test : DrmPipeline::CommitMode::Commit)) { + if (DrmPipeline::commitPipelines({m_pipeline}, active ? DrmPipeline::CommitMode::Test : DrmPipeline::CommitMode::CommitModeset)) { m_pipeline->applyPendingChanges(); setDpmsModeInternal(mode); if (active) { @@ -370,13 +367,6 @@ bool DrmOutput::needsSoftwareTransformation() const return m_pipeline->pending.transformation != outputToPlaneTransform(transform()); } -void DrmOutput::pageFlipped() -{ - Q_ASSERT(m_pageFlipPending || !m_gpu->atomicModeSetting()); - m_pageFlipPending = false; - m_pipeline->pageFlipped(); -} - bool DrmOutput::present(const QSharedPointer &buffer, QRegion damagedRegion) { if (!buffer || buffer->bufferId() == 0) { @@ -393,7 +383,6 @@ bool DrmOutput::present(const QSharedPointer &buffer, QRegion damaged } } if (m_pipeline->present(buffer)) { - m_pageFlipPending = true; Q_EMIT outputChange(damagedRegion); return true; } else { @@ -494,4 +483,14 @@ 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(); +} + } diff --git a/src/backends/drm/drm_output.h b/src/backends/drm/drm_output.h index 796e31140f..fabbf5e827 100644 --- a/src/backends/drm/drm_output.h +++ b/src/backends/drm/drm_output.h @@ -20,6 +20,7 @@ #include #include #include +#include namespace KWin { @@ -47,7 +48,6 @@ public: bool moveCursor() override; bool present(const QSharedPointer &buffer, QRegion damagedRegion) override; - void pageFlipped(); DrmConnector *connector() const; DrmPipeline *pipeline() const; @@ -61,6 +61,9 @@ public: void applyQueuedChanges(const WaylandOutputConfig &config); void revertQueuedChanges(); + void pageFlipped(std::chrono::nanoseconds timestamp); + void presentFailed(); + private: friend class DrmGpu; friend class DrmBackend; @@ -84,7 +87,6 @@ private: DrmConnector *m_connector; QSharedPointer m_cursor; - bool m_pageFlipPending = false; QTimer m_turnOffTimer; }; diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp index 2905645210..828b6a9244 100644 --- a/src/backends/drm/drm_pipeline.cpp +++ b/src/backends/drm/drm_pipeline.cpp @@ -46,11 +46,21 @@ DrmPipeline::DrmPipeline(DrmConnector *conn) DrmPipeline::~DrmPipeline() { + m_output = nullptr; + if (m_pageflipPending && m_current.crtc) { + pageFlipped({}); + } } bool DrmPipeline::present(const QSharedPointer &buffer) { Q_ASSERT(pending.crtc); + if (!buffer) { + if (m_output) { + m_output->presentFailed(); + } + return false; + } m_primaryBuffer = buffer; if (gpu()->useEglStreams() && gpu()->eglBackend() != nullptr && gpu() == gpu()->platform()->primaryGpu()) { // EglStreamBackend queues normal page flips through EGL, @@ -59,19 +69,42 @@ bool DrmPipeline::present(const QSharedPointer &buffer) return true; } } + bool directScanout = false; +#if HAVE_GBM + // with direct scanout disallow modesets, calling presentFailed() and logging warnings + if (auto buf = dynamic_cast(buffer.data()); buf && buf->clientBuffer()) { + directScanout = true; + } +#endif + if (gpu()->needsModeset()) { + if (directScanout) { + return false; + } + m_modesetPresentPending = true; + return gpu()->maybeModeset(); + } if (gpu()->atomicModeSetting()) { if (!commitPipelines({this}, CommitMode::Commit)) { // update properties and try again updateProperties(); 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 (!presentLegacy()) { qCWarning(KWIN_DRM) << "Present failed!" << strerror(errno); + if (m_output) { + m_output->presentFailed(); + } return false; } } @@ -87,8 +120,8 @@ bool DrmPipeline::commitPipelines(const QVector &pipelines, Commit qCDebug(KWIN_DRM) << "Failed to allocate drmModeAtomicReq!" << strerror(errno); return false; } - uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK; - const auto &failed = [pipelines, req](){ + uint32_t flags = 0; + const auto &failed = [pipelines, req, mode](){ drmModeAtomicFree(req); for (const auto &pipeline : pipelines) { pipeline->printDebugInfo(); @@ -101,6 +134,10 @@ bool DrmPipeline::commitPipelines(const QVector &pipelines, Commit pipeline->pending.crtc->rollbackPending(); pipeline->pending.crtc->primaryPlane()->rollbackPending(); } + if (mode != CommitMode::Test && pipeline->activePending() && pipeline->output()) { + pipeline->m_modesetPresentPending = false; + pipeline->output()->presentFailed(); + } } return false; }; @@ -114,11 +151,22 @@ bool DrmPipeline::commitPipelines(const QVector &pipelines, Commit return failed(); } } - if (drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, (flags & (~DRM_MODE_PAGE_FLIP_EVENT)) | DRM_MODE_ATOMIC_TEST_ONLY, pipelines[0]->output()) != 0) { + bool modeset = flags & DRM_MODE_ATOMIC_ALLOW_MODESET; + Q_ASSERT(!modeset || mode != CommitMode::Commit); + if (modeset) { + // The kernel fails commits with DRM_MODE_PAGE_FLIP_EVENT when a crtc is disabled in the commit + // and already was disabled before, to work around some quirks in old userspace. + // Instead of using DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK, do the modeset in a blocking + // fashion without page flip events and directly call the pageFlipped method afterwards + flags = flags & (~DRM_MODE_PAGE_FLIP_EVENT); + } else { + flags |= DRM_MODE_ATOMIC_NONBLOCK; + } + if (drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, (flags & (~DRM_MODE_PAGE_FLIP_EVENT)) | DRM_MODE_ATOMIC_TEST_ONLY, nullptr) != 0) { qCWarning(KWIN_DRM) << "Atomic test for" << mode << "failed!" << strerror(errno); return failed(); } - if (mode != CommitMode::Test && drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, flags, pipelines[0]->output()) != 0) { + if (mode != CommitMode::Test && drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, flags, nullptr) != 0) { qCWarning(KWIN_DRM) << "Atomic commit failed! This should never happen!" << strerror(errno); return failed(); } @@ -130,6 +178,8 @@ bool DrmPipeline::commitPipelines(const QVector &pipelines, Commit pipeline->pending.crtc->primaryPlane()->commitPending(); } if (mode != CommitMode::Test) { + pipeline->m_modesetPresentPending = false; + pipeline->m_pageflipPending = true; pipeline->m_connector->commit(); if (pipeline->pending.crtc) { pipeline->pending.crtc->primaryPlane()->setNext(pipeline->m_primaryBuffer); @@ -137,6 +187,9 @@ bool DrmPipeline::commitPipelines(const QVector &pipelines, Commit pipeline->pending.crtc->primaryPlane()->commit(); } pipeline->m_current = pipeline->pending; + if (modeset && pipeline->activePending()) { + pipeline->pageFlipped(std::chrono::steady_clock::now().time_since_epoch()); + } } } drmModeAtomicFree(req); @@ -152,11 +205,21 @@ bool DrmPipeline::commitPipelines(const QVector &pipelines, Commit if (failure) { // at least try to revert the config for (const auto &pipeline : pipelines) { - pipeline->pending = pipeline->m_next; + pipeline->revertPendingChanges(); pipeline->applyPendingChangesLegacy(); + if (mode == CommitMode::CommitModeset && pipeline->output() && pipeline->activePending()) { + pipeline->output()->presentFailed(); + } } return false; } else { + for (const auto &pipeline : pipelines) { + pipeline->applyPendingChanges(); + pipeline->m_current = pipeline->pending; + if (mode == CommitMode::CommitModeset && mode != CommitMode::Test && pipeline->activePending()) { + pipeline->pageFlipped(std::chrono::steady_clock::now().time_since_epoch()); + } + } return true; } } @@ -201,6 +264,7 @@ bool DrmPipeline::presentLegacy() qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno) << m_primaryBuffer; return false; } + m_pageflipPending = true; pending.crtc->setNext(m_primaryBuffer); return true; } @@ -378,12 +442,16 @@ DrmGpu *DrmPipeline::gpu() const return m_connector->gpu(); } -void DrmPipeline::pageFlipped() +void DrmPipeline::pageFlipped(std::chrono::nanoseconds timestamp) { m_current.crtc->flipBuffer(); if (m_current.crtc->primaryPlane()) { m_current.crtc->primaryPlane()->flipBuffer(); } + m_pageflipPending = false; + if (m_output) { + m_output->pageFlipped(timestamp); + } } void DrmPipeline::setOutput(DrmOutput *output) @@ -426,7 +494,8 @@ bool DrmPipeline::needsModeset() const || pending.active != m_current.active || pending.modeIndex != m_current.modeIndex || pending.rgbRange != m_current.rgbRange - || pending.transformation != m_current.transformation; + || pending.transformation != m_current.transformation + || m_modesetPresentPending; } bool DrmPipeline::activePending() const @@ -439,6 +508,21 @@ void DrmPipeline::revertPendingChanges() pending = m_next; } +bool DrmPipeline::pageflipPending() const +{ + return m_pageflipPending; +} + +bool DrmPipeline::modesetPresentPending() const +{ + return m_modesetPresentPending; +} + +DrmCrtc *DrmPipeline::currentCrtc() const +{ + return m_current.crtc; +} + DrmGammaRamp::DrmGammaRamp(DrmGpu *gpu, const GammaRamp &lut) : lut(lut) , size(lut.size()) diff --git a/src/backends/drm/drm_pipeline.h b/src/backends/drm/drm_pipeline.h index b2ea09e264..b8b0f3f4c1 100644 --- a/src/backends/drm/drm_pipeline.h +++ b/src/backends/drm/drm_pipeline.h @@ -15,6 +15,7 @@ #include #include +#include #include "drm_object_plane.h" #include "renderloop_p.h" @@ -54,6 +55,7 @@ public: bool present(const QSharedPointer &buffer); bool needsModeset() const; + bool needsCommit() const; void prepareModeset(); void applyPendingChanges(); void revertPendingChanges(); @@ -65,9 +67,12 @@ public: QPoint cursorPos() const; DrmConnector *connector() const; + DrmCrtc *currentCrtc() const; DrmGpu *gpu() const; - void pageFlipped(); + void pageFlipped(std::chrono::nanoseconds timestamp); + bool pageflipPending() const; + bool modesetPresentPending() const; void printDebugInfo() const; QSize sourceSize() const; void updateProperties(); @@ -92,7 +97,8 @@ public: enum class CommitMode { Test, - Commit + Commit, + CommitModeset }; Q_ENUM(CommitMode); static bool commitPipelines(const QVector &pipelines, CommitMode mode); @@ -111,6 +117,8 @@ private: QSharedPointer m_primaryBuffer; QSharedPointer m_oldTestBuffer; + bool m_pageflipPending = false; + bool m_modesetPresentPending = false; QMap> m_formats; int m_lastFlags = 0; diff --git a/src/backends/drm/egl_gbm_backend.cpp b/src/backends/drm/egl_gbm_backend.cpp index 0ab9b6d592..c23fa802a4 100644 --- a/src/backends/drm/egl_gbm_backend.cpp +++ b/src/backends/drm/egl_gbm_backend.cpp @@ -581,11 +581,7 @@ void EglGbmBackend::endFrame(AbstractOutput *drmOutput, const QRegion &renderedR const QRegion dirty = damagedRegion.intersected(output.output->geometry()); QSharedPointer buffer = endFrameWithBuffer(drmOutput, dirty); - if (!buffer || !output.output->present(buffer, dirty)) { - RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(output.output->renderLoop()); - renderLoopPrivate->notifyFrameFailed(); - return; - } + output.output->present(buffer, dirty); } void EglGbmBackend::updateBufferAge(Output &output, const QRegion &dirty) diff --git a/src/backends/drm/scene_qpainter_drm_backend.cpp b/src/backends/drm/scene_qpainter_drm_backend.cpp index da59423b4b..a6ed76c4c2 100644 --- a/src/backends/drm/scene_qpainter_drm_backend.cpp +++ b/src/backends/drm/scene_qpainter_drm_backend.cpp @@ -78,10 +78,7 @@ void DrmQPainterBackend::endFrame(AbstractOutput *output, const QRegion &damage) QSharedPointer back = rendererOutput.swapchain->currentBuffer(); rendererOutput.swapchain->releaseBuffer(back); - if (!drmOutput->present(back, drmOutput->geometry())) { - RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(drmOutput->renderLoop()); - renderLoopPrivate->notifyFrameFailed(); - } + drmOutput->present(back, drmOutput->geometry()); rendererOutput.damageJournal.add(damage); }