platforms/drm: improve buffer handling with GbmBuffer

This commit is contained in:
Xaver Hugl 2021-03-29 13:17:44 +02:00
parent dbdc30da7b
commit 50f03ac6dc
8 changed files with 105 additions and 128 deletions

View file

@ -17,9 +17,8 @@ namespace KWin
class DrmGpu;
class DrmBuffer : public QObject
class DrmBuffer
{
Q_OBJECT
public:
DrmBuffer(DrmGpu *gpu);
virtual ~DrmBuffer() = default;
@ -34,8 +33,6 @@ public:
return m_size;
}
virtual void releaseGbm() {}
DrmGpu *gpu() const {
return m_gpu;
}

View file

@ -28,12 +28,44 @@
namespace KWin
{
// DrmSurfaceBuffer
DrmSurfaceBuffer::DrmSurfaceBuffer(DrmGpu *gpu, const QSharedPointer<GbmSurface> &surface)
: DrmBuffer(gpu)
, m_surface(surface)
GbmBuffer::GbmBuffer(const QSharedPointer<GbmSurface> &surface)
: m_surface(surface)
{
m_bo = m_surface->lockFrontBuffer();
}
GbmBuffer::GbmBuffer(gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface)
: m_bo(buffer)
, m_bufferInterface(bufferInterface)
{
if (m_bufferInterface) {
m_bufferInterface->ref();
connect(m_bufferInterface, &KWaylandServer::BufferInterface::aboutToBeDestroyed, this, &GbmBuffer::clearBufferInterface);
}
}
GbmBuffer::~GbmBuffer()
{
if (m_bufferInterface) {
clearBufferInterface();
}
if (m_surface) {
m_surface->releaseBuffer(m_bo);
} else if (m_bo) {
gbm_bo_destroy(m_bo);
}
}
void GbmBuffer::clearBufferInterface()
{
disconnect(m_bufferInterface, &KWaylandServer::BufferInterface::aboutToBeDestroyed, this, &DrmGbmBuffer::clearBufferInterface);
m_bufferInterface->unref();
m_bufferInterface = nullptr;
}
DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, const QSharedPointer<GbmSurface> &surface)
: DrmBuffer(gpu), GbmBuffer(surface)
{
if (!m_bo) {
qCWarning(KWIN_DRM) << "Locking front buffer failed";
return;
@ -41,45 +73,20 @@ DrmSurfaceBuffer::DrmSurfaceBuffer(DrmGpu *gpu, const QSharedPointer<GbmSurface>
initialize();
}
DrmSurfaceBuffer::DrmSurfaceBuffer(DrmGpu *gpu, gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface)
: DrmBuffer(gpu)
, m_bo(buffer)
, m_bufferInterface(bufferInterface)
DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface)
: DrmBuffer(gpu), GbmBuffer(buffer, bufferInterface)
{
if (m_bufferInterface) {
m_bufferInterface->ref();
connect(m_bufferInterface, &KWaylandServer::BufferInterface::aboutToBeDestroyed, this, &DrmSurfaceBuffer::clearBufferInterface);
}
initialize();
}
DrmSurfaceBuffer::~DrmSurfaceBuffer()
DrmGbmBuffer::~DrmGbmBuffer()
{
if (m_bufferId) {
drmModeRmFB(m_gpu->fd(), m_bufferId);
}
releaseGbm();
if (m_bufferInterface) {
clearBufferInterface();
}
}
void DrmSurfaceBuffer::releaseGbm()
{
if (m_surface) {
m_surface->releaseBuffer(m_bo);
}
m_bo = nullptr;
}
void DrmSurfaceBuffer::clearBufferInterface()
{
disconnect(m_bufferInterface, &KWaylandServer::BufferInterface::aboutToBeDestroyed, this, &DrmSurfaceBuffer::clearBufferInterface);
m_bufferInterface->unref();
m_bufferInterface = nullptr;
}
void DrmSurfaceBuffer::initialize()
void DrmGbmBuffer::initialize()
{
m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo));
uint32_t handles[4] = { };

View file

@ -26,15 +26,35 @@ namespace KWin
class GbmSurface;
class DrmSurfaceBuffer : public DrmBuffer
class GbmBuffer : public QObject
{
Q_OBJECT
public:
GbmBuffer(const QSharedPointer<GbmSurface> &surface);
GbmBuffer(gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface);
virtual ~GbmBuffer();
gbm_bo* getBo() const {
return m_bo;
}
protected:
QSharedPointer<GbmSurface> m_surface;
gbm_bo *m_bo = nullptr;
KWaylandServer::BufferInterface *m_bufferInterface = nullptr;
void clearBufferInterface();
};
class DrmGbmBuffer : public DrmBuffer, public GbmBuffer
{
public:
DrmSurfaceBuffer(DrmGpu *gpu, const QSharedPointer<GbmSurface> &surface);
DrmSurfaceBuffer(DrmGpu *gpu, gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface);
~DrmSurfaceBuffer() override;
DrmGbmBuffer(DrmGpu *gpu, const QSharedPointer<GbmSurface> &surface);
DrmGbmBuffer(DrmGpu *gpu, gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface);
~DrmGbmBuffer() override;
bool needsModeChange(DrmBuffer *b) const override {
if (DrmSurfaceBuffer *sb = dynamic_cast<DrmSurfaceBuffer*>(b)) {
if (DrmGbmBuffer *sb = dynamic_cast<DrmGbmBuffer*>(b)) {
return hasBo() != sb->hasBo();
} else {
return true;
@ -45,18 +65,7 @@ public:
return m_bo != nullptr;
}
gbm_bo* getBo() const {
return m_bo;
}
void releaseGbm() override;
private:
QSharedPointer<GbmSurface> m_surface;
gbm_bo *m_bo = nullptr;
KWaylandServer::BufferInterface *m_bufferInterface = nullptr;
void clearBufferInterface();
void initialize();
};

View file

@ -45,6 +45,9 @@ public:
QSharedPointer<DrmBuffer> next() {
return m_nextBuffer;
}
void setCurrent(const QSharedPointer<DrmBuffer> &buffer) {
m_currentBuffer = buffer;
}
void setNext(const QSharedPointer<DrmBuffer> &buffer) {
m_nextBuffer = buffer;
}

View file

@ -77,14 +77,12 @@ void DrmOutput::teardown()
//this is needed so that the pageflipcallback handle isn't deleted
}
void DrmOutput::releaseGbm()
void DrmOutput::releaseBuffers()
{
if (const auto &b = m_crtc->current()) {
b->releaseGbm();
}
if (m_primaryPlane && m_primaryPlane->current()) {
m_primaryPlane->current()->releaseGbm();
}
m_crtc->setCurrent(nullptr);
m_crtc->setNext(nullptr);
m_primaryPlane->setCurrent(nullptr);
m_primaryPlane->setNext(nullptr);
}
bool DrmOutput::hideCursor()
@ -566,23 +564,11 @@ void DrmOutput::pageFlipped()
return;
}
if (m_gpu->atomicModeSetting()) {
if (!m_primaryPlane->next()) {
if (m_primaryPlane->current()) {
m_primaryPlane->current()->releaseGbm();
}
return;
}
for (DrmPlane *p : m_nextPlanesFlipList) {
p->flipBuffer();
}
m_nextPlanesFlipList.clear();
} else {
if (!m_crtc->next()) {
// on manual vt switch
if (const auto &b = m_crtc->current()) {
b->releaseGbm();
}
}
m_crtc->flipBuffer();
}

View file

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

View file

@ -64,20 +64,13 @@ void EglGbmBackend::cleanupFramebuffer(Output &output)
void EglGbmBackend::cleanupOutput(Output &output)
{
cleanupFramebuffer(output);
output.output->releaseGbm();
output.output->releaseBuffers();
output.buffer = nullptr;
output.secondaryBuffer = nullptr;
if (output.eglSurface != EGL_NO_SURFACE) {
eglDestroySurface(eglDisplay(), output.eglSurface);
}
if (output.secondaryGbmBo) {
output.gbmSurface.get()->releaseBuffer(output.secondaryGbmBo);
}
if (output.directScanoutBuffer) {
gbm_bo_destroy(output.directScanoutBuffer);
}
if (output.dmabufFd) {
close(output.dmabufFd);
}
}
bool EglGbmBackend::initializeEgl()
@ -271,30 +264,22 @@ int EglGbmBackend::getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32
if (it == m_secondaryGpuOutputs.end()) {
return -1;
}
if (it->dmabufFd) {
close(it->dmabufFd);
it->dmabufFd = 0;
}
if (it->secondaryGbmBo) {
it->gbmSurface.get()->releaseBuffer(it->secondaryGbmBo);
it->secondaryGbmBo = nullptr;
}
renderFramebufferToSurface(*it);
auto error = eglSwapBuffers(eglDisplay(), it->eglSurface);
if (error != EGL_TRUE) {
qCDebug(KWIN_DRM) << "an error occurred while swapping buffers" << error;
it->secondaryBuffer = nullptr;
return -1;
}
it->secondaryGbmBo = it->gbmSurface->lockFrontBuffer();
int fd = gbm_bo_get_fd(it->secondaryGbmBo);
it->secondaryBuffer = QSharedPointer<GbmBuffer>::create(it->gbmSurface);
int fd = gbm_bo_get_fd(it->buffer->getBo());
if (fd == -1) {
qCDebug(KWIN_DRM) << "failed to export gbm_bo as dma-buf!";
return -1;
}
it->dmabufFd = fd;
*format = gbm_bo_get_format(it->secondaryGbmBo);
*stride = gbm_bo_get_stride(it->secondaryGbmBo);
return it->dmabufFd;
*format = gbm_bo_get_format(it->buffer->getBo());
*stride = gbm_bo_get_stride(it->buffer->getBo());
return fd;
}
QRegion EglGbmBackend::beginFrameForSecondaryGpu(AbstractOutput *output)
@ -450,15 +435,13 @@ void EglGbmBackend::renderFramebufferToSurface(Output &output)
data.height = (uint32_t) size.height();
data.stride = stride;
data.format = format;
gbm_bo *importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR);
if (!importedBuffer) {
qCDebug(KWIN_DRM) << "failed to import dma-buf!" << strerror(errno);
if (gbm_bo *importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR)) {
output.buffer = QSharedPointer<DrmGbmBuffer>::create(m_gpu, importedBuffer, nullptr);
} else {
if (output.directScanoutBuffer) {
gbm_bo_destroy(output.directScanoutBuffer);
}
output.directScanoutBuffer = importedBuffer;
qCDebug(KWIN_DRM) << "failed to import dma-buf!" << strerror(errno);
output.buffer = nullptr;
}
close(fd);
}
}
}
@ -580,9 +563,7 @@ void EglGbmBackend::aboutToStartPainting(int screenId, const QRegion &damagedReg
bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion)
{
if (output.directScanoutBuffer) {
output.buffer = QSharedPointer<DrmSurfaceBuffer>::create(m_gpu, output.directScanoutBuffer, output.bufferInterface);
} else if (isPrimary()) {
if (isPrimary() && !directScanoutActive(output)) {
if (supportsSwapBuffersWithDamage()) {
QVector<EGLint> rects = regionToRects(damagedRegion, output.output);
if (!eglSwapBuffersWithDamageEXT(eglDisplay(), output.eglSurface,
@ -596,8 +577,8 @@ bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion
return false;
}
}
output.buffer = QSharedPointer<DrmSurfaceBuffer>::create(m_gpu, output.gbmSurface);
} else {
output.buffer = QSharedPointer<DrmGbmBuffer>::create(m_gpu, output.gbmSurface);
} else if (!output.buffer) {
qCDebug(KWIN_DRM) << "imported gbm_bo does not exist!";
return false;
}
@ -613,6 +594,11 @@ bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion
return true;
}
bool EglGbmBackend::directScanoutActive(const Output &output)
{
return output.surfaceInterface != nullptr;
}
SceneOpenGLTexturePrivate *EglGbmBackend::createBackendTexture(SceneOpenGLTexture *texture)
{
return new EglGbmTexture(texture, this);
@ -631,12 +617,7 @@ void EglGbmBackend::setViewport(const Output &output) const
QRegion EglGbmBackend::beginFrame(int screenId)
{
Output &output = m_outputs[screenId];
if (output.directScanoutBuffer) {
gbm_bo_destroy(output.directScanoutBuffer);
output.directScanoutBuffer = nullptr;
output.surfaceInterface = nullptr;
output.bufferInterface = nullptr;
}
output.surfaceInterface = nullptr;
if (isPrimary()) {
return prepareRenderingForOutput(output);
} else {
@ -757,12 +738,8 @@ bool EglGbmBackend::scanout(int screenId, SurfaceItem *surfaceItem)
} else {
damage = output.output->geometry();
}
if (output.directScanoutBuffer) {
gbm_bo_destroy(output.directScanoutBuffer);
}
output.directScanoutBuffer = importedBuffer;
output.buffer = QSharedPointer<DrmGbmBuffer>::create(m_gpu, importedBuffer, buffer);
output.surfaceInterface = surface;
output.bufferInterface = buffer;
return presentOnOutput(output, damage);
}
@ -784,8 +761,7 @@ QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *abstra
return glTexture;
}
EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR,
itOutput->directScanoutBuffer ? itOutput->directScanoutBuffer : itOutput->buffer->getBo(), nullptr);
EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR, itOutput->buffer->getBo(), nullptr);
if (image == EGL_NO_IMAGE_KHR) {
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << glGetError();
return {};

View file

@ -25,9 +25,10 @@ namespace KWin
{
class AbstractOutput;
class DrmBuffer;
class DrmSurfaceBuffer;
class DrmGbmBuffer;
class DrmOutput;
class GbmSurface;
class GbmBuffer;
/**
* @brief OpenGL Backend using Egl on a GBM surface.
@ -68,7 +69,8 @@ private:
struct Output {
DrmOutput *output = nullptr;
QSharedPointer<DrmSurfaceBuffer> buffer;
QSharedPointer<DrmGbmBuffer> buffer;
QSharedPointer<GbmBuffer> secondaryBuffer;
QSharedPointer<GbmSurface> gbmSurface;
EGLSurface eglSurface = EGL_NO_SURFACE;
int bufferAge = 0;
@ -83,11 +85,7 @@ private:
QSharedPointer<GLVertexBuffer> vbo;
} render;
int dmabufFd = 0;
gbm_bo *secondaryGbmBo = nullptr;
gbm_bo *directScanoutBuffer = nullptr;
KWaylandServer::SurfaceInterface *surfaceInterface = nullptr;
KWaylandServer::BufferInterface *bufferInterface = nullptr;
};
bool resetOutput(Output &output, DrmOutput *drmOutput);
@ -104,6 +102,7 @@ private:
QRegion prepareRenderingForOutput(Output &output) const;
bool presentOnOutput(Output &output, const QRegion &damagedRegion);
bool directScanoutActive(const Output &output);
void cleanupOutput(Output &output);
void cleanupFramebuffer(Output &output);