Skip to content

Scenes in the 0 FPS partition bucket freeze permanently on Apple silicon (int.MaxValue interval, never resumes) #10059

Description

@robtfm

Summary

On Apple-silicon macOS, any scene whose partition bucket reaches the 0 FPS tier stops ticking permanently. It never resumes when the player comes back into range; only /reload (a fresh SceneFacade) revives it. The scene's own tick-driven logic, network sync and console output all stop, while renderer-driven things (Tweens, avatars, comms) keep working, so the scene looks alive.

The freeze support in SceneFacade assumes that (int)(1000f / 0) yields int.MinValue. That is the x86/x64 result. On arm64 the float→int conversion saturates to int.MaxValue, so the freeze branch is skipped and the update loop sleeps for ~24 days instead.

Environment

  • Explorer v0.178.0-alpha (also reproduced on 0.177) via DecentralandLauncherLight, macOS, Apple M4 Max, IL2CPP universal binary running arm64.
  • Code references against main @ a9ff4354c.

Steps to reproduce

Needs a world: worlds instantiate all their scenes at spawn regardless of distance, so a far scene is running at FPS 0. (Genesis scenes are not loaded until the player is closer, so they don't start in the 0 FPS bucket.)

  1. Enter a world at a parcel ~10 parcels away from a scene that animates something from a system (per-tick code) and something else with a Tween. Test scene: robtfm.dcl.eth, spawn at 50,40, the scene is at 50,50 (green cube rotated from engine.addSystem, blue cube rotated by a Tween, 5 s console heartbeat).
  2. Walk to the scene.

Alternative, same world: spawn inside the scene (both cubes spin), walk 6+ parcels away, come back.

Expected

Both cubes spin, the heartbeat logs every 5 s, the scene's state sync catches up.

Actual

Only the Tween cube spins. The system-driven cube is frozen in its first-tick pose, the heartbeat never prints, and (for an authoritative-multiplayer scene) the client never syncs state. /reload fixes it immediately. Spawning at 52,48 (inside the 20 FPS bucket) or directly in the scene works.

Root cause

  • RealmPartitionSettingsAsset.fpsBuckets = { 30, 20, 10, 5, 0 } over PartitionSettingsAsset.distanceBuckets (16, 32, 48, 64, 80 … m in the shipped asset): bucket ≥ 4 ⇒ target FPS 0. Worlds instantiate every scene at spawn, so a far world scene starts at FPS 0; a loaded scene the player walks away from drops into it as well.
  • SceneFacade.SetTargetFPS(int fps): intervalMS = (int)(1000f / fps); — for 0 this casts +Infinity to int.
  • SceneFacade.IdleWhileRunningAsync:
    // Support scene freeze (0 FPS, int.MinValue)
    while (intervalMS < 0) {await DCLTask.Delay(10, ct); }
    The guard only triggers for a negative interval. On x86/x64 the out-of-range conversion produces int.MinValue (0x80000000) and this works. On arm64 the conversion saturates to int.MaxValue, so the loop falls through to
    int sleepMS = Math.Max(intervalMS - (int)stopWatch.ElapsedMilliseconds, 0);
    await DCLTask.Delay(sleepMS, ct);
    with sleepMS ≈ int.MaxValue (~24.8 days). A later SetTargetFPS(30) from ControlSceneUpdateLoopSystem.ChangeSceneFps only rewrites the field; nothing wakes the pending delay.

Net effect: the scene runs main() and exactly one update, then sleeps until disposal.

Suggested fix

Represent the freeze explicitly instead of relying on the cast, e.g. intervalMS = fps <= 0 ? -1 : (int)(1000f / fps) (or a dedicated flag) so IdleWhileRunningAsync idles on every platform. Note also that on x86 the same subtraction intervalMS - elapsed would wrap when the guard is bypassed, so a guard on the sign of the value is fragile in both directions.

Notes

  • Production ReportsHandlingSettings keeps only severity 4/1 for SCENE_LOADING, so nothing about this reaches Player.log; the [ALWAYS] lifecycle lines show the scene "started" once and never "disposed" until /reload.
  • Discovered while testing local-scene-development / authoritative-server presence: the symptom there is "client never receives server CRDT after walking into the scene", which is the frozen SDK runtime, not comms.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    1-highHigh severity issuesMac OnlyIssue only happens on Mac buildsbugSomething isn't workingsdk

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions