From fbc59729b24dd6c3c4052a729431fa54ffe42b0d Mon Sep 17 00:00:00 2001 From: ricande Date: Thu, 15 Oct 2026 20:46:55 +0200 Subject: [PATCH] graphics/gl: forget deleted textures in the binding cache delete_texture deleted the GL texture but left it in GlCache. GL rebinds units that had the texture to 0, but the cache still recorded it as bound. If a texture created next got the same GL name, bind_texture took it as already bound and skipped glBindTexture, so its upload went to whatever was really bound and the new texture stayed empty. This happens to macroquad's font atlas when it grows right after a flush that ended with text: the new atlas texture is never filled and all text is drawn as black rectangles (not-fl3/macroquad#1006). GlCache::forget_texture resets the cache entries, including the stored binding, that refer to the deleted texture. It makes no GL calls, so bindings of other textures are not touched; this replaces the commented-out clear_texture_bindings() call. --- src/graphics/gl.rs | 3 +-- src/graphics/gl/cache.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/graphics/gl.rs b/src/graphics/gl.rs index 5fb34c096..6c997ca30 100644 --- a/src/graphics/gl.rs +++ b/src/graphics/gl.rs @@ -911,12 +911,11 @@ impl RenderingBackend for GlContext { } fn delete_texture(&mut self, texture: TextureId) { - //self.cache.clear_texture_bindings(); - let t = self.textures.get(texture); match &t.raw { TextureOrRenderbuffer::Texture(raw) => unsafe { glDeleteTextures(1, raw as *const _); + self.cache.forget_texture(*raw); }, TextureOrRenderbuffer::Renderbuffer(raw) => unsafe { glDeleteRenderbuffers(1, raw as *const _); diff --git a/src/graphics/gl/cache.rs b/src/graphics/gl/cache.rs index 2a3c1dcc7..0c99630a5 100644 --- a/src/graphics/gl/cache.rs +++ b/src/graphics/gl/cache.rs @@ -116,6 +116,25 @@ impl GlCache { self.index_buffer = 0; } + /// Forgets `texture` wherever the cache records it as bound. Deleting a + /// texture already rebinds those units to 0 in GL, so no GL call is + /// needed; but if the cache kept the name, a texture created later with + /// the same name would be taken as already bound and never be bound. + pub fn forget_texture(&mut self, texture: GLuint) { + for cached in &mut self.textures { + if cached.texture == texture { + *cached = CachedTexture { + target: 0, + texture: 0, + }; + } + } + if self.stored_texture == texture { + self.stored_target = 0; + self.stored_texture = 0; + } + } + pub fn clear_texture_bindings(&mut self) { for ix in 0..MAX_SHADERSTAGE_IMAGES { if self.textures[ix].texture != 0 {