diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -72,8 +72,8 @@ OpenGLBackend* createOpenGLBackend() override; void init() override; - DrmBuffer *createBuffer(const QSize &size); - DrmBuffer *createBuffer(gbm_surface *surface); + DrmDumbBuffer *createBuffer(const QSize &size); + DrmSurfaceBuffer *createBuffer(gbm_surface *surface); void present(DrmBuffer *buffer, DrmOutput *output); int fd() const { @@ -93,6 +93,10 @@ void outputWentOff(); void checkOutputsAreOn(); + // QPainter reuses buffers + bool deleteBufferAfterPageFlip() const { + return m_deleteBufferAfterPageFlip; + } // returns use of AMS, default is not/legacy bool atomicModeSetting() const { return m_atomicModeSetting; @@ -140,7 +144,8 @@ int m_fd = -1; int m_drmId = 0; QVector m_outputs; - DrmBuffer *m_cursor[2]; + DrmDumbBuffer *m_cursor[2]; + bool m_deleteBufferAfterPageFlip; bool m_atomicModeSetting = false; bool m_cursorEnabled = false; int m_cursorIndex = 0; diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -163,7 +163,7 @@ } m_active = true; if (!usesSoftwareCursor()) { - DrmBuffer *c = m_cursor[(m_cursorIndex + 1) % 2]; + DrmDumbBuffer *c = m_cursor[(m_cursorIndex + 1) % 2]; const QPoint cp = Cursor::pos() - softwareCursorHotspot(); for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { DrmOutput *o = *it; @@ -604,7 +604,7 @@ void DrmBackend::setCursor() { - DrmBuffer *c = m_cursor[m_cursorIndex]; + DrmDumbBuffer *c = m_cursor[m_cursorIndex]; m_cursorIndex = (m_cursorIndex + 1) % 2; if (m_cursorEnabled) { for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { @@ -671,30 +671,31 @@ QPainterBackend *DrmBackend::createQPainterBackend() { + m_deleteBufferAfterPageFlip = false; return new DrmQPainterBackend(this); } OpenGLBackend *DrmBackend::createOpenGLBackend() { + m_deleteBufferAfterPageFlip = true; #if HAVE_GBM return new EglGbmBackend(this); #else return Platform::createOpenGLBackend(); #endif } -DrmBuffer *DrmBackend::createBuffer(const QSize &size) +DrmDumbBuffer *DrmBackend::createBuffer(const QSize &size) { - DrmBuffer *b = new DrmBuffer(this, size); + DrmDumbBuffer *b = new DrmDumbBuffer(this, size); m_buffers << b; return b; } -DrmBuffer *DrmBackend::createBuffer(gbm_surface *surface) +DrmSurfaceBuffer *DrmBackend::createBuffer(gbm_surface *surface) { #if HAVE_GBM - DrmBuffer *b = new DrmBuffer(this, surface); - b->m_deleteAfterPageFlip = true; + DrmSurfaceBuffer *b = new DrmSurfaceBuffer(this, surface); m_buffers << b; return b; #else diff --git a/plugins/platforms/drm/drm_buffer.h b/plugins/platforms/drm/drm_buffer.h --- a/plugins/platforms/drm/drm_buffer.h +++ b/plugins/platforms/drm/drm_buffer.h @@ -34,51 +34,74 @@ class DrmBuffer { public: - ~DrmBuffer(); + DrmBuffer(DrmBackend *backend); + ~DrmBuffer() = default; + + virtual bool modeChange(DrmBuffer *b) const = 0; - bool map(QImage::Format format = QImage::Format_RGB32); - QImage *image() const { - return m_image; - } - quint32 handle() const { - return m_handle; - } - const QSize &size() const { - return m_size; - } quint32 bufferId() const { return m_bufferId; } quint32 stride() const { return m_stride; } - gbm_bo *gbm() const { - return m_bo; + + virtual void releaseGbm() {} + +protected: + DrmBackend *m_backend; + quint32 m_bufferId = 0; + quint32 m_stride = 0; + quint64 m_bufferSize = 0; +}; + +class DrmSurfaceBuffer : public DrmBuffer +{ +public: + DrmSurfaceBuffer(DrmBackend *backend, gbm_surface *surface); + ~DrmSurfaceBuffer(); + + bool modeChange(DrmBuffer *b) const override { + return isGbm() != static_cast(b)->isGbm(); } + bool isGbm() const { return m_bo != nullptr; } - bool deleteAfterPageFlip() const { - return m_deleteAfterPageFlip; - } - - void releaseGbm(); + void releaseGbm() override; private: - friend class DrmBackend; - DrmBuffer(DrmBackend *backend, const QSize &size); - DrmBuffer(DrmBackend *backend, gbm_surface *surface); - DrmBackend *m_backend; gbm_surface *m_surface = nullptr; gbm_bo *m_bo = nullptr; - QSize m_size; +}; + +class DrmDumbBuffer : public DrmBuffer +{ +public: + DrmDumbBuffer(DrmBackend *backend, const QSize &size); + ~DrmDumbBuffer(); + + bool modeChange(DrmBuffer *b) const override { + return m_stride == b->stride(); + } + + bool map(QImage::Format format = QImage::Format_RGB32); + quint32 handle() const { + return m_handle; + } + QImage *image() const { + return m_image; + } + const QSize &size() const { + return m_size; + } + +private: quint32 m_handle = 0; - quint32 m_bufferId = 0; - quint32 m_stride = 0; quint64 m_bufferSize = 0; void *m_memory = nullptr; QImage *m_image = nullptr; - bool m_deleteAfterPageFlip = false; + QSize m_size; }; } diff --git a/plugins/platforms/drm/drm_buffer.cpp b/plugins/platforms/drm/drm_buffer.cpp --- a/plugins/platforms/drm/drm_buffer.cpp +++ b/plugins/platforms/drm/drm_buffer.cpp @@ -34,9 +34,60 @@ namespace KWin { +#if HAVE_GBM +static void gbmCallback(gbm_bo *bo, void *data) +{ + DrmBuffer *b = static_cast(data); + delete b; +} +#endif -DrmBuffer::DrmBuffer(DrmBackend *backend, const QSize &size) +DrmBuffer:: DrmBuffer(DrmBackend *backend) : m_backend(backend) +{} + +// DrmSurfaceBuffer +DrmSurfaceBuffer::DrmSurfaceBuffer(DrmBackend *backend, gbm_surface *surface) + : DrmBuffer(backend) + , m_surface(surface) +{ +#if HAVE_GBM + m_bo = gbm_surface_lock_front_buffer(surface); + if (!m_bo) { + qCWarning(KWIN_DRM) << "Locking front buffer failed"; + return; + } + QSize size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo)); + m_stride = gbm_bo_get_stride(m_bo); + if (drmModeAddFB(m_backend->fd(), size.width(), size.height(), 24, 32, m_stride, gbm_bo_get_handle(m_bo).u32, &m_bufferId) != 0) { + qCWarning(KWIN_DRM) << "drmModeAddFB failed"; + } + gbm_bo_set_user_data(m_bo, this, gbmCallback); +#endif +} + +DrmSurfaceBuffer::~DrmSurfaceBuffer() +{ + m_backend->bufferDestroyed(this); + if (m_bufferId) { + drmModeRmFB(m_backend->fd(), m_bufferId); + } + releaseGbm(); +} + +void DrmSurfaceBuffer::releaseGbm() +{ +#if HAVE_GBM + if (m_bo) { + gbm_surface_release_buffer(m_surface, m_bo); + m_bo = nullptr; + } +#endif +} + +// DrmDumbBuffer +DrmDumbBuffer::DrmDumbBuffer(DrmBackend *backend, const QSize &size) + : DrmBuffer(backend) , m_size(size) { drm_mode_create_dumb createArgs; @@ -57,41 +108,7 @@ } } - -#if HAVE_GBM -static void gbmCallback(gbm_bo *bo, void *data) -{ - DrmBackend *backend = reinterpret_cast(data); - const auto &buffers = backend->buffers(); - for (auto buffer: buffers) { - if (buffer->gbm() == bo) { - delete buffer; - return; - } - } -} -#endif - -DrmBuffer::DrmBuffer(DrmBackend *backend, gbm_surface *surface) - : m_backend(backend) - , m_surface(surface) -{ -#if HAVE_GBM - m_bo = gbm_surface_lock_front_buffer(surface); - if (!m_bo) { - qCWarning(KWIN_DRM) << "Locking front buffer failed"; - return; - } - m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo)); - m_stride = gbm_bo_get_stride(m_bo); - if (drmModeAddFB(m_backend->fd(), m_size.width(), m_size.height(), 24, 32, m_stride, gbm_bo_get_handle(m_bo).u32, &m_bufferId) != 0) { - qCWarning(KWIN_DRM) << "drmModeAddFB failed"; - } - gbm_bo_set_user_data(m_bo, m_backend, gbmCallback); -#endif -} - -DrmBuffer::~DrmBuffer() +DrmDumbBuffer::~DrmDumbBuffer() { m_backend->bufferDestroyed(this); delete m_image; @@ -106,10 +123,9 @@ destroyArgs.handle = m_handle; drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs); } - releaseGbm(); } -bool DrmBuffer::map(QImage::Format format) +bool DrmDumbBuffer::map(QImage::Format format) { if (!m_handle || !m_bufferId) { return false; @@ -129,14 +145,4 @@ return !m_image->isNull(); } -void DrmBuffer::releaseGbm() -{ -#if HAVE_GBM - if (m_bo) { - gbm_surface_release_buffer(m_surface, m_bo); - m_bo = nullptr; - } -#endif -} - } diff --git a/plugins/platforms/drm/drm_object_crtc.h b/plugins/platforms/drm/drm_object_crtc.h --- a/plugins/platforms/drm/drm_object_crtc.h +++ b/plugins/platforms/drm/drm_object_crtc.h @@ -25,8 +25,6 @@ namespace KWin { -class DrmBuffer; - class DrmCrtc : public DrmObject { public: diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -46,6 +46,7 @@ class DrmBackend; class DrmBuffer; +class DrmDumbBuffer; class DrmPlane; class DrmConnector; class DrmCrtc; @@ -61,7 +62,7 @@ QSize physicalSize; }; virtual ~DrmOutput(); - void showCursor(DrmBuffer *buffer); + void showCursor(DrmDumbBuffer *buffer); void hideCursor(); void moveCursor(const QPoint &globalPos); bool init(drmModeConnector *connector); @@ -112,7 +113,6 @@ void initUuid(); void setGlobalPos(const QPoint &pos); - void pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer); bool initPrimaryPlane(); bool initCursorPlane(); DrmObject::AtomicReturn atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); @@ -126,7 +126,7 @@ drmModeModeInfo m_mode; DrmBuffer *m_currentBuffer = nullptr; DrmBuffer *m_nextBuffer = nullptr; - DrmBuffer *m_blackBuffer = nullptr; + DrmDumbBuffer *m_blackBuffer = nullptr; struct CrtcCleanup { static void inline cleanup(_drmModeCrtc *ptr) { drmModeFreeCrtc(ptr); // TODO: Atomically? See compositor-drm.c l.3670 diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -82,7 +82,7 @@ } } -void DrmOutput::showCursor(DrmBuffer *c) +void DrmOutput::showCursor(DrmDumbBuffer *c) { const QSize &s = c->size(); drmModeSetCursor(m_backend->fd(), m_crtcId, c->handle(), s.width(), s.height()); @@ -664,36 +664,45 @@ void DrmOutput::pageFlipped() { - if (m_backend->atomicModeSetting()){ - foreach (DrmPlane *p, m_planesFlipList) { - pageFlippedBufferRemover(p->current(), p->next()); - p->setCurrent(p->next()); - p->setNext(nullptr); - } - m_planesFlipList.clear(); + // Egl based surface buffers get destroyed, QPainter based dumb buffers not + // TODO1: when we later allow different buffer subclasses on planes and the front buffer, the + // deleteBufferAfterPageFlip boolean needs to be replaced by individual buffer cleanup fcts + // TODO2: split up DrmOutput in two for dumb and egl/gbm surface buffer compatible subclasses completely? + if (m_backend->deleteBufferAfterPageFlip()) { - } else { - if (!m_nextBuffer) { - return; - } - pageFlippedBufferRemover(m_currentBuffer, m_nextBuffer); - m_currentBuffer = m_nextBuffer; - m_nextBuffer = nullptr; - } - cleanupBlackBuffer(); -} + auto bufferHandler = [] (DrmBuffer *oldbuffer, DrmBuffer *newbuffer) { + if (oldbuffer != newbuffer) { + delete oldbuffer; + } + newbuffer->releaseGbm(); + }; + + if (m_backend->atomicModeSetting()) { + for (DrmPlane *p : m_planesFlipList) { + bufferHandler(p->current(), p->next()); + p->setCurrent(p->next()); + p->setNext(nullptr); + } + m_planesFlipList.clear(); -void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer) -{ - if (newbuffer->deleteAfterPageFlip()) { - if ( oldbuffer && oldbuffer != newbuffer ) { - delete oldbuffer; + } else { + bufferHandler(m_currentBuffer, m_nextBuffer); + m_currentBuffer = m_nextBuffer; + m_nextBuffer = nullptr; } } else { - // although oldbuffer's pointer is remapped in pageFlipped(), - // we ignore the pointer completely anywhere else in this case - newbuffer->releaseGbm(); + if (m_backend->atomicModeSetting()){ + for (DrmPlane *p : m_planesFlipList) { + p->setCurrent(p->next()); + p->setNext(nullptr); + } + m_planesFlipList.clear(); + } else { + m_currentBuffer = m_nextBuffer; + m_nextBuffer = nullptr; + } } + cleanupBlackBuffer(); } void DrmOutput::cleanupBlackBuffer() @@ -732,6 +741,14 @@ bool DrmOutput::presentAtomically(DrmBuffer *buffer) { + auto bufferErrorHandler = [this] (DrmBuffer *b) { + if (this->m_backend->deleteBufferAfterPageFlip()) { + delete b; + } else { + b->releaseGbm(); + } + }; + if (!LogindIntegration::self()->isActiveSession()) { qCWarning(KWIN_DRM) << "Logind session not active."; return false; @@ -752,7 +769,7 @@ drmModeAtomicReq *req = drmModeAtomicAlloc(); if (!req) { qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request"; - delete buffer; + bufferErrorHandler(buffer); return false; } @@ -763,14 +780,14 @@ if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId)) { qCWarning(KWIN_DRM) << "Failed to create property blob"; - delete buffer; + bufferErrorHandler(buffer); return false; } ret = atomicReqModesetPopulate(req, true); if (ret == DrmObject::AtomicReturn::Error){ drmModeAtomicFree(req); - delete buffer; + bufferErrorHandler(buffer); return false; } if (ret == DrmObject::AtomicReturn::Success) { @@ -790,7 +807,7 @@ drmModeAtomicFree(req); m_primaryPlane->setNext(nullptr); m_planesFlipList.clear(); - delete buffer; + bufferErrorHandler(buffer); return false; } if (ret == DrmObject::AtomicReturn::Success) { @@ -806,7 +823,7 @@ drmModeAtomicFree(req); m_primaryPlane->setNext(nullptr); m_planesFlipList.clear(); - delete buffer; + bufferErrorHandler(buffer); return false; } m_planesFlipList << m_primaryPlane; @@ -817,7 +834,7 @@ drmModeAtomicFree(req); m_primaryPlane->setNext(nullptr); m_planesFlipList.clear(); - delete buffer; + bufferErrorHandler(buffer); return false; } @@ -848,27 +865,30 @@ } // Do we need to set a new mode first? - if (m_lastStride != buffer->stride() || m_lastGbm != buffer->isGbm()){ - if (!setModeLegacy(buffer)) + if (!m_currentBuffer || m_currentBuffer->modeChange(buffer)) { + if (!setModeLegacy(buffer)) { return false; + } } int errno_save = 0; - const bool ok = drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; - if (ok) { + if (drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0) { m_nextBuffer = buffer; + return true; } else { errno_save = errno; qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno); - delete buffer; + if (m_backend->deleteBufferAfterPageFlip()) { + delete buffer; + } else { + buffer->releaseGbm(); + } + return false; } - return ok; } bool DrmOutput::setModeLegacy(DrmBuffer *buffer) { if (drmModeSetCrtc(m_backend->fd(), m_crtcId, buffer->bufferId(), 0, 0, &m_connector, 1, &m_mode) == 0) { - m_lastStride = buffer->stride(); - m_lastGbm = buffer->isGbm(); return true; } else { qCWarning(KWIN_DRM) << "Mode setting failed"; diff --git a/plugins/platforms/drm/egl_gbm_backend.h b/plugins/platforms/drm/egl_gbm_backend.h --- a/plugins/platforms/drm/egl_gbm_backend.h +++ b/plugins/platforms/drm/egl_gbm_backend.h @@ -27,7 +27,7 @@ namespace KWin { class DrmBackend; -class DrmBuffer; +class DrmSurfaceBuffer; class DrmOutput; /** @@ -59,7 +59,7 @@ bool initRenderingContext(); struct Output { DrmOutput *output = nullptr; - DrmBuffer *buffer = nullptr; + DrmSurfaceBuffer *buffer = nullptr; gbm_surface *gbmSurface = nullptr; EGLSurface eglSurface = EGL_NO_SURFACE; int bufferAge = 0; diff --git a/plugins/platforms/drm/scene_qpainter_drm_backend.h b/plugins/platforms/drm/scene_qpainter_drm_backend.h --- a/plugins/platforms/drm/scene_qpainter_drm_backend.h +++ b/plugins/platforms/drm/scene_qpainter_drm_backend.h @@ -26,7 +26,7 @@ { class DrmBackend; -class DrmBuffer; +class DrmDumbBuffer; class DrmOutput; class DrmQPainterBackend : public QObject, public QPainterBackend @@ -47,7 +47,7 @@ private: void initOutput(DrmOutput *output); struct Output { - DrmBuffer *buffer[2]; + DrmDumbBuffer *buffer[2]; DrmOutput *output; int index = 0; };