Skip to content

Commit

Permalink
Draft for handling shared textures in statistics
Browse files Browse the repository at this point in the history
  • Loading branch information
javagl committed Nov 23, 2024
1 parent bd0f24f commit b7b4da0
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/engine/Source/Renderer/Texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
110 changes: 107 additions & 3 deletions packages/engine/Source/Scene/Cesium3DTilesetStatistics.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import defined from "../Core/defined.js";
import Model3DTileContent from "./Model/Model3DTileContent.js";

/**
* @private
Expand Down Expand Up @@ -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
}

Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
17 changes: 16 additions & 1 deletion packages/engine/Source/Scene/GltfTextureLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -332,6 +345,7 @@ GltfTextureLoader.prototype.process = function (frameState) {
textureJob.set(
this._gltf,
this._textureInfo,
this.cacheKey,
this._image,
this._mipLevels,
frameState.context,
Expand All @@ -346,6 +360,7 @@ GltfTextureLoader.prototype.process = function (frameState) {
texture = createTexture(
this._gltf,
this._textureInfo,
this.cacheKey,
this._image,
this._mipLevels,
frameState.context,
Expand Down
10 changes: 10 additions & 0 deletions packages/engine/Source/Scene/Model/Model3DTileContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
24 changes: 19 additions & 5 deletions packages/engine/Source/Scene/Model/ModelStatistics.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -111,7 +111,7 @@ ModelStatistics.prototype.clear = function () {
this.propertyTablesByteLength = 0;

this._bufferIdSet = {};
this._textureIdSet = {};
this._textureIdByteLengths = {};
this._batchTextureIdMap.removeAll();
};

Expand Down Expand Up @@ -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];
};

/**
Expand Down

0 comments on commit b7b4da0

Please sign in to comment.