From b14f7959eb5f4d2b690ac26fdfee76abc837240c Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Tue, 21 Mar 2023 18:27:43 +0100 Subject: [PATCH] backends/drm: add another multi gpu fallback With the dmabuf multi-gpu path, a buffer is imported to the secondary GPU and presented directly, but importing a buffer that's usable for scanout is not possible that way on most hardware. To prevent CPU copy from being needed in those cases, this commit introduces a fallback where the buffer is imported for rendering only, and then copied to a local buffer that's presented on the screen. CCBUG: 452219 CCBUG: 465809 --- src/backends/drm/drm_egl_backend.cpp | 116 +++++++++++------- src/backends/drm/drm_egl_backend.h | 8 +- src/backends/drm/drm_egl_layer_surface.cpp | 107 +++++++++++++--- src/backends/drm/drm_egl_layer_surface.h | 10 +- src/composite.cpp | 5 + src/libkwineffects/kwinglutils.h | 3 +- .../scenes/opengl/eglcontext.cpp | 18 +++ .../scenes/opengl/eglcontext.h | 10 +- 8 files changed, 208 insertions(+), 69 deletions(-) diff --git a/src/backends/drm/drm_egl_backend.cpp b/src/backends/drm/drm_egl_backend.cpp index 6aca35dfde..c88fdb77c1 100644 --- a/src/backends/drm/drm_egl_backend.cpp +++ b/src/backends/drm/drm_egl_backend.cpp @@ -48,11 +48,15 @@ EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend) { drmBackend->setRenderBackend(this); setIsDirectRendering(true); + connect(m_backend, &DrmBackend::gpuRemoved, this, [this](DrmGpu *gpu) { + m_contexts.erase(gpu->eglDisplay()); + }); } EglGbmBackend::~EglGbmBackend() { m_backend->releaseBuffers(); + m_contexts.clear(); cleanup(); m_backend->setRenderBackend(nullptr); } @@ -65,23 +69,7 @@ bool EglGbmBackend::initializeEgl() // Use eglGetPlatformDisplayEXT() to get the display pointer // if the implementation supports it. if (!display) { - const bool hasMesaGBM = hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm")); - const bool hasKHRGBM = hasClientExtension(QByteArrayLiteral("EGL_KHR_platform_gbm")); - const GLenum platform = hasMesaGBM ? EGL_PLATFORM_GBM_MESA : EGL_PLATFORM_GBM_KHR; - - if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || (!hasMesaGBM && !hasKHRGBM)) { - setFailed("Missing one or more extensions between EGL_EXT_platform_base, " - "EGL_MESA_platform_gbm, EGL_KHR_platform_gbm"); - return false; - } - - if (!m_backend->primaryGpu()->gbmDevice()) { - setFailed("Could not create gbm device"); - return false; - } - - m_backend->primaryGpu()->setEglDisplay(EglDisplay::create(eglGetPlatformDisplayEXT(platform, m_backend->primaryGpu()->gbmDevice(), nullptr))); - display = m_backend->primaryGpu()->eglDisplay(); + display = createEglDisplay(m_backend->primaryGpu()); if (!display) { return false; } @@ -90,6 +78,25 @@ bool EglGbmBackend::initializeEgl() return true; } +EglDisplay *EglGbmBackend::createEglDisplay(DrmGpu *gpu) const +{ + const bool hasMesaGBM = hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm")); + const bool hasKHRGBM = hasClientExtension(QByteArrayLiteral("EGL_KHR_platform_gbm")); + const GLenum platform = hasMesaGBM ? EGL_PLATFORM_GBM_MESA : EGL_PLATFORM_GBM_KHR; + + if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || (!hasMesaGBM && !hasKHRGBM)) { + qCWarning(KWIN_DRM, "Missing one or more extensions between EGL_EXT_platform_base, EGL_MESA_platform_gbm, EGL_KHR_platform_gbm"); + return nullptr; + } + + if (!gpu->gbmDevice()) { + return nullptr; + } + + gpu->setEglDisplay(EglDisplay::create(eglGetPlatformDisplayEXT(platform, gpu->gbmDevice(), nullptr))); + return gpu->eglDisplay(); +} + void EglGbmBackend::init() { if (!initializeEgl()) { @@ -107,10 +114,29 @@ void EglGbmBackend::init() bool EglGbmBackend::initRenderingContext() { - return initBufferConfigs() && createContext(EGL_NO_CONFIG_KHR) && makeCurrent(); + return initBufferConfigs(eglDisplayObject()) && createContext(EGL_NO_CONFIG_KHR) && makeCurrent(); } -bool EglGbmBackend::initBufferConfigs() +EglContext *EglGbmBackend::contextForGpu(DrmGpu *gpu) +{ + if (gpu == m_backend->primaryGpu()) { + return contextObject(); + } + auto display = gpu->eglDisplay(); + if (!display) { + display = createEglDisplay(gpu); + if (!display) { + return nullptr; + } + } + auto &context = m_contexts[display]; + if (!context) { + context = EglContext::create(display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT); + } + return context.get(); +} + +bool EglGbmBackend::initBufferConfigs(EglDisplay *display) { const EGLint config_attribs[] = { EGL_SURFACE_TYPE, @@ -132,7 +158,7 @@ bool EglGbmBackend::initBufferConfigs() EGLint count; EGLConfig configs[1024]; - if (!eglChooseConfig(eglDisplay(), config_attribs, configs, + if (!eglChooseConfig(display->handle(), config_attribs, configs, sizeof(configs) / sizeof(EGLConfig), &count)) { qCCritical(KWIN_DRM) << "eglChooseConfig failed:" << getEglErrorString(); @@ -140,36 +166,37 @@ bool EglGbmBackend::initBufferConfigs() } // Loop through all configs, choosing the first one that has suitable format. + auto &formats = m_formats[display]; for (EGLint i = 0; i < count; i++) { EGLint gbmFormat; - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_NATIVE_VISUAL_ID, &gbmFormat); + eglGetConfigAttrib(display->handle(), configs[i], EGL_NATIVE_VISUAL_ID, &gbmFormat); GbmFormat format; format.drmFormat = gbmFormat; EGLint red, green, blue; // Query number of bits for color channel - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_RED_SIZE, &red); - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_GREEN_SIZE, &green); - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_BLUE_SIZE, &blue); - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_ALPHA_SIZE, &format.alphaSize); + eglGetConfigAttrib(display->handle(), configs[i], EGL_RED_SIZE, &red); + eglGetConfigAttrib(display->handle(), configs[i], EGL_GREEN_SIZE, &green); + eglGetConfigAttrib(display->handle(), configs[i], EGL_BLUE_SIZE, &blue); + eglGetConfigAttrib(display->handle(), configs[i], EGL_ALPHA_SIZE, &format.alphaSize); format.bpp = red + green + blue; - if (m_formats.contains(gbmFormat)) { + if (formats.contains(gbmFormat)) { continue; } - m_formats[gbmFormat] = format; + formats[gbmFormat] = format; } - if (!m_formats.isEmpty()) { + if (!formats.isEmpty()) { return true; } qCCritical(KWIN_DRM, "Choosing EGL config did not return a supported config. There were %u configs", count); for (EGLint i = 0; i < count; i++) { EGLint gbmFormat, blueSize, redSize, greenSize, alphaSize; - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_NATIVE_VISUAL_ID, &gbmFormat); - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_RED_SIZE, &redSize); - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_GREEN_SIZE, &greenSize); - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_BLUE_SIZE, &blueSize); - eglGetConfigAttrib(eglDisplay(), configs[i], EGL_ALPHA_SIZE, &alphaSize); + eglGetConfigAttrib(display->handle(), configs[i], EGL_NATIVE_VISUAL_ID, &gbmFormat); + eglGetConfigAttrib(display->handle(), configs[i], EGL_RED_SIZE, &redSize); + eglGetConfigAttrib(display->handle(), configs[i], EGL_GREEN_SIZE, &greenSize); + eglGetConfigAttrib(display->handle(), configs[i], EGL_BLUE_SIZE, &blueSize); + eglGetConfigAttrib(display->handle(), configs[i], EGL_ALPHA_SIZE, &alphaSize); gbm_format_name_desc name; gbm_format_get_name(gbmFormat, &name); qCCritical(KWIN_DRM, "EGL config %d has format %s with %d,%d,%d,%d bits for r,g,b,a", i, name.name, redSize, greenSize, blueSize, alphaSize); @@ -210,9 +237,18 @@ std::shared_ptr EglGbmBackend::textureForOutput(Output *output) const std::optional EglGbmBackend::gbmFormatForDrmFormat(uint32_t format) const { - // TODO use a hardcoded lookup table where needed instead? - const auto it = m_formats.constFind(format); - return it == m_formats.constEnd() ? std::optional() : *it; + const auto it = m_formats.find(eglDisplayObject()); + if (it == m_formats.end()) { + return std::nullopt; + } else { + const auto &formats = *it; + const auto formatIt = formats.find(format); + if (formatIt == formats.end()) { + return std::nullopt; + } else { + return *formatIt; + } + } } bool EglGbmBackend::prefer10bpc() const @@ -254,13 +290,7 @@ EGLImageKHR EglGbmBackend::importBufferObjectAsImage(gbm_bo *bo) std::shared_ptr EglGbmBackend::importBufferObjectAsTexture(gbm_bo *bo) { - EGLImageKHR image = importBufferObjectAsImage(bo); - if (image != EGL_NO_IMAGE_KHR) { - return std::make_shared(eglDisplay(), image, GL_RGBA8, QSize(gbm_bo_get_width(bo), gbm_bo_get_height(bo))); - } else { - qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString(); - return nullptr; - } + return importDmaBufAsTexture(dmaBufAttributesForBo(bo)); } } // namespace KWin diff --git a/src/backends/drm/drm_egl_backend.h b/src/backends/drm/drm_egl_backend.h index 6c361aec84..fc91839a77 100644 --- a/src/backends/drm/drm_egl_backend.h +++ b/src/backends/drm/drm_egl_backend.h @@ -73,20 +73,22 @@ public: std::shared_ptr textureForOutput(Output *requestedOutput) const override; - std::shared_ptr testBuffer(DrmAbstractOutput *output); std::optional gbmFormatForDrmFormat(uint32_t format) const; DrmGpu *gpu() const; EGLImageKHR importBufferObjectAsImage(gbm_bo *bo); std::shared_ptr importBufferObjectAsTexture(gbm_bo *bo); + EglContext *contextForGpu(DrmGpu *gpu); private: bool initializeEgl(); - bool initBufferConfigs(); + bool initBufferConfigs(EglDisplay *display); bool initRenderingContext(); + EglDisplay *createEglDisplay(DrmGpu *gpu) const; DrmBackend *m_backend; - QHash m_formats; + std::map> m_contexts; + QHash> m_formats; friend class EglGbmTexture; }; diff --git a/src/backends/drm/drm_egl_layer_surface.cpp b/src/backends/drm/drm_egl_layer_surface.cpp index 5c1f8cceee..33ab840d3e 100644 --- a/src/backends/drm/drm_egl_layer_surface.cpp +++ b/src/backends/drm/drm_egl_layer_surface.cpp @@ -65,7 +65,7 @@ std::optional EglGbmLayerSurface::startRendering(cons if (!checkSurface(bufferSize, formats)) { return std::nullopt; } - if (eglMakeCurrent(m_eglBackend->eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, m_eglBackend->context()) != EGL_TRUE) { + if (!m_eglBackend->contextObject()->makeCurrent()) { return std::nullopt; } @@ -89,7 +89,6 @@ std::optional EglGbmLayerSurface::startRendering(cons } } m_surface.currentBuffer = buffer; - m_surface.texture = texture; return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(fbo.get()), @@ -127,7 +126,7 @@ bool EglGbmLayerSurface::doesSurfaceFit(const QSize &size, const QMap EglGbmLayerSurface::texture() const { - return m_surface.texture; + return m_surface.textureCache[m_surface.currentBuffer->bo()].first; } std::shared_ptr EglGbmLayerSurface::renderTestBuffer(const QSize &bufferSize, const QMap> &formats) @@ -216,6 +215,10 @@ std::optional EglGbmLayerSurface::createSurface(con qCDebug(KWIN_DRM) << "chose linear dmabuf import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier(); return surface; } + if (const auto surface = doTestFormats(formats, MultiGpuImportMode::Egl)) { + qCDebug(KWIN_DRM) << "chose egl import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier(); + return surface; + } if (const auto surface = doTestFormats(formats, MultiGpuImportMode::DumbBuffer)) { qCDebug(KWIN_DRM) << "chose cpu import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier(); return surface; @@ -234,16 +237,29 @@ std::optional EglGbmLayerSurface::createSurface(con std::optional EglGbmLayerSurface::createSurface(const QSize &size, uint32_t format, const QVector &modifiers, MultiGpuImportMode importMode) const { + QVector renderModifiers = modifiers; + if (importMode == MultiGpuImportMode::Egl) { + const auto context = m_eglBackend->contextForGpu(m_gpu); + if (!context || context->isSoftwareRenderer()) { + return std::nullopt; + } + renderModifiers = context->displayObject()->supportedDrmFormats()[format]; + } Surface ret; ret.importMode = importMode; ret.forceLinear = importMode == MultiGpuImportMode::DumbBuffer || importMode == MultiGpuImportMode::LinearDmabuf || m_bufferTarget != BufferTarget::Normal; - ret.gbmSwapchain = createGbmSwapchain(size, format, modifiers, ret.forceLinear); + ret.gbmSwapchain = createGbmSwapchain(m_eglBackend->gpu(), size, format, renderModifiers, ret.forceLinear); if (!ret.gbmSwapchain) { return std::nullopt; } if (importMode == MultiGpuImportMode::DumbBuffer || m_bufferTarget == BufferTarget::Dumb) { - ret.importSwapchain = std::make_shared(m_gpu, size, format); - if (ret.importSwapchain->isEmpty()) { + ret.importDumbSwapchain = std::make_shared(m_gpu, size, format); + if (ret.importDumbSwapchain->isEmpty()) { + return std::nullopt; + } + } else if (importMode == MultiGpuImportMode::Egl) { + ret.importGbmSwapchain = createGbmSwapchain(m_gpu, size, format, modifiers, false); + if (!ret.importGbmSwapchain) { return std::nullopt; } } @@ -253,17 +269,17 @@ std::optional EglGbmLayerSurface::createSurface(con return ret; } -std::shared_ptr EglGbmLayerSurface::createGbmSwapchain(const QSize &size, uint32_t format, const QVector &modifiers, bool forceLinear) const +std::shared_ptr EglGbmLayerSurface::createGbmSwapchain(DrmGpu *gpu, const QSize &size, uint32_t format, const QVector &modifiers, bool forceLinear) const { static bool modifiersEnvSet = false; static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0; - bool allowModifiers = m_gpu->addFB2ModifiersSupported() && (!modifiersEnvSet || (modifiersEnvSet && modifiersEnv)) && !modifiers.isEmpty(); + bool allowModifiers = gpu->addFB2ModifiersSupported() && (!modifiersEnvSet || (modifiersEnvSet && modifiersEnv)) && !modifiers.isEmpty(); #if !HAVE_GBM_BO_GET_FD_FOR_PLANE - allowModifiers &= m_gpu == m_eglBackend->gpu(); + allowModifiers &= m_gpu == gpu; #endif if (allowModifiers) { - const auto ret = GbmSwapchain::createSwapchain(m_eglBackend->gpu(), size, format, forceLinear ? linearModifier : modifiers); + const auto ret = GbmSwapchain::createSwapchain(gpu, size, format, forceLinear ? linearModifier : modifiers); if (const auto surface = std::get_if>(&ret)) { return *surface; } else if (std::get(ret) != GbmSwapchain::Error::ModifiersUnsupported) { @@ -271,13 +287,13 @@ std::shared_ptr EglGbmLayerSurface::createGbmSwapchain(const QSize } } uint32_t gbmFlags = GBM_BO_USE_RENDERING; - if (m_gpu == m_eglBackend->gpu()) { + if (m_gpu == gpu) { gbmFlags |= GBM_BO_USE_SCANOUT; } - if (forceLinear || m_gpu != m_eglBackend->gpu()) { + if (forceLinear || m_gpu != gpu) { gbmFlags |= GBM_BO_USE_LINEAR; } - const auto ret = GbmSwapchain::createSwapchain(m_eglBackend->gpu(), size, format, gbmFlags); + const auto ret = GbmSwapchain::createSwapchain(gpu, size, format, gbmFlags); const auto swapchain = std::get_if>(&ret); return swapchain ? *swapchain : nullptr; } @@ -301,6 +317,8 @@ std::shared_ptr EglGbmLayerSurface::importBuffer(Surface &surfac { if (m_bufferTarget == BufferTarget::Dumb || surface.importMode == MultiGpuImportMode::DumbBuffer) { return importWithCpu(surface, sourceBuffer.get()); + } else if (surface.importMode == MultiGpuImportMode::Egl) { + return importWithEgl(surface, sourceBuffer.get()); } else if (m_gpu != m_eglBackend->gpu()) { return importDmabuf(sourceBuffer.get()); } else { @@ -326,14 +344,73 @@ std::shared_ptr EglGbmLayerSurface::importDmabuf(GbmBuffer *sour return ret; } +std::shared_ptr EglGbmLayerSurface::importWithEgl(Surface &surface, GbmBuffer *sourceBuffer) const +{ + Q_ASSERT(surface.importGbmSwapchain); + + const auto context = m_eglBackend->contextForGpu(m_gpu); + if (!context || context->isSoftwareRenderer()) { + return nullptr; + } + context->makeCurrent(); + auto &sourceTexture = surface.importedTextureCache[sourceBuffer->bo()]; + if (!sourceTexture) { + sourceTexture = context->importDmaBufAsTexture(dmaBufAttributesForBo(sourceBuffer->bo())); + } + if (!sourceTexture) { + qCWarning(KWIN_DRM, "failed to import the source texture!"); + return nullptr; + } + const auto [localBuffer, repaint] = surface.importGbmSwapchain->acquire(); + auto &[texture, fbo] = surface.importTextureCache[localBuffer->bo()]; + if (!texture) { + texture = context->importDmaBufAsTexture(dmaBufAttributesForBo(localBuffer->bo())); + if (!texture) { + qCWarning(KWIN_DRM, "failed to import the local texture!"); + return nullptr; + } + } + if (!fbo) { + fbo = std::make_shared(texture.get()); + if (!fbo->valid()) { + qCWarning(KWIN_DRM, "failed to create the fbo!"); + fbo.reset(); + return nullptr; + } + } + glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle()); + glViewport(0, 0, fbo->size().width(), fbo->size().height()); + glClearColor(0, 1, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + const auto shader = context->shaderManager()->pushShader(ShaderTrait::MapTexture); + QMatrix4x4 mat; + mat.scale(1, -1); + mat.ortho(QRect(QPoint(), fbo->size())); + shader->setUniform(GLShader::ModelViewProjectionMatrix, mat); + + sourceTexture->bind(); + sourceTexture->render(fbo->size(), 1); + sourceTexture->unbind(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + context->shaderManager()->popShader(); + glFlush(); + // restore the old context + context->doneCurrent(); + m_eglBackend->makeCurrent(); + return DrmFramebuffer::createFramebuffer(localBuffer); +} + std::shared_ptr EglGbmLayerSurface::importWithCpu(Surface &surface, GbmBuffer *sourceBuffer) const { - Q_ASSERT(surface.importSwapchain && !surface.importSwapchain->isEmpty()); + Q_ASSERT(surface.importDumbSwapchain && !surface.importDumbSwapchain->isEmpty()); if (!sourceBuffer->map(GBM_BO_TRANSFER_READ)) { qCWarning(KWIN_DRM, "mapping a %s gbm_bo failed: %s", formatName(sourceBuffer->format()).name, strerror(errno)); return nullptr; } - const auto importBuffer = surface.importSwapchain->acquireBuffer(); + const auto importBuffer = surface.importDumbSwapchain->acquireBuffer(); if (sourceBuffer->planeCount() != 1 || sourceBuffer->strides()[0] != importBuffer->strides()[0]) { qCCritical(KWIN_DRM, "stride of gbm_bo (%d) and dumb buffer (%d) with format %s don't match!", sourceBuffer->strides()[0], importBuffer->strides()[0], formatName(sourceBuffer->format()).name); diff --git a/src/backends/drm/drm_egl_layer_surface.h b/src/backends/drm/drm_egl_layer_surface.h index ce01fa19cb..05454596d6 100644 --- a/src/backends/drm/drm_egl_layer_surface.h +++ b/src/backends/drm/drm_egl_layer_surface.h @@ -69,13 +69,16 @@ private: enum class MultiGpuImportMode { Dmabuf, LinearDmabuf, + Egl, DumbBuffer }; struct Surface { std::shared_ptr gbmSwapchain; - std::shared_ptr texture; - std::shared_ptr importSwapchain; + std::shared_ptr importDumbSwapchain; + std::shared_ptr importGbmSwapchain; + QHash> importedTextureCache; + QHash, std::shared_ptr>> importTextureCache; MultiGpuImportMode importMode; std::shared_ptr currentBuffer; std::shared_ptr currentFramebuffer; @@ -86,11 +89,12 @@ private: bool doesSurfaceFit(const Surface &surface, const QSize &size, const QMap> &formats) const; std::optional createSurface(const QSize &size, const QMap> &formats) const; std::optional createSurface(const QSize &size, uint32_t format, const QVector &modifiers, MultiGpuImportMode importMode) const; - std::shared_ptr createGbmSwapchain(const QSize &size, uint32_t format, const QVector &modifiers, bool forceLinear) const; + std::shared_ptr createGbmSwapchain(DrmGpu *gpu, 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, const std::shared_ptr &sourceBuffer) const; std::shared_ptr importDmabuf(GbmBuffer *sourceBuffer) const; + std::shared_ptr importWithEgl(Surface &surface, GbmBuffer *sourceBuffer) const; std::shared_ptr importWithCpu(Surface &surface, GbmBuffer *sourceBuffer) const; Surface m_surface; diff --git a/src/composite.cpp b/src/composite.cpp index cddc611050..8533cee457 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -506,6 +506,11 @@ void Compositor::stop() disconnect(workspace(), &Workspace::outputRemoved, this, &Compositor::removeOutput); } + if (m_backend->compositingType() == OpenGLCompositing) { + // some layers need a context current for destruction + static_cast(m_backend.get())->makeCurrent(); + } + const auto superlayers = m_superlayers; for (auto it = superlayers.begin(); it != superlayers.end(); ++it) { removeSuperLayer(*it); diff --git a/src/libkwineffects/kwinglutils.h b/src/libkwineffects/kwinglutils.h index 629a316785..9e43446408 100644 --- a/src/libkwineffects/kwinglutils.h +++ b/src/libkwineffects/kwinglutils.h @@ -194,6 +194,7 @@ Q_DECLARE_FLAGS(ShaderTraits, ShaderTrait) class KWINGLUTILS_EXPORT ShaderManager { public: + explicit ShaderManager(); ~ShaderManager(); /** @@ -295,8 +296,6 @@ public: static void cleanup(); private: - ShaderManager(); - void bindFragDataLocations(GLShader *shader); void bindAttributeLocations(GLShader *shader) const; diff --git a/src/platformsupport/scenes/opengl/eglcontext.cpp b/src/platformsupport/scenes/opengl/eglcontext.cpp index cdcd557702..5b0df8ba1f 100644 --- a/src/platformsupport/scenes/opengl/eglcontext.cpp +++ b/src/platformsupport/scenes/opengl/eglcontext.cpp @@ -10,6 +10,7 @@ #include "egldisplay.h" #include "kwineglimagetexture.h" #include "kwineglutils_p.h" +#include "kwinglutils.h" #include "utils/common.h" #include "utils/egl_context_attribute_builder.h" @@ -23,6 +24,7 @@ std::unique_ptr EglContext::create(EglDisplay *display, EGLConfig co { auto context = createContext(display, config, sharedContext); if (context) { + eglMakeCurrent(display->handle(), EGL_NO_SURFACE, EGL_NO_SURFACE, context); return std::make_unique(display, config, context); } else { return nullptr; @@ -33,11 +35,23 @@ EglContext::EglContext(EglDisplay *display, EGLConfig config, ::EGLContext conte : m_display(display) , m_handle(context) , m_config(config) + , m_shaderManager(std::make_unique()) { + // It is not legal to not have a vertex array object bound in a core context + // to make code handling old and new OpenGL versions easier, bind a dummy vao that's used for everything + if (!isOpenglES() && hasOpenglExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) { + makeCurrent(); + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + } } EglContext::~EglContext() { + if (m_vao) { + makeCurrent(); + glDeleteVertexArrays(1, &m_vao); + } doneCurrent(); eglDestroyContext(m_display->handle(), m_handle); } @@ -182,4 +196,8 @@ std::shared_ptr EglContext::importDmaBufAsTexture(const DmaBufAttribu } } +ShaderManager *EglContext::shaderManager() const +{ + return m_shaderManager.get(); +} } diff --git a/src/platformsupport/scenes/opengl/eglcontext.h b/src/platformsupport/scenes/opengl/eglcontext.h index 5a5b2a20fa..88eafa7f8f 100644 --- a/src/platformsupport/scenes/opengl/eglcontext.h +++ b/src/platformsupport/scenes/opengl/eglcontext.h @@ -21,6 +21,7 @@ namespace KWin { class EglDisplay; +class ShaderManager; class KWIN_EXPORT EglContext : public OpenGlContext { @@ -36,15 +37,18 @@ public: ::EGLContext handle() const; EGLConfig config() const; bool isValid() const; + ShaderManager *shaderManager() const; static std::unique_ptr create(EglDisplay *display, EGLConfig config, ::EGLContext sharedContext); private: static ::EGLContext createContext(EglDisplay *display, EGLConfig config, ::EGLContext sharedContext); - EglDisplay *m_display = nullptr; - ::EGLContext m_handle = EGL_NO_CONTEXT; - EGLConfig m_config = EGL_NO_CONFIG_KHR; + EglDisplay *const m_display; + const ::EGLContext m_handle; + const EGLConfig m_config; + const std::unique_ptr m_shaderManager; + uint32_t m_vao = 0; }; }