From 97f119af14d3cc1be24400a5e542d26fca96d36b Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Thu, 18 Sep 2014 14:36:16 +0300 Subject: [PATCH] EGLFS: Add support for multiple displays Change-Id: Id039e0ed2d99562eb2a5cfe1e7b34013c75ff3ac Reviewed-by: Laszlo Agocs --- .../qeglplatformintegration.cpp | 13 +- .../qeglplatformintegration_p.h | 4 - src/plugins/platforms/eglfs/qeglfscontext.cpp | 4 +- src/plugins/platforms/eglfs/qeglfshooks.h | 8 +- .../platforms/eglfs/qeglfshooks_kms.cpp | 839 +++++++++++------- .../platforms/eglfs/qeglfshooks_stub.cpp | 23 +- .../platforms/eglfs/qeglfsintegration.cpp | 24 +- .../platforms/eglfs/qeglfsintegration.h | 4 +- src/plugins/platforms/eglfs/qeglfswindow.cpp | 5 +- 9 files changed, 574 insertions(+), 350 deletions(-) diff --git a/src/platformsupport/eglconvenience/qeglplatformintegration.cpp b/src/platformsupport/eglconvenience/qeglplatformintegration.cpp index fa1ec9e1be..a9f0c0f448 100644 --- a/src/platformsupport/eglconvenience/qeglplatformintegration.cpp +++ b/src/platformsupport/eglconvenience/qeglplatformintegration.cpp @@ -84,8 +84,7 @@ QT_BEGIN_NAMESPACE */ QEGLPlatformIntegration::QEGLPlatformIntegration() - : m_screen(0), - m_display(EGL_NO_DISPLAY), + : m_display(EGL_NO_DISPLAY), m_inputContext(0), m_fontDb(new QGenericUnixFontDatabase), m_services(new QGenericUnixServices), @@ -95,7 +94,6 @@ QEGLPlatformIntegration::QEGLPlatformIntegration() QEGLPlatformIntegration::~QEGLPlatformIntegration() { - } void QEGLPlatformIntegration::initialize() @@ -108,9 +106,6 @@ void QEGLPlatformIntegration::initialize() if (!eglInitialize(m_display, &major, &minor)) qFatal("Could not initialize egl display"); - m_screen = createScreen(); - screenAdded(m_screen); - m_inputContext = QPlatformInputContextFactory::create(); m_vtHandler.reset(new QFbVtHandler); @@ -121,8 +116,6 @@ void QEGLPlatformIntegration::destroy() foreach (QWindow *w, qGuiApp->topLevelWindows()) w->destroy(); - delete m_screen; - if (m_display != EGL_NO_DISPLAY) eglTerminate(m_display); } @@ -228,7 +221,7 @@ void *QEGLPlatformIntegration::nativeResourceForIntegration(const QByteArray &re switch (resourceType(resource)) { case EglDisplay: - result = m_screen->display(); + result = display(); break; case NativeDisplay: result = reinterpret_cast(nativeDisplay()); @@ -266,7 +259,7 @@ void *QEGLPlatformIntegration::nativeResourceForWindow(const QByteArray &resourc if (window && window->handle()) result = static_cast(window->handle()->screen())->display(); else - result = m_screen->display(); + result = display(); break; case EglWindow: if (window && window->handle()) diff --git a/src/platformsupport/eglconvenience/qeglplatformintegration_p.h b/src/platformsupport/eglconvenience/qeglplatformintegration_p.h index 2080ff0352..7f0037db92 100644 --- a/src/platformsupport/eglconvenience/qeglplatformintegration_p.h +++ b/src/platformsupport/eglconvenience/qeglplatformintegration_p.h @@ -52,7 +52,6 @@ QT_BEGIN_NAMESPACE -class QEGLPlatformScreen; class QEGLPlatformWindow; class QEGLPlatformContext; class QFbVtHandler; @@ -67,7 +66,6 @@ public: void initialize() Q_DECL_OVERRIDE; void destroy() Q_DECL_OVERRIDE; - QEGLPlatformScreen *screen() const { return m_screen; } EGLDisplay display() const { return m_display; } QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE; @@ -93,7 +91,6 @@ public: QFunctionPointer platformFunction(const QByteArray &function) const Q_DECL_OVERRIDE; protected: - virtual QEGLPlatformScreen *createScreen() const = 0; virtual QEGLPlatformWindow *createWindow(QWindow *window) const = 0; virtual QEGLPlatformContext *createContext(const QSurfaceFormat &format, QPlatformOpenGLContext *shareContext, @@ -110,7 +107,6 @@ protected: private: static void loadKeymapStatic(const QString &filename); - QEGLPlatformScreen *m_screen; EGLDisplay m_display; QPlatformInputContext *m_inputContext; QScopedPointer m_fontDb; diff --git a/src/plugins/platforms/eglfs/qeglfscontext.cpp b/src/plugins/platforms/eglfs/qeglfscontext.cpp index 310b460087..00778373ae 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.cpp +++ b/src/plugins/platforms/eglfs/qeglfscontext.cpp @@ -94,9 +94,9 @@ void QEglFSContext::swapBuffers(QPlatformSurface *surface) cursor->paintOnScreen(); } - QEglFSHooks::hooks()->waitForVSync(); + QEglFSHooks::hooks()->waitForVSync(surface); QEGLPlatformContext::swapBuffers(surface); - QEglFSHooks::hooks()->presentBuffer(); + QEglFSHooks::hooks()->presentBuffer(surface); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfshooks.h b/src/plugins/platforms/eglfs/qeglfshooks.h index 078a174a16..b2b8d46741 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks.h +++ b/src/plugins/platforms/eglfs/qeglfshooks.h @@ -43,6 +43,8 @@ QT_BEGIN_NAMESPACE class QEglFSScreen; +class QEglFSIntegration; +class QPlatformSurface; class QEglFSHooks { @@ -51,6 +53,8 @@ public: virtual void platformInit(); virtual void platformDestroy(); virtual EGLNativeDisplayType platformDisplay() const; + virtual void screenInit(); + virtual void screenDestroy(); virtual QSizeF physicalScreenSize() const; virtual QSize screenSize() const; virtual QDpi logicalDpi() const; @@ -67,8 +71,8 @@ public: virtual bool hasCapability(QPlatformIntegration::Capability cap) const; virtual QPlatformCursor *createCursor(QPlatformScreen *screen) const; virtual bool filterConfig(EGLDisplay display, EGLConfig config) const; - virtual void waitForVSync() const; - virtual void presentBuffer(); + virtual void waitForVSync(QPlatformSurface *surface) const; + virtual void presentBuffer(QPlatformSurface *surface); virtual QByteArray fbDeviceName() const; virtual int framebufferIndex() const; diff --git a/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp b/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp index 61a1ee7a87..a14de922f4 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp @@ -40,6 +40,9 @@ ****************************************************************************/ #include "qeglfshooks.h" +#include "qeglfsintegration.h" +#include "qeglfsscreen.h" + #include #include #include @@ -49,6 +52,7 @@ #include #include #include +#include #include #include @@ -56,12 +60,106 @@ QT_USE_NAMESPACE -class QKmsCursor : public QPlatformCursor +struct QEglFSKmsOutput { + uint32_t conn_id; + uint32_t crtc_id; + QSizeF physical_size; + drmModeModeInfo mode; + bool mode_set; + drmModeCrtc *saved_crtc; +}; + +class QEglFSKmsDevice +{ + Q_DISABLE_COPY(QEglFSKmsDevice) + + QString m_path; + int m_dri_fd; + gbm_device *m_gbm_device; + + QList m_validOutputs; + + bool setup_kms(); + static void pageFlipHandler(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); +public: + QEglFSKmsDevice(const QString &path); + + bool open(); + void close(); + + void createScreens(); + + gbm_device *device() const; + int fd() const; + + void handleDrmEvent(); +}; + +class QEglFSKmsCursor; +class QEglFSKmsScreen : public QEglFSScreen +{ + QEglFSKmsDevice *m_device; + gbm_surface *m_gbm_surface; + + gbm_bo *m_gbm_bo_current; + gbm_bo *m_gbm_bo_next; + + bool m_mode_set; + + QEglFSKmsOutput m_output; + QPoint m_pos; + QScopedPointer m_cursor; + + struct FrameBuffer { + FrameBuffer() : fb(0) {} + uint32_t fb; + }; + static void bufferDestroyedHandler(gbm_bo *bo, void *data); + FrameBuffer *framebufferForBufferObject(gbm_bo *bo); + + static QMutex m_waitForFlipMutex; + +public: + QEglFSKmsScreen(QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position); + ~QEglFSKmsScreen(); + + QRect geometry() const Q_DECL_OVERRIDE; + int depth() const Q_DECL_OVERRIDE; + QImage::Format format() const Q_DECL_OVERRIDE; + + QSizeF physicalSize() const Q_DECL_OVERRIDE; + QDpi logicalDpi() const Q_DECL_OVERRIDE; + Qt::ScreenOrientation nativeOrientation() const Q_DECL_OVERRIDE; + Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE; + + QPlatformCursor *cursor() const Q_DECL_OVERRIDE; + + QEglFSKmsDevice *device() const { return m_device; } + + gbm_surface *surface() const { return m_gbm_surface; } + gbm_surface *createSurface(); + void destroySurface(); + + void waitForFlip(); + void flip(); + void flipFinished(); + + QEglFSKmsOutput &output() { return m_output; } + void restoreMode(); +}; + +QMutex QEglFSKmsScreen::m_waitForFlipMutex; + +class QEglFSKmsCursor : public QPlatformCursor { Q_OBJECT public: - QKmsCursor(gbm_device *gbm_device, int dri_fd, uint32_t crtcId); - ~QKmsCursor(); + QEglFSKmsCursor(QEglFSKmsScreen *screen); + ~QEglFSKmsCursor(); // input methods void pointerEvent(const QMouseEvent & event) Q_DECL_OVERRIDE; @@ -74,9 +172,7 @@ public: private: void initCursorAtlas(); - gbm_device *m_gbm_device; - int m_dri_fd; - uint32_t m_crtc; + QEglFSKmsScreen *m_screen; gbm_bo *m_bo; QPoint m_pos; QPlatformCursorImage m_cursorImage; @@ -101,9 +197,7 @@ public: void platformInit() Q_DECL_OVERRIDE; void platformDestroy() Q_DECL_OVERRIDE; EGLNativeDisplayType platformDisplay() const Q_DECL_OVERRIDE; - QSizeF physicalScreenSize() const Q_DECL_OVERRIDE; - QSize screenSize() const Q_DECL_OVERRIDE; - int screenDepth() const Q_DECL_OVERRIDE; + void screenInit() Q_DECL_OVERRIDE; QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const Q_DECL_OVERRIDE; EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow, const QSize &size, @@ -112,62 +206,20 @@ public: void destroyNativeWindow(EGLNativeWindowType window) Q_DECL_OVERRIDE; bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; QPlatformCursor *createCursor(QPlatformScreen *screen) const Q_DECL_OVERRIDE; - void presentBuffer() Q_DECL_OVERRIDE; + void waitForVSync(QPlatformSurface *surface) const Q_DECL_OVERRIDE; + void presentBuffer(QPlatformSurface *surface) Q_DECL_OVERRIDE; bool supportsPBuffers() const Q_DECL_OVERRIDE; private: - bool setup_kms(); - - struct FrameBuffer { - FrameBuffer() : fb(0) {} - uint32_t fb; - }; - static void bufferDestroyedHandler(gbm_bo *bo, void *data); - FrameBuffer *framebufferForBufferObject(gbm_bo *bo); - - static void pageFlipHandler(int fd, - unsigned int sequence, - unsigned int tv_sec, - unsigned int tv_usec, - void *user_data); - -private: - // device bits - QByteArray m_device; - int m_dri_fd; - gbm_device *m_gbm_device; - - // KMS bits - drmModeConnector *m_drm_connector; - drmModeEncoder *m_drm_encoder; - drmModeModeInfo m_drm_mode; - quint32 m_drm_crtc; - - // Drawing bits - gbm_surface *m_gbm_surface; - gbm_bo *m_gbm_bo_current; - gbm_bo *m_gbm_bo_next; - bool m_flipping; - bool m_mode_set; + QEglFSKmsDevice *m_device; }; static QEglKmsHooks kms_hooks; QEglFSHooks *platformHooks = &kms_hooks; QEglKmsHooks::QEglKmsHooks() - : m_dri_fd(-1) - , m_gbm_device(Q_NULLPTR) - , m_drm_connector(Q_NULLPTR) - , m_drm_encoder(Q_NULLPTR) - , m_drm_crtc(0) - , m_gbm_surface(Q_NULLPTR) - , m_gbm_bo_current(Q_NULLPTR) - , m_gbm_bo_next(Q_NULLPTR) - , m_flipping(false) - , m_mode_set(false) -{ - -} + : m_device(Q_NULLPTR) +{} void QEglKmsHooks::platformInit() { @@ -178,52 +230,27 @@ void QEglKmsHooks::platformInit() if (devices.isEmpty()) qFatal("Could not find DRM device!"); - m_device = devices.first().toLocal8Bit(); - m_dri_fd = qt_safe_open(m_device.constData(), O_RDWR | O_CLOEXEC); - if (m_dri_fd == -1) { - qErrnoWarning("Could not open DRM device %s", m_device.constData()); - qFatal("DRM device required, aborting."); - } - - if (!setup_kms()) - qFatal("Could not set up KMS on device %s!", m_device.constData()); - - m_gbm_device = gbm_create_device(m_dri_fd); - if (!m_gbm_device) - qFatal("Could not initialize gbm on device %s!", m_device.constData()); + m_device = new QEglFSKmsDevice(devices.first()); + if (!m_device->open()) + qFatal("DRM device required, aborting"); } void QEglKmsHooks::platformDestroy() { - gbm_device_destroy(m_gbm_device); - m_gbm_device = Q_NULLPTR; - - if (qt_safe_close(m_dri_fd) == -1) - qErrnoWarning("Could not close DRM device %s", m_device.constData()); - - m_dri_fd = -1; + m_device->close(); + delete m_device; + m_device = Q_NULLPTR; } EGLNativeDisplayType QEglKmsHooks::platformDisplay() const { - return static_cast(m_gbm_device); + Q_ASSERT(m_device); + return static_cast(m_device->device()); } -QSizeF QEglKmsHooks::physicalScreenSize() const +void QEglKmsHooks::screenInit() { - return QSizeF(m_drm_connector->mmWidth, - m_drm_connector->mmHeight); -} - -QSize QEglKmsHooks::screenSize() const -{ - return QSize(m_drm_mode.hdisplay, - m_drm_mode.vdisplay); -} - -int QEglKmsHooks::screenDepth() const -{ - return 32; + m_device->createScreens(); } QSurfaceFormat QEglKmsHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) const @@ -241,31 +268,24 @@ EGLNativeWindowType QEglKmsHooks::createNativeWindow(QPlatformWindow *platformWi const QSize &size, const QSurfaceFormat &format) { - Q_UNUSED(platformWindow); Q_UNUSED(size); Q_UNUSED(format); - if (m_gbm_surface) { - qWarning("Only single window apps supported!"); + QEglFSKmsScreen *screen = static_cast(platformWindow->screen()); + if (screen->surface()) { + qWarning("Only single window per screen supported!"); return 0; } - m_gbm_surface = gbm_surface_create(m_gbm_device, - screenSize().width(), - screenSize().height(), - GBM_FORMAT_XRGB8888, - GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!m_gbm_surface) - qFatal("Could not initialize GBM surface"); - - return reinterpret_cast(m_gbm_surface); + return reinterpret_cast(screen->createSurface()); } EGLNativeWindowType QEglKmsHooks::createNativeOffscreenWindow(const QSurfaceFormat &format) { Q_UNUSED(format); + Q_ASSERT(m_device); - gbm_surface *surface = gbm_surface_create(m_gbm_device, + gbm_surface *surface = gbm_surface_create(m_device->device(), 1, 1, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); @@ -276,8 +296,6 @@ EGLNativeWindowType QEglKmsHooks::createNativeOffscreenWindow(const QSurfaceForm void QEglKmsHooks::destroyNativeWindow(EGLNativeWindowType window) { gbm_surface *surface = reinterpret_cast(window); - if (surface == m_gbm_surface) - m_gbm_surface = Q_NULLPTR; gbm_surface_destroy(surface); } @@ -296,135 +314,23 @@ bool QEglKmsHooks::hasCapability(QPlatformIntegration::Capability cap) const QPlatformCursor *QEglKmsHooks::createCursor(QPlatformScreen *screen) const { Q_UNUSED(screen); - return new QKmsCursor(m_gbm_device, m_dri_fd, m_drm_crtc); + return Q_NULLPTR; } -void QEglKmsHooks::bufferDestroyedHandler(gbm_bo *bo, void *data) +void QEglKmsHooks::waitForVSync(QPlatformSurface *surface) const { - QEglKmsHooks::FrameBuffer *fb = static_cast(data); + QWindow *window = static_cast(surface->surface()); + QEglFSKmsScreen *screen = static_cast(window->screen()->handle()); - if (fb->fb) { - gbm_device *device = gbm_bo_get_device(bo); - drmModeRmFB(gbm_device_get_fd(device), fb->fb); - } - - delete fb; + screen->waitForFlip(); } -QEglKmsHooks::FrameBuffer *QEglKmsHooks::framebufferForBufferObject(gbm_bo *bo) +void QEglKmsHooks::presentBuffer(QPlatformSurface *surface) { - { - FrameBuffer *fb = static_cast(gbm_bo_get_user_data(bo)); - if (fb) - return fb; - } + QWindow *window = static_cast(surface->surface()); + QEglFSKmsScreen *screen = static_cast(window->screen()->handle()); - uint32_t width = gbm_bo_get_width(bo); - uint32_t height = gbm_bo_get_height(bo); - uint32_t stride = gbm_bo_get_stride(bo); - uint32_t handle = gbm_bo_get_handle(bo).u32; - - QScopedPointer fb(new FrameBuffer); - - int ret = drmModeAddFB(m_dri_fd, width, height, 24, 32, - stride, handle, &fb->fb); - - if (ret) { - qWarning("Failed to create KMS FB!"); - return Q_NULLPTR; - } - - gbm_bo_set_user_data(bo, fb.data(), bufferDestroyedHandler); - return fb.take(); -} - -void QEglKmsHooks::pageFlipHandler(int fd, - unsigned int sequence, - unsigned int tv_sec, - unsigned int tv_usec, - void *user_data) -{ - Q_UNUSED(fd); - Q_UNUSED(sequence); - Q_UNUSED(tv_sec); - Q_UNUSED(tv_usec); - - QEglKmsHooks *hooks = static_cast(user_data); - - if (hooks->m_gbm_bo_current) - gbm_surface_release_buffer(hooks->m_gbm_surface, - hooks->m_gbm_bo_current); - - hooks->m_gbm_bo_current = hooks->m_gbm_bo_next; - hooks->m_gbm_bo_next = Q_NULLPTR; - - // We are no longer flipping - hooks->m_flipping = false; -} - -void QEglKmsHooks::presentBuffer() -{ - if (!m_gbm_surface) { - qWarning("Cannot sync before platform init!"); - return; - } - - m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface); - if (!m_gbm_bo_next) { - qWarning("Could not lock GBM surface front buffer!"); - return; - } - - QEglKmsHooks::FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next); - - if (!m_mode_set) { - int ret = drmModeSetCrtc(m_dri_fd, - m_drm_crtc, - fb->fb, - 0, 0, - &m_drm_connector->connector_id, 1, - &m_drm_mode); - if (ret) { - qErrnoWarning("Could not set DRM mode!"); - } else { - m_mode_set = true; - } - } - - int ret = drmModePageFlip(m_dri_fd, - m_drm_encoder->crtc_id, - fb->fb, - DRM_MODE_PAGE_FLIP_EVENT, - this); - if (ret) { - qErrnoWarning("Could not queue DRM page flip!"); - return; - } - - m_flipping = true; - - drmEventContext drmEvent = { - DRM_EVENT_CONTEXT_VERSION, - Q_NULLPTR, // vblank handler - pageFlipHandler // page flip handler - }; - - fd_set fds; - FD_ZERO(&fds); - FD_SET(m_dri_fd, &fds); - - while (m_flipping) { - ret = qt_safe_select(m_dri_fd + 1, &fds, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR); - - if (ret == 0) { - // timeout - } else if (ret == -1) { - qErrnoWarning("Error while selecting on DRM fd"); - break; - } else if (drmHandleEvent(m_dri_fd, &drmEvent)) { - qWarning("Could not handle DRM event!"); - } - } + screen->flip(); } bool QEglKmsHooks::supportsPBuffers() const @@ -432,76 +338,9 @@ bool QEglKmsHooks::supportsPBuffers() const return false; } -bool QEglKmsHooks::setup_kms() -{ - drmModeRes *resources; - drmModeConnector *connector; - drmModeEncoder *encoder; - quint32 crtc = 0; - int i; - - resources = drmModeGetResources(m_dri_fd); - if (!resources) { - qWarning("drmModeGetResources failed"); - return false; - } - - for (i = 0; i < resources->count_connectors; i++) { - connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); - if (connector == NULL) - continue; - - if (connector->connection == DRM_MODE_CONNECTED && - connector->count_modes > 0) { - break; - } - - drmModeFreeConnector(connector); - } - - if (i == resources->count_connectors) { - qWarning("No currently active connector found."); - return false; - } - - for (i = 0; i < resources->count_encoders; i++) { - encoder = drmModeGetEncoder(m_dri_fd, resources->encoders[i]); - - if (encoder == NULL) - continue; - - if (encoder->encoder_id == connector->encoder_id) - break; - - drmModeFreeEncoder(encoder); - } - - for (int j = 0; j < resources->count_crtcs; j++) { - if ((encoder->possible_crtcs & (1 << j))) { - crtc = resources->crtcs[j]; - break; - } - } - - if (crtc == 0) - qFatal("No suitable CRTC available"); - - m_drm_connector = connector; - m_drm_encoder = encoder; - m_drm_mode = connector->modes[0]; - m_drm_crtc = crtc; - - drmModeFreeResources(resources); - - return true; -} - - -QKmsCursor::QKmsCursor(gbm_device *gbm_device, int dri_fd, uint32_t crtcId) - : m_gbm_device(gbm_device) - , m_dri_fd(dri_fd) - , m_crtc(crtcId) - , m_bo(gbm_bo_create(gbm_device, 64, 64, GBM_FORMAT_ARGB8888, +QEglFSKmsCursor::QEglFSKmsCursor(QEglFSKmsScreen *screen) + : m_screen(screen) + , m_bo(gbm_bo_create(m_screen->device()->device(), 64, 64, GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE)) , m_cursorImage(0, 0, 0, 0, 0, 0) , m_visible(true) @@ -512,25 +351,25 @@ QKmsCursor::QKmsCursor(gbm_device *gbm_device, int dri_fd, uint32_t crtcId) initCursorAtlas(); } - drmModeMoveCursor(m_dri_fd, m_crtc, 0, 0); + drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); } -QKmsCursor::~QKmsCursor() +QEglFSKmsCursor::~QEglFSKmsCursor() { - drmModeSetCursor(m_dri_fd, m_crtc, 0, 0, 0); - drmModeMoveCursor(m_dri_fd, m_crtc, 0, 0); + drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0); + drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); gbm_bo_destroy(m_bo); m_bo = Q_NULLPTR; } -void QKmsCursor::pointerEvent(const QMouseEvent &event) +void QEglFSKmsCursor::pointerEvent(const QMouseEvent &event) { setPos(event.screenPos().toPoint()); } #ifndef QT_NO_CURSOR -void QKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) +void QEglFSKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) { Q_UNUSED(window); @@ -574,21 +413,21 @@ void QKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) uint32_t handle = gbm_bo_get_handle(m_bo).u32; QPoint hot = m_cursorImage.hotspot(); - int status = drmModeSetCursor2(m_dri_fd, m_crtc, handle, 64, 64, hot.x(), hot.y()); + int status = drmModeSetCursor2(m_screen->device()->fd(), m_screen->output().crtc_id, handle, 64, 64, hot.x(), hot.y()); if (status != 0) qWarning("Could not set cursor: %d", status); } #endif // QT_NO_CURSOR -QPoint QKmsCursor::pos() const +QPoint QEglFSKmsCursor::pos() const { return m_pos; } -void QKmsCursor::setPos(const QPoint &pos) +void QEglFSKmsCursor::setPos(const QPoint &pos) { QPoint adjustedPos = pos - m_cursorImage.hotspot(); - int ret = drmModeMoveCursor(m_dri_fd, m_crtc, adjustedPos.x(), adjustedPos.y()); + int ret = drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, adjustedPos.x(), adjustedPos.y()); if (ret == 0) { m_pos = pos; } else { @@ -596,7 +435,7 @@ void QKmsCursor::setPos(const QPoint &pos) } } -void QKmsCursor::initCursorAtlas() +void QEglFSKmsCursor::initCursorAtlas() { static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR"); if (json.isEmpty()) @@ -604,8 +443,8 @@ void QKmsCursor::initCursorAtlas() QFile file(QString::fromUtf8(json)); if (!file.open(QFile::ReadOnly)) { - drmModeSetCursor(m_dri_fd, m_crtc, 0, 0, 0); - drmModeMoveCursor(m_dri_fd, m_crtc, 0, 0); + drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0); + drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0); m_visible = false; return; } @@ -635,4 +474,384 @@ void QKmsCursor::initCursorAtlas() m_cursorAtlas.image = image; } +void QEglFSKmsScreen::bufferDestroyedHandler(gbm_bo *bo, void *data) +{ + FrameBuffer *fb = static_cast(data); + + if (fb->fb) { + gbm_device *device = gbm_bo_get_device(bo); + drmModeRmFB(gbm_device_get_fd(device), fb->fb); + } + + delete fb; +} + +QEglFSKmsScreen::FrameBuffer *QEglFSKmsScreen::framebufferForBufferObject(gbm_bo *bo) +{ + { + FrameBuffer *fb = static_cast(gbm_bo_get_user_data(bo)); + if (fb) + return fb; + } + + uint32_t width = gbm_bo_get_width(bo); + uint32_t height = gbm_bo_get_height(bo); + uint32_t stride = gbm_bo_get_stride(bo); + uint32_t handle = gbm_bo_get_handle(bo).u32; + + QScopedPointer fb(new FrameBuffer); + + int ret = drmModeAddFB(m_device->fd(), width, height, 24, 32, + stride, handle, &fb->fb); + + if (ret) { + qWarning("Failed to create KMS FB!"); + return Q_NULLPTR; + } + + gbm_bo_set_user_data(bo, fb.data(), bufferDestroyedHandler); + return fb.take(); + +} + +QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position) + : QEglFSScreen(eglGetDisplay(device->device())) + , m_device(device) + , m_gbm_surface(Q_NULLPTR) + , m_gbm_bo_current(Q_NULLPTR) + , m_gbm_bo_next(Q_NULLPTR) + , m_output(output) + , m_pos(position) + , m_cursor(new QEglFSKmsCursor(this)) +{ +} + +QEglFSKmsScreen::~QEglFSKmsScreen() +{ + restoreMode(); +} + +QRect QEglFSKmsScreen::geometry() const +{ + return QRect(m_pos.x(), m_pos.y(), + m_output.mode.hdisplay, + m_output.mode.vdisplay); +} + +int QEglFSKmsScreen::depth() const +{ + return 32; +} + +QImage::Format QEglFSKmsScreen::format() const +{ + return QImage::Format_RGB32; +} + +QSizeF QEglFSKmsScreen::physicalSize() const +{ + return m_output.physical_size; +} + +QDpi QEglFSKmsScreen::logicalDpi() const +{ + QSizeF ps = physicalSize(); + QSize s = geometry().size(); + + if (ps.isValid() && s.isValid()) + return QDpi(25.4 * s.width() / ps.width(), + 25.4 * s.height() / ps.height()); + else + return QDpi(100, 100); +} + +Qt::ScreenOrientation QEglFSKmsScreen::nativeOrientation() const +{ + return Qt::PrimaryOrientation; +} + +Qt::ScreenOrientation QEglFSKmsScreen::orientation() const +{ + return Qt::PrimaryOrientation; +} + +QPlatformCursor *QEglFSKmsScreen::cursor() const +{ + return m_cursor.data(); +} + +gbm_surface *QEglFSKmsScreen::createSurface() +{ + if (!m_gbm_surface) + m_gbm_surface = gbm_surface_create(m_device->device(), + geometry().width(), + geometry().height(), + GBM_FORMAT_XRGB8888, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + return m_gbm_surface; +} + +void QEglFSKmsScreen::destroySurface() +{ + if (m_gbm_bo_current) { + gbm_bo_destroy(m_gbm_bo_current); + m_gbm_bo_current = Q_NULLPTR; + } + + if (m_gbm_bo_next) { + gbm_bo_destroy(m_gbm_bo_next); + m_gbm_bo_next = Q_NULLPTR; + } + + if (m_gbm_surface) { + gbm_surface_destroy(m_gbm_surface); + m_gbm_surface = Q_NULLPTR; + } +} + +void QEglFSKmsScreen::waitForFlip() +{ + // Don't lock the mutex unless we actually need to + if (!m_gbm_bo_next) + return; + + QMutexLocker lock(&m_waitForFlipMutex); + while (m_gbm_bo_next) + m_device->handleDrmEvent(); +} + +void QEglFSKmsScreen::flip() +{ + if (!m_gbm_surface) { + qWarning("Cannot sync before platform init!"); + return; + } + + m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface); + if (!m_gbm_bo_next) { + qWarning("Could not lock GBM surface front buffer!"); + return; + } + + FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next); + + if (!m_mode_set) { + m_output.saved_crtc = drmModeGetCrtc(m_device->fd(), m_output.crtc_id); + int ret = drmModeSetCrtc(m_device->fd(), + m_output.crtc_id, + fb->fb, + 0, 0, + &m_output.conn_id, 1, + &m_output.mode); + if (ret) { + qErrnoWarning("Could not set DRM mode!"); + } else { + m_mode_set = true; + } + } + + int ret = drmModePageFlip(m_device->fd(), + m_output.crtc_id, + fb->fb, + DRM_MODE_PAGE_FLIP_EVENT, + this); + if (ret) { + qErrnoWarning("Could not queue DRM page flip!"); + gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next); + m_gbm_bo_next = Q_NULLPTR; + } +} + +void QEglFSKmsScreen::flipFinished() +{ + if (m_gbm_bo_current) + gbm_surface_release_buffer(m_gbm_surface, + m_gbm_bo_current); + + m_gbm_bo_current = m_gbm_bo_next; + m_gbm_bo_next = Q_NULLPTR; +} + +void QEglFSKmsScreen::restoreMode() +{ + if (m_mode_set && m_output.saved_crtc) { + drmModeSetCrtc(m_device->fd(), + m_output.saved_crtc->crtc_id, + m_output.saved_crtc->buffer_id, + 0, 0, + &m_output.conn_id, 1, + &m_output.saved_crtc->mode); + drmModeFreeCrtc(m_output.saved_crtc); + m_output.saved_crtc = Q_NULLPTR; + m_mode_set = false; + } +} + +static QList findConnectors(int dri_fd, drmModeRes *resources) +{ + QList connectors; + + for (int i = 0; i < resources->count_connectors; i++) { + drmModeConnector *connector = drmModeGetConnector(dri_fd, resources->connectors[i]); + if (!connector) + continue; + + if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes > 0) { + connectors.append(connector); + continue; + } + + drmModeFreeConnector(connector); + } + + return connectors; +} + +static drmModeEncoder *findEncoder(int dri_fd, drmModeRes *resources, uint32_t encoder_id) +{ + for (int i = 0; i < resources->count_encoders; i++) { + drmModeEncoder *encoder = drmModeGetEncoder(dri_fd, resources->encoders[i]); + + if (!encoder) + continue; + + if (encoder->encoder_id == encoder_id) + return encoder; + + drmModeFreeEncoder(encoder); + } + + return Q_NULLPTR; +} + +bool QEglFSKmsDevice::setup_kms() +{ + drmModeRes *resources = drmModeGetResources(m_dri_fd); + if (!resources) { + qWarning("drmModeGetResources failed"); + return false; + } + + QList connectors = findConnectors(m_dri_fd, resources); + if (connectors.isEmpty()) { + qWarning("No currently active connectors found"); + return false; + } + + while (!connectors.isEmpty()) { + drmModeConnector *connector = connectors.takeFirst(); + drmModeEncoder *encoder = findEncoder(m_dri_fd, resources, connector->encoder_id); + + if (!encoder) { + drmModeFreeConnector(connector); + continue; + } + + QEglFSKmsOutput output = { + connector->connector_id, + encoder->crtc_id, + QSizeF(connector->mmWidth, connector->mmHeight), + connector->modes[0], + false, + Q_NULLPTR + }; + + drmModeFreeEncoder(encoder); + drmModeFreeConnector(connector); + + m_validOutputs.append(output); + } + + drmModeFreeResources(resources); + + return m_validOutputs.size() > 0; +} + +void QEglFSKmsDevice::pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + Q_UNUSED(fd); + Q_UNUSED(sequence); + Q_UNUSED(tv_sec); + Q_UNUSED(tv_usec); + + QEglFSKmsScreen *screen = static_cast(user_data); + screen->flipFinished(); +} + +QEglFSKmsDevice::QEglFSKmsDevice(const QString &path) + : m_path(path) + , m_dri_fd(-1) + , m_gbm_device(Q_NULLPTR) +{ +} + +bool QEglFSKmsDevice::open() +{ + Q_ASSERT(m_dri_fd == -1); + Q_ASSERT(m_gbm_device == Q_NULLPTR); + + m_dri_fd = qt_safe_open(m_path.toLocal8Bit().constData(), O_RDWR | O_CLOEXEC); + if (m_dri_fd == -1) { + qErrnoWarning("Could not open DRM device %s", qPrintable(m_path)); + return false; + } + + m_gbm_device = gbm_create_device(m_dri_fd); + if (!m_gbm_device) { + qErrnoWarning("Could not create GBM device"); + qt_safe_close(m_dri_fd); + m_dri_fd = -1; + return false; + } + + return true; +} + +void QEglFSKmsDevice::close() +{ + if (m_gbm_device) { + gbm_device_destroy(m_gbm_device); + m_gbm_device = Q_NULLPTR; + } + + if (m_dri_fd != -1) { + qt_safe_close(m_dri_fd); + m_dri_fd = -1; + } +} + +void QEglFSKmsDevice::createScreens() +{ + if (!setup_kms()) + return; + + QEglFSIntegration *integration = static_cast(QGuiApplicationPrivate::platformIntegration()); + QPoint pos; + foreach (const QEglFSKmsOutput &output, m_validOutputs) { + integration->addScreen(new QEglFSKmsScreen(this, output, pos)); + pos.rx() += output.mode.hdisplay; + } +} + +gbm_device *QEglFSKmsDevice::device() const +{ + return m_gbm_device; +} + +int QEglFSKmsDevice::fd() const +{ + return m_dri_fd; +} + +void QEglFSKmsDevice::handleDrmEvent() +{ + drmEventContext drmEvent = { + DRM_EVENT_CONTEXT_VERSION, + Q_NULLPTR, // vblank handler + pageFlipHandler // page flip handler + }; + + drmHandleEvent(m_dri_fd, &drmEvent); +} + #include "qeglfshooks_kms.moc" diff --git a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp index 04acc4c759..ddf19face6 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp @@ -32,9 +32,13 @@ ****************************************************************************/ #include "qeglfshooks.h" +#include "qeglfsintegration.h" +#include "qeglfsscreen.h" + #include #include #include +#include #if defined(Q_OS_LINUX) #include @@ -94,6 +98,18 @@ EGLNativeDisplayType QEglFSHooks::platformDisplay() const return EGL_DEFAULT_DISPLAY; } +void QEglFSHooks::screenInit() +{ + QEglFSIntegration *integration = static_cast(QGuiApplicationPrivate::platformIntegration()); + integration->addScreen(new QEglFSScreen(integration->display())); +} + +void QEglFSHooks::screenDestroy() +{ + while (!qApp->screens().isEmpty()) + delete qApp->screens().last()->handle(); +} + QSizeF QEglFSHooks::physicalScreenSize() const { return q_physicalScreenSizeFromFb(framebuffer, screenSize()); @@ -184,8 +200,10 @@ QPlatformCursor *QEglFSHooks::createCursor(QPlatformScreen *screen) const return new QEGLPlatformCursor(screen); } -void QEglFSHooks::waitForVSync() const +void QEglFSHooks::waitForVSync(QPlatformSurface *surface) const { + Q_UNUSED(surface); + #if defined(FBIO_WAITFORVSYNC) static const bool forceSync = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCEVSYNC"); if (forceSync && framebuffer != -1) { @@ -196,8 +214,9 @@ void QEglFSHooks::waitForVSync() const #endif } -void QEglFSHooks::presentBuffer() +void QEglFSHooks::presentBuffer(QPlatformSurface *surface) { + Q_UNUSED(surface); } bool QEglFSHooks::supportsPBuffers() const diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 16a113691f..ffae64d31c 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -77,6 +77,11 @@ bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) cons return QEGLPlatformIntegration::hasCapability(cap); } +void QEglFSIntegration::addScreen(QPlatformScreen *screen) +{ + screenAdded(screen); +} + void QEglFSIntegration::initialize() { QEglFSHooks::hooks()->platformInit(); @@ -85,10 +90,13 @@ void QEglFSIntegration::initialize() if (!mDisableInputHandlers) createInputHandlers(); + + QEglFSHooks::hooks()->screenInit(); } void QEglFSIntegration::destroy() { + QEglFSHooks::hooks()->screenDestroy(); QEGLPlatformIntegration::destroy(); QEglFSHooks::hooks()->platformDestroy(); } @@ -98,11 +106,6 @@ EGLNativeDisplayType QEglFSIntegration::nativeDisplay() const return QEglFSHooks::hooks()->platformDisplay(); } -QEGLPlatformScreen *QEglFSIntegration::createScreen() const -{ - return new QEglFSScreen(display()); -} - QEGLPlatformWindow *QEglFSIntegration::createWindow(QWindow *window) const { return new QEglFSWindow(window); @@ -138,17 +141,6 @@ QPlatformOffscreenSurface *QEglFSIntegration::createOffscreenSurface(EGLDisplay // Never return null. Multiple QWindows are not supported by this plugin. } -QVariant QEglFSIntegration::styleHint(QPlatformIntegration::StyleHint hint) const -{ - switch (hint) - { - case QPlatformIntegration::ShowIsFullScreen: - return screen()->compositingWindow() == 0; - default: - return QPlatformIntegration::styleHint(hint); - } -} - EGLConfig QEglFSIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) { class Chooser : public QEglConfigChooser { diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.h b/src/plugins/platforms/eglfs/qeglfsintegration.h index 59aabebb7a..86038910ca 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.h +++ b/src/plugins/platforms/eglfs/qeglfsintegration.h @@ -45,16 +45,16 @@ class QEglFSIntegration : public QEGLPlatformIntegration public: QEglFSIntegration(); + void addScreen(QPlatformScreen *screen); + void initialize() Q_DECL_OVERRIDE; void destroy() Q_DECL_OVERRIDE; bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; - QVariant styleHint(QPlatformIntegration::StyleHint hint) const Q_DECL_OVERRIDE; static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); protected: - QEGLPlatformScreen *createScreen() const Q_DECL_OVERRIDE; QEGLPlatformWindow *createWindow(QWindow *window) const Q_DECL_OVERRIDE; QEGLPlatformContext *createContext(const QSurfaceFormat &format, QPlatformOpenGLContext *shareContext, diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp index 1645d81878..ef1f496b29 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp @@ -147,8 +147,9 @@ void QEglFSWindow::invalidateSurface() void QEglFSWindow::resetSurface() { - EGLDisplay display = static_cast(screen())->display(); - m_window = QEglFSHooks::hooks()->createNativeWindow(this, QEglFSHooks::hooks()->screenSize(), m_format); + QEglFSScreen *nativeScreen = static_cast(screen()); + EGLDisplay display = nativeScreen->display(); + m_window = QEglFSHooks::hooks()->createNativeWindow(this, nativeScreen->geometry().size(), m_format); m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL); if (m_surface == EGL_NO_SURFACE) { EGLint error = eglGetError();