NASA-AMMOS / 3DTilesRendererJS

Renderer for 3D Tiles in Javascript using three.js
https://nasa-ammos.github.io/3DTilesRendererJS/example/bundle/mars.html
Apache License 2.0
1.62k stars 289 forks source link

LRUCache: Provide ability to set a min and max threshold based on estimated tile byte size #384

Closed jesse-small closed 2 months ago

jesse-small commented 1 year ago

Is your feature request related to a problem? Please describe.

The LRUCache ensures we don't load too many tiles, and also appropriately load/unload tiles as they are used/unused. If every tile loaded was the same, this idea would work well. However some tiles are heavier than others. The size of the mesh, the textures or materials loaded along with them, etc. So for some 3d-tiles having a cache limit of 600 might be totally viable and work well, where as a very high res photo textured mesh may actually crash the browser after keep 600 tiles in memory.

Describe the solution you'd like

I would like the ability to add a callback to calculate the "weight" of a tile. This could be used to better determine how expensive some tiles are over others. For example if I load tile that consists of a cube and its coloured red, that is a relatively light tile that is cheap to store in memory. But if I load a tile that is a complicated mesh and its material is photo realistic textures, that is much more expensive to store in memory. This callback would allow us to add our own logic to calculate a weight (default could be 1 to avoid breaking any existing behaviour), and use that to get better control over how much data we actually cache at any given time.

Again we don't have to assume we know how users want to weigh their tiles, but if we give them the option to override the weights that seems like an easy compromise.

Describe alternatives you've considered

I have thought about adjusting the size of the cache based on the 3d-tile that is loaded, but when loading a root tile we don't always know how heavy a given 3d-tile set will be. This also makes sharing the cache much more difficult if I enable both a CAD tileset and a photo mesh tileset. One number might work for one but not the other.

Additional context

Currently I am having a difficult time configuring an appropriate cache min/max because what might be totally acceptable for some 3d-tiles is way too much for others depending on how the 3d-tile was built and parsed.

gkjohnson commented 1 year ago

I think this is a good idea. We can add two properties and another callback to the LRUCache to handle this. I'm having trouble coming up with names for these fields - maybe something like minBytes and maxBytes?

We should be able to estimate the memory footprint of the model pretty quickly once its been loaded so this can be built into the renderer.

Feel free to contribute a a PR for this if you'd like!

jesse-small commented 1 year ago

I would be happy to put together a PR for this! I agree I think something like minBytes and maxBytes would be a good starting place.

gkjohnson commented 3 months ago

Cesium provides these functions for estimating texture byte size for compressed textures for more accuracy:

https://github.com/CesiumGS/cesium/blob/0a69f67b393ba194eefb7254600811c4b712ddc0/packages/engine/Source/Core/PixelFormat.js#L341-L393

gkjohnson commented 3 months ago

Three.js provides a function for estimating byte usage. It does not include mipmaps, though, which can be calculated by multiplying by 1 + 1/3:

https://threejs.org/docs/?q=textureu#api/en/extras/TextureUtils.getByteLength

Approach: