File indexing completed on 2024-11-10 04:56:27
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "drm_egl_layer.h" 0010 #include "core/iccprofile.h" 0011 #include "drm_backend.h" 0012 #include "drm_buffer.h" 0013 #include "drm_egl_backend.h" 0014 #include "drm_gpu.h" 0015 #include "drm_output.h" 0016 #include "drm_pipeline.h" 0017 #include "scene/surfaceitem_wayland.h" 0018 #include "wayland/surface.h" 0019 0020 #include <QRegion> 0021 #include <drm_fourcc.h> 0022 #include <errno.h> 0023 #include <gbm.h> 0024 #include <unistd.h> 0025 0026 namespace KWin 0027 { 0028 0029 static OutputTransform drmToOutputTransform(DrmPipeline *pipeline) 0030 { 0031 auto angle = DrmPlane::transformationToDegrees(pipeline->renderOrientation()); 0032 if (angle < 0) { 0033 angle += 360; 0034 } 0035 OutputTransform flip = (pipeline->renderOrientation() & DrmPlane::Transformation::ReflectX) ? OutputTransform::FlipX : OutputTransform(); 0036 switch (angle % 360) { 0037 case 0: 0038 return flip; 0039 case 90: 0040 return flip.combine(OutputTransform::Rotate90); 0041 case 180: 0042 return flip.combine(OutputTransform::Rotate180); 0043 case 270: 0044 return flip.combine(OutputTransform::Rotate270); 0045 default: 0046 Q_UNREACHABLE(); 0047 } 0048 } 0049 0050 EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline) 0051 : DrmPipelineLayer(pipeline) 0052 , m_surface(pipeline->gpu(), eglBackend) 0053 , m_dmabufFeedback(pipeline->gpu(), eglBackend) 0054 { 0055 } 0056 0057 std::optional<OutputLayerBeginFrameInfo> EglGbmLayer::beginFrame() 0058 { 0059 m_scanoutBuffer.reset(); 0060 m_dmabufFeedback.renderingSurface(); 0061 0062 return m_surface.startRendering(m_pipeline->mode()->size(), drmToOutputTransform(m_pipeline).combine(OutputTransform::FlipY), m_pipeline->formats(), m_pipeline->colorDescription(), m_pipeline->output()->channelFactors(), m_pipeline->iccProfile(), m_pipeline->output()->needsColormanagement()); 0063 } 0064 0065 bool EglGbmLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) 0066 { 0067 const bool ret = m_surface.endRendering(damagedRegion); 0068 if (ret) { 0069 m_currentDamage = damagedRegion; 0070 } 0071 return ret; 0072 } 0073 0074 QRegion EglGbmLayer::currentDamage() const 0075 { 0076 return m_currentDamage; 0077 } 0078 0079 bool EglGbmLayer::checkTestBuffer() 0080 { 0081 return m_surface.renderTestBuffer(m_pipeline->mode()->size(), m_pipeline->formats()) != nullptr; 0082 } 0083 0084 std::shared_ptr<GLTexture> EglGbmLayer::texture() const 0085 { 0086 if (m_scanoutBuffer) { 0087 const auto ret = m_surface.eglBackend()->importDmaBufAsTexture(*m_scanoutBuffer->buffer()->dmabufAttributes()); 0088 ret->setContentTransform(drmToOutputTransform(m_pipeline).combine(OutputTransform::FlipY)); 0089 return ret; 0090 } else { 0091 return m_surface.texture(); 0092 } 0093 } 0094 0095 ColorDescription EglGbmLayer::colorDescription() const 0096 { 0097 return m_surface.colorDescription(); 0098 } 0099 0100 bool EglGbmLayer::scanout(SurfaceItem *surfaceItem) 0101 { 0102 static bool valid; 0103 static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid; 0104 if (directScanoutDisabled) { 0105 return false; 0106 } 0107 if (surfaceItem->colorDescription() != m_pipeline->colorDescription() || m_pipeline->output()->channelFactors() != QVector3D(1, 1, 1) || m_pipeline->iccProfile()) { 0108 // TODO use GAMMA_LUT, CTM and DEGAMMA_LUT to allow direct scanout with HDR 0109 return false; 0110 } 0111 0112 SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem); 0113 if (!item || !item->surface()) { 0114 return false; 0115 } 0116 const auto surface = item->surface(); 0117 if (surface->bufferTransform() != m_pipeline->output()->transform()) { 0118 return false; 0119 } 0120 const auto buffer = surface->buffer(); 0121 if (!buffer) { 0122 return false; 0123 } 0124 0125 const DmaBufAttributes *dmabufAttributes = buffer->dmabufAttributes(); 0126 if (!dmabufAttributes) { 0127 return false; 0128 } 0129 0130 const auto formats = m_pipeline->formats(); 0131 if (!formats.contains(dmabufAttributes->format)) { 0132 m_dmabufFeedback.scanoutFailed(surface, formats); 0133 return false; 0134 } 0135 if (dmabufAttributes->modifier == DRM_FORMAT_MOD_INVALID && m_pipeline->gpu()->platform()->gpuCount() > 1) { 0136 // importing a buffer from another GPU without an explicit modifier can mess up the buffer format 0137 return false; 0138 } 0139 if (!formats[dmabufAttributes->format].contains(dmabufAttributes->modifier)) { 0140 return false; 0141 } 0142 m_scanoutBuffer = m_pipeline->gpu()->importBuffer(buffer, FileDescriptor{}); 0143 if (m_scanoutBuffer && m_pipeline->testScanout()) { 0144 m_dmabufFeedback.scanoutSuccessful(surface); 0145 m_currentDamage = surfaceItem->mapFromBuffer(surfaceItem->damage()); 0146 surfaceItem->resetDamage(); 0147 // ensure the pixmap is updated when direct scanout ends 0148 surfaceItem->destroyPixmap(); 0149 m_surface.forgetDamage(); // TODO: Use absolute frame sequence numbers for indexing the DamageJournal. It's more flexible and less error-prone 0150 return true; 0151 } else { 0152 m_dmabufFeedback.scanoutFailed(surface, formats); 0153 m_scanoutBuffer.reset(); 0154 return false; 0155 } 0156 } 0157 0158 std::shared_ptr<DrmFramebuffer> EglGbmLayer::currentBuffer() const 0159 { 0160 return m_scanoutBuffer ? m_scanoutBuffer : m_surface.currentBuffer(); 0161 } 0162 0163 bool EglGbmLayer::hasDirectScanoutBuffer() const 0164 { 0165 return m_scanoutBuffer != nullptr; 0166 } 0167 0168 void EglGbmLayer::releaseBuffers() 0169 { 0170 m_scanoutBuffer.reset(); 0171 m_surface.destroyResources(); 0172 } 0173 0174 std::chrono::nanoseconds EglGbmLayer::queryRenderTime() const 0175 { 0176 return m_surface.queryRenderTime(); 0177 } 0178 }