16
\$\begingroup\$

I am starting to work on optimization for my app. I need to understand the information Unity displays about file sizes. For example, just to prove a point, here is a 2.2 KB completely blank PNG which in Unity when set to use as a UI image (for UI Toolkit) registers as 2.1 MB:

enter image description here

What does that represent? I have heard it is the size in RAM this will take while this image is in use. Is that correct?

I think Unity applies its own compression on these images during build. But does it mean this empty graphic would use 2.1 MB RAM during runtime if displayed?

I am building for mobile so it is important for me to get system resource use as low as possible. These sizes seem not to depend on the content of the image but rather just the pixel size (height and width) so I suspect the best principle is to make all files for this usage as small physically as possible using Sprite/splicing instead to fill space.

Or would an equally stretched sprite take up the same space at runtime?

\$\endgroup\$
3
  • 8
    \$\begingroup\$ FWIW, if you need a completely homogeneous (e.g. [0,0,0,0] RGBA) texture, 1x1 size is good enough to look pixel-perfect, regardless of the scaling you'll apply to it (as long as the scaling factor is int and excluding some insane values that'd break the shader's float precision) :D I'd even say this is not really a joking matter sometimes, since e.g. it's a trick that's been used in days of yore while doing HTML layouts. \$\endgroup\$
    – spamove
    Aug 21, 2022 at 15:04
  • \$\begingroup\$ If you need a blank image for some reason, try to create it with scripts. Maybe Sprite.Create can be useful for you The required RAM is dependant on the pixelsPerUnit setting of this function \$\endgroup\$ Aug 21, 2022 at 19:01
  • \$\begingroup\$ @Sharkbyteprojects the RAM use is actually proportionate to the resolution of the source texture, not the puxelsPerUnit setting. PPU just sets a scale factor for sizing the sprite in world coordinates — it does not cause the sprite to contain more or fewer texels of source data. \$\endgroup\$
    – DMGregory
    Aug 23, 2022 at 4:08

1 Answer 1

51
\$\begingroup\$

PNG compression works by predicting the next pixel from the pixels that came before.

If the image is completely blank, ie. every pixel is exactly the same as the pixel that came before, then that prediction is very easy, so you can get tiny file sizes even for high-resolution images. That makes it a misleading benchmark to use here.

The trouble is, GPUs cannot read PNGs. They need to be able to seek to different spots in the texture at random, but to do that with a PNG, you'd need to read and do calculations on the data for all pixels before the one you're trying to read before you could determine its colour.

So PNG textures need to be decompressed to use for rendering. One way to do that is to unpack them to literal RGBA8: 8 bits per colour channel, 32 bits per pixel (bpp).

Working that out for your image, that's 1490 x 376 pixels x 32 bpp = 17 928 680 bits = 2 240 960 bytes = 2.137 MiB.

This seems to indicate that because you've set the texture type to "Editor GUI and Legacy GUI", Unity is assuming you need lossless handling of this image, so selecting "Automatic" under the import format options is defaulting to RGBA8.

That 2.137 MiB is the amount of video RAM it will take to use this texture in your scene. If you need to edit the texture from a C# script, then you'll also need a corresponding 2.137 MiB of main RAM for the CPU-side copy of the texture.

There are also GPU compression formats that work differently - typically using a fixed number of bits per pixel (bpp). This is important for realtime rendering - the fixed bitwidth of each block of pixels means you can calculate exactly what memory offset to seek to when you want to sample a specific part of the texture, rather than needing to scan and decompress all the pixels leading to that one as PNG does. This means we can work with them directly from their compressed format, rather than decompressing them to full 32 bpp RGBA data.

For instance, if we store this texture in DXT-5, it will break the image into 4x4 blocks, rounding your image size up to 1492x376, then compress each block to 8 bits per pixel. That's about 547.84 kiB, or about a quarter of the size as uncompressed. You can play with the options in the format and compression drop-downs to find the right trade-off between memory size and visual quality for your needs.

Increasing the rendered size of this texture by displaying it on a scaled-up sprite does not increase the amount of RAM it takes to store. So reducing resolution is a great way to save on texture memory - SDF textures make great use of this, by helping you get crisp lines even on scaled-up textures, when you want a flat-colour/gradient vector art look.

\$\endgroup\$
1
  • \$\begingroup\$ I have no idea how I got here, but this answer is just beautiful! \$\endgroup\$ Aug 24, 2022 at 18:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .