refactor: Simplify texture loading and disposal in draw-relief-icons renderer

This commit is contained in:
Azgaar 2026-03-12 19:36:25 +01:00
parent ae6d105bf1
commit dc6ff785ba
2 changed files with 19 additions and 31 deletions

View file

@ -4,7 +4,7 @@ import { byId } from "../utils";
export interface WebGLLayerConfig { export interface WebGLLayerConfig {
id: string; id: string;
setup: (group: Group) => void; // called once after WebGL2 confirmed; add meshes to group setup: (group: Group) => void; // called once after WebGL2 confirmed; add meshes to group
render: (group: Group) => void; // called each frame before renderer.render(); update uniforms/geometry render?: (group: Group) => void; // called each frame before renderer.render(); update uniforms/geometry
dispose: (group: Group) => void; // called on unregister(); dispose all GPU objects in group dispose: (group: Group) => void; // called on unregister(); dispose all GPU objects in group
} }
interface RegisteredLayer { interface RegisteredLayer {
@ -48,7 +48,7 @@ export class WebGL2LayerClass {
return true; return true;
} }
register(config: WebGLLayerConfig): void { register(config: WebGLLayerConfig) {
if (!this.scene) { if (!this.scene) {
// init() has not been called yet — queue for processing in init() // init() has not been called yet — queue for processing in init()
this.pendingConfigs.push(config); this.pendingConfigs.push(config);
@ -63,7 +63,7 @@ export class WebGL2LayerClass {
this.layers.set(config.id, { config, group }); this.layers.set(config.id, { config, group });
} }
unregister(id: string): void { unregister(id: string) {
const layer = this.layers.get(id); const layer = this.layers.get(id);
if (!layer || !this.scene) return; if (!layer || !this.scene) return;
const scene = this.scene; const scene = this.scene;
@ -74,7 +74,7 @@ export class WebGL2LayerClass {
if (this.canvas && !anyVisible) this.canvas.style.display = "none"; if (this.canvas && !anyVisible) this.canvas.style.display = "none";
} }
setVisible(id: string, visible: boolean): void { setVisible(id: string, visible: boolean) {
const layer = this.layers.get(id); const layer = this.layers.get(id);
if (!layer) return; if (!layer) return;
layer.group.visible = visible; layer.group.visible = visible;
@ -83,14 +83,14 @@ export class WebGL2LayerClass {
if (visible) this.requestRender(); if (visible) this.requestRender();
} }
clearLayer(id: string): void { clearLayer(id: string) {
const layer = this.layers.get(id); const layer = this.layers.get(id);
if (!layer) return; if (!layer) return;
layer.group.clear(); layer.group.clear();
this.requestRender(); this.requestRender();
} }
requestRender(): void { requestRender() {
if (this.rafId !== null) return; if (this.rafId !== null) return;
this.rafId = requestAnimationFrame(() => { this.rafId = requestAnimationFrame(() => {
this.rafId = null; this.rafId = null;
@ -98,7 +98,7 @@ export class WebGL2LayerClass {
}); });
} }
syncTransform(): void { private syncTransform() {
if (!this.camera) return; if (!this.camera) return;
const x = -viewX / scale; const x = -viewX / scale;
const y = -viewY / scale; const y = -viewY / scale;
@ -112,11 +112,12 @@ export class WebGL2LayerClass {
this.camera.updateProjectionMatrix(); this.camera.updateProjectionMatrix();
} }
private render(): void { private render() {
if (!this.renderer || !this.scene || !this.camera) return; if (!this.renderer || !this.scene || !this.camera) return;
this.syncTransform(); this.syncTransform();
for (const layer of this.layers.values()) { for (const layer of this.layers.values()) {
if (layer.group.visible) layer.config.render(layer.group); if (layer.group.visible && layer.config.render)
layer.config.render(layer.group);
} }
this.renderer.render(this.scene, this.camera); this.renderer.render(this.scene, this.camera);
} }

View file

@ -25,10 +25,7 @@ WebGLLayer.register({
id: "terrain", id: "terrain",
setup(group: Group): void { setup(group: Group): void {
terrainGroup = group; terrainGroup = group;
preloadTextures(); for (const set of Object.keys(RELIEF_SYMBOLS)) loadTexture(set);
},
render(_group: Group): void {
// no-op: relief geometry is static between drawRelief() calls
}, },
dispose(group: Group): void { dispose(group: Group): void {
group.traverse((obj) => { group.traverse((obj) => {
@ -38,14 +35,11 @@ WebGLLayer.register({
(obj.material as MeshBasicMaterial).dispose(); (obj.material as MeshBasicMaterial).dispose();
} }
}); });
disposeTextureCache(); for (const tex of textureCache.values()) tex?.dispose();
textureCache.clear();
}, },
}); });
function preloadTextures(): void {
for (const set of Object.keys(RELIEF_SYMBOLS)) loadTexture(set);
}
function loadTexture(set: string): Promise<Texture | null> { function loadTexture(set: string): Promise<Texture | null> {
if (textureCache.has(set)) if (textureCache.has(set))
return Promise.resolve(textureCache.get(set) ?? null); return Promise.resolve(textureCache.get(set) ?? null);
@ -147,11 +141,6 @@ function buildSetMesh(
return new Mesh(geo, mat); return new Mesh(geo, mat);
} }
function disposeTextureCache(): void {
for (const tex of textureCache.values()) tex?.dispose();
textureCache.clear();
}
function buildReliefScene(icons: ReliefIcon[]): void { function buildReliefScene(icons: ReliefIcon[]): void {
if (!terrainGroup) return; if (!terrainGroup) return;
terrainGroup.traverse((obj) => { terrainGroup.traverse((obj) => {
@ -208,14 +197,12 @@ window.drawRelief = (
drawSvg(icons, parentEl); drawSvg(icons, parentEl);
} else { } else {
const set = parentEl.getAttribute("set") || "simple"; const set = parentEl.getAttribute("set") || "simple";
loadTexture(set).then(() => { if (icons !== lastBuiltIcons || set !== lastBuiltSet) {
if (icons !== lastBuiltIcons || set !== lastBuiltSet) { buildReliefScene(icons);
buildReliefScene(icons); lastBuiltIcons = icons;
lastBuiltIcons = icons; lastBuiltSet = set;
lastBuiltSet = set; }
} WebGLLayer.requestRender();
WebGLLayer.requestRender();
});
} }
}; };