backends/drm: port DrmPipeline and DrmGpu to use layers
Instead of having the render backends manage layers, have DrmGpu and DrmPipeline do it. This makes it possible to unify code paths for leased and normal outputs, remove some redirection and have more freedom with assigning layers to screens.
This commit is contained in:
parent
cdac2690d1
commit
a04bdf2355
24 changed files with 295 additions and 206 deletions
|
@ -9,6 +9,7 @@
|
|||
#include "drm_abstract_output.h"
|
||||
#include "drm_gpu.h"
|
||||
#include "drm_backend.h"
|
||||
#include "renderloop_p.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -30,4 +31,14 @@ QRect DrmAbstractOutput::renderGeometry() const
|
|||
return geometry();
|
||||
}
|
||||
|
||||
void DrmAbstractOutput::frameFailed() const
|
||||
{
|
||||
RenderLoopPrivate::get(m_renderLoop)->notifyFrameFailed();
|
||||
}
|
||||
|
||||
void DrmAbstractOutput::pageFlipped(std::chrono::nanoseconds timestamp) const
|
||||
{
|
||||
RenderLoopPrivate::get(m_renderLoop)->notifyFrameCompleted(timestamp);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ public:
|
|||
|
||||
RenderLoop *renderLoop() const override;
|
||||
QRect renderGeometry() const override;
|
||||
void frameFailed() const override;
|
||||
void pageFlipped(std::chrono::nanoseconds timestamp) const override;
|
||||
|
||||
protected:
|
||||
friend class DrmGpu;
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace KWin
|
|||
|
||||
class DrmBuffer;
|
||||
class DrmGpu;
|
||||
class DrmLayer;
|
||||
|
||||
class DrmDisplayDevice
|
||||
{
|
||||
|
@ -26,7 +27,11 @@ public:
|
|||
|
||||
DrmGpu *gpu() const;
|
||||
|
||||
virtual bool present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion) = 0;
|
||||
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;
|
||||
|
@ -34,6 +39,7 @@ public:
|
|||
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;
|
||||
|
||||
protected:
|
||||
DrmGpu *const m_gpu;
|
||||
|
|
|
@ -309,9 +309,9 @@ bool DrmGpu::updateOutputs()
|
|||
if (testPendingConfiguration()) {
|
||||
for (const auto &pipeline : qAsConst(m_pipelines)) {
|
||||
pipeline->applyPendingChanges();
|
||||
if (!pipeline->pending.crtc && pipeline->output()) {
|
||||
if (const auto drmOutput = dynamic_cast<DrmAbstractOutput*>(pipeline->displayDevice()); drmOutput && !pipeline->pending.crtc) {
|
||||
pipeline->pending.enabled = false;
|
||||
pipeline->output()->setEnabled(false);
|
||||
drmOutput->setEnabled(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -421,6 +421,9 @@ bool DrmGpu::testPipelines()
|
|||
// pipelines that are enabled but not active need to be activated for the test
|
||||
QVector<DrmPipeline*> inactivePipelines;
|
||||
for (const auto &pipeline : qAsConst(m_pipelines)) {
|
||||
if (!pipeline->pending.layer) {
|
||||
pipeline->pending.layer = m_renderBackend->createLayer(pipeline->displayDevice());
|
||||
}
|
||||
if (!pipeline->pending.active) {
|
||||
pipeline->pending.active = true;
|
||||
inactivePipelines << pipeline;
|
||||
|
@ -553,6 +556,7 @@ void DrmGpu::removeOutput(DrmOutput *output)
|
|||
qCDebug(KWIN_DRM) << "Removing output" << output;
|
||||
m_drmOutputs.removeOne(output);
|
||||
m_pipelines.removeOne(output->pipeline());
|
||||
output->pipeline()->pending.layer.reset();
|
||||
m_outputs.removeOne(output);
|
||||
Q_EMIT outputRemoved(output);
|
||||
delete output;
|
||||
|
@ -560,12 +564,17 @@ void DrmGpu::removeOutput(DrmOutput *output)
|
|||
|
||||
EglGbmBackend *DrmGpu::eglBackend() const
|
||||
{
|
||||
return m_eglBackend;
|
||||
return dynamic_cast<EglGbmBackend*>(m_renderBackend);
|
||||
}
|
||||
|
||||
void DrmGpu::setEglBackend(EglGbmBackend *eglBackend)
|
||||
DrmRenderBackend *DrmGpu::renderBackend() const
|
||||
{
|
||||
m_eglBackend = eglBackend;
|
||||
return m_renderBackend;
|
||||
}
|
||||
|
||||
void DrmGpu::setRenderBackend(DrmRenderBackend *backend)
|
||||
{
|
||||
m_renderBackend = backend;
|
||||
}
|
||||
|
||||
DrmBackend *DrmGpu::platform() const {
|
||||
|
@ -661,6 +670,7 @@ void DrmGpu::removeLeaseOutput(DrmLeaseOutput *output)
|
|||
qCDebug(KWIN_DRM) << "Removing leased output" << output;
|
||||
m_leaseOutputs.removeOne(output);
|
||||
m_pipelines.removeOne(output->pipeline());
|
||||
output->pipeline()->pending.layer.reset();
|
||||
delete output;
|
||||
}
|
||||
|
||||
|
@ -740,10 +750,10 @@ bool DrmGpu::maybeModeset()
|
|||
}
|
||||
const bool ok = DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::CommitModeset, unusedObjects());
|
||||
for (DrmPipeline *pipeline : qAsConst(pipelines)) {
|
||||
if (pipeline->modesetPresentPending() && pipeline->output()) {
|
||||
if (pipeline->modesetPresentPending()) {
|
||||
pipeline->resetModesetPresentPending();
|
||||
if (!ok) {
|
||||
pipeline->output()->presentFailed();
|
||||
pipeline->displayDevice()->frameFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ class DrmPipeline;
|
|||
class DrmAbstractOutput;
|
||||
class DrmVirtualOutput;
|
||||
class DrmLeaseOutput;
|
||||
class DrmRenderBackend;
|
||||
|
||||
class DrmGpu : public QObject
|
||||
{
|
||||
|
@ -59,6 +60,7 @@ public:
|
|||
gbm_device *gbmDevice() const;
|
||||
EGLDisplay eglDisplay() const;
|
||||
EglGbmBackend *eglBackend() const;
|
||||
DrmRenderBackend *renderBackend() const;
|
||||
DrmBackend *platform() const;
|
||||
/**
|
||||
* Returns the clock from which presentation timestamps are sourced. The returned value
|
||||
|
@ -71,7 +73,7 @@ public:
|
|||
const QVector<DrmPipeline*> pipelines() const;
|
||||
|
||||
void setEglDisplay(EGLDisplay display);
|
||||
void setEglBackend(EglGbmBackend *eglBackend);
|
||||
void setRenderBackend(DrmRenderBackend *backend);
|
||||
|
||||
bool updateOutputs();
|
||||
|
||||
|
@ -120,7 +122,7 @@ private:
|
|||
clockid_t m_presentationClock;
|
||||
gbm_device* m_gbmDevice;
|
||||
EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
|
||||
QPointer<EglGbmBackend> m_eglBackend;
|
||||
DrmRenderBackend *m_renderBackend;
|
||||
DrmBackend* const m_platform;
|
||||
|
||||
QVector<DrmPlane*> m_planes;
|
||||
|
|
|
@ -41,6 +41,9 @@ public:
|
|||
virtual QSharedPointer<DrmBuffer> testBuffer() = 0;
|
||||
|
||||
virtual QSharedPointer<DrmBuffer> currentBuffer() const = 0;
|
||||
virtual QRegion currentDamage() const = 0;
|
||||
virtual bool hasDirectScanoutBuffer() const = 0;
|
||||
|
||||
virtual DrmDisplayDevice *displayDevice() const = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -32,11 +32,13 @@ DrmLeaseOutput::DrmLeaseOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDe
|
|||
, DrmDisplayDevice(pipeline->gpu())
|
||||
, m_pipeline(pipeline)
|
||||
{
|
||||
m_pipeline->setDisplayDevice(this);
|
||||
qCDebug(KWIN_DRM) << "offering connector" << m_pipeline->connector()->id() << "for lease";
|
||||
}
|
||||
|
||||
DrmLeaseOutput::~DrmLeaseOutput()
|
||||
{
|
||||
m_pipeline->setDisplayDevice(nullptr);
|
||||
qCDebug(KWIN_DRM) << "revoking lease offer for connector" << m_pipeline->connector()->id();
|
||||
}
|
||||
|
||||
|
@ -76,10 +78,13 @@ DrmPipeline *DrmLeaseOutput::pipeline() const
|
|||
return m_pipeline;
|
||||
}
|
||||
|
||||
bool DrmLeaseOutput::present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion)
|
||||
bool DrmLeaseOutput::present()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrmLeaseOutput::testScanout()
|
||||
{
|
||||
Q_UNUSED(buffer)
|
||||
Q_UNUSED(damagedRegion)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -122,4 +127,18 @@ QRect DrmLeaseOutput::renderGeometry() const
|
|||
return QRect(QPoint(), m_pipeline->sourceSize());
|
||||
}
|
||||
|
||||
DrmLayer *DrmLeaseOutput::outputLayer() const
|
||||
{
|
||||
return m_pipeline->pending.layer.data();
|
||||
}
|
||||
|
||||
void DrmLeaseOutput::frameFailed() const
|
||||
{
|
||||
}
|
||||
|
||||
void DrmLeaseOutput::pageFlipped(std::chrono::nanoseconds timestamp) const
|
||||
{
|
||||
Q_UNUSED(timestamp)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
KWaylandServer::DrmLeaseV1Interface *lease() const;
|
||||
DrmPipeline *pipeline() const;
|
||||
|
||||
bool present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion) override;
|
||||
bool present() override;
|
||||
DrmPlane::Transformations softwareTransforms() const override;
|
||||
QSize bufferSize() const override;
|
||||
QSize sourceSize() const override;
|
||||
|
@ -48,6 +48,10 @@ public:
|
|||
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const override;
|
||||
int maxBpc() const override;
|
||||
QRect renderGeometry() const override;
|
||||
DrmLayer *outputLayer() const override;
|
||||
bool testScanout() override;
|
||||
void frameFailed() const override;
|
||||
void pageFlipped(std::chrono::nanoseconds timestamp) const override;
|
||||
|
||||
private:
|
||||
DrmPipeline *m_pipeline;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "waylandoutputconfig.h"
|
||||
#include "dumb_swapchain.h"
|
||||
#include "cursor.h"
|
||||
#include "drm_layer.h"
|
||||
// Qt
|
||||
#include <QMatrix4x4>
|
||||
#include <QCryptographicHash>
|
||||
|
@ -45,7 +46,7 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline)
|
|||
, m_pipeline(pipeline)
|
||||
, m_connector(pipeline->connector())
|
||||
{
|
||||
m_pipeline->setOutput(this);
|
||||
m_pipeline->setDisplayDevice(this);
|
||||
const auto conn = m_pipeline->connector();
|
||||
m_renderLoop->setRefreshRate(m_pipeline->pending.mode->refreshRate());
|
||||
setSubPixelInternal(conn->subpixel());
|
||||
|
@ -78,7 +79,7 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline)
|
|||
|
||||
DrmOutput::~DrmOutput()
|
||||
{
|
||||
m_pipeline->setOutput(nullptr);
|
||||
m_pipeline->setDisplayDevice(nullptr);
|
||||
}
|
||||
|
||||
static bool isCursorSpriteCompatible(const QImage *buffer, const QImage *sprite)
|
||||
|
@ -318,12 +319,8 @@ void DrmOutput::updateModes()
|
|||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion)
|
||||
bool DrmOutput::present()
|
||||
{
|
||||
if (!buffer || buffer->bufferId() == 0) {
|
||||
presentFailed();
|
||||
return false;
|
||||
}
|
||||
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop);
|
||||
if (m_pipeline->pending.syncMode != renderLoopPrivate->presentMode) {
|
||||
m_pipeline->pending.syncMode = renderLoopPrivate->presentMode;
|
||||
|
@ -334,14 +331,21 @@ bool DrmOutput::present(const QSharedPointer<DrmBuffer> &buffer, QRegion damaged
|
|||
setVrrPolicy(RenderLoop::VrrPolicy::Never);
|
||||
}
|
||||
}
|
||||
if (m_pipeline->present(buffer)) {
|
||||
Q_EMIT outputChange(damagedRegion);
|
||||
if (m_pipeline->present()) {
|
||||
Q_EMIT outputChange(m_pipeline->pending.layer->currentDamage());
|
||||
return true;
|
||||
} else {
|
||||
qCWarning(KWIN_DRM) << "Presentation failed!" << strerror(errno);
|
||||
frameFailed();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::testScanout()
|
||||
{
|
||||
return m_pipeline->testScanout();
|
||||
}
|
||||
|
||||
int DrmOutput::gammaRampSize() const
|
||||
{
|
||||
return m_pipeline->pending.crtc ? m_pipeline->pending.crtc->gammaRampSize() : 256;
|
||||
|
@ -446,16 +450,6 @@ void DrmOutput::revertQueuedChanges()
|
|||
m_pipeline->revertPendingChanges();
|
||||
}
|
||||
|
||||
void DrmOutput::pageFlipped(std::chrono::nanoseconds timestamp)
|
||||
{
|
||||
RenderLoopPrivate::get(m_renderLoop)->notifyFrameCompleted(timestamp);
|
||||
}
|
||||
|
||||
void DrmOutput::presentFailed()
|
||||
{
|
||||
RenderLoopPrivate::get(m_renderLoop)->notifyFrameFailed();
|
||||
}
|
||||
|
||||
int DrmOutput::maxBpc() const
|
||||
{
|
||||
auto prop = m_connector->getProp(DrmConnector::PropertyIndex::MaxBpc);
|
||||
|
@ -477,4 +471,9 @@ DrmPlane::Transformations DrmOutput::softwareTransforms() const
|
|||
}
|
||||
}
|
||||
|
||||
DrmLayer *DrmOutput::outputLayer() const
|
||||
{
|
||||
return m_pipeline->pending.layer.data();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,25 +43,24 @@ public:
|
|||
DrmOutput(DrmPipeline *pipeline);
|
||||
~DrmOutput() override;
|
||||
|
||||
bool present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion) override;
|
||||
|
||||
DrmConnector *connector() const;
|
||||
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;
|
||||
|
||||
bool queueChanges(const WaylandOutputConfig &config);
|
||||
void applyQueuedChanges(const WaylandOutputConfig &config);
|
||||
void revertQueuedChanges();
|
||||
void updateModes();
|
||||
|
||||
void pageFlipped(std::chrono::nanoseconds timestamp);
|
||||
void presentFailed();
|
||||
bool usesSoftwareCursor() const override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "drm_backend.h"
|
||||
#include "egl_gbm_backend.h"
|
||||
#include "drm_buffer_gbm.h"
|
||||
#include "drm_layer.h"
|
||||
|
||||
#include <gbm.h>
|
||||
#include <drm_fourcc.h>
|
||||
|
@ -31,30 +32,37 @@ namespace KWin
|
|||
{
|
||||
|
||||
DrmPipeline::DrmPipeline(DrmConnector *conn)
|
||||
: m_output(nullptr)
|
||||
: m_displayDevice(nullptr)
|
||||
, m_connector(conn)
|
||||
{
|
||||
}
|
||||
|
||||
DrmPipeline::~DrmPipeline()
|
||||
{
|
||||
m_output = nullptr;
|
||||
if (m_pageflipPending && m_current.crtc) {
|
||||
pageFlipped({});
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
||||
bool DrmPipeline::testScanout()
|
||||
{
|
||||
// TODO make the modeset check only be tested at most once per scanout cycle
|
||||
if (gpu()->needsModeset()) {
|
||||
return false;
|
||||
}
|
||||
if (gpu()->atomicModeSetting()) {
|
||||
return commitPipelines({this}, CommitMode::Test);
|
||||
} else {
|
||||
// no other way to test than to do it.
|
||||
// As we only have a maximum of one test per scanout cycle, this is fine
|
||||
return presentLegacy();
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmPipeline::present()
|
||||
{
|
||||
Q_ASSERT(pending.crtc);
|
||||
Q_ASSERT(buffer);
|
||||
m_primaryBuffer = buffer;
|
||||
// with direct scanout disallow modesets, calling presentFailed() and logging warnings
|
||||
const bool directScanout = isBufferForDirectScanout();
|
||||
if (gpu()->needsModeset()) {
|
||||
if (directScanout) {
|
||||
return false;
|
||||
}
|
||||
m_modesetPresentPending = true;
|
||||
return gpu()->maybeModeset();
|
||||
}
|
||||
|
@ -72,35 +80,22 @@ bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
|||
}
|
||||
}
|
||||
if (!commitPipelines({this}, CommitMode::Commit)) {
|
||||
if (directScanout) {
|
||||
return false;
|
||||
}
|
||||
qCWarning(KWIN_DRM) << "Atomic present failed!" << strerror(errno);
|
||||
printDebugInfo();
|
||||
if (m_output) {
|
||||
m_output->presentFailed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pending.layer->hasDirectScanoutBuffer()) {
|
||||
// already presented
|
||||
return true;
|
||||
}
|
||||
if (!presentLegacy()) {
|
||||
qCWarning(KWIN_DRM) << "Present failed!" << strerror(errno);
|
||||
if (m_output) {
|
||||
m_output->presentFailed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmPipeline::isBufferForDirectScanout() const
|
||||
{
|
||||
const auto buf = dynamic_cast<DrmGbmBuffer*>(m_primaryBuffer.data());
|
||||
return buf && buf->clientBuffer();
|
||||
}
|
||||
|
||||
bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode, const QVector<DrmObject*> &unusedObjects)
|
||||
{
|
||||
Q_ASSERT(!pipelines.isEmpty());
|
||||
|
@ -133,7 +128,7 @@ bool DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline*> &pipelines,
|
|||
return false;
|
||||
};
|
||||
for (const auto &pipeline : pipelines) {
|
||||
if (!pipeline->checkTestBuffer()) {
|
||||
if (!pipeline->pending.layer->testBuffer()) {
|
||||
qCWarning(KWIN_DRM) << "Checking test buffer failed for" << mode;
|
||||
return failed();
|
||||
}
|
||||
|
@ -197,8 +192,9 @@ bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
|
|||
pending.crtc->setPending(DrmCrtc::PropertyIndex::VrrEnabled, pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive);
|
||||
pending.crtc->setPending(DrmCrtc::PropertyIndex::Gamma_LUT, pending.gamma ? pending.gamma->blobId() : 0);
|
||||
const auto modeSize = pending.mode->size();
|
||||
pending.crtc->primaryPlane()->set(QPoint(0, 0), m_primaryBuffer ? m_primaryBuffer->size() : bufferSize(), QPoint(0, 0), modeSize);
|
||||
pending.crtc->primaryPlane()->setBuffer(activePending() ? m_primaryBuffer.get() : nullptr);
|
||||
const auto buffer = pending.layer->currentBuffer().data();
|
||||
pending.crtc->primaryPlane()->set(QPoint(0, 0), buffer ? buffer->size() : bufferSize(), QPoint(0, 0), modeSize);
|
||||
pending.crtc->primaryPlane()->setBuffer(activePending() ? buffer : nullptr);
|
||||
|
||||
if (pending.crtc->cursorPlane()) {
|
||||
pending.crtc->cursorPlane()->set(QPoint(0, 0), gpu()->cursorSize(), pending.cursorPos, gpu()->cursorSize());
|
||||
|
@ -271,10 +267,6 @@ uint32_t DrmPipeline::calculateUnderscan()
|
|||
|
||||
void DrmPipeline::atomicCommitFailed()
|
||||
{
|
||||
if (m_oldTestBuffer) {
|
||||
m_primaryBuffer = m_oldTestBuffer;
|
||||
m_oldTestBuffer = nullptr;
|
||||
}
|
||||
m_connector->rollbackPending();
|
||||
if (pending.crtc) {
|
||||
pending.crtc->rollbackPending();
|
||||
|
@ -287,7 +279,6 @@ void DrmPipeline::atomicCommitFailed()
|
|||
|
||||
void DrmPipeline::atomicCommitSuccessful(CommitMode mode)
|
||||
{
|
||||
m_oldTestBuffer = nullptr;
|
||||
m_connector->commitPending();
|
||||
if (pending.crtc) {
|
||||
pending.crtc->commitPending();
|
||||
|
@ -303,7 +294,7 @@ void DrmPipeline::atomicCommitSuccessful(CommitMode mode)
|
|||
m_connector->commit();
|
||||
if (pending.crtc) {
|
||||
pending.crtc->commit();
|
||||
pending.crtc->primaryPlane()->setNext(m_primaryBuffer);
|
||||
pending.crtc->primaryPlane()->setNext(pending.layer->currentBuffer());
|
||||
pending.crtc->primaryPlane()->commit();
|
||||
if (pending.crtc->cursorPlane()) {
|
||||
pending.crtc->cursorPlane()->setNext(pending.cursorBo);
|
||||
|
@ -317,32 +308,6 @@ void DrmPipeline::atomicCommitSuccessful(CommitMode mode)
|
|||
}
|
||||
}
|
||||
|
||||
bool DrmPipeline::checkTestBuffer()
|
||||
{
|
||||
const auto backend = gpu()->eglBackend();
|
||||
if (!pending.crtc || (!(backend && m_output) && m_primaryBuffer && m_primaryBuffer->size() == bufferSize()) || isBufferForDirectScanout()) {
|
||||
return true;
|
||||
}
|
||||
QSharedPointer<DrmBuffer> buffer;
|
||||
if (backend && m_output) {
|
||||
buffer = backend->testBuffer(m_output);
|
||||
} else if (backend && gpu()->gbmDevice()) {
|
||||
gbm_bo *bo = gbm_bo_create(gpu()->gbmDevice(), bufferSize().width(), bufferSize().height(), DRM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
|
||||
if (!bo) {
|
||||
return false;
|
||||
}
|
||||
buffer = QSharedPointer<DrmGbmBuffer>::create(gpu(), bo, nullptr);
|
||||
} else {
|
||||
buffer = QSharedPointer<DrmDumbBuffer>::create(gpu(), bufferSize(), DRM_FORMAT_XRGB8888);
|
||||
}
|
||||
if (buffer && buffer->bufferId()) {
|
||||
m_oldTestBuffer = m_primaryBuffer;
|
||||
m_primaryBuffer = buffer;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrmPipeline::setCursor(const QSharedPointer<DrmDumbBuffer> &buffer, const QPoint &hotspot)
|
||||
{
|
||||
if (pending.cursorBo == buffer && pending.cursorHotspot == hotspot) {
|
||||
|
@ -360,8 +325,8 @@ bool DrmPipeline::setCursor(const QSharedPointer<DrmDumbBuffer> &buffer, const Q
|
|||
}
|
||||
if (result) {
|
||||
m_next = pending;
|
||||
if (m_output && (visibleBefore || isCursorVisible())) {
|
||||
m_output->renderLoop()->scheduleRepaint();
|
||||
if (const auto drmOutput = dynamic_cast<DrmOutput*>(m_displayDevice); drmOutput && (visibleBefore || isCursorVisible())) {
|
||||
drmOutput->renderLoop()->scheduleRepaint();
|
||||
}
|
||||
} else {
|
||||
pending = m_next;
|
||||
|
@ -385,8 +350,8 @@ bool DrmPipeline::moveCursor(QPoint pos)
|
|||
}
|
||||
if (result) {
|
||||
m_next = pending;
|
||||
if (m_output && (visibleBefore || isCursorVisible())) {
|
||||
m_output->renderLoop()->scheduleRepaint();
|
||||
if (const auto drmOutput = dynamic_cast<DrmOutput*>(m_displayDevice); drmOutput && (visibleBefore || isCursorVisible())) {
|
||||
drmOutput->renderLoop()->scheduleRepaint();
|
||||
}
|
||||
} else {
|
||||
pending = m_next;
|
||||
|
@ -446,19 +411,12 @@ void DrmPipeline::pageFlipped(std::chrono::nanoseconds timestamp)
|
|||
m_current.crtc->cursorPlane()->flipBuffer();
|
||||
}
|
||||
m_pageflipPending = false;
|
||||
if (m_output) {
|
||||
m_output->pageFlipped(timestamp);
|
||||
}
|
||||
m_displayDevice->pageFlipped(timestamp);
|
||||
}
|
||||
|
||||
void DrmPipeline::setOutput(DrmOutput *output)
|
||||
void DrmPipeline::setDisplayDevice(DrmDisplayDevice *device)
|
||||
{
|
||||
m_output = output;
|
||||
}
|
||||
|
||||
DrmOutput *DrmPipeline::output() const
|
||||
{
|
||||
return m_output;
|
||||
m_displayDevice = device;
|
||||
}
|
||||
|
||||
static const QMap<uint32_t, QVector<uint64_t>> legacyFormats = {
|
||||
|
@ -541,6 +499,11 @@ DrmCrtc *DrmPipeline::currentCrtc() const
|
|||
return m_current.crtc;
|
||||
}
|
||||
|
||||
DrmDisplayDevice *DrmPipeline::displayDevice() const
|
||||
{
|
||||
return m_displayDevice;
|
||||
}
|
||||
|
||||
DrmGammaRamp::DrmGammaRamp(DrmGpu *gpu, const GammaRamp &lut)
|
||||
: m_gpu(gpu)
|
||||
, m_lut(lut)
|
||||
|
|
|
@ -31,6 +31,8 @@ class DrmBuffer;
|
|||
class DrmDumbBuffer;
|
||||
class GammaRamp;
|
||||
class DrmConnectorMode;
|
||||
class DrmLayer;
|
||||
class DrmDisplayDevice;
|
||||
|
||||
class DrmGammaRamp
|
||||
{
|
||||
|
@ -60,7 +62,8 @@ public:
|
|||
* tests the pending commit first and commits it if the test passes
|
||||
* if the test fails, there is a guarantee for no lasting changes
|
||||
*/
|
||||
bool present(const QSharedPointer<DrmBuffer> &buffer);
|
||||
bool present();
|
||||
bool testScanout();
|
||||
|
||||
bool needsModeset() const;
|
||||
void applyPendingChanges();
|
||||
|
@ -91,8 +94,8 @@ public:
|
|||
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const;
|
||||
QMap<uint32_t, QVector<uint64_t>> supportedFormats() const;
|
||||
|
||||
void setOutput(DrmOutput *output);
|
||||
DrmOutput *output() const;
|
||||
void setDisplayDevice(DrmDisplayDevice *device);
|
||||
DrmDisplayDevice *displayDevice() const;
|
||||
|
||||
struct State {
|
||||
DrmCrtc *crtc = nullptr;
|
||||
|
@ -104,6 +107,8 @@ public:
|
|||
RenderLoopPrivate::SyncMode syncMode = RenderLoopPrivate::SyncMode::Fixed;
|
||||
QSharedPointer<DrmGammaRamp> gamma;
|
||||
|
||||
QSharedPointer<DrmLayer> layer;
|
||||
|
||||
QPoint cursorPos;
|
||||
QPoint cursorHotspot;
|
||||
QSharedPointer<DrmDumbBuffer> cursorBo;
|
||||
|
@ -124,7 +129,6 @@ public:
|
|||
static bool commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode, const QVector<DrmObject*> &unusedObjects = {});
|
||||
|
||||
private:
|
||||
bool checkTestBuffer();
|
||||
bool activePending() const;
|
||||
bool isCursorVisible() const;
|
||||
bool isBufferForDirectScanout() const;
|
||||
|
@ -150,11 +154,9 @@ private:
|
|||
static void printFlags(uint32_t flags);
|
||||
static void printProps(DrmObject *object, PrintMode mode);
|
||||
|
||||
DrmOutput *m_output = nullptr;
|
||||
DrmDisplayDevice *m_displayDevice = nullptr;
|
||||
DrmConnector *m_connector = nullptr;
|
||||
|
||||
QSharedPointer<DrmBuffer> m_primaryBuffer;
|
||||
QSharedPointer<DrmBuffer> m_oldTestBuffer;
|
||||
bool m_pageflipPending = false;
|
||||
bool m_modesetPresentPending = false;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "drm_object_crtc.h"
|
||||
#include "drm_object_connector.h"
|
||||
#include "logging.h"
|
||||
#include "drm_layer.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
|
@ -21,33 +22,32 @@ namespace KWin
|
|||
|
||||
bool DrmPipeline::presentLegacy()
|
||||
{
|
||||
if ((!pending.crtc->current() || pending.crtc->current()->needsModeChange(m_primaryBuffer.get())) && !legacyModeset()) {
|
||||
const auto buffer = pending.layer->currentBuffer();
|
||||
if ((!pending.crtc->current() || pending.crtc->current()->needsModeChange(buffer.get())) && !legacyModeset()) {
|
||||
return false;
|
||||
}
|
||||
if (drmModePageFlip(gpu()->fd(), pending.crtc->id(), m_primaryBuffer ? m_primaryBuffer->bufferId() : 0, DRM_MODE_PAGE_FLIP_EVENT, nullptr) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno) << m_primaryBuffer;
|
||||
if (drmModePageFlip(gpu()->fd(), pending.crtc->id(), buffer ? buffer->bufferId() : 0, DRM_MODE_PAGE_FLIP_EVENT, nullptr) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno) << buffer;
|
||||
return false;
|
||||
}
|
||||
m_pageflipPending = true;
|
||||
pending.crtc->setNext(m_primaryBuffer);
|
||||
pending.crtc->setNext(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmPipeline::legacyModeset()
|
||||
{
|
||||
uint32_t connId = m_connector->id();
|
||||
if (!checkTestBuffer() || drmModeSetCrtc(gpu()->fd(), pending.crtc->id(), m_primaryBuffer->bufferId(), 0, 0, &connId, 1, pending.mode->nativeMode()) != 0) {
|
||||
if (!pending.layer->testBuffer() || drmModeSetCrtc(gpu()->fd(), pending.crtc->id(), pending.layer->currentBuffer()->bufferId(), 0, 0, &connId, 1, pending.mode->nativeMode()) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno);
|
||||
pending = m_next;
|
||||
m_primaryBuffer = m_oldTestBuffer;
|
||||
return false;
|
||||
}
|
||||
m_oldTestBuffer = nullptr;
|
||||
// make sure the buffer gets kept alive, or the modeset gets reverted by the kernel
|
||||
if (pending.crtc->current()) {
|
||||
pending.crtc->setNext(m_primaryBuffer);
|
||||
pending.crtc->setNext(pending.layer->currentBuffer());
|
||||
} else {
|
||||
pending.crtc->setCurrent(m_primaryBuffer);
|
||||
pending.crtc->setCurrent(pending.layer->currentBuffer());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ std::optional<QRegion> DrmQPainterLayer::startRendering()
|
|||
|
||||
bool DrmQPainterLayer::endRendering(const QRegion &damagedRegion)
|
||||
{
|
||||
m_currentDamage = damagedRegion;
|
||||
m_swapchain->releaseBuffer(m_swapchain->currentBuffer(), damagedRegion);
|
||||
return true;
|
||||
}
|
||||
|
@ -63,9 +64,19 @@ QSharedPointer<DrmBuffer> DrmQPainterLayer::currentBuffer() const
|
|||
return m_swapchain ? m_swapchain->currentBuffer() : nullptr;
|
||||
}
|
||||
|
||||
QRegion DrmQPainterLayer::currentDamage() const
|
||||
{
|
||||
return m_currentDamage;
|
||||
}
|
||||
|
||||
DrmDisplayDevice *DrmQPainterLayer::displayDevice() const
|
||||
{
|
||||
return m_displayDevice;
|
||||
}
|
||||
|
||||
bool DrmQPainterLayer::hasDirectScanoutBuffer() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,12 +23,15 @@ public:
|
|||
bool scanout(SurfaceItem *surfaceItem) override;
|
||||
QSharedPointer<DrmBuffer> testBuffer() override;
|
||||
QSharedPointer<DrmBuffer> currentBuffer() const override;
|
||||
QRegion currentDamage() const override;
|
||||
bool hasDirectScanoutBuffer() const override;
|
||||
DrmDisplayDevice *displayDevice() const override;
|
||||
|
||||
private:
|
||||
bool doesSwapchainFit() const;
|
||||
|
||||
QSharedPointer<DumbSwapchain> m_swapchain;
|
||||
QRegion m_currentDamage;
|
||||
DrmDisplayDevice *const m_displayDevice;
|
||||
};
|
||||
|
||||
|
|
28
src/backends/drm/drm_render_backend.h
Normal file
28
src/backends/drm/drm_render_backend.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
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 <QSharedPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class DrmLayer;
|
||||
class DrmDisplayDevice;
|
||||
|
||||
class DrmRenderBackend
|
||||
{
|
||||
public:
|
||||
virtual ~DrmRenderBackend() = default;
|
||||
|
||||
virtual QSharedPointer<DrmLayer> createLayer(DrmDisplayDevice *displayDevice) const = 0;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -14,9 +14,12 @@
|
|||
#include "drm_gpu.h"
|
||||
#include "drm_backend.h"
|
||||
#include "logging.h"
|
||||
#include "drm_layer.h"
|
||||
#include "drm_render_backend.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static int s_serial = 0;
|
||||
DrmVirtualOutput::DrmVirtualOutput(DrmGpu *gpu, const QSize &size)
|
||||
: DrmVirtualOutput(QString::number(s_serial++), gpu, size)
|
||||
|
@ -40,20 +43,19 @@ DrmVirtualOutput::DrmVirtualOutput(const QString &name, DrmGpu *gpu, const QSize
|
|||
modes,
|
||||
QByteArray("EDID_") + name.toUtf8());
|
||||
m_renderLoop->setRefreshRate(modes[m_modeIndex].refreshRate);
|
||||
|
||||
m_layer = gpu->renderBackend()->createLayer(this);
|
||||
}
|
||||
|
||||
DrmVirtualOutput::~DrmVirtualOutput()
|
||||
{
|
||||
}
|
||||
|
||||
bool DrmVirtualOutput::present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion)
|
||||
bool DrmVirtualOutput::present()
|
||||
{
|
||||
Q_UNUSED(damagedRegion)
|
||||
|
||||
m_currentBuffer = buffer;
|
||||
m_vsyncMonitor->arm();
|
||||
m_pageFlipPending = true;
|
||||
Q_EMIT outputChange(damagedRegion);
|
||||
Q_EMIT outputChange(m_layer->currentDamage());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -119,4 +121,14 @@ DrmPlane::Transformations DrmVirtualOutput::softwareTransforms() const
|
|||
return DrmPlane::Transformation::Rotate0;
|
||||
}
|
||||
|
||||
DrmLayer *DrmVirtualOutput::outputLayer() const
|
||||
{
|
||||
return m_layer.data();
|
||||
}
|
||||
|
||||
bool DrmVirtualOutput::testScanout()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace KWin
|
|||
|
||||
class SoftwareVsyncMonitor;
|
||||
class VirtualBackend;
|
||||
class DrmLayer;
|
||||
|
||||
class DrmVirtualOutput : public DrmAbstractOutput
|
||||
{
|
||||
|
@ -28,7 +29,7 @@ public:
|
|||
DrmVirtualOutput(DrmGpu *gpu, const QSize &size);
|
||||
~DrmVirtualOutput() override;
|
||||
|
||||
bool present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion) override;
|
||||
bool present() override;
|
||||
QSize bufferSize() const override;
|
||||
QSize sourceSize() const override;
|
||||
|
||||
|
@ -39,13 +40,15 @@ public:
|
|||
int gammaRampSize() const override;
|
||||
bool setGammaRamp(const GammaRamp &gamma) override;
|
||||
DrmPlane::Transformations softwareTransforms() const override;
|
||||
DrmLayer *outputLayer() const override;
|
||||
bool testScanout() override;
|
||||
|
||||
private:
|
||||
void vblank(std::chrono::nanoseconds timestamp);
|
||||
void setDpmsMode(DpmsMode mode) override;
|
||||
void updateEnablement(bool enable) override;
|
||||
|
||||
QSharedPointer<DrmBuffer> m_currentBuffer;
|
||||
QSharedPointer<DrmLayer> m_layer;
|
||||
bool m_pageFlipPending = true;
|
||||
int m_modeIndex = 0;
|
||||
|
||||
|
|
|
@ -49,20 +49,14 @@ EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend)
|
|||
: AbstractEglBackend(drmBackend->primaryGpu()->deviceId())
|
||||
, m_backend(drmBackend)
|
||||
{
|
||||
drmBackend->primaryGpu()->setEglBackend(this);
|
||||
connect(m_backend, &DrmBackend::outputAdded, this, &EglGbmBackend::addOutput);
|
||||
connect(m_backend, &DrmBackend::outputRemoved, this, &EglGbmBackend::removeOutput);
|
||||
drmBackend->primaryGpu()->setRenderBackend(this);
|
||||
setIsDirectRendering(true);
|
||||
}
|
||||
|
||||
EglGbmBackend::~EglGbmBackend()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void EglGbmBackend::cleanupSurfaces()
|
||||
{
|
||||
m_surfaces.clear();
|
||||
m_backend->primaryGpu()->setRenderBackend(nullptr);
|
||||
}
|
||||
|
||||
bool EglGbmBackend::initializeEgl()
|
||||
|
@ -124,23 +118,9 @@ bool EglGbmBackend::initRenderingContext()
|
|||
if (!createContext() || !makeCurrent()) {
|
||||
return false;
|
||||
}
|
||||
const auto outputs = m_backend->outputs();
|
||||
for (const auto &output : outputs) {
|
||||
addOutput(output);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EglGbmBackend::addOutput(AbstractOutput *output) {
|
||||
auto drmOutput = static_cast<DrmAbstractOutput*>(output);
|
||||
m_surfaces.insert(drmOutput, QSharedPointer<EglGbmLayer>::create(m_backend->primaryGpu(), drmOutput));
|
||||
}
|
||||
|
||||
void EglGbmBackend::removeOutput(AbstractOutput *output)
|
||||
{
|
||||
m_surfaces.remove(output);
|
||||
}
|
||||
|
||||
bool EglGbmBackend::initBufferConfigs()
|
||||
{
|
||||
const EGLint config_attribs[] = {
|
||||
|
@ -250,9 +230,8 @@ static QVector<EGLint> regionToRects(const QRegion ®ion, DrmAbstractOutput *o
|
|||
|
||||
void EglGbmBackend::aboutToStartPainting(AbstractOutput *output, const QRegion &damagedRegion)
|
||||
{
|
||||
Q_ASSERT_X(output, "aboutToStartPainting", "not using per screen rendering");
|
||||
Q_ASSERT(m_surfaces.contains(output));
|
||||
const auto &surface = m_surfaces[output];
|
||||
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);
|
||||
|
@ -274,34 +253,34 @@ SurfaceTexture *EglGbmBackend::createSurfaceTextureWayland(SurfacePixmapWayland
|
|||
|
||||
QRegion EglGbmBackend::beginFrame(AbstractOutput *output)
|
||||
{
|
||||
Q_ASSERT(m_surfaces.contains(output));
|
||||
return m_surfaces[output]->startRendering().value_or(QRegion());
|
||||
return static_cast<DrmAbstractOutput*>(output)->outputLayer()->startRendering().value_or(QRegion());
|
||||
}
|
||||
|
||||
void EglGbmBackend::endFrame(AbstractOutput *output, const QRegion &renderedRegion,
|
||||
const QRegion &damagedRegion)
|
||||
{
|
||||
Q_ASSERT(m_surfaces.contains(output));
|
||||
Q_UNUSED(renderedRegion)
|
||||
|
||||
m_surfaces[output]->endRendering(damagedRegion);
|
||||
static_cast<DrmAbstractOutput*>(output)->present(m_surfaces[output]->currentBuffer(), damagedRegion);
|
||||
const auto drmOutput = static_cast<DrmAbstractOutput*>(output);
|
||||
drmOutput->outputLayer()->endRendering(damagedRegion);
|
||||
drmOutput->present();
|
||||
}
|
||||
|
||||
bool EglGbmBackend::scanout(AbstractOutput *output, SurfaceItem *surfaceItem)
|
||||
{
|
||||
return m_surfaces[output]->scanout(surfaceItem);
|
||||
}
|
||||
|
||||
QSharedPointer<DrmBuffer> EglGbmBackend::testBuffer(DrmAbstractOutput *output)
|
||||
{
|
||||
return m_surfaces[output]->testBuffer();
|
||||
const auto drmOutput = static_cast<DrmAbstractOutput*>(output);
|
||||
if (drmOutput->outputLayer()->scanout(surfaceItem)) {
|
||||
drmOutput->present();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *output) const
|
||||
{
|
||||
Q_ASSERT(m_surfaces.contains(output));
|
||||
return m_surfaces[output]->texture();
|
||||
const auto drmOutput = static_cast<DrmAbstractOutput*>(output);
|
||||
return static_cast<EglGbmLayer*>(drmOutput->outputLayer())->texture();
|
||||
}
|
||||
|
||||
GbmFormat EglGbmBackend::gbmFormatForDrmFormat(uint32_t format) const
|
||||
|
@ -352,6 +331,11 @@ EGLConfig EglGbmBackend::config(uint32_t format) const
|
|||
return m_configs[format];
|
||||
}
|
||||
|
||||
QSharedPointer<DrmLayer> EglGbmBackend::createLayer(DrmDisplayDevice *displayDevice) const
|
||||
{
|
||||
return QSharedPointer<EglGbmLayer>::create(m_backend->primaryGpu(), displayDevice);
|
||||
}
|
||||
|
||||
bool operator==(const GbmFormat &lhs, const GbmFormat &rhs)
|
||||
{
|
||||
return lhs.drmFormat == rhs.drmFormat;
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KWIN_EGL_GBM_BACKEND_H
|
||||
#define KWIN_EGL_GBM_BACKEND_H
|
||||
#pragma once
|
||||
#include "abstract_egl_backend.h"
|
||||
#include "drm_render_backend.h"
|
||||
|
||||
#include <kwinglutils.h>
|
||||
|
||||
|
@ -52,7 +52,7 @@ bool operator==(const GbmFormat &lhs, const GbmFormat &rhs);
|
|||
/**
|
||||
* @brief OpenGL Backend using Egl on a GBM surface.
|
||||
*/
|
||||
class EglGbmBackend : public AbstractEglBackend
|
||||
class EglGbmBackend : public AbstractEglBackend, public DrmRenderBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -67,6 +67,7 @@ public:
|
|||
void init() override;
|
||||
bool scanout(AbstractOutput *output, SurfaceItem *surfaceItem) override;
|
||||
bool prefer10bpc() const override;
|
||||
QSharedPointer<DrmLayer> createLayer(DrmDisplayDevice *displayDevice) const override;
|
||||
|
||||
QSharedPointer<GLTexture> textureForOutput(AbstractOutput *requestedOutput) const override;
|
||||
|
||||
|
@ -76,17 +77,13 @@ public:
|
|||
std::optional<uint32_t> chooseFormat(DrmDisplayDevice *displyDevice) const;
|
||||
|
||||
protected:
|
||||
void cleanupSurfaces() override;
|
||||
void aboutToStartPainting(AbstractOutput *output, const QRegion &damage) override;
|
||||
|
||||
private:
|
||||
bool initializeEgl();
|
||||
bool initBufferConfigs();
|
||||
bool initRenderingContext();
|
||||
void addOutput(AbstractOutput *output);
|
||||
void removeOutput(AbstractOutput *output);
|
||||
|
||||
QMap<AbstractOutput *, QSharedPointer<EglGbmLayer>> m_surfaces;
|
||||
DrmBackend *m_backend;
|
||||
QVector<GbmFormat> m_formats;
|
||||
QMap<uint32_t, EGLConfig> m_configs;
|
||||
|
@ -95,5 +92,3 @@ private:
|
|||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
|
|
@ -50,6 +50,7 @@ EglGbmLayer::~EglGbmLayer()
|
|||
|
||||
std::optional<QRegion> EglGbmLayer::startRendering()
|
||||
{
|
||||
m_scanoutBuffer.reset();
|
||||
// dmabuf feedback
|
||||
if (!m_scanoutCandidate.attemptedThisFrame && m_scanoutCandidate.surface) {
|
||||
if (const auto feedback = m_scanoutCandidate.surface->dmabufFeedbackV1()) {
|
||||
|
@ -117,13 +118,27 @@ bool EglGbmLayer::endRendering(const QRegion &damagedRegion)
|
|||
const auto buffer = m_gbmSurface->swapBuffersForDrm(damagedRegion);
|
||||
if (buffer) {
|
||||
m_currentBuffer = buffer;
|
||||
m_currentDamage = damagedRegion;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
QRegion EglGbmLayer::currentDamage() const
|
||||
{
|
||||
return m_currentDamage;
|
||||
}
|
||||
|
||||
QSharedPointer<DrmBuffer> EglGbmLayer::testBuffer()
|
||||
{
|
||||
if (!m_currentBuffer || !doesGbmSurfaceFit(m_gbmSurface.data())) {
|
||||
if (doesGbmSurfaceFit(m_oldGbmSurface.data())) {
|
||||
// re-use old surface and buffer without rendering
|
||||
m_gbmSurface = m_oldGbmSurface;
|
||||
if (m_gbmSurface->currentBuffer()) {
|
||||
m_currentBuffer = m_gbmSurface->currentDrmBuffer();
|
||||
return m_currentBuffer;
|
||||
}
|
||||
}
|
||||
if (!renderTestBuffer() && m_importMode == MultiGpuImportMode::DumbBufferXrgb8888) {
|
||||
// try multi-gpu import again, this time with DRM_FORMAT_XRGB8888
|
||||
renderTestBuffer();
|
||||
|
@ -401,10 +416,11 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
const auto bo = QSharedPointer<DrmGbmBuffer>::create(m_displayDevice->gpu(), importedBuffer, buffer);
|
||||
if (!bo->bufferId()) {
|
||||
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;
|
||||
}
|
||||
|
@ -421,10 +437,12 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem)
|
|||
} else {
|
||||
damage = m_displayDevice->renderGeometry();
|
||||
}
|
||||
if (m_displayDevice->present(bo, damage)) {
|
||||
m_currentBuffer = bo;
|
||||
if (m_displayDevice->testScanout()) {
|
||||
m_currentBuffer = m_scanoutBuffer;
|
||||
m_currentDamage = damage;
|
||||
return true;
|
||||
} else {
|
||||
m_scanoutBuffer.reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -462,7 +480,7 @@ void EglGbmLayer::sendDmabufFeedback(KWaylandServer::LinuxDmaBufV1ClientBuffer *
|
|||
|
||||
QSharedPointer<DrmBuffer> EglGbmLayer::currentBuffer() const
|
||||
{
|
||||
return m_currentBuffer;
|
||||
return m_scanoutBuffer ? m_scanoutBuffer : m_currentBuffer;
|
||||
}
|
||||
|
||||
DrmDisplayDevice *EglGbmLayer::displayDevice() const
|
||||
|
@ -480,4 +498,9 @@ EGLSurface EglGbmLayer::eglSurface() const
|
|||
return m_gbmSurface ? m_gbmSurface->eglSurface() : EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
bool EglGbmLayer::hasDirectScanoutBuffer() const
|
||||
{
|
||||
return m_scanoutBuffer != nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ public:
|
|||
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;
|
||||
|
||||
DrmDisplayDevice *displayDevice() const override;
|
||||
|
@ -76,7 +78,9 @@ private:
|
|||
bool attemptedThisFrame = false;
|
||||
} m_scanoutCandidate;
|
||||
|
||||
QSharedPointer<DrmBuffer> m_scanoutBuffer;
|
||||
QSharedPointer<DrmBuffer> m_currentBuffer;
|
||||
QRegion m_currentDamage;
|
||||
QSharedPointer<GbmSurface> m_gbmSurface;
|
||||
QSharedPointer<GbmSurface> m_oldGbmSurface;
|
||||
QSharedPointer<ShadowBuffer> m_shadowBuffer;
|
||||
|
|
|
@ -23,29 +23,37 @@ DrmQPainterBackend::DrmQPainterBackend(DrmBackend *backend)
|
|||
: QPainterBackend()
|
||||
, m_backend(backend)
|
||||
{
|
||||
connect(m_backend, &DrmBackend::outputEnabled, this, [this] (const auto output) {
|
||||
m_swapchains[output] = QSharedPointer<DrmQPainterLayer>::create(static_cast<DrmAbstractOutput*>(output));
|
||||
});
|
||||
connect(m_backend, &DrmBackend::outputDisabled, this, [this] (const auto output) {
|
||||
m_swapchains.remove(output);
|
||||
});
|
||||
m_backend->primaryGpu()->setRenderBackend(this);
|
||||
}
|
||||
|
||||
DrmQPainterBackend::~DrmQPainterBackend()
|
||||
{
|
||||
m_backend->primaryGpu()->setRenderBackend(nullptr);
|
||||
}
|
||||
|
||||
QImage *DrmQPainterBackend::bufferForScreen(AbstractOutput *output)
|
||||
{
|
||||
return static_cast<DrmDumbBuffer*>(m_swapchains[output]->currentBuffer().data())->image();
|
||||
const auto drmOutput = static_cast<DrmAbstractOutput*>(output);
|
||||
return static_cast<DrmDumbBuffer*>(drmOutput->outputLayer()->currentBuffer().data())->image();
|
||||
}
|
||||
|
||||
QRegion DrmQPainterBackend::beginFrame(AbstractOutput *output)
|
||||
{
|
||||
return m_swapchains[output]->startRendering().value_or(QRegion());
|
||||
const auto drmOutput = static_cast<DrmAbstractOutput*>(output);
|
||||
return drmOutput->outputLayer()->startRendering().value_or(QRegion());
|
||||
}
|
||||
|
||||
void DrmQPainterBackend::endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damage)
|
||||
{
|
||||
Q_UNUSED(renderedRegion)
|
||||
m_swapchains[output]->endRendering(damage);
|
||||
static_cast<DrmAbstractOutput*>(output)->present(m_swapchains[output]->currentBuffer(), output->geometry());
|
||||
const auto drmOutput = static_cast<DrmAbstractOutput*>(output);
|
||||
drmOutput->outputLayer()->endRendering(damage);
|
||||
static_cast<DrmAbstractOutput*>(output)->present();
|
||||
}
|
||||
|
||||
QSharedPointer<DrmLayer> DrmQPainterBackend::createLayer(DrmDisplayDevice *displayDevice) const
|
||||
{
|
||||
return QSharedPointer<DrmQPainterLayer>::create(displayDevice);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,16 +6,15 @@
|
|||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KWIN_SCENE_QPAINTER_DRM_BACKEND_H
|
||||
#define KWIN_SCENE_QPAINTER_DRM_BACKEND_H
|
||||
#pragma once
|
||||
#include "qpainterbackend.h"
|
||||
#include "drm_render_backend.h"
|
||||
#include "dumb_swapchain.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "dumb_swapchain.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
|
@ -23,20 +22,19 @@ class DrmBackend;
|
|||
class DrmAbstractOutput;
|
||||
class DrmQPainterLayer;
|
||||
|
||||
class DrmQPainterBackend : public QPainterBackend
|
||||
class DrmQPainterBackend : public QPainterBackend, public DrmRenderBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DrmQPainterBackend(DrmBackend *backend);
|
||||
~DrmQPainterBackend();
|
||||
|
||||
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) const override;
|
||||
|
||||
private:
|
||||
QMap<AbstractOutput *, QSharedPointer<DrmQPainterLayer>> m_swapchains;
|
||||
DrmBackend *m_backend;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue