From 06ae95ceb3136ff3fd149ef2701803e080759480 Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Wed, 4 Nov 2026 19:53:33 +0400 Subject: [PATCH] Keep scaling the shared atmosphere vectors in place, and take a rebuilt globe's base from the value they had before any scaling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pointing the scaled uniforms at the scaler's own vectors (bc1b56b6) left the atmosphere unscaled: takram's materials read the AtmosphereParameters.DEFAULT vectors themselves, so the frame dimmed (luma 161 against 214) and qa-navara-bloom found nothing to bloom. The scaler writes those vectors in place again, and a page-wide map keeps each one's value from before it was first scaled: a globe built later on the page takes its base from there instead of from the scaled value. Bloom measures as before (214.4 → 240.3), and qa-strict-context-loss passes 3 of 3 under eight busy threads. --- src/globe/navara/look.js | 67 +++++++++++++++------------------- src/globe/navara/look.test.mjs | 11 ++---- 2 files changed, 33 insertions(+), 45 deletions(-) diff --git a/src/globe/navara/look.js b/src/globe/navara/look.js index 36fabbd5a..ce8b8e8ad 100644 --- a/src/globe/navara/look.js +++ b/src/globe/navara/look.js @@ -253,70 +253,62 @@ const IRRADIANCE_OFF = Object.freeze({ const VISIBLE = Object.freeze({ visible: true }); const HIDDEN = Object.freeze({ visible: false }); +// Every luminance vector a scaler has written, with the value it had +// before: one map for the page, as the vectors are. +const unscaled = new WeakMap(); + /** * The atmosphere scale (viewOptions.js ATMOSPHERE_LUMINANCE_SCALE): each * luminance factor is set to `scale` times the base value recorded the - * first time it is seen, so a vector Navara shares between materials, or - * copies into a rebuilt one, is never scaled twice. A scaled uniform points - * at this scaler's own vector; the vector Navara built it with is never - * written. Those are takram's AtmosphereParameters.DEFAULT vectors, one per - * page: written in place, the next globe on the page (a rebuild after a lost - * context) recorded a scaled value as its base and drew the atmosphere - * scale² bright. The stars keep the base value. Returns `apply()`, true - * when it changed what a uniform holds. + * first time it is seen, never multiplied in place, so a vector Navara + * shares between materials, or copies into a rebuilt one, is never scaled + * twice. The stars keep the base value: when they share the sky's vector, + * they get their own. Returns `apply()`, true when it changed a uniform. + * + * The vectors are takram's AtmosphereParameters.DEFAULT ones, one per page + * and read by every globe's materials, so the base of a vector an earlier + * globe already scaled is the value it had before that (`unscaled`): a + * globe rebuilt on the page after a lost context took the scaled value as + * its base and drew the atmosphere scale² bright. */ export function createLuminanceScaler(scale, forEachUniform, handles) { if (scale === 1 || !forEachUniform) return () => false; - // key -> the vectors uniforms point at, and the values they must hold - const bases = new Map(); + const bases = new Map(); // key -> { base, target } + const scaledVectors = new WeakSet(); let changed = false; const entry = (key, value) => { let found = bases.get(key); if (!found) { - const baseValue = value.clone(); - const targetValue = value.clone().multiplyScalar(scale); + const base = unscaled.get(value) ?? value; found = { - base: baseValue.clone(), - target: targetValue.clone(), - baseValue, - targetValue, + base: base.clone(), + target: base.clone().multiplyScalar(scale), }; bases.set(key, found); } return found; }; - // A material may write into the vector its uniform holds. - const restore = () => { - for (const { base, target, baseValue, targetValue } of bases.values()) { - if (!base.equals(baseValue)) { - base.copy(baseValue); - changed = true; - } - if (!target.equals(targetValue)) { - target.copy(targetValue); - changed = true; - } - } - }; let applyScale = true; const record = (key, uniform) => entry(key, uniform.value); const scaleOne = (key, uniform, scaled) => { if (!scaled) return; const { base, target } = entry(key, uniform.value); const desired = applyScale ? target : base; - if (uniform.value === desired) return; - if (!uniform.value.equals(desired)) changed = true; - uniform.value = desired; + scaledVectors.add(uniform.value); + if (uniform.value.equals(desired)) return; + if (!unscaled.has(uniform.value)) + unscaled.set(uniform.value, uniform.value.clone()); + uniform.value.copy(desired); + changed = true; }; const keepBase = (key, uniform, scaled) => { if (scaled) return; - const { base, target } = entry(key, uniform.value); - // Never this scaler's own vectors: they change with the scale. - if (uniform.value === target || uniform.value === base) { - if (!uniform.value.equals(base)) changed = true; + const { base } = entry(key, uniform.value); + if (scaledVectors.has(uniform.value)) { uniform.value = base.clone(); + changed = true; } else if (!uniform.value.equals(base)) { - uniform.value = base.clone(); + uniform.value.copy(base); changed = true; } }; @@ -325,7 +317,6 @@ export function createLuminanceScaler(scale, forEachUniform, handles) { changed = false; // Bases first, from the values as Navara built them. forEachUniform(handles, record); - restore(); forEachUniform(handles, scaleOne); forEachUniform(handles, keepBase); return changed; diff --git a/src/globe/navara/look.test.mjs b/src/globe/navara/look.test.mjs index 304309cfa..8b7bdb15e 100644 --- a/src/globe/navara/look.test.mjs +++ b/src/globe/navara/look.test.mjs @@ -568,17 +568,14 @@ test('a second look on the page scales the shared atmosphere vectors from their }; const first = build(); near(first.sky[SKY].value.x, 1.5 * k); - assert.equal( - pageDefault[SKY].value.x, - 1.5, - 'the shared vector is not written', - ); - // The first globe's context is lost and a new one is built on the page. + // The first globe's context is lost and a new one is built on the page: + // its materials hold the vectors the first one scaled. const second = build(); near(second.sky[SKY].value.x, 1.5 * k); near(second.clouds[SUN].value.x, 1.3 * k); + near(pageDefault[SKY].value.x, 1.5 * k); assert.equal(second.stars[SKY].value.x, 1.5); - near(first.sky[SKY].value.x, 1.5 * k); + assert.equal(second.stars[SUN].value.x, 1.3); }); test('a value a material writes into the scaled vector is put back on the next frame', () => {