# Embedded UI Performance The renderer exposes two opt-in primitives for animation-heavy interfaces on embedded WebGL devices. They operate below the component layer: applications do not need to remove transitions, delay focus, or replace their UI architecture. ## Retained damage rendering Set `webglRetainedRendering` when most frames change only part of an otherwise static screen: ```ts const renderer = new RendererMain({ // ...normal renderer settings renderEngine: WebGlCoreRenderer, webglRetainedRendering: true, }); ``` The renderer then retains the WebGL drawing buffer and, for each frame: 1. Unions the previous and current bounds of dirty nodes. 2. Clears only that physical-pixel damage rectangle. 3. Intersects authored clipping with the damage rectangle. 4. Redraws the scene through the clipped region. Structural scene changes, viewport changes, clear-color changes, invalid coordinates, and time-driven shaders automatically fall back to a complete redraw. Render-to-texture updates explicitly damage their on-screen parent, so animating the opacity of a cached subtree stays correct without rebuilding its contents. This mode requests `preserveDrawingBuffer`. That can change swap-chain memory and presentation behavior, so it is disabled by default and should be measured on target hardware. It is most useful for TV-style scenes with a large static background and small focus, card, or cached-hero animations. ## GPU-compressed image delivery On devices that expose ASTC/ETC, deliver GPU-ready block-compressed textures instead of decoding JPEG/WebP/PNG into RGBA and uploading every pixel. For example, a 1920×1080 RGBA texture occupies about 8.3 MB before padding; ASTC 6×6 occupies about 0.92 MB. The renderer uploads the ASTC blocks directly and the GPU samples them without expanding the texture in memory. Use one renderer-wide source policy so component code remains format-agnostic: Compressed-container support is compile-time gated to keep applications that never use it small. Define `__enableCompressedTextures__` as `true` in the TV bundle (for example, in Vite's `define` configuration) before supplying the resolver. Leaving the flag undefined preserves the renderer's default regular image path. ```ts const renderer = new RendererMain({ // ...normal renderer settings imageSourceResolver: (src, capabilities) => { if (capabilities.compressedTextureAstc) { return { src: astcCdnUrl(src, '6x6'), type: 'compressed', fallbackSrc: src, }; } return src; }, }); ``` The resolver runs before image fetch/decode. If the preferred asset cannot be fetched or parsed, the authored source loads automatically. The original URL remains the texture cache key, so component identity and UI behavior do not change. `renderer.getCapabilities()` returns cached ASTC, ETC/ETC1, S3TC, PVRTC, multi-draw, and parallel-shader support flags without a later GL sync. The built-in loader accepts raw `.astc`, compressed KTX1, native block-compressed KTX2 (without Basis/supercompression), and PVR containers. Encode dynamic remote images on a server/CDN or in an offline asset build; do not ASTC-encode a 1080p image synchronously on a TV CPU. ASTC 6×6 is a strong default for hero art, while 4×4 or 5×5 is safer for artwork containing small text. Always retain an ordinary fallback for devices without that format. ## Shader warm-up WebGL compiles shader source online and does not expose portable program binaries. By default, a custom shader's first `createShader` call therefore compiles and links its program on the calling frame. Move that work into startup with a warm-up manifest after registering custom shader types: ```ts stage.shManager.registerShaderType('heroReveal', HeroReveal); stage.shManager.registerShaderType('heroAtmosphere', HeroAtmosphere); renderer.preloadShaders([{ name: 'heroReveal' }, { name: 'heroAtmosphere' }]); ``` `preloadShaders` resolves the same properties and cache markers as `createShader`, compiles each unique program once, and stores it in the normal program cache without allocating shader nodes. Later `createShader` calls reuse the linked program. Canvas renderers treat the manifest as an inexpensive no-op. A lost WebGL context fails soft; genuine GLSL errors still throw. Include every source-generating variant in the manifest. Uniform-only values do not create distinct programs unless the shader's `getCacheMarkers` says they do. ## Automatic subtree raster caching Complex clipped containers can be promoted to render textures without marking individual components: ```ts const renderer = new RendererMain({ // ...normal renderer settings autoRenderTexture: { enabled: true, minDescendantQuads: 8, maxTexturePixels: 1280 * 720, memoryBudget: 32 * 1024 * 1024, }, }); ``` Promotion only runs when the renderer becomes idle. A candidate must be a clipped WebGL subtree, fit both the per-texture and total memory limits, contain enough renderable descendants, and have no time-driven shader or nested render texture. Parent opacity and transform changes composite the cached quad without rebuilding its contents. Genuine descendant changes still invalidate it. An automatic cache that invalidates for three consecutive frames is demoted to ordinary rendering, and every automatic cache is released immediately if the GL context reports memory pressure. The setting defaults off; Canvas, dynamic subtrees, and over-budget devices continue through the existing renderer. ## Cheap fades For a complex subtree that fades as one visual unit, render the subtree to a texture and animate the render-texture parent's alpha. Render textures are alpha isolation boundaries: descendants retain local alpha inside the cache and the parent alpha is applied once when the cached texture is composited. With retained damage rendering enabled, the fade damages the cached quad rather than forcing a full-screen clear. ## Measurement Measure release builds on the physical target. Track median, p95, and p99 frame times in addition to a binary jank percentage: a 17 ms frame and a 70 ms frame may both be labeled janky but have very different user impact. Verify route swaps, scrolling, disappearing nodes, clipping, and render-texture updates for retained-pixel artifacts before enabling the mode broadly.