platforms/drm: CPU copy for multi-gpu
Not all GPUs can scan out linear buffers, so if import with a dmabuf fails manually copy the data into a non-linear gbm buffer instead. BUG: 432707
This commit is contained in:
parent
2d9e2f0c70
commit
7de0f1f2ad
12 changed files with 346 additions and 124 deletions
|
@ -13,6 +13,7 @@ set(DRM_SOURCES
|
|||
drm_gpu.cpp
|
||||
egl_multi_backend.cpp
|
||||
abstract_egl_drm_backend.cpp
|
||||
dumb_swapchain.cpp
|
||||
)
|
||||
|
||||
if (HAVE_GBM)
|
||||
|
|
|
@ -27,13 +27,24 @@ public:
|
|||
virtual int screenCount() const = 0;
|
||||
virtual void addOutput(DrmOutput *output) = 0;
|
||||
virtual void removeOutput(DrmOutput *output) = 0;
|
||||
virtual int getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32_t *format, uint32_t *stride) {
|
||||
virtual bool swapBuffers(DrmOutput *output) {
|
||||
Q_UNUSED(output)
|
||||
return false;
|
||||
}
|
||||
virtual bool exportFramebuffer(DrmOutput *output, void *data, const QSize &size, uint32_t stride) {
|
||||
Q_UNUSED(output)
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(size)
|
||||
Q_UNUSED(stride)
|
||||
return false;
|
||||
}
|
||||
virtual int exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride) {
|
||||
Q_UNUSED(output)
|
||||
Q_UNUSED(format)
|
||||
Q_UNUSED(stride)
|
||||
return 0;
|
||||
}
|
||||
virtual QRegion beginFrameForSecondaryGpu(AbstractOutput *output) {
|
||||
virtual QRegion beginFrameForSecondaryGpu(DrmOutput *output) {
|
||||
Q_UNUSED(output)
|
||||
return QRegion();
|
||||
}
|
||||
|
|
|
@ -60,7 +60,9 @@ public:
|
|||
QImage *image() const {
|
||||
return m_image;
|
||||
}
|
||||
|
||||
void *data() const {
|
||||
return m_memory;
|
||||
}
|
||||
quint32 stride() const {
|
||||
return m_stride;
|
||||
}
|
||||
|
|
|
@ -32,11 +32,17 @@ GbmBuffer::GbmBuffer(const QSharedPointer<GbmSurface> &surface)
|
|||
: m_surface(surface)
|
||||
{
|
||||
m_bo = m_surface->lockFrontBuffer();
|
||||
if (m_bo) {
|
||||
m_stride = gbm_bo_get_stride(m_bo);
|
||||
} else {
|
||||
qCWarning(KWIN_DRM) << "failed to lock front buffer!" << strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
GbmBuffer::GbmBuffer(gbm_bo *buffer, KWaylandServer::BufferInterface *bufferInterface)
|
||||
: m_bo(buffer)
|
||||
, m_bufferInterface(bufferInterface)
|
||||
, m_stride(gbm_bo_get_stride(m_bo))
|
||||
{
|
||||
if (m_bufferInterface) {
|
||||
m_bufferInterface->ref();
|
||||
|
@ -54,14 +60,32 @@ void GbmBuffer::releaseBuffer()
|
|||
if (m_bufferInterface) {
|
||||
clearBufferInterface();
|
||||
}
|
||||
if (m_surface && m_bo) {
|
||||
if (!m_bo) {
|
||||
return;
|
||||
}
|
||||
if (m_mapping) {
|
||||
gbm_bo_unmap(m_bo, m_mapping);
|
||||
}
|
||||
if (m_surface) {
|
||||
m_surface->releaseBuffer(m_bo);
|
||||
} else if (m_bo) {
|
||||
} else {
|
||||
gbm_bo_destroy(m_bo);
|
||||
}
|
||||
m_bo = nullptr;
|
||||
}
|
||||
|
||||
bool GbmBuffer::map(uint32_t flags)
|
||||
{
|
||||
if (m_data) {
|
||||
return true;
|
||||
}
|
||||
if (!m_bo) {
|
||||
return false;
|
||||
}
|
||||
m_data = gbm_bo_map(m_bo, 0, 0, gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo), flags, &m_stride, &m_mapping);
|
||||
return m_data;
|
||||
}
|
||||
|
||||
void GbmBuffer::clearBufferInterface()
|
||||
{
|
||||
disconnect(m_bufferInterface, &KWaylandServer::BufferInterface::aboutToBeDestroyed, this, &DrmGbmBuffer::clearBufferInterface);
|
||||
|
|
|
@ -40,11 +40,23 @@ public:
|
|||
|
||||
void releaseBuffer();
|
||||
|
||||
bool map(uint32_t flags);
|
||||
void *mappedData() const {
|
||||
return m_data;
|
||||
}
|
||||
uint32_t stride() const {
|
||||
return m_stride;
|
||||
}
|
||||
|
||||
protected:
|
||||
QSharedPointer<GbmSurface> m_surface;
|
||||
gbm_bo *m_bo = nullptr;
|
||||
KWaylandServer::BufferInterface *m_bufferInterface = nullptr;
|
||||
|
||||
void *m_data = nullptr;
|
||||
void *m_mapping = nullptr;
|
||||
uint32_t m_stride = 0;
|
||||
|
||||
void clearBufferInterface();
|
||||
};
|
||||
|
||||
|
|
54
src/plugins/platforms/drm/dumb_swapchain.cpp
Normal file
54
src/plugins/platforms/drm/dumb_swapchain.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
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 "dumb_swapchain.h"
|
||||
|
||||
#include "gbm.h"
|
||||
|
||||
#include "drm_gpu.h"
|
||||
#include "logging.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
DumbSwapchain::DumbSwapchain(DrmGpu *gpu, const QSize &size)
|
||||
: m_size(size)
|
||||
{
|
||||
for (int i = 0; i < 3; i++) {
|
||||
auto buffer = QSharedPointer<DrmDumbBuffer>::create(gpu, size);
|
||||
if (!buffer->bufferId()) {
|
||||
break;
|
||||
}
|
||||
if (!buffer->map()) {
|
||||
break;
|
||||
}
|
||||
buffer->image()->fill(Qt::black);
|
||||
m_buffers << buffer;
|
||||
}
|
||||
if (m_buffers.count() < 3) {
|
||||
qCWarning(KWIN_DRM) << "Failed to create gbm buffers for swapchain!";
|
||||
m_buffers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<DrmDumbBuffer> DumbSwapchain::acquireBuffer()
|
||||
{
|
||||
if (m_buffers.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
index = (index + 1) % m_buffers.count();
|
||||
return m_buffers[index];
|
||||
}
|
||||
|
||||
QSharedPointer<DrmDumbBuffer> DumbSwapchain::currentBuffer() const
|
||||
{
|
||||
return m_buffers[index];
|
||||
}
|
||||
|
||||
}
|
45
src/plugins/platforms/drm/dumb_swapchain.h
Normal file
45
src/plugins/platforms/drm/dumb_swapchain.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
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 <QVector>
|
||||
#include <QSize>
|
||||
#include <QSharedPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class DrmDumbBuffer;
|
||||
class DrmGpu;
|
||||
|
||||
class DumbSwapchain
|
||||
{
|
||||
public:
|
||||
DumbSwapchain(DrmGpu *gpu, const QSize &size);
|
||||
|
||||
QSharedPointer<DrmDumbBuffer> acquireBuffer();
|
||||
QSharedPointer<DrmDumbBuffer> currentBuffer() const;
|
||||
|
||||
QSize size() const {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return m_buffers.isEmpty();
|
||||
}
|
||||
|
||||
private:
|
||||
QSize m_size;
|
||||
|
||||
int index = 0;
|
||||
QVector<QSharedPointer<DrmDumbBuffer>> m_buffers;
|
||||
};
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
#include "surfaceitem_wayland.h"
|
||||
#include "drm_gpu.h"
|
||||
#include "linux_dmabuf.h"
|
||||
#include "dumb_swapchain.h"
|
||||
// kwin libs
|
||||
#include <kwinglplatform.h>
|
||||
#include <kwineglimagetexture.h>
|
||||
|
@ -76,6 +77,7 @@ void EglGbmBackend::cleanupOutput(Output &output)
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
@ -204,6 +206,7 @@ bool EglGbmBackend::resetOutput(Output &output, DrmOutput *drmOutput)
|
|||
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;
|
||||
|
@ -261,7 +264,54 @@ void EglGbmBackend::removeOutput(DrmOutput *drmOutput)
|
|||
outputs.erase(it);
|
||||
}
|
||||
|
||||
int EglGbmBackend::getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32_t *format, uint32_t *stride)
|
||||
bool EglGbmBackend::swapBuffers(DrmOutput *drmOutput)
|
||||
{
|
||||
auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(),
|
||||
[drmOutput] (const Output &output) {
|
||||
return output.output == drmOutput;
|
||||
}
|
||||
);
|
||||
if (it == m_secondaryGpuOutputs.end()) {
|
||||
return false;
|
||||
}
|
||||
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 false;
|
||||
}
|
||||
it->secondaryBuffer = QSharedPointer<GbmBuffer>::create(it->gbmSurface);
|
||||
if (it->secondaryBuffer->getBo()) {
|
||||
return true;
|
||||
} else {
|
||||
it->secondaryBuffer = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EglGbmBackend::exportFramebuffer(DrmOutput *drmOutput, void *data, const QSize &size, uint32_t stride)
|
||||
{
|
||||
auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(),
|
||||
[drmOutput] (const Output &output) {
|
||||
return output.output == drmOutput;
|
||||
}
|
||||
);
|
||||
if (it == m_secondaryGpuOutputs.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!it->secondaryBuffer || !it->secondaryBuffer->map(GBM_BO_TRANSFER_READ)) {
|
||||
return false;
|
||||
}
|
||||
if (stride != it->secondaryBuffer->stride()) {
|
||||
// shouldn't happen if formats are the same
|
||||
return false;
|
||||
}
|
||||
return memcpy(data, it->secondaryBuffer->mappedData(), size.height() * stride);
|
||||
}
|
||||
|
||||
int EglGbmBackend::exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride)
|
||||
{
|
||||
DrmOutput *drmOutput = static_cast<DrmOutput*>(output);
|
||||
auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(),
|
||||
|
@ -272,14 +322,6 @@ int EglGbmBackend::getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32
|
|||
if (it == m_secondaryGpuOutputs.end()) {
|
||||
return -1;
|
||||
}
|
||||
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->secondaryBuffer = QSharedPointer<GbmBuffer>::create(it->gbmSurface);
|
||||
int fd = gbm_bo_get_fd(it->secondaryBuffer->getBo());
|
||||
if (fd == -1) {
|
||||
qCDebug(KWIN_DRM) << "failed to export gbm_bo as dma-buf!";
|
||||
|
@ -290,9 +332,8 @@ int EglGbmBackend::getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32
|
|||
return fd;
|
||||
}
|
||||
|
||||
QRegion EglGbmBackend::beginFrameForSecondaryGpu(AbstractOutput *output)
|
||||
QRegion EglGbmBackend::beginFrameForSecondaryGpu(DrmOutput *drmOutput)
|
||||
{
|
||||
DrmOutput *drmOutput = static_cast<DrmOutput*>(output);
|
||||
auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(),
|
||||
[drmOutput] (const Output &output) {
|
||||
return output.output == drmOutput;
|
||||
|
@ -304,6 +345,56 @@ QRegion EglGbmBackend::beginFrameForSecondaryGpu(AbstractOutput *output)
|
|||
return prepareRenderingForOutput(*it);
|
||||
}
|
||||
|
||||
void EglGbmBackend::importFramebuffer(Output &output) const
|
||||
{
|
||||
if (!renderingBackend()->swapBuffers(output.output)) {
|
||||
qCWarning(KWIN_DRM) << "swapping buffers failed on output" << output.output;
|
||||
return;
|
||||
}
|
||||
output.buffer = nullptr;
|
||||
const auto size = output.output->modeSize();
|
||||
if (output.importMode == ImportMode::Dmabuf) {
|
||||
uint32_t stride = 0;
|
||||
uint32_t format = 0;
|
||||
int fd = renderingBackend()->exportFramebufferAsDmabuf(output.output, &format, &stride);
|
||||
if (fd != -1) {
|
||||
struct gbm_import_fd_data data = {};
|
||||
data.fd = fd;
|
||||
data.width = (uint32_t) size.width();
|
||||
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);
|
||||
close(fd);
|
||||
if (importedBuffer) {
|
||||
auto buffer = QSharedPointer<DrmGbmBuffer>::create(m_gpu, importedBuffer, nullptr);
|
||||
if (buffer->bufferId() > 0) {
|
||||
output.buffer = buffer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
qCDebug(KWIN_DRM) << "import with dmabuf failed! Switching to CPU import on output" << output.output;
|
||||
output.importMode = ImportMode::DumbBuffer;
|
||||
}
|
||||
// ImportMode::DumbBuffer
|
||||
if (!output.importSwapchain || output.importSwapchain->size() != size) {
|
||||
output.importSwapchain = QSharedPointer<DumbSwapchain>::create(m_gpu, size);
|
||||
if (output.importSwapchain->isEmpty()) {
|
||||
output.importSwapchain = nullptr;
|
||||
}
|
||||
}
|
||||
if (output.importSwapchain) {
|
||||
auto buffer = output.importSwapchain->acquireBuffer();
|
||||
if (renderingBackend()->exportFramebuffer(output.output, buffer->data(), size, buffer->stride())) {
|
||||
output.buffer = buffer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
qCWarning(KWIN_DRM) << "all imports failed on output" << output.output;
|
||||
// TODO turn off output?
|
||||
}
|
||||
|
||||
const float vertices[] = {
|
||||
-1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
|
@ -377,81 +468,58 @@ void EglGbmBackend::initRenderTarget(Output &output)
|
|||
|
||||
void EglGbmBackend::renderFramebufferToSurface(Output &output)
|
||||
{
|
||||
if (!output.render.framebuffer && isPrimary()) {
|
||||
if (!output.render.framebuffer) {
|
||||
// No additional render target.
|
||||
return;
|
||||
}
|
||||
makeContextCurrent(output);
|
||||
|
||||
const auto size = output.output->modeSize();
|
||||
if (isPrimary()) {
|
||||
// primary GPU
|
||||
makeContextCurrent(output);
|
||||
glViewport(0, 0, size.width(), size.height());
|
||||
|
||||
glViewport(0, 0, size.width(), size.height());
|
||||
auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
|
||||
|
||||
auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
|
||||
QMatrix4x4 mvpMatrix;
|
||||
|
||||
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);
|
||||
} else {
|
||||
// secondary GPU: render on primary and import framebuffer
|
||||
uint32_t stride = 0;
|
||||
uint32_t format = 0;
|
||||
int fd = renderingBackend()->getDmabufForSecondaryGpuOutput(output.output, &format, &stride);
|
||||
if (fd != -1) {
|
||||
struct gbm_import_fd_data data = {};
|
||||
data.fd = fd;
|
||||
data.width = (uint32_t) size.width();
|
||||
data.height = (uint32_t) size.height();
|
||||
data.stride = stride;
|
||||
data.format = format;
|
||||
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 {
|
||||
qCDebug(KWIN_DRM) << "failed to import dma-buf!" << strerror(errno);
|
||||
output.buffer = nullptr;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
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
|
||||
|
@ -587,7 +655,7 @@ bool EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion
|
|||
}
|
||||
output.buffer = QSharedPointer<DrmGbmBuffer>::create(m_gpu, output.gbmSurface);
|
||||
} else if (!output.buffer) {
|
||||
qCDebug(KWIN_DRM) << "imported gbm_bo does not exist!";
|
||||
qCDebug(KWIN_DRM) << "imported buffer does not exist!";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -668,7 +736,11 @@ void EglGbmBackend::endFrame(int screenId, const QRegion &renderedRegion,
|
|||
Output &output = m_outputs[screenId];
|
||||
DrmOutput *drmOutput = output.output;
|
||||
|
||||
renderFramebufferToSurface(output);
|
||||
if (isPrimary()) {
|
||||
renderFramebufferToSurface(output);
|
||||
} else {
|
||||
importFramebuffer(output);
|
||||
}
|
||||
|
||||
const QRegion dirty = damagedRegion.intersected(output.output->geometry());
|
||||
if (!presentOnOutput(output, dirty)) {
|
||||
|
@ -760,13 +832,20 @@ bool EglGbmBackend::scanout(int screenId, SurfaceItem *surfaceItem)
|
|||
|
||||
QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *abstractOutput) const
|
||||
{
|
||||
const QVector<KWin::EglGbmBackend::Output>::const_iterator itOutput = std::find_if(m_outputs.begin(), m_outputs.end(),
|
||||
auto itOutput = std::find_if(m_outputs.begin(), m_outputs.end(),
|
||||
[abstractOutput] (const auto &output) {
|
||||
return output.output == abstractOutput;
|
||||
}
|
||||
);
|
||||
if (itOutput == m_outputs.end()) {
|
||||
return {};
|
||||
itOutput = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(),
|
||||
[abstractOutput] (const auto &output) {
|
||||
return output.output == abstractOutput;
|
||||
}
|
||||
);
|
||||
if (itOutput == m_secondaryGpuOutputs.end()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
DrmOutput *drmOutput = itOutput->output;
|
||||
|
@ -776,7 +855,12 @@ QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *abstra
|
|||
return glTexture;
|
||||
}
|
||||
|
||||
EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR, itOutput->buffer->getBo(), nullptr);
|
||||
auto gbmBuffer = dynamic_cast<GbmBuffer*>(itOutput->buffer.data());
|
||||
if (!gbmBuffer) {
|
||||
qCWarning(KWIN_DRM) << "Failed to record frame: Dumb buffer used for presentation!";
|
||||
return {};
|
||||
}
|
||||
EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR, gbmBuffer->getBo(), nullptr);
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << glGetError();
|
||||
return {};
|
||||
|
|
|
@ -31,6 +31,7 @@ class DrmGbmBuffer;
|
|||
class DrmOutput;
|
||||
class GbmSurface;
|
||||
class GbmBuffer;
|
||||
class DumbSwapchain;
|
||||
|
||||
/**
|
||||
* @brief OpenGL Backend using Egl on a GBM surface.
|
||||
|
@ -58,8 +59,10 @@ public:
|
|||
|
||||
void addOutput(DrmOutput *output) override;
|
||||
void removeOutput(DrmOutput *output) override;
|
||||
int getDmabufForSecondaryGpuOutput(AbstractOutput *output, uint32_t *format, uint32_t *stride) override;
|
||||
QRegion beginFrameForSecondaryGpu(AbstractOutput *output) override;
|
||||
bool swapBuffers(DrmOutput *output) override;
|
||||
bool exportFramebuffer(DrmOutput *output, void *data, const QSize &size, uint32_t stride) override;
|
||||
int exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride) override;
|
||||
QRegion beginFrameForSecondaryGpu(DrmOutput *output) override;
|
||||
|
||||
bool directScanoutAllowed(int screen) const override;
|
||||
|
||||
|
@ -72,9 +75,13 @@ private:
|
|||
bool initBufferConfigs();
|
||||
bool initRenderingContext();
|
||||
|
||||
enum class ImportMode {
|
||||
Dmabuf,
|
||||
DumbBuffer
|
||||
};
|
||||
struct Output {
|
||||
DrmOutput *output = nullptr;
|
||||
QSharedPointer<DrmGbmBuffer> buffer;
|
||||
QSharedPointer<DrmBuffer> buffer;
|
||||
QSharedPointer<GbmBuffer> secondaryBuffer;
|
||||
QSharedPointer<GbmSurface> gbmSurface;
|
||||
EGLSurface eglSurface = EGL_NO_SURFACE;
|
||||
|
@ -91,6 +98,8 @@ private:
|
|||
} render;
|
||||
|
||||
KWaylandServer::SurfaceInterface *surfaceInterface = nullptr;
|
||||
ImportMode importMode = ImportMode::Dmabuf;
|
||||
QSharedPointer<DumbSwapchain> importSwapchain;
|
||||
};
|
||||
|
||||
bool resetOutput(Output &output, DrmOutput *drmOutput);
|
||||
|
@ -105,6 +114,7 @@ private:
|
|||
void prepareRenderFramebuffer(const Output &output) const;
|
||||
void renderFramebufferToSurface(Output &output);
|
||||
QRegion prepareRenderingForOutput(Output &output) const;
|
||||
void importFramebuffer(Output &output) const;
|
||||
|
||||
bool presentOnOutput(Output &output, const QRegion &damagedRegion);
|
||||
bool directScanoutActive(const Output &output);
|
||||
|
|
|
@ -97,14 +97,8 @@ PlatformSurfaceTexture *EglMultiBackend::createPlatformSurfaceTextureWayland(Sur
|
|||
|
||||
QSharedPointer<GLTexture> EglMultiBackend::textureForOutput(AbstractOutput *requestedOutput) const
|
||||
{
|
||||
// this assumes that the wrong backends return {}
|
||||
for (auto backend : qAsConst(m_backends)) {
|
||||
auto texture = backend->textureForOutput(requestedOutput);
|
||||
if (!texture.isNull()) {
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
// this assumes that all outputs are rendered on backend 0
|
||||
return m_backends[0]->textureForOutput(requestedOutput);
|
||||
}
|
||||
|
||||
void EglMultiBackend::screenGeometryChanged(const QSize &size)
|
||||
|
|
|
@ -44,12 +44,9 @@ DrmQPainterBackend::DrmQPainterBackend(DrmBackend *backend, DrmGpu *gpu)
|
|||
void DrmQPainterBackend::initOutput(DrmOutput *output)
|
||||
{
|
||||
Output o;
|
||||
auto initBuffer = [&o, output, this] (int index) {
|
||||
o.buffer[index] = QSharedPointer<DrmDumbBuffer>::create(m_gpu, output->pixelSize());
|
||||
if (o.buffer[index]->map()) {
|
||||
o.buffer[index]->image()->fill(Qt::black);
|
||||
}
|
||||
};
|
||||
o.swapchain = QSharedPointer<DumbSwapchain>::create(m_gpu, output->pixelSize());
|
||||
o.output = output;
|
||||
m_outputs << o;
|
||||
connect(output, &DrmOutput::modeChanged, this,
|
||||
[output, this] {
|
||||
auto it = std::find_if(m_outputs.begin(), m_outputs.end(),
|
||||
|
@ -60,26 +57,14 @@ void DrmQPainterBackend::initOutput(DrmOutput *output)
|
|||
if (it == m_outputs.end()) {
|
||||
return;
|
||||
}
|
||||
auto initBuffer = [it, output, this] (int index) {
|
||||
it->buffer[index] = QSharedPointer<DrmDumbBuffer>::create(m_gpu, output->pixelSize());
|
||||
if (it->buffer[index]->map()) {
|
||||
it->buffer[index]->image()->fill(Qt::black);
|
||||
}
|
||||
};
|
||||
initBuffer(0);
|
||||
initBuffer(1);
|
||||
it->swapchain = QSharedPointer<DumbSwapchain>::create(m_gpu, output->pixelSize());
|
||||
}
|
||||
);
|
||||
initBuffer(0);
|
||||
initBuffer(1);
|
||||
o.output = output;
|
||||
m_outputs << o;
|
||||
}
|
||||
|
||||
QImage *DrmQPainterBackend::bufferForScreen(int screenId)
|
||||
{
|
||||
const Output &o = m_outputs.at(screenId);
|
||||
return o.buffer[o.index]->image();
|
||||
return m_outputs[screenId].swapchain->currentBuffer()->image();
|
||||
}
|
||||
|
||||
bool DrmQPainterBackend::needsFullRepaint(int screenId) const
|
||||
|
@ -90,8 +75,7 @@ bool DrmQPainterBackend::needsFullRepaint(int screenId) const
|
|||
|
||||
void DrmQPainterBackend::beginFrame(int screenId)
|
||||
{
|
||||
Output &rendererOutput = m_outputs[screenId];
|
||||
rendererOutput.index = (rendererOutput.index + 1) % 2;
|
||||
m_outputs[screenId].swapchain->acquireBuffer();
|
||||
}
|
||||
|
||||
void DrmQPainterBackend::endFrame(int screenId, int mask, const QRegion &damage)
|
||||
|
@ -102,7 +86,7 @@ void DrmQPainterBackend::endFrame(int screenId, int mask, const QRegion &damage)
|
|||
const Output &rendererOutput = m_outputs[screenId];
|
||||
DrmOutput *drmOutput = rendererOutput.output;
|
||||
|
||||
if (!drmOutput->present(rendererOutput.buffer[rendererOutput.index])) {
|
||||
if (!drmOutput->present(rendererOutput.swapchain->currentBuffer())) {
|
||||
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(drmOutput->renderLoop());
|
||||
renderLoopPrivate->notifyFrameFailed();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <QVector>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "dumb_swapchain.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
|
@ -36,9 +38,8 @@ public:
|
|||
private:
|
||||
void initOutput(DrmOutput *output);
|
||||
struct Output {
|
||||
QSharedPointer<DrmDumbBuffer> buffer[2];
|
||||
DrmOutput *output;
|
||||
int index = 0;
|
||||
QSharedPointer<DumbSwapchain> swapchain;
|
||||
};
|
||||
QVector<Output> m_outputs;
|
||||
DrmBackend *m_backend;
|
||||
|
|
Loading…
Reference in a new issue