[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.
This commit is contained in:
parent
5c79f777a4
commit
7c8c1dac0a
6 changed files with 126 additions and 34 deletions
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
************************************************/
|
************************************************/
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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.
|
||||||
**/
|
**/
|
||||||
|
|
Loading…
Reference in a new issue