platforms/drm: refactor the gbm surface

icc-effect-5.26.4
Xaver Hugl 2021-05-25 19:25:55 +02:00
parent 4b5571163e
commit da71d218c8
11 changed files with 123 additions and 273 deletions

View File

@ -371,13 +371,6 @@ target_link_libraries(testXkb
add_test(NAME kwin-testXkb COMMAND testXkb)
ecm_mark_as_test(testXkb)
if (HAVE_GBM)
add_executable(testGbmSurface test_gbm_surface.cpp ../src/plugins/platforms/drm/gbm_surface.cpp)
target_link_libraries(testGbmSurface Qt::Test)
add_test(NAME kwin-testGbmSurface COMMAND testGbmSurface)
ecm_mark_as_test(testGbmSurface)
endif()
########################################################
# Test FTrace
########################################################

View File

@ -1,108 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "../src/plugins/platforms/drm/gbm_surface.h"
#include <QtTest>
#include <gbm.h>
// mocking
struct gbm_device {
bool surfaceShouldFail = false;
};
struct gbm_surface {
uint32_t width;
uint32_t height;
uint32_t format;
uint32_t flags;
};
struct gbm_bo {
};
struct gbm_surface *gbm_surface_create(struct gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags)
{
if (gbm && gbm->surfaceShouldFail) {
return nullptr;
}
auto ret = new gbm_surface{width, height, format, flags};
return ret;
}
void gbm_surface_destroy(struct gbm_surface *surface)
{
delete surface;
}
struct gbm_bo *gbm_surface_lock_front_buffer(struct gbm_surface *surface)
{
Q_UNUSED(surface)
return new gbm_bo;
}
void gbm_surface_release_buffer(struct gbm_surface *surface, struct gbm_bo *bo)
{
Q_UNUSED(surface)
delete bo;
}
using KWin::GbmSurface;
class GbmSurfaceTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testCreate();
void testCreateFailure();
void testBo();
};
void GbmSurfaceTest::testCreate()
{
GbmSurface surface(nullptr, 2, 3, 4, 5);
gbm_surface *native = surface.surface();
QVERIFY(surface);
QCOMPARE(native->width, 2u);
QCOMPARE(native->height, 3u);
QCOMPARE(native->format, 4u);
QCOMPARE(native->flags, 5u);
}
void GbmSurfaceTest::testCreateFailure()
{
gbm_device dev{true};
GbmSurface surface(&dev, 2, 3, 4, 5);
QVERIFY(!surface);
gbm_surface *native = surface.surface();
QVERIFY(!native);
}
void GbmSurfaceTest::testBo()
{
GbmSurface surface(nullptr, 2, 3, 4, 5);
// release buffer on nullptr should not be a problem
surface.releaseBuffer(nullptr);
// now an actual buffer
auto bo = surface.lockFrontBuffer();
surface.releaseBuffer(bo);
// and a surface which fails
gbm_device dev{true};
GbmSurface surface2(&dev, 2, 3, 4, 5);
QVERIFY(!surface2.lockFrontBuffer());
auto bo2 = surface.lockFrontBuffer();
// this won't do anything
surface2.releaseBuffer(bo2);
// so we need to clean up properly
surface.releaseBuffer(bo2);
}
QTEST_GUILESS_MAIN(GbmSurfaceTest)
#include "test_gbm_surface.moc"

View File

@ -25,8 +25,6 @@ public:
virtual bool needsModeChange(DrmBuffer *b) const {Q_UNUSED(b) return false;}
virtual void releaseGbm() {}
quint32 bufferId() const {
return m_bufferId;
}

View File

