KWin
Loading...
Searching...
No Matches
drm_egl_layer.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "drm_egl_layer.h"
10#include "core/iccprofile.h"
11#include "drm_backend.h"
12#include "drm_buffer.h"
13#include "drm_egl_backend.h"
14#include "drm_gpu.h"
15#include "drm_output.h"
16#include "drm_pipeline.h"
18#include "wayland/surface.h"
19
20#include <QRegion>
21#include <drm_fourcc.h>
22#include <errno.h>
23#include <gbm.h>
24#include <unistd.h>
25
26namespace KWin
27{
28
29static OutputTransform drmToOutputTransform(DrmPipeline *pipeline)
30{
31 auto angle = DrmPlane::transformationToDegrees(pipeline->renderOrientation());
32 if (angle < 0) {
33 angle += 360;
34 }
35 OutputTransform flip = (pipeline->renderOrientation() & DrmPlane::Transformation::ReflectX) ? OutputTransform::FlipX : OutputTransform();
36 switch (angle % 360) {
37 case 0:
38 return flip;
39 case 90:
40 return flip.combine(OutputTransform::Rotate90);
41 case 180:
42 return flip.combine(OutputTransform::Rotate180);
43 case 270:
44 return flip.combine(OutputTransform::Rotate270);
45 default:
46 Q_UNREACHABLE();
47 }
48}
49
51 : DrmPipelineLayer(pipeline)
52 , m_surface(pipeline->gpu(), eglBackend)
53 , m_dmabufFeedback(pipeline->gpu(), eglBackend)
54{
55}
56
57std::optional<OutputLayerBeginFrameInfo> EglGbmLayer::beginFrame()
58{
59 m_scanoutBuffer.reset();
60 m_dmabufFeedback.renderingSurface();
61
63}
64
65bool EglGbmLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
66{
67 const bool ret = m_surface.endRendering(damagedRegion);
68 if (ret) {
69 m_currentDamage = damagedRegion;
70 }
71 return ret;
72}
73
75{
76 return m_currentDamage;
77}
78
80{
81 return m_surface.renderTestBuffer(m_pipeline->mode()->size(), m_pipeline->formats()) != nullptr;
82}
83
84std::shared_ptr<GLTexture> EglGbmLayer::texture() const
85{
86 if (m_scanoutBuffer) {
87 const auto ret = m_surface.eglBackend()->importDmaBufAsTexture(*m_scanoutBuffer->buffer()->dmabufAttributes());
88 ret->setContentTransform(drmToOutputTransform(m_pipeline).combine(OutputTransform::FlipY));
89 return ret;
90 } else {
91 return m_surface.texture();
92 }
93}
94
99
101{
102 static bool valid;
103 static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid;
104 if (directScanoutDisabled) {
105 return false;
106 }
107 if (surfaceItem->colorDescription() != m_pipeline->colorDescription() || m_pipeline->output()->channelFactors() != QVector3D(1, 1, 1) || m_pipeline->iccProfile()) {
108 // TODO use GAMMA_LUT, CTM and DEGAMMA_LUT to allow direct scanout with HDR
109 return false;
110 }
111
112 SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem);
113 if (!item || !item->surface()) {
114 return false;
115 }
116 const auto surface = item->surface();
117 if (surface->bufferTransform() != m_pipeline->output()->transform()) {
118 return false;
119 }
120 const auto buffer = surface->buffer();
121 if (!buffer) {
122 return false;
123 }
124
125 const DmaBufAttributes *dmabufAttributes = buffer->dmabufAttributes();
126 if (!dmabufAttributes) {
127 return false;
128 }
129
130 const auto formats = m_pipeline->formats();
131 if (!formats.contains(dmabufAttributes->format)) {
132 m_dmabufFeedback.scanoutFailed(surface, formats);
133 return false;
134 }
135 if (dmabufAttributes->modifier == DRM_FORMAT_MOD_INVALID && m_pipeline->gpu()->platform()->gpuCount() > 1) {
136 // importing a buffer from another GPU without an explicit modifier can mess up the buffer format
137 return false;
138 }
139 if (!formats[dmabufAttributes->format].contains(dmabufAttributes->modifier)) {
140 return false;
141 }
142 m_scanoutBuffer = m_pipeline->gpu()->importBuffer(buffer, FileDescriptor{});
143 if (m_scanoutBuffer && m_pipeline->testScanout()) {
144 m_dmabufFeedback.scanoutSuccessful(surface);
145 m_currentDamage = surfaceItem->mapFromBuffer(surfaceItem->damage());
146 surfaceItem->resetDamage();
147 // ensure the pixmap is updated when direct scanout ends
148 surfaceItem->destroyPixmap();
149 m_surface.forgetDamage(); // TODO: Use absolute frame sequence numbers for indexing the DamageJournal. It's more flexible and less error-prone
150 return true;
151 } else {
152 m_dmabufFeedback.scanoutFailed(surface, formats);
153 m_scanoutBuffer.reset();
154 return false;
155 }
156}
157
158std::shared_ptr<DrmFramebuffer> EglGbmLayer::currentBuffer() const
159{
160 return m_scanoutBuffer ? m_scanoutBuffer : m_surface.currentBuffer();
161}
162
164{
165 return m_scanoutBuffer != nullptr;
166}
167
169{
170 m_scanoutBuffer.reset();
171 m_surface.destroyResources();
172}
173
174std::chrono::nanoseconds EglGbmLayer::queryRenderTime() const
175{
176 return m_surface.queryRenderTime();
177}
178}
std::shared_ptr< GLTexture > importDmaBufAsTexture(const DmaBufAttributes &attributes) const
void scanoutFailed(SurfaceInterface *surface, const QMap< uint32_t, QList< uint64_t > > &formats)
void scanoutSuccessful(SurfaceInterface *surface)
size_t gpuCount() const
DrmBackend * platform() const
Definition drm_gpu.cpp:585
std::shared_ptr< DrmFramebuffer > importBuffer(GraphicsBuffer *buffer, FileDescriptor &&explicitFence)
Definition drm_gpu.cpp:839
bool needsColormanagement() const
QVector3D channelFactors() const
DrmOutput * output() const
const std::shared_ptr< IccProfile > & iccProfile() const
QMap< uint32_t, QList< uint64_t > > formats() const
DrmGpu * gpu() const
const ColorDescription & colorDescription() const
std::shared_ptr< DrmConnectorMode > mode() const
DrmPipeline *const m_pipeline
Definition drm_layer.h:44
static int32_t transformationToDegrees(Transformations transformation)
OpenGL Backend using Egl on a GBM surface.
ColorDescription colorDescription() const
void releaseBuffers() override
std::optional< OutputLayerBeginFrameInfo > beginFrame() override
std::shared_ptr< DrmFramebuffer > currentBuffer() const override
bool hasDirectScanoutBuffer() const override
bool checkTestBuffer() override
EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline)
bool scanout(SurfaceItem *surfaceItem) override
QRegion currentDamage() const override
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override
std::chrono::nanoseconds queryRenderTime() const override
std::shared_ptr< GLTexture > texture() const override
std::optional< OutputLayerBeginFrameInfo > startRendering(const QSize &bufferSize, OutputTransform transformation, const QMap< uint32_t, QList< uint64_t > > &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, const std::shared_ptr< IccProfile > &iccProfile, bool enableColormanagement)
std::shared_ptr< GLTexture > texture() const
std::chrono::nanoseconds queryRenderTime() const
EglGbmBackend * eglBackend() const
const ColorDescription & colorDescription() const
bool endRendering(const QRegion &damagedRegion)
std::shared_ptr< DrmFramebuffer > currentBuffer() const
std::shared_ptr< DrmFramebuffer > renderTestBuffer(const QSize &bufferSize, const QMap< uint32_t, QList< uint64_t > > &formats)
const ColorDescription & colorDescription() const
Definition item.cpp:445
OutputTransform transform() const
Definition output.cpp:369
GraphicsBuffer * buffer() const
Definition surface.cpp:809
QRegion damage() const
QRegion mapFromBuffer(const QRegion &region) const
SurfaceInterface * surface() const