[SceneOpenGL] Render per DrmOutput in EglGbmBackend

For each DrmOutput a gbm_surface and EglSurface is created. When
rendering per screen the context is made current on each of the
surfaces.

Note: viewport handling needs to be improved by e.g. passing through
the scene to restore to correct viewport after dropping an FBO.

Furthermore it seems like buffer age is not working correctly in this
setup (not overly surprising).

Dynamic changes are not yet supported.
icc-effect-5.14.5
Martin Gräßlin 2015-04-17 15:48:55 +02:00
parent 5c79f777a4
commit 7c8c1dac0a
6 changed files with 126 additions and 34 deletions

View File

@ -68,11 +68,18 @@ void AbstractEglBackend::cleanup()
cleanupGL(); cleanupGL();
doneCurrent(); doneCurrent();
eglDestroyContext(m_display, m_context); eglDestroyContext(m_display, m_context);
eglDestroySurface(m_display, m_surface); cleanupSurfaces();
eglTerminate(m_display); eglTerminate(m_display);
eglReleaseThread(); eglReleaseThread();
} }
void AbstractEglBackend::cleanupSurfaces()
{
if (m_surface != EGL_NO_SURFACE) {
eglDestroySurface(m_display, m_surface);
}
}
bool AbstractEglBackend::initEglAPI() bool AbstractEglBackend::initEglAPI()
{ {
EGLint major, minor; EGLint major, minor;

View File

@ -59,6 +59,7 @@ protected:
m_config = config; m_config = config;
} }
void cleanup(); void cleanup();
virtual void cleanupSurfaces();
bool initEglAPI(); bool initEglAPI();
void initKWinGL(); void initKWinGL();
void initBufferAge(); void initBufferAge();

View File

