From 2f312f35c9de18b0e97221d0e7e894708a2bc407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 14 Apr 2015 11:25:16 +0200 Subject: [PATCH] Add a DrmOutput class to be used by the DrmBackend The DrmOutput corresponds to one output we perform mode setting on. This implies that page flip is now performed on the DrmOutput. As our compositor cannot drive multiple outputs yet, we only use the first one and call into bufferSwapComplete and aboutToBufferSwap when either all page flips are completed or respectivly the first one is performed. Setting cursor is also handled on each DrmOutput. When the DrmOutput is initialized it is blanked with a black buffer which gets destroyed once the first page flip event has occurred (meanig the buffer is no longer needed). Before setting the black buffer we store the current buffer which will be set back on tear down. Because of that tearing down the DrmBackend needs to wait till all page flips ended. --- drm_backend.cpp | 163 ++++++++++++++++++++++++++++++++++++++---------- drm_backend.h | 55 +++++++++++++--- 2 files changed, 175 insertions(+), 43 deletions(-) diff --git a/drm_backend.cpp b/drm_backend.cpp index e70476d9b8..807307e9c0 100644 --- a/drm_backend.cpp +++ b/drm_backend.cpp @@ -67,7 +67,11 @@ DrmBackend::DrmBackend(QObject *parent) DrmBackend::~DrmBackend() { if (m_fd >= 0) { - hideCursor(); + // wait for pageflips + while (m_pageFlipsPending != 0) { + QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); + } + qDeleteAll(m_outputs); delete m_cursor[0]; delete m_cursor[1]; close(m_fd); @@ -99,15 +103,16 @@ void DrmBackend::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, u Q_UNUSED(frame) Q_UNUSED(sec) Q_UNUSED(usec) - DrmBuffer *buffer = reinterpret_cast(data); - buffer->m_backend->m_pageFlipPending = false; -#if HAVE_GBM - if (buffer->m_bo) { - gbm_surface_release_buffer(buffer->m_surface, buffer->m_bo); - buffer->m_bo = nullptr; + auto output = reinterpret_cast(data); + output->pageFlipped(); + output->m_backend->m_pageFlipsPending--; + if (output->m_backend->m_pageFlipsPending == 0) { + // TODO: improve, this currently means we wait for all page flips or all outputs. + // It would be better to driver the repaint per output + if (Compositor::self()) { + Compositor::self()->bufferSwapComplete(); + } } -#endif - Compositor::self()->bufferSwapComplete(); } void DrmBackend::openDrm() @@ -178,31 +183,28 @@ void DrmBackend::queryResources() if (!crtc) { continue; } - m_resolution = QSize(crtc->mode.hdisplay, crtc->mode.vdisplay); - m_crtcId = encoder->crtc_id; - m_connector = connector->connector_id; - m_mode = crtc->mode; - // TODO: improve - DrmBuffer *b = new DrmBuffer(this, m_resolution); - b->map(); - b->image()->fill(Qt::black); - drmModeSetCrtc(m_fd, m_crtcId, b->m_bufferId, 0, 0, &m_connector, 1, &m_mode); - // for the moment only one crtc - break; + DrmOutput *drmOutput = new DrmOutput(this); + drmOutput->m_crtcId = encoder->crtc_id; + drmOutput->m_mode = crtc->mode; + drmOutput->m_connector = connector->connector_id; + drmOutput->init(); + m_outputs << drmOutput; } + // TODO: install global space } void DrmBackend::present(DrmBuffer *buffer) { - if (!buffer || buffer->m_bufferId == 0) { + if (m_outputs.isEmpty()) { return; } - if (m_pageFlipPending) { - return; + // TODO: correct output + if (m_outputs.first()->present(buffer)) { + m_pageFlipsPending++; + if (m_pageFlipsPending == 1 && Compositor::self()) { + Compositor::self()->aboutToSwapBuffers(); + } } - m_pageFlipPending = true; - Compositor::self()->aboutToSwapBuffers(); - drmModePageFlip(m_fd, m_crtcId, buffer->m_bufferId, DRM_MODE_PAGE_FLIP_EVENT, buffer); } void DrmBackend::installCursorFromServer() @@ -245,7 +247,9 @@ void DrmBackend::setCursor() { DrmBuffer *c = m_cursor[m_cursorIndex]; m_cursorIndex = (m_cursorIndex + 1) % 2; - drmModeSetCursor(m_fd, m_crtcId, c->m_handle, c->m_size.width(), c->m_size.height()); + for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { + (*it)->showCursor(c); + } } void DrmBackend::updateCursor() @@ -268,13 +272,25 @@ void DrmBackend::updateCursor() void DrmBackend::hideCursor() { - drmModeSetCursor(m_fd, m_crtcId, 0, 0, 0); + for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { + (*it)->hideCursor(); + } } void DrmBackend::moveCursor() { const QPoint p = Cursor::pos() - softwareCursorHotspot(); - drmModeMoveCursor(m_fd, m_crtcId, p.x(), p.y()); + for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { + (*it)->moveCursor(p); + } +} + +QSize DrmBackend::size() const +{ + if (m_outputs.isEmpty()) { + return QSize(); + } + return m_outputs.first()->size(); } Screens *DrmBackend::createScreens(QObject *parent) @@ -307,6 +323,79 @@ DrmBuffer *DrmBackend::createBuffer(gbm_surface *surface) #endif } +DrmOutput::DrmOutput(DrmBackend *backend) + : m_backend(backend) +{ +} + +DrmOutput::~DrmOutput() +{ + hideCursor(); + if (!m_savedCrtc.isNull()) { + drmModeSetCrtc(m_backend->fd(), m_savedCrtc->crtc_id, m_savedCrtc->buffer_id, + m_savedCrtc->x, m_savedCrtc->y, &m_connector, 1, &m_savedCrtc->mode); + } + cleanupBlackBuffer(); +} + +void DrmOutput::hideCursor() +{ + drmModeSetCursor(m_backend->fd(), m_crtcId, 0, 0, 0); +} + +void DrmOutput::showCursor(DrmBuffer *c) +{ + const QSize &s = c->size(); + drmModeSetCursor(m_backend->fd(), m_crtcId, c->handle(), s.width(), s.height()); +} + +void DrmOutput::moveCursor(const QPoint &globalPos) +{ + const QPoint p = globalPos - m_globalPos; + drmModeMoveCursor(m_backend->fd(), m_crtcId, p.x(), p.y()); +} + +QSize DrmOutput::size() const +{ + return QSize(m_mode.hdisplay, m_mode.vdisplay); +} + +bool DrmOutput::present(DrmBuffer *buffer) +{ + if (!buffer || buffer->bufferId() == 0) { + return false; + } + if (m_currentBuffer) { + return false; + } + m_currentBuffer = buffer; + return drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; +} + +void DrmOutput::pageFlipped() +{ + m_currentBuffer->releaseGbm(); + m_currentBuffer = nullptr; + cleanupBlackBuffer(); +} + +void DrmOutput::cleanupBlackBuffer() +{ + if (m_blackBuffer) { + delete m_blackBuffer; + m_blackBuffer = nullptr; + } +} + +void DrmOutput::init() +{ + m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId)); + m_blackBuffer = m_backend->createBuffer(size()); + m_blackBuffer->map(); + m_blackBuffer->image()->fill(Qt::black); + drmModeSetCrtc(m_backend->fd(), m_crtcId, m_blackBuffer->bufferId(), 0, 0, &m_connector, 1, &m_mode); +} + DrmBuffer::DrmBuffer(DrmBackend *backend, const QSize &size) : m_backend(backend) , m_size(size) @@ -368,11 +457,7 @@ DrmBuffer::~DrmBuffer() destroyArgs.handle = m_handle; drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs); } -#if HAVE_GBM - if (m_bo) { - gbm_surface_release_buffer(m_surface, m_bo); - } -#endif + releaseGbm(); } bool DrmBuffer::map(QImage::Format format) @@ -395,4 +480,14 @@ bool DrmBuffer::map(QImage::Format format) 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/drm_backend.h b/drm_backend.h index ef36898bda..7af30023d9 100644 --- a/drm_backend.h +++ b/drm_backend.h @@ -34,6 +34,7 @@ namespace KWin class Udev; class DrmBuffer; +class DrmOutput; class KWIN_EXPORT DrmBackend : public AbstractBackend { @@ -53,10 +54,7 @@ public: DrmBuffer *createBuffer(gbm_surface *surface); void present(DrmBuffer *buffer); - QSize size() const { - // TODO: this is wrong - return m_resolution; - } + QSize size() const; int fd() const { return m_fd; } @@ -76,14 +74,43 @@ private: QScopedPointer m_udev; int m_fd = -1; int m_drmId = 0; - // TODO: this is wrong - QSize m_resolution; + QVector m_outputs; + DrmBuffer *m_cursor[2]; + int m_cursorIndex = 0; + int m_pageFlipsPending = 0; +}; + +class DrmOutput +{ +public: + virtual ~DrmOutput(); + void showCursor(DrmBuffer *buffer); + void hideCursor(); + void moveCursor(const QPoint &globalPos); + bool present(DrmBuffer *buffer); + void pageFlipped(); + void init(); + + QSize size() const; + +private: + friend class DrmBackend; + DrmOutput(DrmBackend *backend); + void cleanupBlackBuffer(); + + DrmBackend *m_backend; + QPoint m_globalPos; quint32 m_crtcId = 0; quint32 m_connector = 0; drmModeModeInfo m_mode; - bool m_pageFlipPending = false; - DrmBuffer *m_cursor[2]; - int m_cursorIndex = 0; + DrmBuffer *m_currentBuffer = nullptr; + DrmBuffer *m_blackBuffer = nullptr; + struct CrtcCleanup { + static void inline cleanup(_drmModeCrtc *ptr) { + drmModeFreeCrtc(ptr); + } + }; + QScopedPointer<_drmModeCrtc, CrtcCleanup> m_savedCrtc; }; class DrmBuffer @@ -95,6 +122,16 @@ public: 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; + } + void releaseGbm(); private: friend class DrmBackend;