platforms/drm: refactor post processing bits

Putting the OpenGL post processing rotation into its own class cleans
the EglGbmBackend code up a bit and adds post processing rotation for
the EglStreamBackend
This commit is contained in:
Xaver Hugl 2021-06-22 02:23:17 +02:00
parent b482226d6c
commit 80a7362efc
7 changed files with 215 additions and 154 deletions

View file

@ -14,6 +14,7 @@ set(DRM_SOURCES
egl_multi_backend.cpp
abstract_egl_drm_backend.cpp
dumb_swapchain.cpp
shadowbuffer.cpp
)
if (HAVE_GBM)

View file

@ -24,6 +24,7 @@
#include "linux_dmabuf.h"
#include "dumb_swapchain.h"
#include "kwineglutils_p.h"
#include "shadowbuffer.h"
// kwin libs
#include <kwinglplatform.h>
#include <kwineglimagetexture.h>
@ -60,21 +61,12 @@ void EglGbmBackend::cleanupSurfaces()
m_outputs.clear();
}
void EglGbmBackend::cleanupFramebuffer(Output &output)
{
if (!output.render.framebuffer) {
return;
}
makeContextCurrent(output);
glDeleteTextures(1, &output.render.texture);
output.render.texture = 0;
glDeleteFramebuffers(1, &output.render.framebuffer);
output.render.framebuffer = 0;
}
void EglGbmBackend::cleanupOutput(Output &output)
{
cleanupFramebuffer(output);
if (output.shadowBuffer) {
makeContextCurrent(output);
output.shadowBuffer = nullptr;
}
if (output.eglSurface != EGL_NO_SURFACE) {
// gbm buffers have to be released before destroying the egl surface
@ -205,7 +197,15 @@ bool EglGbmBackend::resetOutput(Output &output, DrmOutput *drmOutput)
output.eglSurface = eglSurface;
output.gbmSurface = gbmSurface;
resetFramebuffer(output);
if (output.output->hardwareTransforms()) {
output.shadowBuffer = nullptr;
} else {
makeContextCurrent(output);
output.shadowBuffer = QSharedPointer<ShadowBuffer>::create(output.output->pixelSize());
if (!output.shadowBuffer->isComplete()) {
return false;
}
}
return true;
}
@ -393,138 +393,14 @@ void EglGbmBackend::importFramebuffer(Output &output) const
// TODO turn off output?
}
const float vertices[] = {
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, -1.0f,
1.0f, 1.0f,
};
const float texCoords[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
bool EglGbmBackend::resetFramebuffer(Output &output)
{
cleanupFramebuffer(output);
if (output.output->hardwareTransforms()) {
// No need for an extra render target.
return true;
}
makeContextCurrent(output);
glGenFramebuffers(1, &output.render.framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, output.render.framebuffer);
GLRenderTarget::setKWinFramebuffer(output.render.framebuffer);
glGenTextures(1, &output.render.texture);
glBindTexture(GL_TEXTURE_2D, output.render.texture);
const QSize texSize = output.output->pixelSize();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texSize.width(), texSize.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
output.render.texture, 0);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
qCWarning(KWIN_DRM) << "Error: framebuffer not complete";
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
GLRenderTarget::setKWinFramebuffer(0);
return true;
}
void EglGbmBackend::initRenderTarget(Output &output)
{
if (output.render.vbo) {
// Already initialized.
return;
}
QSharedPointer<GLVertexBuffer> vbo(new GLVertexBuffer(KWin::GLVertexBuffer::Static));
vbo->setData(6, 2, vertices, texCoords);
output.render.vbo = vbo;
}
void EglGbmBackend::renderFramebufferToSurface(Output &output)
{
if (!output.render.framebuffer) {
if (!output.shadowBuffer) {
// No additional render target.
return;
}
makeContextCurrent(output);
const auto size = output.output->modeSize();
glViewport(0, 0, size.width(), size.height());
auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
QMatrix4x4 mvpMatrix;
const DrmOutput *drmOutput = output.output;
switch (drmOutput->transform()) {
case DrmOutput::Transform::Normal:
case DrmOutput::Transform::Flipped:
break;
case DrmOutput::Transform::Rotated90:
case DrmOutput::Transform::Flipped90:
mvpMatrix.rotate(90, 0, 0, 1);
break;
case DrmOutput::Transform::Rotated180:
case DrmOutput::Transform::Flipped180:
mvpMatrix.rotate(180, 0, 0, 1);
break;
case DrmOutput::Transform::Rotated270:
case DrmOutput::Transform::Flipped270:
mvpMatrix.rotate(270, 0, 0, 1);
break;
}
switch (drmOutput->transform()) {
case DrmOutput::Transform::Flipped:
case DrmOutput::Transform::Flipped90:
case DrmOutput::Transform::Flipped180:
case DrmOutput::Transform::Flipped270:
mvpMatrix.scale(-1, 1);
break;
default:
break;
}
shader->setUniform(GLShader::ModelViewProjectionMatrix, mvpMatrix);
initRenderTarget(output);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
GLRenderTarget::setKWinFramebuffer(0);
glBindTexture(GL_TEXTURE_2D, output.render.texture);
output.render.vbo->render(GL_TRIANGLES);
ShaderManager::instance()->popShader();
glBindTexture(GL_TEXTURE_2D, 0);
}
void EglGbmBackend::prepareRenderFramebuffer(const Output &output) const
{
// When render.framebuffer is 0 we may just reset to the screen framebuffer.
glBindFramebuffer(GL_FRAMEBUFFER, output.render.framebuffer);
GLRenderTarget::setKWinFramebuffer(output.render.framebuffer);
output.shadowBuffer->render(output.output);
}
bool EglGbmBackend::makeContextCurrent(const Output &output) const
@ -706,7 +582,9 @@ QRegion EglGbmBackend::beginFrame(int screenId)
QRegion EglGbmBackend::prepareRenderingForOutput(Output &output) const
{
makeContextCurrent(output);
prepareRenderFramebuffer(output);
if (output.shadowBuffer) {
output.shadowBuffer->bind();
}
setViewport(output);
if (supportsBufferAge()) {
@ -861,8 +739,8 @@ QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *abstra
}
DrmOutput *drmOutput = itOutput->output;
if (!drmOutput->hardwareTransforms()) {
const auto glTexture = QSharedPointer<KWin::GLTexture>::create(itOutput->render.texture, GL_RGBA8, drmOutput->pixelSize());
if (itOutput->shadowBuffer) {
const auto glTexture = QSharedPointer<KWin::GLTexture>::create(itOutput->shadowBuffer->texture(), GL_RGBA8, drmOutput->pixelSize());
glTexture->setYInverted(true);
return glTexture;
}

View file

@ -32,6 +32,7 @@ class DrmOutput;
class GbmSurface;
class GbmBuffer;
class DumbSwapchain;
class ShadowBuffer;
/**
* @brief OpenGL Backend using Egl on a GBM surface.
@ -91,11 +92,7 @@ private:
*/
QList<QRegion> damageHistory;
struct {
GLuint framebuffer = 0;
GLuint texture = 0;
QSharedPointer<GLVertexBuffer> vbo;
} render;
QSharedPointer<ShadowBuffer> shadowBuffer;
KWaylandServer::SurfaceInterface *surfaceInterface = nullptr;
ImportMode importMode = ImportMode::Dmabuf;
@ -108,9 +105,6 @@ private:
bool makeContextCurrent(const Output &output) const;
void setViewport(const Output &output) const;
bool resetFramebuffer(Output &output);
void initRenderTarget(Output &output);
void prepareRenderFramebuffer(const Output &output) const;
void renderFramebufferToSurface(Output &output);
QRegion prepareRenderingForOutput(Output &output) const;

View file

@ -25,6 +25,7 @@
#include "drm_gpu.h"
#include "dumb_swapchain.h"
#include "kwineglutils_p.h"
#include "shadowbuffer.h"
#include <QOpenGLContext>
#include <KWaylandServer/buffer_interface.h>
@ -88,13 +89,13 @@ EglStreamBackend::~EglStreamBackend()
void EglStreamBackend::cleanupSurfaces()
{
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
for (auto it = m_outputs.begin(); it != m_outputs.end(); ++it) {
cleanupOutput(*it);
}
m_outputs.clear();
}
void EglStreamBackend::cleanupOutput(const Output &o)
void EglStreamBackend::cleanupOutput(Output &o)
{
if (o.eglSurface != EGL_NO_SURFACE) {
eglDestroySurface(eglDisplay(), o.eglSurface);
@ -102,6 +103,7 @@ void EglStreamBackend::cleanupOutput(const Output &o)
if (o.eglStream != EGL_NO_STREAM_KHR) {
pEglDestroyStreamKHR(eglDisplay(), o.eglStream);
}
o.shadowBuffer = nullptr;
}
bool EglStreamBackend::initializeEgl()
@ -356,6 +358,15 @@ bool EglStreamBackend::resetOutput(Output &o, DrmOutput *drmOutput)
o.eglStream = stream;
o.eglSurface = eglSurface;
if (!drmOutput->hardwareTransforms()) {
makeContextCurrent(o);
o.shadowBuffer = QSharedPointer<ShadowBuffer>::create(o.output->pixelSize());
if (!o.shadowBuffer->isComplete()) {
cleanupOutput(o);
return false;
}
}
} else {
QSize size = drmOutput->hardwareTransforms() ? drmOutput->pixelSize() : drmOutput->modeSize();
o.dumbSwapchain = QSharedPointer<DumbSwapchain>::create(m_gpu, size);
@ -477,6 +488,9 @@ QRegion EglStreamBackend::beginFrame(int screenId)
const Output &o = m_outputs.at(screenId);
if (isPrimary()) {
makeContextCurrent(o);
if (o.shadowBuffer) {
o.shadowBuffer->bind();
}
return o.output->geometry();
} else {
return renderingBackend()->beginFrameForSecondaryGpu(o.output);
@ -496,6 +510,9 @@ void EglStreamBackend::endFrame(int screenId, const QRegion &renderedRegion, con
QSharedPointer<DrmDumbBuffer> buffer;
if (isPrimary()) {
buffer = renderOutput.buffer;
if (renderOutput.shadowBuffer) {
renderOutput.shadowBuffer->render(renderOutput.output);
}
if (!eglSwapBuffers(eglDisplay(), renderOutput.eglSurface)) {
qCCritical(KWIN_DRM) << "eglSwapBuffers() failed:" << getEglErrorString();
frameFailed = true;

View file

@ -20,6 +20,7 @@ namespace KWin
class DrmOutput;
class DrmDumbBuffer;
class DumbSwapchain;
class ShadowBuffer;
/**
* @brief OpenGL Backend using Egl with an EGLDevice.
@ -65,13 +66,14 @@ private:
QSharedPointer<DrmDumbBuffer> buffer;
EGLSurface eglSurface = EGL_NO_SURFACE;
EGLStreamKHR eglStream = EGL_NO_STREAM_KHR;
QSharedPointer<ShadowBuffer> shadowBuffer;
// for operation as secondary GPU
QSharedPointer<DumbSwapchain> dumbSwapchain;
};
bool resetOutput(Output &output, DrmOutput *drmOutput);
bool makeContextCurrent(const Output &output);
void cleanupOutput(const Output &output);
void cleanupOutput(Output &output);
QVector<Output> m_outputs;
KWaylandServer::EglStreamControllerInterface *m_eglStreamControllerInterface;

View file

@ -0,0 +1,131 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "shadowbuffer.h"
#include "logging.h"
#include "drm_output.h"
namespace KWin
{
static const float vertices[] = {
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, -1.0f,
1.0f, 1.0f,
};
static const float texCoords[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
ShadowBuffer::ShadowBuffer(const QSize &size)
{
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
GLRenderTarget::setKWinFramebuffer(m_framebuffer);
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
qCCritical(KWIN_DRM) << "Error: framebuffer not complete!";
return;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
GLRenderTarget::setKWinFramebuffer(0);
m_vbo.reset(new GLVertexBuffer(KWin::GLVertexBuffer::Static));
m_vbo->setData(6, 2, vertices, texCoords);
}
ShadowBuffer::~ShadowBuffer()
{
glDeleteTextures(1, &m_texture);
glDeleteFramebuffers(1, &m_framebuffer);
}
void ShadowBuffer::render(DrmOutput *output)
{
const auto size = output->modeSize();
glViewport(0, 0, size.width(), size.height());
auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
QMatrix4x4 mvpMatrix;
switch (output->transform()) {
case DrmOutput::Transform::Normal:
case DrmOutput::Transform::Flipped:
break;
case DrmOutput::Transform::Rotated90:
case DrmOutput::Transform::Flipped90:
mvpMatrix.rotate(90, 0, 0, 1);
break;
case DrmOutput::Transform::Rotated180:
case DrmOutput::Transform::Flipped180:
mvpMatrix.rotate(180, 0, 0, 1);
break;
case DrmOutput::Transform::Rotated270:
case DrmOutput::Transform::Flipped270:
mvpMatrix.rotate(270, 0, 0, 1);
break;
}
switch (output->transform()) {
case DrmOutput::Transform::Flipped:
case DrmOutput::Transform::Flipped90:
case DrmOutput::Transform::Flipped180:
case DrmOutput::Transform::Flipped270:
mvpMatrix.scale(-1, 1);
break;
default:
break;
}
shader->setUniform(GLShader::ModelViewProjectionMatrix, mvpMatrix);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
GLRenderTarget::setKWinFramebuffer(0);
glBindTexture(GL_TEXTURE_2D, m_texture);
m_vbo->render(GL_TRIANGLES);
ShaderManager::instance()->popShader();
glBindTexture(GL_TEXTURE_2D, 0);
}
void ShadowBuffer::bind()
{
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
GLRenderTarget::setKWinFramebuffer(m_framebuffer);
}
bool ShadowBuffer::isComplete() const
{
return m_texture && m_framebuffer && m_vbo;
}
int ShadowBuffer::texture() const
{
return m_texture;
}
}

View file

@ -0,0 +1,38 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QSize>
#include <kwinglutils.h>
namespace KWin
{
class DrmOutput;
class ShadowBuffer
{
public:
ShadowBuffer(const QSize &size);
~ShadowBuffer();
bool isComplete() const;
void bind();
void render(DrmOutput *output);
int texture() const;
private:
GLuint m_texture;
GLuint m_framebuffer;
QScopedPointer<GLVertexBuffer> m_vbo;
};
}