libkwineffects: handle GLTexture upload failures

This is required for properly dealing with GPU resets
This commit is contained in:
Xaver Hugl 2023-05-15 13:18:41 +02:00
parent 572bc75de4
commit 16fb2848ed
14 changed files with 135 additions and 141 deletions

View file

@ -94,97 +94,6 @@ GLTexture::GLTexture(std::unique_ptr<GLTexturePrivate> &&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) GLTexture::GLTexture(GLuint textureId, GLenum internalFormat, const QSize &size, int levels, bool isImmutable)
: d_ptr(new GLTexturePrivate()) : d_ptr(new GLTexturePrivate())
{ {
@ -767,4 +676,74 @@ std::unique_ptr<GLTexture> GLTexture::allocate(GLenum internalFormat, const QSiz
return ret; return ret;
} }
std::unique_ptr<GLTexture> 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<GLTexture>(texture, internalFormat, image.size(), 1, immutable);
ret->setContentTransform(TextureTransform::MirrorY);
ret->d_ptr->m_foreign = false;
return ret;
}
std::unique_ptr<GLTexture> GLTexture::upload(const QPixmap &pixmap)
{
return upload(pixmap.toImage());
}
} // namespace KWin } // namespace KWin

View file

@ -49,9 +49,6 @@ class KWINGLUTILS_EXPORT GLTexture
{ {
public: public:
explicit GLTexture(GLenum target); 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 * Creates the underlying texture object. Returns @c true if the texture has been created
@ -157,6 +154,8 @@ public:
static bool supportsFormatRG(); static bool supportsFormatRG();
static std::unique_ptr<GLTexture> allocate(GLenum internalFormat, const QSize &size, int levels = 1, bool needsMutability = false); static std::unique_ptr<GLTexture> allocate(GLenum internalFormat, const QSize &size, int levels = 1, bool needsMutability = false);
static std::unique_ptr<GLTexture> upload(const QImage &image);
static std::unique_ptr<GLTexture> upload(const QPixmap &pixmap);
protected: protected:
const std::unique_ptr<GLTexturePrivate> d_ptr; const std::unique_ptr<GLTexturePrivate> d_ptr;

View file

@ -407,10 +407,7 @@ void OffscreenQuickView::hide()
GLTexture *OffscreenQuickView::bufferAsTexture() GLTexture *OffscreenQuickView::bufferAsTexture()
{ {
if (d->m_useBlit) { if (d->m_useBlit) {
if (d->m_image.isNull()) { d->m_textureExport = GLTexture::upload(d->m_image);
return nullptr;
}
d->m_textureExport.reset(new GLTexture(d->m_image));
} else { } else {
if (!d->m_fbo) { if (!d->m_fbo) {
return nullptr; return nullptr;

View file

@ -77,7 +77,7 @@ bool BasicEGLSurfaceTextureInternal::updateFromImage(const QRegion &region)
} }
if (!m_texture) { if (!m_texture) {
m_texture.reset(new GLTexture(image)); m_texture = GLTexture::upload(image);
} else { } else {
const QRegion nativeRegion = scale(region, image.devicePixelRatio()); const QRegion nativeRegion = scale(region, image.devicePixelRatio());
for (const QRect &rect : nativeRegion) { for (const QRect &rect : nativeRegion) {
@ -85,7 +85,7 @@ bool BasicEGLSurfaceTextureInternal::updateFromImage(const QRegion &region)
} }
} }
return true; return m_texture != nullptr;
} }
} // namespace KWin } // namespace KWin

View file

@ -60,12 +60,10 @@ void BasicEGLSurfaceTextureWayland::update(const QRegion &region)
bool BasicEGLSurfaceTextureWayland::loadShmTexture(KWaylandServer::ShmClientBuffer *buffer) bool BasicEGLSurfaceTextureWayland::loadShmTexture(KWaylandServer::ShmClientBuffer *buffer)
{ {
const QImage &image = buffer->data(); m_texture = GLTexture::upload(buffer->data());
if (Q_UNLIKELY(image.isNull())) { if (!m_texture) {
return false; return false;
} }
m_texture.reset(new GLTexture(image));
m_texture->setFilter(GL_LINEAR); m_texture->setFilter(GL_LINEAR);
m_texture->setWrapMode(GL_CLAMP_TO_EDGE); m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
m_texture->setContentTransform(TextureTransform::MirrorY); m_texture->setContentTransform(TextureTransform::MirrorY);

View file

@ -691,7 +691,10 @@ void BlurEffect::generateNoiseTexture()
// The noise texture looks distorted when not scaled with integer // The noise texture looks distorted when not scaled with integer
noiseImage = noiseImage.scaled(noiseImage.size() * m_scalingFactor); 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->setFilter(GL_NEAREST);
m_noiseTexture->setWrapMode(GL_REPEAT); m_noiseTexture->setWrapMode(GL_REPEAT);
} }
@ -833,6 +836,9 @@ void BlurEffect::applyNoise(const ScreenData &data, const RenderTarget &renderTa
{ {
if (!m_noiseTexture) { if (!m_noiseTexture) {
generateNoiseTexture(); generateNoiseTexture();
if (!m_noiseTexture) {
return;
}
} }
m_shader->bind(BlurShader::NoiseSampleType); m_shader->bind(BlurShader::NoiseSampleType);

View file

@ -551,7 +551,7 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
if (cursorImage.isNull()) { if (cursorImage.isNull()) {
m_cursor.texture = nullptr; m_cursor.texture = nullptr;
} else { } else {
m_cursor.texture = std::make_unique<GLTexture>(cursorImage.image()); m_cursor.texture = GLTexture::upload(cursorImage.image());
} }
} }
if (m_cursor.texture) { if (m_cursor.texture) {

View file

@ -79,6 +79,9 @@ void ScreenEdgeEffect::paintScreen(const RenderTarget &renderTarget, const Rende
} }
if (effects->isOpenGLCompositing()) { if (effects->isOpenGLCompositing()) {
GLTexture *texture = glow->texture.get(); GLTexture *texture = glow->texture.get();
if (!texture) {
return;
}
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::Modulate | ShaderTrait::TransformColorspace); 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); texture->render(glow->geometry.size(), scale);
glDisable(GL_BLEND); glDisable(GL_BLEND);
} else if (effects->compositingType() == QPainterCompositing) { } 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); tmp.fill(Qt::transparent);
QPainter p(&tmp); QPainter p(&tmp);
p.drawImage(0, 0, *glow->image.get()); p.drawImage(0, 0, glow->image);
QColor color(Qt::transparent); QColor color(Qt::transparent);
color.setAlphaF(opacity); color.setAlphaF(opacity);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
@ -140,9 +143,9 @@ void ScreenEdgeEffect::edgeApproaching(ElectricBorder border, qreal factor, cons
effects->addRepaint(glow->geometry); effects->addRepaint(glow->geometry);
if (border == ElectricLeft || border == ElectricRight || border == ElectricTop || border == ElectricBottom) { if (border == ElectricLeft || border == ElectricRight || border == ElectricTop || border == ElectricBottom) {
if (effects->isOpenGLCompositing()) { if (effects->isOpenGLCompositing()) {
glow->texture.reset(createEdgeGlow<GLTexture>(border, geometry.size())); glow->texture = GLTexture::upload(createEdgeGlow(border, geometry.size()));
} else if (effects->compositingType() == QPainterCompositing) { } else if (effects->compositingType() == QPainterCompositing) {
glow->image.reset(createEdgeGlow<QImage>(border, geometry.size())); glow->image = createEdgeGlow(border, geometry.size());
} }
} }
} }
@ -172,25 +175,23 @@ std::unique_ptr<Glow> ScreenEdgeEffect::createGlow(ElectricBorder border, qreal
if (effects->isOpenGLCompositing()) { if (effects->isOpenGLCompositing()) {
effects->makeOpenGLContextCurrent(); effects->makeOpenGLContextCurrent();
if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) { if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) {
glow->texture.reset(createCornerGlow<GLTexture>(border)); glow->texture = GLTexture::upload(createCornerGlow(border));
} else { } else {
glow->texture.reset(createEdgeGlow<GLTexture>(border, geometry.size())); glow->texture = GLTexture::upload(createEdgeGlow(border, geometry.size()));
}
if (glow->texture) {
glow->texture->setWrapMode(GL_CLAMP_TO_EDGE);
} }
if (!glow->texture) { if (!glow->texture) {
return nullptr; return nullptr;
} }
glow->texture->setWrapMode(GL_CLAMP_TO_EDGE);
} else if (effects->compositingType() == QPainterCompositing) { } else if (effects->compositingType() == QPainterCompositing) {
if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) { if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) {
glow->image.reset(createCornerGlow<QImage>(border)); glow->image = createCornerGlow(border);
glow->pictureSize = cornerGlowSize(border); glow->pictureSize = cornerGlowSize(border);
} else { } else {
glow->image.reset(createEdgeGlow<QImage>(border, geometry.size())); glow->image = createEdgeGlow(border, geometry.size());
glow->pictureSize = geometry.size(); glow->pictureSize = geometry.size();
} }
if (!glow->image) { if (glow->image.isNull()) {
return nullptr; return nullptr;
} }
} }
@ -198,22 +199,21 @@ std::unique_ptr<Glow> ScreenEdgeEffect::createGlow(ElectricBorder border, qreal
return glow; return glow;
} }
template<typename T> QImage ScreenEdgeEffect::createCornerGlow(ElectricBorder border)
T *ScreenEdgeEffect::createCornerGlow(ElectricBorder border)
{ {
ensureGlowSvg(); ensureGlowSvg();
switch (border) { switch (border) {
case ElectricTopLeft: case ElectricTopLeft:
return new T(m_glow->pixmap(QStringLiteral("bottomright")).toImage()); return m_glow->pixmap(QStringLiteral("bottomright")).toImage();
case ElectricTopRight: case ElectricTopRight:
return new T(m_glow->pixmap(QStringLiteral("bottomleft")).toImage()); return m_glow->pixmap(QStringLiteral("bottomleft")).toImage();
case ElectricBottomRight: case ElectricBottomRight:
return new T(m_glow->pixmap(QStringLiteral("topleft")).toImage()); return m_glow->pixmap(QStringLiteral("topleft")).toImage();
case ElectricBottomLeft: case ElectricBottomLeft:
return new T(m_glow->pixmap(QStringLiteral("topright")).toImage()); return m_glow->pixmap(QStringLiteral("topright")).toImage();
default: default:
return nullptr; return QImage{};
} }
} }
@ -235,8 +235,7 @@ QSize ScreenEdgeEffect::cornerGlowSize(ElectricBorder border)
} }
} }
template<typename T> QImage ScreenEdgeEffect::createEdgeGlow(ElectricBorder border, const QSize &size)
T *ScreenEdgeEffect::createEdgeGlow(ElectricBorder border, const QSize &size)
{ {
ensureGlowSvg(); ensureGlowSvg();
@ -268,7 +267,7 @@ T *ScreenEdgeEffect::createEdgeGlow(ElectricBorder border, const QSize &size)
pixmapPosition = QPoint(size.width() - c.width(), 0); pixmapPosition = QPoint(size.width() - c.width(), 0);
break; break;
default: default:
return nullptr; return QImage{};
} }
QPixmap image(size); QPixmap image(size);
image.fill(Qt::transparent); 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.drawPixmap(QPoint(pixmapPosition.x(), size.height() - r.height()), r);
} }
p.end(); p.end();
return new T(image.toImage()); return image.toImage();
} }
bool ScreenEdgeEffect::isActive() const bool ScreenEdgeEffect::isActive() const

View file

@ -42,10 +42,8 @@ private Q_SLOTS:
private: private:
void ensureGlowSvg(); void ensureGlowSvg();
std::unique_ptr<Glow> createGlow(ElectricBorder border, qreal factor, const QRect &geometry); std::unique_ptr<Glow> createGlow(ElectricBorder border, qreal factor, const QRect &geometry);
template<typename T> QImage createCornerGlow(ElectricBorder border);
T *createCornerGlow(ElectricBorder border); QImage createEdgeGlow(ElectricBorder border, const QSize &size);
template<typename T>
T *createEdgeGlow(ElectricBorder border, const QSize &size);
QSize cornerGlowSize(ElectricBorder border); QSize cornerGlowSize(ElectricBorder border);
Plasma::Svg *m_glow = nullptr; Plasma::Svg *m_glow = nullptr;
std::map<ElectricBorder, std::unique_ptr<Glow>> m_borders; std::map<ElectricBorder, std::unique_ptr<Glow>> m_borders;
@ -56,7 +54,7 @@ class Glow
{ {
public: public:
std::unique_ptr<GLTexture> texture; std::unique_ptr<GLTexture> texture;
std::unique_ptr<QImage> image; QImage image;
QSize pictureSize; QSize pictureSize;
qreal strength; qreal strength;
QRect geometry; QRect geometry;

View file

@ -212,6 +212,9 @@ void StartupFeedbackEffect::paintScreen(const RenderTarget &renderTarget, const
default: default:
return; // safety return; // safety
} }
if (!texture) {
return;
}
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLShader *shader = nullptr; GLShader *shader = nullptr;
@ -356,14 +359,20 @@ void StartupFeedbackEffect::prepareTextures(const QPixmap &pix)
switch (m_type) { switch (m_type) {
case BouncingFeedback: case BouncingFeedback:
for (int i = 0; i < 5; ++i) { 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]->setFilter(GL_LINEAR);
m_bouncingTextures[i]->setWrapMode(GL_CLAMP_TO_EDGE); m_bouncingTextures[i]->setWrapMode(GL_CLAMP_TO_EDGE);
} }
break; break;
case BlinkingFeedback: case BlinkingFeedback:
case PassiveFeedback: case PassiveFeedback:
m_texture.reset(new GLTexture(pix)); m_texture = GLTexture::upload(pix);
if (!m_texture) {
return;
}
m_texture->setFilter(GL_LINEAR); m_texture->setFilter(GL_LINEAR);
m_texture->setWrapMode(GL_CLAMP_TO_EDGE); m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
break; break;

View file

@ -236,7 +236,7 @@ void TrackMouseEffect::loadTexture()
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
if (effects->isOpenGLCompositing()) { if (effects->isOpenGLCompositing()) {
QImage img(f[i]); QImage img(f[i]);
m_texture[i] = std::make_unique<GLTexture>(img); m_texture[i] = GLTexture::upload(img);
m_lastRect[i].setSize(img.size()); m_lastRect[i].setSize(img.size());
} }
if (effects->compositingType() == QPainterCompositing) { if (effects->compositingType() == QPainterCompositing) {

View file

@ -155,7 +155,10 @@ GLTexture *ZoomEffect::ensureCursorTexture()
m_cursorTextureDirty = false; m_cursorTextureDirty = false;
const auto cursor = effects->cursorImage(); const auto cursor = effects->cursorImage();
if (!cursor.image().isNull()) { if (!cursor.image().isNull()) {
m_cursorTexture = std::make_unique<GLTexture>(cursor.image()); m_cursorTexture = GLTexture::upload(cursor.image());
if (!m_cursorTexture) {
return nullptr;
}
m_cursorTexture->setWrapMode(GL_CLAMP_TO_EDGE); m_cursorTexture->setWrapMode(GL_CLAMP_TO_EDGE);
} }
} }

View file

@ -49,7 +49,7 @@ void ImageItemOpenGL::preprocess()
m_textureKey = m_image.cacheKey(); m_textureKey = m_image.cacheKey();
if (!m_texture || m_texture->size() != m_image.size()) { if (!m_texture || m_texture->size() != m_image.size()) {
m_texture = std::make_unique<GLTexture>(m_image); m_texture = GLTexture::upload(m_image);
} else { } else {
m_texture->update(m_image); m_texture->update(m_image);
} }

View file

@ -162,7 +162,10 @@ std::shared_ptr<GLTexture> DecorationShadowTextureCache::getTexture(ShadowTextur
} }
Data d; Data d;
d.providers << provider; d.providers << provider;
d.texture = std::make_shared<GLTexture>(shadow->decorationShadowImage()); d.texture = GLTexture::upload(shadow->decorationShadowImage());
if (!d.texture) {
return nullptr;
}
d.texture->setFilter(GL_LINEAR); d.texture->setFilter(GL_LINEAR);
d.texture->setWrapMode(GL_CLAMP_TO_EDGE); d.texture->setWrapMode(GL_CLAMP_TO_EDGE);
m_cache.insert(decoShadow.get(), d); m_cache.insert(decoShadow.get(), d);
@ -252,7 +255,10 @@ void OpenGLShadowTextureProvider::update()
} }
} }
m_texture = std::make_shared<GLTexture>(image); m_texture = GLTexture::upload(image);
if (!m_texture) {
return;
}
m_texture->setFilter(GL_LINEAR); m_texture->setFilter(GL_LINEAR);
m_texture->setWrapMode(GL_CLAMP_TO_EDGE); m_texture->setWrapMode(GL_CLAMP_TO_EDGE);