diff --git a/abstract_egl_backend.cpp b/abstract_egl_backend.cpp index a18a9734f7..4a363909a6 100644 --- a/abstract_egl_backend.cpp +++ b/abstract_egl_backend.cpp @@ -68,11 +68,18 @@ void AbstractEglBackend::cleanup() cleanupGL(); doneCurrent(); eglDestroyContext(m_display, m_context); - eglDestroySurface(m_display, m_surface); + cleanupSurfaces(); eglTerminate(m_display); eglReleaseThread(); } +void AbstractEglBackend::cleanupSurfaces() +{ + if (m_surface != EGL_NO_SURFACE) { + eglDestroySurface(m_display, m_surface); + } +} + bool AbstractEglBackend::initEglAPI() { EGLint major, minor; diff --git a/abstract_egl_backend.h b/abstract_egl_backend.h index 0caa656299..90100b0f35 100644 --- a/abstract_egl_backend.h +++ b/abstract_egl_backend.h @@ -59,6 +59,7 @@ protected: m_config = config; } void cleanup(); + virtual void cleanupSurfaces(); bool initEglAPI(); void initKWinGL(); void initBufferAge(); diff --git a/egl_gbm_backend.cpp b/egl_gbm_backend.cpp index 8302bfbdf5..f6531ba545 100644 --- a/egl_gbm_backend.cpp +++ b/egl_gbm_backend.cpp @@ -48,14 +48,24 @@ EglGbmBackend::~EglGbmBackend() { // TODO: cleanup front buffer? cleanup(); - if (m_gbmSurface) { - gbm_surface_destroy(m_gbmSurface); - } if (m_device) { gbm_device_destroy(m_device); } } +void EglGbmBackend::cleanupSurfaces() +{ + for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { + const Output &o = *it; + if (o.eglSurface != EGL_NO_SURFACE) { + eglDestroySurface(eglDisplay(), o.eglSurface); + } + if ((*it).gbmSurface) { + gbm_surface_destroy((*it).gbmSurface); + } + } +} + bool EglGbmBackend::initializeEgl() { initClientExtensions(); @@ -136,27 +146,41 @@ bool EglGbmBackend::initRenderingContext() } setContext(context); - EGLSurface surface = EGL_NO_SURFACE; - m_gbmSurface = gbm_surface_create(m_device, m_backend->size().width(), m_backend->size().height(), - GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!m_gbmSurface) { - qCCritical(KWIN_CORE) << "Create gbm surface failed"; + const auto outputs = m_backend->outputs(); + for (DrmOutput *drmOutput: outputs) { + Output o; + o.output = drmOutput; + o.gbmSurface = gbm_surface_create(m_device, drmOutput->size().width(), drmOutput->size().height(), + GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (!o.gbmSurface) { + qCCritical(KWIN_CORE) << "Create gbm surface failed"; + continue; + } + o.eglSurface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *)o.gbmSurface, nullptr); + if (o.eglSurface == EGL_NO_SURFACE) { + qCCritical(KWIN_CORE) << "Create Window Surface failed"; + gbm_surface_destroy(o.gbmSurface); + continue; + } + m_outputs << o; + } + if (m_outputs.isEmpty()) { + qCCritical(KWIN_CORE) << "Create Window Surfaces failed"; return false; } - surface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *) m_gbmSurface, nullptr); + // set our first surface as the one for the abstract backend, just to make it happy + setSurface(m_outputs.first().eglSurface); - if (surface == EGL_NO_SURFACE) { - qCCritical(KWIN_CORE) << "Create Window Surface failed"; - return false; - } - setSurface(surface); - - return makeContextCurrent(); + return makeContextCurrent(m_outputs.first()); } -bool EglGbmBackend::makeContextCurrent() +bool EglGbmBackend::makeContextCurrent(const Output &output) { - if (eglMakeCurrent(eglDisplay(), surface(), surface(), context()) == EGL_FALSE) { + const EGLSurface surface = output.eglSurface; + if (surface == EGL_NO_SURFACE) { + return false; + } + if (eglMakeCurrent(eglDisplay(), surface, surface, context()) == EGL_FALSE) { qCCritical(KWIN_CORE) << "Make Context Current failed"; return false; } @@ -166,6 +190,11 @@ bool EglGbmBackend::makeContextCurrent() qCWarning(KWIN_CORE) << "Error occurred while creating context " << error; return false; } + // TODO: ensure the viewport is set correctly each time + const QSize &overall = screens()->size(); + const QRect &v = output.output->geometry(); + // TODO: are the values correct? + glViewport(-v.x(), v.height() - overall.height() - v.y(), overall.width(), overall.height()); return true; } @@ -203,13 +232,16 @@ bool EglGbmBackend::initBufferConfigs() void EglGbmBackend::present() { - eglSwapBuffers(eglDisplay(), surface()); - auto oldBuffer = m_frontBuffer; - m_frontBuffer = m_backend->createBuffer(m_gbmSurface); - m_backend->present(m_frontBuffer); - delete oldBuffer; - if (supportsBufferAge()) { - eglQuerySurface(eglDisplay(), surface(), EGL_BUFFER_AGE_EXT, &m_bufferAge); + for (auto &o: m_outputs) { + makeContextCurrent(o); + eglSwapBuffers(eglDisplay(), o.eglSurface); + auto oldBuffer = o.buffer; + o.buffer = m_backend->createBuffer(o.gbmSurface); + m_backend->present(o.buffer, o.output); + delete oldBuffer; + if (supportsBufferAge()) { + eglQuerySurface(eglDisplay(), o.eglSurface, EGL_BUFFER_AGE_EXT, &o.bufferAge); + } } } @@ -227,12 +259,20 @@ SceneOpenGL::TexturePrivate *EglGbmBackend::createBackendTexture(SceneOpenGL::Te QRegion EglGbmBackend::prepareRenderingFrame() { QRegion repaint; - if (supportsBufferAge()) - repaint = accumulatedDamageHistory(m_bufferAge); + if (supportsBufferAge()) { + for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { + repaint = repaint.united(accumulatedDamageHistory((*it).bufferAge)); + } + } startRenderTimer(); return repaint; } +void EglGbmBackend::prepareRenderingForScreen(int screenId) +{ + makeContextCurrent(m_outputs.at(screenId)); +} + void EglGbmBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { if (damagedRegion.isEmpty()) { @@ -247,7 +287,9 @@ void EglGbmBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegi if (!renderedRegion.isEmpty()) glFlush(); - m_bufferAge = 1; + for (auto &o: m_outputs) { + o.bufferAge = 1; + } return; } present(); @@ -262,6 +304,11 @@ bool EglGbmBackend::usesOverlayWindow() const return false; } +bool EglGbmBackend::perScreenRendering() const +{ + return true; +} + /************************************************ * EglTexture ************************************************/ diff --git a/egl_gbm_backend.h b/egl_gbm_backend.h index 2b440e1dbf..25b3e5f9e6 100644 --- a/egl_gbm_backend.h +++ b/egl_gbm_backend.h @@ -29,6 +29,7 @@ namespace KWin { class DrmBackend; class DrmBuffer; +class DrmOutput; /** * @brief OpenGL Backend using Egl on a GBM surface. @@ -44,21 +45,29 @@ public: QRegion prepareRenderingFrame() override; void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; bool usesOverlayWindow() const override; + bool perScreenRendering() const override; + void prepareRenderingForScreen(int screenId) override; protected: void present() override; + void cleanupSurfaces() override; private: void init(); bool initializeEgl(); bool initBufferConfigs(); bool initRenderingContext(); - bool makeContextCurrent(); + struct Output { + DrmOutput *output = nullptr; + DrmBuffer *buffer = nullptr; + gbm_surface *gbmSurface = nullptr; + EGLSurface eglSurface = EGL_NO_SURFACE; + int bufferAge = 0; + }; + bool makeContextCurrent(const Output &output); DrmBackend *m_backend; gbm_device *m_device = nullptr; - gbm_surface *m_gbmSurface = nullptr; - DrmBuffer *m_frontBuffer = nullptr; - int m_bufferAge = 0; + QVector m_outputs; friend class EglGbmTexture; }; diff --git a/scene_opengl.cpp b/scene_opengl.cpp index a7ab18fe42..972de5d7f6 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -357,6 +357,16 @@ OverlayWindow* OpenGLBackend::overlayWindow() return NULL; } +void OpenGLBackend::prepareRenderingForScreen(int screenId) +{ + Q_UNUSED(screenId) +} + +bool OpenGLBackend::perScreenRendering() const +{ + return false; +} + /************************************************ * SceneOpenGL ***********************************************/ @@ -658,7 +668,19 @@ qint64 SceneOpenGL::paint(QRegion damage, ToplevelList toplevels) // by prepareRenderingFrame(). validRegion is the region that has been // repainted, and may be larger than updateRegion. QRegion updateRegion, validRegion; - paintScreen(&mask, damage, repaint, &updateRegion, &validRegion); // call generic implementation + if (m_backend->perScreenRendering()) { + for (int i = 0; i < screens()->count(); ++i) { + const QRect &geo = screens()->geometry(i); + QRegion update; + QRegion valid; + m_backend->prepareRenderingForScreen(i); + paintScreen(&mask, damage.intersected(geo), repaint.intersected(geo), &update, &valid); // call generic implementation + updateRegion = updateRegion.united(update); + validRegion = validRegion.united(valid); + } + } else { + paintScreen(&mask, damage, repaint, &updateRegion, &validRegion); // call generic implementation + } #ifndef KWIN_HAVE_OPENGLES const QSize &screenSize = screens()->size(); diff --git a/scene_opengl.h b/scene_opengl.h index f76fd7ec9e..248eb8a61b 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -403,6 +403,12 @@ public: virtual bool makeCurrent() = 0; virtual void doneCurrent() = 0; virtual bool usesOverlayWindow() const = 0; + /** + * Whether the rendering needs to be split per screen. + * Default implementation returns @c false. + **/ + virtual bool perScreenRendering() const; + virtual void prepareRenderingForScreen(int screenId); /** * @brief Compositor is going into idle mode, flushes any pending paints. **/