Skip to content

Queue NativeXr EndFrame on its own frame's AfterRender tick - #1957

Merged
CedricGuillemet merged 1 commit into
BabylonJS:masterfrom
CedricGuillemet:nativexr-endframe-fix
Oct 22, 2026
Merged

CedricGuillemet merged 1 commit into
BabylonJS:masterfrom
CedricGuillemet:nativexr-endframe-fix

Conversation

@CedricGuillemet

@CedricGuillemet CedricGuillemet commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

ScheduleFrame chained EndFrame as an AfterRenderScheduler continuation of the JS frame task. arcana releases the task's FrameCompletionScope before running its continuations, so the render thread could drain AfterRender before EndFrame was queued. EndFrame(N) then ran a frame late, consumed frame N+1 and left EndFrame(N+1) with a null Frame:

NativeXrImpl.cpp:410 assert(m_sessionState->Frame != nullptr)

Queue EndFrame on AfterRender from the FrameStart task instead. The frame completion scope acquired in that task still keeps the AfterRender tick from running until the JS callbacks are done, and FrameTask still completes only after EndFrame, which EndSessionAsync relies on.

NativeXrFrameLifecycle.EndFrameAlwaysHasActiveFrame aborted 10/10 runs within 2.4 s before this change and passes 60 s runs with it.

ScheduleFrame chained EndFrame as an AfterRenderScheduler continuation of the
JS frame task. arcana releases the task's FrameCompletionScope before running
its continuations, so the render thread could drain AfterRender before
EndFrame was queued. EndFrame(N) then ran a frame late, consumed frame N+1
and left EndFrame(N+1) with a null Frame:

  NativeXrImpl.cpp:410 assert(m_sessionState->Frame != nullptr)

Queue EndFrame on AfterRender from the FrameStart task instead. The frame
completion scope acquired in that task still keeps the AfterRender tick from
running until the JS callbacks are done, and FrameTask still completes only
after EndFrame, which EndSessionAsync relies on.

NativeXrFrameLifecycle.EndFrameAlwaysHasActiveFrame aborted 10/10 runs within
2.4 s before this change and passes 60 s runs with it.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 37730cd1-0d50-4d5f-b57b-f378fdfe9c8e

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused scheduling change addresses the race while preserving callback synchronization and shutdown completion guarantees.

No open issues 🎉

What changed in this PR

Fixes NativeXr frame ordering so EndFrame runs on its own frame’s AfterRender tick.

Changes:

  • Queues EndFrame directly from the FrameStart task instead of a JavaScript-task continuation.
  • Preserves frame-completion synchronization and session-shutdown ordering.
File Description
Plugins/​NativeXr/​Source/​NativeXrImpl.cpp Schedules EndFrame earlier and documents the timing race.

Review effort: Balanced

@CedricGuillemet
CedricGuillemet merged commit 37d30f5 into BabylonJS:master Oct 22, 2026
27 checks passed
CedricGuillemet added a commit to CedricGuillemet/BabylonReactNative-1 that referenced this pull request Oct 22, 2026
Use upstream BabylonNative commit 37d30f54649543290ad25c6c3626c247c1372773, the merged fix from BabylonJS/BabylonNative#1957.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 06586ce7-bc10-46c1-83b5-3334fc60600d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants