From ccc73d7ef0a1000a94d9c4cbf53d80966dd8ecf8 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Fri, 23 Jun 2023 18:34:22 +0200 Subject: [PATCH] backends/drm: fix multi gpu with NVidia as primary NVidia GPUs can't render to linear buffers, so we can't force a linear modifier for CPU rendering. The proprietary NVidia driver also can't map gbm buffers with all formats and modifiers, so instead of mapping the gbm buffer, this patch changes KWin to instead use glReadPixels for accessing the source buffer --- src/backends/drm/drm_egl_layer_surface.cpp | 87 +++++++++++++------ src/backends/drm/drm_egl_layer_surface.h | 5 +- .../scenes/opengl/eglswapchain.cpp | 6 ++ 3 files changed, 68 insertions(+), 30 deletions(-) diff --git a/src/backends/drm/drm_egl_layer_surface.cpp b/src/backends/drm/drm_egl_layer_surface.cpp index 1812adcb13..3e4562452f 100644 --- a/src/backends/drm/drm_egl_layer_surface.cpp +++ b/src/backends/drm/drm_egl_layer_surface.cpp @@ -28,6 +28,7 @@ namespace KWin static const QVector linearModifier = {DRM_FORMAT_MOD_LINEAR}; static const QVector implicitModifier = {DRM_FORMAT_MOD_INVALID}; +static const QVector cpuCopyFormats = {DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888}; static gbm_format_name_desc formatName(uint32_t format) { @@ -133,7 +134,7 @@ bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion) m_surface.damageJournal.add(damagedRegion); m_surface.gbmSwapchain->release(m_surface.currentSlot); glFlush(); - const auto buffer = importBuffer(m_surface, m_surface.currentSlot->buffer()); + const auto buffer = importBuffer(m_surface, m_surface.currentSlot.get()); if (buffer) { m_surface.currentFramebuffer = buffer; return true; @@ -272,15 +273,37 @@ std::optional EglGbmLayerSurface::createSurface(con } } +static QVector filterModifiers(const QVector &one, const QVector &two) +{ + QVector ret = one; + ret.erase(std::remove_if(ret.begin(), ret.end(), [&two](uint64_t mod) { + return !two.contains(mod); + }), + ret.end()); + return ret; +} + std::optional EglGbmLayerSurface::createSurface(const QSize &size, uint32_t format, const QVector &modifiers, MultiGpuImportMode importMode) const { - QVector renderModifiers = modifiers; + const bool cpuCopy = importMode == MultiGpuImportMode::DumbBuffer || m_bufferTarget == BufferTarget::Dumb; + QVector renderModifiers; if (importMode == MultiGpuImportMode::Egl) { const auto context = m_eglBackend->contextForGpu(m_gpu); if (!context || context->isSoftwareRenderer()) { return std::nullopt; } - renderModifiers = context->displayObject()->allSupportedDrmFormats()[format]; + renderModifiers = filterModifiers(context->displayObject()->allSupportedDrmFormats()[format], + m_eglBackend->eglDisplayObject()->supportedDrmFormats().value(format)); + } else if (cpuCopy) { + if (!cpuCopyFormats.contains(format)) { + return std::nullopt; + } + renderModifiers = m_eglBackend->eglDisplayObject()->supportedDrmFormats().value(format); + } else { + renderModifiers = filterModifiers(modifiers, m_eglBackend->eglDisplayObject()->supportedDrmFormats().value(format)); + } + if (renderModifiers.empty()) { + return std::nullopt; } Surface ret; ret.importMode = importMode; @@ -289,7 +312,7 @@ std::optional EglGbmLayerSurface::createSurface(con if (!ret.gbmSwapchain) { return std::nullopt; } - if (importMode == MultiGpuImportMode::DumbBuffer || m_bufferTarget == BufferTarget::Dumb) { + if (cpuCopy) { ret.importDumbSwapchain = std::make_shared(m_gpu->graphicsBufferAllocator(), size, format); } else if (importMode == MultiGpuImportMode::Egl) { ret.importGbmSwapchain = createGbmSwapchain(m_gpu, m_eglBackend->contextForGpu(m_gpu), size, format, modifiers, false); @@ -303,7 +326,7 @@ std::optional EglGbmLayerSurface::createSurface(con return ret; } -std::shared_ptr EglGbmLayerSurface::createGbmSwapchain(DrmGpu *gpu, EglContext *context, const QSize &size, uint32_t format, const QVector &modifiers, bool forceLinear) const +std::shared_ptr EglGbmLayerSurface::createGbmSwapchain(DrmGpu *gpu, EglContext *context, const QSize &size, uint32_t format, const QVector &modifiers, bool preferLinear) const { static bool modifiersEnvSet = false; static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0; @@ -311,9 +334,17 @@ std::shared_ptr EglGbmLayerSurface::createGbmSwapchain(DrmGpu *gpu #if !HAVE_GBM_BO_GET_FD_FOR_PLANE allowModifiers &= m_gpu == gpu; #endif - - if (forceLinear || (m_gpu != gpu && !allowModifiers)) { - return EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, linearModifier); + const bool linearSupported = modifiers.contains(DRM_FORMAT_MOD_LINEAR); + const bool forceLinear = m_gpu != gpu && !allowModifiers; + if (forceLinear && !linearSupported) { + return nullptr; + } + if (linearSupported && (preferLinear || forceLinear)) { + if (const auto swapchain = EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, linearModifier)) { + return swapchain; + } else if (forceLinear) { + return nullptr; + } } if (allowModifiers) { @@ -331,7 +362,7 @@ std::shared_ptr EglGbmLayerSurface::doRenderTestBuffer(Surface & if (!slot) { return nullptr; } - if (const auto ret = importBuffer(surface, slot->buffer())) { + if (const auto ret = importBuffer(surface, slot.get())) { surface.currentSlot = slot; surface.currentFramebuffer = ret; return ret; @@ -340,14 +371,14 @@ std::shared_ptr EglGbmLayerSurface::doRenderTestBuffer(Surface & } } -std::shared_ptr EglGbmLayerSurface::importBuffer(Surface &surface, GraphicsBuffer *buffer) const +std::shared_ptr EglGbmLayerSurface::importBuffer(Surface &surface, EglSwapchainSlot *slot) const { if (m_bufferTarget == BufferTarget::Dumb || surface.importMode == MultiGpuImportMode::DumbBuffer) { - return importWithCpu(surface, buffer); + return importWithCpu(surface, slot); } else if (surface.importMode == MultiGpuImportMode::Egl) { - return importWithEgl(surface, buffer); + return importWithEgl(surface, slot->buffer()); } else { - const auto ret = m_gpu->importBuffer(buffer); + const auto ret = m_gpu->importBuffer(slot->buffer()); if (!ret) { qCWarning(KWIN_DRM, "Failed to create framebuffer: %s", strerror(errno)); } @@ -416,31 +447,31 @@ std::shared_ptr EglGbmLayerSurface::importWithEgl(Surface &surfa return m_gpu->importBuffer(slot->buffer()); } -std::shared_ptr EglGbmLayerSurface::importWithCpu(Surface &surface, GraphicsBuffer *sourceBuffer) const +std::shared_ptr EglGbmLayerSurface::importWithCpu(Surface &surface, EglSwapchainSlot *source) const { Q_ASSERT(surface.importDumbSwapchain); - const GraphicsBufferView sourceView(sourceBuffer); - if (sourceView.isNull()) { - qCWarning(KWIN_DRM) << "EglGbmLayerSurface::importWithCpu: failed to map the source buffer"; - return nullptr; - } const auto slot = surface.importDumbSwapchain->acquire(); if (!slot) { qCWarning(KWIN_DRM) << "EglGbmLayerSurface::importWithCpu: failed to get a target dumb buffer"; return nullptr; } - - if (sourceView.image()->bytesPerLine() == slot->view()->image()->bytesPerLine()) { - std::memcpy(slot->view()->image()->bits(), sourceView.image()->bits(), sourceView.image()->sizeInBytes()); + const auto size = source->buffer()->size(); + const qsizetype srcStride = 4 * size.width(); + GLFramebuffer::pushFramebuffer(source->framebuffer()); + QImage *const dst = slot->view()->image(); + if (dst->bytesPerLine() == srcStride) { + glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst->bits()); } else { - // copy line by line - QImage *const src = slot->view()->image(); - QImage *const dst = slot->view()->image(); - const uint64_t lineWidth = std::min(src->bytesPerLine(), dst->bytesPerLine()); - for (int i = 0; i < src->height(); i++) { - std::memcpy(dst->scanLine(i), src->scanLine(i), lineWidth); + // there's padding, need to copy line by line + if (surface.cpuCopyCache.size() != dst->size()) { + surface.cpuCopyCache = QImage(dst->size(), QImage::Format_RGBA8888); + } + glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface.cpuCopyCache.bits()); + for (int i = 0; i < dst->height(); i++) { + std::memcpy(dst->scanLine(i), surface.cpuCopyCache.scanLine(i), srcStride); } } + GLFramebuffer::popFramebuffer(); const auto ret = m_gpu->importBuffer(slot->buffer()); if (!ret) { diff --git a/src/backends/drm/drm_egl_layer_surface.h b/src/backends/drm/drm_egl_layer_surface.h index 19f39e06d5..241bce1720 100644 --- a/src/backends/drm/drm_egl_layer_surface.h +++ b/src/backends/drm/drm_egl_layer_surface.h @@ -85,6 +85,7 @@ private: std::shared_ptr importDumbSwapchain; std::shared_ptr importGbmSwapchain; QHash> importedTextureCache; + QImage cpuCopyCache; MultiGpuImportMode importMode; std::shared_ptr currentFramebuffer; bool forceLinear = false; @@ -96,9 +97,9 @@ private: std::shared_ptr createGbmSwapchain(DrmGpu *gpu, EglContext *context, const QSize &size, uint32_t format, const QVector &modifiers, bool forceLinear) const; std::shared_ptr doRenderTestBuffer(Surface &surface) const; - std::shared_ptr importBuffer(Surface &surface, GraphicsBuffer *sourceBuffer) const; + std::shared_ptr importBuffer(Surface &surface, EglSwapchainSlot *source) const; std::shared_ptr importWithEgl(Surface &surface, GraphicsBuffer *sourceBuffer) const; - std::shared_ptr importWithCpu(Surface &surface, GraphicsBuffer *sourceBuffer) const; + std::shared_ptr importWithCpu(Surface &surface, EglSwapchainSlot *source) const; Surface m_surface; Surface m_oldSurface; diff --git a/src/platformsupport/scenes/opengl/eglswapchain.cpp b/src/platformsupport/scenes/opengl/eglswapchain.cpp index abcf77efc2..1d9c98e86a 100644 --- a/src/platformsupport/scenes/opengl/eglswapchain.cpp +++ b/src/platformsupport/scenes/opengl/eglswapchain.cpp @@ -102,6 +102,9 @@ std::shared_ptr EglSwapchain::acquire() } auto slot = std::make_shared(m_context, buffer); + if (!slot->framebuffer()->valid()) { + return nullptr; + } m_slots.append(slot); return slot; } @@ -134,6 +137,9 @@ std::shared_ptr EglSwapchain::create(GraphicsBufferAllocator *allo } const QVector> slots{std::make_shared(context, seed)}; + if (!slots.front()->framebuffer()->valid()) { + return nullptr; + } return std::make_shared(std::move(allocator), context, size,