File indexing completed on 2024-11-10 04:56:28

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_surface.h"
0010 
0011 #include "config-kwin.h"
0012 #include "core/colortransformation.h"
0013 #include "core/graphicsbufferview.h"
0014 #include "core/iccprofile.h"
0015 #include "drm_egl_backend.h"
0016 #include "drm_gpu.h"
0017 #include "drm_logging.h"
0018 #include "icc_shader.h"
0019 #include "opengl/eglnativefence.h"
0020 #include "opengl/eglswapchain.h"
0021 #include "opengl/gllut.h"
0022 #include "opengl/glrendertimequery.h"
0023 #include "platformsupport/scenes/qpainter/qpainterswapchain.h"
0024 #include "utils/drm_format_helper.h"
0025 
0026 #include <drm_fourcc.h>
0027 #include <errno.h>
0028 #include <gbm.h>
0029 #include <unistd.h>
0030 
0031 namespace KWin
0032 {
0033 
0034 static const QList<uint64_t> linearModifier = {DRM_FORMAT_MOD_LINEAR};
0035 static const QList<uint64_t> implicitModifier = {DRM_FORMAT_MOD_INVALID};
0036 static const QList<uint32_t> cpuCopyFormats = {DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888};
0037 
0038 static const bool bufferAgeEnabled = qEnvironmentVariable("KWIN_USE_BUFFER_AGE") != QStringLiteral("0");
0039 
0040 static gbm_format_name_desc formatName(uint32_t format)
0041 {
0042     gbm_format_name_desc ret;
0043     gbm_format_get_name(format, &ret);
0044     return ret;
0045 }
0046 
0047 EglGbmLayerSurface::EglGbmLayerSurface(DrmGpu *gpu, EglGbmBackend *eglBackend, BufferTarget target, FormatOption formatOption)
0048     : m_gpu(gpu)
0049     , m_eglBackend(eglBackend)
0050     , m_requestedBufferTarget(target)
0051     , m_formatOption(formatOption)
0052 {
0053 }
0054 
0055 EglGbmLayerSurface::~EglGbmLayerSurface() = default;
0056 
0057 EglGbmLayerSurface::Surface::~Surface()
0058 {
0059     if (importContext) {
0060         importContext->makeCurrent();
0061         importGbmSwapchain.reset();
0062         importedTextureCache.clear();
0063         importContext.reset();
0064     }
0065     if (context) {
0066         context->makeCurrent();
0067     }
0068 }
0069 
0070 void EglGbmLayerSurface::destroyResources()
0071 {
0072     m_surface = {};
0073     m_oldSurface = {};
0074 }
0075 
0076 std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::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)
0077 {
0078     if (!checkSurface(bufferSize, formats)) {
0079         return std::nullopt;
0080     }
0081 
0082     if (!m_eglBackend->contextObject()->makeCurrent()) {
0083         return std::nullopt;
0084     }
0085 
0086     auto slot = m_surface->gbmSwapchain->acquire();
0087     if (!slot) {
0088         return std::nullopt;
0089     }
0090 
0091     if (slot->framebuffer()->colorAttachment()->contentTransform() != transformation) {
0092         m_surface->damageJournal.clear();
0093     }
0094     slot->framebuffer()->colorAttachment()->setContentTransform(transformation);
0095     m_surface->currentSlot = slot;
0096 
0097     if (m_surface->targetColorDescription != colorDescription || m_surface->channelFactors != channelFactors
0098         || m_surface->colormanagementEnabled != enableColormanagement || m_surface->iccProfile != iccProfile) {
0099         m_surface->damageJournal.clear();
0100         m_surface->colormanagementEnabled = enableColormanagement;
0101         m_surface->targetColorDescription = colorDescription;
0102         m_surface->channelFactors = channelFactors;
0103         m_surface->iccProfile = iccProfile;
0104         if (iccProfile) {
0105             if (!m_surface->iccShader) {
0106                 m_surface->iccShader = std::make_unique<IccShader>();
0107             }
0108         } else {
0109             m_surface->iccShader.reset();
0110         }
0111         if (enableColormanagement) {
0112             m_surface->intermediaryColorDescription = ColorDescription(colorDescription.colorimetry(), NamedTransferFunction::linear,
0113                                                                        colorDescription.sdrBrightness(), colorDescription.minHdrBrightness(),
0114                                                                        colorDescription.maxFrameAverageBrightness(), colorDescription.maxHdrHighlightBrightness(),
0115                                                                        colorDescription.sdrColorimetry());
0116         } else {
0117             m_surface->intermediaryColorDescription = colorDescription;
0118         }
0119     }
0120 
0121     const QRegion repaint = bufferAgeEnabled ? m_surface->damageJournal.accumulate(slot->age(), infiniteRegion()) : infiniteRegion();
0122     if (enableColormanagement) {
0123         if (!m_surface->shadowBuffer || m_surface->shadowTexture->size() != m_surface->gbmSwapchain->size()) {
0124             m_surface->shadowTexture = GLTexture::allocate(GL_RGBA16F, m_surface->gbmSwapchain->size());
0125             if (!m_surface->shadowTexture) {
0126                 return std::nullopt;
0127             }
0128             m_surface->shadowBuffer = std::make_unique<GLFramebuffer>(m_surface->shadowTexture.get());
0129         }
0130         m_surface->shadowTexture->setContentTransform(m_surface->currentSlot->framebuffer()->colorAttachment()->contentTransform());
0131         m_surface->renderStart = std::chrono::steady_clock::now();
0132         m_surface->timeQuery->begin();
0133         return OutputLayerBeginFrameInfo{
0134             .renderTarget = RenderTarget(m_surface->shadowBuffer.get(), m_surface->intermediaryColorDescription),
0135             .repaint = repaint,
0136         };
0137     } else {
0138         m_surface->shadowTexture.reset();
0139         m_surface->shadowBuffer.reset();
0140         m_surface->renderStart = std::chrono::steady_clock::now();
0141         m_surface->timeQuery->begin();
0142         return OutputLayerBeginFrameInfo{
0143             .renderTarget = RenderTarget(m_surface->currentSlot->framebuffer()),
0144             .repaint = repaint,
0145         };
0146     }
0147 }
0148 
0149 bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion)
0150 {
0151     if (m_surface->colormanagementEnabled) {
0152         GLFramebuffer *fbo = m_surface->currentSlot->framebuffer();
0153         GLFramebuffer::pushFramebuffer(fbo);
0154         ShaderBinder binder = m_surface->iccShader ? ShaderBinder(m_surface->iccShader->shader()) : ShaderBinder(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace);
0155         if (m_surface->iccShader) {
0156             m_surface->iccShader->setUniforms(m_surface->iccProfile, m_surface->intermediaryColorDescription.sdrBrightness(), m_surface->channelFactors);
0157         } else {
0158             QMatrix4x4 ctm;
0159             ctm(0, 0) = m_surface->channelFactors.x();
0160             ctm(1, 1) = m_surface->channelFactors.y();
0161             ctm(2, 2) = m_surface->channelFactors.z();
0162             binder.shader()->setUniform(GLShader::Mat4Uniform::ColorimetryTransformation, ctm);
0163             binder.shader()->setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(m_surface->intermediaryColorDescription.transferFunction()));
0164             binder.shader()->setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(m_surface->targetColorDescription.transferFunction()));
0165             binder.shader()->setUniform(GLShader::FloatUniform::SdrBrightness, m_surface->intermediaryColorDescription.sdrBrightness());
0166             binder.shader()->setUniform(GLShader::FloatUniform::MaxHdrBrightness, m_surface->intermediaryColorDescription.maxHdrHighlightBrightness());
0167         }
0168         QMatrix4x4 mat;
0169         mat.scale(1, -1);
0170         mat *= fbo->colorAttachment()->contentTransform().toMatrix();
0171         mat.scale(1, -1);
0172         mat.ortho(QRectF(QPointF(), fbo->size()));
0173         binder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mat);
0174         glDisable(GL_BLEND);
0175         m_surface->shadowTexture->render(m_surface->gbmSwapchain->size());
0176         GLFramebuffer::popFramebuffer();
0177     }
0178     m_surface->damageJournal.add(damagedRegion);
0179     m_surface->gbmSwapchain->release(m_surface->currentSlot);
0180     m_surface->timeQuery->end();
0181     glFlush();
0182     EGLNativeFence sourceFence(m_eglBackend->eglDisplayObject());
0183     if (!sourceFence.isValid()) {
0184         // llvmpipe doesn't do synchronization properly: https://gitlab.freedesktop.org/mesa/mesa/-/issues/9375
0185         // and NVidia doesn't support implicit sync
0186         glFinish();
0187     }
0188     const auto buffer = importBuffer(m_surface.get(), m_surface->currentSlot.get(), sourceFence.fileDescriptor());
0189     m_surface->renderEnd = std::chrono::steady_clock::now();
0190     if (buffer) {
0191         m_surface->currentFramebuffer = buffer;
0192         return true;
0193     } else {
0194         return false;
0195     }
0196 }
0197 
0198 std::chrono::nanoseconds EglGbmLayerSurface::queryRenderTime() const
0199 {
0200     if (!m_surface) {
0201         return std::chrono::nanoseconds::zero();
0202     }
0203     const auto cpuTime = m_surface->renderEnd - m_surface->renderStart;
0204     if (m_surface->timeQuery) {
0205         m_eglBackend->makeCurrent();
0206         auto gpuTime = m_surface->timeQuery->result();
0207         if (m_surface->importTimeQuery && m_eglBackend->contextForGpu(m_gpu)->makeCurrent()) {
0208             gpuTime += m_surface->importTimeQuery->result();
0209         }
0210         return std::max(gpuTime, cpuTime);
0211     } else {
0212         return cpuTime;
0213     }
0214 }
0215 
0216 EglGbmBackend *EglGbmLayerSurface::eglBackend() const
0217 {
0218     return m_eglBackend;
0219 }
0220 
0221 std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::currentBuffer() const
0222 {
0223     return m_surface ? m_surface->currentFramebuffer : nullptr;
0224 }
0225 
0226 const ColorDescription &EglGbmLayerSurface::colorDescription() const
0227 {
0228     if (m_surface) {
0229         return m_surface->shadowTexture ? m_surface->intermediaryColorDescription : m_surface->targetColorDescription;
0230     } else {
0231         return ColorDescription::sRGB;
0232     }
0233 }
0234 
0235 bool EglGbmLayerSurface::doesSurfaceFit(const QSize &size, const QMap<uint32_t, QList<uint64_t>> &formats) const
0236 {
0237     return doesSurfaceFit(m_surface.get(), size, formats);
0238 }
0239 
0240 std::shared_ptr<GLTexture> EglGbmLayerSurface::texture() const
0241 {
0242     if (m_surface) {
0243         return m_surface->shadowTexture ? m_surface->shadowTexture : m_surface->currentSlot->texture();
0244     } else {
0245         return nullptr;
0246     }
0247 }
0248 
0249 std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::renderTestBuffer(const QSize &bufferSize, const QMap<uint32_t, QList<uint64_t>> &formats)
0250 {
0251     if (checkSurface(bufferSize, formats)) {
0252         return m_surface->currentFramebuffer;
0253     } else {
0254         return nullptr;
0255     }
0256 }
0257 
0258 void EglGbmLayerSurface::forgetDamage()
0259 {
0260     if (m_surface) {
0261         m_surface->damageJournal.clear();
0262     }
0263 }
0264 
0265 bool EglGbmLayerSurface::checkSurface(const QSize &size, const QMap<uint32_t, QList<uint64_t>> &formats)
0266 {
0267     if (doesSurfaceFit(m_surface.get(), size, formats)) {
0268         return true;
0269     }
0270     if (doesSurfaceFit(m_oldSurface.get(), size, formats)) {
0271         m_surface = std::move(m_oldSurface);
0272         return true;
0273     }
0274     if (auto newSurface = createSurface(size, formats)) {
0275         m_oldSurface = std::move(m_surface);
0276         if (m_oldSurface) {
0277             m_oldSurface->damageJournal.clear(); // TODO: Use absolute frame sequence numbers for indexing the DamageJournal
0278         }
0279         m_surface = std::move(newSurface);
0280         return true;
0281     }
0282     return false;
0283 }
0284 
0285 bool EglGbmLayerSurface::doesSurfaceFit(Surface *surface, const QSize &size, const QMap<uint32_t, QList<uint64_t>> &formats) const
0286 {
0287     if (!surface || !surface->gbmSwapchain || surface->gbmSwapchain->size() != size) {
0288         return false;
0289     }
0290     switch (surface->importMode) {
0291     case MultiGpuImportMode::None:
0292     case MultiGpuImportMode::Dmabuf:
0293     case MultiGpuImportMode::LinearDmabuf: {
0294         const auto format = surface->gbmSwapchain->format();
0295         return formats.contains(format) && (surface->gbmSwapchain->modifier() == DRM_FORMAT_MOD_INVALID || formats[format].contains(surface->gbmSwapchain->modifier()));
0296     }
0297     case MultiGpuImportMode::DumbBuffer:
0298         return formats.contains(surface->importDumbSwapchain->format());
0299     case MultiGpuImportMode::Egl: {
0300         const auto format = surface->importGbmSwapchain->format();
0301         return formats.contains(format) && (surface->importGbmSwapchain->modifier() == DRM_FORMAT_MOD_INVALID || formats[format].contains(surface->importGbmSwapchain->modifier()));
0302     }
0303     }
0304     Q_UNREACHABLE();
0305 }
0306 
0307 std::unique_ptr<EglGbmLayerSurface::Surface> EglGbmLayerSurface::createSurface(const QSize &size, const QMap<uint32_t, QList<uint64_t>> &formats) const
0308 {
0309     QList<FormatInfo> preferredFormats;
0310     QList<FormatInfo> fallbackFormats;
0311     for (auto it = formats.begin(); it != formats.end(); it++) {
0312         const auto format = FormatInfo::get(it.key());
0313         if (format.has_value() && format->bitsPerColor >= 8) {
0314             if (format->bitsPerPixel <= 32) {
0315                 preferredFormats.push_back(format.value());
0316             } else {
0317                 fallbackFormats.push_back(format.value());
0318             }
0319         }
0320     }
0321 
0322     // special case: the cursor plane needs linear, but not all GPUs (NVidia) can render to linear
0323     auto bufferTarget = m_requestedBufferTarget;
0324     if (m_gpu == m_eglBackend->gpu()) {
0325         const auto checkSurfaceNeedsLinear = [&formats](const FormatInfo &fmt) {
0326             const auto &mods = formats[fmt.drmFormat];
0327             return std::all_of(mods.cbegin(), mods.cend(), [](const auto &mod) {
0328                 return mod == DRM_FORMAT_MOD_LINEAR;
0329             });
0330         };
0331         const bool needsLinear =
0332             std::all_of(preferredFormats.cbegin(), preferredFormats.cend(), checkSurfaceNeedsLinear) && std::all_of(fallbackFormats.cbegin(), fallbackFormats.cend(), checkSurfaceNeedsLinear);
0333         if (needsLinear) {
0334             const auto renderFormats = m_eglBackend->eglDisplayObject()->allSupportedDrmFormats();
0335             const auto checkFormatSupportsLinearRender = [&renderFormats](const auto &formatInfo) {
0336                 const auto it = renderFormats.constFind(formatInfo.drmFormat);
0337                 return it != renderFormats.cend() && it->nonExternalOnlyModifiers.contains(DRM_FORMAT_MOD_LINEAR);
0338             };
0339             const bool noLinearSupport =
0340                 std::none_of(preferredFormats.cbegin(), preferredFormats.cend(), checkFormatSupportsLinearRender) && std::none_of(fallbackFormats.cbegin(), fallbackFormats.cend(), checkFormatSupportsLinearRender);
0341             if (noLinearSupport) {
0342                 bufferTarget = BufferTarget::Dumb;
0343             }
0344         }
0345     }
0346 
0347     const auto sort = [this](const auto &lhs, const auto &rhs) {
0348         if (lhs.drmFormat == rhs.drmFormat) {
0349             // prefer having an alpha channel
0350             return lhs.alphaBits > rhs.alphaBits;
0351         } else if (m_eglBackend->prefer10bpc() && ((lhs.bitsPerColor == 10) != (rhs.bitsPerColor == 10))) {
0352             // prefer 10bpc / 30bpp formats
0353             return lhs.bitsPerColor == 10;
0354         } else {
0355             // fallback: prefer formats with lower bandwidth requirements
0356             return lhs.bitsPerPixel < rhs.bitsPerPixel;
0357         }
0358     };
0359     const auto doTestFormats = [this, &size, &formats, bufferTarget](const QList<FormatInfo> &gbmFormats, MultiGpuImportMode importMode) -> std::unique_ptr<Surface> {
0360         for (const auto &format : gbmFormats) {
0361             if (m_formatOption == FormatOption::RequireAlpha && format.alphaBits == 0) {
0362                 continue;
0363             }
0364             auto surface = createSurface(size, format.drmFormat, formats[format.drmFormat], importMode, bufferTarget);
0365             if (surface) {
0366                 return surface;
0367             }
0368         }
0369         return nullptr;
0370     };
0371     const auto testFormats = [this, &sort, &doTestFormats](QList<FormatInfo> &formats) -> std::unique_ptr<Surface> {
0372         std::sort(formats.begin(), formats.end(), sort);
0373         if (m_gpu == m_eglBackend->gpu()) {
0374             return doTestFormats(formats, MultiGpuImportMode::None);
0375         }
0376         if (auto surface = doTestFormats(formats, MultiGpuImportMode::Egl)) {
0377             qCDebug(KWIN_DRM) << "chose egl import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier();
0378             return surface;
0379         }
0380         if (auto surface = doTestFormats(formats, MultiGpuImportMode::Dmabuf)) {
0381             qCDebug(KWIN_DRM) << "chose dmabuf import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier();
0382             return surface;
0383         }
0384         if (auto surface = doTestFormats(formats, MultiGpuImportMode::LinearDmabuf)) {
0385             qCDebug(KWIN_DRM) << "chose linear dmabuf import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier();
0386             return surface;
0387         }
0388         if (auto surface = doTestFormats(formats, MultiGpuImportMode::DumbBuffer)) {
0389             qCDebug(KWIN_DRM) << "chose cpu import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier();
0390             return surface;
0391         }
0392         return nullptr;
0393     };
0394     if (auto ret = testFormats(preferredFormats)) {
0395         return ret;
0396     } else if (auto ret = testFormats(fallbackFormats)) {
0397         return ret;
0398     } else {
0399         return nullptr;
0400     }
0401 }
0402 
0403 static QList<uint64_t> filterModifiers(const QList<uint64_t> &one, const QList<uint64_t> &two)
0404 {
0405     QList<uint64_t> ret = one;
0406     ret.erase(std::remove_if(ret.begin(), ret.end(), [&two](uint64_t mod) {
0407                   return !two.contains(mod);
0408               }),
0409               ret.end());
0410     return ret;
0411 }
0412 
0413 std::unique_ptr<EglGbmLayerSurface::Surface> EglGbmLayerSurface::createSurface(const QSize &size, uint32_t format, const QList<uint64_t> &modifiers, MultiGpuImportMode importMode, BufferTarget bufferTarget) const
0414 {
0415     const bool cpuCopy = importMode == MultiGpuImportMode::DumbBuffer || bufferTarget == BufferTarget::Dumb;
0416     QList<uint64_t> renderModifiers;
0417     auto ret = std::make_unique<Surface>();
0418     const auto drmFormat = m_eglBackend->eglDisplayObject()->allSupportedDrmFormats()[format];
0419     if (importMode == MultiGpuImportMode::Egl) {
0420         ret->importContext = m_eglBackend->contextForGpu(m_gpu);
0421         if (!ret->importContext || ret->importContext->isSoftwareRenderer()) {
0422             return nullptr;
0423         }
0424         const auto importDrmFormat = ret->importContext->displayObject()->allSupportedDrmFormats()[format];
0425         renderModifiers = filterModifiers(importDrmFormat.allModifiers,
0426                                           drmFormat.nonExternalOnlyModifiers);
0427         // transferring non-linear buffers with implicit modifiers between GPUs is likely to yield wrong results
0428         renderModifiers.removeAll(DRM_FORMAT_MOD_INVALID);
0429     } else if (cpuCopy) {
0430         if (!cpuCopyFormats.contains(format)) {
0431             return nullptr;
0432         }
0433         renderModifiers = drmFormat.nonExternalOnlyModifiers;
0434     } else {
0435         renderModifiers = filterModifiers(modifiers, drmFormat.nonExternalOnlyModifiers);
0436     }
0437     if (renderModifiers.empty()) {
0438         return nullptr;
0439     }
0440     ret->context = m_eglBackend->contextForGpu(m_eglBackend->gpu());
0441     ret->bufferTarget = bufferTarget;
0442     ret->importMode = importMode;
0443     ret->gbmSwapchain = createGbmSwapchain(m_eglBackend->gpu(), m_eglBackend->contextObject(), size, format, renderModifiers, importMode, bufferTarget);
0444     if (!ret->gbmSwapchain) {
0445         return nullptr;
0446     }
0447     if (cpuCopy) {
0448         ret->importDumbSwapchain = std::make_unique<QPainterSwapchain>(m_gpu->graphicsBufferAllocator(), size, format);
0449     } else if (importMode == MultiGpuImportMode::Egl) {
0450         ret->importGbmSwapchain = createGbmSwapchain(m_gpu, ret->importContext.get(), size, format, modifiers, MultiGpuImportMode::None, BufferTarget::Normal);
0451         if (!ret->importGbmSwapchain) {
0452             return nullptr;
0453         }
0454         ret->importTimeQuery = std::make_unique<GLRenderTimeQuery>();
0455     }
0456     ret->timeQuery = std::make_unique<GLRenderTimeQuery>();
0457     if (!doRenderTestBuffer(ret.get())) {
0458         return nullptr;
0459     }
0460     return ret;
0461 }
0462 
0463 std::shared_ptr<EglSwapchain> EglGbmLayerSurface::createGbmSwapchain(DrmGpu *gpu, EglContext *context, const QSize &size, uint32_t format, const QList<uint64_t> &modifiers, MultiGpuImportMode importMode, BufferTarget bufferTarget) const
0464 {
0465     static bool modifiersEnvSet = false;
0466     static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
0467     bool allowModifiers = (m_gpu->addFB2ModifiersSupported() || importMode == MultiGpuImportMode::Egl || importMode == MultiGpuImportMode::DumbBuffer) && (!modifiersEnvSet || (modifiersEnvSet && modifiersEnv)) && modifiers != implicitModifier;
0468 #if !HAVE_GBM_BO_GET_FD_FOR_PLANE
0469     allowModifiers &= m_gpu == gpu;
0470 #endif
0471     const bool linearSupported = modifiers.contains(DRM_FORMAT_MOD_LINEAR);
0472     const bool preferLinear = importMode == MultiGpuImportMode::DumbBuffer || bufferTarget == BufferTarget::Linear;
0473     const bool forceLinear = importMode == MultiGpuImportMode::LinearDmabuf || (importMode != MultiGpuImportMode::None && importMode != MultiGpuImportMode::DumbBuffer && !allowModifiers);
0474     if (forceLinear && !linearSupported) {
0475         return nullptr;
0476     }
0477     if (linearSupported && (preferLinear || forceLinear)) {
0478         if (const auto swapchain = EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, linearModifier)) {
0479             return swapchain;
0480         } else if (forceLinear) {
0481             return nullptr;
0482         }
0483     }
0484 
0485     if (allowModifiers) {
0486         if (auto swapchain = EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, modifiers)) {
0487             return swapchain;
0488         }
0489     }
0490 
0491     return EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, implicitModifier);
0492 }
0493 
0494 std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::doRenderTestBuffer(Surface *surface) const
0495 {
0496     auto slot = surface->gbmSwapchain->acquire();
0497     if (!slot) {
0498         return nullptr;
0499     }
0500     if (const auto ret = importBuffer(surface, slot.get(), FileDescriptor{})) {
0501         surface->currentSlot = slot;
0502         surface->currentFramebuffer = ret;
0503         return ret;
0504     } else {
0505         return nullptr;
0506     }
0507 }
0508 
0509 std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importBuffer(Surface *surface, EglSwapchainSlot *slot, const FileDescriptor &readFence) const
0510 {
0511     if (surface->bufferTarget == BufferTarget::Dumb || surface->importMode == MultiGpuImportMode::DumbBuffer) {
0512         return importWithCpu(surface, slot);
0513     } else if (surface->importMode == MultiGpuImportMode::Egl) {
0514         return importWithEgl(surface, slot->buffer(), readFence);
0515     } else {
0516         const auto ret = m_gpu->importBuffer(slot->buffer(), readFence.duplicate());
0517         if (!ret) {
0518             qCWarning(KWIN_DRM, "Failed to create framebuffer: %s", strerror(errno));
0519         }
0520         return ret;
0521     }
0522 }
0523 
0524 std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithEgl(Surface *surface, GraphicsBuffer *sourceBuffer, const FileDescriptor &readFence) const
0525 {
0526     Q_ASSERT(surface->importGbmSwapchain);
0527 
0528     const auto display = m_eglBackend->displayForGpu(m_gpu);
0529     // older versions of the NVidia proprietary driver support neither implicit sync nor EGL_ANDROID_native_fence_sync
0530     if (!readFence.isValid() || !display->supportsNativeFence()) {
0531         glFinish();
0532     }
0533 
0534     if (!surface->importContext->makeCurrent()) {
0535         return nullptr;
0536     }
0537     surface->importTimeQuery->begin();
0538 
0539     if (readFence.isValid()) {
0540         const auto destinationFence = EGLNativeFence::importFence(surface->importContext->displayObject(), readFence.duplicate());
0541         destinationFence.waitSync();
0542     }
0543 
0544     auto &sourceTexture = surface->importedTextureCache[sourceBuffer];
0545     if (!sourceTexture) {
0546         sourceTexture = surface->importContext->importDmaBufAsTexture(*sourceBuffer->dmabufAttributes());
0547     }
0548     if (!sourceTexture) {
0549         qCWarning(KWIN_DRM, "failed to import the source texture!");
0550         return nullptr;
0551     }
0552     auto slot = surface->importGbmSwapchain->acquire();
0553     if (!slot) {
0554         qCWarning(KWIN_DRM, "failed to import the local texture!");
0555         return nullptr;
0556     }
0557 
0558     GLFramebuffer *fbo = slot->framebuffer();
0559     glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle());
0560     glViewport(0, 0, fbo->size().width(), fbo->size().height());
0561 
0562     const auto shader = surface->importContext->shaderManager()->pushShader(sourceTexture->target() == GL_TEXTURE_EXTERNAL_OES ? ShaderTrait::MapExternalTexture : ShaderTrait::MapTexture);
0563     QMatrix4x4 mat;
0564     mat.scale(1, -1);
0565     mat.ortho(QRect(QPoint(), fbo->size()));
0566     shader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mat);
0567 
0568     sourceTexture->bind();
0569     sourceTexture->render(fbo->size());
0570     sourceTexture->unbind();
0571 
0572     glBindFramebuffer(GL_FRAMEBUFFER, 0);
0573 
0574     surface->importContext->shaderManager()->popShader();
0575     glFlush();
0576     EGLNativeFence endFence(display);
0577     if (!endFence.isValid()) {
0578         glFinish();
0579     }
0580     surface->importGbmSwapchain->release(slot);
0581     surface->importTimeQuery->end();
0582 
0583     // restore the old context
0584     m_eglBackend->makeCurrent();
0585     return m_gpu->importBuffer(slot->buffer(), endFence.fileDescriptor().duplicate());
0586 }
0587 
0588 std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithCpu(Surface *surface, EglSwapchainSlot *source) const
0589 {
0590     Q_ASSERT(surface->importDumbSwapchain);
0591     const auto slot = surface->importDumbSwapchain->acquire();
0592     if (!slot) {
0593         qCWarning(KWIN_DRM) << "EglGbmLayerSurface::importWithCpu: failed to get a target dumb buffer";
0594         return nullptr;
0595     }
0596     const auto size = source->buffer()->size();
0597     const qsizetype srcStride = 4 * size.width();
0598     GLFramebuffer::pushFramebuffer(source->framebuffer());
0599     QImage *const dst = slot->view()->image();
0600     if (dst->bytesPerLine() == srcStride) {
0601         glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst->bits());
0602     } else {
0603         // there's padding, need to copy line by line
0604         if (surface->cpuCopyCache.size() != dst->size()) {
0605             surface->cpuCopyCache = QImage(dst->size(), QImage::Format_RGBA8888);
0606         }
0607         glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface->cpuCopyCache.bits());
0608         for (int i = 0; i < dst->height(); i++) {
0609             std::memcpy(dst->scanLine(i), surface->cpuCopyCache.scanLine(i), srcStride);
0610         }
0611     }
0612     GLFramebuffer::popFramebuffer();
0613 
0614     const auto ret = m_gpu->importBuffer(slot->buffer(), FileDescriptor{});
0615     if (!ret) {
0616         qCWarning(KWIN_DRM, "Failed to create a framebuffer: %s", strerror(errno));
0617     }
0618     surface->importDumbSwapchain->release(slot);
0619     return ret;
0620 }
0621 }
0622 
0623 #include "moc_drm_egl_layer_surface.cpp"