mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-08 12:23:59 +00:00
Dispose the Navara bloom and sharpen passes when their effect is deleted
EffectComposer.removePass does not dispose a pass. One withPassDisposal mixin now does it for the bloom, sharpen, styles and overlay effects.
This commit is contained in:
1 parent
b49f9dddf0
commit
cbe1d841ea
6 files changed
+67
-27
No files matched your search
@@ -9,6 +9,7 @@ import { Effect as NavaraEffect, EffectDesc } from '@navaramap/three';
|
||||
import { BloomEffect } from 'postprocessing';
|
||||
import { createBloomPort } from './bloomSettings.js';
|
||||
import { pipelinePassNames } from './internals.js';
|
||||
import { withPassDisposal } from './frameDescriptor.js';
|
||||
|
||||
export const BLOOM_EFFECT_KEY = 'gevBloom';
|
||||
|
||||
@@ -20,7 +21,7 @@ function apply(effect, settings) {
|
||||
}
|
||||
|
||||
/** config.gevBloom: { settings } (bloomSettingsFromStrength). */
|
||||
class BloomEffectDesc extends EffectDesc {
|
||||
class BloomEffectDesc extends withPassDisposal(EffectDesc) {
|
||||
static key = BLOOM_EFFECT_KEY;
|
||||
// Nothing anchors after it but the styles: before them, else before the
|
||||
// final copy (the styles anchor after it).
|
||||
|
||||
@@ -16,3 +16,20 @@ export function withFrameUpdates(Base) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Navara's EffectDesc.onDestroy takes its pass out of the composer, and
|
||||
// EffectComposer.removePass does not dispose it: the pass's render targets,
|
||||
// materials and shader programs would stay on the GPU for every effect
|
||||
// added and deleted. A GEV effect disposes its own raw pass once removed.
|
||||
export function withPassDisposal(Base) {
|
||||
return class extends Base {
|
||||
onDestroy() {
|
||||
const pass = this.raw?.rawPass;
|
||||
try {
|
||||
super.onDestroy();
|
||||
} finally {
|
||||
pass?.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { withFrameUpdates } from './frameDescriptor.js';
|
||||
import { withFrameUpdates, withPassDisposal } from './frameDescriptor.js';
|
||||
|
||||
test('deleting a descriptor unsubscribes only that renderer from the warm view', () => {
|
||||
const frames = new Set();
|
||||
@@ -41,3 +41,39 @@ test('deleting a descriptor unsubscribes only that renderer from the warm view',
|
||||
b.onDestroy();
|
||||
assert.equal(frames.size, 0);
|
||||
});
|
||||
|
||||
test('a deleted effect disposes the pass the composer no longer holds', () => {
|
||||
const events = [];
|
||||
const pass = { dispose: () => events.push('dispose') };
|
||||
class Base {
|
||||
_instance = { rawPass: pass };
|
||||
get raw() {
|
||||
return this._instance;
|
||||
}
|
||||
onDestroy() {
|
||||
events.push('remove');
|
||||
this._instance = undefined;
|
||||
}
|
||||
}
|
||||
const effect = new (withPassDisposal(Base))();
|
||||
effect.onDestroy();
|
||||
assert.deepEqual(events, ['remove', 'dispose']);
|
||||
|
||||
// A pass that was never created: nothing to dispose.
|
||||
const empty = new (withPassDisposal(
|
||||
class {
|
||||
onDestroy() {}
|
||||
},
|
||||
))();
|
||||
assert.doesNotThrow(() => empty.onDestroy());
|
||||
|
||||
// The pass is disposed even when Navara's removal throws.
|
||||
class Failing extends Base {
|
||||
onDestroy() {
|
||||
throw Error('removal failed');
|
||||
}
|
||||
}
|
||||
events.length = 0;
|
||||
assert.throws(() => new (withPassDisposal(Failing))().onDestroy(), /removal/);
|
||||
assert.deepEqual(events, ['dispose']);
|
||||
});
|
||||
@@ -12,7 +12,7 @@
|
||||
// FIRMS opts into a separate native-stage overlay before sharpening/AA.
|
||||
// Only that stage owns an encoded-RGBA8 blend target; W1 stays in place.
|
||||
|
||||
import { withFrameUpdates } from './frameDescriptor.js';
|
||||
import { withFrameUpdates, withPassDisposal } from './frameDescriptor.js';
|
||||
import { EffectDesc, Pass as NavaraPass } from '@navaramap/three';
|
||||
import { GevOverlayPass } from './overlayPass.js';
|
||||
import { sonarUniformsFor } from './sonar.js';
|
||||
@@ -22,7 +22,10 @@ export const OVERLAY_EFFECT_KEY = 'gevOverlay';
|
||||
const PRE_SHARPEN_KEY = 'gevPreSharpenOverlay';
|
||||
const registeredViews = new WeakMap();
|
||||
|
||||
class GevOverlayEffectDesc extends withFrameUpdates(EffectDesc) {
|
||||
// The pass owns its GPU target and context event handlers until disposed.
|
||||
class GevOverlayEffectDesc extends withPassDisposal(
|
||||
withFrameUpdates(EffectDesc),
|
||||
) {
|
||||
static key = OVERLAY_EFFECT_KEY;
|
||||
// Navara takes the first existing anchor, so the latest pass comes first.
|
||||
static insertAfter = ['fxaa', 'smaa', 'gevSharpen', 'toneMapping'];
|
||||
@@ -45,17 +48,6 @@ class GevOverlayEffectDesc extends withFrameUpdates(EffectDesc) {
|
||||
);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
// Navara removes the pass from its composer; our pass owns its GPU target
|
||||
// and context event handlers until it is explicitly disposed.
|
||||
const pass = this.raw?.rawPass;
|
||||
try {
|
||||
super.onDestroy();
|
||||
} finally {
|
||||
pass?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Each drawn frame: the scene depth the overlays test against, then each
|
||||
// overlay's own per-frame work (pixel footprint, origin, viewport).
|
||||
updateFrame(time) {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// has no public call to merge effects. A disabled pass is skipped: off costs
|
||||
// nothing.
|
||||
|
||||
import { withFrameUpdates } from './frameDescriptor.js';
|
||||
import { withFrameUpdates, withPassDisposal } from './frameDescriptor.js';
|
||||
import { Effect as NavaraEffect, EffectDesc } from '@navaramap/three';
|
||||
import { Effect, EffectAttribute } from 'postprocessing';
|
||||
import { RGBADepthPacking, Uniform } from 'three';
|
||||
@@ -87,7 +87,9 @@ class UnsharpMaskEffect extends Effect {
|
||||
}
|
||||
|
||||
/** config.gevSharpen: { amount } (Cesium's `amount` uniform). */
|
||||
export class SharpenEffectDesc extends withFrameUpdates(EffectDesc) {
|
||||
export class SharpenEffectDesc extends withPassDisposal(
|
||||
withFrameUpdates(EffectDesc),
|
||||
) {
|
||||
static key = SHARPEN_EFFECT_KEY;
|
||||
static insertAfter = ['toneMapping'];
|
||||
static insertBefore = ['smaa', 'fxaa', 'gevOverlay', 'final'];
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
|
||||
import { EffectDesc, Pass as NavaraPass } from '@navaramap/three';
|
||||
import { pipelinePassNames } from './internals.js';
|
||||
import { withPassDisposal } from './frameDescriptor.js';
|
||||
import {
|
||||
GevStylesPass,
|
||||
STYLES_EFFECT_KEY,
|
||||
createStylesPort,
|
||||
} from './stylePass.js';
|
||||
|
||||
class StylesEffectDesc extends EffectDesc {
|
||||
class StylesEffectDesc extends withPassDisposal(EffectDesc) {
|
||||
static key = STYLES_EFFECT_KEY;
|
||||
// Last before Navara's final copy: after bloom when it is on, else after
|
||||
// everything the scene and its overlays draw (nothing anchors after it).
|
||||
@@ -31,15 +32,6 @@ class StylesEffectDesc extends EffectDesc {
|
||||
// postprocessing copy: wrap it (as overlay.js does).
|
||||
return new NavaraPass(new GevStylesPass(this.controller), null);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
const pass = this.raw?.rawPass;
|
||||
try {
|
||||
super.onDestroy();
|
||||
} finally {
|
||||
pass?.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user