diff --git a/CHANGES.md b/CHANGES.md index 690dbc14cd7..564ac774993 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,7 +12,7 @@ #### Additions :tada: - Add `ITwinData.loadGeospatialFeatures(iTwinId, collectionId)` function to load data from the [Geospatial Features API](https://developer.bentley.com/apis/geospatial-features/operations/get-features/) [#12449](https://github.com/CesiumGS/cesium/pull/12449) -- Implemented `texturesByteLength`, `visited`, and `numberOfTilesWithContentReady` in `VoxelPrimitive.statistics`. +- Implemented `texturesByteLength`, `visited`, and `numberOfTilesWithContentReady` in `VoxelPrimitive.statistics`. To use statistics, set `options.calculateStatistics` to `true` in the constructor. Note `VoxelPrimitive` is experimental. #### Fixes :wrench: diff --git a/packages/engine/Source/Scene/VoxelPrimitive.js b/packages/engine/Source/Scene/VoxelPrimitive.js index 5134067b9a8..fc9ff0d2532 100644 --- a/packages/engine/Source/Scene/VoxelPrimitive.js +++ b/packages/engine/Source/Scene/VoxelPrimitive.js @@ -38,6 +38,7 @@ import Cesium3DTilesetStatistics from "./Cesium3DTilesetStatistics.js"; * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The model matrix used to transform the primitive. * @param {CustomShader} [options.customShader] The custom shader used to style the primitive. * @param {Clock} [options.clock] The clock used to control time dynamic behavior. + * @param {Boolean} [options.calculateStatistics] Generate statistics for performance profile. * * @see VoxelProvider * @see Cesium3DTilesVoxelProvider @@ -77,6 +78,12 @@ function VoxelPrimitive(options) { */ this._statistics = new Cesium3DTilesetStatistics(); + /** + * @type {boolean} + * @private + */ + this._calculateStatistics = defaultValue(options.calculateStatistics, false); + /** * This member is not created until the provider is ready. * @@ -1062,6 +1069,7 @@ Object.defineProperties(VoxelPrimitive.prototype, { /** * Loading and rendering information for requested content + * To use `visited` and `numberOfTilesWithContentReady` statistics, set options._calculateStatistics` to `true` in the constructor. * @type {Cesium3DTilesetStatistics} * @readonly * @private @@ -1145,8 +1153,6 @@ VoxelPrimitive.prototype.update = function (frameState) { this._clock, ); - this.statistics.clear(); - const traversal = this._traversal; const sampleCountOld = traversal._sampleCount; diff --git a/packages/engine/Source/Scene/VoxelTraversal.js b/packages/engine/Source/Scene/VoxelTraversal.js index 38ca5327bb1..ef15099180c 100644 --- a/packages/engine/Source/Scene/VoxelTraversal.js +++ b/packages/engine/Source/Scene/VoxelTraversal.js @@ -1,6 +1,7 @@ import Cartesian2 from "../Core/Cartesian2.js"; import CesiumMath from "../Core/Math.js"; import CullingVolume from "../Core/CullingVolume.js"; +import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; import DoubleEndedPriorityQueue from "../Core/DoubleEndedPriorityQueue.js"; @@ -93,6 +94,15 @@ function VoxelTraversal( */ this._debugPrint = false; + /** + * @type {boolean} + * @private + */ + this._calculateStatistics = defaultValue( + this._primitive._calculateStatistics, + false, + ); + /** * @type {number} * @private @@ -332,16 +342,23 @@ VoxelTraversal.prototype.update = function ( generateOctree(this, sampleCount, levelBlendFactor); const timestamp2 = getTimestamp(); - const loadAndUnloadTimeMs = timestamp1 - timestamp0; - const generateOctreeTimeMs = timestamp2 - timestamp1; - const totalTimeMs = timestamp2 - timestamp0; - postPassesUpdate( - this, - frameState, - loadAndUnloadTimeMs, - generateOctreeTimeMs, - totalTimeMs, - ); + const checkEventListeners = + primitive.loadProgress.numberOfListeners > 0 || + primitive.allTilesLoaded.numberOfListeners > 0 || + primitive.initialTilesLoaded.numberOfListeners > 0; + + if (this._debugPrint || this._calculateStatistics || checkEventListeners) { + const loadAndUnloadTimeMs = timestamp1 - timestamp0; + const generateOctreeTimeMs = timestamp2 - timestamp1; + const totalTimeMs = timestamp2 - timestamp0; + postPassesUpdate( + this, + frameState, + loadAndUnloadTimeMs, + generateOctreeTimeMs, + totalTimeMs, + ); + } }; /** @@ -794,14 +811,6 @@ function postPassesUpdate( loadStateByCount[KeyframeNode.LoadState.LOADED]; primitive.statistics.visited = nodeCountTotal; - const checkEventListeners = - primitive.loadProgress.numberOfListeners > 0 || - primitive.allTilesLoaded.numberOfListeners > 0 || - primitive.initialTilesLoaded.numberOfListeners > 0; - if (!that._debugPrint && !checkEventListeners) { - return; - } - const numberOfPendingRequests = loadStateByCount[KeyframeNode.LoadState.RECEIVING]; const numberOfTilesProcessing = diff --git a/packages/engine/Specs/Scene/VoxelPrimitiveSpec.js b/packages/engine/Specs/Scene/VoxelPrimitiveSpec.js index 50ead9862c1..9abd5141528 100644 --- a/packages/engine/Specs/Scene/VoxelPrimitiveSpec.js +++ b/packages/engine/Specs/Scene/VoxelPrimitiveSpec.js @@ -70,7 +70,7 @@ describe( expect(spyUpdate.calls.count()).toEqual(1); }); - it("initial tiles loaded and all tiles loaded events are raised and statistics are updates", async function () { + it("initial tiles loaded and all tiles loaded events are raised", async function () { const spyUpdate1 = jasmine.createSpy("listener"); const spyUpdate2 = jasmine.createSpy("listener"); const primitive = new VoxelPrimitive({ provider }); @@ -83,11 +83,56 @@ describe( }); expect(spyUpdate1.calls.count()).toEqual(1); expect(spyUpdate2.calls.count()).toEqual(1); + }); + + it("statistics are updated when event listeners are assigned", async function () { + const spyUpdate1 = jasmine.createSpy("listener"); + const spyUpdate2 = jasmine.createSpy("listener"); + const primitive = new VoxelPrimitive({ provider }); + scene.primitives.add(primitive); + primitive.allTilesLoaded.addEventListener(spyUpdate1); + primitive.initialTilesLoaded.addEventListener(spyUpdate2); + await pollToPromise(() => { + scene.renderForSpecs(); + return primitive._traversal._initialTilesLoaded; + }); expect(primitive.statistics.numberOfTilesWithContentReady).toEqual(1); expect(primitive.statistics.visited).toEqual(1); expect(primitive.statistics.texturesByteLength).toEqual(134217728); }); + it("statistics are updated when constructor option is true", async function () { + const primitive = new VoxelPrimitive({ + provider, + calculateStatistics: true, + }); + scene.primitives.add(primitive); + await pollToPromise(() => { + scene.renderForSpecs(); + return primitive.ready; + }); + await pollToPromise(() => { + scene.renderForSpecs(); + return primitive._traversal._initialTilesLoaded; + }); + expect(primitive.statistics.numberOfTilesWithContentReady).toEqual(1); + expect(primitive.statistics.visited).toEqual(1); + }); + + it("statistics are not updated when constructor option is false", async function () { + const primitive = new VoxelPrimitive({ provider }); + scene.primitives.add(primitive); + await pollToPromise(() => { + scene.renderForSpecs(); + return primitive.ready; + }); + for (let i = 0; i < 10; i++) { + scene.renderForSpecs(); + } + expect(primitive.statistics.numberOfTilesWithContentReady).toEqual(0); + expect(primitive.statistics.visited).toEqual(0); + }); + it("tile load, load progress and tile visible events are raised", async function () { const spyUpdate1 = jasmine.createSpy("listener"); const spyUpdate2 = jasmine.createSpy("listener"); diff --git a/packages/engine/Specs/Scene/VoxelTraversalSpec.js b/packages/engine/Specs/Scene/VoxelTraversalSpec.js index 74a19543178..00222076e4f 100644 --- a/packages/engine/Specs/Scene/VoxelTraversalSpec.js +++ b/packages/engine/Specs/Scene/VoxelTraversalSpec.js @@ -173,12 +173,11 @@ describe( const megatexture = traversal.megatextures[0]; expect(megatexture.occupiedCount).toBe(1); - expect(traversal._primitive.statistics.texturesByteLength).toEqual( - 134217728, - ); + expect(traversal.textureMemoryByteLength).toEqual(500); }); it("tile failed event is raised", async function () { + traversal._calculateStatistics = true; const keyFrameLocation = 0; const recomputeBoundingVolumes = true; const pauseUpdate = false;