Skip to content

add Support for XR anchors #442

Draft
cefoot wants to merge 6 commits into
De-Panther:masterfrom
cefoot:xr-anchor
Draft

add Support for XR anchors #442
cefoot wants to merge 6 commits into
De-Panther:masterfrom
cefoot:xr-anchor

Conversation

@cefoot
Copy link
Copy Markdown
Contributor

@cefoot cefoot commented Apr 30, 2026

Summary by CodeRabbit

  • New Features
    • WebXR Anchors for immersive AR: create anchors from viewer hit-tests, next-hit-test, or explicit poses and receive per-frame pose updates.
    • New optional "anchors" feature flag in WebXR AR settings to enable anchor functionality.
    • Anchor lifecycle controls and events: create, delete (single or all), and receive create/delete/update notifications.
    • XR Anchors sample with prefabs and example scripts demonstrating usage.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

Adds WebXR Anchors: WebGL JS bindings and heap-backed anchor state, managed C# anchor data/events/subsystem APIs and WebXRManager surface methods, a new settings flag, package samples (prefabs, example, asmdef), and a CHANGELOG entry documenting usage.

Changes

Cohort / File(s) Summary
Native JS / WebGL bridge
Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib, Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre
Add Unity-callable anchor functions and Module.WebXR APIs; implement fixed anchor heap layout, init/clear, request queue, per-frame updates, and native→C# callbacks for anchor create/delete.
C# Data & Settings
Packages/webxr/Runtime/Scripts/WebXRControllerData.cs, Packages/webxr/Runtime/XRPlugin/WebXRSettings.cs
Add WebXRAnchorData type and new ExtraFeatureTypes.anchors = 4 flag.
Subsystem & Manager APIs
Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs, Packages/webxr/Runtime/Scripts/WebXRManager.cs, Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs
Introduce anchor delegates/events (created, deleted, update), managed per-frame anchor updates, native interop externs and callbacks, public create/delete APIs on subsystem, and WebXRManager surface methods; make WebXRManager partial.
Sample Implementation & Assets
Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs, Packages/webxr/Samples~/Anchors/Prefabs/*, Packages/webxr/Samples~/Anchors/.../*.meta, Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef*
Add XR Anchors sample: MonoBehaviour handling anchor lifecycle, prefab instantiation per-anchor, placement helpers, and add prefab/material/assembly metadata.
Package manifest & docs
Packages/webxr/package.json, Packages/webxr/CHANGELOG.md
Register "XR Anchors Sample" in package.json and document Unreleased anchor support and usage guidance in CHANGELOG.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Manager as WebXRManager
    participant Subsystem as WebXRSubsystem
    participant JS as Module.WebXR
    participant Browser as Browser/WebXR
    participant App as Application

    User->>Manager: CreateAnchorFromViewerHitTest()
    Manager->>Subsystem: CreateAnchorFromViewerHitTest()
    Subsystem->>JS: createAnchorFromViewerHitTest()
    JS->>Browser: request anchor (viewer hit-test)
    Browser-->>JS: anchor created (id, pose)
    JS->>Subsystem: OnAnchorCreated(anchorId)
    Subsystem->>Manager: OnAnchorCreated event
    Manager->>App: OnAnchorCreated

    Note over Browser,JS: per-frame anchor pose updates
    Browser->>JS: anchor pose update
    JS->>Subsystem: update anchorsDataArray
    Subsystem->>Manager: OnAnchorUpdate(WebXRAnchorData)
    Manager->>App: update GameObject transform

    User->>Manager: DeleteAnchor(id)
    Manager->>Subsystem: DeleteAnchor(id)
    Subsystem->>JS: deleteAnchor(id)
    JS->>Browser: delete anchor
    Browser-->>JS: anchor deleted
    JS->>Subsystem: OnAnchorDeleted(id)
    Subsystem->>Manager: OnAnchorDeleted
    Manager->>App: destroy GameObject
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • De-Panther

Poem

🐰 I nibble code where anchors grow,

Hit-tests plant, and transforms show,
Frames tick true and prefabs spring,
Small seeds of AR take wing—hop, sing!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.12% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'add Support for XR anchors' directly and clearly summarizes the main change in the pull request, which comprehensively adds WebXR anchor functionality across multiple layers (JavaScript bindings, C# APIs, settings, samples, and documentation).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
Packages/webxr/Runtime/XRPlugin/WebXRSettings.cs (1)

24-25: ⚡ Quick win

Gate anchor operations when anchors feature is not enabled.

Now that ExtraFeatureTypes.anchors exists, runtime behavior should align with it. Anchor methods in Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs (Lines 776-820) currently execute without checking whether anchors were requested, which can create confusing no-op/failure behavior depending on session capabilities.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Packages/webxr/Runtime/XRPlugin/WebXRSettings.cs` around lines 24 - 25,
Anchor-related methods in WebXRSubsystem (the anchor add/remove/update APIs)
must be gated by the new ExtraFeatureTypes.anchors flag; update the anchor
methods (e.g., the methods that currently run between the previous block
~776-820 — TryAddAnchor/TryRemoveAnchor or similarly named anchor ops) to first
check whether anchors were requested/enabled and immediately return the
appropriate failure/no-op if not. Concretely, locate the anchor operation
methods in WebXRSubsystem, add a guard that checks the requested/session
features for ExtraFeatureTypes.anchors (or the subsystem’s
requestedFeatures/sessionFeatures set) and short-circuit the operation with a
clear false/early-return when anchors are not enabled, so anchors never run
unless ExtraFeatureTypes.anchors was requested.
Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs (1)

235-249: 💤 Low value

Consider initializing the anchors array upfront.

Currently, WebXRAnchorData objects are lazily created on every UpdateAnchors call. Since the array size is fixed at 32, initializing all slots once (e.g., in InternalStart) would avoid repeated null checks.

♻️ Proposed initialization

Add to InternalStart():

   private void InternalStart()
   {
+    for (int i = 0; i < MaxWebXRAnchors; i++)
+    {
+      anchors[i] = new WebXRAnchorData { frame = -1, id = -1 };
+    }
 `#if` UNITY_WEBGL
     Native.SetWebXREvents(OnStartAR, OnStartVR, UpdateVisibilityState, OnEndXR, OnXRCapabilities, OnInputProfiles);

Then simplify UpdateAnchors:

   private void UpdateAnchors()
   {
     for (int i = 0; i < MaxWebXRAnchors; i++)
     {
-      if (anchors[i] == null)
-      {
-        anchors[i] = new WebXRAnchorData { frame = -1, id = -1 };
-      }
-
       if (GetAnchorFromAnchorsArray(i, anchors[i]))
       {
         OnAnchorUpdate?.Invoke(anchors[i]);
       }
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs` around lines 235 - 249,
The anchors array is lazily populated every UpdateAnchors call causing repeated
null checks; in InternalStart() preallocate and initialize
anchors[0..MaxWebXRAnchors-1] with new WebXRAnchorData { frame = -1, id = -1 }
so UpdateAnchors can assume anchors[i] is non-null; keep using
GetAnchorFromAnchorsArray(i, anchors[i]) and OnAnchorUpdate?.Invoke(anchors[i])
but remove the per-iteration null creation logic to rely on the preinitialized
anchors array.
Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs (1)

56-68: 💤 Low value

Consider adding a null check for anchoredPrefab.

If anchoredPrefab is not assigned in the Inspector, Instantiate(anchoredPrefab) will throw a NullReferenceException.

🛡️ Proposed defensive check
     private void OnAnchorUpdate(WebXRAnchorData anchor)
     {
         if (!anchor.tracked)
             return;

         if (!instances.TryGetValue(anchor.id, out Transform instance))
         {
+            if (anchoredPrefab == null)
+            {
+                Debug.LogWarning("WebXRAnchorExample: anchoredPrefab is not assigned.");
+                return;
+            }
             instance = Instantiate(anchoredPrefab);
             instances.Add(anchor.id, instance);
         }

         instance.SetPositionAndRotation(anchor.position, anchor.rotation);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Packages/webxr/Samples`~/Anchors/Scripts/WebXRAnchorExample.cs around lines
56 - 68, OnAnchorUpdate can throw a NullReferenceException if anchoredPrefab is
not set; add a defensive null check for the anchoredPrefab before calling
Instantiate(anchoredPrefab) (e.g., if anchoredPrefab is null, log a warning via
Debug.LogWarning and return), ensuring you still respect the tracked check and
avoid adding to instances; update the logic around instances.TryGetValue /
Instantiate in OnAnchorUpdate to early-exit when anchoredPrefab is null so
Instantiate is never called with a null prefab.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Packages/webxr/Runtime/XRPlugin/WebXRSettings.cs`:
- Around line 24-25: Anchor-related methods in WebXRSubsystem (the anchor
add/remove/update APIs) must be gated by the new ExtraFeatureTypes.anchors flag;
update the anchor methods (e.g., the methods that currently run between the
previous block ~776-820 — TryAddAnchor/TryRemoveAnchor or similarly named anchor
ops) to first check whether anchors were requested/enabled and immediately
return the appropriate failure/no-op if not. Concretely, locate the anchor
operation methods in WebXRSubsystem, add a guard that checks the
requested/session features for ExtraFeatureTypes.anchors (or the subsystem’s
requestedFeatures/sessionFeatures set) and short-circuit the operation with a
clear false/early-return when anchors are not enabled, so anchors never run
unless ExtraFeatureTypes.anchors was requested.

In `@Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs`:
- Around line 235-249: The anchors array is lazily populated every UpdateAnchors
call causing repeated null checks; in InternalStart() preallocate and initialize
anchors[0..MaxWebXRAnchors-1] with new WebXRAnchorData { frame = -1, id = -1 }
so UpdateAnchors can assume anchors[i] is non-null; keep using
GetAnchorFromAnchorsArray(i, anchors[i]) and OnAnchorUpdate?.Invoke(anchors[i])
but remove the per-iteration null creation logic to rely on the preinitialized
anchors array.

In `@Packages/webxr/Samples`~/Anchors/Scripts/WebXRAnchorExample.cs:
- Around line 56-68: OnAnchorUpdate can throw a NullReferenceException if
anchoredPrefab is not set; add a defensive null check for the anchoredPrefab
before calling Instantiate(anchoredPrefab) (e.g., if anchoredPrefab is null, log
a warning via Debug.LogWarning and return), ensuring you still respect the
tracked check and avoid adding to instances; update the logic around
instances.TryGetValue / Instantiate in OnAnchorUpdate to early-exit when
anchoredPrefab is null so Instantiate is never called with a null prefab.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 917fe127-f323-42ce-b428-f5e7ef1cc3e0

📥 Commits

Reviewing files that changed from the base of the PR and between 28d3212 and 94db827.

⛔ Files ignored due to path filters (6)
  • Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab is excluded by !**/*.prefab
  • Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat is excluded by !**/*.mat
  • Packages/webxr/Samples~/Anchors/Prefabs/Green.mat is excluded by !**/*.mat
  • Packages/webxr/Samples~/Anchors/Prefabs/Red.mat is excluded by !**/*.mat
  • Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat is excluded by !**/*.mat
  • Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab is excluded by !**/*.prefab
📒 Files selected for processing (21)
  • Packages/webxr/CHANGELOG.md
  • Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib
  • Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre
  • Packages/webxr/Runtime/Scripts/WebXRControllerData.cs
  • Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs
  • Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs.meta
  • Packages/webxr/Runtime/Scripts/WebXRManager.cs
  • Packages/webxr/Runtime/XRPlugin/WebXRSettings.cs
  • Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs
  • Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab.meta
  • Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat.meta
  • Packages/webxr/Samples~/Anchors/Prefabs/Green.mat.meta
  • Packages/webxr/Samples~/Anchors/Prefabs/Red.mat.meta
  • Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat.meta
  • Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab.meta
  • Packages/webxr/Samples~/Anchors/Scripts.meta
  • Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs
  • Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs.meta
  • Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef
  • Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef.meta
  • Packages/webxr/package.json

@cefoot cefoot changed the title Xr anchor - branch rebase add Support for XR anchors Apr 30, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre`:
- Around line 1263-1306: Async createAnchor promises (from
this.lastViewerHitTestResult.createAnchor and frame.createAnchor) can resolve
after onEndSession or a new session starts and incorrectly call
thisXRMananger.registerAnchor; fix by capturing a current session token or
reference before calling createAnchor (e.g., const activeSession = session or a
sessionCounter snapshot) and, in each .then/.catch, verify the captured token
still matches the live session (or that session is not ended) before calling
thisXRMananger.registerAnchor; if it doesn't match, drop the result and
optionally clean up the late anchor. Apply the same guard to all three async
creation branches that call createAnchor.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2e994e1d-3911-4a17-85f3-c1db7024db59

📥 Commits

Reviewing files that changed from the base of the PR and between 94db827 and 6401623.

📒 Files selected for processing (2)
  • Packages/webxr/CHANGELOG.md
  • Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre
✅ Files skipped from review due to trivial changes (1)
  • Packages/webxr/CHANGELOG.md

Comment on lines +1263 to +1306
this.lastViewerHitTestResult.createAnchor().then(function(anchor) {
thisXRMananger.registerAnchor(anchor);
}).catch(function(error) {
console.warn('Could not create WebXR anchor from hit-test result:', error);
});
} else if (request.type == 'wait-viewer-hit-test') {
if (!this.lastViewerHitTestResult) {
console.info('Cannot create WebXR anchor now will try again.');
this.pendingAnchorRequests.push(request);
continue;
}

if (!this.lastViewerHitTestResult.createAnchor) {
console.warn('Cannot create WebXR anchor: XRHitTestResult.createAnchor is unavailable.');
continue;
}

this.lastViewerHitTestResult.createAnchor().then(function(anchor) {
thisXRMananger.registerAnchor(anchor);
}).catch(function(error) {
console.warn('Could not create WebXR anchor from hit-test result:', error);
});
} else if (request.type == 'pose') {
if (!frame.createAnchor || !window.XRRigidTransform) {
console.warn('Cannot create WebXR anchor: XRFrame.createAnchor or XRRigidTransform is unavailable.');
continue;
}

var transform = new XRRigidTransform(
{
x: request.position.x,
y: request.position.y,
z: -request.position.z
},
{
x: -request.rotation.x,
y: -request.rotation.y,
z: request.rotation.z,
w: request.rotation.w
}
);

frame.createAnchor(transform, session.refSpace).then(function(anchor) {
thisXRMananger.registerAnchor(anchor);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard async anchor creation against session teardown.

These createAnchor(...).then(...) callbacks can resolve after onEndSession() runs or after a new XR session starts. In that case registerAnchor() will recreate stale entries, fire onAnchorCreated, and leak anchors into the next session. Capture the current session (or a monotonically increasing session token) and ignore/delete late results before registering them.

💡 One way to harden the callbacks
-            this.lastViewerHitTestResult.createAnchor().then(function(anchor) {
-              thisXRMananger.registerAnchor(anchor);
+            this.lastViewerHitTestResult.createAnchor().then(function(anchor) {
+              if (thisXRMananger.xrSession !== session || !session.isInSession) {
+                if (anchor && anchor.delete) {
+                  anchor.delete();
+                }
+                return;
+              }
+              thisXRMananger.registerAnchor(anchor);
             }).catch(function(error) {
               console.warn('Could not create WebXR anchor from hit-test result:', error);
             });

Apply the same guard to the other two async creation branches in this block.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre` around lines 1263 - 1306,
Async createAnchor promises (from this.lastViewerHitTestResult.createAnchor and
frame.createAnchor) can resolve after onEndSession or a new session starts and
incorrectly call thisXRMananger.registerAnchor; fix by capturing a current
session token or reference before calling createAnchor (e.g., const
activeSession = session or a sessionCounter snapshot) and, in each .then/.catch,
verify the captured token still matches the live session (or that session is not
ended) before calling thisXRMananger.registerAnchor; if it doesn't match, drop
the result and optionally clean up the late anchor. Apply the same guard to all
three async creation branches that call createAnchor.

Copy link
Copy Markdown
Owner

@De-Panther De-Panther left a comment

Choose a reason for hiding this comment

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

Sorry, but here I'm going to disappoint you.
My plan for anchors is implementing XRAnchorSubsystem for ARFoundation.
That way existing experiences that use the OpenXR package with anchors won't need changes. (Or will need minimum changes)

You can look at WebXRLoader on how to init and manage some XR Subsystems.

I think it is possible with your current JS changes.

In the future I would also like to switch to ARFoundation XRRaycastSubsystem for the hit-test.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs (1)

580-585: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Drop anchor callbacks once the XR session is no longer active.

Anchor creation/deletion completes via callbacks, but OnEndXR() only flips the state. A completion that arrives after teardown will still raise OnAnchorCreated/OnAnchorDeleted, which leaks anchor lifecycle events from a dead AR session.

Suggested fix
     [MonoPInvokeCallback(typeof(AnchorCreatedEvent))]
     public static void OnAnchorCreatedInternal(int anchorId)
     {
+      if (Instance == null || Instance.xrState != WebXRState.AR)
+      {
+        return;
+      }
       OnAnchorCreated?.Invoke(anchorId);
     }
     
     [MonoPInvokeCallback(typeof(AnchorDeletedEvent))]
     public static void OnAnchorDeletedInternal(int anchorId)
     {
+      if (Instance == null || Instance.xrState != WebXRState.AR)
+      {
+        return;
+      }
       OnAnchorDeleted?.Invoke(anchorId);
     }

Also applies to: 587-597

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs` around lines 580 - 585,
OnEndXR currently only flips state, but anchor lifecycle callbacks
(OnAnchorCreated/OnAnchorDeleted) can still fire after teardown and leak events;
update OnEndXR to unregister or clear any anchor-related callbacks/delegates
(e.g., remove listeners for OnAnchorCreated and OnAnchorDeleted) or set a
session-ended flag on Instance that the anchor handlers check and return early,
and ensure the same change is applied to the similar teardown block around the
587-597 region so no anchor callbacks are processed after
webXRLoader.EndEssentialSubsystems() and Instance.setXrState(...) are called.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs`:
- Around line 237-239: The guard currently only returns when settings exists and
the anchors flag is off, which allows code to proceed when
WebXRSettings.GetSettings() is null; change each occurrence to explicitly
require the anchors feature by returning early when settings is null OR the
anchors flag is not set (i.e. replace conditions like "if (settings != null &&
!settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors))"
with "if (settings == null ||
!settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors))"
so the polling and the anchor create/delete API paths never run unless the
anchors feature is explicitly present).

---

Outside diff comments:
In `@Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs`:
- Around line 580-585: OnEndXR currently only flips state, but anchor lifecycle
callbacks (OnAnchorCreated/OnAnchorDeleted) can still fire after teardown and
leak events; update OnEndXR to unregister or clear any anchor-related
callbacks/delegates (e.g., remove listeners for OnAnchorCreated and
OnAnchorDeleted) or set a session-ended flag on Instance that the anchor
handlers check and return early, and ensure the same change is applied to the
similar teardown block around the 587-597 region so no anchor callbacks are
processed after webXRLoader.EndEssentialSubsystems() and
Instance.setXrState(...) are called.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ff29e1c-cf25-4695-a687-634e880cf0e0

📥 Commits

Reviewing files that changed from the base of the PR and between 6401623 and 0e3f4e1.

📒 Files selected for processing (1)
  • Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs

Comment thread Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs
@cefoot
Copy link
Copy Markdown
Contributor Author

cefoot commented Apr 30, 2026

Thanks, that makes sense. I agree that XRAnchorSubsystem is the better architecture, especially for ARFoundation compatibility.

The current implementation was created to unblock a customer project, and for that use case the anchors are working well already. Because of that, I cannot invest the time for a proper XRAnchorSubsystem refactor right away, although I would really like to do it.

I think the current JS anchor backend could still be useful as a starting point for the subsystem implementation. Would you prefer me to close this PR for now, or keep it open as a prototype/reference until I or someone else can pick up the subsystem approach?

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs`:
- Around line 833-845: In DeleteAnchor, validate the public API parameter before
calling into native: check that the anchorId parameter is non-negative (e.g.,
anchorId >= 0) and bail out (or log and return) if it is negative, then only
call Native.DeleteAnchor(anchorId); update the guard in the DeleteAnchor method
(and keep the existing UNITY_WEBGL / AnchorsFeatureEnabled checks) to prevent
forwarding invalid anchor IDs to Native.DeleteAnchor.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 073b3077-478b-4989-8c92-98e5005f3bb8

📥 Commits

Reviewing files that changed from the base of the PR and between 0e3f4e1 and 58ea328.

📒 Files selected for processing (1)
  • Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs

Comment on lines +833 to +845
public void DeleteAnchor(int anchorId)
{
#if UNITY_WEBGL
if (xrState != WebXRState.AR)
{
return;
}
if (!AnchorsFeatureEnabled())
{
return;
}
Native.DeleteAnchor(anchorId);
#endif
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate anchorId before native delete call.

Line 844 currently forwards any integer to native code. Since this is a public API, add a non-negative guard to prevent invalid calls from misuse paths.

Suggested patch
 public void DeleteAnchor(int anchorId)
 {
 `#if` UNITY_WEBGL
   if (xrState != WebXRState.AR)
   {
     return;
   }
   if (!AnchorsFeatureEnabled())
   {
     return;
   }
+  if (anchorId < 0)
+  {
+    return;
+  }
   Native.DeleteAnchor(anchorId);
 `#endif`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void DeleteAnchor(int anchorId)
{
#if UNITY_WEBGL
if (xrState != WebXRState.AR)
{
return;
}
if (!AnchorsFeatureEnabled())
{
return;
}
Native.DeleteAnchor(anchorId);
#endif
public void DeleteAnchor(int anchorId)
{
`#if` UNITY_WEBGL
if (xrState != WebXRState.AR)
{
return;
}
if (!AnchorsFeatureEnabled())
{
return;
}
if (anchorId < 0)
{
return;
}
Native.DeleteAnchor(anchorId);
`#endif`
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs` around lines 833 - 845, In
DeleteAnchor, validate the public API parameter before calling into native:
check that the anchorId parameter is non-negative (e.g., anchorId >= 0) and bail
out (or log and return) if it is negative, then only call
Native.DeleteAnchor(anchorId); update the guard in the DeleteAnchor method (and
keep the existing UNITY_WEBGL / AnchorsFeatureEnabled checks) to prevent
forwarding invalid anchor IDs to Native.DeleteAnchor.

@De-Panther
Copy link
Copy Markdown
Owner

Yes, the JS code is a start.
And I don't think the XR Subsystem is too far from your implementation.
So this PR is a start. But I don't want to merge it, as it'll become the standard like the current un-finished hit-test.

@cefoot cefoot marked this pull request as draft April 30, 2026 07:05
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