@ -28,15 +28,11 @@
namespace KWin
{
GbmBuffer::GbmBuffer(const QSharedPointer<GbmSurface> &surface)
GbmBuffer::GbmBuffer(GbmSurface *surface, gbm_bo *bo)
: m_surface(surface)
, m_bo(bo)
{
m_bo = m_surface->lockFrontBuffer();
if (m_bo) {
m_stride = gbm_bo_get_stride(m_bo);
} else {
qCCritical(KWIN_DRM) << "failed to lock front buffer!" << strerror(errno);
}
m_stride = gbm_bo_get_stride(m_bo);
}
GbmBuffer::GbmBuffer(gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface)
@ -67,7 +63,8 @@ void GbmBuffer::releaseBuffer()
gbm_bo_unmap(m_bo, m_mapping);
}
if (m_surface) {
m_surface->releaseBuffer(m_bo);
m_surface->releaseBuffer(this);
m_surface = nullptr;
} else {
gbm_bo_destroy(m_bo);
}
@ -93,12 +90,9 @@ void GbmBuffer::clearBufferInterface()
m_bufferInterface = nullptr;
}
DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, const QSharedPointer<GbmSurface> &surface)
: DrmBuffer(gpu), GbmBuffer(surface)
DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, GbmSurface *surface, gbm_bo *bo)
: DrmBuffer(gpu), GbmBuffer(surface, bo)
{
if (!m_bo) {
return;
}
initialize();
}
@ -117,11 +111,6 @@ DrmGbmBuffer::~DrmGbmBuffer()
}
}
void DrmGbmBuffer::releaseGbm()
{
releaseBuffer();
}
void DrmGbmBuffer::initialize()
{
m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo));

View File

@ -30,7 +30,7 @@ class GbmBuffer : public QObject
{
Q_OBJECT
public:
GbmBuffer(const QSharedPointer<GbmSurface> &surface);
GbmBuffer(GbmSurface *surface, gbm_bo *bo);
GbmBuffer(gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface);
virtual ~GbmBuffer();
@ -49,7 +49,7 @@ public:
}
protected:
QSharedPointer<GbmSurface> m_surface;
GbmSurface *m_surface = nullptr;
gbm_bo *m_bo = nullptr;
KWaylandServer::BufferInterface *m_bufferInterface = nullptr;
@ -63,7 +63,7 @@ protected:
class DrmGbmBuffer : public DrmBuffer, public GbmBuffer
{
public:
DrmGbmBuffer(DrmGpu *gpu, const QSharedPointer<GbmSurface> &surface);
DrmGbmBuffer(DrmGpu *gpu, GbmSurface *surface, gbm_bo *bo);
DrmGbmBuffer(DrmGpu *gpu, gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface);
~DrmGbmBuffer() override;
@ -75,8 +75,6 @@ public:
}
}
void releaseGbm() override;
bool hasBo() const {
return m_bo != nullptr;
}

View File

