File indexing completed on 2025-04-20 10:57:34

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 "drm_abstract_output.h"
0011 #include "drm_backend.h"
0012 #include "drm_buffer_gbm.h"
0013 #include "drm_egl_backend.h"
0014 #include "drm_gpu.h"
0015 #include "drm_logging.h"
0016 #include "drm_output.h"
0017 #include "drm_pipeline.h"
0018 #include "egl_dmabuf.h"
0019 #include "scene/surfaceitem_wayland.h"
0020 #include "wayland/linuxdmabufv1clientbuffer.h"
0021 #include "wayland/surface_interface.h"
0022 
0023 #include <QRegion>
0024 #include <drm_fourcc.h>
0025 #include <errno.h>
0026 #include <gbm.h>
0027 #include <unistd.h>
0028 
0029 namespace KWin
0030 {
0031 
0032 EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline)
0033     : DrmPipelineLayer(pipeline)
0034     , m_surface(pipeline->gpu(), eglBackend)
0035     , m_dmabufFeedback(pipeline->gpu(), eglBackend)
0036 {
0037 }
0038 
0039 std::optional<OutputLayerBeginFrameInfo> EglGbmLayer::beginFrame()
0040 {
0041     m_scanoutBuffer.reset();
0042     m_dmabufFeedback.renderingSurface();
0043 
0044     return m_surface.startRendering(m_pipeline->bufferSize(), m_pipeline->renderOrientation(), m_pipeline->bufferOrientation(), m_pipeline->formats());
0045 }
0046 
0047 void EglGbmLayer::aboutToStartPainting(const QRegion &damagedRegion)
0048 {
0049     m_surface.aboutToStartPainting(m_pipeline->output(), damagedRegion);
0050 }
0051 
0052 bool EglGbmLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0053 {
0054     const bool ret = m_surface.endRendering(m_pipeline->renderOrientation(), damagedRegion);
0055     if (ret) {
0056         m_currentDamage = damagedRegion;
0057     }
0058     return ret;
0059 }
0060 
0061 QRegion EglGbmLayer::currentDamage() const
0062 {
0063     return m_currentDamage;
0064 }
0065 
0066 bool EglGbmLayer::checkTestBuffer()
0067 {
0068     return m_surface.renderTestBuffer(m_pipeline->bufferSize(), m_pipeline->formats()) != nullptr;
0069 }
0070 
0071 std::shared_ptr<GLTexture> EglGbmLayer::texture() const
0072 {
0073     if (m_scanoutBuffer) {
0074         return m_surface.eglBackend()->importBufferObjectAsTexture(static_cast<GbmBuffer *>(m_scanoutBuffer->buffer())->bo());
0075     } else {
0076         return m_surface.texture();
0077     }
0078 }
0079 
0080 bool EglGbmLayer::scanout(SurfaceItem *surfaceItem)
0081 {
0082     static bool valid;
0083     static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid;
0084     if (directScanoutDisabled) {
0085         return false;
0086     }
0087 
0088     SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem);
0089     if (!item || !item->surface()) {
0090         return false;
0091     }
0092     const auto surface = item->surface();
0093     if (m_pipeline->bufferOrientation() != DrmPlane::Transformations(DrmPlane::Transformation::Rotate0) || surface->bufferTransform() != m_pipeline->output()->transform()) {
0094         return false;
0095     }
0096     const auto buffer = qobject_cast<KWaylandServer::LinuxDmaBufV1ClientBuffer *>(surface->buffer());
0097     if (!buffer) {
0098         return false;
0099     }
0100 
0101     const auto formats = m_pipeline->formats();
0102     if (!formats.contains(buffer->format())) {
0103         m_dmabufFeedback.scanoutFailed(surface, formats);
0104         return false;
0105     }
0106     if (buffer->attributes().modifier == DRM_FORMAT_MOD_INVALID && m_pipeline->gpu()->platform()->gpuCount() > 1) {
0107         // importing a buffer from another GPU without an explicit modifier can mess up the buffer format
0108         return false;
0109     }
0110     if (!formats[buffer->format()].contains(buffer->attributes().modifier)) {
0111         return false;
0112     }
0113     const auto gbmBuffer = GbmBuffer::importBuffer(m_pipeline->gpu(), buffer);
0114     if (!gbmBuffer) {
0115         m_dmabufFeedback.scanoutFailed(surface, formats);
0116         return false;
0117     }
0118     m_scanoutBuffer = DrmFramebuffer::createFramebuffer(gbmBuffer);
0119     if (m_scanoutBuffer && m_pipeline->testScanout()) {
0120         m_dmabufFeedback.scanoutSuccessful(surface);
0121         m_currentDamage = surfaceItem->damage();
0122         surfaceItem->resetDamage();
0123         // ensure the pixmap is updated when direct scanout ends
0124         surfaceItem->destroyPixmap();
0125         return true;
0126     } else {
0127         m_dmabufFeedback.scanoutFailed(surface, formats);
0128         m_scanoutBuffer.reset();
0129         return false;
0130     }
0131 }
0132 
0133 std::shared_ptr<DrmFramebuffer> EglGbmLayer::currentBuffer() const
0134 {
0135     return m_scanoutBuffer ? m_scanoutBuffer : m_surface.currentBuffer();
0136 }
0137 
0138 bool EglGbmLayer::hasDirectScanoutBuffer() const
0139 {
0140     return m_scanoutBuffer != nullptr;
0141 }
0142 
0143 void EglGbmLayer::releaseBuffers()
0144 {
0145     m_scanoutBuffer.reset();
0146     m_surface.destroyResources();
0147 }
0148 }