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:
parent
b482226d6c
commit
80a7362efc
7 changed files with 215 additions and 154 deletions
|
@ -14,6 +14,7 @@ set(DRM_SOURCES
|
|||
egl_multi_backend.cpp
|
||||
abstract_egl_drm_backend.cpp
|
||||
dumb_swapchain.cpp
|
||||
shadowbuffer.cpp
|
||||
)
|
||||
|
||||
if (HAVE_GBM)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
131
src/plugins/platforms/drm/shadowbuffer.cpp
Normal file
131
src/plugins/platforms/drm/shadowbuffer.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
38
src/plugins/platforms/drm/shadowbuffer.h
Normal file
38
src/plugins/platforms/drm/shadowbuffer.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue