doodle/pkg/doodads/doodad.go
Noah Petherbridge db5760ee83 Optimize memory by freeing up SDL2 textures
* Added to the F3 Debug Overlay is a "Texture:" label that counts the number
  of textures currently loaded by the (SDL2) render engine.
* Added Teardown() functions to Level, Doodad and the Chunker they both use
  to free up SDL2 textures for all their cached graphics.
* The Canvas.Destroy() function now cleans up all textures that the Canvas
  is responsible for: calling the Teardown() of the Level or Doodad, calling
  Destroy() on all level actors, and cleaning up Wallpaper textures.
* The Destroy() method of the game's various Scenes will properly Destroy()
  their canvases to clean up when transitioning to another scene. The
  MainScene, MenuScene, EditorScene and PlayScene.
* Fix the sprites package to actually cache the ui.Image widgets. The game
  has very few sprites so no need to free them just yet.

Some tricky places that were leaking textures have been cleaned up:

* Canvas.InstallActors() destroys the canvases of existing actors before it
  reinitializes the list and installs the replacements.
* The DraggableActor when the user is dragging an actor around their level
  cleans up the blueprint masked drag/drop actor before nulling it out.

Misc changes:

* The player character cheats during Play Mode will immediately swap out the
  player character on the current level.
* Properly call the Close() function instead of Hide() to dismiss popup
  windows. The Close() function itself calls Hide() but also triggers
  WindowClose event handlers. The Doodad Dropper subscribes to its close
  event to free textures for all its doodad canvases.
2022-04-09 14:41:24 -07:00

108 lines
2.6 KiB
Go

package doodads
import (
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/drawtool"
"git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/go/render"
)
// Doodad is a reusable component for Levels that have scripts and graphics.
type Doodad struct {
level.Base
Filename string `json:"-"` // used internally, not saved in json
Hidden bool `json:"hidden,omitempty"`
Palette *level.Palette `json:"palette"`
Script string `json:"script"`
Hitbox render.Rect `json:"hitbox"`
Layers []Layer `json:"layers"`
Tags map[string]string `json:"data"` // arbitrary key/value data storage
// Undo history, temporary live data not persisted to the level file.
UndoHistory *drawtool.History `json:"-"`
}
// Layer holds a layer of drawing data for a Doodad.
type Layer struct {
Name string `json:"name"`
Chunker *level.Chunker `json:"chunks"`
}
// New creates a new Doodad.
func New(size int) *Doodad {
if size == 0 {
size = balance.DoodadSize
}
return &Doodad{
Base: level.Base{
Version: 1,
},
Palette: level.DefaultPalette(),
Hitbox: render.NewRect(size, size),
Layers: []Layer{
{
Name: "main",
Chunker: level.NewChunker(size),
},
},
Tags: map[string]string{},
UndoHistory: drawtool.NewHistory(balance.UndoHistory),
}
}
// Teardown cleans up texture cache memory when the doodad is no longer needed by the game.
func (d *Doodad) Teardown() {
var (
chunks int
textures int
)
for _, layer := range d.Layers {
for coord := range layer.Chunker.IterChunks() {
if chunk, ok := layer.Chunker.GetChunk(coord); ok {
freed := chunk.Teardown()
chunks++
textures += freed
}
}
}
// Debug log if any textures were actually freed.
if textures > 0 {
log.Debug("Teardown doodad (%s): Freed %d textures across %d chunks", d.Title, textures, chunks)
}
}
// Tag gets a value from the doodad's tags.
func (d *Doodad) Tag(name string) string {
if v, ok := d.Tags[name]; ok {
return v
}
log.Warn("Doodad(%s).Tag(%s): tag not defined", d.Title, name)
return ""
}
// ChunkSize returns the chunk size of the Doodad's first layer.
func (d *Doodad) ChunkSize() int {
return d.Layers[0].Chunker.Size
}
// Rect returns a rect of the ChunkSize for scaling a Canvas widget.
func (d *Doodad) Rect() render.Rect {
var size = d.ChunkSize()
return render.Rect{
W: size,
H: size,
}
}
// Inflate attaches the pixels to their swatches after loading from disk.
func (d *Doodad) Inflate() {
d.Palette.Inflate()
for _, layer := range d.Layers {
layer.Chunker.Inflate(d.Palette)
}
}