From 0811b76946a493341fe8489c8811bdbaa4a5132c Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sat, 31 Jan 2026 13:54:48 -0500 Subject: [PATCH] Fix FontAtlas texture deletion bug - text now renders in WebGL The FontAtlas class was missing move semantics, causing the GPU texture to be deleted immediately after creation. When FontAtlas was moved into the cache with std::move(), the default move constructor copied textureId_, then the original's destructor deleted the texture the cache was using. Added: - Move constructor that transfers ownership and clears source textureId_ - Move assignment operator with proper cleanup of existing resources - Deleted copy operations since GPU textures can't be shared Also cleaned up the text fragment shader to use proper alpha sampling. Co-Authored-By: Claude Opus 4.5 --- src/platform/SDL2Renderer.cpp | 50 ++++++++++++++++++++++++++++++++--- src/platform/SDL2Renderer.h | 8 ++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/platform/SDL2Renderer.cpp b/src/platform/SDL2Renderer.cpp index 24c1c68..9ab2bdc 100644 --- a/src/platform/SDL2Renderer.cpp +++ b/src/platform/SDL2Renderer.cpp @@ -96,7 +96,7 @@ void main() { } )"; -// Text shader is same as sprite for now +// Text shader - uses alpha from texture, color from vertex static const char* TEXT_VERTEX_SHADER = SPRITE_VERTEX_SHADER; static const char* TEXT_FRAGMENT_SHADER = R"( #ifdef GL_ES @@ -107,9 +107,11 @@ varying vec2 v_texcoord; uniform sampler2D u_texture; void main() { - // Text rendering: use texture alpha as coverage - float alpha = texture2D(u_texture, v_texcoord).a; - gl_FragColor = vec4(v_color.rgb, v_color.a * alpha); + // Font atlas stores glyph alpha in texture alpha channel + // RGB is white (255,255,255), alpha varies per glyph pixel + vec4 texSample = texture2D(u_texture, v_texcoord); + // Use vertex color for RGB, texture alpha for transparency + gl_FragColor = vec4(v_color.rgb, v_color.a * texSample.a); } )"; @@ -1862,6 +1864,46 @@ bool Shader::isAvailable() { FontAtlas::FontAtlas() = default; +FontAtlas::FontAtlas(FontAtlas&& other) noexcept + : textureId_(other.textureId_) + , fontSize_(other.fontSize_) + , ascent_(other.ascent_) + , descent_(other.descent_) + , lineHeight_(other.lineHeight_) + , glyphCache_(std::move(other.glyphCache_)) + , stbFontInfo_(other.stbFontInfo_) +{ + // Clear source to prevent double-deletion + other.textureId_ = 0; + other.stbFontInfo_ = nullptr; +} + +FontAtlas& FontAtlas::operator=(FontAtlas&& other) noexcept { + if (this != &other) { + // Clean up existing resources + if (textureId_) { + SDL2Renderer::getInstance().deleteTexture(textureId_); + } + if (stbFontInfo_) { + delete static_cast(stbFontInfo_); + } + + // Transfer ownership + textureId_ = other.textureId_; + fontSize_ = other.fontSize_; + ascent_ = other.ascent_; + descent_ = other.descent_; + lineHeight_ = other.lineHeight_; + glyphCache_ = std::move(other.glyphCache_); + stbFontInfo_ = other.stbFontInfo_; + + // Clear source to prevent double-deletion + other.textureId_ = 0; + other.stbFontInfo_ = nullptr; + } + return *this; +} + FontAtlas::~FontAtlas() { if (textureId_) { SDL2Renderer::getInstance().deleteTexture(textureId_); diff --git a/src/platform/SDL2Renderer.h b/src/platform/SDL2Renderer.h index 8246175..3a34238 100644 --- a/src/platform/SDL2Renderer.h +++ b/src/platform/SDL2Renderer.h @@ -154,6 +154,14 @@ public: FontAtlas(); ~FontAtlas(); + // Move semantics - transfer ownership of GPU resources + FontAtlas(FontAtlas&& other) noexcept; + FontAtlas& operator=(FontAtlas&& other) noexcept; + + // Disable copy - texture resources can't be shared + FontAtlas(const FontAtlas&) = delete; + FontAtlas& operator=(const FontAtlas&) = delete; + // Load font data bool load(const unsigned char* fontData, size_t dataSize, float fontSize);