mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-01 23:42:25 -04:00
795276935c
- Shared render: cache Font encode/width, skip unchanged palette/ShaderFX uniform sends, reuse quads and scratch tables, cheaper sprite palettes - Mods: fast path for loaded requires, memoized sandbox verdicts, fewer Hooks:call allocations, drop emptied hook chains - Audio: bulk PCM writes, cry/SFX prewarm on the worker, sliced sync fallback, cached Gen 3 SE sources - Gen 1: per-frame SGB battle palettes, hoisted overworld FX, cached menus - Gen 2: numeric attr grid, indexed anim cells, entity culling, memoized cache keys, palette shader set once per tile run - Gen 3: reuse native tile batches, per-cell metatile rebuilds, partial FRLG tile anim uploads, near-view ghost ticking, packed FRLG font atlas - Fixes: emergency quit timer, Gen 3 batch regression and stale sprite index, trainer card texture leak, missing pic re-reads, anim tint cache, Crystal ghost grass overdraw, refresh-rate dependent counters, pad swap
133 lines
4.5 KiB
Lua
133 lines
4.5 KiB
Lua
#!/usr/bin/env luajit
|
|
-- Gen 3 Audio.playSe caches the static Source per (id, pan, gain, mono,
|
|
-- loop): a repeat SE neither re-bakes nor rebuilds SoundData / Source, the
|
|
-- player-slot bookkeeping (_seSources/_seByPlayer/_seMeta) behaves as with a
|
|
-- fresh Source, and anything baked into the PCM (pan, volume) still misses.
|
|
-- luajit tests/game3_se_source_cache_test.lua
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local S = require("tests.harness").suite("game3 se source cache")
|
|
local check, eq = S.check, S.eq
|
|
|
|
love = require("tests.love_stub")
|
|
|
|
local built, sources = 0, 0
|
|
local Source = {}
|
|
Source.__index = Source
|
|
function Source:play() self.playing = true; self.plays = self.plays + 1 end
|
|
function Source:stop() self.playing = false; self.stops = self.stops + 1 end
|
|
function Source:isPlaying() return self.playing end
|
|
function Source:setVolume(v) self.volume = v end
|
|
function Source:getVolume() return self.volume or 1 end
|
|
function Source:setLooping(v) self.looping = v end
|
|
function Source:clone()
|
|
sources = sources + 1
|
|
return setmetatable({ sd = self.sd, plays = 0, stops = 0, clone = true },
|
|
Source)
|
|
end
|
|
love.audio = love.audio or {}
|
|
love.audio.newSource = function(sd)
|
|
sources = sources + 1
|
|
return setmetatable({ sd = sd, plays = 0, stops = 0 }, Source)
|
|
end
|
|
local realNewSoundData = love.sound.newSoundData
|
|
love.sound.newSoundData = function(...)
|
|
built = built + 1
|
|
return realNewSoundData(...)
|
|
end
|
|
|
|
local Player = require("src.core.game3.m4a_player")
|
|
local bakes = 0
|
|
Player.songInfo = function(_, id)
|
|
return { kind = "se", player = (id == 7) and 2 or 1 }
|
|
end
|
|
Player.start = function() return true end
|
|
Player.bakeSlot = function()
|
|
bakes = bakes + 1
|
|
local L, R = {}, {}
|
|
for i = 1, 64 do L[i] = math.sin(i) * 0.5; R[i] = math.cos(i) * 0.5 end
|
|
return L, R
|
|
end
|
|
|
|
local Audio = require("src.core.game3.audio")
|
|
Audio._ready = true
|
|
Audio._pack = {}
|
|
Audio._cache = {}
|
|
Audio._seRawClear()
|
|
|
|
local function live(id)
|
|
local n = 0
|
|
for _, src in ipairs(Audio._seSources) do
|
|
local meta = Audio._seMeta[src]
|
|
if meta and meta.id == id then n = n + 1 end
|
|
end
|
|
return n
|
|
end
|
|
|
|
check(Audio.playSe(5), "first play")
|
|
local first = Audio._seByPlayer[1]
|
|
eq(bakes, 1, "first play bakes")
|
|
eq(sources, 1, "first play builds one Source")
|
|
check(first and first.playing, "first play is sounding")
|
|
|
|
check(Audio.playSe(5), "repeat play")
|
|
eq(bakes, 1, "repeat does not re-bake")
|
|
eq(sources, 1, "repeat reuses the cached Source")
|
|
check(Audio._seByPlayer[1] == first, "same Source on its player slot")
|
|
check(first.playing and first.plays == 2, "cached Source restarted")
|
|
eq(live(5), 1, "one live entry for the SE (the previous play was replaced)")
|
|
check(Audio._seMeta[first] and Audio._seMeta[first].id == 5
|
|
and Audio._seMeta[first].player == 1, "meta rebuilt for the replay")
|
|
|
|
-- a different SE on the same player replaces it, then the first comes back
|
|
local builtBefore = built
|
|
Audio.playSe(6)
|
|
check(not first.playing, "player 1 SE replaced stops the cached Source")
|
|
check(Audio._seMeta[first] == nil, "replaced Source forgotten")
|
|
Audio.playSe(5)
|
|
check(Audio._seByPlayer[1] == first and first.playing,
|
|
"cached Source reused after being replaced")
|
|
eq(built - builtBefore, 1, "only the new SE built SoundData")
|
|
|
|
-- PCM-baked parameters miss the cache
|
|
local n = sources
|
|
Audio.playSe(5, { pan = 63 })
|
|
eq(sources, n + 1, "a different pan builds its own Source")
|
|
Audio.playSe(5, { pan = 63 })
|
|
eq(sources, n + 1, "and that one is cached too")
|
|
Audio.playSe(5, { volume = 0.5 })
|
|
eq(sources, n + 2, "a different volume builds its own Source")
|
|
|
|
-- stopSe still stops and forgets it
|
|
Audio.playSe(5)
|
|
local cached = Audio._seByPlayer[1]
|
|
Audio.stopSe(5)
|
|
check(not cached.playing and Audio._seMeta[cached] == nil
|
|
and Audio._seByPlayer[1] == nil, "stopSe stops and forgets a cached Source")
|
|
|
|
-- a cached Source still tracked live (not replaced) is cloned, not cut off
|
|
Audio._seSources[#Audio._seSources + 1] = cached
|
|
Audio._seMeta[cached] = { id = 5, player = 3 }
|
|
cached.playing = true
|
|
Audio.playSe(5)
|
|
local now = Audio._seByPlayer[1]
|
|
check(now ~= cached and now.clone and now.playing,
|
|
"live cached Source is cloned for the new play")
|
|
check(cached.playing, "the live one keeps playing")
|
|
Audio.stopSe()
|
|
|
|
-- a re-install / session end drops the cache with the raw memo
|
|
Audio._seRawClear()
|
|
n = sources
|
|
Audio.playSe(5)
|
|
eq(sources, n + 1, "cache cleared with the raw memo")
|
|
eq(bakes, 3, "and the SE is baked again (5 and 6 before)")
|
|
|
|
-- non-memoable plays (explicit loop/maxSec) never touch the cache
|
|
n = sources
|
|
Audio.playSe(5, { maxSec = 1 })
|
|
Audio.playSe(5, { maxSec = 1 })
|
|
eq(sources, n + 2, "maxSec plays build fresh Sources")
|
|
|
|
S.finish()
|