Even more legacy texture compression?

I noticed that alpha-compressed legacy textures on TVs come as uncompressed files. From this I can conclude that none of the available compression formats with alpha is supported by it?

Basis compression from the editor does not run on the old version of the TV web engine (LG, Samsung).

For textures without transparency, ETC1 compression is performed. But most of the textures in the active project have transparency (world and UI sprites).

Also, applications may restart with a message about using too much memory.

Do I have any options to improve performance in this direction?

For what compression formats are supported, load this on your device:
https://webglreport.com/

and look for these (these are what my Mac supports):
Screenshot 2024-05-03 at 08.40.21

You could enable debug logging to see some of the allocations, to see where the largest sizes come from:

pc.Tracing.set(pc.TRACEID_TEXTURE_ALLOC);

and similarly for
TRACEID_VRAM_TEXTURE, TRACEID_VRAM_VB, TRACEID_VRAM_IB

you can also enable this (for one frame) to get a list of all loaded textures and their sizes: TRACEID_TEXTURES

2 Likes

TRACEID_TEXTURES on PC

Textures: 34
debug.js:85 0. /api/assets/files/Res/Fonts/AGCrownStyle.png?id=175875465&branchId=258d60f2-5ade-4ada-b9a1-e62192c81bb9&t=733ffa7ed23c176f1ec2be91b06ff362 1024x1024 VRAM: 5.33 MB
debug.js:85 1. Radial Gradient.png 512x512 VRAM: 1.00 MB
debug.js:85 2. FON_2_1.png 2048x1024 VRAM: 1.00 MB
debug.js:85 3. Marine_1.png 1024x1024 VRAM: 1.00 MB
debug.js:85 4. Fruits_2.png 1024x1024 VRAM: 1.00 MB
debug.js:85 5. Wild_Animals_2.png 1024x1024 VRAM: 1.00 MB
debug.js:85 6. Pets.png 1024x1024 VRAM: 1.00 MB
debug.js:85 7. FON_1.png 1024x1024 VRAM: 0.50 MB
debug.js:85 8. stars_testura.png 1024x1024 VRAM: 0.50 MB
debug.js:85 9. border-1.png 256x256 VRAM: 0.25 MB
debug.js:85 10. lang.png 512x512 VRAM: 0.25 MB
debug.js:85 11. Confetti_Part.png 215x215 VRAM: 0.18 MB
debug.js:85 12. Linear Gradient.png 512x512 VRAM: 0.13 MB
debug.js:85 13. AreaLightLUT 64x64 VRAM: 0.06 MB
debug.js:85 14. AreaLightLUT 64x64 VRAM: 0.06 MB
debug.js:85 15. mini-stats-word-atlas 512x32 VRAM: 0.06 MB
debug.js:85 16. frame-1.png 256x256 VRAM: 0.06 MB
debug.js:85 17. success-icon.png 256x256 VRAM: 0.06 MB
debug.js:85 18. loading-icon.png 256x256 VRAM: 0.06 MB
debug.js:85 19. frame-2t.png 64x64 VRAM: 0.02 MB
debug.js:85 20. shadow_1.png 128x128 VRAM: 0.02 MB
debug.js:85 21. mini-stats-graph-texture 256x4 VRAM: 0.00 MB
debug.js:85 22. BlueNoise32 32x32 VRAM: 0.00 MB
debug.js:85 23. skin 9x9 VRAM: 0.00 MB
debug.js:85 24. material_placeholder 2x2 VRAM: 0.00 MB
debug.js:85 25. material_placeholder 2x2 VRAM: 0.00 MB
debug.js:85 26. material_placeholder 2x2 VRAM: 0.00 MB
debug.js:85 27. material_placeholder 2x2 VRAM: 0.00 MB
debug.js:85 28. vertical-mask-2.png 2x2 VRAM: 0.00 MB
debug.js:85 29. vertical-mask-1.png 2x2 VRAM: 0.00 MB
debug.js:85 30. CookieAtlas 1x1 VRAM: 0.00 MB
debug.js:85 31. ShadowMap2D 1x1 VRAM: 0.00 MB
debug.js:85 32. element-system 1x1 VRAM: 0.00 MB
debug.js:85 33. sprite 1x1 VRAM: 0.00 MB
debug.js:85 Total: 13.55MB

