Commit f07d6bd4 authored by Xaver Hugl's avatar Xaver Hugl Committed by Vlad Zahorodnii
Browse files

backends/drm: port the cursor to use output layers

CCBUG: 447797
parent 12515b46
Pipeline #174306 passed with stage
in 14 minutes and 44 seconds
......@@ -31,6 +31,7 @@ set(DRM_SOURCES
egl_gbm_layer_surface.cpp
dmabuf_feedback.cpp
drm_dumb_buffer.cpp
egl_gbm_cursor_layer.cpp
)
add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES})
......
......@@ -196,7 +196,7 @@ std::shared_ptr<GbmBuffer> GbmBuffer::importBuffer(DrmGpu *gpu, KWaylandServer::
}
}
std::shared_ptr<GbmBuffer> GbmBuffer::importBuffer(DrmGpu *gpu, GbmBuffer *buffer)
std::shared_ptr<GbmBuffer> GbmBuffer::importBuffer(DrmGpu *gpu, GbmBuffer *buffer, uint32_t flags)
{
const auto fds = buffer->fds();
if (fds[0] == -1) {
......@@ -219,7 +219,7 @@ std::shared_ptr<GbmBuffer> GbmBuffer::importBuffer(DrmGpu *gpu, GbmBuffer *buffe
data.strides[i] = strides[i];
data.offsets[i] = offsets[i];
}
gbm_bo *bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
gbm_bo *bo = gbm_bo_import(gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, flags);
if (bo) {
return std::make_shared<GbmBuffer>(gpu, bo);
} else {
......
......@@ -14,6 +14,7 @@
#include <QSharedPointer>
#include <epoxy/egl.h>
#include <gbm.h>
struct gbm_bo;
......@@ -45,7 +46,7 @@ public:
QSharedPointer<GLTexture> createTexture(EGLDisplay eglDisplay) const;
static std::shared_ptr<GbmBuffer> importBuffer(DrmGpu *gpu, KWaylandServer::LinuxDmaBufV1ClientBuffer *clientBuffer);
static std::shared_ptr<GbmBuffer> importBuffer(DrmGpu *gpu, GbmBuffer *buffer);
static std::shared_ptr<GbmBuffer> importBuffer(DrmGpu *gpu, GbmBuffer *buffer, uint32_t flags = GBM_BO_USE_SCANOUT);
private:
void createFds() override;
......
......@@ -287,7 +287,7 @@ bool DrmGpu::updateOutputs()
addedOutputs << output;
Q_EMIT outputAdded(output);
}
pipeline->setLayer(m_platform->renderBackend()->createDrmPipelineLayer(pipeline));
pipeline->setLayers(m_platform->renderBackend()->createPrimaryLayer(pipeline), m_platform->renderBackend()->createCursorLayer(pipeline));
pipeline->setActive(!conn->isNonDesktop());
pipeline->applyPendingChanges();
}
......@@ -567,7 +567,7 @@ void DrmGpu::removeOutput(DrmOutput *output)
qCDebug(KWIN_DRM) << "Removing output" << output;
m_drmOutputs.removeOne(output);
m_pipelines.removeOne(output->pipeline());
output->pipeline()->setLayer(nullptr);
output->pipeline()->setLayers(nullptr, nullptr);
m_outputs.removeOne(output);
Q_EMIT outputRemoved(output);
delete output;
......@@ -666,7 +666,7 @@ void DrmGpu::removeLeaseOutput(DrmLeaseOutput *output)
qCDebug(KWIN_DRM) << "Removing leased output" << output;
m_leaseOutputs.removeOne(output);
m_pipelines.removeOne(output->pipeline());
output->pipeline()->setLayer(nullptr);
output->pipeline()->setLayers(nullptr, nullptr);
delete output;
}
......@@ -790,7 +790,7 @@ void DrmGpu::releaseBuffers()
crtc->releaseBuffers();
}
for (const auto &pipeline : qAsConst(m_pipelines)) {
pipeline->layer()->releaseBuffers();
pipeline->primaryLayer()->releaseBuffers();
}
for (const auto &output : qAsConst(m_outputs)) {
if (const auto virtualOutput = qobject_cast<DrmVirtualOutput *>(output)) {
......@@ -802,7 +802,7 @@ void DrmGpu::releaseBuffers()
void DrmGpu::recreateSurfaces()
{
for (const auto &pipeline : qAsConst(m_pipelines)) {
pipeline->setLayer(m_platform->renderBackend()->createDrmPipelineLayer(pipeline));
pipeline->setLayers(m_platform->renderBackend()->createPrimaryLayer(pipeline), m_platform->renderBackend()->createCursorLayer(pipeline));
pipeline->applyPendingChanges();
}
for (const auto &output : qAsConst(m_outputs)) {
......
......@@ -36,6 +36,30 @@ bool DrmPipelineLayer::hasDirectScanoutBuffer() const
return false;
}
DrmOverlayLayer::DrmOverlayLayer(DrmPipeline *pipeline)
: DrmPipelineLayer(pipeline)
{
}
void DrmOverlayLayer::setPosition(const QPoint &pos)
{
m_position = pos;
}
QPoint DrmOverlayLayer::position() const
{
return m_position;
}
void DrmOverlayLayer::setVisible(bool visible)
{
m_visible = visible;
}
bool DrmOverlayLayer::isVisible() const
{
return m_visible;
}
}
#include "drm_layer.moc"
......@@ -45,4 +45,19 @@ protected:
DrmPipeline *const m_pipeline;
};
class DrmOverlayLayer : public DrmPipelineLayer
{
public:
DrmOverlayLayer(DrmPipeline *pipeline);
void setPosition(const QPoint &pos);
void setVisible(bool visible);
QPoint position() const;
bool isVisible() const;
protected:
QPoint m_position;
bool m_visible = false;
};
}
......@@ -19,6 +19,8 @@
#include "drm_dumb_buffer.h"
#include "drm_layer.h"
#include "dumb_swapchain.h"
#include "egl_gbm_backend.h"
#include "kwinglutils.h"
#include "logging.h"
#include "main.h"
#include "outputconfiguration.h"
......@@ -102,16 +104,6 @@ DrmOutput::~DrmOutput()
m_pipeline->setOutput(nullptr);
}
static bool isCursorSpriteCompatible(const QImage *buffer, const QImage *sprite)
{
// Note that we need compare the rects in the device independent pixels because the
// buffer and the cursor sprite image may have different scale factors.
const QRect bufferRect(QPoint(0, 0), buffer->size() / buffer->devicePixelRatio());
const QRect spriteRect(QPoint(0, 0), sprite->size() / sprite->devicePixelRatio());
return bufferRect.contains(spriteRect);
}
void DrmOutput::updateCursor()
{
static bool valid;
......@@ -120,56 +112,40 @@ void DrmOutput::updateCursor()
m_setCursorSuccessful = false;
return;
}
if (!m_pipeline->crtc()) {
const auto layer = m_pipeline->cursorLayer();
if (!m_pipeline->crtc() || !layer) {
return;
}
const Cursor *cursor = Cursors::self()->currentCursor();
if (!cursor) {
m_pipeline->setCursor(nullptr);
if (!cursor || cursor->image().isNull() || Cursors::self()->isCursorHidden()) {
if (layer->isVisible()) {
layer->setVisible(false);
m_pipeline->setCursor();
}
return;
}
const QImage cursorImage = cursor->image();
if (cursorImage.isNull() || Cursors::self()->isCursorHidden()) {
m_pipeline->setCursor(nullptr);
return;
}
if (m_cursor && m_cursor->isEmpty()) {
m_pipeline->setCursor(nullptr);
return;
}
const auto plane = m_pipeline->crtc()->cursorPlane();
if (!m_cursor || (plane && !plane->formats().value(m_cursor->drmFormat()).contains(DRM_FORMAT_MOD_LINEAR))) {
if (plane && (!plane->formats().contains(DRM_FORMAT_ARGB8888) || !plane->formats().value(DRM_FORMAT_ARGB8888).contains(DRM_FORMAT_MOD_LINEAR))) {
m_pipeline->setCursor(nullptr);
m_setCursorSuccessful = false;
return;
}
m_cursor = QSharedPointer<DumbSwapchain>::create(m_gpu, m_gpu->cursorSize(), plane ? DRM_FORMAT_ARGB8888 : DRM_FORMAT_XRGB8888, QImage::Format::Format_ARGB32_Premultiplied);
if (!m_cursor || m_cursor->isEmpty()) {
m_pipeline->setCursor(nullptr);
m_setCursorSuccessful = false;
return;
const QMatrix4x4 monitorMatrix = logicalToNativeMatrix(geometry(), scale(), transform());
const QSize surfaceSize = m_gpu->cursorSize() / scale();
const QRect cursorRect = monitorMatrix.mapRect(QRect(cursor->geometry().topLeft(), surfaceSize));
if (cursorRect.width() > m_gpu->cursorSize().width() || cursorRect.height() > m_gpu->cursorSize().height()) {
if (layer->isVisible()) {
layer->setVisible(false);
m_pipeline->setCursor();
}
}
m_cursor->releaseBuffer(m_cursor->currentBuffer());
m_cursor->acquireBuffer();
QImage *c = m_cursor->currentBuffer()->image();
c->setDevicePixelRatio(scale());
if (!isCursorSpriteCompatible(c, &cursorImage)) {
// If the cursor image is too big, fall back to rendering the software cursor.
m_pipeline->setCursor(nullptr);
m_setCursorSuccessful = false;
return;
}
c->fill(Qt::transparent);
QPainter p;
p.begin(c);
p.setWorldTransform(logicalToNativeMatrix(cursor->rect(), 1, transform()).toTransform());
p.setRenderHint(QPainter::SmoothPixmapTransform);
p.drawImage(QPoint(0, 0), cursorImage);
p.end();
m_setCursorSuccessful = m_pipeline->setCursor(DrmFramebuffer::createFramebuffer(m_cursor->currentBuffer()), logicalToNativeMatrix(cursor->rect(), scale(), transform()).map(cursor->hotspot()));
moveCursor();
if (dynamic_cast<EglGbmBackend *>(m_gpu->platform()->renderBackend())) {
renderCursorOpengl(cursor->geometry().size() * scale());
} else {
renderCursorQPainter();
}
layer->setPosition(cursorRect.topLeft());
layer->setVisible(cursor->geometry().intersects(geometry()));
if (layer->isVisible()) {
m_setCursorSuccessful = m_pipeline->setCursor(logicalToNativeMatrix(QRect(QPoint(), cursorRect.size()), scale(), transform()).map(cursor->hotspot()));
}
}
void DrmOutput::moveCursor()
......@@ -177,12 +153,24 @@ void DrmOutput::moveCursor()
if (!m_setCursorSuccessful || !m_pipeline->crtc()) {
return;
}
const auto layer = m_pipeline->cursorLayer();
Cursor *cursor = Cursors::self()->currentCursor();
if (!cursor || cursor->image().isNull() || Cursors::self()->isCursorHidden() || !cursor->geometry().intersects(geometry())) {
if (layer->isVisible()) {
layer->setVisible(false);
m_pipeline->setCursor();
}
return;
}
const QMatrix4x4 monitorMatrix = logicalToNativeMatrix(geometry(), scale(), transform());
const QMatrix4x4 hotspotMatrix = logicalToNativeMatrix(cursor->rect(), scale(), transform());
m_moveCursorSuccessful = m_pipeline->moveCursor(monitorMatrix.map(cursor->pos()) - hotspotMatrix.map(cursor->hotspot()));
const QSize surfaceSize = m_gpu->cursorSize() / scale();
const QRect cursorRect = monitorMatrix.mapRect(QRect(cursor->geometry().topLeft(), surfaceSize));
layer->setVisible(true);
layer->setPosition(cursorRect.topLeft());
m_moveCursorSuccessful = m_pipeline->moveCursor();
layer->setVisible(m_moveCursorSuccessful);
if (!m_moveCursorSuccessful) {
m_pipeline->setCursor(nullptr);
m_pipeline->setCursor();
}
}
......@@ -322,7 +310,7 @@ bool DrmOutput::present()
}
bool modeset = gpu()->needsModeset();
if (modeset ? m_pipeline->maybeModeset() : m_pipeline->present()) {
Q_EMIT outputChange(m_pipeline->layer()->currentDamage());
Q_EMIT outputChange(m_pipeline->primaryLayer()->currentDamage());
return true;
} else if (!modeset) {
qCWarning(KWIN_DRM) << "Presentation failed!" << strerror(errno);
......@@ -393,6 +381,8 @@ void DrmOutput::applyQueuedChanges(const OutputConfiguration &config)
m_renderLoop->scheduleRepaint();
Q_EMIT changed();
updateCursor();
}
void DrmOutput::revertQueuedChanges()
......@@ -407,7 +397,7 @@ bool DrmOutput::usesSoftwareCursor() const
DrmOutputLayer *DrmOutput::outputLayer() const
{
return m_pipeline->layer();
return m_pipeline->primaryLayer();
}
void DrmOutput::setColorTransformation(const QSharedPointer<ColorTransformation> &transformation)
......@@ -420,4 +410,76 @@ void DrmOutput::setColorTransformation(const QSharedPointer<ColorTransformation>
m_pipeline->revertPendingChanges();
}
}
void DrmOutput::renderCursorOpengl(const QSize &cursorSize)
{
const auto layer = m_pipeline->cursorLayer();
auto allocateTexture = [this]() {
const QImage img = Cursors::self()->currentCursor()->image();
if (img.isNull()) {
m_cursorTextureDirty = false;
return;
}
m_cursorTexture.reset(new GLTexture(img));
m_cursorTexture->setWrapMode(GL_CLAMP_TO_EDGE);
m_cursorTextureDirty = false;
};
if (!m_cursorTexture) {
allocateTexture();
// handle shape update on case cursor image changed
connect(Cursors::self(), &Cursors::currentCursorChanged, this, [this]() {
m_cursorTextureDirty = true;
});
} else if (m_cursorTextureDirty) {
const QImage image = Cursors::self()->currentCursor()->image();
if (image.size() == m_cursorTexture->size()) {
m_cursorTexture->update(image);
m_cursorTextureDirty = false;
} else {
allocateTexture();
}
}
const auto [renderTarget, repaint] = layer->beginFrame();
QMatrix4x4 mvp;
mvp.ortho(QRect(QPoint(), renderTarget.size()));
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_cursorTexture->bind();
ShaderBinder binder(ShaderTrait::MapTexture);
binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
m_cursorTexture->render(QRect(0, 0, cursorSize.width(), cursorSize.height()));
m_cursorTexture->unbind();
glDisable(GL_BLEND);
layer->endFrame(infiniteRegion(), infiniteRegion());
}
void DrmOutput::renderCursorQPainter()
{
const auto layer = m_pipeline->cursorLayer();
const Cursor *cursor = Cursors::self()->currentCursor();
const QImage cursorImage = cursor->image();
const auto [renderTarget, repaint] = layer->beginFrame();
QImage *c = std::get<QImage *>(renderTarget.nativeHandle());
c->setDevicePixelRatio(scale());
c->fill(Qt::transparent);
QPainter p;
p.begin(c);
p.setWorldTransform(logicalToNativeMatrix(cursor->rect(), 1, transform()).toTransform());
p.setRenderHint(QPainter::SmoothPixmapTransform);
p.drawImage(QPoint(0, 0), cursorImage);
p.end();
layer->endFrame(infiniteRegion(), infiniteRegion());
}
}
......@@ -29,6 +29,7 @@ class DrmConnector;
class DrmGpu;
class DrmPipeline;
class DumbSwapchain;
class GLTexture;
class KWIN_EXPORT DrmOutput : public DrmAbstractOutput
{
......@@ -61,14 +62,16 @@ private:
void updateCursor();
void moveCursor();
void renderCursorOpengl(const QSize &cursorSize);
void renderCursorQPainter();
DrmPipeline *m_pipeline;
DrmConnector *m_connector;
QSharedPointer<DumbSwapchain> m_cursor;
bool m_setCursorSuccessful = false;
bool m_moveCursorSuccessful = false;
QRect m_lastCursorGeometry;
bool m_cursorTextureDirty = true;
std::unique_ptr<GLTexture> m_cursorTexture;
QTimer m_turnOffTimer;
};
......
......@@ -31,6 +31,9 @@
namespace KWin
{
static const QMap<uint32_t, QVector<uint64_t>> legacyFormats = {{DRM_FORMAT_XRGB8888, {}}};
static const QMap<uint32_t, QVector<uint64_t>> legacyCursorFormats = {{DRM_FORMAT_ARGB8888, {}}};
DrmPipeline::DrmPipeline(DrmConnector *conn)
: m_connector(conn)
{
......@@ -182,9 +185,11 @@ bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
m_pending.crtc->primaryPlane()->setBuffer(activePending() ? fb : nullptr);
if (m_pending.crtc->cursorPlane()) {
m_pending.crtc->cursorPlane()->set(QPoint(0, 0), gpu()->cursorSize(), m_pending.cursorPos, gpu()->cursorSize());
m_pending.crtc->cursorPlane()->setBuffer(activePending() ? m_pending.cursorFb.get() : nullptr);
m_pending.crtc->cursorPlane()->setPending(DrmPlane::PropertyIndex::CrtcId, (activePending() && m_pending.cursorFb) ? m_pending.crtc->id() : 0);
const auto layer = cursorLayer();
bool active = activePending() && layer->isVisible();
m_pending.crtc->cursorPlane()->set(QPoint(0, 0), gpu()->cursorSize(), layer->position(), gpu()->cursorSize());
m_pending.crtc->cursorPlane()->setBuffer(active ? layer->currentBuffer().get() : nullptr);
m_pending.crtc->cursorPlane()->setPending(DrmPlane::PropertyIndex::CrtcId, active ? m_pending.crtc->id() : 0);
}
}
if (!m_connector->atomicPopulate(req)) {
......@@ -289,7 +294,7 @@ void DrmPipeline::atomicCommitSuccessful(CommitMode mode)
m_pending.crtc->primaryPlane()->setNext(m_pending.layer->currentBuffer());
m_pending.crtc->primaryPlane()->commit();
if (m_pending.crtc->cursorPlane()) {
m_pending.crtc->cursorPlane()->setNext(m_pending.cursorFb);
m_pending.crtc->cursorPlane()->setNext(cursorLayer()->currentBuffer());
m_pending.crtc->cursorPlane()->commit();
}
}
......@@ -300,40 +305,30 @@ void DrmPipeline::atomicCommitSuccessful(CommitMode mode)
}
}
bool DrmPipeline::setCursor(const std::shared_ptr<DrmFramebuffer> &buffer, const QPoint &hotspot)
bool DrmPipeline::setCursor(const QPoint &hotspot)
{
if (m_pending.cursorFb == buffer && m_pending.cursorHotspot == hotspot) {
return true;
}
bool result;
const bool visibleBefore = isCursorVisible();
m_pending.cursorFb = buffer;
m_pending.cursorHotspot = hotspot;
// explicitly check for the cursor plane and not for AMS, as we might not always have one
if (m_pending.crtc->cursorPlane()) {
result = commitPipelines({this}, CommitMode::Test);
if (result && m_output) {
m_output->renderLoop()->scheduleRepaint();
}
} else {
result = setCursorLegacy();
}
if (result) {
m_next = m_pending;
if (m_output && (visibleBefore || isCursorVisible())) {
m_output->renderLoop()->scheduleRepaint();
}
} else {
m_pending = m_next;
}
return result;
}
bool DrmPipeline::moveCursor(QPoint pos)
bool DrmPipeline::moveCursor()
{
if (m_pending.cursorPos == pos) {
return true;
}
const bool visibleBefore = isCursorVisible();
bool result;
m_pending.cursorPos = pos;
// explicitly check for the cursor plane and not for AMS, as we might not always have one
if (m_pending.crtc->cursorPlane()) {
result = commitPipelines({this}, CommitMode::Test);
......@@ -342,7 +337,7 @@ bool DrmPipeline::moveCursor(QPoint pos)
}
if (result) {
m_next = m_pending;
if (m_output && (visibleBefore || isCursorVisible())) {
if (m_output) {
m_output->renderLoop()->scheduleRepaint();
}
} else {
......@@ -368,12 +363,6 @@ QSize DrmPipeline::bufferSize() const
return modeSize;
}
bool DrmPipeline::isCursorVisible() const
{
const QRect mode = QRect(QPoint(), m_pending.mode->size());
return m_pending.cursorFb && (!m_pending.cursorFb->buffer() || QRect(m_pending.cursorPos, m_pending.cursorFb->buffer()->size()).intersects(mode));
}
DrmConnector *DrmPipeline::connector() const
{
return m_connector;
......@@ -414,6 +403,15 @@ QMap<uint32_t, QVector<uint64_t>> DrmPipeline::formats() const
return m_pending.formats;
}
QMap<uint32_t, QVector<uint64_t>> DrmPipeline::cursorFormats() const
{
if (m_pending.crtc && m_pending.crtc->cursorPlane()) {
return m_pending.crtc->cursorPlane()->formats();
} else {
return legacyCursorFormats;
}
}
bool DrmPipeline::pruneModifier()
{
if (!m_pending.layer->currentBuffer()
......@@ -582,11 +580,16 @@ bool DrmPipeline::enabled() const
return m_pending.enabled;
}
DrmPipelineLayer *DrmPipeline::layer() const
DrmPipelineLayer *DrmPipeline::primaryLayer() const
{
return m_pending.layer.get();
}
DrmOverlayLayer *DrmPipeline::cursorLayer() const
{
return m_pending.cursorLayer.get();
}
DrmPlane::Transformations DrmPipeline::renderOrientation() const
{
return m_pending.renderOrientation;
......@@ -612,8 +615,6 @@ Output::RgbRange DrmPipeline::rgbRange() const
return m_pending.rgbRange;
}
static const QMap<uint32_t, QVector<uint64_t>> legacyFormats = {{DRM_FORMAT_XRGB8888, {}}};
void DrmPipeline::setCrtc(DrmCrtc *crtc)
{
if (crtc && m_pending.crtc && crtc->gammaRampSize() != m_pending.crtc->gammaRampSize() && m_pending.colorTransformation) {
......@@ -642,9 +643,10 @@ void DrmPipeline::setEnable(bool enable)
m_pending.enabled = enable;
}
void DrmPipeline::setLayer(const QSharedPointer<DrmPipelineLayer> &layer)
void DrmPipeline::setLayers(const QSharedPointer<DrmPipelineLayer> &primaryLayer, const QSharedPointer<DrmOverlayLayer> &cursorLayer)
{
m_pending.layer = layer;
m_pending.layer = primaryLayer;
m_pending.cursorLayer = cursorLayer;
}
void DrmPipeline::setRenderOrientation(DrmPlane::Transformations orientation)
......
......@@ -31,6 +31,7 @@ class DrmCrtc;
class GammaRamp;
class DrmConnectorMode;
class DrmPipelineLayer;
class DrmOverlayLayer;
class DrmGammaRamp
{
......@@ -65,8 +66,8 @@ public:
void applyPendingChanges();
void revertPendingChanges();
bool setCursor(const std::shared_ptr<DrmFramebuffer> &buffer, const QPoint &hotspot = QPoint());
bool moveCursor(QPoint pos);
bool setCursor(const QPoint &hotspot = QPoint());
bool moveCursor();
DrmConnector *connector() const;
DrmCrtc *currentCrtc() const;
......@@ -83,6 +84,7 @@ public:
QSize bufferSize() const;
QMap<uint32_t, QVector<uint64_t>> formats() const;
QMap<uint32_t, QVector<uint64_t>> cursorFormats() const;
bool pruneModifier();
void setOutput(DrmOutput *output);
......@@ -92,7 +94,8 @@ public:
QSharedPointer<DrmConnectorMode> mode() const;
bool active() const;
bool enabled() const;
DrmPipelineLayer *layer() const;
DrmPipelineLayer *primaryLayer() const;
DrmOverlayLayer *cursorLayer() const;
DrmPlane::Transformations renderOrientation() const;
DrmPlane::Transformations bufferOrientation() const;
RenderLoopPrivate::SyncMode syncMode() const;
......@@ -103,7 +106,7 @@ public:
void setMode(const QSharedPointer<DrmConnectorMode> &mode);
void setActive(bool active);
void setEnable(bool enable);
void setLayer(const QSharedPointer<DrmPipelineLayer> &layer);
void setLayers(const QSharedPointer