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 }