@ -48,14 +48,24 @@ EglGbmBackend::~EglGbmBackend()
{ {
// TODO: cleanup front buffer? // TODO: cleanup front buffer?
cleanup(); cleanup();
if (m_gbmSurface) {
gbm_surface_destroy(m_gbmSurface);
}
if (m_device) { if (m_device) {
gbm_device_destroy(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() bool EglGbmBackend::initializeEgl()
{ {
initClientExtensions(); initClientExtensions();
@ -136,27 +146,41 @@ bool EglGbmBackend::initRenderingContext()
} }
setContext(context); setContext(context);
EGLSurface surface = EGL_NO_SURFACE; const auto outputs = m_backend->outputs();
m_gbmSurface = gbm_surface_create(m_device, m_backend->size().width(), m_backend->size().height(), for (DrmOutput *drmOutput: outputs) {
GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); Output o;
if (!m_gbmSurface) { o.output = drmOutput;
qCCritical(KWIN_CORE) << "Create gbm surface failed"; 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; 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) { return makeContextCurrent(m_outputs.first());
qCCritical(KWIN_CORE) << "Create Window Surface failed";
return false;
}
setSurface(surface);
return makeContextCurrent();
} }
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"; qCCritical(KWIN_CORE) << "Make Context Current failed";
return false; return false;
} }
@ -166,6 +190,11 @@ bool EglGbmBackend::makeContextCurrent()
qCWarning(KWIN_CORE) << "Error occurred while creating context " << error; qCWarning(KWIN_CORE) << "Error occurred while creating context " << error;
return false; 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; return true;
} }
@ -203,13 +232,16 @@ bool EglGbmBackend::initBufferConfigs()
void EglGbmBackend::present() void EglGbmBackend::present()
{ {
eglSwapBuffers(eglDisplay(), surface()); for (auto &o: m_outputs) {
auto oldBuffer = m_frontBuffer; makeContextCurrent(o);
m_frontBuffer = m_backend->createBuffer(m_gbmSurface); eglSwapBuffers(eglDisplay(), o.eglSurface);
m_backend->present(m_frontBuffer); auto oldBuffer = o.buffer;
delete oldBuffer; o.buffer = m_backend->createBuffer(o.gbmSurface);
if (supportsBufferAge()) { m_backend->present(o.buffer, o.output);
eglQuerySurface(eglDisplay(), surface(), EGL_BUFFER_AGE_EXT, &m_bufferAge); 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 EglGbmBackend::prepareRenderingFrame()
{ {
QRegion repaint; QRegion repaint;
if (supportsBufferAge()) if (supportsBufferAge()) {
repaint = accumulatedDamageHistory(m_bufferAge); for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
repaint = repaint.united(accumulatedDamageHistory((*it).bufferAge));
}
}
startRenderTimer(); startRenderTimer();
return repaint; return repaint;
} }
void EglGbmBackend::prepareRenderingForScreen(int screenId)
{
makeContextCurrent(m_outputs.at(screenId));
}
void EglGbmBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) void EglGbmBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
{ {
if (damagedRegion.isEmpty()) { if (damagedRegion.isEmpty()) {
@ -247,7 +287,9 @@ void EglGbmBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegi
if (!renderedRegion.isEmpty()) if (!renderedRegion.isEmpty())
glFlush(); glFlush();
m_bufferAge = 1; for (auto &o: m_outputs) {
o.bufferAge = 1;
}
return; return;
} }
present(); present();
@ -262,6 +304,11 @@ bool EglGbmBackend::usesOverlayWindow() const
return false; return false;
} }
bool EglGbmBackend::perScreenRendering() const
{
return true;
}
/************************************************ /************************************************
* EglTexture * EglTexture
************************************************/ ************************************************/

View File

@ -29,6 +29,7 @@ namespace KWin
{ {
class DrmBackend; class DrmBackend;
class DrmBuffer; class DrmBuffer;
class DrmOutput;
/** /**
* @brief OpenGL Backend using Egl on a GBM surface. * @brief OpenGL Backend using Egl on a GBM surface.
@ -44,21 +45,29 @@ public:
QRegion prepareRenderingFrame() override; QRegion prepareRenderingFrame() override;
void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override;
bool usesOverlayWindow() const override; bool usesOverlayWindow() const override;
bool perScreenRendering() const override;
void prepareRenderingForScreen(int screenId) override;
protected: protected:
void present() override; void present() override;
void cleanupSurfaces() override;
private: private:
void init(); void init();
bool initializeEgl(); bool initializeEgl();
bool initBufferConfigs(); bool initBufferConfigs();
bool initRenderingContext(); 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; DrmBackend *m_backend;
gbm_device *m_device = nullptr; gbm_device *m_device = nullptr;
gbm_surface *m_gbmSurface = nullptr; QVector<Output> m_outputs;
DrmBuffer *m_frontBuffer = nullptr;
int m_bufferAge = 0;
friend class EglGbmTexture; friend class EglGbmTexture;
}; };

View File

@ -357,6 +357,16 @@ OverlayWindow* OpenGLBackend::overlayWindow()
return NULL; return NULL;
} }
void OpenGLBackend::prepareRenderingForScreen(int screenId)
{
Q_UNUSED(screenId)
}
bool OpenGLBackend::perScreenRendering() const
{
return false;
}
/************************************************ /************************************************
* SceneOpenGL * SceneOpenGL
***********************************************/ ***********************************************/
@ -658,7 +668,19 @@ qint64 SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
// by prepareRenderingFrame(). validRegion is the region that has been // by prepareRenderingFrame(). validRegion is the region that has been
// repainted, and may be larger than updateRegion. // repainted, and may be larger than updateRegion.
QRegion updateRegion, validRegion; 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 #ifndef KWIN_HAVE_OPENGLES
const QSize &screenSize = screens()->size(); const QSize &screenSize = screens()->size();

View File

@ -403,6 +403,12 @@ public:
virtual bool makeCurrent() = 0; virtual bool makeCurrent() = 0;
virtual void doneCurrent() = 0; virtual void doneCurrent() = 0;
virtual bool usesOverlayWindow() const = 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. * @brief Compositor is going into idle mode, flushes any pending paints.
**/ **/