Repository navigation
Releases: cloudwithax/azahar
Release list
RK3568 Optimization v22
Skip texture hash and pipeline lookup on unchanged draws
RK3568 Optimization v21
Changes
- Skip UseFragmentShader (FSConfig construction + hash + map lookup) when no FS-relevant PICA registers have changed since the last draw
- Tracks dirty state for tex_units, texenv, framebuffer, lights, and light_lut categories before reset
- Saves ~200-300ns per draw on Cortex-A55 for consecutive draws with identical fragment shader config
RK3568 Optimization v20 - Per-Draw Overhead Elimination
Changes (aggressive per-draw cost reduction)
FramebufferHelper destructor skip
Consecutive draws to the same framebuffer with the same draw rect now skip the destructor's InvalidateRegion calls entirely. Previously every single draw did:
- 2x
GetSubRectInterval(boost::icl interval math) - 2x
InvalidateRegion→ForEachPage(hash map walks) +MarkValid+dirty_regions.set()
Since these operations are idempotent, skipping them for repeated draws is safe and eliminates ~500ns/draw on A55.
SyncDrawUniforms early exit
Added a quick scan of the dirty register qwords before entering the 14 individual dirty flag checks. When no PICA registers changed between draws (very common for repeated geometry), all branch evaluations are skipped entirely.
BeginRendering framebuffer cache
Skip RenderPass struct construction, Handle()/RenderPass() virtual calls, and member writes when the framebuffer pointer hasn't changed. The inner comparison was already fast-pathed but the outer function still reconstructed everything.
Previous v19 changes also included
- FlushRegion empty() fast path (skip interval map traversal)
- GetFramebufferSurfaces config caching (skip page table walks)
RK3568 Optimization v19 - Rasterizer Cache Fast Paths
Changes
- FlushRegion empty() fast path: Skip boost::icl red-black tree traversal when dirty_regions is empty. SetupVertexArray calls FlushRegion up to 12x per draw, each costing 50-200ns on A55 due to pointer chasing.
- GetFramebufferSurfaces caching: Hash PICA framebuffer config and skip GetSurfaceSubRect (page table walk) + ValidateSurface (interval intersection) on consecutive draws to the same framebuffer. Properly invalidated on surface changes, CPU memory writes, and frame boundaries.
These target the rasterizer cache layer, which was doing redundant surface lookups and interval map queries on every draw call.
RK3568 v18 - CPU-Side Rendering Overhead Reduction
Changes
Performance overlay confirmed the RK3568 is CPU-bound (80% CPU, GPU barely touched). These changes reduce CPU cycles in the Vulkan rendering pipeline:
- SyncDrawState skip — snapshot-compare packed PICA register values between draws; when unchanged (very common in MK7 track geometry), skip all ~20 bitfield Assign() read-modify-write operations entirely
- Merged index buffer bind + draw into single Record call — eliminates one
TypedCommandplacement-new + virtual dispatch per indexed draw (~200ns saved on Cortex-A55 per draw) - Pre-computed vertex buffer offsets — moves
std::transformof 16 DeviceSize casts out of the command recording lambda onto the CPU side before recording
These target the observation that on in-order Cortex-A55 cores, every scheduler.Record() call and every bitfield write has measurable cost that compounds across hundreds of draws per frame.
RK3568 v17 - Performance Overlay Fix + Pipeline Optimizations
Changes
Bug Fix
- Fixed performance overlay crash on GammaOS Lite / LineageOS forks — the
getPerfStatsJNI function was caching a local JNI reference in a static variable, which becomes a dangling pointer after the call returns. On GammaOS the GC reclaims it, crashing the app. Now allocates a fresh 72-byte array each call (overlay polls once/sec, so negligible cost).
Performance (from v16, included in this build)
- Explicit subpass dependencies for Mali G52 render pass pipelining
- Eliminated redundant end-of-pass barriers for non-shadow rendering
- Index buffer rebind guard
- Present descriptor set caching
- Batched stream buffer waits
RK3568 Performance Update v16 - Vulkan Pipeline Optimization
Changes
- Explicit subpass dependencies — declare proper
VK_SUBPASS_EXTERNALdependencies so Mali G52 can pipeline render passes back-to-back without implicit full-pipeline stalls - Eliminate redundant end-of-pass barriers — non-shadow render passes now rely entirely on subpass dependencies for execution/memory ordering, removing per-pass
pipelineBarriercalls - Index buffer rebind guard — skip redundant
bindIndexBufferwhen offset and type are unchanged between draws - Present descriptor caching — cache the presentation descriptor set and only reallocate when screen image views or sampler change (saves 3
vkUpdateDescriptorSetswrites per frame) - Batched stream buffer waits — single
vkWaitSemaphoreson the highest tick instead of per-watch loop when reclaiming buffer space - Remove redundant per-frame settings reads — bg_color already uses the
bg_color_update_requestedflag in DrawScreens
All changes target the Vulkan rendering pipeline on RK3568 (Mali G52).
RK3568 Performance Update v15 - Adaptive Resolution & Quality
Changes
- Adaptive resolution scaling — dynamically lowers resolution on Android when frames are slow, recovers when headroom returns
- Adaptive quality controls — progressively disables custom textures, texture filtering, and accurate multiplication under sustained frame pressure
- Custom texture upload budgeting — throttles texture uploads based on frame timing to prevent stuttering
- Vulkan pipeline & shader cache optimizations — reduced overhead in pipeline creation and shader disk cache
- Right-eye disabler improvements — better heuristics for skipping redundant stereo rendering work
All changes target RK3568 (Cortex-A55) low-power SoC performance.
RK3568 Performance Update v14
Aggressive TBDR renderpass splitting optimization and surface cache enhancements for RK3568 (Mali-G52) devices.
RK3568 Optimization v9 - MK7 GPU Pipeline
MK7-Targeted GPU Pipeline Optimizations for Mali TBDR
New in v9
- Texture descriptor dirty tracking — Skip redundant descriptor allocation and 3 image sampler writes when consecutive draws use identical textures (kart parts, repeated track geometry)
- Shadow atomic CAS early-out — Pre-check depth before entering the imageAtomicCompSwap spin-loop on Mali without fragment shader interlock; most shadow fragments skip the expensive atomic entirely
- Lighting normalize deduplication — Pre-normalize half_vector and view once per light iteration instead of per LUT lookup, saving 4-8+ rsqrt ops per fragment (both GLSL and SPIR-V)
- Combiner buffer passthrough skip — Dead assignment elimination in TEV stages
- Async shader compilation gate removed — Small draws (≤6 vertices) no longer force synchronous compilation, eliminating shader stutter during course preview flyovers
- GLSL reserve 8MB → 512KB — Reduced memory pressure during shader variant storms
- Shader workers 1 → 2 on 4-core SoCs (RK3568)
GPU pipeline fixes
- Subpass attachment layouts: eGeneral → eColorAttachmentOptimal / eDepthStencilAttachmentOptimal (enables AFBC on Mali)
- Narrowed post-renderpass barrier dst scope to eShaderRead only
- Fixed AFBC-disabling need_format_list bug in ScaleUp (was always false for scaled RGBA8)
- Tile-local clears via vkCmdClearAttachments instead of degenerate begin+end renderpass
- Fixed present thread variable shadowing + unreachable threshold ordering
- Exact mip level ranges in CopyTextures barriers
- Upload buffer 512MB → 64MB, MinDrawsToFlush 10 → 30