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"