Open inodentry opened 1 month ago
Yeah we should have something like this. Faster load times, faster runtime (more cache efficient to sample a lower-res texture), and less memory usage (the main draw).
"This, of course, only applies to assets stored in file formats that have mipmaps (KTX2 and DDS)" - I think .basis
does too, doesn't it?
It does iirc, but basisu is generally not worth it https://github.com/bevyengine/bevy/issues/14671.
I personally really dislike Basis Universal and I completely forgot about it when I was writing the OP. 😆
It aims to solve a problem (targeting both mobile GPUs using ASTC and desktop GPUs using BCn using the same asset files) which is largely non-existent in practice (you ship different builds for desktop and mobile anyway; just ship your assets in the appropriate format). And the cost is worse texture quality (it has to make compromises with the lossy compression) and slower loading times (you have to decode it on the CPU; sure it is fast, but with the native GPU formats you can copy directly to GPU memory and no decoding is needed).
Anyway, let's not derail the thread.
What problem does this solve or what need does it fill?
It is common for games to offer multiple levels of graphics settings, like: "Low", "High", "Ultra". When it comes to Texture Quality, this typically means the size of textures used. For example, a 2048x2048 texture will use 4x the GPU memory than a 1024x1024 texture and add more overhead during rendering, but look a lot crisper.
In 3D games, mipmaps are essentially required for high quality and performant rendering. If the game's assets are stored as KTX2 or DDS files with mipmaps, the smaller texture sizes are already available on-disk (and will, in fact, be loaded by Bevy and into GPU memory) anyway. In order to limit the max texture size at lower graphics settings, it could be implemented simply by just skipping the first mip level or two.
For example, if we have a 2048x2048 texture as a KTX2 file with a full set of mipmaps down to 1x1 (12 levels total), our hypothetical game could behave as follows:
What solution would you like?
If Bevy supports specifying which mip levels to load, this could be done trivially, and reduce loading times by loading less data from disk.
What alternative(s) have you considered?
1) Load everything anyway, and then simply not use it. The range of mip levels used by the GPU can be restricted using the
lod_min_clamp
andlod_max_clamp
fields in theSamplerDescriptor
. Disadvantage: we don't get any savings in GPU memory or loading times, so it kinda defeats the point.2) Implement a two-step process, by discarding the unwanted data from the
Image
asset after it is loaded. We get GPU memory savings, but no loading time savings. Also, the full resolution data is stored intermittently in CPU memory, before we get a chance to delete it. Inefficient: loading data only to immediately drop it.3) Create separate asset files with a lower base resolution. Works, but is redundant, wastes disk space. The lower resolution images are already available as mip levels in the larger asset.
Additional context
This, of course, only applies to assets stored in file formats that have mipmaps (KTX2 and DDS). When loading images from, say, PNGs, this is irrelevant. In that case, if you want multiple different sizes, you need multiple files, or to downscale the image at runtime.