mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
perf(globe): less churn in movers and lines
Review of Tasks 2–3: - the mover store's instance checks no longer build arrays or closures per set() (orientation and colour checked field by field), and dirty() returns one reused range object; - Cesium models get their matrix and colour through scratch objects (Model.modelMatrix and Model.color copy what they are given) instead of a new Matrix4 and Color per placement; - a destroyed MoverSet releases its store (count 0, empty instance arrays, no pick ids) so a set object a layer still holds keeps no memory, and the Cesium parts drop their billboard, point and model lists; - (with the overlay commit) the Navara line viewport is updated in place. Node micro-measure, 10 000 instances set + packed per frame: 0.27-0.29 ms, about 1.7 KB per frame (0.17 B per instance), as before. Contract: Cesium 70/70, Navara 76/76.
This commit is contained in:
1 parent
d483e5897e
commit
39d522c9a9
3 files changed
+84
-17
No files matched your search
@@ -12,6 +12,8 @@ const scratchPosition = new Cesium.Cartesian3();
|
|||||||
const scratchColor = new Cesium.Color();
|
const scratchColor = new Cesium.Color();
|
||||||
const scratchQuaternion = new Cesium.Quaternion();
|
const scratchQuaternion = new Cesium.Quaternion();
|
||||||
const scratchRotation = new Cesium.Matrix3();
|
const scratchRotation = new Cesium.Matrix3();
|
||||||
|
// Model.modelMatrix and Model.color copy what they are given.
|
||||||
|
const scratchModelMatrix = new Cesium.Matrix4();
|
||||||
|
|
||||||
/** The atlas cells as canvases, by key (a DOM canvas or image source). */
|
/** The atlas cells as canvases, by key (a DOM canvas or image source). */
|
||||||
async function cropAtlasCells(atlas, keys) {
|
async function cropAtlasCells(atlas, keys) {
|
||||||
@@ -143,6 +145,9 @@ function spritePart(scene, options, store, factories, requestRender, owners) {
|
|||||||
},
|
},
|
||||||
destroy() {
|
destroy() {
|
||||||
destroyed = true;
|
destroyed = true;
|
||||||
|
billboards.length = 0;
|
||||||
|
shownCell.length = 0;
|
||||||
|
cells = null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -180,7 +185,9 @@ function pointPart(options, store, factories, requestRender, owners) {
|
|||||||
}
|
}
|
||||||
requestRender('movers');
|
requestRender('movers');
|
||||||
},
|
},
|
||||||
destroy() {},
|
destroy() {
|
||||||
|
points.length = 0;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,13 +210,12 @@ function modelPart(options, store, factories, requestRender, owners) {
|
|||||||
item.modelMatrix = Cesium.Matrix4.fromRotationTranslation(
|
item.modelMatrix = Cesium.Matrix4.fromRotationTranslation(
|
||||||
Cesium.Matrix3.fromQuaternion(scratchQuaternion, scratchRotation),
|
Cesium.Matrix3.fromQuaternion(scratchQuaternion, scratchRotation),
|
||||||
readPosition(store, index),
|
readPosition(store, index),
|
||||||
new Cesium.Matrix4(),
|
scratchModelMatrix,
|
||||||
);
|
);
|
||||||
item.scale = store.scales[index];
|
item.scale = store.scales[index];
|
||||||
item.show = index < store.count && store.shows[index] === 1;
|
item.show = index < store.count && store.shows[index] === 1;
|
||||||
item.id = store.pickIds[index];
|
item.id = store.pickIds[index];
|
||||||
if (model.colorBlend)
|
if (model.colorBlend) item.color = readColor(store, index);
|
||||||
item.color = Cesium.Color.clone(readColor(store, index));
|
|
||||||
};
|
};
|
||||||
const ensure = (index) => {
|
const ensure = (index) => {
|
||||||
if (models[index] || loading[index]) return;
|
if (models[index] || loading[index]) return;
|
||||||
@@ -258,6 +264,8 @@ function modelPart(options, store, factories, requestRender, owners) {
|
|||||||
},
|
},
|
||||||
destroy() {
|
destroy() {
|
||||||
destroyed = true;
|
destroyed = true;
|
||||||
|
models.length = 0;
|
||||||
|
loading.length = 0;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+51
-12
@@ -150,17 +150,18 @@ export function normalizeMoverSetOptions(options) {
|
|||||||
export function createMoverStore(options) {
|
export function createMoverStore(options) {
|
||||||
const { capacity, cellKeys, kind } = options;
|
const { capacity, cellKeys, kind } = options;
|
||||||
const cellIndex = new Map(cellKeys.map((key, index) => [key, index]));
|
const cellIndex = new Map(cellKeys.map((key, index) => [key, index]));
|
||||||
const positions = new Float64Array(capacity * 3);
|
let positions = new Float64Array(capacity * 3);
|
||||||
const orientations = new Float64Array(capacity * 4);
|
let orientations = new Float64Array(capacity * 4);
|
||||||
const colors = new Float32Array(capacity * 4).fill(1);
|
let colors = new Float32Array(capacity * 4).fill(1);
|
||||||
const rotations = new Float32Array(capacity);
|
let rotations = new Float32Array(capacity);
|
||||||
const scales = new Float32Array(capacity).fill(1);
|
let scales = new Float32Array(capacity).fill(1);
|
||||||
const cells = new Uint16Array(capacity);
|
let cells = new Uint16Array(capacity);
|
||||||
const shows = new Uint8Array(capacity).fill(1);
|
let shows = new Uint8Array(capacity).fill(1);
|
||||||
const pickIds = new Array(capacity);
|
let pickIds = new Array(capacity);
|
||||||
for (let index = 0; index < capacity; index++)
|
for (let index = 0; index < capacity; index++)
|
||||||
orientations[index * 4 + 3] = 1;
|
orientations[index * 4 + 3] = 1;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
const range = { from: 0, to: 0 };
|
||||||
let dirtyFrom = Infinity;
|
let dirtyFrom = Infinity;
|
||||||
let dirtyTo = -Infinity;
|
let dirtyTo = -Infinity;
|
||||||
const touch = (from, to) => {
|
const touch = (from, to) => {
|
||||||
@@ -189,7 +190,10 @@ export function createMoverStore(options) {
|
|||||||
orientation !== undefined &&
|
orientation !== undefined &&
|
||||||
!(
|
!(
|
||||||
isObject(orientation) &&
|
isObject(orientation) &&
|
||||||
['x', 'y', 'z', 'w'].every((axis) => isFiniteNumber(orientation[axis]))
|
isFiniteNumber(orientation.x) &&
|
||||||
|
isFiniteNumber(orientation.y) &&
|
||||||
|
isFiniteNumber(orientation.z) &&
|
||||||
|
isFiniteNumber(orientation.w)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
throw new TypeError('Mover orientation must be a quaternion');
|
throw new TypeError('Mover orientation must be a quaternion');
|
||||||
@@ -198,7 +202,10 @@ export function createMoverStore(options) {
|
|||||||
!(
|
!(
|
||||||
(Array.isArray(color) || ArrayBuffer.isView(color)) &&
|
(Array.isArray(color) || ArrayBuffer.isView(color)) &&
|
||||||
color.length === 4 &&
|
color.length === 4 &&
|
||||||
Array.prototype.every.call(color, isFiniteNumber)
|
isFiniteNumber(color[0]) &&
|
||||||
|
isFiniteNumber(color[1]) &&
|
||||||
|
isFiniteNumber(color[2]) &&
|
||||||
|
isFiniteNumber(color[3])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
throw new TypeError('Mover color must be [r, g, b, a]');
|
throw new TypeError('Mover color must be [r, g, b, a]');
|
||||||
@@ -256,9 +263,40 @@ export function createMoverStore(options) {
|
|||||||
count = next;
|
count = next;
|
||||||
},
|
},
|
||||||
positionsView: () => positions.subarray(0, count * 3),
|
positionsView: () => positions.subarray(0, count * 3),
|
||||||
|
/**
|
||||||
|
* Drops the instance arrays (a destroyed set): the count goes to 0 and
|
||||||
|
* the arrays to empty ones, so a set object still held keeps no memory.
|
||||||
|
*/
|
||||||
|
release() {
|
||||||
|
count = 0;
|
||||||
|
positions = new Float64Array(0);
|
||||||
|
orientations = new Float64Array(0);
|
||||||
|
colors = new Float32Array(0);
|
||||||
|
rotations = new Float32Array(0);
|
||||||
|
scales = new Float32Array(0);
|
||||||
|
cells = new Uint16Array(0);
|
||||||
|
shows = new Uint8Array(0);
|
||||||
|
pickIds = [];
|
||||||
|
Object.assign(this, {
|
||||||
|
positions,
|
||||||
|
orientations,
|
||||||
|
colors,
|
||||||
|
rotations,
|
||||||
|
scales,
|
||||||
|
cells,
|
||||||
|
shows,
|
||||||
|
pickIds,
|
||||||
|
});
|
||||||
|
this.clean();
|
||||||
|
},
|
||||||
/** Indices changed since clean(), as [from, to), or null. */
|
/** Indices changed since clean(), as [from, to), or null. */
|
||||||
dirty: () =>
|
/** The changed range (one object, reused: read it at once), or null. */
|
||||||
dirtyFrom === Infinity ? null : { from: dirtyFrom, to: dirtyTo },
|
dirty() {
|
||||||
|
if (dirtyFrom === Infinity) return null;
|
||||||
|
range.from = dirtyFrom;
|
||||||
|
range.to = dirtyTo;
|
||||||
|
return range;
|
||||||
|
},
|
||||||
clean() {
|
clean() {
|
||||||
dirtyFrom = Infinity;
|
dirtyFrom = Infinity;
|
||||||
dirtyTo = -Infinity;
|
dirtyTo = -Infinity;
|
||||||
@@ -296,6 +334,7 @@ export function createMoverSet(store, { commit, destroy }) {
|
|||||||
if (destroyed) return;
|
if (destroyed) return;
|
||||||
destroyed = true;
|
destroyed = true;
|
||||||
destroy();
|
destroy();
|
||||||
|
store.release?.();
|
||||||
},
|
},
|
||||||
isDestroyed: () => destroyed,
|
isDestroyed: () => destroyed,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ test('the set facade forwards to the store and an engine commit, once destroyed
|
|||||||
);
|
);
|
||||||
const calls = [];
|
const calls = [];
|
||||||
const set = createMoverSet(store, {
|
const set = createMoverSet(store, {
|
||||||
commit: () => calls.push(['commit', store.dirty()]),
|
commit: () => calls.push(['commit', { ...store.dirty() }]),
|
||||||
destroy: () => calls.push(['destroy']),
|
destroy: () => calls.push(['destroy']),
|
||||||
});
|
});
|
||||||
set.set(0, { position: { x: 1, y: 2, z: 3 } });
|
set.set(0, { position: { x: 1, y: 2, z: 3 } });
|
||||||
@@ -187,3 +187,23 @@ test('the set facade forwards to the store and an engine commit, once destroyed
|
|||||||
assert.throws(() => set.set(0, {}), /destroyed/);
|
assert.throws(() => set.set(0, {}), /destroyed/);
|
||||||
assert.throws(() => set.commit(), /destroyed/);
|
assert.throws(() => set.commit(), /destroyed/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a destroyed set releases its instance arrays', () => {
|
||||||
|
const store = createMoverStore(
|
||||||
|
normalizeMoverSetOptions({
|
||||||
|
id: 'r',
|
||||||
|
kind: 'point',
|
||||||
|
capacity: 1000,
|
||||||
|
point: { sizePx: 2 },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const set = createMoverSet(store, { commit() {}, destroy() {} });
|
||||||
|
set.set(0, { position: { x: 1, y: 2, z: 3 }, pickId: 'p' });
|
||||||
|
set.setCount(1);
|
||||||
|
set.destroy();
|
||||||
|
assert.equal(store.count, 0);
|
||||||
|
assert.equal(store.positions.length, 0);
|
||||||
|
assert.equal(store.pickIds.length, 0);
|
||||||
|
assert.equal(set.positionsView().length, 0);
|
||||||
|
assert.equal(store.dirty(), null);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user