[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();
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;

View File

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

View File

@ -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
************************************************/

View File

@ -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<Output> m_outputs;
friend class EglGbmTexture;
};

View File

@ -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();

View File

@ -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.
**/