fix: harden GIF support — ref-counted sources, defensive detection, play budget

1. Move GIF loading behind TextureManager.loadGif/releaseGif with
   ref-counting (fixes shared-source invalidation on duplicate GIFs)
2. Strip query strings/fragments before .gif extension check
3. GIFs now load paused — separate MAX_PLAYING_GIFS=8 budget in
   culling system, only nearest N animate (others show static frame)
This commit is contained in:
Hiren Kangad
2026-03-10 19:41:50 +05:30
parent 5a64708bd4
commit e45fce3e67
4 changed files with 122 additions and 15 deletions
+18 -1
View File
@@ -176,6 +176,7 @@ const PixiCanvas = forwardRef<PixiCanvasHandle, PixiCanvasProps>(
// Distance-based priority: nearest to viewport center get resources first.
const MAX_LOADED_TEXTURES = 60; // max image textures in GPU
const MAX_PLAYING_GIFS = 8; // max GIFs actively animating
const MAX_INITIALIZED_VIDEOS = 6; // max <video> elements (tier 1+)
const MAX_POSTER_VIDEOS = 4; // max poster textures in GPU (tier 2)
@@ -215,12 +216,20 @@ const PixiCanvas = forwardRef<PixiCanvasHandle, PixiCanvasProps>(
}
}
// ---- Image budget management ----
// ---- Image + GIF texture budget management ----
// Both static images and GIFs share the texture load budget.
// GIFs additionally have a separate play budget (only nearest N animate).
imageItems.sort((a, b) => a.dist - b.dist);
const shouldLoadImage = new Set(
imageItems.slice(0, MAX_LOADED_TEXTURES).map(i => i.item.id)
);
// Build GIF play budget: only nearest loaded GIFs should animate
const gifItems = imageItems.filter(i => i.sprite instanceof AnimatedGifSprite);
const shouldPlayGif = new Set(
gifItems.slice(0, MAX_PLAYING_GIFS).map(i => i.item.id)
);
let imgLoadsThisTick = 0;
for (const { item, sprite } of imageItems) {
if (shouldLoadImage.has(item.id) && !sprite.loaded && imgLoadsThisTick < 3) {
@@ -228,6 +237,14 @@ const PixiCanvas = forwardRef<PixiCanvasHandle, PixiCanvasProps>(
loadedImages.add(item.id);
imgLoadsThisTick++;
}
// Manage GIF play/stop based on play budget
if (sprite instanceof AnimatedGifSprite && sprite.loaded) {
if (shouldPlayGif.has(item.id)) {
sprite.play();
} else {
sprite.stop();
}
}
}
// Unload images that left the viewport (check tracked set, not all items)