From 16fb2848ed273d9f48b43bc396403da0693c7a9e Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Mon, 15 May 2023 13:18:41 +0200 Subject: [PATCH] libkwineffects: handle GLTexture upload failures This is required for properly dealing with GPU resets --- src/libkwineffects/kwingltexture.cpp | 161 ++++++++---------- src/libkwineffects/kwingltexture.h | 5 +- src/libkwineffects/kwinoffscreenquickview.cpp | 5 +- .../basiceglsurfacetexture_internal.cpp | 4 +- .../opengl/basiceglsurfacetexture_wayland.cpp | 6 +- src/plugins/blur/blur.cpp | 8 +- src/plugins/screencast/screencaststream.cpp | 2 +- src/plugins/screenedge/screenedgeeffect.cpp | 45 +++-- src/plugins/screenedge/screenedgeeffect.h | 8 +- .../startupfeedback/startupfeedback.cpp | 13 +- src/plugins/trackmouse/trackmouse.cpp | 2 +- src/plugins/zoom/zoom.cpp | 5 +- src/scene/imageitem.cpp | 2 +- src/scene/workspacescene_opengl.cpp | 10 +- 14 files changed, 135 insertions(+), 141 deletions(-) diff --git a/src/libkwineffects/kwingltexture.cpp b/src/libkwineffects/kwingltexture.cpp index 865e5a8050..26e0cb1f97 100644 --- a/src/libkwineffects/kwingltexture.cpp +++ b/src/libkwineffects/kwingltexture.cpp @@ -94,97 +94,6 @@ GLTexture::GLTexture(std::unique_ptr &&dd) { } -GLTexture::GLTexture(const QImage &image, GLenum target) - : d_ptr(new GLTexturePrivate()) -{ - Q_D(GLTexture); - - if (image.isNull()) { - return; - } - - d->m_target = target; - - if (d->m_target != GL_TEXTURE_RECTANGLE_ARB) { - d->m_scale.setWidth(1.0 / image.width()); - d->m_scale.setHeight(1.0 / image.height()); - } else { - d->m_scale.setWidth(1.0); - d->m_scale.setHeight(1.0); - } - - d->m_size = image.size(); - setContentTransform(TextureTransform::MirrorY); - d->m_canUseMipmaps = false; - d->m_mipLevels = 1; - - d->updateMatrix(); - - const bool created = create(); - Q_ASSERT(created); - bind(); - - if (!GLPlatform::instance()->isGLES()) { - QImage im; - GLenum internalFormat; - GLenum format; - GLenum type; - - const QImage::Format index = image.format(); - - if (index < sizeof(formatTable) / sizeof(formatTable[0]) && formatTable[index].internalFormat - && !(formatTable[index].type == GL_UNSIGNED_SHORT && !d->s_supportsTexture16Bit)) { - internalFormat = formatTable[index].internalFormat; - format = formatTable[index].format; - type = formatTable[index].type; - im = image; - } else { - im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); - internalFormat = GL_RGBA8; - format = GL_BGRA; - type = GL_UNSIGNED_INT_8_8_8_8_REV; - } - - d->m_internalFormat = internalFormat; - - if (d->s_supportsTextureStorage) { - glTexStorage2D(d->m_target, 1, internalFormat, im.width(), im.height()); - glTexSubImage2D(d->m_target, 0, 0, 0, im.width(), im.height(), - format, type, im.constBits()); - d->m_immutable = true; - } else { - glTexParameteri(d->m_target, GL_TEXTURE_MAX_LEVEL, d->m_mipLevels - 1); - glTexImage2D(d->m_target, 0, internalFormat, im.width(), im.height(), 0, - format, type, im.constBits()); - } - } else { - d->m_internalFormat = GL_RGBA8; - - if (d->s_supportsARGB32) { - const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); - glTexImage2D(d->m_target, 0, GL_BGRA_EXT, im.width(), im.height(), - 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.constBits()); - } else { - const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); - glTexImage2D(d->m_target, 0, GL_RGBA, im.width(), im.height(), - 0, GL_RGBA, GL_UNSIGNED_BYTE, im.constBits()); - } - } - - unbind(); - setFilter(GL_LINEAR); -} - -GLTexture::GLTexture(const QPixmap &pixmap, GLenum target) - : GLTexture(pixmap.toImage(), target) -{ -} - -GLTexture::GLTexture(const QString &fileName) - : GLTexture(QImage(fileName)) -{ -} - GLTexture::GLTexture(GLuint textureId, GLenum internalFormat, const QSize &size, int levels, bool isImmutable) : d_ptr(new GLTexturePrivate()) { @@ -767,4 +676,74 @@ std::unique_ptr GLTexture::allocate(GLenum internalFormat, const QSiz return ret; } +std::unique_ptr GLTexture::upload(const QImage &image) +{ + if (image.isNull()) { + return nullptr; + } + GLuint texture = 0; + glGenTextures(1, &texture); + if (texture == 0) { + qCWarning(LIBKWINGLUTILS, "generating OpenGL texture handle failed"); + return nullptr; + } + glBindTexture(GL_TEXTURE_2D, texture); + + GLenum internalFormat; + bool immutable = false; + if (!GLPlatform::instance()->isGLES()) { + QImage im; + GLenum format; + GLenum type; + + const QImage::Format index = image.format(); + + if (index < sizeof(formatTable) / sizeof(formatTable[0]) && formatTable[index].internalFormat + && !(formatTable[index].type == GL_UNSIGNED_SHORT && !GLTexturePrivate::s_supportsTexture16Bit)) { + internalFormat = formatTable[index].internalFormat; + format = formatTable[index].format; + type = formatTable[index].type; + im = image; + } else { + im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + internalFormat = GL_RGBA8; + format = GL_BGRA; + type = GL_UNSIGNED_INT_8_8_8_8_REV; + } + + if (GLTexturePrivate::s_supportsTextureStorage) { + glTexStorage2D(GL_TEXTURE_2D, 1, internalFormat, im.width(), im.height()); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, im.width(), im.height(), + format, type, im.constBits()); + immutable = true; + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, im.width(), im.height(), 0, + format, type, im.constBits()); + } + } else { + internalFormat = GL_RGBA8; + + if (GLTexturePrivate::s_supportsARGB32) { + const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, im.width(), im.height(), + 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.constBits()); + } else { + const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, im.width(), im.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, im.constBits()); + } + } + glBindTexture(GL_TEXTURE_2D, 0); + auto ret = std::make_unique(texture, internalFormat, image.size(), 1, immutable); + ret->setContentTransform(TextureTransform::MirrorY); + ret->d_ptr->m_foreign = false; + return ret; +} + +std::unique_ptr GLTexture::upload(const QPixmap &pixmap) +{ + return upload(pixmap.toImage()); +} + } // namespace KWin diff --git a/src/libkwineffects/kwingltexture.h b/src/libkwineffects/kwingltexture.h index 61fd7cbb12..6124f87787 100644 --- a/src/libkwineffects/kwingltexture.h +++ b/src/libkwineffects/kwingltexture.h @@ -49,9 +49,6 @@ class KWINGLUTILS_EXPORT GLTexture { public: explicit GLTexture(GLenum target); - explicit GLTexture(const QImage &image, GLenum target = GL_TEXTURE_2D); - explicit GLTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D); - explicit GLTexture(const QString &fileName); /** * Creates the underlying texture object. Returns @c true if the texture has been created @@ -157,6 +154,8 @@ public: static bool supportsFormatRG(); static std::unique_ptr allocate(GLenum internalFormat, const QSize &size, int levels = 1, bool needsMutability = false); + static std::unique_ptr upload(const QImage &image); + static std::unique_ptr upload(const QPixmap &pixmap); protected: const std::unique_ptr d_ptr; diff --git a/src/libkwineffects/kwinoffscreenquickview.cpp b/src/libkwineffects/kwinoffscreenquickview.cpp index 0825d19c13..1d78bc2145 100644 --- a/src/libkwineffects/kwinoffscreenquickview.cpp +++ b/src/libkwineffects/kwinoffscreenquickview.cpp @@ -407,10 +407,7 @@ void OffscreenQuickView::hide() GLTexture *OffscreenQuickView::bufferAsTexture() { if (d->m_useBlit) { - if (d->m_image.isNull()) { - return nullptr; - } - d->m_textureExport.reset(new GLTexture(d->m_image)); + d->m_textureExport = GLTexture::upload(d->m_image); } else { if (!d->m_fbo) { return nullptr; diff --git a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_internal.cpp b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_internal.cpp index 9df1ac2b97..d5689d9f80 100644 --- a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_internal.cpp +++ b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_internal.cpp @@ -77,7 +77,7 @@ bool BasicEGLSurfaceTextureInternal::updateFromImage(const QRegion ®ion) } if (!m_texture) { - m_texture.reset(new GLTexture(image)); + m_texture = GLTexture::upload(image); } else { const QRegion nativeRegion = scale(region, image.devicePixelRatio()); for (const QRect &rect : nativeRegion) { @@ -85,7 +85,7 @@ bool BasicEGLSurfaceTextureInternal::updateFromImage(const QRegion ®ion) } } - return true; + return m_texture != nullptr; } } // namespace KWin diff --git a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp index b303b8c77f..1f6bea097f 100644 --- a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp +++ b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp @@ -60,12 +60,10 @@ void BasicEGLSurfaceTextureWayland::update(const QRegion ®ion) bool BasicEGLSurfaceTextureWayland::loadShmTexture(KWaylandServer::ShmClientBuffer *buffer) { - const QImage &image = buffer->data(); - if (Q_UNLIKELY(image.isNull())) { + m_texture = GLTexture::upload(buffer->data()); + if (!m_texture) { return false; } - - m_texture.reset(new GLTexture(image)); m_texture->setFilter(GL_LINEAR); m_texture->setWrapMode(GL_CLAMP_TO_EDGE); m_texture->setContentTransform(TextureTransform::MirrorY); diff --git a/src/plugins/blur/blur.cpp b/src/plugins/blur/blur.cpp index 8833fab9e1..5aa3fab8c4 100644 --- a/src/plugins/blur/blur.cpp +++ b/src/plugins/blur/blur.cpp @@ -691,7 +691,10 @@ void BlurEffect::generateNoiseTexture() // The noise texture looks distorted when not scaled with integer noiseImage = noiseImage.scaled(noiseImage.size() * m_scalingFactor); - m_noiseTexture.reset(new GLTexture(noiseImage)); + m_noiseTexture = GLTexture::upload(noiseImage); + if (!m_noiseTexture) { + return; + } m_noiseTexture->setFilter(GL_NEAREST); m_noiseTexture->setWrapMode(GL_REPEAT); } @@ -833,6 +836,9 @@ void BlurEffect::applyNoise(const ScreenData &data, const RenderTarget &renderTa { if (!m_noiseTexture) { generateNoiseTexture(); + if (!m_noiseTexture) { + return; + } } m_shader->bind(BlurShader::NoiseSampleType); diff --git a/src/plugins/screencast/screencaststream.cpp b/src/plugins/screencast/screencaststream.cpp index 1b36b61647..726f84a9a2 100644 --- a/src/plugins/screencast/screencaststream.cpp +++ b/src/plugins/screencast/screencaststream.cpp @@ -551,7 +551,7 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion) if (cursorImage.isNull()) { m_cursor.texture = nullptr; } else { - m_cursor.texture = std::make_unique(cursorImage.image()); + m_cursor.texture = GLTexture::upload(cursorImage.image()); } } if (m_cursor.texture) { diff --git a/src/plugins/screenedge/screenedgeeffect.cpp b/src/plugins/screenedge/screenedgeeffect.cpp index 9cc84fa56c..dd95209ff0 100644 --- a/src/plugins/screenedge/screenedgeeffect.cpp +++ b/src/plugins/screenedge/screenedgeeffect.cpp @@ -79,6 +79,9 @@ void ScreenEdgeEffect::paintScreen(const RenderTarget &renderTarget, const Rende } if (effects->isOpenGLCompositing()) { GLTexture *texture = glow->texture.get(); + if (!texture) { + return; + } glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::Modulate | ShaderTrait::TransformColorspace); @@ -92,10 +95,10 @@ void ScreenEdgeEffect::paintScreen(const RenderTarget &renderTarget, const Rende texture->render(glow->geometry.size(), scale); glDisable(GL_BLEND); } else if (effects->compositingType() == QPainterCompositing) { - QImage tmp(glow->image->size(), QImage::Format_ARGB32_Premultiplied); + QImage tmp(glow->image.size(), QImage::Format_ARGB32_Premultiplied); tmp.fill(Qt::transparent); QPainter p(&tmp); - p.drawImage(0, 0, *glow->image.get()); + p.drawImage(0, 0, glow->image); QColor color(Qt::transparent); color.setAlphaF(opacity); p.setCompositionMode(QPainter::CompositionMode_DestinationIn); @@ -140,9 +143,9 @@ void ScreenEdgeEffect::edgeApproaching(ElectricBorder border, qreal factor, cons effects->addRepaint(glow->geometry); if (border == ElectricLeft || border == ElectricRight || border == ElectricTop || border == ElectricBottom) { if (effects->isOpenGLCompositing()) { - glow->texture.reset(createEdgeGlow(border, geometry.size())); + glow->texture = GLTexture::upload(createEdgeGlow(border, geometry.size())); } else if (effects->compositingType() == QPainterCompositing) { - glow->image.reset(createEdgeGlow(border, geometry.size())); + glow->image = createEdgeGlow(border, geometry.size()); } } } @@ -172,25 +175,23 @@ std::unique_ptr ScreenEdgeEffect::createGlow(ElectricBorder border, qreal if (effects->isOpenGLCompositing()) { effects->makeOpenGLContextCurrent(); if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) { - glow->texture.reset(createCornerGlow(border)); + glow->texture = GLTexture::upload(createCornerGlow(border)); } else { - glow->texture.reset(createEdgeGlow(border, geometry.size())); - } - if (glow->texture) { - glow->texture->setWrapMode(GL_CLAMP_TO_EDGE); + glow->texture = GLTexture::upload(createEdgeGlow(border, geometry.size())); } if (!glow->texture) { return nullptr; } + glow->texture->setWrapMode(GL_CLAMP_TO_EDGE); } else if (effects->compositingType() == QPainterCompositing) { if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) { - glow->image.reset(createCornerGlow(border)); + glow->image = createCornerGlow(border); glow->pictureSize = cornerGlowSize(border); } else { - glow->image.reset(createEdgeGlow(border, geometry.size())); + glow->image = createEdgeGlow(border, geometry.size()); glow->pictureSize = geometry.size(); } - if (!glow->image) { + if (glow->image.isNull()) { return nullptr; } } @@ -198,22 +199,21 @@ std::unique_ptr ScreenEdgeEffect::createGlow(ElectricBorder border, qreal return glow; } -template -T *ScreenEdgeEffect::createCornerGlow(ElectricBorder border) +QImage ScreenEdgeEffect::createCornerGlow(ElectricBorder border) { ensureGlowSvg(); switch (border) { case ElectricTopLeft: - return new T(m_glow->pixmap(QStringLiteral("bottomright")).toImage()); + return m_glow->pixmap(QStringLiteral("bottomright")).toImage(); case ElectricTopRight: - return new T(m_glow->pixmap(QStringLiteral("bottomleft")).toImage()); + return m_glow->pixmap(QStringLiteral("bottomleft")).toImage(); case ElectricBottomRight: - return new T(m_glow->pixmap(QStringLiteral("topleft")).toImage()); + return m_glow->pixmap(QStringLiteral("topleft")).toImage(); case ElectricBottomLeft: - return new T(m_glow->pixmap(QStringLiteral("topright")).toImage()); + return m_glow->pixmap(QStringLiteral("topright")).toImage(); default: - return nullptr; + return QImage{}; } } @@ -235,8 +235,7 @@ QSize ScreenEdgeEffect::cornerGlowSize(ElectricBorder border) } } -template -T *ScreenEdgeEffect::createEdgeGlow(ElectricBorder border, const QSize &size) +QImage ScreenEdgeEffect::createEdgeGlow(ElectricBorder border, const QSize &size) { ensureGlowSvg(); @@ -268,7 +267,7 @@ T *ScreenEdgeEffect::createEdgeGlow(ElectricBorder border, const QSize &size) pixmapPosition = QPoint(size.width() - c.width(), 0); break; default: - return nullptr; + return QImage{}; } QPixmap image(size); image.fill(Qt::transparent); @@ -294,7 +293,7 @@ T *ScreenEdgeEffect::createEdgeGlow(ElectricBorder border, const QSize &size) p.drawPixmap(QPoint(pixmapPosition.x(), size.height() - r.height()), r); } p.end(); - return new T(image.toImage()); + return image.toImage(); } bool ScreenEdgeEffect::isActive() const diff --git a/src/plugins/screenedge/screenedgeeffect.h b/src/plugins/screenedge/screenedgeeffect.h index b818445282..61f77a3ba2 100644 --- a/src/plugins/screenedge/screenedgeeffect.h +++ b/src/plugins/screenedge/screenedgeeffect.h @@ -42,10 +42,8 @@ private Q_SLOTS: private: void ensureGlowSvg(); std::unique_ptr createGlow(ElectricBorder border, qreal factor, const QRect &geometry); - template - T *createCornerGlow(ElectricBorder border); - template - T *createEdgeGlow(ElectricBorder border, const QSize &size); + QImage createCornerGlow(ElectricBorder border); + QImage createEdgeGlow(ElectricBorder border, const QSize &size); QSize cornerGlowSize(ElectricBorder border); Plasma::Svg *m_glow = nullptr; std::map> m_borders; @@ -56,7 +54,7 @@ class Glow { public: std::unique_ptr texture; - std::unique_ptr image; + QImage image; QSize pictureSize; qreal strength; QRect geometry; diff --git a/src/plugins/startupfeedback/startupfeedback.cpp b/src/plugins/startupfeedback/startupfeedback.cpp index 510ef3e27e..2061034b94 100644 --- a/src/plugins/startupfeedback/startupfeedback.cpp +++ b/src/plugins/startupfeedback/startupfeedback.cpp @@ -212,6 +212,9 @@ void StartupFeedbackEffect::paintScreen(const RenderTarget &renderTarget, const default: return; // safety } + if (!texture) { + return; + } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GLShader *shader = nullptr; @@ -356,14 +359,20 @@ void StartupFeedbackEffect::prepareTextures(const QPixmap &pix) switch (m_type) { case BouncingFeedback: for (int i = 0; i < 5; ++i) { - m_bouncingTextures[i].reset(new GLTexture(scalePixmap(pix, BOUNCE_SIZES[i]))); + m_bouncingTextures[i] = GLTexture::upload(scalePixmap(pix, BOUNCE_SIZES[i])); + if (!m_bouncingTextures[i]) { + return; + } m_bouncingTextures[i]->setFilter(GL_LINEAR); m_bouncingTextures[i]->setWrapMode(GL_CLAMP_TO_EDGE); } break; case BlinkingFeedback: case PassiveFeedback: - m_texture.reset(new GLTexture(pix)); + m_texture = GLTexture::upload(pix); + if (!m_texture) { + return; + } m_texture->setFilter(GL_LINEAR); m_texture->setWrapMode(GL_CLAMP_TO_EDGE); break; diff --git a/src/plugins/trackmouse/trackmouse.cpp b/src/plugins/trackmouse/trackmouse.cpp index 0daa36f56e..5979424da5 100644 --- a/src/plugins/trackmouse/trackmouse.cpp +++ b/src/plugins/trackmouse/trackmouse.cpp @@ -236,7 +236,7 @@ void TrackMouseEffect::loadTexture() for (int i = 0; i < 2; ++i) { if (effects->isOpenGLCompositing()) { QImage img(f[i]); - m_texture[i] = std::make_unique(img); + m_texture[i] = GLTexture::upload(img); m_lastRect[i].setSize(img.size()); } if (effects->compositingType() == QPainterCompositing) { diff --git a/src/plugins/zoom/zoom.cpp b/src/plugins/zoom/zoom.cpp index 825295ed37..6b4c1dab17 100644 --- a/src/plugins/zoom/zoom.cpp +++ b/src/plugins/zoom/zoom.cpp @@ -155,7 +155,10 @@ GLTexture *ZoomEffect::ensureCursorTexture() m_cursorTextureDirty = false; const auto cursor = effects->cursorImage(); if (!cursor.image().isNull()) { - m_cursorTexture = std::make_unique(cursor.image()); + m_cursorTexture = GLTexture::upload(cursor.image()); + if (!m_cursorTexture) { + return nullptr; + } m_cursorTexture->setWrapMode(GL_CLAMP_TO_EDGE); } } diff --git a/src/scene/imageitem.cpp b/src/scene/imageitem.cpp index adbe038af1..920fc7fe02 100644 --- a/src/scene/imageitem.cpp +++ b/src/scene/imageitem.cpp @@ -49,7 +49,7 @@ void ImageItemOpenGL::preprocess() m_textureKey = m_image.cacheKey(); if (!m_texture || m_texture->size() != m_image.size()) { - m_texture = std::make_unique(m_image); + m_texture = GLTexture::upload(m_image); } else { m_texture->update(m_image); } diff --git a/src/scene/workspacescene_opengl.cpp b/src/scene/workspacescene_opengl.cpp index fe9f0c4f99..9f7428ba3e 100644 --- a/src/scene/workspacescene_opengl.cpp +++ b/src/scene/workspacescene_opengl.cpp @@ -162,7 +162,10 @@ std::shared_ptr DecorationShadowTextureCache::getTexture(ShadowTextur } Data d; d.providers << provider; - d.texture = std::make_shared(shadow->decorationShadowImage()); + d.texture = GLTexture::upload(shadow->decorationShadowImage()); + if (!d.texture) { + return nullptr; + } d.texture->setFilter(GL_LINEAR); d.texture->setWrapMode(GL_CLAMP_TO_EDGE); m_cache.insert(decoShadow.get(), d); @@ -252,7 +255,10 @@ void OpenGLShadowTextureProvider::update() } } - m_texture = std::make_shared(image); + m_texture = GLTexture::upload(image); + if (!m_texture) { + return; + } m_texture->setFilter(GL_LINEAR); m_texture->setWrapMode(GL_CLAMP_TO_EDGE);