2022-04-10 19:39:27 +00:00
|
|
|
package uix
|
|
|
|
|
|
|
|
import (
|
|
|
|
"runtime"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/level"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/log"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/shmem"
|
|
|
|
"git.kirsle.net/go/render"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Memory optimization features of the Canvas.
|
|
|
|
|
|
|
|
/*
|
|
|
|
LoadUnloadChunks optimizes memory for (level) canvases by warming up chunk images
|
|
|
|
that fall within the LoadingViewport and freeing chunks that are outside of it.
|
|
|
|
*/
|
|
|
|
func (w *Canvas) LoadUnloadChunks() {
|
|
|
|
if w.level == nil || shmem.Tick%balance.CanvasLoadUnloadModuloTicks != 0 || !balance.Feature.LoadUnloadChunk {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
vp = w.LoadingViewport()
|
|
|
|
chunks = make(chan render.Point)
|
|
|
|
chunksInside = map[render.Point]interface{}{}
|
|
|
|
chunksTeardown = []*level.Chunk{}
|
|
|
|
cores = runtime.NumCPU()
|
|
|
|
wg sync.WaitGroup
|
|
|
|
|
|
|
|
// Collect metrics for the debug overlay.
|
|
|
|
resultInside int
|
|
|
|
resultOutside int
|
|
|
|
)
|
|
|
|
|
|
|
|
// Collect the chunks that are inside the viewport so we know which ones are not.
|
|
|
|
for chunk := range w.level.Chunker.IterViewportChunks(vp) {
|
|
|
|
chunksInside[chunk] = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Spawn background goroutines to process the chunks quickly.
|
|
|
|
for i := 0; i < cores; i++ {
|
|
|
|
wg.Add(1)
|
|
|
|
go func(i int) {
|
|
|
|
for coord := range chunks {
|
2022-04-30 03:34:59 +00:00
|
|
|
if _, ok := chunksInside[coord]; ok {
|
|
|
|
// This chunk is INSIDE our viewport, preload its bitmap.
|
|
|
|
if chunk, ok := w.level.Chunker.GetChunk(coord); ok {
|
2022-04-10 19:39:27 +00:00
|
|
|
_ = chunk.CachedBitmap(render.Invisible)
|
|
|
|
resultInside++
|
2022-04-30 03:34:59 +00:00
|
|
|
continue
|
2022-04-10 19:39:27 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-30 03:34:59 +00:00
|
|
|
|
|
|
|
// Chunks outside the viewport, we won't load them and
|
|
|
|
// the Chunker will flush them out to (zip) file.
|
|
|
|
resultOutside++
|
2022-04-10 19:39:27 +00:00
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
for chunk := range w.level.Chunker.IterChunks() {
|
|
|
|
chunks <- chunk
|
|
|
|
}
|
|
|
|
close(chunks)
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
// Tear down the SDL2 textures of chunks to free.
|
|
|
|
for i, chunk := range chunksTeardown {
|
|
|
|
if chunk == nil {
|
|
|
|
log.Error("LoadUnloadChunks: chunksTeardown#%d was nil??", i)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
chunk.Teardown()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Export the metrics for the debug overlay.
|
|
|
|
w.loadUnloadInside = resultInside
|
|
|
|
w.loadUnloadOutside = resultOutside
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadUnloadMetrics returns the canvas's stored metrics from the LoadUnloadChunks
|
|
|
|
// function, for the debug overlay.
|
|
|
|
func (w *Canvas) LoadUnloadMetrics() (inside, outside int) {
|
|
|
|
return w.loadUnloadInside, w.loadUnloadOutside
|
|
|
|
}
|