The TV does not run the debug engine (the loading bar is not displayed) and there are no errors in the console (maybe unsupported font formats from what I see in the Network panel), so there are no TRACEID_TEXTURES for it.

I assumed the alpha texture files were delivered to the TV uncompressed from the Network panel
(.png extension and size).

And ETC1 compressed textures with no alpha.

WebGL report for TV

Looks like it only supports ETC1 which has no alpha support. (So even Basis won’t help you here)

Sounds like the fonts texture file is the largest VRAM usage. Do you need all the characters that are currently being generated? Can you reduce it to a subset? If not, consider using HTML for your UI instead.

Some the other images are large in dimension. Given the TV resolution, does FON_2_1.png and the other textures need to be as large as they are? Can any of them be reduced down in dimension size?

You could also separate RGB and ALPHA channels to separate textures. That way those could be compressed. In the material, diffuse (or emissive) and opacity textures can be specified as two separate textures.

Unfortunately, for sprite assets (texture atlases) / element components, they don’t have support for a separate alpha texture. I guess you could use a material asset for the UI element component but that could be a little tedious to set up depending on the number of sprites you have.

Edit: Or perhaps you could use a custom shader (eg PlayCanvas Examples) that color keys a specific colour as fully transparent

2 Likes

@yaustar and that’s a really cool idea, thanks! I’ll try it later and report back with the results :+1:

I would look at the font suggestion first as that’s taking up over a third of all your texture VRAM usage

I already use a minimal set of Latin and Cyrillic characters for font (requires Ukrainian language support). I would like to somehow control the resolution of the generating texture, but I don’t see such an option in the editor.

I can’t remember if the font texture needs the alpha channel. If not, you can look at this thread where we did a post build process to convert the font texture to Basis. Perhaps you can do the same to ETC1 Basis compression of font png created from font asset - #10 by RyutoHaga

Finally got around to this task and made this small script that replaces the selected color with a transparent one using the opacity chunk (applying it for default sprite and element material).

We still need to work something out together with the artists to get rid of the artifacts, but the result is promising.

I’m not sure that I understood all the nuances of working with chunks, am I not doing anything that can create problems in the code where I manipulate the chunk?

Maybe there is some resource that describes with examples how everything works on the current version of chunks? Because I see only a brief description of the changes in the manual.

var OpacitySetter = pc.createScript('opacitySetter');

OpacitySetter.attributes.add('alphaColor', {type:'rgb', default:[1,1,1]});

OpacitySetter.prototype.postInitialize = function(){
    OpacitySetter.instance = this;
    this.materialsSet = new Set();
    this.apply(this.app.systems.sprite.defaultMaterial);
    this.apply(this.app.systems.sprite.default9SlicedMaterialSlicedMode);
    this.apply(this.app.systems.element.defaultScreenSpaceImageMaterial);
    this.apply(this.app.systems.element.defaultScreenSpaceImage9SlicedMaterial);
    for(const defaultImageMaterial of this.app.systems.element.defaultImageMaterials)
        this.apply(defaultImageMaterial);
};

/**@param {pc.Material} material
 * @param {Number} alphaColor
*/
OpacitySetter.prototype.apply = function(material){
    if(this.materialsSet.has(material)) return;
    this.materialsSet.add(material);
    
    material.chunks.APIVersion = pc.CHUNKAPI_1_70;
    material.chunks.opacityPS = 
        "#ifdef MAPFLOAT\n" +
        "uniform float material_opacity;\n"+
        "#endif\n"+
        "uniform sampler2D texture_opacityMap;\n" +
        "\n" +
        "void getOpacity() {\n" +
        "    vec2 uv = $UV;\n" +
        "    vec4 texColor = texture2D(texture_opacityMap, uv);\n"+
        "    if (texColor.rgb == vec3(" + this.alphaColor.r + "," + this.alphaColor.g + "," + this.alphaColor.b + ")) { \n" +
        "       dAlpha = 0.0; \n" +
        "    } else {\n" +
        "       dAlpha = texColor.a;\n" +
        "       #ifdef MAPFLOAT\n"+
        "       dAlpha *= material_opacity;\n"+
        "       #endif\n"+
        "    }\n" +
        "}\n\n";
    material.update();
}

EDIT: Code changes to bring back support for element opacity option in Editor.

1 Like