From a8da853de086ea3cc730aa2b1a1636bce9b10713 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sun, 11 Feb 2024 16:45:49 -0800 Subject: [PATCH] SDL: Support textures regenerating when flushed * We store a Go image.Image copy of all textures in the SDL2 renderer. * When the underlying SDL2 textures are freed, regenerate them when needed from the underlying Go Image. --- sdl/texture.go | 64 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/sdl/texture.go b/sdl/texture.go index 76d798a..32be887 100644 --- a/sdl/texture.go +++ b/sdl/texture.go @@ -14,15 +14,21 @@ import ( func (r *Renderer) Copy(t render.Texturer, src, dst render.Rect) { if tex, ok := t.(*Texture); ok { var ( - a = RectToSDL(src) - b = RectToSDL(dst) + a = RectToSDL(src) + b = RectToSDL(dst) + texture, err = tex.getTexture() ) - r.renderer.Copy(tex.tex, &a, &b) + if err == nil { + r.renderer.Copy(texture, &a, &b) + } else { + fmt.Printf("Renderer.Copy: texture %s could not be created: %s\n", tex.name, err) + } } } // Texture can hold on to SDL textures for caching and optimization. type Texture struct { + name string render *Renderer // backref to free them up thoroughly tex *sdl.Texture image image.Image @@ -30,13 +36,16 @@ type Texture struct { height int32 } -// StoreTexture caches an SDL texture from a bitmap. -func (r *Renderer) StoreTexture(name string, img image.Image) (render.Texturer, error) { - var ( - fh = bytes.NewBuffer([]byte{}) - ) +// Render the SDL2 Texture from the base Image if the texture is not ready. +func (t *Texture) getTexture() (*sdl.Texture, error) { + if t.tex != nil { + return t.tex, nil + } - err := bmp.Encode(fh, img) + var ( + fh = bytes.NewBuffer([]byte{}) + err = bmp.Encode(fh, t.image) + ) if err != nil { return nil, fmt.Errorf("NewTexture: bmp.Encode: %s", err) } @@ -57,19 +66,30 @@ func (r *Renderer) StoreTexture(name string, img image.Image) (render.Texturer, key := sdl.MapRGB(surface.Format, 255, 255, 255) surface.SetColorKey(true, key) - texture, err := r.renderer.CreateTextureFromSurface(surface) + texture, err := t.render.renderer.CreateTextureFromSurface(surface) if err != nil { return nil, fmt.Errorf("NewBitmap: create texture: %s", err) } + t.tex = texture + t.width = surface.W + t.height = surface.H + return t.tex, nil +} + +// StoreTexture caches an SDL texture from a bitmap. +func (r *Renderer) StoreTexture(name string, img image.Image) (render.Texturer, error) { tex := &Texture{ + name: name, render: r, - width: surface.W, - height: surface.H, - tex: texture, image: img, } + _, err := tex.getTexture() + if err != nil { + return tex, err + } + r.textureMu.Lock() r.textures[name] = tex r.textureMu.Unlock() @@ -120,14 +140,6 @@ func (t *Texture) Free() error { t.tex = nil } - // Free up the cached texture too to garbage collect the image.Image cache etc. - for name, tex := range t.render.textures { - if tex == t { - delete(t.render.textures, name) - break - } - } - return err } @@ -139,7 +151,10 @@ func (r *Renderer) LoadTexture(name string) (render.Texturer, error) { if tex, ok := r.textures[name]; ok { // If the SDL2 texture had been freed, recreate it. if tex.tex == nil { - return r.StoreTexture(name, tex.image) + _, err := tex.getTexture() + if err != nil { + return tex, err + } } return tex, nil } @@ -153,8 +168,11 @@ func (r *Renderer) FreeTextures() int { var num = len(r.textures) for name, tex := range r.textures { + if tex.tex != nil { + tex.tex.Destroy() + tex.tex = nil + } delete(r.textures, name) - tex.tex.Destroy() } return num }