From b7b4da0e70cee0c86335d80e6679f5197f0f8ce3 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 23 Nov 2024 13:44:08 +0100 Subject: [PATCH 1/9] Draft for handling shared textures in statistics --- packages/engine/Source/Renderer/Texture.js | 2 +- .../Source/Scene/Cesium3DTilesetStatistics.js | 110 +++++++++++++++++- .../engine/Source/Scene/GltfTextureLoader.js | 17 ++- .../Source/Scene/Model/Model3DTileContent.js | 10 ++ .../Source/Scene/Model/ModelStatistics.js | 24 +++- 5 files changed, 153 insertions(+), 10 deletions(-) diff --git a/packages/engine/Source/Renderer/Texture.js b/packages/engine/Source/Renderer/Texture.js index 8d9135fb7764..5c7088ad7009 100644 --- a/packages/engine/Source/Renderer/Texture.js +++ b/packages/engine/Source/Renderer/Texture.js @@ -216,7 +216,7 @@ function Texture(options) { ? PixelFormat.compressedTextureSizeInBytes(pixelFormat, width, height) : PixelFormat.textureSizeInBytes(pixelFormat, pixelDatatype, width, height); - this._id = createGuid(); + this._id = options.id ?? createGuid(); this._context = context; this._textureFilterAnisotropic = context._textureFilterAnisotropic; this._textureTarget = gl.TEXTURE_2D; diff --git a/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js b/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js index ed80f3fc67fd..fb9db7162efa 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js @@ -1,4 +1,5 @@ import defined from "../Core/defined.js"; +import Model3DTileContent from "./Model/Model3DTileContent.js"; /** * @private @@ -29,6 +30,7 @@ function Cesium3DTilesetStatistics() { // Memory statistics this.geometryByteLength = 0; this.texturesByteLength = 0; + this.texturesReferenceCounterById = {}; this.batchTableByteLength = 0; // batch textures and any binary metadata properties not otherwise accounted for } @@ -45,6 +47,40 @@ Cesium3DTilesetStatistics.prototype.clear = function () { this.numberOfTilesCulledWithChildrenUnion = 0; }; +/** + * Update the given statistics with the information from the given + * content. + * + * This function does vastly different things, depending on how it + * is called: + * + * When the `load` parameter is `false`, then it updates the parts + * of the statistics that summarize the `...Selected` elements, + * indicating how many elements (features, points, triangles) are + * selected for rendering. + * (In this case, the `decrement` parameter apparently always has + * to be `false` - probably because these value are reset to 0 + * after each frame or so....) + * + * When the `load` parameter is `true`, then it updates the parts of + * the statistics that summarize the `...Loaded` and `...ByteLength` + * properties. These basically describe what is currently loaded + * in memory. + * In this case, the `decrement` parameter indicates whether the + * operation that triggered this update was a "load" or an "unload" + * operation: When `decrement===false`, then the operation was a + * "load", and the values will be incremented. When `decrement===true`, + * then the operation was an "unload", and the values are decremented. + * + * In any case, this function will be called recursively with the + * `innerContents` of the given content. + * + * @param {Cesium3DTilesetStatistics} statistics - The statistics + * @param {Cesium3DTileContent} content - The conetnt + * @param {boolean} decrement - Whether the values should be decremented + * @param {boolean} load - This is `true` when the update is for a "load" + * operation, and `false` when it is for a "selection" operation + */ function updatePointAndFeatureCounts(statistics, content, decrement, load) { const contents = content.innerContents; const pointsLength = content.pointsLength; @@ -62,12 +98,60 @@ function updatePointAndFeatureCounts(statistics, content, decrement, load) { statistics.geometryByteLength += decrement ? -geometryByteLength : geometryByteLength; - statistics.texturesByteLength += decrement - ? -texturesByteLength - : texturesByteLength; statistics.batchTableByteLength += decrement ? -batchTableByteLength : batchTableByteLength; + + if (content instanceof Model3DTileContent) { + const textureIds = content.getTextureIds(); + //console.log(`Update stats with ${textureIds} for decrement ${decrement}`); + + let totalTexturesByteLengthChange = 0; + if (decrement) { + for (const textureId of textureIds) { + const referenceCounter = + statistics.texturesReferenceCounterById[textureId]; + const textureByteLength = content.getTextureByteLengthById(textureId); + + // XXX TODO Sanity check + if (!defined(referenceCounter) || referenceCounter === 0) { + console.log( + `ERROR decrement, but referenceCounter is ${referenceCounter} for textureId ${textureId}`, + ); + continue; + } + if (referenceCounter === 1) { + //console.log(`Decrement, referenceCounter dropped to 0 for textureId ${textureId}, reducing by ${textureByteLength} for textureId ${textureId}`); + delete statistics.texturesReferenceCounterById[textureId]; + totalTexturesByteLengthChange -= textureByteLength; + } else { + //console.log(`Decrement, referenceCounter became ${referenceCounter - 1} for textureId ${textureId}`); + statistics.texturesReferenceCounterById[textureId] = + referenceCounter - 1; + } + } + } else { + for (const textureId of textureIds) { + const referenceCounter = + statistics.texturesReferenceCounterById[textureId] ?? 0; + const textureByteLength = content.getTextureByteLengthById(textureId); + + statistics.texturesReferenceCounterById[textureId] = + referenceCounter + 1; + if (referenceCounter === 1) { + //console.log(`Increment, referenceCounter became ${referenceCounter + 1}, increasing by ${textureByteLength} for textureId ${textureId}`); + totalTexturesByteLengthChange += textureByteLength; + } else { + //console.log(`Increment, referenceCounter became ${referenceCounter + 1} for textureId ${textureId}`); + } + } + } + statistics.texturesByteLength += totalTexturesByteLengthChange; + } else { + statistics.texturesByteLength += decrement + ? -texturesByteLength + : texturesByteLength; + } } else { statistics.numberOfFeaturesSelected += decrement ? -featuresLength @@ -80,6 +164,22 @@ function updatePointAndFeatureCounts(statistics, content, decrement, load) { : trianglesLength; } + // XXX TODO Debug log + if (load) { + console.log( + `After ${decrement ? "unload" : "load "} statistics.texturesByteLength now ${statistics.texturesByteLength}`, + ); + + /*/ + console.log("Details:"); + const textureIds = Object.keys(statistics.texturesReferenceCounterById); + for (const textureId of textureIds) { + const referenceCounter = statistics.texturesReferenceCounterById[textureId]; + console.log(` referenceCounter ${referenceCounter} for ${textureId}`); + } + //*/ + } + if (defined(contents)) { const length = contents.length; for (let i = 0; i < length; ++i) { @@ -124,6 +224,10 @@ Cesium3DTilesetStatistics.clone = function (statistics, result) { statistics.numberOfTilesCulledWithChildrenUnion; result.geometryByteLength = statistics.geometryByteLength; result.texturesByteLength = statistics.texturesByteLength; + result.texturesByteLengthById = { ...statistics.texturesByteLengthById }; + result.texturesReferenceCounterById = { + ...statistics.texturesReferenceCounterById, + }; result.batchTableByteLength = statistics.batchTableByteLength; }; export default Cesium3DTilesetStatistics; diff --git a/packages/engine/Source/Scene/GltfTextureLoader.js b/packages/engine/Source/Scene/GltfTextureLoader.js index 4c07eef67629..e060d03947f4 100644 --- a/packages/engine/Source/Scene/GltfTextureLoader.js +++ b/packages/engine/Source/Scene/GltfTextureLoader.js @@ -170,6 +170,7 @@ GltfTextureLoader.prototype.load = async function () { function CreateTextureJob() { this.gltf = undefined; this.textureInfo = undefined; + this.textureId = undefined; this.image = undefined; this.context = undefined; this.texture = undefined; @@ -178,12 +179,14 @@ function CreateTextureJob() { CreateTextureJob.prototype.set = function ( gltf, textureInfo, + textureId, image, mipLevels, context, ) { this.gltf = gltf; this.textureInfo = textureInfo; + this.textureId = textureId; this.image = image; this.mipLevels = mipLevels; this.context = context; @@ -193,13 +196,21 @@ CreateTextureJob.prototype.execute = function () { this.texture = createTexture( this.gltf, this.textureInfo, + this.textureId, this.image, this.mipLevels, this.context, ); }; -function createTexture(gltf, textureInfo, image, mipLevels, context) { +function createTexture( + gltf, + textureInfo, + textureId, + image, + mipLevels, + context, +) { // internalFormat is only defined for CompressedTextureBuffer const internalFormat = image.internalFormat; @@ -261,6 +272,7 @@ function createTexture(gltf, textureInfo, image, mipLevels, context) { } texture = Texture.create({ + id: textureId, context: context, source: { arrayBufferView: image.bufferView, // Only defined for CompressedTextureBuffer @@ -276,6 +288,7 @@ function createTexture(gltf, textureInfo, image, mipLevels, context) { image = resizeImageToNextPowerOfTwo(image); } texture = Texture.create({ + id: textureId, context: context, source: image, sampler: sampler, @@ -332,6 +345,7 @@ GltfTextureLoader.prototype.process = function (frameState) { textureJob.set( this._gltf, this._textureInfo, + this.cacheKey, this._image, this._mipLevels, frameState.context, @@ -346,6 +360,7 @@ GltfTextureLoader.prototype.process = function (frameState) { texture = createTexture( this._gltf, this._textureInfo, + this.cacheKey, this._image, this._mipLevels, frameState.context, diff --git a/packages/engine/Source/Scene/Model/Model3DTileContent.js b/packages/engine/Source/Scene/Model/Model3DTileContent.js index e4266704bdad..dc6376702be2 100644 --- a/packages/engine/Source/Scene/Model/Model3DTileContent.js +++ b/packages/engine/Source/Scene/Model/Model3DTileContent.js @@ -152,6 +152,16 @@ Object.defineProperties(Model3DTileContent.prototype, { }, }); +// XXX TODO COMMENT +Model3DTileContent.prototype.getTextureIds = function () { + return this._model.statistics.getTextureIds(); +}; + +// XXX TODO COMMENT +Model3DTileContent.prototype.getTextureByteLengthById = function (textureId) { + return this._model.statistics.getTextureByteLengthById(textureId); +}; + /** * Returns the object that was created for the given extension. * diff --git a/packages/engine/Source/Scene/Model/ModelStatistics.js b/packages/engine/Source/Scene/Model/ModelStatistics.js index 3188895efc5a..ef90dc6a355a 100644 --- a/packages/engine/Source/Scene/Model/ModelStatistics.js +++ b/packages/engine/Source/Scene/Model/ModelStatistics.js @@ -61,7 +61,7 @@ function ModelStatistics() { // Sets of buffers and textures that have already been counted. // This is to prevent double-counting cached assets. this._bufferIdSet = {}; - this._textureIdSet = {}; + this._textureIdByteLengths = {}; // Associated array of batch textures that have already been counted. // This allows for quick look-up to check if a texture has been counted, @@ -111,7 +111,7 @@ ModelStatistics.prototype.clear = function () { this.propertyTablesByteLength = 0; this._bufferIdSet = {}; - this._textureIdSet = {}; + this._textureIdByteLengths = {}; this._batchTextureIdMap.removeAll(); }; @@ -160,12 +160,26 @@ ModelStatistics.prototype.addTexture = function (texture) { Check.typeOf.object("texture", texture); //>>includeEnd('debug'); - if (!this._textureIdSet.hasOwnProperty(texture._id)) { + if (!this._textureIdByteLengths.hasOwnProperty(texture._id)) { this.texturesByteLength += texture.sizeInBytes; + this._textureIdByteLengths[texture._id] = texture.sizeInBytes; + } else { + // XXX TODO Only a sanity check. It looks like + // this function can be called several times + // with the same texture (hence the check above) + // but that should be OK... + //console.log(`XXX ModelStatistics: Texture already tracked ${texture._id}`); } +}; - // Simulate set insertion. - this._textureIdSet[texture._id] = true; +// XXX TODO COMMENT +ModelStatistics.prototype.getTextureIds = function () { + return Object.keys(this._textureIdByteLengths); +}; + +// XXX TODO COMMENT +ModelStatistics.prototype.getTextureByteLengthById = function (textureId) { + return this._textureIdByteLengths[textureId]; }; /** From 0b1991bae329ebb4f6246b50b95d8dcd1349d2fd Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 23 Nov 2024 14:42:57 +0100 Subject: [PATCH 2/9] Remove clone of field that does not exist --- packages/engine/Source/Scene/Cesium3DTilesetStatistics.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js b/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js index fb9db7162efa..b9c302e4fa66 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js @@ -170,11 +170,12 @@ function updatePointAndFeatureCounts(statistics, content, decrement, load) { `After ${decrement ? "unload" : "load "} statistics.texturesByteLength now ${statistics.texturesByteLength}`, ); - /*/ + //*/ console.log("Details:"); const textureIds = Object.keys(statistics.texturesReferenceCounterById); for (const textureId of textureIds) { - const referenceCounter = statistics.texturesReferenceCounterById[textureId]; + const referenceCounter = + statistics.texturesReferenceCounterById[textureId]; console.log(` referenceCounter ${referenceCounter} for ${textureId}`); } //*/ @@ -224,7 +225,6 @@ Cesium3DTilesetStatistics.clone = function (statistics, result) { statistics.numberOfTilesCulledWithChildrenUnion; result.geometryByteLength = statistics.geometryByteLength; result.texturesByteLength = statistics.texturesByteLength; - result.texturesByteLengthById = { ...statistics.texturesByteLengthById }; result.texturesReferenceCounterById = { ...statistics.texturesReferenceCounterById, }; From 9dcb393cc1d237394fd943d645d41ecb829b950b Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Mon, 25 Nov 2024 21:16:16 +0100 Subject: [PATCH 3/9] Cleanups for shared texture statistics handling --- packages/engine/Source/Renderer/Texture.js | 1 + .../Source/Scene/Cesium3DTilesetStatistics.js | 229 ++++++++---------- .../engine/Source/Scene/GltfTextureLoader.js | 4 +- .../Source/Scene/Model/Model3DTileContent.js | 18 +- .../Source/Scene/Model/ModelStatistics.js | 24 +- 5 files changed, 137 insertions(+), 139 deletions(-) diff --git a/packages/engine/Source/Renderer/Texture.js b/packages/engine/Source/Renderer/Texture.js index 5c7088ad7009..1e5eed6dd71d 100644 --- a/packages/engine/Source/Renderer/Texture.js +++ b/packages/engine/Source/Renderer/Texture.js @@ -27,6 +27,7 @@ import TextureMinificationFilter from "./TextureMinificationFilter.js"; * @property {number} [width] The pixel width of the texture. If not supplied, must be available from the source. * @property {number} [height] The pixel height of the texture. If not supplied, must be available from the source. * @property {boolean} [preMultiplyAlpha] If true, the alpha channel will be multiplied into the other channels. + * @property {string} [id] A unique identifier for the texture. If this is not given, then a GUID will be created. * * @private */ diff --git a/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js b/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js index b9c302e4fa66..650bc2c69a85 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js @@ -48,159 +48,124 @@ Cesium3DTilesetStatistics.prototype.clear = function () { }; /** - * Update the given statistics with the information from the given - * content. + * Increment the counters for the points, triangles, and features + * that are currently selected for rendering. * - * This function does vastly different things, depending on how it - * is called: + * This will be called recursively for the given content and + * all its inner contents * - * When the `load` parameter is `false`, then it updates the parts - * of the statistics that summarize the `...Selected` elements, - * indicating how many elements (features, points, triangles) are - * selected for rendering. - * (In this case, the `decrement` parameter apparently always has - * to be `false` - probably because these value are reset to 0 - * after each frame or so....) - * - * When the `load` parameter is `true`, then it updates the parts of - * the statistics that summarize the `...Loaded` and `...ByteLength` - * properties. These basically describe what is currently loaded - * in memory. - * In this case, the `decrement` parameter indicates whether the - * operation that triggered this update was a "load" or an "unload" - * operation: When `decrement===false`, then the operation was a - * "load", and the values will be incremented. When `decrement===true`, - * then the operation was an "unload", and the values are decremented. - * - * In any case, this function will be called recursively with the - * `innerContents` of the given content. - * - * @param {Cesium3DTilesetStatistics} statistics - The statistics - * @param {Cesium3DTileContent} content - The conetnt - * @param {boolean} decrement - Whether the values should be decremented - * @param {boolean} load - This is `true` when the update is for a "load" - * operation, and `false` when it is for a "selection" operation + * @param {Cesium3DTileContent} content */ -function updatePointAndFeatureCounts(statistics, content, decrement, load) { - const contents = content.innerContents; - const pointsLength = content.pointsLength; - const trianglesLength = content.trianglesLength; - const featuresLength = content.featuresLength; - const geometryByteLength = content.geometryByteLength; - const texturesByteLength = content.texturesByteLength; - const batchTableByteLength = content.batchTableByteLength; - - if (load) { - statistics.numberOfFeaturesLoaded += decrement - ? -featuresLength - : featuresLength; - statistics.numberOfPointsLoaded += decrement ? -pointsLength : pointsLength; - statistics.geometryByteLength += decrement - ? -geometryByteLength - : geometryByteLength; - statistics.batchTableByteLength += decrement - ? -batchTableByteLength - : batchTableByteLength; - - if (content instanceof Model3DTileContent) { - const textureIds = content.getTextureIds(); - //console.log(`Update stats with ${textureIds} for decrement ${decrement}`); - - let totalTexturesByteLengthChange = 0; - if (decrement) { - for (const textureId of textureIds) { - const referenceCounter = - statistics.texturesReferenceCounterById[textureId]; - const textureByteLength = content.getTextureByteLengthById(textureId); - - // XXX TODO Sanity check - if (!defined(referenceCounter) || referenceCounter === 0) { - console.log( - `ERROR decrement, but referenceCounter is ${referenceCounter} for textureId ${textureId}`, - ); - continue; - } - if (referenceCounter === 1) { - //console.log(`Decrement, referenceCounter dropped to 0 for textureId ${textureId}, reducing by ${textureByteLength} for textureId ${textureId}`); - delete statistics.texturesReferenceCounterById[textureId]; - totalTexturesByteLengthChange -= textureByteLength; - } else { - //console.log(`Decrement, referenceCounter became ${referenceCounter - 1} for textureId ${textureId}`); - statistics.texturesReferenceCounterById[textureId] = - referenceCounter - 1; - } - } - } else { - for (const textureId of textureIds) { - const referenceCounter = - statistics.texturesReferenceCounterById[textureId] ?? 0; - const textureByteLength = content.getTextureByteLengthById(textureId); +Cesium3DTilesetStatistics.prototype.incrementSelectionCounts = function ( + content, +) { + this.numberOfFeaturesSelected += content.featuresLength; + this.numberOfPointsSelected += content.pointsLength; + this.numberOfTrianglesSelected += content.trianglesLength; - statistics.texturesReferenceCounterById[textureId] = - referenceCounter + 1; - if (referenceCounter === 1) { - //console.log(`Increment, referenceCounter became ${referenceCounter + 1}, increasing by ${textureByteLength} for textureId ${textureId}`); - totalTexturesByteLengthChange += textureByteLength; - } else { - //console.log(`Increment, referenceCounter became ${referenceCounter + 1} for textureId ${textureId}`); - } - } - } - statistics.texturesByteLength += totalTexturesByteLengthChange; - } else { - statistics.texturesByteLength += decrement - ? -texturesByteLength - : texturesByteLength; + // Recursive calls on all inner contents + const contents = content.innerContents; + if (defined(contents)) { + const length = contents.length; + for (let i = 0; i < length; ++i) { + this.incrementSelectionCounts(contents[i]); } - } else { - statistics.numberOfFeaturesSelected += decrement - ? -featuresLength - : featuresLength; - statistics.numberOfPointsSelected += decrement - ? -pointsLength - : pointsLength; - statistics.numberOfTrianglesSelected += decrement - ? -trianglesLength - : trianglesLength; } +}; - // XXX TODO Debug log - if (load) { - console.log( - `After ${decrement ? "unload" : "load "} statistics.texturesByteLength now ${statistics.texturesByteLength}`, - ); +/** + * Increment the counters for the number of features and points that + * are currently loaded, and the lengths (size in bytes) of the + * occupied memory. + * + * This will be called recursively for the given content and + * all its inner contents + * + * @param {Cesium3DTileContent} content + */ +Cesium3DTilesetStatistics.prototype.incrementLoadCounts = function (content) { + this.numberOfFeaturesLoaded += content.featuresLength; + this.numberOfPointsLoaded += content.pointsLength; + this.geometryByteLength += content.geometryByteLength; + this.batchTableByteLength += content.batchTableByteLength; - //*/ - console.log("Details:"); - const textureIds = Object.keys(statistics.texturesReferenceCounterById); + // When the content is not a `Model3DTileContent`, then its + // textures byte length is added directly + if (!(content instanceof Model3DTileContent)) { + this.texturesByteLength += content.texturesByteLength; + } else { + // When the content is a `Model3DTileContent`, then increment the + // reference counter for all its textures. The byte length of any + // newly tracked texture to the total textures byte length + const textureIds = content.getTextureIds(); for (const textureId of textureIds) { const referenceCounter = - statistics.texturesReferenceCounterById[textureId]; - console.log(` referenceCounter ${referenceCounter} for ${textureId}`); + this.texturesReferenceCounterById[textureId] ?? 0; + if (referenceCounter === 0) { + const textureByteLength = content.getTextureByteLengthById(textureId); + this.texturesByteLength += textureByteLength; + } + this.texturesReferenceCounterById[textureId] = referenceCounter + 1; } - //*/ } + // Recursive calls on all inner contents + const contents = content.innerContents; if (defined(contents)) { const length = contents.length; for (let i = 0; i < length; ++i) { - updatePointAndFeatureCounts(statistics, contents[i], decrement, load); + this.incrementLoadCounts(contents[i]); } } -} - -Cesium3DTilesetStatistics.prototype.incrementSelectionCounts = function ( - content, -) { - updatePointAndFeatureCounts(this, content, false, false); -}; - -Cesium3DTilesetStatistics.prototype.incrementLoadCounts = function (content) { - updatePointAndFeatureCounts(this, content, false, true); }; +/** + * Decrement the counters for the number of features and points that + * are currently loaded, and the lengths (size in bytes) of the + * occupied memory. + * + * This will be called recursively for the given content and + * all its inner contents + * + * @param {Cesium3DTileContent} content + */ Cesium3DTilesetStatistics.prototype.decrementLoadCounts = function (content) { - updatePointAndFeatureCounts(this, content, true, true); + this.numberOfFeaturesLoaded -= content.featuresLength; + this.numberOfPointsLoaded -= content.pointsLength; + this.geometryByteLength -= content.geometryByteLength; + this.batchTableByteLength -= content.batchTableByteLength; + + // When the content is not a `Model3DTileContent`, then its + // textures byte length is subtracted directly + if (!(content instanceof Model3DTileContent)) { + this.texturesByteLength -= content.texturesByteLength; + } else { + // When the content is a `Model3DTileContent`, then increment the + // reference counter for all its textures. The byte length of any + // texture that is no longer references is subtracted from the + // total textures byte length + const textureIds = content.getTextureIds(); + for (const textureId of textureIds) { + const referenceCounter = this.texturesReferenceCounterById[textureId]; + if (referenceCounter === 1) { + delete this.texturesReferenceCounterById[textureId]; + const textureByteLength = content.getTextureByteLengthById(textureId); + this.texturesByteLength += textureByteLength; + } else { + this.texturesReferenceCounterById[textureId] = referenceCounter - 1; + } + } + } + // Recursive calls on all inner contents + const contents = content.innerContents; + if (defined(contents)) { + const length = contents.length; + for (let i = 0; i < length; ++i) { + this.decrementLoadCounts(contents[i]); + } + } + + console.log("after decrementLoadCounts", JSON.stringify(this, null, 2)); }; Cesium3DTilesetStatistics.clone = function (statistics, result) { diff --git a/packages/engine/Source/Scene/GltfTextureLoader.js b/packages/engine/Source/Scene/GltfTextureLoader.js index e060d03947f4..afa6dad8f177 100644 --- a/packages/engine/Source/Scene/GltfTextureLoader.js +++ b/packages/engine/Source/Scene/GltfTextureLoader.js @@ -345,7 +345,7 @@ GltfTextureLoader.prototype.process = function (frameState) { textureJob.set( this._gltf, this._textureInfo, - this.cacheKey, + this._cacheKey, this._image, this._mipLevels, frameState.context, @@ -360,7 +360,7 @@ GltfTextureLoader.prototype.process = function (frameState) { texture = createTexture( this._gltf, this._textureInfo, - this.cacheKey, + this._cacheKey, this._image, this._mipLevels, frameState.context, diff --git a/packages/engine/Source/Scene/Model/Model3DTileContent.js b/packages/engine/Source/Scene/Model/Model3DTileContent.js index dc6376702be2..143acbe8cb48 100644 --- a/packages/engine/Source/Scene/Model/Model3DTileContent.js +++ b/packages/engine/Source/Scene/Model/Model3DTileContent.js @@ -152,12 +152,24 @@ Object.defineProperties(Model3DTileContent.prototype, { }, }); -// XXX TODO COMMENT +/** + * Returns an array containing the `texture.id` values for all textures + * that are part of this content. + * + * @returns {string[]} The texture IDs + */ Model3DTileContent.prototype.getTextureIds = function () { return this._model.statistics.getTextureIds(); }; -// XXX TODO COMMENT +/** + * Returns the length, in bytes, of the texture data for the texture with + * the given ID that is part of this content, or `undefined` if this + * content does not contain the texture with the given ID. + * + * @param {string} textureId The texture ID + * @returns {number|undefined} The texture byte length + */ Model3DTileContent.prototype.getTextureByteLengthById = function (textureId) { return this._model.statistics.getTextureByteLengthById(textureId); }; @@ -167,7 +179,7 @@ Model3DTileContent.prototype.getTextureByteLengthById = function (textureId) { * * The given name may be the name of a glTF extension, like `"EXT_example_extension"`. * If the specified extension was present in the root of the underlying glTF asset, - * and a loder for the specified extension has processed the extension data, then + * and a loader for the specified extension has processed the extension data, then * this will return the model representation of the extension. * * @param {string} extensionName The name of the extension diff --git a/packages/engine/Source/Scene/Model/ModelStatistics.js b/packages/engine/Source/Scene/Model/ModelStatistics.js index ef90dc6a355a..6275305ecb64 100644 --- a/packages/engine/Source/Scene/Model/ModelStatistics.js +++ b/packages/engine/Source/Scene/Model/ModelStatistics.js @@ -61,6 +61,14 @@ function ModelStatistics() { // Sets of buffers and textures that have already been counted. // This is to prevent double-counting cached assets. this._bufferIdSet = {}; + + /** + * The mapping from `texture.id` strings to the byte length of the + * respective texture + * + * @type {object} + * @private + */ this._textureIdByteLengths = {}; // Associated array of batch textures that have already been counted. @@ -172,12 +180,24 @@ ModelStatistics.prototype.addTexture = function (texture) { } }; -// XXX TODO COMMENT +/** + * Returns an array containing the `texture.id` values for all textures + * that are part of the model. + * + * @returns {string[]} The texture IDs + */ ModelStatistics.prototype.getTextureIds = function () { return Object.keys(this._textureIdByteLengths); }; -// XXX TODO COMMENT +/** + * Returns the length, in bytes, of the texture data for the texture with + * the given ID that is part of the model, or `undefined` if the model + * does not contain the texture with the given ID. + * + * @param {string} textureId The texture ID + * @returns {number|undefined} The texture byte length + */ ModelStatistics.prototype.getTextureByteLengthById = function (textureId) { return this._textureIdByteLengths[textureId]; }; From bd074a07e3686cc86e1717b68a2f899b2769e392 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Mon, 25 Nov 2024 21:29:24 +0100 Subject: [PATCH 4/9] Remove debug output --- packages/engine/Source/Scene/Model/ModelStatistics.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/engine/Source/Scene/Model/ModelStatistics.js b/packages/engine/Source/Scene/Model/ModelStatistics.js index 6275305ecb64..b8a60af26df0 100644 --- a/packages/engine/Source/Scene/Model/ModelStatistics.js +++ b/packages/engine/Source/Scene/Model/ModelStatistics.js @@ -171,12 +171,6 @@ ModelStatistics.prototype.addTexture = function (texture) { if (!this._textureIdByteLengths.hasOwnProperty(texture._id)) { this.texturesByteLength += texture.sizeInBytes; this._textureIdByteLengths[texture._id] = texture.sizeInBytes; - } else { - // XXX TODO Only a sanity check. It looks like - // this function can be called several times - // with the same texture (hence the check above) - // but that should be OK... - //console.log(`XXX ModelStatistics: Texture already tracked ${texture._id}`); } }; From bcdc430fc4527dd162404bb35a08588c916dab7a Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Mon, 25 Nov 2024 21:32:13 +0100 Subject: [PATCH 5/9] Update specs for ModelStatistics --- .../Specs/Scene/Model/ModelStatisticsSpec.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/engine/Specs/Scene/Model/ModelStatisticsSpec.js b/packages/engine/Specs/Scene/Model/ModelStatisticsSpec.js index d471effc9773..c83454747c6d 100644 --- a/packages/engine/Specs/Scene/Model/ModelStatisticsSpec.js +++ b/packages/engine/Specs/Scene/Model/ModelStatisticsSpec.js @@ -15,7 +15,7 @@ describe("Scene/Model/ModelStatistics", function () { expect(statistics.propertyTablesByteLength).toBe(0); expect(statistics.batchTexturesByteLength).toBe(0); expect(statistics._bufferIdSet).toEqual({}); - expect(statistics._textureIdSet).toEqual({}); + expect(statistics._textureIdByteLengths).toEqual({}); expect(statistics._batchTextureIdMap).toEqual(emptyMap); }); @@ -27,7 +27,7 @@ describe("Scene/Model/ModelStatistics", function () { statistics.texturesByteLength = 10; statistics.propertyTablesByteLength = 10; statistics._bufferIdSet = { uuid1: true, uuid2: true }; - statistics._textureIdSet = { uuid3: true }; + statistics._textureIdByteLengths = { uuid3: 1234 }; const map = new AssociativeArray(); map.set("uuid", {}); @@ -41,7 +41,7 @@ describe("Scene/Model/ModelStatistics", function () { expect(statistics.propertyTablesByteLength).toBe(0); expect(statistics.batchTexturesByteLength).toBe(0); expect(statistics._bufferIdSet).toEqual({}); - expect(statistics._textureIdSet).toEqual({}); + expect(statistics._textureIdByteLengths).toEqual({}); expect(statistics._batchTextureIdMap).toEqual(emptyMap); }); @@ -78,7 +78,7 @@ describe("Scene/Model/ModelStatistics", function () { expect(statistics.propertyTablesByteLength).toBe(0); expect(statistics.batchTexturesByteLength).toBe(0); expect(statistics._bufferIdSet).toEqual({ uuid: true }); - expect(statistics._textureIdSet).toEqual({}); + expect(statistics._textureIdByteLengths).toEqual({}); expect(statistics._batchTextureIdMap).toEqual(emptyMap); }); @@ -99,7 +99,7 @@ describe("Scene/Model/ModelStatistics", function () { expect(statistics.propertyTablesByteLength).toBe(0); expect(statistics.batchTexturesByteLength).toBe(0); expect(statistics._bufferIdSet).toEqual({ uuid: true }); - expect(statistics._textureIdSet).toEqual({}); + expect(statistics._textureIdByteLengths).toEqual({}); expect(statistics._batchTextureIdMap).toEqual(emptyMap); }); @@ -129,7 +129,7 @@ describe("Scene/Model/ModelStatistics", function () { expect(statistics.propertyTablesByteLength).toBe(0); expect(statistics.batchTexturesByteLength).toBe(0); expect(statistics._bufferIdSet).toEqual({ uuid1: true, uuid2: true }); - expect(statistics._textureIdSet).toEqual({}); + expect(statistics._textureIdByteLengths).toEqual({}); expect(statistics._batchTextureIdMap).toEqual(emptyMap); }); @@ -158,7 +158,7 @@ describe("Scene/Model/ModelStatistics", function () { expect(statistics.propertyTablesByteLength).toBe(0); expect(statistics.batchTexturesByteLength).toBe(0); expect(statistics._bufferIdSet).toEqual({}); - expect(statistics._textureIdSet).toEqual({ uuid: true }); + expect(statistics._textureIdByteLengths).toEqual({ uuid: 10 }); expect(statistics._batchTextureIdMap).toEqual(emptyMap); }); @@ -188,7 +188,7 @@ describe("Scene/Model/ModelStatistics", function () { expect(statistics.propertyTablesByteLength).toBe(0); expect(statistics.batchTexturesByteLength).toBe(0); expect(statistics._bufferIdSet).toEqual({}); - expect(statistics._textureIdSet).toEqual({ uuid1: true, uuid2: true }); + expect(statistics._textureIdByteLengths).toEqual({ uuid1: 2, uuid2: 3 }); expect(statistics._batchTextureIdMap).toEqual(emptyMap); }); @@ -219,7 +219,7 @@ describe("Scene/Model/ModelStatistics", function () { expect(statistics.propertyTablesByteLength).toBe(0); expect(statistics.batchTexturesByteLength).toBe(0); expect(statistics._bufferIdSet).toEqual({}); - expect(statistics._textureIdSet).toEqual({}); + expect(statistics._textureIdByteLengths).toEqual({}); expect(statistics._batchTextureIdMap).toEqual(expectedMap); // Simulate creating a batch texture @@ -265,7 +265,7 @@ describe("Scene/Model/ModelStatistics", function () { expect(statistics.propertyTablesByteLength).toBe(0); expect(statistics.batchTexturesByteLength).toBe(0); expect(statistics._bufferIdSet).toEqual({}); - expect(statistics._textureIdSet).toEqual({}); + expect(statistics._textureIdByteLengths).toEqual({}); expect(statistics._batchTextureIdMap).toEqual(expectedMap); // Simulate creating first batch texture From 32dd865b95c1323a0e7ef1cbfc88c50cfd8a7c4f Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Mon, 25 Nov 2024 21:42:29 +0100 Subject: [PATCH 6/9] Draft for shared texture handling specs --- .../TilesetWithSharedTextures/plane-0.glb | Bin 0 -> 1416 bytes .../TilesetWithSharedTextures/plane-1.glb | Bin 0 -> 1416 bytes .../TilesetWithSharedTextures/plane-2.glb | Bin 0 -> 1416 bytes .../TilesetWithSharedTextures/plane-3.glb | Bin 0 -> 1416 bytes .../TilesetWithSharedTextures/texture0.png | Bin 0 -> 1672 bytes .../TilesetWithSharedTextures/texture1.png | Bin 0 -> 1567 bytes .../TilesetWithSharedTextures/tileset.json | 116 ++++++++++++++++++ .../engine/Specs/Scene/Cesium3DTilesetSpec.js | 43 +++++++ 8 files changed, 159 insertions(+) create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/plane-0.glb create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/plane-1.glb create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/plane-2.glb create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/plane-3.glb create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/texture0.png create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/texture1.png create mode 100644 Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/tileset.json diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/plane-0.glb b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/plane-0.glb new file mode 100644 index 0000000000000000000000000000000000000000..084e4ffb427b16e7c71ee7c42c3a3dae988861f8 GIT binary patch literal 1416 zcma)6+m6~W5KXu3e;_|c5H5?ld8sI6B~}aB0$o)?sB(fyTqSX2J9JkFsgM1LepElH zGfu*7sn|$KJY%0Zb2&*E&u*_Q%liG^vi`oath=c<-VhoDOerB1apxQ2=c^TykDUD^ zu45DbSu=07QcN2&5J@Idmg?Da#)x~}Y_)9?h&+Yuu1&P@l4-wp-*M(PHR17Tx`KZm6_9qge+x}Y9WbB?n0P)Z6!rgi?q&^+h~MbN4Z~znj1gp znuc-190HCtlZp!%6=~HSvW4LoIh4`>E!?v-)Ddx;*Ii`XtL0EV=ahp&+JRdzM=C=U zRl9T3ZSPz40@{SLN7xRC03A~;`4TjU2M&X^FfX3akQFKAl0!L~J?UIBr=F!D_L3*8 zkZ^Idfo=pQR5QtGTwpU_j+mx#%!7%@!zhIhg}9(#s#ssdLf+EAba`G$uzX`{sxz~K z8gV>bGWa5#EfAG)s|cthA}f;03*q1_jkAc31XfGDhu)X311MoBazAEM=)sb>TIS58 z&s0>_ok^Z>&DW(#HT28RHJTgp;7y0Kp*M#1*<|cZM!kFDLL+B`Z+*|3eE#mhWPlkG z=J0ngQ>j{07$%#}*61z;qi9$H{AOQEnyQ#;Bc^%vsleL+6iu=iq=tVSOj`vYSc5aG zB32c(fRqC)wn}_~5V;Hx47t>5wLkEG`!pQG^Nn={_gi=#U%x({x6%KteSTzZu$q7#4DTyP;VXY9-e(oXmPm+=Yi6VvgUi;j0KaLZ|^V=)KFn+%`jKA*;<8J0oHiY^<%X1-f;w(1AD}sQ@N6vl{ z$1;icte6`FIaAv7MUsh>rE311G2&d??T$%&QKYclGl|k(G8^>o`%_|)gg!yg&JtEU zg_X73n#UCOD<~jvMbX_+JPrq)9f94nOnj`SwWlg=?YltQ{eT3ggp5*m$WkV$5|TLNE`+H!0x6P4q*RtWtyaiYRCrCOx%Gpt zX&ATkA>ddunR5Z7BJHL{wlEwmhe{ftg?pBUDk6@3-9yH`S`O87PB|!~9k>m1q%uTN zb-Fjb&c0PIpf;R6!gfIf=$I+VSD-;Wa2TwOdGUmXtW2qp9LiDbNfnaW%`6SEmpoym zgv+BXbR#gKib+o65}SE)%oL4d?oUM#Mk!nr;(&r_&ITeD@|OC#%Zplq)f-z=pP84` zh~w#s!HuxCKvc!8BOs8-n&iL>XK9>8bS$u1;y(1hd>ujwD^Ykcn?Vm�el{rt@cc zR27~{o^Zw2l}Qye%qtX{8}i`JM)Q$7f&Q6f;!el?d*VPR=fiIU*PVX;Zoz1P8Zzea zdC+62ds7-Zo$gi(F9oCQSOWZdUrCzgF;!Yk3s953D|kaO&73A#3|hlaj<$VJtj6WI zDr41AOGq`ra;w6Z2vJBM!H`R%@dv(dpGFh-e`8$1^A`S(f4@GSx6%Koe;|L3AV7-Ne3=-d5>+7`uxb)QlM_tvl*EzkuvQ3ZANM2nqxO?_ z=Onz9iAGA|JKx=NFOMC?)7vY{vVOm}tiSIp>u%zYHiU*DQ%XohyxE2X`EtqRBWFK} z=h!58*34fn71KrxMUsh>rF#0DG2&e}TWyi<{Jy;M=%bc0? znOc=~XObse^K~gw4gCsojpBwp_>;kO;E$kvHW~TjVfUVR(8y{3ThI5$pT9dW89;`F zIou9rDphL=!DQoZ4ewGAiiRbCZ}zpMsfwvKWSY5$hY@Goe;|L3AiNZ<`7$v^C8|O?VAUjqCMP(FrzDOXhqXdT`?w#mAGM#f zJ12oanP{XWzVqEZ_wv|5w79)84CD8E!}$BoFz)8gbW5n`u`ClZBldDj+}!t>eB|sW zu`QFh&x$#|pE0FHPsFK6SfUosDI@lEyVEs^C-MZg`zBFZOXkDD{a{8+64NIL+Fikl zC$O?sJM$Q#{R#*OTtf8r5Rao_cMs5RS|+|$(>g#EvesPy?R`Li)50#I0vbj~t&GekMBwz`XR0&CJau>kV8@?2ABUCEQ>{ct_D$Ly`)ZF?( zH#CS^+6XWmj?Et8ZTSY)eA}f%Z7HnyhhIAsZTH-tmzI+`)32TwN5t~B~*2MN9W2WF4wG|bHvid*vF%*TtdGll+{Wa`W&gL`5_Cl{k{L&uqY{%*l&02(sp za6Ra;RJ|z#olbA3iI;#-bSwsZy{{xqvWP0pre(i=8lgEEjnfFYhF1=zoq51m?Wb2o ztSTxTB`_%`SZtO4g@?$cht7~oqwxpsw@>3KeBT&X@V8Bc^UQp+e44~-nn(9 M!+*7|BOP@A0RJqMfB*mh literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/texture0.png b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/texture0.png new file mode 100644 index 0000000000000000000000000000000000000000..6c7e5ee581b0ccf25b5ad630278485657e20e42d GIT binary patch literal 1672 zcmb`I>p#;A0LFi=$!V12vWm(jEplniiA@qB=Gsz2|ukFS;P`nZHWQF?FXL&Y<6O$ zf^6SaOuGw6E1@S6lZ>cNi4pX;f48tGHkFTe2&oG}tze>1ewaV65-D=6W;)`#Cz=e%x;770jHeecr zIG-YmC&KLPD8KW+OZ-nYt0)(|H;$D|9~=1MP1b|8t#X?hiY-n*^nCv(S9Q2u?$V6N z)Nw@YB;G<}ayPZig9o95eiLgOgP>VTd)m2{v-Oj!?vBJg#!&l3`;(2|9)MD3O=u}driXhY zb}XWx{@#;MkQe7&Mhnilq5T|`@-!h*3!kr`0!nbv3DawC$ihi^aR5VGt17S4_Z8Rsz;o?+1G7b$&P4RG0$ zTPDB>rC?|K>DqO0#WNnXy6(&gCRP4BD?Y=4~Z_h<5Ooe)#!(NAVZ>SyG>uYQ&IpSFHsX=QRw4NN9ErPZy1*n1K65xf(B{&2r-L>aJ-wG}mA& z{n3`bJUIi?+QbN=uOb6W!h0G1`t+&0tz}T5^psR_v_+aRtWf^;4#}5rmP8c}`5WQt zIy1tVp|Je!W`~;Kv@G3-&8gRI>{t9;*IC(XepoA4{P{V6yNj1|y;Df~e=kGx>i_@% literal 0 HcmV?d00001 diff --git a/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/texture1.png b/Specs/Data/Cesium3DTiles/Tilesets/TilesetWithSharedTextures/texture1.png new file mode 100644 index 0000000000000000000000000000000000000000..97288521cd3ca2db40c3a08c43fd7c4e7fde19e9 GIT binary patch literal 1567 zcmcJPSu`667=^<~1|6+-+M$-x(^^991T85uv6oPq+Lx+*n>4jX#4?Ini`s&y+Lwqi zV$hHtT00#YTBIo&k!CC<)&!%cZ}U8JzH{&Y@Sk(v?oG3`F%uD%5C#AMA{OSb+b1{v zs{}zO-Usy>0RVu@7BFLnsM*z0r2m~eQUfXazF8XbTHYD?L%$V!FRKq^ob}d3_2--) z5ZCt=zT&ei{*sbfq#<=qYry=R&*G7&Pfh>evs4(z>=*`vao{I*%rS%Yr>!TTI@ihQ zmatv&%o>`zv9P%_uD?q|j~|wD$2)KI-rX6b1lDB88v*_g1|O#!L$wQ`z4vXb@tSUj zZob}!uX#eY2t)yulq*7fj!B^*mR_w#HTN~Cc(~-S?X>w<_JYct+mwL*EPoBg1x8JK zBUMU5-$9R$mnpp+S1sVkLYa^5M&t?<#c79x*|8g^=jQU=cX!rwm{9#fy9nmUjS#g6 zvXiN@Tgws0t*rY6roWxUxrc)*}QCrgQGuTgLf$)LPSZiE45f^xRJV z`ca*?fKbJjNKn<+Op?n@x+V?>@sn!lX@%B8%&P#%)4y&e<&iQ}%S~&~97TIhZ8z+$ zRc6I$m;6pIey;^_GxYh~2F5{J)fc3Jb_|WH^BCJsO6ysz8|=hG)vS`s*E*0(B;$KPbG&fC)QVoP zU*;nIA=h3F$&mE$zjcw!Mk81f;FK|bDZ?-h?2^JEc;Th)8R(aJUihg7^yOJuRpdmN z6Mq#v`46D=G0$J1b|MjqTT$HKHl*Z-;+HY^ocMrzwtN7C1PjfJ*5jsC5wiL7P^jSV?liUfPWzi$c#TJ z?Kg;)z*NcDIv<2H<(Y|S=#xi?1Jy(Fc}I7vwycaxt;#;_!B)5iQM3qc){6e|@1(+~%i;wlGkH`j@ne6e-JkMw7HZTCtl$`!Oe(f? zk*>?pwk^esm&^i&r;pSM!mmgr{^Gd5*22v@}UYyJjQ#o@eRt zRa+3?LxQ4@5|!F*=*f4J8Nd*Uy7~+({@csqRE1sSn3m25=RJ-c(RB16OC(600n992 z0_DrML*>BnHSo29Cij^2Z1hE+(?3XWNW5-l%s}>8RWmux-7=hgB)?^zD?az^$0Ltj z>s>a@UCoZsH>@=8%pk;K*%Qs!K=(NL<(<_Ae}M zza2E*+NiTbmBvqAovB>`>fOLjbHVxhGR(H?MIgISb%u-%I}Bs Date: Tue, 26 Nov 2024 20:49:06 +0100 Subject: [PATCH 7/9] Remove log output. Fix increment/decrement error. --- packages/engine/Source/Scene/Cesium3DTilesetStatistics.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js b/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js index 650bc2c69a85..f7c0d93834b9 100644 --- a/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js +++ b/packages/engine/Source/Scene/Cesium3DTilesetStatistics.js @@ -140,7 +140,7 @@ Cesium3DTilesetStatistics.prototype.decrementLoadCounts = function (content) { if (!(content instanceof Model3DTileContent)) { this.texturesByteLength -= content.texturesByteLength; } else { - // When the content is a `Model3DTileContent`, then increment the + // When the content is a `Model3DTileContent`, then decrement the // reference counter for all its textures. The byte length of any // texture that is no longer references is subtracted from the // total textures byte length @@ -150,7 +150,7 @@ Cesium3DTilesetStatistics.prototype.decrementLoadCounts = function (content) { if (referenceCounter === 1) { delete this.texturesReferenceCounterById[textureId]; const textureByteLength = content.getTextureByteLengthById(textureId); - this.texturesByteLength += textureByteLength; + this.texturesByteLength -= textureByteLength; } else { this.texturesReferenceCounterById[textureId] = referenceCounter - 1; } @@ -164,8 +164,6 @@ Cesium3DTilesetStatistics.prototype.decrementLoadCounts = function (content) { this.decrementLoadCounts(contents[i]); } } - - console.log("after decrementLoadCounts", JSON.stringify(this, null, 2)); }; Cesium3DTilesetStatistics.clone = function (statistics, result) { From 839ff0d8eec6a718d7a9f1de0e2d12e1b6b66424 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 26 Nov 2024 20:50:22 +0100 Subject: [PATCH 8/9] Proper shared textures spec --- .../engine/Specs/Scene/Cesium3DTilesetSpec.js | 73 ++++++++++++++----- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js index 99be4573668e..a05b1be4dc28 100644 --- a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js +++ b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js @@ -967,9 +967,25 @@ describe( ); }); - it("verify memory usage statistics for shared textures", function () { + it("verify memory usage statistics for shared textures", async function () { + // One buffer view with 4 positions, 4 normals, and 4 texture coordinates, + // using a common byte stride of 12 (resulting in 144 bytes) + // and 2*3 unsigned short indices, resulting in a total of 156 bytes + const singleGeometryByteLength = 156; + + // One texture with 128x128 * RGBA pixels = 65536 bytes + const singleTexturesByteLength = 65536; + // Basic camera setup const camera = scene.camera; + + // NOTE: This is really, really important. There are some + // random calls in "beforeEach" and other parts of these + // specs that affect the camera transform. And the camera + // transform is NOT maintained to be consistent with the + // other properties in any way. So reset it here... + camera.lookAtTransform(Matrix4.IDENTITY); + camera.position = new Cartesian3(0, -1, 0); camera.direction = Cartesian3.clone(Cartesian3.UNIT_Y); camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); @@ -979,31 +995,48 @@ describe( // Move the camera to see no tiles camera.position = new Cartesian3(100, -1, 100); - return Cesium3DTilesTester.loadTileset( + const tileset = await Cesium3DTilesTester.loadTileset( scene, tilesetUrlWithSharedTextures, - ).then(function (tileset) { - const statistics = tileset._statistics; + ); - // No tiles loaded - expect(statistics.geometryByteLength).toEqual(0); - expect(statistics.texturesByteLength).toEqual(0); + const statistics = tileset._statistics; - // Move the camera to stare at the center of the first tile - camera.position = new Cartesian3(0.5, -13, 0.5); + // No tiles loaded + expect(statistics.geometryByteLength).toEqual(0); + expect(statistics.texturesByteLength).toEqual(0); - return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset).then( - function () { - console.log("Statistics ", JSON.stringify(statistics, null, 2)); - // TODO - expect(statistics.geometryByteLength).toEqual(123); - expect(statistics.texturesByteLength).toEqual(234); + // Move the camera to stare at the center of the first tile + camera.position = new Cartesian3(0.5, -1, 0.5); + await Cesium3DTilesTester.waitForTilesLoaded(scene, tileset); - // TODO Eventually, trim - //tileset.trimLoadedTiles(); - }, - ); - }); + // A single tile and texture was loaded + expect(statistics.geometryByteLength).toEqual(singleGeometryByteLength); + expect(statistics.texturesByteLength).toEqual(singleTexturesByteLength); + + // Move the camera back to see all tiles + camera.position = new Cartesian3(3.5, -14, 0.5); + await Cesium3DTilesTester.waitForTilesLoaded(scene, tileset); + + // All tiles have been loaded: 4 times the geometry, BUT + // ONLY 2 times the texture + expect(statistics.geometryByteLength).toEqual( + singleGeometryByteLength * 4, + ); + expect(statistics.texturesByteLength).toEqual( + singleTexturesByteLength * 2, + ); + + // Move the camera back to stare at the center of the first tile again + camera.position = new Cartesian3(0.5, -1, 0.5); + + // Trim any previously loaded tiles + tileset.trimLoadedTiles(); + await Cesium3DTilesTester.waitForTilesLoaded(scene, tileset); + + // Again, only a single tile and texture should be loaded + expect(statistics.geometryByteLength).toEqual(singleGeometryByteLength); + expect(statistics.texturesByteLength).toEqual(singleTexturesByteLength); }); it("verify memory usage statistics for shared resources", function () { From 131ebff6ed565ddce1de98eece9d89c1cd53e22e Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 26 Nov 2024 18:01:09 -0500 Subject: [PATCH 9/9] Update CHANGES.md [skip ci] --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index b68c43afe47a..6c6aca2d2063 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ ##### Fixes :wrench: +- Fixed bug where shared external textures from glTF files were not accounted for in resource statistics. [#12331](https://github.com/CesiumGS/cesium/pull/12331) - Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) - Fixed lag or crashes when loading many models in the same frame. [#12320](https://github.com/CesiumGS/cesium/pull/12320) - Updated WMS example URL in UrlTemplateImageryProvider documentation to use an active service. [#12323](https://github.com/CesiumGS/cesium/pull/12323)