texture GC와 idle resource eviction
GPU resource는 만들기만 하면 끝이 아닙니다. 한동안 쓰지 않는 texture와 cached render target을 언제 내릴지 정책이 필요합니다.
frame budget 안에서 texture를 해제하는 코드
해제도 한 프레임에 몰아서 하지 말고 budget을 둡니다.
function collectTextureGarbage(cache, frame, budget) {
const candidates = [...cache.values()]
.filter((texture) => frame - texture.lastUsedFrame > budget.maxIdleFrames)
.sort((a, b) => a.lastUsedFrame - b.lastUsedFrame);
let destroyed = 0;
let freedBytes = 0;
for (const texture of candidates) {
if (destroyed >= budget.destroyPerFrameLimit) break;
texture.destroy();
cache.delete(texture.key);
destroyed += 1;
freedBytes += texture.bytes;
}
return { destroyed, freedBytes };
}
eviction 기준
lastUsedFrame
memoryBudget
assetPriority
visibleSoon
destroyPerFrameLimit
많은 texture를 한 번에 해제하면 그 순간 프레임이 튈 수 있습니다. 삭제도 작업량을 나눠 처리하는 편이 좋습니다.
visibleSoon asset은 eviction에서 제외한다
function markVisibleSoonTextures(cache, viewport, sceneIndex) {
const expanded = expandRect(viewport.worldBounds, viewport.width * 0.5);
const nearNodes = sceneIndex.queryRect(expanded);
for (const node of nearNodes) {
if (!node.textureId) continue;
const texture = cache.get(node.textureId);
if (texture) texture.visibleSoon = true;
}
}
function canEvict(texture, frame, budget) {
if (texture.pinned || texture.visibleSoon) return false;
return frame - texture.lastUsedFrame > budget.maxIdleFrames;
}
지금 화면에 없더라도 곧 보일 가능성이 높은 texture를 지우면 pan 직후 다시 upload가 발생합니다. eviction 정책은 현재 visible뿐 아니라 near viewport도 함께 봐야 합니다.
오늘의 핵심
dispose는 즉시 해제이고, texture GC는 정책입니다. editor는 둘 다 필요합니다.