[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();
|
||||
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;
|
||||
|
|
|
@ -59,6 +59,7 @@ protected:
|
|||
m_config = config;
|
||||
}
|
||||
void cleanup();
|
||||
virtual void cleanupSurfaces();
|
||||
bool initEglAPI();
|
||||
void initKWinGL();
|
||||
void initBufferAge();
|
||||
|
|
|
@ -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
|
||||
************************************************/
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
**/
|
||||
|
|
Loading…
Reference in a new issue