@ -80,24 +80,6 @@ void DrmOutput::teardown()
//this is needed so that the pageflipcallback handle isn't deleted
}
void DrmOutput::releaseGbm()
{
if (auto buffer = m_crtc->current()) {
buffer->releaseGbm();
}
if (auto buffer = m_crtc->next()) {
buffer->releaseGbm();
}
if (m_primaryPlane) {
if (auto buffer = m_primaryPlane->current()) {
buffer->releaseGbm();
}
if (auto buffer = m_primaryPlane->next()) {
buffer->releaseGbm();
}
}
}
bool DrmOutput::hideCursor()
{
if (RenderLoopPrivate::get(m_renderLoop)->presentMode == RenderLoopPrivate::SyncMode::Adaptive

View File

@ -44,7 +44,6 @@ public:
///queues deleting the output after a page flip has completed.
void teardown();
void releaseGbm();
bool showCursor(DrmDumbBuffer *buffer);
bool showCursor();
bool hideCursor();

View File

@ -55,27 +55,11 @@ EglGbmBackend::~EglGbmBackend()
void EglGbmBackend::cleanupSurfaces()
{
for (auto it = m_outputs.begin(); it != m_outputs.end(); ++it) {
cleanupOutput(*it);
}
// shadow buffer needs context current for destruction
makeCurrent();
m_outputs.clear();
}
void EglGbmBackend::cleanupOutput(Output &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
output.output->releaseGbm();
output.secondaryBuffer = nullptr;
eglDestroySurface(eglDisplay(), output.eglSurface);
}
}
bool EglGbmBackend::initializeEgl()
{
initClientExtensions();
@ -151,18 +135,6 @@ bool EglGbmBackend::initRenderingContext()
}
return true;
}
EGLSurface EglGbmBackend::createEglSurface(QSharedPointer<GbmSurface> gbmSurface) const
{
auto eglSurface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(),
(void *)(gbmSurface->surface()), nullptr);
if (eglSurface == EGL_NO_SURFACE) {
qCCritical(KWIN_DRM) << "Creating EGL surface failed:" << getEglErrorString();
return EGL_NO_SURFACE;
}
return eglSurface;
}
bool EglGbmBackend::resetOutput(Output &output, DrmOutput *drmOutput)
{
output.output = drmOutput;
@ -174,27 +146,11 @@ bool EglGbmBackend::resetOutput(Output &output, DrmOutput *drmOutput)
} else {
flags |= GBM_BO_USE_LINEAR;
}
auto gbmSurface = QSharedPointer<GbmSurface>::create(m_gpu->gbmDevice(),
size.width(), size.height(),
GBM_FORMAT_XRGB8888,
flags);
auto gbmSurface = QSharedPointer<GbmSurface>::create(m_gpu, size, GBM_FORMAT_XRGB8888, flags);
if (!gbmSurface) {
qCCritical(KWIN_DRM) << "Creating GBM surface failed" << strerror(errno);
qCCritical(KWIN_DRM) << "Creating GBM surface failed:" << strerror(errno);
return false;
}
auto eglSurface = createEglSurface(gbmSurface);
if (eglSurface == EGL_NO_SURFACE) {
return false;
}
// destroy previous surface
if (output.eglSurface != EGL_NO_SURFACE) {
// gbm buffers have to be released before destroying the egl surface
output.output->releaseGbm();
output.secondaryBuffer = nullptr;
eglDestroySurface(eglDisplay(), output.eglSurface);
}
output.eglSurface = eglSurface;
output.gbmSurface = gbmSurface;
if (output.output->hardwareTransforms()) {
@ -255,7 +211,8 @@ void EglGbmBackend::removeOutput(DrmOutput *drmOutput)
return;
}
if (isPrimary()) {
cleanupOutput(*it);
// shadow buffer needs context current for destruction
makeCurrent();
} else {
renderingBackend()->removeOutput((*it).output);
}
@ -273,19 +230,7 @@ bool EglGbmBackend::swapBuffers(DrmOutput *drmOutput)
return false;
}
renderFramebufferToSurface(*it);
auto error = eglSwapBuffers(eglDisplay(), it->eglSurface);
if (error != EGL_TRUE) {
qCCritical(KWIN_DRM) << "an error occurred while swapping buffers" << error;
it->secondaryBuffer = nullptr;
return false;
}
it->secondaryBuffer = QSharedPointer<GbmBuffer>::create(it->gbmSurface);
if (it->secondaryBuffer->getBo()) {
return true;
} else {
it->secondaryBuffer = nullptr;
return false;
}
return it->gbmSurface->swapBuffers() != nullptr;
}
bool EglGbmBackend::exportFramebuffer(DrmOutput *drmOutput, void *data, const QSize &size, uint32_t stride)
@ -298,15 +243,15 @@ bool EglGbmBackend::exportFramebuffer(DrmOutput *drmOutput, void *data, const QS
if (it == m_secondaryGpuOutputs.end()) {
return false;
}
if (!it->secondaryBuffer || !it->secondaryBuffer->map(GBM_BO_TRANSFER_READ)) {
auto bo = it->gbmSurface->currentBuffer();
if (!bo->map(GBM_BO_TRANSFER_READ)) {
return false;
}
if (stride != it->secondaryBuffer->stride()) {
if (stride != bo->stride()) {
// shouldn't happen if formats are the same
return false;
}
return memcpy(data, it->secondaryBuffer->mappedData(), size.height() * stride);
return memcpy(data, bo->mappedData(), size.height() * stride);
}
int EglGbmBackend::exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride)
@ -320,13 +265,14 @@ int EglGbmBackend::exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format
if (it == m_secondaryGpuOutputs.end()) {
return -1;
}
int fd = gbm_bo_get_fd(it->secondaryBuffer->getBo());
auto bo = it->gbmSurface->currentBuffer()->getBo();
int fd = gbm_bo_get_fd(bo);
if (fd == -1) {
qCWarning(KWIN_DRM) << "failed to export gbm_bo as dma-buf:" << strerror(errno);
return -1;
}
*format = gbm_bo_get_format(it->secondaryBuffer->getBo());
*stride = gbm_bo_get_stride(it->secondaryBuffer->getBo());
*format = gbm_bo_get_format(bo);
*stride = gbm_bo_get_stride(bo);
return fd;
}
@ -406,11 +352,11 @@ void EglGbmBackend::renderFramebufferToSurface(Output &output)
bool EglGbmBackend::makeContextCurrent(const Output &output) const
{
Q_ASSERT(isPrimary());
const EGLSurface surface = output.eglSurface;
if (surface == EGL_NO_SURFACE) {
const auto surface = output.gbmSurface;
if (!surface) {
return false;
}
if (eglMakeCurrent(eglDisplay(), surface, surface, context()) == EGL_FALSE) {
if (eglMakeCurrent(eglDisplay(), surface->eglSurface(), surface->eglSurface(), context()) == EGL_FALSE) {
qCCritical(KWIN_DRM) << "eglMakeCurrent failed:" << getEglErrorString();
return false;
}
@ -503,7 +449,7 @@ void EglGbmBackend::aboutToStartPainting(int screenId, const QRegion &damagedReg
const QRegion region = damagedRegion & output.output->geometry();
QVector<EGLint> rects = regionToRects(region, output.output);
const bool correct = eglSetDamageRegionKHR(eglDisplay(), output.eglSurface,
const bool correct = eglSetDamageRegionKHR(eglDisplay(), output.gbmSurface->eglSurface(),
rects.data(), rects.count()/4);
if (!correct) {
qCWarning(KWIN_DRM) << "eglSetDamageRegionKHR failed:" << getEglErrorString();
@ -514,20 +460,7 @@ void EglGbmBackend::aboutToStartPainting(int screenId, const QRegion &damagedReg
bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion)
{
if (isPrimary() && !directScanoutActive(output)) {
if (supportsSwapBuffersWithDamage()) {
QVector<EGLint> rects = regionToRects(damagedRegion, output.output);
if (!eglSwapBuffersWithDamageEXT(eglDisplay(), output.eglSurface,
rects.data(), rects.count() / 4)) {
qCCritical(KWIN_DRM) << "eglSwapBuffersWithDamageEXT() failed:" << getEglErrorString();
return false;
}
} else {
if (!eglSwapBuffers(eglDisplay(), output.eglSurface)) {
qCCritical(KWIN_DRM) << "eglSwapBuffers() failed:" << getEglErrorString();
return false;
}
}
output.buffer = QSharedPointer<DrmGbmBuffer>::create(m_gpu, output.gbmSurface);
output.buffer = output.gbmSurface->swapBuffersForDrm();
} else if (!output.buffer) {
qCDebug(KWIN_DRM) << "imported buffer does not exist!";
return false;
@ -539,7 +472,7 @@ bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion
}
if (supportsBufferAge()) {
eglQuerySurface(eglDisplay(), output.eglSurface, EGL_BUFFER_AGE_EXT, &output.bufferAge);
eglQuerySurface(eglDisplay(), output.gbmSurface->eglSurface(), EGL_BUFFER_AGE_EXT, &output.bufferAge);
}
return true;
}

View File

@ -83,9 +83,7 @@ private:
struct Output {
DrmOutput *output = nullptr;
QSharedPointer<DrmBuffer> buffer;
QSharedPointer<GbmBuffer> secondaryBuffer;
QSharedPointer<GbmSurface> gbmSurface;
EGLSurface eglSurface = EGL_NO_SURFACE;
int bufferAge = 0;
/**
* @brief The damage history for the past 10 frames.
@ -100,7 +98,6 @@ private:
};
bool resetOutput(Output &output, DrmOutput *drmOutput);
EGLSurface createEglSurface(QSharedPointer<GbmSurface> gbmSurface) const;
bool makeContextCurrent(const Output &output) const;
void setViewport(const Output &output) const;
@ -113,9 +110,6 @@ private:
bool presentOnOutput(Output &output, const QRegion &damagedRegion);
bool directScanoutActive(const Output &output);
void cleanupOutput(Output &output);
void cleanupFramebuffer(Output &output);
QVector<Output> m_outputs;
QVector<Output> m_secondaryGpuOutputs;

View File

@ -9,36 +9,94 @@
#include "gbm_surface.h"
#include <gbm.h>
#include <errno.h>
#include "abstract_egl_backend.h"
#include "drm_gpu.h"
#include "logging.h"
namespace KWin
{
GbmSurface::GbmSurface(gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags)
: m_surface(gbm_surface_create(gbm, width, height, format, flags))
GbmSurface::GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, uint32_t flags)
: m_surface(gbm_surface_create(gpu->gbmDevice(), size.width(), size.height(), format, flags))
, m_gpu(gpu)
{
if (!m_surface) {
qCCritical(KWIN_DRM) << "Could not create gbm surface!" << strerror(errno);
return;
}
m_eglSurface = eglCreatePlatformWindowSurfaceEXT(m_gpu->eglDisplay(), m_gpu->eglBackend()->config(), (void *)(m_surface), nullptr);
if (m_eglSurface == EGL_NO_SURFACE) {
qCCritical(KWIN_DRM) << "Creating EGL surface failed!" << eglGetError();
}
}
GbmSurface::~GbmSurface()
{
auto buffers = m_lockedBuffers;
for (auto buffer : buffers) {
buffer->releaseBuffer();
}
if (m_eglSurface != EGL_NO_SURFACE) {
eglDestroySurface(m_gpu->eglDisplay(), m_eglSurface);
}
if (m_surface) {
gbm_surface_destroy(m_surface);
}
}
gbm_bo *GbmSurface::lockFrontBuffer()
QSharedPointer<DrmGbmBuffer> GbmSurface::swapBuffersForDrm()
{
if (!m_surface) {
auto error = eglSwapBuffers(m_gpu->eglDisplay(), m_eglSurface);
if (error != EGL_TRUE) {
qCCritical(KWIN_DRM) << "an error occurred while swapping buffers" << error;
return nullptr;
}
return gbm_surface_lock_front_buffer(m_surface);
}
void GbmSurface::releaseBuffer(gbm_bo *bo)
{
if (!bo || !m_surface) {
return;
auto bo = gbm_surface_lock_front_buffer(m_surface);
if (!bo) {
return nullptr;
}
gbm_surface_release_buffer(m_surface, bo);
auto buffer = QSharedPointer<DrmGbmBuffer>::create(m_gpu, this, bo);
m_currentBuffer = buffer;
m_lockedBuffers << m_currentBuffer.get();
if (!buffer->bufferId()) {
return nullptr;
}
m_currentDrmBuffer = buffer;
return buffer;
}
QSharedPointer<GbmBuffer> GbmSurface::swapBuffers()
{
auto error = eglSwapBuffers(m_gpu->eglDisplay(), m_eglSurface);
if (error != EGL_TRUE) {
qCCritical(KWIN_DRM) << "an error occurred while swapping buffers" << error;
return nullptr;
}
auto bo = gbm_surface_lock_front_buffer(m_surface);
if (!bo) {
return nullptr;
}
m_currentBuffer = QSharedPointer<GbmBuffer>::create(this, bo);
m_lockedBuffers << m_currentBuffer.get();
return m_currentBuffer;
}
void GbmSurface::releaseBuffer(GbmBuffer *buffer)
{
gbm_surface_release_buffer(m_surface, buffer->getBo());
m_lockedBuffers.removeOne(buffer);
}
QSharedPointer<GbmBuffer> GbmSurface::currentBuffer() const
{
return m_currentBuffer;
}
QSharedPointer<DrmGbmBuffer> GbmSurface::currentDrmBuffer() const
{
return m_currentDrmBuffer;
}
}

View File

@ -10,8 +10,11 @@
#define KWIN_DRM_GBM_SURFACE_H
#include <cstdint>
#include <epoxy/egl.h>
#include <QVector>
#include "drm_buffer_gbm.h"
struct gbm_bo;
struct gbm_device;
struct gbm_surface;
@ -21,22 +24,33 @@ namespace KWin
class GbmSurface
{
public:
explicit GbmSurface(gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags);
explicit GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, uint32_t flags);
~GbmSurface();
gbm_bo *lockFrontBuffer();
void releaseBuffer(gbm_bo *bo);
QSharedPointer<DrmGbmBuffer> swapBuffersForDrm();
QSharedPointer<GbmBuffer> swapBuffers();
operator bool() const {
return m_surface != nullptr;
void releaseBuffer(GbmBuffer *buffer);
QSharedPointer<GbmBuffer> currentBuffer() const;
QSharedPointer<DrmGbmBuffer> currentDrmBuffer() const;
EGLSurface eglSurface() const {
return m_eglSurface;
}
gbm_surface* surface() const {
return m_surface;
bool isValid() const {
return m_surface != nullptr && m_eglSurface != EGL_NO_SURFACE;
}
private:
gbm_surface *m_surface;
DrmGpu *m_gpu;
EGLSurface m_eglSurface = EGL_NO_SURFACE;
QSharedPointer<GbmBuffer> m_currentBuffer;
QSharedPointer<DrmGbmBuffer> m_currentDrmBuffer;
QVector<GbmBuffer*> m_lockedBuffers;
};
}