Put each body's photograph on it one frame at a time, so entering the Sun's system no longer stalls
buildMarker gave every photographed body its map at once. A texture is copied to the GPU in the first frame that draws it, and the 28 maps arrive within about 40 ms of each other, so that frame copied some 20 megapixels of JPEG (seven maps at 2048x1024) through copyExternalImageToTexture: a second long task of 135-162 ms about 1.25 s after entering, measured here four times on the committed renderer (reviewers measured 160-210 against 85-100 without the 18 new maps). de34fff's "adds no long task" was measured before those maps landed. A photographed body now starts in its kind's flat colour, as a derived one does, and its texture waits in a queue; each update() puts the first one that has loaded on its body. The copies are spread one a frame, and all 38 bodies have their maps within half a second of the first. In the running app, five fresh entries into the Sun's system at 1600x1000 left one long task of 52-66 ms or none at all ([66], [52], [62], [], [56] ms, where the committed renderer gave [62, 149], [56, 135], [74, 162], [78, 162]). Control: putting every loaded photograph on in one frame fails "puts them on their bodies once loaded, one a frame". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import { orientationAt } from '../../shared/astro/rotational-elements';
|
||||
import { BodyRecord, RotationalElements } from '../../shared/models/body.model';
|
||||
import { ExoplanetRecord } from '../../shared/models/exoplanet.model';
|
||||
import { SystemOrbitsRenderer } from './system-orbits-renderer';
|
||||
import { bodyTexturePath, loadCachedTexture } from '../../shared/rendering/texture-catalog';
|
||||
|
||||
/** The clock's UT date that names a TDB one: TT - UT, which moves by under a second a year, earlier. */
|
||||
const utOf = (jdTdb: number): number => jdTdb - ttMinusUtSeconds(jdTdb) / 86400;
|
||||
@@ -448,6 +449,32 @@ describe('rotation without IAU elements', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('photographs', () => {
|
||||
it('puts them on their bodies once loaded, one a frame, so the GPU is not handed every map at once', () => {
|
||||
// Ids no other test here draws, since the loaded textures are shared through the cache.
|
||||
const ids = ['ganymede', 'callisto'];
|
||||
const records: BodyRecord[] = ids.map((id, index) => ({
|
||||
id, systemStarId: 0, name: id, kind: 'planet', radiusKm: 2500, orbitSource: 'test',
|
||||
orbit: { semiMajorAxisAu: 1 + index, eccentricity: 0, inclinationDeg: 0, longitudeOfAscendingNodeDeg: 0, argumentOfPeriapsisDeg: 0, meanAnomalyAtEpochDeg: 0, epochJd: DEFAULT_EPOCH_JD },
|
||||
rates: keplerRates(1 + index, GM_SUN_AU3_PER_DAY2)
|
||||
}));
|
||||
const renderer = new SystemOrbitsRenderer(records, []);
|
||||
const maps = (): Array<THREE.Texture | null> => renderer.members.map((member) => ((member.marker as THREE.Mesh).material as THREE.MeshStandardMaterial).map);
|
||||
|
||||
renderer.update(DEFAULT_EPOCH_JD);
|
||||
expect(maps()).toEqual([null, null]); // not loaded yet: jsdom never loads an image
|
||||
|
||||
for (const id of ids) {
|
||||
loadCachedTexture(bodyTexturePath(id)!).image = { width: 2, height: 1 };
|
||||
}
|
||||
renderer.update(DEFAULT_EPOCH_JD);
|
||||
expect(maps().filter(Boolean)).toHaveLength(1);
|
||||
renderer.update(DEFAULT_EPOCH_JD);
|
||||
expect(maps()).toEqual(ids.map((id) => loadCachedTexture(bodyTexturePath(id)!)));
|
||||
renderer.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
describe('derived surfaces', () => {
|
||||
const maps = (renderer: SystemOrbitsRenderer): Array<THREE.Texture | null> =>
|
||||
renderer.members.map((member) => ((member.marker as THREE.Mesh).material as THREE.MeshStandardMaterial).map);
|
||||
|
||||
Reference in New Issue
Block a user