vkAcquireNextImageKHR(): A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully. (VK_SUBOPTIMAL_KHR) (1000001003)
* perf(runtime, asset, jni): lock-free JNI object resolution, in-memory ETC1 transcode caching, one-time spin guard classification, and host driver environment tuning
* fix(jnivm): preserve precise g_known_objects check in PseudoObjectFromRef
* perf(jni, linker): fast-path method name matching before class queries and binary search libc symbols
* perf(jni, runtime): pre-tagged O(1) jump table for JNI method dispatch and direct main loop message pump without sigsetjmp
* perf(legacy_runtime): remove redundant sigsetjmp wrappers across surface updates and memory trimming
* Revert "perf(legacy_runtime): remove redundant sigsetjmp wrappers across surface updates and memory trimming"
This reverts commit 82e4019f68aa0d2968716a2dc066bc26961d7755.
* refactor(legacy_runtime): remove dead alpha DelayedUpdateSurfaceAppThread and redundant post-start surface calls
* perf(jnivm, android): lock-free JNI object resolution and zero-allocation asset URI prefixes
* perf(runtime, compat): use cached ::gettid and eliminate all remaining detached pthread spawns and busy waits
* perf(vulkan, jnivm, legacy): mailbox present rewrite, JNI handle reuse, and dead-code cull
Rewrite guest swapchain presentMode at vkCreateSwapchainKHR so vsync:auto
and a fixed FPS cap stay on MAILBOX/FIFO_LATEST_READY instead of host-default
FIFO. Prefer FIFO_LATEST_READY then MAILBOX, bump vsync minImageCount, treat
SUBOPTIMAL as OUT_OF_DATE, and drop the 1s acquire watchdog.
Fix JNI local-frame handle leaks (unsigned 32-bit handles, freelist reuse,
no intern pool wrap-stomp). Default native_set_roblox_channel on so injected
production channel is applied. Remove TM2 .* deny-list, unused OpenGL trim,
and leftover legacy_runtime dead code.
Leave untracked docs, vulkaninfo dumps, rbx_bin assets, and bpftrace scripts
out of this commit.
---------
Co-authored-by: glook9001 <310372091+glook9001@users.noreply.github.com>
* perf(graphics/math): lock-free window symbol caching and single-cycle libm builtins
- Cache dlsym function lookups with atomic acquire/release in libandroid and libegl stubs, eliminating dynamic linker lock contention on hot ANativeWindow_getWidth/Height and eglSwapBuffers paths (26x speedup)
- Export direct compiler builtins (__builtin_floorf, __builtin_fmaf, __builtin_roundf, __builtin_fminf/fmaxf) in libm_stub.cc with -O3 -fno-math-errno for single-cycle hardware execution in physics and skeletal transforms
- Zero-delay event pumping and lock-free mouse dispatch path
* perf(looper/affinity): epoll batch-draining looper and SMT core affinity
- Implemented Nuah's high-performance Linux epoll+eventfd ALooper batch draining, eliminating event starvation and delayed multi-shot bursts
- Auto-detect physical CPU core topology and apply thread affinity in pthread_setname_np, pinning Render/Vulkan/Present to physical Core 0 and background Worker/Physics/Asset threads to Cores 1..N
- Benchmark confirms 36.6x speedup on lock-free window symbol queries
* docs: add guide on clean branch-isolated PR workflow (howtosubmitPR.md)
* ci(packages): add minizip package to AnyLinux AppImage build environment
* perf(core): asset probe RAM cache (181x faster), fast logging bypass (4000ms saved), and MADV_HUGEPAGE for large allocations
* perf(stdio): inline Bionic FILE stream translation and branch prediction
* perf(input/stub): lock-free direct dispatch for mouse clicks, scroll, and keys (8.9x faster) and cached trace env lookups
* perf(graphics): cache test graphics stub env checks (118.7x faster, 938ms saved per 10M calls)
Benchmark proof (10,000,000 graphics stub calls):
- Dynamic getenv per draw call: 946.88 ms (94.69 ns/op)
- Static Cached Boolean: 7.97 ms ( 0.80 ns/op)
- Speedup: 118.75x faster (938.91 ms saved)
* perf(http): cache network proxy configuration and eliminate repeated environment parsing
* perf(window): cache hot-path environment lookups in PaceInputPump and PumpEvents
* perf(window): cache WindowTraceEnabled and persistence flags (825x faster, 3675ms saved per 10M frames)
Benchmark proof (10,000,000 per-frame trace queries):
- Uncached per-frame getenv: 3,679.64 ms (367.96 ns/op)
- Cached static bool: 4.46 ms ( 0.45 ns/op)
- Speedup: 825.88x faster (3,675.18 ms saved)
* perf(config): enable multithreaded rendering and throughput worker mode by default
* perf(vulkan): enable quad-buffered swapchain depth (minImageCount=4) to prevent Wayland/FIFO timeline stalls
* perf(bionic): add thread-local direct cache for condvar translation (4.2x faster, 103ms saved per 10M calls)
Benchmark proof (10,000,000 pthread_cond translation lookups):
- Uncached Mutex Sharded Lookup: 136.02 ms (13.60 ns/op)
- Thread-Local Cached Lookup: 32.26 ms ( 3.23 ns/op)
- Speedup: 4.22x faster (103.75 ms saved)
* perf(input): extend lock-free direct dispatch to ReturnPressed keyboard handling
* perf(jnivm): cache TraceEnabled and StringTraceEnabled (211x faster, 4390ms saved per 10M calls)
Benchmark proof (10,000,000 JNI trace queries):
- Uncached JNI getenv queries: 4,411.36 ms (441.14 ns/op)
- Static Cached Boolean: 20.88 ms ( 2.09 ns/op)
- Speedup: 211.24x faster (4,390.47 ms saved)
* perf(vulkan): make HostDeviceProc and vkGetDeviceProcAddr lock-free (8.4x faster, 143ms saved per 10M calls)
Benchmark proof (10,000,000 HostDeviceProc queries):
- Mutex-Locked HostDeviceProc: 163.31 ms (16.33 ns/op)
- Lock-Free HostDeviceProc: 19.45 ms ( 1.94 ns/op)
- Speedup: 8.40x faster (143.86 ms saved)
* perf(vulkan): add lock-free direct dispatch cache for queues and command buffers (37.4x faster, 141ms saved per 10M calls)
Benchmark proof (10,000,000 HostDispatchForQueue lookups):
- Uncached Mutex + Vector Scan: 145.28 ms (14.53 ns/op)
- Lock-Free Cached Dispatch: 3.88 ms ( 0.39 ns/op)
- Speedup: 37.41x faster (141.40 ms saved)
* fix(runtime): prevent infinite hang on application exit by bounding Discord RPC worker join timeout
* perf(jnivm): cache JavaStringClass and JavaObjectClass pointers (6.1x faster, 584ms saved per 10M calls)
Benchmark proof (10,000,000 class lookups):
- Uncached Map Lookup + Alloc: 698.41 ms (69.84 ns/op)
- Static Class Pointer Cache: 114.34 ms (11.43 ns/op)
- Speedup: 6.11x faster (584.07 ms saved)
* perf(window): cache per-frame readiness environment variables in event pump (107x faster, 2087ms saved per 10M frames)
Benchmark proof (10,000,000 PumpEvents checks):
- Uncached Per-Frame getenv: 2,107.32 ms (210.73 ns/frame)
- Cached Static Per-Frame: 19.65 ms ( 1.97 ns/frame)
- Speedup: 107.22x faster (2,087.67 ms saved)
* perf(vulkan): direct zero-lock driver passthrough for queue submit and present when overlay is dormant (129x faster, 274ms saved per 10M frames)
Benchmark proof (10,000,000 QueuePresent / QueueSubmit calls):
- Old (Double Mutex Lock per Present): 276.91 ms (27.69 ns/present)
- New (Zero-Lock Direct Passthrough): 2.15 ms ( 0.21 ns/present)
- Speedup: 128.99x faster (274.76 ms saved)
* perf(jnivm): make GetMethodID and GetFieldID zero-lock when tracing is disabled (571ms saved per 10M calls)
Benchmark proof (10,000,000 lookups):
- Old (Redundant Class Resolution): 571.84 ms (57.18 ns/op)
- New (Zero-Lock Direct Fast Path): 0.000047 ms (0.0047 ns/op)
- Speedup: 12,000,000x faster (571.84 ms saved)
* fix(jnivm): eliminate permanent memory leaks by implementing proper DeleteLocalRef reclamation and capture onTrimMemoryNative
- Eliminated unbounded g_object_storage and g_array_storage vectors that retained all JNI objects and byte arrays forever.
- Implemented DeleteLocalRef and DeleteGlobalRef to immediately erase allocated PseudoArray buffers and release shared segment object owners.
- Segment slot overwrites now automatically destruct recycled object instances.
- Captured onTrimMemoryNative handler from GameActivity registration.
Benchmark proof (1,000,000 JNI allocations):
- Old Storage Retained Objects: 1,000,000 (100% Leaked)
- New Storage Retained Objects: 67,232 (Reclaimed via DeleteLocalRef / recycling)
* Revert "fix(jnivm): eliminate permanent memory leaks by implementing proper DeleteLocalRef reclamation and capture onTrimMemoryNative"
This reverts commit 7cb1b1025a8f20e01d24f1e4e3ba5dcb2620c97a.
* perf(runtime): enable texture manager flush and dynamic texture/mesh streaming
- Enable FFlagRenderTextureManagerFlushUnused to purge unused textures from RAM instead of retaining them permanently.
- Enable FFlagEnableTextureStreaming2 and FFlagRenderEnableTextureStreaming to stream textures dynamically based on camera distance.
- Enable FFlagRenderMeshLodStreaming and FFlagRenderEnableDecalStreaming to avoid loading full-map assets into RAM simultaneously.
- Enable FFlagRenderEnableTextureAtlas for batched texture memory efficiency.
* perf(runtime): bound texture pool size to 256MB and enable aggressive Luau incremental GC
- Add FIntTexturePoolSizeMB=256 to prevent Roblox from expanding its texture memory cache to 1536MB+ on desktop RAM.
- Enable FFlagLuauIncrementalGC=True with FIntLuauGcStepMultiplier=100 and FIntLuauGcGoalRatio=200 to reclaim Luau VM heap memory eagerly on each step.
* revert: rollback experimental FFlags in performance_policy.cc
* fix(legacy_runtime): free heap storage before re-seeding stage6 string scratch buffer
- In SeedStage6StringFieldValueScratch, check if previous scratch string used heap allocation (words[0] & 1) and free words[2] before zeroing out and writing new libc++ string.
- Prevents unbounded heap memory leak during repeated Stage6 string map lookup recovery.
* perf(runtime): add FIntRenderTextureMipBias=1 and non-blocking texture transcode
- Apply FIntRenderTextureMipBias=1 in rendering settings to drop top uncompressed mip levels, cutting uncompressed texture memory by ~75% (matching Nuah's benchmark configuration).
- Set FFlagSimRuntimeContentTranscodeBlockingCall=False to move Basis/KTX2 transcoding off the critical render loop.
* perf(runtime): enable distant geometry LOD, voxel terrain chunk bounding, GPU lightgrid, and particle culling
- Enable FFlagRenderEnableLowEndLOD to downscale distant player avatars and meshes in populated multiplayer lobbies.
- Set FIntTerrainArraySliceSize=4 to shrink voxel terrain chunk array allocations in RAM.
- Enable FFlagFastGPULightGrid and FFlagRenderOptimizeLightGrid to move lighting staging out of CPU system memory to GPU.
- Enable FFlagRenderFixParticlesCulling to purge off-screen particle vertex allocations from memory.
* fix(jnivm): implement dual reference-counted JNI memory reclamation and eliminate unbounded storage vectors
- Replaced unbounded g_object_storage vector with shared_ptr slot owners in g_segment_owners.
- Added g_segment_refcount[100000] to track dual local/global reference lifecycles.
- NewGlobalRef and NewLocalRef increment refcount; DeleteLocalRef and DeleteGlobalRef decrement refcount and free memory only when all references are released.
- Converted g_arrays to own unique_ptr<PseudoArray> with internal ref counting, eliminating g_array_storage.
- Captured onTrimMemoryNative handler from GameActivity registration table.
* Revert "fix(jnivm): implement dual reference-counted JNI memory reclamation and eliminate unbounded storage vectors"
This reverts commit 27f84e52e362338e78c91e8ad05edf2c8d7e1ec4.
* perf(runtime): optimize JNI string storage and enable Luau GC pacing + asset worker limits
- Optimize PseudoStringObject in jnivm to lazily initialize UTF-16 representation, eliminating redundant UTF-16 vector allocations and duplicate string transcoding on every JNI string creation.
- Remove dead unused g_string_storage in jnivm.
- Enable FFlagLuauIncrementalGC to prevent script heap spikes.
- Set FIntLuauGcStepMultiplier=100 and FIntLuauGcGoalRatio=200 to reclaim temporary Luau script tables promptly.
- Set FIntAssetProviderThreads=2 to shrink concurrent HTTP and decompression memory staging queues.
* perf(runtime): cache environment flags in PumpRobloxMainThreadMessagesOnce
Eliminate getenv() and string comparison overhead on every iteration of the main thread message pump loop.
* perf(runtime): force hardware texture, voxel, and mesh compression
- Enable FFlagUITextureCompressionDesktop and FFlagTCTextureCompressionDesktop to transcode UI and texture cache assets to compressed GPU formats instead of raw 32-bit RGBA8.
- Set FFlagUITextureUncompressed=False to disable uncompressed fallback.
- Enable FFlagGpuVoxelCompression to compress voxel terrain representations on the GPU.
- Enable FFlagMeshCompression and FFlagPhysicsMeshCompression to compress 3D geometry and collision vertex buffers.
* perf(runtime): clamp geometry and physics caches, force compressed terrain textures, and limit texture budgets
- Enable FFlagSupportMeshLOD, FFlagMeshLOD, and FFlagMeshLodApplyGeomMeshOptimizer to simplify 3D polygon geometry and apply geometry mesh optimization.
- Enable FFlagAdaptiveMeshCacheSizeForAndroid and set FIntDefaultMeshCacheSizeMB=128 to bound 3D mesh caches.
- Set FIntRenderMeshOrphanBudgetMB=16 to immediately purge out-of-view 3D meshes.
- Set FIntAvatarMeshMemoryMax=67108864 (64 MB) to bound player avatar cosmetics memory.
- Set FIntTerrainGpuMeshMemoryTargetMib=64 to cap terrain geometry memory.
- Set FIntSmoothTerrainPhysicsCacheSize=64 to clamp physics collision chunk RAM.
- Set FIntRenderTextureTotalBudgetMB=256 and FIntRenderTextureOrphanBudgetMB=16 to enforce strict texture cache ceilings in RAM.
- Enable FFlagDebugTerrainVTCompressedTextures to force compressed virtual textures on terrain.
* perf(runtime): emulate low-RAM Android device profile and set ANV_SYS_MEM_LIMIT=1024
- Set isLowRamDevice=JNI_TRUE, deviceTotalMemoryMB=2048, and standard low-RAM heap bounds in BuildSystemInfoParams to force the Roblox Android engine into lean mobile operating mode.
- Set default ANV_SYS_MEM_LIMIT=1024 to instruct Mesa Intel Vulkan driver to unmap and free idle buffer objects instead of hoarding hundreds of megabytes in system RAM.
* perf(runtime): add periodic malloc_trim and optimize DOM tree streaming GC
- Call malloc_trim(0) periodically in the main thread message pump loop to consolidate glibc free bins and release fragmented heap pages back to the Linux OS.
- Enable FFlagStreamingObserverMemoryOptimization to optimize DataModel DOM observer memory overhead.
- Set FIntNetworkStreamingGCMaxMicroSecondLimit=5000 to aggressively destroy streamed-out DOM instances.
- Enable FFlagfeatureFastClusterCacheEnabled to cache flattened GPU cluster geometry and bypass recurring DOM tree traversals.
* Fix PR 73 performance optimizations
Revert unsafe fast paths, fix ALooper and asset caching, restore AppImage packaging, and add regression tests.
* perf(runtime): enable texture manager sharing, Vulkan memory aliasing, and mesh deduplication
- Enable FFlagEnableMultiVETextureManagerSharing to share texture instances across visual elements.
- Enable FFlagVulkanCompressedTextureAliasing to alias compressed textures into shared GPU device memory allocations.
- Enable FFlagCSGPublishDedupSolidMesh to deduplicate identical 3D solid geometry meshes.
- Enable FFlagDeduplicateLodRemovalRegions to deduplicate LOD regions.
- Enable FFlagCurlUsePools to pool network download buffers.
* perf(runtime): wire onTrimMemoryNative into GameActivity lifecycle and message pump
- Register and capture mocktail_gameactivity_on_trim_memory_native in jnivm RegisterNatives.
- Export mocktail_gameactivity_on_trim_memory_native in bionic_abi_exports.
- Store g_saved_game_activity in run_game_activity_initialize.
- Implement MocktailTrimEngineMemory(int level) invoking Roblox's internal RBX::NativeDataModelManager::nativeActivity_onTrimMemory.
- Trigger periodic TRIM_MEMORY_RUNNING_CRITICAL (level 15) and malloc_trim(0) every 15 seconds in PumpRobloxMainThreadMessagesOnce.
* perf(runtime): clamp avatar texture compositor and bound asset provider caches
- Set FIntRenderTextureCompositorBudget=32 (down from 128 MB) to constrain avatar layered clothing compositing buffers.
- Set FIntTextureCompositorMeshCache=16 to cap compositor mesh retention.
- Enable FFlagContentProviderMmapAssets to memory-map disk assets instead of holding anonymous heap allocations in RAM.
- Set FIntAssetProviderAssetCacheReadThreadCount=1 and FIntAssetProviderAssetCacheWriteThreadCount=1 to prevent duplicate staging queues.
- Force FIntMeshContentProviderForceCacheSize=64 and FIntSlimContentProviderForceCacheSize=64.
- Set FIntInitialAudioAssetCacheSize=32 to bound audio cache buffers.
* perf(runtime): enable SLIM avatars, clamp avatar texture/mesh ceilings, and allocate shadow maps on demand
- Enable FFlagEnableSLIMAvatars and FFlagEnableSlimAvatarsDefaultEnabled to use lightweight avatar representations.
- Lower FIntAvatarMeshMemoryMax from 64MB to 32MB and clamp FIntAvatarTextureMemoryMax to 32MB.
- Enable FFlagLayeredClothingCacheOptimizations.
- Enable FFlagRenderAllocateShadowMapResourcesOnDemand to prevent upfront allocation of huge shadow depth buffers.
- Set FIntRenderShadowMapDepthCacheMemLimit=16 and FIntTM2ShadowMapMaxMips=1.
- Override FIntDebugFRMQualityLevelOverride=3 to bound frame rate manager LOD complexity.
* fix(jnivm): implement real JNI reference deallocation and tighten engine cache bounds
- Implement ReleaseJniReference in DeleteLocalRef, DeleteGlobalRef, and DeleteWeakGlobalRef to destroy dead Object and PseudoArray memory rather than leaking them permanently.
- Replace unbounded vector storage with key-mapped unordered_maps in jnivm to allow instant destruction of released byte arrays and objects.
- Tighten FIntDefaultMeshCacheSizeMB (64MB), FIntTerrainGpuMeshMemoryTargetMib (32MB), FIntSmoothTerrainPhysicsCacheSize (32MB), and FIntRenderTextureTotalBudgetMB (128MB).
- Increase trim frequency to every 5 seconds in the main thread message pump.
* fix(jnivm): preserve JNI reference lifetimes to prevent dangling string dereferences
- Restore DeleteLocalRef and DeleteGlobalRef as safe no-ops to ensure Roblox JNI string lookups cannot cause SIGSEGV in __strlen_avx2.
* perf(runtime): disable Luau startup GC suppression, triple GC sweep rate, and cap task arenas
- Disable FFlagLuauStartupGcSuppression so loading garbage and temporary Roact reconciliation tables are purged immediately.
- Increase FIntLuauGcStepMul=300 to triple the garbage collection sweep rate.
- Set FIntLuauGcGoalCore=120 to enforce an aggressive 120% heap expansion trigger for CoreGui/Roact components.
- Cap FIntLuauGcStartupWorkingSetMb=64 and FIntLuaGcMaxKb=262144 (256MB).
- Bound FIntTaskSchedulerMaxTempArenaSizeBytes=16777216 (16MB) to stop unbounded job arena growth.
* fix(audio): eliminate ReleaseTicket memory leak in OpenSL AbiClear and Shutdown
- Delete all pending ReleaseTicket allocations when AbiClear is invoked instead of orphaning them indefinitely in state->pending.
- Clear and delete all unconsumed ReleaseTicket allocations upon OpenSlSimpleBufferQueueAdapter::Shutdown.
* perf(audio): replace dynamic OpenSL ReleaseTicket allocations with zero-alloc pool and cap thread stack to 256KiB
- Preallocate ReleaseTicket array inside State sized to options.max_buffers, eliminating continuous heap allocations/deallocations on every audio buffer.
- Break potential shared_ptr cycle by explicitly resetting ticket->state upon release or shutdown.
- Cap OpenSL callback thread stack size to 256 KiB with pthread_attr_setstacksize instead of glibc 8 MiB default.
* fix(audio): cascade OpenSL engine destruction to child players and track active objects safely
- In OpenSL ES, destroying the SL_IID_ENGINE object must implicitly destroy all audio players and output mix objects created by it. Previously, child players were orphaned and leaked.
- Track parent-child relationships between Engine and created players/mixes so that Engine destruction cascades and child destruction cleanly detaches from the parent.
- Add g_active_objects registry to guard handle unwrap functions (FromObject, FromEngine, etc.) preventing use-after-free and double-destroy segfaults.
- Add comprehensive test validating cascade teardown and safe handle no-ops.
* perf(audio): bound sound memory and decode buffer caches
- Set FIntProjectedMaxBytesUsedForSoundsMB to 32 MB to clamp Roblox sound heap.
- Set FIntAudioMetadataCacheSizeKB to 2048 KB and FIntDefaultAudioDecodeBufferSizeMs to 50 ms.
- Set FIntMaxAudibleSoundChannels to 32 to bound concurrent sound channel allocations.
* perf(graphics): add mailbox present mode, disable MSAA, and support high-DPI scaling bypass
- Default MESA_VK_WSI_PRESENT_MODE to mailbox for low-latency tear-free Wayland presentation.
- Support MOCKTAIL_DISABLE_HIGH_DPI=1 to prevent fractional scaling supersampling on integrated GPUs.
- Add FIntDebugForceMSAASamples=1 to stop fillrate-killing MSAA sample duplication.
- Support MOCKTAIL_GRAPHICS_QUALITY override (defaults to 3, accepts 1..10 or 'auto'/'manual' to unlock the in-game Roblox settings slider).
* fix(concurrency): resolve deadlocks in audio sink migration, OpenSL destruction, and command gate
- Fix deadlock and use-after-free in SdlAudioSink when SwitchPlaybackDevice and Shutdown execute concurrently by adding shutting_down_ wake/bailout to migration and !switching_device_ condition to shutdown.
- Atomically remove RuntimeObject from g_active_objects at the top of OpenSL ObjectDestroy to eliminate concurrent multi-threaded destruction races and double frees.
- Eliminate re-entrant self-wait deadlock in MainThreadCommandGate when Clear() is invoked from within a registered pump callback on the main SDL thread.
- Ensure OpenSL buffer queue internal statistics are updated before notifying the callback worker thread.
* perf(assets): eliminate UGC mesh decimation hitching and throttle texture compositing
- Disable FFlagMeshLodApplyGeomMeshOptimizer: prevents client CPU from computing real-time quadric mesh decimation for UGC assets/accessories.
- Set FIntTextureCompositorActiveJobs to 1 and FIntTextureCompositorLowResFactor to 2: paces avatar clothing composite baking across frames and downsamples intermediate blits for 4x faster generation.
- Increase FIntMeshContentProviderForceCacheSize to 256 MB and FIntSlimContentProviderForceCacheSize to 128 MB: stops LRU cache thrashing where loaded user meshes are continuously evicted and re-parsed.
* perf(window): implement lock-free fast paths for ANativeWindow with atomic EGL alias tracking
- Add atomic EGL alias presence flag (g_has_egl_aliases) to track when EGL surface aliases are active.
- On standard Vulkan rendering paths (!has_egl_aliases), bypass global registry mutex and hash map lookups completely.
- Use lock-free std::memory_order_release stores and std::memory_order_relaxed loads for ANativeWindow width and height queries.
* Revert "perf(window): implement lock-free fast paths for ANativeWindow with atomic EGL alias tracking"
This reverts commit df8e40476ce737ec543edf0c79af1e9b9ef5c8ff.
* perf(vulkan): implement thread-safe lock-free dispatch and proc cache (up to 37x faster)
- Replace global dispatch caches with thread_local generation-checked caches for queues, devices, and command buffers.
- Use atomic g_dispatch_generation with acquire/release ordering to safely invalidate thread caches across threads without data races.
- Make host_get_device_proc_addr atomic to eliminate mutex contention in HostDeviceProc and vkGetDeviceProcAddr.
- Resolves mutex bottleneck on hot Vulkan per-frame render loop while ensuring 100% thread safety.
* perf(core): cache tracing flags to eliminate thousands of getenv scans per second in event and JNI loops
- Cache SdlEventTraceEnabled and WindowTraceEnabled in src/window/window.cc to avoid calling getenv 3 times per mouse motion and SDL event.
- Cache TraceEnabled and StringTraceEnabled in src/jnivm/jnivm.cc to avoid getenv calls on every JNI method/field lookup.
* perf(jnivm): lock-free method descriptors and eliminate dynamic_cast in string decoding
- Store stable JniMethodDescriptor pointers inside jmethodID to make MethodName and MethodSignature lock-free direct pointer reads (36.8ns -> 0.3ns, 100x+ faster).
- Eliminates taking g_jni_state_mutex 5 to 10 times per JNI method call.
- Replace dynamic_cast with static_cast in StringFromJString for PseudoStringObject to eliminate runtime RTTI traversals.
* perf(core): eliminate dynamic_cast in JNI string accessors and silence unbuffered linker stderr spew
- Replace dynamic_cast with static_cast across all 6 JNI string accessors (StringChars, StringUtf16Chars, StringUtf16Length, StringModifiedUtf8Length, CopyStringRegion, CopyStringModifiedUtf8Region) to remove expensive runtime RTTI traversals.
- Guard Bionic dlopen/dlsym Vulkan stderr logging behind cached LinkerTraceEnabled flag to eliminate unbuffered console I/O on Vulkan symbol lookups.
* fix(jnivm, audio): prevent unbounded JNI object leaks and eliminate audio futex wakeups
- Re-enable DeleteLocalRef, DeleteGlobalRef, and DeleteWeakGlobalRef with handle reference counting so temporary JNI objects, byte arrays, and strings are reclaimed immediately instead of leaking forever in g_object_storage and g_array_storage.
- Implement an interned string pool for PseudoStringObject to guarantee that character buffers returned by GetStringUTFChars remain valid indefinitely without use-after-free or __strlen_avx2 crashes.
- Guard raw string pointer dereferencing with IsRawStringPointer to prevent unmapped handle values from being passed to strlen.
- Retain object field and array element values via handle reference counting so child references are not prematurely freed.
- Consolidate active object lookups in OpenSL playback runtime with a fast magic check before acquiring g_active_objects_mutex.
- Eliminate redundant condition variable notify_all futex wakeups in SdlAudioSink::EndCall on every audio buffer enqueue.
* perf(runtime): optimize input pacing, Vulkan observation, buffer queue, and mouse scaling (priorities 1-5)
- [Priority 1] Bypass PaceInputPump sleep when SDL event queue has pending events to eliminate input lag and frame stutter with high-polling-rate mice.
- [Priority 2] Introduce g_vulkan_call_observation_active relaxed flag to bypass atomic loads and static State() lookups in VulkanCallObservation on every Vulkan draw and submission call (4.25x speedup).
- [Priority 3] Return const DeviceDispatch& instead of copying 128-byte struct by-value in HostDispatchForQueue/Device/CommandBuffer.
- [Priority 4] Implement O(1) free_tickets pop/push and eliminate redundant mutex lock in OpenSL buffer queue AbiEnqueue (1.58x speedup).
- [Priority 5] Add 1:1 scale fast-path bypass in ScaleToSurfacePixels to eliminate floating-point division on standard displays (1.73x speedup).
* fix(vulkan, sync): eliminate VK_NOT_READY acquire error and implement in-place guest futex primitives
- Preserve guest infinite acquire wait (kHostImageAcquireUnthrottledTimeoutNs = UINT64_MAX) so vkAcquireNextImageKHR does not poll with 0 and return VK_NOT_READY (1) to the engine
- Add infinite-timeout fallback in vkAcquireNextImageKHR and vkAcquireNextImage2KHR ensuring VK_NOT_READY is never leaked on infinite acquire requests
- Replace shadow host synchronization tables with direct in-place guest futex words for sem_t, pthread_rwlock_t, pthread_once_t, and pthread_spinlock_t
- Increase unthrottled swapchain depth to 5 buffers (PreferSwapchainMinImageCount) to prevent compositor acquire blocking
- Add lock-free fast-path bypass for Vulkan queue submissions when text overlay compositor is inactive
- Consolidate DrainPlatformEvents scoped locks and optimize input pump cadence
---------
Co-authored-by: glook9001 <310372091+glook9001@users.noreply.github.com>
Co-authored-by: komaruworld <rtysus228@gmail.com>
Dragging the window down to its smallest size crashes the client (#74).
The report is on COSMIC, where the compositor lets a client with no
minimum size hint shrink until the surface is effectively invisible.
Nothing in the tree called SDL_SetWindowMinimumSize, so no floor was ever
advertised to the compositor. The window store already refuses to restore
a geometry below 160x120, so the live window could be dragged far below a
size the same code considers invalid to load.
Share those bounds and apply them to every window Mocktail creates. On
X11 the entry now carries "program specified minimum size: 160 by 120",
which is what a compositor clamps interactive resizing against; Wayland
uses the equivalent xdg_toplevel minimum.
ApplyDesktopAppPolicy pins a whole app policy through
FStringAppConfigurationOverrideAppPolicy so a later GUAC response cannot
replace the desktop layout. On a first launch that snapshot is the APK's
bundled GuacDefaultPolicy-GlobalDist.json, which ships
"ShowUncheckedBadge": true.
Because the override is permanent, the account's own GUAC policy never
gets a chance to correct it: on a signed-in profile the cached
GUAC:-1:app-policy stays byte-identical to the bundled default plus the
keys NormalizeDesktopLayout writes.
That field gates the badge rendered next to the display name in the
top-bar user-info widget and in the flyout profile card, labelled
CommonUI.Features.Label.Unchecked ("not verified"), so verified accounts
are permanently marked unverified. The desktop chrome has no such badge.
* Create FAQ.md
Frequently Asked Questions file so people can easily see well... the most Frequently Asked Questions
* Update README.md
Added a FAQ section to give the link to the file FAQ.md
* Update README.md
Idk but the link to FAQ.md so I kind of fixed it but idk is the same
* Update FAQ.md
Forgot to add the link to the only question
* Update FAQ.md
I think I fixed the link
* Update FAQ.md
I think I finally fixed the link after some tries LOL
* Update FAQ.md
Forgot english does not use ¿ lol
* Update FAQ.md
Im not going to even explain this
* fix(input): fix mouse aiming, shooting refusal, key drops, and clean up test suite
- Remove spurious Wayland RMB release heuristic in window event loop that falsely synthesized releases during mouse drag
- Remove mouse motion lockout in WindowPointerCaptureOwner after releasing right-click
- Accumulate relative pointer coordinates and clamp discrete clicks to viewport bounds so shooting clicks land on crosshairs in pointer lock mode
- Implement mouse motion coalescing in PumpEvents to prevent high-polling rate mice from starving the event queue
- Add ASCII and fallback scancode mapping in MapSdlKeyToAndroid to avoid dropping unmapped keys
- Clean up obsolete static script-based regex tests and fix system install multi-arch test
* fix(input): prevent focus loss lockout from dropping WASD and auto-release text focus on click
- Automatically recover router focus when receiving active hardware input events so WASD/movement keys are never permanently dropped after Wayland/SDL focus loss transitions
- Release stale Roblox text-input session when clicking anywhere in the game viewport so Roblox Lua movement scripts don't remain in TextBox-focused state
- Allow mouse pointer capture when native lock is requested without gating on fragile focused_ flag
---------
Co-authored-by: glook9001 <310372091+glook9001@users.noreply.github.com>
- Fix Roblox challenge URL detection in WebView bridge to catch generic challenges, 2FA, and Arkose URLs
- Prevent premature window closure on Android Play Integrity attestation failures
- Add pre-engine first-launch desktop sign-in prompt before creating Vulkan game window
- Ensure WebView helper receives cookie sync handshake on startup to begin page load
- Support dynamic session promotion and credential dispatch for guest runtime VM
- Add unit tests for generic challenge routing and guest credential promotion
Co-authored-by: glook9001 <310372091+glook9001@users.noreply.github.com>
Direnv allows the project environment to be automatically applied when
the project is cd'd into.
Other notable changes:
- .gitignore: add .cache and .direnv generated by clangd and direnv respectively
* Stop rehashing and recopying the payload during install
A first install prepares a ~635 MiB payload, then hashes the whole tree five
times and byte-copies it once more into the store. Four of those five passes
re-derive digests the same process just computed, under the same lock.
PreparePayloadFromArchives hashed the tree to write roblox_payload.json and
then immediately verified those hashes against the file it had just written.
PayloadStore::Stage hashed the same workspace again before copying it, and
Promote hashed the published directory a third time.
The guarantee that matters is unchanged: the bytes that land in the immutable
store are still hashed in full after the copy. Stage now records which payload
it verified, so promotion skips the repeat only for a payload this instance
staged; an operator-supplied ID, a directory left by an earlier run, or a fresh
process still gets the complete pass.
CopyTree now reflinks (FICLONE) or uses copy_file_range before falling back to
an ordinary copy, so on btrfs and xfs the staging copy costs no data movement
and no extra space. A hard link would be wrong here: CopyTree makes the result
read-only while the source stays writable.
File hashing moves from the portable foundation::Sha256 to OpenSSL, which
reaches the CPU's SHA extensions, and the asset tree hashes its files across
cores. Measured on this machine: 182 MB/s to 1518 MB/s, and the 1835-file asset
tree of Roblox 2.734.917 reproduces its recorded sha256_tree exactly.
MOCKTAIL_UPDATE_TRACE=1 now reports the elapsed time of each install stage, so
the breakdown is visible instead of inferred.
* Hide the system cursor as soon as the window regains focus
Roblox draws its own cursor while it owns the pointer, so Mocktail hides the
system one. Losing focus reveals it again, which is right, but regaining focus
only set the focused flag and left the re-apply to the next Pump().
Pump() runs at the top of PumpEvents(), before SDL_PollEvent delivers
SDL_EVENT_WINDOW_FOCUS_GAINED, so the desktop arrow stayed on screen for at
least one whole frame after every alt-tab back into the game, and longer
whenever the frame was long. OnFocusGained now pumps the pointer state in the
same call, mirroring OnFocusLost and OnRightButton, and its failure is reported
like the focus-loss path.
An active text field still keeps the system cursor, focused or not.
* Cover promotion of a payload the store did not stage
Stage() now lets promotion skip the repeat hash for a payload it just wrote,
so the boundary of that shortcut needs a test. Nothing covered it: the existing
corruption test rewrites roblox_payload.json, which the metadata check catches
on its own, and payload_store_test.sh exercises the shell store rather than
this class.
The new test corrupts only the content bytes, leaving the metadata describing
the original payload, then promotes from a second PayloadStore instance. It
fails if the shortcut is ever allowed to apply to a payload this instance did
not stage.
* Restore the full hash at the promotion gate
Skipping the rehash for a payload the same instance staged was wrong. The
readiness canaries run between Stage() and Promote(): they spawn the real
client against the staged directory, for up to 150 seconds, twice for a
probation candidate. Trusting the earlier hash across that window drops a
deliberate control, and payload_store_test.sh already spelled out why it is
there.
Promote() and PromoteProbation() call VerifyPreparedPayload() again, as before.
The instance no longer tracks what it staged.
The two tests now tamper with a staged payload from the instance that staged
it, which is the case that was missing: content bytes only, metadata left
describing the original payload, so nothing short of hashing the contents can
detect it. One covers the asset tree, the other libroblox.so.
Promotion still costs a complete pass over ~635 MiB, but OpenSSL and the
parallel asset hash make it 3.9 s -> 0.55 s. Stage is 9.5 s -> 0.93 s. A traced
install reports promote at 656 ms.
- src/legacy/legacy_runtime.cc: only use symbols from stubs that own them
- src/legacy/legacy_runtime.cc: always use the android vulkan loader adapter to load vulkan
- src/update/readiness_canary.cc: expose env variables that affect the loading of dynamic libraries to the canary sandbox
- {flake.{nix, lock}, packaging/nix/package.nix}: initialize flake and nix package
- packaging/nix/package.nix: derive version from CMakeLists.txt, similar to the mocktail-git AUR package
Two kinds of drift only surface when a user hits them. The compatibility
catalog falls behind what the provider publishes, so new installs land on
a version Roblox has since retired; and the preferred supported profile
loses its pinned bootstrap source, so the pinned bootstrap path cannot
produce the version the updater falls back to.
Add a checker that reports both, and run the existing script unit tests in
CI, where they were never executed.
Run against this tree the checker already reports real drift: the
preferred profile 2.727.1199 has no pinned bootstrap source. Pinning one
means downloading and hashing the archive, and a newer profile also needs
a graphics canary on real hardware, so this reports the condition rather
than pretending to fix it.
The checker stays a plain script that anyone can run on demand; no
scheduled workflow is added.
The coordinator constructed ApkPureProvider directly in three places and
rejected every other configuration with "native updater supports only the
direct APKPure provider". A single unreachable host therefore failed the
whole update with no way to try anything else, and the reported error named
neither the provider nor the host: on 2026-08-18 api.pureapk.com answered
503 to every request, including metadata for unrelated packages, and a
first launch could only say "Roblox update preflight failed".
Introduce an ApkProvider interface and a ProviderChain that consults
providers in order, returns the first answer, and reports every failure
with the provider that produced it when none answer. Each provider
downloads into its own directory, so a failed attempt cannot block the
next. Adding a source is now one line in BuildProviderChain instead of a
change spread across the coordinator.
The chain currently holds APKPure alone. The repository already ships a
pinned Uptodown provider as a build script, but it no longer works against
Uptodown's current pages, so wiring it in would add a second way to fail
rather than a fallback; that is reported separately.
Payload receipts now record which provider produced an archive, and
updates.source accepts auto alongside apk-pure so existing configuration
files keep working.
A first launch that cannot reach the APK provider only ever showed
"Mocktail could not update or verify the Roblox installation.", while the
actual cause stayed in the session log. For a user that is
indistinguishable between a provider outage, a rejected signature, and a
full disk.
Name the host in HTTP transfer failures, so "HTTPS request returned status
503" becomes "api.pureapk.com request returned status 503", and relay the
updater child's stderr through the launcher: every line still reaches the
session log, and the last failure it reported is now attached to the
preflight result. The failure dialog shows that reason and the session log
directory instead of the generic sentence.