backends/drm: remove some unnecessary redirection

This commit is contained in:
Xaver Hugl 2022-02-18 01:26:29 +01:00
parent 0ba5916fe6
commit aac7f50d8b
29 changed files with 572 additions and 362 deletions

View file

@ -27,6 +27,7 @@ set(DRM_SOURCES
gbm_surface.cpp
gbm_dmabuf.cpp
placeholderinputeventfilter.cpp
virtual_egl_gbm_layer.cpp
)
add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES})

View file

@ -41,4 +41,20 @@ void DrmAbstractOutput::pageFlipped(std::chrono::nanoseconds timestamp) const
RenderLoopPrivate::get(m_renderLoop)->notifyFrameCompleted(timestamp);
}
QVector<int32_t> DrmAbstractOutput::regionToRects(const QRegion &region) const
{
const int height = pixelSize().height();
const QMatrix4x4 matrix = AbstractWaylandOutput::logicalToNativeMatrix(geometry(), scale(), transform());
QVector<EGLint> rects;
rects.reserve(region.rectCount() * 4);
for (const QRect &_rect : region) {
const QRect rect = matrix.mapRect(_rect);
rects << rect.left();
rects << height - (rect.y() + rect.height());
rects << rect.width();
rects << rect.height();
}
return rects;
}
}

View file

@ -26,6 +26,7 @@ public:
QRect renderGeometry() const override;
void frameFailed() const override;
void pageFlipped(std::chrono::nanoseconds timestamp) const override;
QVector<int32_t> regionToRects(const QRegion &region) const override;
protected:
friend class DrmGpu;

View file

@ -21,9 +21,10 @@
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
#include <drm_fourcc.h>
// KWaylandServer
#include "KWaylandServer/clientbuffer.h"
#include <drm_fourcc.h>
#include "KWaylandServer/linuxdmabufv1clientbuffer.h"
namespace KWin
{
@ -31,17 +32,42 @@ namespace KWin
GbmBuffer::GbmBuffer(GbmSurface *surface, gbm_bo *bo)
: m_surface(surface)
, m_bo(bo)
{
m_stride = gbm_bo_get_stride(m_bo);
}
GbmBuffer::GbmBuffer(gbm_bo *buffer, KWaylandServer::ClientBuffer *clientBuffer)
: m_bo(buffer)
, m_clientBuffer(clientBuffer)
, m_stride(gbm_bo_get_stride(m_bo))
{
if (m_clientBuffer) {
m_clientBuffer->ref();
}
GbmBuffer::GbmBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer)
: m_clientBuffer(clientBuffer)
{
clientBuffer->ref();
const auto planes = clientBuffer->planes();
if (planes.first().modifier != DRM_FORMAT_MOD_INVALID || planes.first().offset > 0 || planes.count() > 1) {
gbm_import_fd_modifier_data data = {};
data.format = clientBuffer->format();
data.width = (uint32_t) clientBuffer->size().width();
data.height = (uint32_t) clientBuffer->size().height();
data.num_fds = planes.count();
data.modifier = planes.first().modifier;
for (int i = 0; i < planes.count(); i++) {
data.fds[i] = planes[i].fd;
data.offsets[i] = planes[i].offset;
data.strides[i] = planes[i].stride;
}
m_bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
} else {
const auto &plane = planes.first();
gbm_import_fd_data data = {};
data.fd = plane.fd;
data.width = (uint32_t) clientBuffer->size().width();
data.height = (uint32_t) clientBuffer->size().height();
data.stride = plane.stride;
data.format = clientBuffer->format();
m_bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT);
}
if (m_bo) {
m_stride = gbm_bo_get_stride(m_bo);
} else if (errno != EINVAL) {
qCWarning(KWIN_DRM) << "Importing buffer for direct scanout failed:" << strerror(errno);
}
}
@ -105,13 +131,15 @@ uint32_t GbmBuffer::stride() const
DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, GbmSurface *surface, gbm_bo *bo)
: DrmBuffer(gpu, gbm_bo_get_format(bo), gbm_bo_get_modifier(bo)), GbmBuffer(surface, bo)
: DrmBuffer(gpu, gbm_bo_get_format(bo), gbm_bo_get_modifier(bo))
, GbmBuffer(surface, bo)
{
initialize();
}
DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, gbm_bo *buffer, KWaylandServer::ClientBuffer *clientBuffer)
: DrmBuffer(gpu, gbm_bo_get_format(buffer), gbm_bo_get_modifier(buffer)), GbmBuffer(buffer, clientBuffer)
DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer)
: DrmBuffer(gpu, clientBuffer->format(), clientBuffer->planes().constFirst().modifier)
, GbmBuffer(gpu, clientBuffer)
{
initialize();
}
@ -127,6 +155,9 @@ DrmGbmBuffer::~DrmGbmBuffer()
void DrmGbmBuffer::initialize()
{
if (!m_bo) {
return;
}
m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo));
uint32_t handles[4] = { };
uint32_t strides[4] = { };

View file

@ -19,6 +19,7 @@ struct gbm_bo;
namespace KWaylandServer
{
class ClientBuffer;
class LinuxDmaBufV1ClientBuffer;
}
namespace KWin
@ -31,7 +32,7 @@ class GbmBuffer : public QObject
Q_OBJECT
public:
GbmBuffer(GbmSurface *surface, gbm_bo *bo);
GbmBuffer(gbm_bo *buffer, KWaylandServer::ClientBuffer *clientBuffer);
GbmBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer);
virtual ~GbmBuffer();
void releaseBuffer();
@ -56,7 +57,7 @@ class DrmGbmBuffer : public DrmBuffer, public GbmBuffer
{
public:
DrmGbmBuffer(DrmGpu *gpu, GbmSurface *surface, gbm_bo *bo);
DrmGbmBuffer(DrmGpu *gpu, gbm_bo *buffer, KWaylandServer::ClientBuffer *clientBuffer);
DrmGbmBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer);
~DrmGbmBuffer() override;
bool needsModeChange(DrmBuffer *b) const override;

View file

@ -17,7 +17,7 @@ namespace KWin
class DrmBuffer;
class DrmGpu;
class DrmLayer;
class DrmOutputLayer;
class DrmDisplayDevice
{
@ -28,18 +28,11 @@ public:
DrmGpu *gpu() const;
virtual bool present() = 0;
virtual bool testScanout() = 0;
virtual void frameFailed() const = 0;
virtual void pageFlipped(std::chrono::nanoseconds timestamp) const = 0;
virtual DrmPlane::Transformations softwareTransforms() const = 0;
virtual QSize bufferSize() const = 0;
virtual QSize sourceSize() const = 0;
virtual bool isFormatSupported(uint32_t drmFormat) const = 0;
virtual QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const = 0;
virtual int maxBpc() const = 0;
virtual QRect renderGeometry() const = 0;
virtual DrmLayer *outputLayer() const = 0;
virtual DrmOutputLayer *outputLayer() const = 0;
virtual QVector<int32_t> regionToRects(const QRegion &region) const = 0;
protected:
DrmGpu *const m_gpu;

View file

@ -422,7 +422,7 @@ bool DrmGpu::testPipelines()
QVector<DrmPipeline*> inactivePipelines;
for (const auto &pipeline : qAsConst(m_pipelines)) {
if (!pipeline->pending.layer) {
pipeline->pending.layer = m_platform->renderBackend()->createLayer(pipeline->displayDevice());
pipeline->pending.layer = m_platform->renderBackend()->createDrmPipelineLayer(pipeline);
}
if (!pipeline->pending.active) {
pipeline->pending.active = true;
@ -772,7 +772,7 @@ QSize DrmGpu::cursorSize() const
void DrmGpu::recreateSurfaces()
{
for (const auto &pipeline : qAsConst(m_pipelines)) {
pipeline->pending.layer = m_platform->renderBackend()->createLayer(pipeline->displayDevice());
pipeline->pending.layer = m_platform->renderBackend()->createDrmPipelineLayer(pipeline);
}
for (const auto &output : qAsConst(m_outputs)) {
if (const auto virtualOutput = qobject_cast<DrmVirtualOutput*>(output)) {

View file

@ -7,20 +7,24 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "drm_layer.h"
#include "drm_pipeline.h"
#include "drm_display_device.h"
#include <QMatrix4x4>
namespace KWin
{
DrmLayer::DrmLayer(KWin::DrmDisplayDevice* device)
: m_displayDevice(device)
DrmOutputLayer::~DrmOutputLayer() = default;
void DrmOutputLayer::aboutToStartPainting(const QRegion &damagedRegion)
{
Q_UNUSED(damagedRegion)
}
DrmLayer::~DrmLayer() = default;
DrmDisplayDevice *DrmLayer::displayDevice() const
DrmPipelineLayer::DrmPipelineLayer(DrmPipeline *pipeline)
: m_pipeline(pipeline)
{
return m_displayDevice;
}
}

View file

@ -18,15 +18,16 @@ namespace KWin
class SurfaceItem;
class DrmBuffer;
class DrmDisplayDevice;
class GLTexture;
class DrmPipeline;
class DrmLayer : public QObject
class DrmOutputLayer : public QObject
{
Q_OBJECT
public:
DrmLayer(DrmDisplayDevice *device);
virtual ~DrmLayer();
virtual ~DrmOutputLayer();
virtual void aboutToStartPainting(const QRegion &damagedRegion);
virtual std::optional<QRegion> startRendering() = 0;
virtual bool endRendering(const QRegion &damagedRegion) = 0;
@ -37,6 +38,16 @@ public:
*/
virtual bool scanout(SurfaceItem *surfaceItem) = 0;
virtual QSharedPointer<GLTexture> texture() const = 0;
virtual QRegion currentDamage() const = 0;
};
class DrmPipelineLayer : public DrmOutputLayer
{
public:
DrmPipelineLayer(DrmPipeline *pipeline);
/**
* @returns a buffer for atomic test commits
* If no fitting buffer is available, a new current buffer is created
@ -44,13 +55,10 @@ public:
virtual QSharedPointer<DrmBuffer> testBuffer() = 0;
virtual QSharedPointer<DrmBuffer> currentBuffer() const = 0;
virtual QRegion currentDamage() const = 0;
virtual bool hasDirectScanoutBuffer() const = 0;
DrmDisplayDevice *displayDevice() const;
protected:
DrmDisplayDevice *const m_displayDevice;
DrmPipeline *const m_pipeline;
};
}

View file

@ -14,6 +14,7 @@
#include "drm_object_crtc.h"
#include "drm_object_plane.h"
#include "drm_pipeline.h"
#include "drm_layer.h"
#include "logging.h"
@ -83,51 +84,12 @@ bool DrmLeaseOutput::present()
return false;
}
bool DrmLeaseOutput::testScanout()
{
return false;
}
DrmPlane::Transformations DrmLeaseOutput::softwareTransforms() const
{
return DrmPlane::Transformation::Rotate0;
}
QSize DrmLeaseOutput::bufferSize() const
{
return m_pipeline->bufferSize();
}
QSize DrmLeaseOutput::sourceSize() const
{
return m_pipeline->sourceSize();
}
bool DrmLeaseOutput::isFormatSupported(uint32_t drmFormat) const
{
return m_pipeline->isFormatSupported(drmFormat);
}
QVector<uint64_t> DrmLeaseOutput::supportedModifiers(uint32_t drmFormat) const
{
return m_pipeline->supportedModifiers(drmFormat);
}
int DrmLeaseOutput::maxBpc() const
{
if (const auto prop = m_pipeline->connector()->getProp(DrmConnector::PropertyIndex::MaxBpc)) {
return prop->maxValue();
} else {
return 8;
}
}
QRect DrmLeaseOutput::renderGeometry() const
{
return QRect(QPoint(), m_pipeline->sourceSize());
}
DrmLayer *DrmLeaseOutput::outputLayer() const
DrmOutputLayer *DrmLeaseOutput::outputLayer() const
{
return m_pipeline->pending.layer.data();
}
@ -141,4 +103,10 @@ void DrmLeaseOutput::pageFlipped(std::chrono::nanoseconds timestamp) const
Q_UNUSED(timestamp)
}
QVector<int32_t> DrmLeaseOutput::regionToRects(const QRegion &region) const
{
Q_UNUSED(region)
return {};
}
}

View file

@ -41,17 +41,11 @@ public:
DrmPipeline *pipeline() const;
bool present() override;
DrmPlane::Transformations softwareTransforms() const override;
QSize bufferSize() const override;
QSize sourceSize() const override;
bool isFormatSupported(uint32_t drmFormat) const override;
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const override;
int maxBpc() const override;
QRect renderGeometry() const override;
DrmLayer *outputLayer() const override;
bool testScanout() override;
DrmOutputLayer *outputLayer() const override;
void frameFailed() const override;
void pageFlipped(std::chrono::nanoseconds timestamp) const override;
QVector<int32_t> regionToRects(const QRegion &region) const override;
private:
DrmPipeline *m_pipeline;

View file

@ -340,11 +340,6 @@ bool DrmOutput::present()
}
}
bool DrmOutput::testScanout()
{
return m_pipeline->testScanout();
}
int DrmOutput::gammaRampSize() const
{
return m_pipeline->pending.crtc ? m_pipeline->pending.crtc->gammaRampSize() : 256;
@ -373,26 +368,6 @@ DrmPipeline *DrmOutput::pipeline() const
return m_pipeline;
}
QSize DrmOutput::bufferSize() const
{
return m_pipeline->bufferSize();
}
QSize DrmOutput::sourceSize() const
{
return m_pipeline->sourceSize();
}
bool DrmOutput::isFormatSupported(uint32_t drmFormat) const
{
return m_pipeline->isFormatSupported(drmFormat);
}
QVector<uint64_t> DrmOutput::supportedModifiers(uint32_t drmFormat) const
{
return m_pipeline->supportedModifiers(drmFormat);
}
bool DrmOutput::queueChanges(const WaylandOutputConfig &config)
{
static bool valid;
@ -449,28 +424,12 @@ void DrmOutput::revertQueuedChanges()
m_pipeline->revertPendingChanges();
}
int DrmOutput::maxBpc() const
{
auto prop = m_connector->getProp(DrmConnector::PropertyIndex::MaxBpc);
return prop ? prop->maxValue() : 8;
}
bool DrmOutput::usesSoftwareCursor() const
{
return !m_setCursorSuccessful || !m_moveCursorSuccessful;
}
DrmPlane::Transformations DrmOutput::softwareTransforms() const
{
if (m_pipeline->pending.bufferTransformation == m_pipeline->pending.sourceTransformation) {
return DrmPlane::Transformation::Rotate0;
} else {
// TODO handle sourceTransformation != Rotate0
return m_pipeline->pending.sourceTransformation;
}
}
DrmLayer *DrmOutput::outputLayer() const
DrmOutputLayer *DrmOutput::outputLayer() const
{
return m_pipeline->pending.layer.data();
}

View file

@ -47,14 +47,7 @@ public:
DrmPipeline *pipeline() const;
bool present() override;
bool testScanout() override;
QSize bufferSize() const override;
QSize sourceSize() const override;
bool isFormatSupported(uint32_t drmFormat) const override;
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const override;
DrmPlane::Transformations softwareTransforms() const override;
int maxBpc() const override;
DrmLayer *outputLayer() const override;
DrmOutputLayer *outputLayer() const override;
bool queueChanges(const WaylandOutputConfig &config);
void applyQueuedChanges(const WaylandOutputConfig &config);

View file

@ -31,7 +31,7 @@ class DrmBuffer;
class DrmDumbBuffer;
class GammaRamp;
class DrmConnectorMode;
class DrmLayer;
class DrmPipelineLayer;
class DrmDisplayDevice;
class DrmGammaRamp
@ -107,7 +107,7 @@ public:
RenderLoopPrivate::SyncMode syncMode = RenderLoopPrivate::SyncMode::Fixed;
QSharedPointer<DrmGammaRamp> gamma;
QSharedPointer<DrmLayer> layer;
QSharedPointer<DrmPipelineLayer> layer;
QPoint cursorPos;
QPoint cursorHotspot;

View file

@ -13,16 +13,18 @@
#include "scene_qpainter_drm_backend.h"
#include "drm_gpu.h"
#include "drm_backend.h"
#include "drm_pipeline.h"
#include "drm_virtual_output.h"
#include <drm_fourcc.h>
namespace KWin
{
DrmQPainterLayer::DrmQPainterLayer(DrmDisplayDevice *displayDevice)
: DrmLayer(displayDevice)
DrmQPainterLayer::DrmQPainterLayer(DrmPipeline *pipeline)
: DrmPipelineLayer(pipeline)
{
connect(static_cast<DrmQPainterBackend*>(displayDevice->gpu()->platform()->renderBackend()), &DrmQPainterBackend::aboutToBeDestroyed, this, [this]() {
connect(static_cast<DrmQPainterBackend*>(pipeline->gpu()->platform()->renderBackend()), &DrmQPainterBackend::aboutToBeDestroyed, this, [this]() {
m_swapchain.reset();
});
}
@ -30,10 +32,10 @@ DrmQPainterLayer::DrmQPainterLayer(DrmDisplayDevice *displayDevice)
std::optional<QRegion> DrmQPainterLayer::startRendering()
{
if (!doesSwapchainFit()) {
m_swapchain = QSharedPointer<DumbSwapchain>::create(m_displayDevice->gpu(), m_displayDevice->sourceSize(), DRM_FORMAT_XRGB8888);
m_swapchain = QSharedPointer<DumbSwapchain>::create(m_pipeline->gpu(), m_pipeline->sourceSize(), DRM_FORMAT_XRGB8888);
}
QRegion needsRepaint;
if (!m_swapchain->acquireBuffer(m_displayDevice->renderGeometry(), &needsRepaint)) {
if (!m_swapchain->acquireBuffer(m_pipeline->displayDevice()->renderGeometry(), &needsRepaint)) {
return std::optional<QRegion>();
}
return needsRepaint;
@ -55,14 +57,14 @@ bool DrmQPainterLayer::scanout(SurfaceItem *surfaceItem)
QSharedPointer<DrmBuffer> DrmQPainterLayer::testBuffer()
{
if (!doesSwapchainFit()) {
m_swapchain = QSharedPointer<DumbSwapchain>::create(m_displayDevice->gpu(), m_displayDevice->sourceSize(), DRM_FORMAT_XRGB8888);
m_swapchain = QSharedPointer<DumbSwapchain>::create(m_pipeline->gpu(), m_pipeline->sourceSize(), DRM_FORMAT_XRGB8888);
}
return m_swapchain->currentBuffer();
}
bool DrmQPainterLayer::doesSwapchainFit() const
{
return m_swapchain && m_swapchain->size() == m_displayDevice->sourceSize();
return m_swapchain && m_swapchain->size() == m_pipeline->sourceSize();
}
QSharedPointer<DrmBuffer> DrmQPainterLayer::currentBuffer() const
@ -80,4 +82,55 @@ bool DrmQPainterLayer::hasDirectScanoutBuffer() const
return false;
}
QSharedPointer<GLTexture> DrmQPainterLayer::texture() const
{
return nullptr;
}
QImage *DrmQPainterLayer::image()
{
return m_swapchain ? m_swapchain->currentBuffer()->image() : nullptr;
}
DrmVirtualQPainterLayer::DrmVirtualQPainterLayer(DrmVirtualOutput *output)
: m_output(output)
{
}
std::optional<QRegion> DrmVirtualQPainterLayer::startRendering()
{
if (m_image.isNull() || m_image.size() != m_output->pixelSize()) {
m_image = QImage(m_output->pixelSize(), QImage::Format_RGB32);
}
return QRegion();
}
bool DrmVirtualQPainterLayer::endRendering(const QRegion &damagedRegion)
{
m_currentDamage = damagedRegion;
return true;
}
bool DrmVirtualQPainterLayer::scanout(SurfaceItem *surfaceItem)
{
Q_UNUSED(surfaceItem);
return false;
}
QSharedPointer<GLTexture> DrmVirtualQPainterLayer::texture() const
{
return nullptr;
}
QRegion DrmVirtualQPainterLayer::currentDamage() const
{
return m_currentDamage;
}
QImage *DrmVirtualQPainterLayer::image()
{
return &m_image;
}
}

View file

@ -9,23 +9,37 @@
#pragma once
#include "drm_layer.h"
#include <QImage>
namespace KWin
{
class DumbSwapchain;
class DrmPipeline;
class DrmVirtualOutput;
class DrmQPainterLayer : public DrmLayer
class QPainterLayer
{
public:
DrmQPainterLayer(DrmDisplayDevice *displayDevice);
virtual ~QPainterLayer() = default;
virtual QImage *image() = 0;
};
class DrmQPainterLayer : public DrmPipelineLayer, QPainterLayer
{
public:
DrmQPainterLayer(DrmPipeline *pipeline);
std::optional<QRegion> startRendering() override;
bool endRendering(const QRegion &damagedRegion) override;
bool scanout(SurfaceItem *surfaceItem) override;
QSharedPointer<DrmBuffer> testBuffer() override;
QSharedPointer<GLTexture> texture() const override;
QSharedPointer<DrmBuffer> currentBuffer() const override;
QRegion currentDamage() const override;
bool hasDirectScanoutBuffer() const override;
QImage *image() override;
private:
bool doesSwapchainFit() const;
@ -34,4 +48,23 @@ private:
QRegion m_currentDamage;
};
class DrmVirtualQPainterLayer : public DrmOutputLayer, QPainterLayer
{
public:
DrmVirtualQPainterLayer(DrmVirtualOutput *output);
std::optional<QRegion> startRendering() override;
bool endRendering(const QRegion &damagedRegion) override;
bool scanout(SurfaceItem *surfaceItem) override;
QSharedPointer<GLTexture> texture() const override;
QRegion currentDamage() const override;
QImage *image() override;
private:
QImage m_image;
QRegion m_currentDamage;
DrmVirtualOutput *const m_output;
};
}

View file

@ -13,15 +13,19 @@
namespace KWin
{
class DrmLayer;
class DrmPipelineLayer;
class DrmDisplayDevice;
class DrmVirtualOutput;
class DrmPipeline;
class DrmOutputLayer;
class DrmRenderBackend
{
public:
virtual ~DrmRenderBackend() = default;
virtual QSharedPointer<DrmLayer> createLayer(DrmDisplayDevice *displayDevice) = 0;
virtual QSharedPointer<DrmPipelineLayer> createDrmPipelineLayer(DrmPipeline *pipeline) = 0;
virtual QSharedPointer<DrmOutputLayer> createLayer(DrmVirtualOutput *output) = 0;
};

View file

@ -77,29 +77,6 @@ void DrmVirtualOutput::updateEnablement(bool enable)
gpu()->platform()->enableOutput(this, enable);
}
QSize DrmVirtualOutput::bufferSize() const
{
return pixelSize();
}
QSize DrmVirtualOutput::sourceSize() const
{
return pixelSize();
}
bool DrmVirtualOutput::isFormatSupported(uint32_t drmFormat) const
{
Q_UNUSED(drmFormat);
return true;
}
QVector<uint64_t> DrmVirtualOutput::supportedModifiers(uint32_t drmFormat) const
{
Q_UNUSED(drmFormat);
// empty list -> implicit modifiers are used / modifier is freely chosen by gbm
return {};
}
int DrmVirtualOutput::gammaRampSize() const
{
return 200;
@ -111,26 +88,11 @@ bool DrmVirtualOutput::setGammaRamp(const GammaRamp &gamma)
return true;
}
int DrmVirtualOutput::maxBpc() const
{
return 8;
}
DrmPlane::Transformations DrmVirtualOutput::softwareTransforms() const
{
return DrmPlane::Transformation::Rotate0;
}
DrmLayer *DrmVirtualOutput::outputLayer() const
DrmOutputLayer *DrmVirtualOutput::outputLayer() const
{
return m_layer.data();
}
bool DrmVirtualOutput::testScanout()
{
return true;
}
void DrmVirtualOutput::recreateSurface()
{
m_layer = m_gpu->platform()->renderBackend()->createLayer(this);

View file

@ -19,7 +19,7 @@ namespace KWin
class SoftwareVsyncMonitor;
class VirtualBackend;
class DrmLayer;
class DrmPipelineLayer;
class DrmVirtualOutput : public DrmAbstractOutput
{
@ -30,19 +30,9 @@ public:
~DrmVirtualOutput() override;
bool present() override;
QSize bufferSize() const override;
QSize sourceSize() const override;
bool isFormatSupported(uint32_t drmFormat) const override;
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const override;
int maxBpc() const override;
int gammaRampSize() const override;
bool setGammaRamp(const GammaRamp &gamma) override;
DrmPlane::Transformations softwareTransforms() const override;
DrmLayer *outputLayer() const override;
bool testScanout() override;
DrmOutputLayer *outputLayer() const override;
void recreateSurface();
private:
@ -50,7 +40,7 @@ private:
void setDpmsMode(DpmsMode mode) override;
void updateEnablement(bool enable) override;
QSharedPointer<DrmLayer> m_layer;
QSharedPointer<DrmOutputLayer> m_layer;
bool m_pageFlipPending = true;
int m_modeIndex = 0;

View file

@ -29,6 +29,7 @@
#include "drm_abstract_output.h"
#include "egl_dmabuf.h"
#include "egl_gbm_layer.h"
#include "virtual_egl_gbm_layer.h"
// kwin libs
#include <kwinglplatform.h>
#include <kwineglimagetexture.h>
@ -208,38 +209,9 @@ bool EglGbmBackend::initBufferConfigs()
return false;
}
static QVector<EGLint> regionToRects(const QRegion &region, DrmAbstractOutput *output)
{
const int height = output->sourceSize().height();
const QMatrix4x4 matrix = DrmOutput::logicalToNativeMatrix(output->geometry(),
output->scale(),
output->transform());
QVector<EGLint> rects;
rects.reserve(region.rectCount() * 4);
for (const QRect &_rect : region) {
const QRect rect = matrix.mapRect(_rect);
rects << rect.left();
rects << height - (rect.y() + rect.height());
rects << rect.width();
rects << rect.height();
}
return rects;
}
void EglGbmBackend::aboutToStartPainting(AbstractOutput *output, const QRegion &damagedRegion)
{
const auto drmOutput = static_cast<DrmAbstractOutput*>(output);
const auto &surface = static_cast<EglGbmLayer*>(drmOutput->outputLayer());
if (surface->bufferAge() > 0 && !damagedRegion.isEmpty() && supportsPartialUpdate()) {
QVector<EGLint> rects = regionToRects(damagedRegion, static_cast<DrmAbstractOutput*>(output));
const bool correct = eglSetDamageRegionKHR(eglDisplay(), surface->eglSurface(), rects.data(), rects.count()/4);
if (!correct) {
qCWarning(KWIN_DRM) << "eglSetDamageRegionKHR failed:" << getEglErrorString();
}
}
static_cast<DrmAbstractOutput*>(output)->outputLayer()->aboutToStartPainting(damagedRegion);
}
SurfaceTexture *EglGbmBackend::createSurfaceTextureInternal(SurfacePixmapInternal *pixmap)
@ -303,23 +275,6 @@ GbmFormat EglGbmBackend::gbmFormatForDrmFormat(uint32_t format) const
}
}
std::optional<uint32_t> EglGbmBackend::chooseFormat(DrmDisplayDevice *device) const
{
// formats are already sorted by order of preference
std::optional<uint32_t> fallback;
for (const auto &format : qAsConst(m_formats)) {
if (device->isFormatSupported(format.drmFormat)) {
int bpc = std::max(format.redSize, std::max(format.greenSize, format.blueSize));
if (bpc <= device->maxBpc() && !fallback.has_value()) {
fallback = format.drmFormat;
} else {
return format.drmFormat;
}
}
}
return fallback;
}
bool EglGbmBackend::prefer10bpc() const
{
static bool ok = false;
@ -332,9 +287,14 @@ EGLConfig EglGbmBackend::config(uint32_t format) const
return m_configs[format];
}
QSharedPointer<DrmLayer> EglGbmBackend::createLayer(DrmDisplayDevice *displayDevice)
QSharedPointer<DrmPipelineLayer> EglGbmBackend::createDrmPipelineLayer(DrmPipeline *pipeline)
{
return QSharedPointer<EglGbmLayer>::create(this, displayDevice);
return QSharedPointer<EglGbmLayer>::create(this, pipeline);
}
QSharedPointer<DrmOutputLayer> EglGbmBackend::createLayer(DrmVirtualOutput *output)
{
return QSharedPointer<VirtualEglGbmLayer>::create(this, output);
}
DrmGpu *EglGbmBackend::gpu() const

View file

@ -39,6 +39,8 @@ class DrmBackend;
class DrmGpu;
class EglGbmLayer;
class DrmDisplayDevice;
class DrmOutputLayer;
class DrmPipeline;
struct GbmFormat {
uint32_t drmFormat = 0;
@ -67,14 +69,14 @@ public:
void init() override;
bool scanout(AbstractOutput *output, SurfaceItem *surfaceItem) override;
bool prefer10bpc() const override;
QSharedPointer<DrmLayer> createLayer(DrmDisplayDevice *displayDevice) override;
QSharedPointer<DrmPipelineLayer> createDrmPipelineLayer(DrmPipeline *pipeline) override;
QSharedPointer<DrmOutputLayer> createLayer(DrmVirtualOutput *output) override;
QSharedPointer<GLTexture> textureForOutput(AbstractOutput *requestedOutput) const override;
QSharedPointer<DrmBuffer> testBuffer(DrmAbstractOutput *output);
EGLConfig config(uint32_t format) const;
GbmFormat gbmFormatForDrmFormat(uint32_t format) const;
std::optional<uint32_t> chooseFormat(DrmDisplayDevice *displyDevice) const;
DrmGpu *gpu() const;
Q_SIGNALS:

View file

@ -34,8 +34,8 @@
namespace KWin
{
EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmDisplayDevice *displayDevice)
: DrmLayer(displayDevice)
EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline)
: DrmPipelineLayer(pipeline)
, m_eglBackend(eglBackend)
{
connect(eglBackend, &EglGbmBackend::aboutToBeDestroyed, this, &EglGbmLayer::destroyResources);
@ -90,7 +90,7 @@ std::optional<QRegion> EglGbmLayer::startRendering()
if (!m_gbmSurface->makeContextCurrent()) {
return std::optional<QRegion>();
}
auto repaintRegion = m_gbmSurface->repaintRegion(m_displayDevice->renderGeometry());
auto repaintRegion = m_gbmSurface->repaintRegion(m_pipeline->displayDevice()->renderGeometry());
// shadow buffer
if (doesShadowBufferFit(m_shadowBuffer.data())) {
@ -99,9 +99,9 @@ std::optional<QRegion> EglGbmLayer::startRendering()
if (doesShadowBufferFit(m_oldShadowBuffer.data())) {
m_shadowBuffer = m_oldShadowBuffer;
} else {
if (m_displayDevice->softwareTransforms() != DrmPlane::Transformations(DrmPlane::Transformation::Rotate0)) {
if (m_pipeline->pending.bufferTransformation != m_pipeline->pending.sourceTransformation) {
const auto format = m_eglBackend->gbmFormatForDrmFormat(m_gbmSurface->format());
m_shadowBuffer = QSharedPointer<ShadowBuffer>::create(m_displayDevice->sourceSize(), format);
m_shadowBuffer = QSharedPointer<ShadowBuffer>::create(m_pipeline->sourceSize(), format);
if (!m_shadowBuffer->isComplete()) {
return std::optional<QRegion>();
}
@ -121,11 +121,23 @@ std::optional<QRegion> EglGbmLayer::startRendering()
return repaintRegion;
}
void EglGbmLayer::aboutToStartPainting(const QRegion &damagedRegion)
{
if (m_gbmSurface->bufferAge() > 0 && !damagedRegion.isEmpty() && m_eglBackend->supportsPartialUpdate()) {
QVector<EGLint> rects = m_pipeline->displayDevice()->regionToRects(damagedRegion);
const bool correct = eglSetDamageRegionKHR(m_eglBackend->eglDisplay(), m_gbmSurface->eglSurface(), rects.data(), rects.count() / 4);
if (!correct) {
qCWarning(KWIN_DRM) << "eglSetDamageRegionKHR failed:" << getEglErrorString();
}
}
}
bool EglGbmLayer::endRendering(const QRegion &damagedRegion)
{
if (m_shadowBuffer) {
GLRenderTarget::popRenderTarget();
m_shadowBuffer->render(m_displayDevice);
// TODO handle m_pipeline->pending.bufferTransformation != Rotate0
m_shadowBuffer->render(m_pipeline->pending.sourceTransformation);
}
GLRenderTarget::popRenderTarget();
const auto buffer = m_gbmSurface->swapBuffersForDrm(damagedRegion);
@ -166,70 +178,83 @@ bool EglGbmLayer::renderTestBuffer()
return false;
}
glClear(GL_COLOR_BUFFER_BIT);
if (!endRendering(m_displayDevice->renderGeometry())) {
if (!endRendering(m_pipeline->displayDevice()->renderGeometry())) {
return false;
}
return true;
}
bool EglGbmLayer::createGbmSurface()
bool EglGbmLayer::createGbmSurface(uint32_t format, const QVector<uint64_t> &modifiers)
{
static bool modifiersEnvSet = false;
static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
auto format = m_eglBackend->chooseFormat(m_displayDevice);
if (!format || m_importMode == MultiGpuImportMode::DumbBufferXrgb8888) {
format = DRM_FORMAT_XRGB8888;
}
const auto modifiers = m_displayDevice->supportedModifiers(format.value());
const auto size = m_displayDevice->bufferSize();
const auto config = m_eglBackend->config(format.value());
const bool allowModifiers = m_eglBackend->gpu()->addFB2ModifiersSupported() && m_displayDevice->gpu()->addFB2ModifiersSupported()
const bool allowModifiers = m_eglBackend->gpu()->addFB2ModifiersSupported() && m_pipeline->gpu()->addFB2ModifiersSupported()
&& ((m_eglBackend->gpu()->isNVidia() && !modifiersEnvSet) || (modifiersEnvSet && modifiersEnv));
const auto size = m_pipeline->bufferSize();
const auto config = m_eglBackend->config(format);
QSharedPointer<GbmSurface> gbmSurface;
#if HAVE_GBM_BO_GET_FD_FOR_PLANE
if (!allowModifiers) {
#else
// modifiers have to be disabled with multi-gpu if gbm_bo_get_fd_for_plane is not available
if (!allowModifiers || m_displayDevice->gpu() != m_eglBackend->gpu()) {
if (!allowModifiers || m_pipeline->gpu() != m_eglBackend->gpu()) {
#endif
int flags = GBM_BO_USE_RENDERING;
if (m_displayDevice->gpu() == m_eglBackend->gpu()) {
if (m_pipeline->gpu() == m_eglBackend->gpu()) {
flags |= GBM_BO_USE_SCANOUT;
} else {
flags |= GBM_BO_USE_LINEAR;
}
gbmSurface = QSharedPointer<GbmSurface>::create(m_eglBackend->gpu(), size, format.value(), flags, config);
gbmSurface = QSharedPointer<GbmSurface>::create(m_eglBackend->gpu(), size, format, flags, config);
} else {
gbmSurface = QSharedPointer<GbmSurface>::create(m_eglBackend->gpu(), size, format.value(), modifiers, config);
gbmSurface = QSharedPointer<GbmSurface>::create(m_eglBackend->gpu(), size, format, modifiers, config);
if (!gbmSurface->isValid()) {
// the egl / gbm implementation may reject the modifier list from another gpu
// as a fallback use linear, to at least make CPU copy more efficient
const QVector<uint64_t> linear = {DRM_FORMAT_MOD_LINEAR};
gbmSurface = QSharedPointer<GbmSurface>::create(m_eglBackend->gpu(), size, format.value(), linear, config);
gbmSurface = QSharedPointer<GbmSurface>::create(m_eglBackend->gpu(), size, format, linear, config);
}
}
if (!gbmSurface->isValid()) {
if (gbmSurface->isValid()) {
m_oldGbmSurface = m_gbmSurface;
m_gbmSurface = gbmSurface;
return true;
} else {
return false;
}
m_oldGbmSurface = m_gbmSurface;
m_gbmSurface = gbmSurface;
return true;
}
bool EglGbmLayer::createGbmSurface()
{
const auto tranches = m_eglBackend->dmabuf()->tranches();
for (const auto &tranche : tranches) {
for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) {
const uint32_t &format = it.key();
if (m_importMode == MultiGpuImportMode::DumbBufferXrgb8888 && format != DRM_FORMAT_XRGB8888) {
continue;
}
if (m_pipeline->isFormatSupported(format) && createGbmSurface(format, m_pipeline->supportedModifiers(format))) {
return true;
}
}
}
return false;
}
bool EglGbmLayer::doesGbmSurfaceFit(GbmSurface *surf) const
{
return surf && surf->size() == m_displayDevice->bufferSize()
&& m_displayDevice->isFormatSupported(surf->format())
return surf && surf->size() == m_pipeline->bufferSize()
&& m_pipeline->isFormatSupported(surf->format())
&& (m_importMode != MultiGpuImportMode::DumbBufferXrgb8888 || surf->format() == DRM_FORMAT_XRGB8888)
&& (surf->modifiers().isEmpty() || m_displayDevice->supportedModifiers(surf->format()) == surf->modifiers());
&& (surf->modifiers().isEmpty() || m_pipeline->supportedModifiers(surf->format()) == surf->modifiers());
}
bool EglGbmLayer::doesShadowBufferFit(ShadowBuffer *buffer) const
{
if (m_displayDevice->softwareTransforms() != DrmPlane::Transformations(DrmPlane::Transformation::Rotate0)) {
return buffer && buffer->texture()->size() == m_displayDevice->sourceSize() && buffer->drmFormat() == m_gbmSurface->format();
if (m_pipeline->pending.bufferTransformation != m_pipeline->pending.sourceTransformation) {
return buffer && buffer->texture()->size() == m_pipeline->sourceSize() && buffer->drmFormat() == m_gbmSurface->format();
} else {
return buffer == nullptr;
}
@ -237,7 +262,7 @@ bool EglGbmLayer::doesShadowBufferFit(ShadowBuffer *buffer) const
bool EglGbmLayer::doesSwapchainFit(DumbSwapchain *swapchain) const
{
return swapchain && swapchain->size() == m_displayDevice->sourceSize() && swapchain->drmFormat() == m_gbmSurface->format();
return swapchain && swapchain->size() == m_pipeline->sourceSize() && swapchain->drmFormat() == m_gbmSurface->format();
}
QSharedPointer<GLTexture> EglGbmLayer::texture() const
@ -248,7 +273,7 @@ QSharedPointer<GLTexture> EglGbmLayer::texture() const
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString();
return QSharedPointer<EGLImageTexture>(nullptr);
}
return QSharedPointer<EGLImageTexture>::create(m_eglBackend->eglDisplay(), image, GL_RGBA8, m_displayDevice->sourceSize());
return QSharedPointer<EGLImageTexture>::create(m_eglBackend->eglDisplay(), image, GL_RGBA8, m_pipeline->sourceSize());
};
if (m_scanoutBuffer) {
return createImage(dynamic_cast<GbmBuffer*>(m_scanoutBuffer.data()));
@ -315,7 +340,7 @@ QSharedPointer<DrmBuffer> EglGbmLayer::importDmabuf()
data.strides[i] = gbm_bo_get_stride_for_plane(bo, i);
data.offsets[i] = gbm_bo_get_offset(bo, i);
}
importedBuffer = gbm_bo_import(m_displayDevice->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
importedBuffer = gbm_bo_import(m_pipeline->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
} else {
#endif
gbm_import_fd_data data = {
@ -329,7 +354,7 @@ QSharedPointer<DrmBuffer> EglGbmLayer::importDmabuf()
qCWarning(KWIN_DRM, "failed to export gbm_bo as dma-buf: %s", strerror(errno));
return nullptr;
}
importedBuffer = gbm_bo_import(m_displayDevice->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
importedBuffer = gbm_bo_import(m_pipeline->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
#if HAVE_GBM_BO_GET_FD_FOR_PLANE
}
#endif
@ -337,7 +362,7 @@ QSharedPointer<DrmBuffer> EglGbmLayer::importDmabuf()
qCWarning(KWIN_DRM, "failed to import gbm_bo for multi-gpu usage: %s", strerror(errno));
return nullptr;
}
const auto buffer = QSharedPointer<DrmGbmBuffer>::create(m_displayDevice->gpu(), importedBuffer, nullptr);
const auto buffer = QSharedPointer<DrmGbmBuffer>::create(m_pipeline->gpu(), nullptr, importedBuffer);
return buffer->bufferId() ? buffer : nullptr;
}
@ -349,7 +374,7 @@ QSharedPointer<DrmBuffer> EglGbmLayer::importWithCpu()
if (doesSwapchainFit(m_oldImportSwapchain.data())) {
m_importSwapchain = m_oldImportSwapchain;
} else {
const auto swapchain = QSharedPointer<DumbSwapchain>::create(m_displayDevice->gpu(), m_displayDevice->sourceSize(), m_gbmSurface->format());
const auto swapchain = QSharedPointer<DumbSwapchain>::create(m_pipeline->gpu(), m_pipeline->sourceSize(), m_gbmSurface->format());
if (swapchain->isEmpty()) {
return nullptr;
}
@ -386,7 +411,7 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem)
return false;
}
const auto buffer = qobject_cast<KWaylandServer::LinuxDmaBufV1ClientBuffer *>(item->surface()->buffer());
if (!buffer || buffer->planes().isEmpty() || buffer->size() != m_displayDevice->sourceSize()) {
if (!buffer || buffer->planes().isEmpty() || buffer->size() != m_pipeline->sourceSize()) {
return false;
}
@ -396,51 +421,14 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem)
m_scanoutCandidate.surface = item->surface();
m_scanoutCandidate.attemptedThisFrame = true;
if (!m_displayDevice->isFormatSupported(buffer->format())) {
if (!m_pipeline->isFormatSupported(buffer->format())) {
sendDmabufFeedback(buffer);
return false;
}
const auto planes = buffer->planes();
gbm_bo *importedBuffer;
if (planes.first().modifier != DRM_FORMAT_MOD_INVALID || planes.first().offset > 0 || planes.count() > 1) {
if (!m_displayDevice->gpu()->addFB2ModifiersSupported() || !m_displayDevice->supportedModifiers(buffer->format()).contains(planes.first().modifier)) {
return false;
}
gbm_import_fd_modifier_data data = {};
data.format = buffer->format();
data.width = (uint32_t) buffer->size().width();
data.height = (uint32_t) buffer->size().height();
data.num_fds = planes.count();
data.modifier = planes.first().modifier;
for (int i = 0; i < planes.count(); i++) {
data.fds[i] = planes[i].fd;
data.offsets[i] = planes[i].offset;
data.strides[i] = planes[i].stride;
}
importedBuffer = gbm_bo_import(m_displayDevice->gpu()->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
} else {
const auto &plane = planes.first();
gbm_import_fd_data data = {};
data.fd = plane.fd;
data.width = (uint32_t) buffer->size().width();
data.height = (uint32_t) buffer->size().height();
data.stride = plane.stride;
data.format = buffer->format();
importedBuffer = gbm_bo_import(m_displayDevice->gpu()->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT);
}
if (!importedBuffer) {
m_scanoutBuffer = QSharedPointer<DrmGbmBuffer>::create(m_pipeline->gpu(), buffer);
if (!m_scanoutBuffer || !m_scanoutBuffer->bufferId()) {
sendDmabufFeedback(buffer);
if (errno != EINVAL) {
qCWarning(KWIN_DRM) << "Importing buffer for direct scanout failed:" << strerror(errno);
}
return false;
}
m_scanoutBuffer = QSharedPointer<DrmGbmBuffer>::create(m_displayDevice->gpu(), importedBuffer, buffer);
if (!m_scanoutBuffer->bufferId()) {
// buffer can't actually be scanned out. Mesa is supposed to prevent this from happening
// in gbm_bo_import but apparently that doesn't always work
m_scanoutBuffer.reset();
sendDmabufFeedback(buffer);
return false;
}
// damage tracking for screen casting
@ -450,13 +438,13 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem)
surfaceItem->resetDamage();
for (const auto &rect : trackedDamage) {
auto damageRect = QRect(rect);
damageRect.translate(m_displayDevice->renderGeometry().topLeft());
damageRect.translate(m_pipeline->displayDevice()->renderGeometry().topLeft());
damage |= damageRect;
}
} else {
damage = m_displayDevice->renderGeometry();
damage = m_pipeline->displayDevice()->renderGeometry();
}
if (m_displayDevice->testScanout()) {
if (m_pipeline->testScanout()) {
m_currentBuffer = m_scanoutBuffer;
m_currentDamage = damage;
return true;
@ -471,9 +459,9 @@ void EglGbmLayer::sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer *
if (!m_scanoutCandidate.attemptedFormats[failedBuffer->format()].contains(failedBuffer->planes().first().modifier)) {
m_scanoutCandidate.attemptedFormats[failedBuffer->format()] << failedBuffer->planes().first().modifier;
}
if (const auto &drmOutput = dynamic_cast<DrmOutput *>(m_displayDevice); drmOutput && m_scanoutCandidate.surface->dmabufFeedbackV1()) {
if (m_scanoutCandidate.surface->dmabufFeedbackV1()) {
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> scanoutTranches;
const auto &drmFormats = drmOutput->pipeline()->supportedFormats();
const auto &drmFormats = m_pipeline->supportedFormats();
const auto tranches = m_eglBackend->dmabuf()->tranches();
for (const auto &tranche : tranches) {
KWaylandServer::LinuxDmaBufV1Feedback::Tranche scanoutTranche;
@ -488,7 +476,7 @@ void EglGbmLayer::sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer *
}
}
if (!scanoutTranche.formatTable.isEmpty()) {
scanoutTranche.device = m_displayDevice->gpu()->deviceId();
scanoutTranche.device = m_pipeline->gpu()->deviceId();
scanoutTranche.flags = KWaylandServer::LinuxDmaBufV1Feedback::TrancheFlag::Scanout;
scanoutTranches << scanoutTranche;
}
@ -502,16 +490,6 @@ QSharedPointer<DrmBuffer> EglGbmLayer::currentBuffer() const
return m_scanoutBuffer ? m_scanoutBuffer : m_currentBuffer;
}
int EglGbmLayer::bufferAge() const
{
return m_gbmSurface ? m_gbmSurface->bufferAge() : 0;
}
EGLSurface EglGbmLayer::eglSurface() const
{
return m_gbmSurface ? m_gbmSurface->eglSurface() : EGL_NO_SURFACE;
}
bool EglGbmLayer::hasDirectScanoutBuffer() const
{
return m_scanoutBuffer != nullptr;

View file

@ -33,26 +33,26 @@ class DrmGpu;
class SurfaceItem;
class GLTexture;
class EglGbmBackend;
class DrmPipeline;
class EglGbmLayer : public DrmLayer
class EglGbmLayer : public DrmPipelineLayer
{
public:
EglGbmLayer(EglGbmBackend *eglBackend, DrmDisplayDevice *displayDevice);
EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline);
~EglGbmLayer();
std::optional<QRegion> startRendering() override;
void aboutToStartPainting(const QRegion &damagedRegion) override;
bool endRendering(const QRegion &damagedRegion) override;
bool scanout(SurfaceItem *surfaceItem) override;
QSharedPointer<DrmBuffer> testBuffer() override;
QSharedPointer<DrmBuffer> currentBuffer() const override;
bool hasDirectScanoutBuffer() const override;
QRegion currentDamage() const override;
QSharedPointer<GLTexture> texture() const;
int bufferAge() const;
EGLSurface eglSurface() const;
QSharedPointer<GLTexture> texture() const override;
private:
bool createGbmSurface(uint32_t format, const QVector<uint64_t> &modifiers);
bool createGbmSurface();
bool doesGbmSurfaceFit(GbmSurface *surf) const;
bool doesShadowBufferFit(ShadowBuffer *buffer) const;

View file

@ -13,6 +13,7 @@
#include "drm_buffer.h"
#include "renderloop_p.h"
#include "drm_qpainter_layer.h"
#include "drm_virtual_output.h"
#include <drm_fourcc.h>
@ -34,7 +35,7 @@ DrmQPainterBackend::~DrmQPainterBackend()
QImage *DrmQPainterBackend::bufferForScreen(AbstractOutput *output)
{
const auto drmOutput = static_cast<DrmAbstractOutput*>(output);
return static_cast<DrmDumbBuffer*>(drmOutput->outputLayer()->currentBuffer().data())->image();
return dynamic_cast<QPainterLayer*>(drmOutput->outputLayer())->image();
}
QRegion DrmQPainterBackend::beginFrame(AbstractOutput *output)
@ -51,9 +52,14 @@ void DrmQPainterBackend::endFrame(AbstractOutput *output, const QRegion &rendere
static_cast<DrmAbstractOutput*>(output)->present();
}
QSharedPointer<DrmLayer> DrmQPainterBackend::createLayer(DrmDisplayDevice *displayDevice)
QSharedPointer<DrmPipelineLayer> DrmQPainterBackend::createDrmPipelineLayer(DrmPipeline *pipeline)
{
return QSharedPointer<DrmQPainterLayer>::create(displayDevice);
return QSharedPointer<DrmQPainterLayer>::create(pipeline);
}
QSharedPointer<DrmOutputLayer> DrmQPainterBackend::createLayer(DrmVirtualOutput *output)
{
return QSharedPointer<DrmVirtualQPainterLayer>::create(output);
}
}

View file

@ -21,6 +21,7 @@ namespace KWin
class DrmBackend;
class DrmAbstractOutput;
class DrmQPainterLayer;
class DrmPipeline;
class DrmQPainterBackend : public QPainterBackend, public DrmRenderBackend
{
@ -32,7 +33,8 @@ public:
QImage *bufferForScreen(AbstractOutput *output) override;
QRegion beginFrame(AbstractOutput *output) override;
void endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damagedRegion) override;
QSharedPointer<DrmLayer> createLayer(DrmDisplayDevice *displayDevice) override;
QSharedPointer<DrmPipelineLayer> createDrmPipelineLayer(DrmPipeline *pipeline) override;
QSharedPointer<DrmOutputLayer> createLayer(DrmVirtualOutput *output) override;
Q_SIGNALS:
void aboutToBeDestroyed();

View file

@ -56,10 +56,9 @@ ShadowBuffer::~ShadowBuffer()
{
}
void ShadowBuffer::render(DrmDisplayDevice *displayDevice)
void ShadowBuffer::render(DrmPlane::Transformations transform)
{
QMatrix4x4 mvpMatrix;
const auto transform = displayDevice->softwareTransforms();
if (transform & DrmPlane::Transformation::Rotate90) {
mvpMatrix.rotate(90, 0, 0, 1);
} else if (transform & DrmPlane::Transformation::Rotate180) {

View file

@ -12,6 +12,7 @@
#include <kwinglutils.h>
#include "egl_gbm_backend.h"
#include "drm_object_plane.h"
namespace KWin
{
@ -25,7 +26,7 @@ public:
~ShadowBuffer();
bool isComplete() const;
void render(DrmDisplayDevice *displayDevice);
void render(DrmPlane::Transformations transform);
GLRenderTarget *renderTarget() const;
QSharedPointer<GLTexture> texture() const;

View file

@ -0,0 +1,190 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "virtual_egl_gbm_layer.h"
#include "gbm_surface.h"
#include "drm_abstract_output.h"
#include "drm_gpu.h"
#include "egl_gbm_backend.h"
#include "shadowbuffer.h"
#include "drm_output.h"
#include "drm_pipeline.h"
#include "dumb_swapchain.h"
#include "logging.h"
#include "egl_dmabuf.h"
#include "surfaceitem_wayland.h"
#include "kwineglimagetexture.h"
#include "drm_backend.h"
#include "drm_virtual_output.h"
#include "kwineglutils_p.h"
#include "KWaylandServer/surface_interface.h"
#include "KWaylandServer/linuxdmabufv1clientbuffer.h"
#include <QRegion>
#include <drm_fourcc.h>
#include <gbm.h>
#include <errno.h>
#include <unistd.h>
namespace KWin
{
VirtualEglGbmLayer::VirtualEglGbmLayer(EglGbmBackend *eglBackend, DrmVirtualOutput *output)
: m_output(output)
, m_eglBackend(eglBackend)
{
connect(eglBackend, &EglGbmBackend::aboutToBeDestroyed, this, &VirtualEglGbmLayer::destroyResources);
}
void VirtualEglGbmLayer::destroyResources()
{
m_gbmSurface.reset();
m_oldGbmSurface.reset();
}
void VirtualEglGbmLayer::aboutToStartPainting(const QRegion &damagedRegion)
{
if (m_gbmSurface->bufferAge() > 0 && !damagedRegion.isEmpty() && m_eglBackend->supportsPartialUpdate()) {
const QRegion region = damagedRegion & m_output->geometry();
QVector<EGLint> rects = m_output->regionToRects(region);
const bool correct = eglSetDamageRegionKHR(m_eglBackend->eglDisplay(), m_gbmSurface->eglSurface(), rects.data(), rects.count() / 4);
if (!correct) {
qCWarning(KWIN_DRM) << "eglSetDamageRegionKHR failed:" << getEglErrorString();
}
}
}
std::optional<QRegion> VirtualEglGbmLayer::startRendering()
{
// gbm surface
if (doesGbmSurfaceFit(m_gbmSurface.data())) {
m_oldGbmSurface.reset();
} else {
if (doesGbmSurfaceFit(m_oldGbmSurface.data())) {
m_gbmSurface = m_oldGbmSurface;
} else {
if (!createGbmSurface()) {
return std::optional<QRegion>();
}
}
}
if (!m_gbmSurface->makeContextCurrent()) {
return std::optional<QRegion>();
}
GLRenderTarget::pushRenderTarget(m_gbmSurface->renderTarget());
return m_gbmSurface->repaintRegion(m_output->geometry());
}
bool VirtualEglGbmLayer::endRendering(const QRegion &damagedRegion)
{
GLRenderTarget::popRenderTarget();
const auto buffer = m_gbmSurface->swapBuffers(damagedRegion.intersected(m_output->geometry()));
if (buffer) {
m_currentBuffer = buffer;
m_currentDamage = damagedRegion;
}
return buffer;
}
QRegion VirtualEglGbmLayer::currentDamage() const
{
return m_currentDamage;
}
bool VirtualEglGbmLayer::createGbmSurface()
{
static bool modifiersEnvSet = false;
static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
const auto tranches = m_eglBackend->dmabuf()->tranches();
for (const auto &tranche : tranches) {
for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) {
const auto size = m_output->pixelSize();
const auto config = m_eglBackend->config(it.key());
const auto modifiers = it.value();
const bool allowModifiers = m_eglBackend->gpu()->addFB2ModifiersSupported() && ((m_eglBackend->gpu()->isNVidia() && !modifiersEnvSet) || (modifiersEnvSet && modifiersEnv));
QSharedPointer<GbmSurface> gbmSurface;
if (!allowModifiers) {
gbmSurface = QSharedPointer<GbmSurface>::create(m_eglBackend->gpu(), size, it.key(), GBM_BO_USE_RENDERING, config);
} else {
gbmSurface = QSharedPointer<GbmSurface>::create(m_eglBackend->gpu(), size, it.key(), it.value(), config);
}
if (!gbmSurface->isValid()) {
continue;
}
m_oldGbmSurface = m_gbmSurface;
m_gbmSurface = gbmSurface;
return true;
}
}
return false;
}
bool VirtualEglGbmLayer::doesGbmSurfaceFit(GbmSurface *surf) const
{
return surf && surf->size() == m_output->pixelSize();
}
QSharedPointer<GLTexture> VirtualEglGbmLayer::texture() const
{
GbmBuffer *gbmBuffer = m_gbmSurface->currentBuffer().get();
if (!gbmBuffer) {
qCWarning(KWIN_DRM) << "Failed to record frame: No gbm buffer!";
return nullptr;
}
EGLImageKHR image = eglCreateImageKHR(m_eglBackend->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 nullptr;
}
return QSharedPointer<EGLImageTexture>::create(m_eglBackend->eglDisplay(), image, GL_RGBA8, m_gbmSurface->size());
}
bool VirtualEglGbmLayer::scanout(SurfaceItem *surfaceItem)
{
static bool valid;
static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid;
if (directScanoutDisabled) {
return false;
}
SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem);
if (!item || !item->surface()) {
return false;
}
const auto buffer = qobject_cast<KWaylandServer::LinuxDmaBufV1ClientBuffer *>(item->surface()->buffer());
if (!buffer || buffer->planes().isEmpty() || buffer->size() != m_output->pixelSize()) {
return false;
}
const auto scanoutBuffer = QSharedPointer<GbmBuffer>::create(m_output->gpu(), buffer);
if (!scanoutBuffer->getBo()) {
return false;
}
// damage tracking for screen casting
QRegion damage;
if (m_scanoutSurface == item->surface()) {
QRegion trackedDamage = surfaceItem->damage();
surfaceItem->resetDamage();
for (const auto &rect : trackedDamage) {
auto damageRect = QRect(rect);
damageRect.translate(m_output->geometry().topLeft());
damage |= damageRect;
}
} else {
damage = m_output->geometry();
}
m_scanoutSurface = item->surface();
m_currentBuffer = scanoutBuffer;
m_currentDamage = damage;
return true;
}
}

View file

@ -0,0 +1,61 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "drm_layer.h"
#include <QSharedPointer>
#include <QPointer>
#include <QMap>
#include <QRegion>
#include <optional>
#include <epoxy/egl.h>
namespace KWaylandServer
{
class SurfaceInterface;
}
namespace KWin
{
class GbmSurface;
class GLTexture;
class EglGbmBackend;
class GbmBuffer;
class DrmVirtualOutput;
class VirtualEglGbmLayer : public DrmOutputLayer
{
public:
VirtualEglGbmLayer(EglGbmBackend *eglBackend, DrmVirtualOutput *output);
void aboutToStartPainting(const QRegion &damagedRegion) override;
std::optional<QRegion> startRendering() override;
bool endRendering(const QRegion &damagedRegion) override;
bool scanout(SurfaceItem *surfaceItem) override;
QRegion currentDamage() const override;
QSharedPointer<GLTexture> texture() const override;
private:
bool createGbmSurface();
bool doesGbmSurfaceFit(GbmSurface *surf) const;
void destroyResources();
QPointer<KWaylandServer::SurfaceInterface> m_scanoutSurface;
QSharedPointer<GbmBuffer> m_currentBuffer;
QRegion m_currentDamage;
QSharedPointer<GbmSurface> m_gbmSurface;
QSharedPointer<GbmSurface> m_oldGbmSurface;
DrmVirtualOutput *const m_output;
EglGbmBackend *const m_eglBackend;
};
}