Implement Android OpenXR mesh manager with incremental streaming [chromium/src : main]

0 views
Skip to first unread message

Brandon Jones (Gerrit)

unread,
Mar 3, 2026, 2:47:04 PM (12 days ago) Mar 3
to Adam Ren (xWF), Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
Attention needed from Adam Ren (xWF) and Alexander Cooper

Brandon Jones added 6 comments

File device/vr/openxr/android/openxr_mesh_manager_android.cc
Line 37, Patchset 7 (Latest): return base::Uuid::ParseLowercase(base::StringPrintf(
Brandon Jones . unresolved

The way that these UUIDs are handled throughout feels expensive. Every use of this method that I can find (see: [1], [2], [3]) is converting to a string, parsing as a base::Uuid, and then immediately converting to string again. Perhaps we can skip some parsing and just have this return the string directly?

Line 97, Patchset 7 (Latest): std::string uuid_str = XrUuidToBaseUuid(uuid).AsLowercaseString();
Brandon Jones . unresolved

[1]

Line 196, Patchset 7 (Latest): start_idx = mesh_processing_offset_;
Brandon Jones . unresolved

Doesn't this have the potential to skip submeshes if a submesh that has already been processed is not present in a subsequent frame?

Line 210, Patchset 7 (Latest): std::string uuid_str = XrUuidToBaseUuid(state.submeshId).AsLowercaseString();
Brandon Jones . unresolved

[2]

Line 241, Patchset 7 (Latest): submesh_poses.push_back(state.submeshPoseInBaseSpace);
Brandon Jones . unresolved

Related to a previous comment I've given on a CL further down the stack: It's not clear to me from reading https://developer.android.com/develop/xr/openxr/extensions/XR_ANDROID_scene_meshing#XrSceneMeshSnapshotCreationResultANDROID if the `lastUpdatedTime` describes the pose _and_ the mesh data, or just the mesh data itself. I'm somewhat inclined to believe that, given that pose is reported along with the update time, that the pose might update with every query regardless of if `lastUpdatedTime` has changed. Would love to get some confirmation on that.

If the pose _can_ update independent of the `lastUpdateTime` then the current code would potentially cause the tracking of the meshes to drift when the mesh itself is unchanged.

Line 249, Patchset 7 (Latest): current_frame_uuids.insert(XrUuidToBaseUuid(state.submeshId).AsLowercaseString());
Brandon Jones . unresolved

[3]

Open in Gerrit

Related details

Attention is currently required from:
  • Adam Ren (xWF)
  • Alexander Cooper
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
Gerrit-Change-Number: 7579917
Gerrit-PatchSet: 7
Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-CC: David Li <dav...@google.com>
Gerrit-Attention: Adam Ren (xWF) <ada...@google.com>
Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
Gerrit-Comment-Date: Tue, 03 Mar 2026 19:46:55 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Adam Ren (xWF) (Gerrit)

unread,
Mar 3, 2026, 11:08:02 PM (12 days ago) Mar 3
to Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
Attention needed from Alexander Cooper, Brandon Jones, Nihav Jain and Tomas Oresky

Adam Ren (xWF) added 6 comments

File device/vr/openxr/android/openxr_mesh_manager_android.cc
Line 37, Patchset 7 (Latest): return base::Uuid::ParseLowercase(base::StringPrintf(
Brandon Jones . resolved

The way that these UUIDs are handled throughout feels expensive. Every use of this method that I can find (see: [1], [2], [3]) is converting to a string, parsing as a base::Uuid, and then immediately converting to string again. Perhaps we can skip some parsing and just have this return the string directly?

Adam Ren (xWF)

Sure, I have directly converted it to the std::string.

Line 97, Patchset 7 (Latest): std::string uuid_str = XrUuidToBaseUuid(uuid).AsLowercaseString();
Brandon Jones . resolved

[1]

Adam Ren (xWF)

Done

Line 196, Patchset 7 (Latest): start_idx = mesh_processing_offset_;
Brandon Jones . resolved

Doesn't this have the potential to skip submeshes if a submesh that has already been processed is not present in a subsequent frame?

Adam Ren (xWF)

Nice point. Since the submesh won't be permanently lost — it will be re-detected the next time the offset wraps back to 0 — the worst case is a delay of up to total_count / kBatchSize frames before it appears. Given that batch processing is disabled by default (kEnableBatchProcessing = false), I think this is acceptable for an MVP. More importantly, once the OpenXR runtime supports it, proper optimizations like bbox-based spatial queries and Level-of-Detail meshing configuration would be more effective and wouldn't have this ordering issue. I'll leave a TODO in the tech report for future improvement of this method.

Line 210, Patchset 7 (Latest): std::string uuid_str = XrUuidToBaseUuid(state.submeshId).AsLowercaseString();
Brandon Jones . resolved

[2]

Adam Ren (xWF)

Done

Line 241, Patchset 7 (Latest): submesh_poses.push_back(state.submeshPoseInBaseSpace);
Brandon Jones . unresolved

Related to a previous comment I've given on a CL further down the stack: It's not clear to me from reading https://developer.android.com/develop/xr/openxr/extensions/XR_ANDROID_scene_meshing#XrSceneMeshSnapshotCreationResultANDROID if the `lastUpdatedTime` describes the pose _and_ the mesh data, or just the mesh data itself. I'm somewhat inclined to believe that, given that pose is reported along with the update time, that the pose might update with every query regardless of if `lastUpdatedTime` has changed. Would love to get some confirmation on that.

If the pose _can_ update independent of the `lastUpdateTime` then the current code would potentially cause the tracking of the meshes to drift when the mesh itself is unchanged.

Adam Ren (xWF)

Thanks for flagging this, Brandon. My understanding is that lastUpdatedTime reflects any change to the submesh — both geometry and pose — since the pose is an intrinsic property of the submesh entity. If pose updates were not reflected in lastUpdatedTime, there would be no mechanism for the app to detect pose-only changes at all, which would be a fundamental API design issue.

I think a pose-only lastUpdatedTime bump would correct drift rather than cause it, since it signals that the runtime has repositioned the mesh — e.g. after a loop closure or relocalization event.

@niha...@google.com @tor...@google.com Could you confirm this? Specifically, does it get bumped when only the pose changes, or only when the geometry (vertices/indices) changes? I believe you're working on the OpenXR runtime side and would have more info here.

Line 249, Patchset 7 (Latest): current_frame_uuids.insert(XrUuidToBaseUuid(state.submeshId).AsLowercaseString());
Brandon Jones . resolved

[3]

Adam Ren (xWF)

Done

Open in Gerrit

Related details

Attention is currently required from:
  • Alexander Cooper
  • Brandon Jones
  • Nihav Jain
  • Tomas Oresky
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
Gerrit-Change-Number: 7579917
Gerrit-PatchSet: 7
Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-CC: David Li <dav...@google.com>
Gerrit-CC: Nihav Jain <niha...@google.com>
Gerrit-CC: Tomas Oresky <tor...@google.com>
Gerrit-Attention: Brandon Jones <baj...@chromium.org>
Gerrit-Attention: Nihav Jain <niha...@google.com>
Gerrit-Attention: Tomas Oresky <tor...@google.com>
Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
Gerrit-Comment-Date: Wed, 04 Mar 2026 04:07:52 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Brandon Jones <baj...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy

Adam Ren (xWF) (Gerrit)

unread,
Mar 4, 2026, 3:17:20 PM (11 days ago) Mar 4
to Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
Attention needed from Alexander Cooper, Brandon Jones, Nihav Jain and Tomas Oresky

Adam Ren (xWF) added 1 comment

File device/vr/openxr/android/openxr_mesh_manager_android.cc
Line 241, Patchset 7: submesh_poses.push_back(state.submeshPoseInBaseSpace);
Brandon Jones . resolved

Related to a previous comment I've given on a CL further down the stack: It's not clear to me from reading https://developer.android.com/develop/xr/openxr/extensions/XR_ANDROID_scene_meshing#XrSceneMeshSnapshotCreationResultANDROID if the `lastUpdatedTime` describes the pose _and_ the mesh data, or just the mesh data itself. I'm somewhat inclined to believe that, given that pose is reported along with the update time, that the pose might update with every query regardless of if `lastUpdatedTime` has changed. Would love to get some confirmation on that.

If the pose _can_ update independent of the `lastUpdateTime` then the current code would potentially cause the tracking of the meshes to drift when the mesh itself is unchanged.

Adam Ren (xWF)

Thanks for flagging this, Brandon. My understanding is that lastUpdatedTime reflects any change to the submesh — both geometry and pose — since the pose is an intrinsic property of the submesh entity. If pose updates were not reflected in lastUpdatedTime, there would be no mechanism for the app to detect pose-only changes at all, which would be a fundamental API design issue.

I think a pose-only lastUpdatedTime bump would correct drift rather than cause it, since it signals that the runtime has repositioned the mesh — e.g. after a loop closure or relocalization event.

@niha...@google.com @tor...@google.com Could you confirm this? Specifically, does it get bumped when only the pose changes, or only when the geometry (vertices/indices) changes? I believe you're working on the OpenXR runtime side and would have more info here.

Adam Ren (xWF)

Confirmed from Salar Khan who implemented XR_ANDROID_scene_meshing. More details can be found in GChat thread

Open in Gerrit

Related details

Attention is currently required from:
  • Alexander Cooper
  • Brandon Jones
  • Nihav Jain
  • Tomas Oresky
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
    Gerrit-Change-Number: 7579917
    Gerrit-PatchSet: 8
    Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
    Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
    Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
    Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
    Gerrit-CC: David Li <dav...@google.com>
    Gerrit-CC: Nihav Jain <niha...@google.com>
    Gerrit-CC: Tomas Oresky <tor...@google.com>
    Gerrit-Attention: Brandon Jones <baj...@chromium.org>
    Gerrit-Attention: Nihav Jain <niha...@google.com>
    Gerrit-Attention: Tomas Oresky <tor...@google.com>
    Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
    Gerrit-Comment-Date: Wed, 04 Mar 2026 20:17:11 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Adam Ren (xWF) <ada...@google.com>
    Comment-In-Reply-To: Brandon Jones <baj...@chromium.org>
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Salar Khan (Gerrit)

    unread,
    Mar 4, 2026, 5:29:57 PM (11 days ago) Mar 4
    to Adam Ren (xWF), Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
    Attention needed from Adam Ren (xWF), Alexander Cooper, Brandon Jones and Tomas Oresky

    Salar Khan added 3 comments

    File device/vr/openxr/android/openxr_mesh_manager_android.cc
    Line 240, Patchset 8 (Latest): submeshes.push_back(submesh);
    submesh_poses.push_back(state.submeshPoseInBaseSpace);
    Salar Khan . unresolved

    nit: every push_back operation will have to reallocate memory and copy old data. Suggest first filling an std::list<size_t> which needs update, then reserving memory for the vectors and populating them in a separate loop.

    Line 290, Patchset 8 (Latest): std::vector<std::vector<XrVector3f>> positions_buffers(submeshes.size());
    std::vector<std::vector<XrVector3f>> normals_buffers(submeshes.size());
    std::vector<std::vector<uint32_t>> indices_buffers(submeshes.size());
    std::vector<std::vector<uint8_t>> semantics_buffers(submeshes.size());
    for (size_t i = 0; i < submeshes.size(); ++i) {
    auto& submesh = submeshes[i];
    if (submesh.vertexCountOutput > 0) {
    submesh.vertexCapacityInput = submesh.vertexCountOutput;
    positions_buffers[i].resize(submesh.vertexCapacityInput);
    normals_buffers[i].resize(submesh.vertexCapacityInput);
    semantics_buffers[i].resize(submesh.vertexCapacityInput);
    submesh.vertexPositions = positions_buffers[i].data();
    submesh.vertexNormals = normals_buffers[i].data();
    submesh.vertexSemantics = semantics_buffers[i].data();
    } else {
    submesh.vertexCapacityInput = 0;
    submesh.vertexPositions = nullptr;
    submesh.vertexNormals = nullptr;
    submesh.vertexSemantics = nullptr;
    }
    if (submesh.indexCountOutput > 0) {
    submesh.indexCapacityInput = submesh.indexCountOutput;
    indices_buffers[i].resize(submesh.indexCapacityInput);
    submesh.indices = indices_buffers[i].data();
    } else {
    submesh.indexCapacityInput = 0;
    submesh.indices = nullptr;
    }
    }
    Salar Khan . unresolved

    nit: fragmented buffers can have performance impact. You could just determine the total vertex and index count, and make each entry in submeshes point to an offset in the buffer.

    Line 348, Patchset 8 (Latest): const auto& positions = positions_buffers[idx];
    mesh->vertices.clear();
    mesh->vertices.reserve(positions.size() * 3);
    for (const auto& pos : positions) {
    mesh->vertices.push_back(pos.x);
    mesh->vertices.push_back(pos.y);
    mesh->vertices.push_back(pos.z);
    }
    // Indices
    if (submesh.indexCountOutput > 0) {
    const auto& indices = indices_buffers[idx];
    mesh->indices.assign(indices.begin(), indices.end());
    } else {
    mesh->indices.clear();
    mesh->indices.reserve(positions.size());
    for (uint32_t j = 0; j < positions.size(); ++j) {
    mesh->indices.push_back(j);
    }
    }
    // Semantic label
    if (!semantics_buffers[idx].empty()) {
    uint8_t first_val = semantics_buffers[idx][0];
    auto label_enum = static_cast<XrSceneMeshSemanticLabelANDROID>(first_val);
    mesh->semantic_label = ToMojomSemanticLabel(label_enum);
    }
    Salar Khan . unresolved

    Any reason you are first using a staging buffer to fetch the data and then copying to the final buffer?

    Why not first prepare mojom::XRMeshData from the submesh states, allocate the buffers, and then use the buffer pointers in xrGetSubmeshData.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Adam Ren (xWF)
    • Alexander Cooper
    • Brandon Jones
    • Tomas Oresky
    Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement is not satisfiedCode-Owners
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
      Gerrit-Change-Number: 7579917
      Gerrit-PatchSet: 8
      Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
      Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
      Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
      Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
      Gerrit-CC: David Li <dav...@google.com>
      Gerrit-CC: Nihav Jain <niha...@google.com>
      Gerrit-CC: Salar Khan <sala...@google.com>
      Gerrit-CC: Tomas Oresky <tor...@google.com>
      Gerrit-Attention: Adam Ren (xWF) <ada...@google.com>
      Gerrit-Attention: Brandon Jones <baj...@chromium.org>
      Gerrit-Attention: Tomas Oresky <tor...@google.com>
      Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
      Gerrit-Comment-Date: Wed, 04 Mar 2026 22:29:49 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Adam Ren (xWF) (Gerrit)

      unread,
      Mar 8, 2026, 8:05:45 PM (7 days ago) Mar 8
      to Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
      Attention needed from Alexander Cooper, Brandon Jones, Salar Khan and Tomas Oresky

      Adam Ren (xWF) added 1 comment

      File device/vr/openxr/android/openxr_mesh_manager_android.cc
      Line 348, Patchset 8: const auto& positions = positions_buffers[idx];

      mesh->vertices.clear();
      mesh->vertices.reserve(positions.size() * 3);
      for (const auto& pos : positions) {
      mesh->vertices.push_back(pos.x);
      mesh->vertices.push_back(pos.y);
      mesh->vertices.push_back(pos.z);
      }
      // Indices
      if (submesh.indexCountOutput > 0) {
      const auto& indices = indices_buffers[idx];
      mesh->indices.assign(indices.begin(), indices.end());
      } else {
      mesh->indices.clear();
      mesh->indices.reserve(positions.size());
      for (uint32_t j = 0; j < positions.size(); ++j) {
      mesh->indices.push_back(j);
      }
      }
      // Semantic label
      if (!semantics_buffers[idx].empty()) {
      uint8_t first_val = semantics_buffers[idx][0];
      auto label_enum = static_cast<XrSceneMeshSemanticLabelANDROID>(first_val);
      mesh->semantic_label = ToMojomSemanticLabel(label_enum);
      }
      Salar Khan . unresolved

      Any reason you are first using a staging buffer to fetch the data and then copying to the final buffer?

      Why not first prepare mojom::XRMeshData from the submesh states, allocate the buffers, and then use the buffer pointers in xrGetSubmeshData.

      Adam Ren (xWF)

      Hi, Salar, the suggestion works for indices only, doesn't work cleanly for vertices due to type mismatch, and is structurally inapplicable to normals and semantics. I think the current buffer-based approach is the correct design given the incompatibility between OpenXR's C struct types and Mojo's flat float arrays.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Alexander Cooper
      • Brandon Jones
      • Salar Khan
      • Tomas Oresky
      Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement is not satisfiedCode-Owners
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
      Gerrit-Change-Number: 7579917
      Gerrit-PatchSet: 9
      Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
      Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
      Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
      Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
      Gerrit-CC: David Li <dav...@google.com>
      Gerrit-CC: Nihav Jain <niha...@google.com>
      Gerrit-CC: Salar Khan <sala...@google.com>
      Gerrit-CC: Tomas Oresky <tor...@google.com>
      Gerrit-Attention: Salar Khan <sala...@google.com>
      Gerrit-Attention: Brandon Jones <baj...@chromium.org>
      Gerrit-Attention: Tomas Oresky <tor...@google.com>
      Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
      Gerrit-Comment-Date: Mon, 09 Mar 2026 00:05:28 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Salar Khan <sala...@google.com>
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Adam Ren (xWF) (Gerrit)

      unread,
      Mar 8, 2026, 8:05:48 PM (7 days ago) Mar 8
      to Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
      Attention needed from Alexander Cooper, Brandon Jones, Salar Khan and Tomas Oresky

      Adam Ren (xWF) added 2 comments

      File device/vr/openxr/android/openxr_mesh_manager_android.cc
      Line 240, Patchset 8: submeshes.push_back(submesh);
      submesh_poses.push_back(state.submeshPoseInBaseSpace);
      Salar Khan . resolved

      nit: every push_back operation will have to reallocate memory and copy old data. Suggest first filling an std::list<size_t> which needs update, then reserving memory for the vectors and populating them in a separate loop.

      Adam Ren (xWF)

      Done

      Line 290, Patchset 8: std::vector<std::vector<XrVector3f>> positions_buffers(submeshes.size());
      Salar Khan . resolved

      nit: fragmented buffers can have performance impact. You could just determine the total vertex and index count, and make each entry in submeshes point to an offset in the buffer.

      Adam Ren (xWF)

      Done

      Gerrit-Comment-Date: Mon, 09 Mar 2026 00:05:38 +0000
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Salar Khan (Gerrit)

      unread,
      Mar 9, 2026, 8:06:08 AM (7 days ago) Mar 9
      to Adam Ren (xWF), Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
      Attention needed from Adam Ren (xWF), Alexander Cooper, Brandon Jones and Tomas Oresky

      Salar Khan added 1 comment

      File device/vr/openxr/android/openxr_mesh_manager_android.cc
      Line 348, Patchset 8: const auto& positions = positions_buffers[idx];
      mesh->vertices.clear();
      mesh->vertices.reserve(positions.size() * 3);
      for (const auto& pos : positions) {
      mesh->vertices.push_back(pos.x);
      mesh->vertices.push_back(pos.y);
      mesh->vertices.push_back(pos.z);
      }
      // Indices
      if (submesh.indexCountOutput > 0) {
      const auto& indices = indices_buffers[idx];
      mesh->indices.assign(indices.begin(), indices.end());
      } else {
      mesh->indices.clear();
      mesh->indices.reserve(positions.size());
      for (uint32_t j = 0; j < positions.size(); ++j) {
      mesh->indices.push_back(j);
      }
      }
      // Semantic label
      if (!semantics_buffers[idx].empty()) {
      uint8_t first_val = semantics_buffers[idx][0];
      auto label_enum = static_cast<XrSceneMeshSemanticLabelANDROID>(first_val);
      mesh->semantic_label = ToMojomSemanticLabel(label_enum);
      }
      Salar Khan . resolved

      Any reason you are first using a staging buffer to fetch the data and then copying to the final buffer?

      Why not first prepare mojom::XRMeshData from the submesh states, allocate the buffers, and then use the buffer pointers in xrGetSubmeshData.

      Adam Ren (xWF)

      Hi, Salar, the suggestion works for indices only, doesn't work cleanly for vertices due to type mismatch, and is structurally inapplicable to normals and semantics. I think the current buffer-based approach is the correct design given the incompatibility between OpenXR's C struct types and Mojo's flat float arrays.

      Salar Khan

      Acknowledged. Yes I know conversions can add bottlenecks. In future I suggest to copy the raw CPU buffer to GPU buffer and perform conversions using compute shader.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Adam Ren (xWF)
      • Alexander Cooper
      • Brandon Jones
      • Tomas Oresky
      Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
        Gerrit-Change-Number: 7579917
        Gerrit-PatchSet: 9
        Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
        Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
        Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: David Li <dav...@google.com>
        Gerrit-CC: Nihav Jain <niha...@google.com>
        Gerrit-CC: Salar Khan <sala...@google.com>
        Gerrit-CC: Tomas Oresky <tor...@google.com>
        Gerrit-Attention: Adam Ren (xWF) <ada...@google.com>
        Gerrit-Attention: Brandon Jones <baj...@chromium.org>
        Gerrit-Attention: Tomas Oresky <tor...@google.com>
        Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
        Gerrit-Comment-Date: Mon, 09 Mar 2026 12:06:02 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Adam Ren (xWF) <ada...@google.com>
        Comment-In-Reply-To: Salar Khan <sala...@google.com>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Salar Khan (Gerrit)

        unread,
        Mar 9, 2026, 8:29:36 AM (7 days ago) Mar 9
        to Adam Ren (xWF), Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
        Attention needed from Adam Ren (xWF), Alexander Cooper, Brandon Jones and Tomas Oresky

        Salar Khan added 8 comments

        File device/vr/openxr/android/openxr_mesh_manager_android.cc
        Line 210, Patchset 9 (Latest): std::list<size_t> indices_needing_update;
        Salar Khan . unresolved

        nit: call this `state_indices_of_meshes_to_update` or something similar, because just using `indices` makes it confusing whether this refers to index of submesh states array or index buffer.

        Line 221, Patchset 9 (Latest): indices_needing_update.push_back(i);
        Salar Khan . unresolved

        nit: use emplace_back.

        Line 228, Patchset 9 (Latest): indices_needing_update.push_back(i);
        Salar Khan . unresolved

        nit: use emplace_back.

        Line 314, Patchset 9 (Latest): // Point each submesh's data fields to its slice of the flat buffers, and
        // record each submesh's starting offset for use in the conversion loop.
        std::vector<uint32_t> vertex_offsets(submeshes.size());
        std::vector<uint32_t> index_offsets(submeshes.size());
        Salar Khan . unresolved

        these might not be needed because in the loop of conversion to mojo structures you can just reference the mesh data buffer of each `XrSceneSubmeshDataANDROID` element in `submeshes`.

        Line 390, Patchset 9 (Latest): const auto& pos = positions_buffer[v_offset + j];
        Salar Khan . unresolved

        you could just do
        `const XrVector3f& pos = submesh.vertexPositions[j];`

        Line 396, Patchset 9 (Latest): if (submesh.indexCountOutput > 0) {
        Salar Khan . unresolved

        tbh: you don't really want to use up memory of a mesh with zero triangle. You could also add this to the skip logic

        Line 408, Patchset 9 (Latest): if (v_count > 0) {
        Salar Khan . unresolved

        nit: you already have a skip logic at begining of loop if vertex count is zero. This is unnecessary.

        Line 396, Patchset 9 (Latest): if (submesh.indexCountOutput > 0) {
        const uint32_t i_offset = index_offsets[idx];
        mesh->indices.assign(indices_buffer.begin() + i_offset,
        indices_buffer.begin() + i_offset +
        submesh.indexCountOutput);
        } else {
        mesh->indices.reserve(v_count);
        for (uint32_t j = 0; j < v_count; ++j) {

        mesh->indices.push_back(j);
        }
        }
        // Semantic label
        if (v_count > 0) {
        uint8_t first_val = semantics_buffer[v_offset];

        auto label_enum = static_cast<XrSceneMeshSemanticLabelANDROID>(first_val);
        mesh->semantic_label = ToMojomSemanticLabel(label_enum);
        }
        Salar Khan . unresolved

        same scenario here. No need of offset array. Each `XrSceneSubmeshDataANDROID` already has pointer to the buffer where their data exists.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Adam Ren (xWF)
        • Alexander Cooper
        • Brandon Jones
        • Tomas Oresky
        Submit Requirements:
          • requirement satisfiedCode-Coverage
          • requirement is not satisfiedCode-Owners
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: chromium/src
          Gerrit-Branch: main
          Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
          Gerrit-Change-Number: 7579917
          Gerrit-PatchSet: 9
          Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
          Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
          Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
          Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
          Gerrit-CC: David Li <dav...@google.com>
          Gerrit-CC: Nihav Jain <niha...@google.com>
          Gerrit-CC: Salar Khan <sala...@google.com>
          Gerrit-CC: Tomas Oresky <tor...@google.com>
          Gerrit-Attention: Adam Ren (xWF) <ada...@google.com>
          Gerrit-Attention: Brandon Jones <baj...@chromium.org>
          Gerrit-Attention: Tomas Oresky <tor...@google.com>
          Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
          Gerrit-Comment-Date: Mon, 09 Mar 2026 12:29:30 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          satisfied_requirement
          unsatisfied_requirement
          open
          diffy

          David Li (Gerrit)

          unread,
          Mar 9, 2026, 11:29:21 AM (7 days ago) Mar 9
          to Adam Ren (xWF), Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, AyeAye, feature-v...@chromium.org
          Attention needed from Adam Ren (xWF), Alexander Cooper, Brandon Jones and Tomas Oresky

          David Li added 1 comment

          File device/vr/openxr/android/openxr_mesh_manager_android.cc
          Line 348, Patchset 8: const auto& positions = positions_buffers[idx];

          mesh->vertices.clear();
          mesh->vertices.reserve(positions.size() * 3);
          for (const auto& pos : positions) {
          mesh->vertices.push_back(pos.x);
          mesh->vertices.push_back(pos.y);
          mesh->vertices.push_back(pos.z);
          }
          // Indices
          if (submesh.indexCountOutput > 0) {
          const auto& indices = indices_buffers[idx];
          mesh->indices.assign(indices.begin(), indices.end());
          } else {
          mesh->indices.clear();
          mesh->indices.reserve(positions.size());
          for (uint32_t j = 0; j < positions.size(); ++j) {

          mesh->indices.push_back(j);
          }
          }
          // Semantic label
          if (!semantics_buffers[idx].empty()) {
          uint8_t first_val = semantics_buffers[idx][0];
          auto label_enum = static_cast<XrSceneMeshSemanticLabelANDROID>(first_val);
          mesh->semantic_label = ToMojomSemanticLabel(label_enum);
          }
          Salar Khan . unresolved

          Any reason you are first using a staging buffer to fetch the data and then copying to the final buffer?

          Why not first prepare mojom::XRMeshData from the submesh states, allocate the buffers, and then use the buffer pointers in xrGetSubmeshData.

          Adam Ren (xWF)

          Hi, Salar, the suggestion works for indices only, doesn't work cleanly for vertices due to type mismatch, and is structurally inapplicable to normals and semantics. I think the current buffer-based approach is the correct design given the incompatibility between OpenXR's C struct types and Mojo's flat float arrays.

          Salar Khan

          Acknowledged. Yes I know conversions can add bottlenecks. In future I suggest to copy the raw CPU buffer to GPU buffer and perform conversions using compute shader.

          David Li

          I don't quite understand the type mismatch. XrVector3f is just three floats so a vector of XrVector3f should be laid out the same as std::vector<float> right?

          Gerrit-Comment-Date: Mon, 09 Mar 2026 15:29:11 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          satisfied_requirement
          unsatisfied_requirement
          open
          diffy

          Salar Khan (Gerrit)

          unread,
          Mar 9, 2026, 8:24:00 PM (6 days ago) Mar 9
          to Adam Ren (xWF), Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
          Attention needed from Adam Ren (xWF), Alexander Cooper, Brandon Jones and Tomas Oresky

          Salar Khan added 1 comment

          File device/vr/openxr/android/openxr_mesh_manager_android.cc
          Salar Khan

          yes. You can safely cast a XrVector3f to a float[3], or even an array of XrVector3f to a float array.

          Gerrit-Comment-Date: Tue, 10 Mar 2026 00:23:51 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          Comment-In-Reply-To: David Li <dav...@google.com>
          satisfied_requirement
          unsatisfied_requirement
          open
          diffy

          Alexander Cooper (Gerrit)

          unread,
          Mar 12, 2026, 4:30:16 PM (3 days ago) Mar 12
          to Adam Ren (xWF), Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, David Li, AyeAye, feature-v...@chromium.org
          Attention needed from Adam Ren (xWF), Brandon Jones and Tomas Oresky

          Alexander Cooper added 24 comments

          Patchset-level comments
          File-level comment, Patchset 11 (Latest):
          Alexander Cooper . resolved

          I'm not sure the complication of the flat array is worth it TBH. Otherwise, we should be using base::span more and the actual MeshId type and Id allocator.

          File device/vr/openxr/android/openxr_mesh_manager_android.h
          Line 51, Patchset 11 (Latest): uint64_t GetOrCreateMeshId(const XrUuid& uuid);
          Alexander Cooper . unresolved

          We should probably be using MeshId here no? Or at the very least another strongly-typed alias.

          File device/vr/openxr/android/openxr_mesh_manager_android.cc
          Line 74, Patchset 11 (Latest): XrSceneMeshingTrackerCreateInfoANDROID create_info{
          XR_TYPE_SCENE_MESHING_TRACKER_CREATE_INFO_ANDROID};
          create_info.semanticLabelSet =
          XR_SCENE_MESH_SEMANTIC_LABEL_SET_DEFAULT_ANDROID;
          create_info.enableNormals = XR_TRUE;
          Alexander Cooper . unresolved

          Consider using an initializer list e.g.:

          XrSceneMeshingTrackerCreateInfoANDROID create_info {
          .type = ,
          .semanticLabelSet= ...,
          .enableNormals = XR_TRUE,
          };

          Line 90, Patchset 11 (Latest): mesh_tracker_ = XR_NULL_HANDLE;
          }
          submesh_cache_.clear();
          uuid_to_id_.clear();
          last_valid_mesh_data_.reset();
          Alexander Cooper . unresolved

          None of this is probably strictly necessary in the destructor, unless there's some ordering or other concerns that should be documented.

          Line 103, Patchset 11 (Latest): uint64_t new_id = next_mesh_id_++;
          Alexander Cooper . unresolved

          We should be using hte MeshId type and a generator and returning that value.

          Line 105, Patchset 11 (Latest): DVLOG(2) << "Created new mesh ID " << new_id << " for UUID " << uuid_str;
          Alexander Cooper . unresolved

          3

          Line 109, Patchset 11 (Latest):mojom::XRMeshDetectionDataPtr OpenXrMeshManagerAndroid::GetDetectedMeshesData(
          Alexander Cooper . unresolved

          Based on the length, it seems like maybe this could/should be broken up somehow?

          Line 117, Patchset 11 (Latest): XrPosef bbox_center{};
          bbox_center.position = {0.0f, 0.0f, 0.0f};
          bbox_center.orientation = {0.0f, 0.0f, 0.0f, 1.0f};
          Line 117, Patchset 11 (Latest): XrPosef bbox_center{};
          bbox_center.position = {0.0f, 0.0f, 0.0f};
          bbox_center.orientation = {0.0f, 0.0f, 0.0f, 1.0f};
          XrBoxf bounding_box;
          bounding_box.center = bbox_center;
          bounding_box.extents = {kUnboundedSceneHalfExtent, kUnboundedSceneHalfExtent,
          kUnboundedSceneHalfExtent};
          Alexander Cooper . unresolved

          Use an initializer list; only chrome style is to match the order of the members.

          Line 194, Patchset 11 (Latest): size_t start_idx = 0;
          size_t end_idx = static_cast<size_t>(submesh_count);
          if constexpr (kEnableBatchProcessing) {
          start_idx = mesh_processing_offset_;
          end_idx = std::min(start_idx + kBatchSize,
          static_cast<size_t>(submesh_count));
          mesh_processing_offset_ = (end_idx == submesh_count) ? 0 : end_idx;
          }
          DVLOG(2) << "Processing meshes [" << start_idx << ", " << end_idx
          << ") of " << submesh_count;
          Alexander Cooper . unresolved

          Chrome style generally frowns on e.g. `_idx` suffixes; but I also think using base::span is cleaner here. You can do something like:

          ```
          auto states_span = base::span(states);
          if constexpr (kEnableBatchProcessing) {
          uint32_t mesh_process_count = std::min(kBatchSize, submesh_count - mesh_processing_offset_);
          states_span = states_span.subspan(mesh_processing_offset_, mesh_process_count);

          // Can do another variation on the ternary instead
          mesh_processing_offset_ += kBatchSize;
          if (mesh_processing_offset_ >= submesh_count_) {
          mesh_processing_offset_ = 0;
          }
          ```

          This allows you to flip to the preferred range-based for loop below.

          Though, if we don't think we'd ever turn on the batch-based processing it may be simpler to just remove it.

          Line 214, Patchset 11 (Latest): auto cache_it = submesh_cache_.find(uuid_str);
          Alexander Cooper . unresolved

          Seems like a good use for
          ```
          auto* mesh = base::FindOrNull(submesh_cache_, uuid_str);
          if (!mesh) {
          mesh = &submesh_cache_[uuid_str];
          mesh.mesh_id = GetOrCreateMeshId(state.submeshId);
          }

          if (mesh.last_updated_time != state.lastUpdatedTime) {
          ...
          }
          ```

          Line 232, Patchset 11 (Latest): // Second pass: now that the count is known, reserve once and populate.
          Alexander Cooper . unresolved

          I don't think this is a good approach. I think it'd be more efficient to only iterate through the list once, it seems like the entire purpose of the first iteration is just to avoid vector resizes as you go along. If it truly becomes an issue you could probably do some heurisitc to estimate an initial capacity for `submeshes` and `submesh_poses` (the total size in the worst case or maybe something like 0.5-0.75)

          Line 242, Patchset 11 (Latest): submesh.vertexCapacityInput = 0;
          submesh.vertexCountOutput = 0;
          submesh.indexCapacityInput = 0;
          submesh.indexCountOutput = 0;

          submesh.vertexPositions = nullptr;
          submesh.vertexNormals = nullptr;
          submesh.vertexSemantics = nullptr;
          submesh.indices = nullptr;
          Alexander Cooper . unresolved

          ISTM that these should be most of the defaults?

          Line 255, Patchset 11 (Latest): if (end_idx == submesh_count) {
          Alexander Cooper . unresolved

          Not clear why only in this case?

          Line 263, Patchset 11 (Latest): for (const auto& [uuid_str, cache_entry] : submesh_cache_) {
          Alexander Cooper . unresolved

          NIT: `_` if unused I believe is acceptable

          Line 284, Patchset 11 (Latest): extension_helper_->ExtensionMethods().xrDestroySceneMeshSnapshotANDROID(
          snapshot);
          Alexander Cooper . unresolved

          Use https://source.chromium.org/chromium/chromium/src/+/main:device/vr/openxr/scoped_openxr_object.h or absl::Cleanup to ensure this gets cleaned up rather than this easily skippable idion.

          Line 299, Patchset 11 (Latest): // Sum total vertex and index counts so we can allocate one flat, contiguous
          // buffer per data type rather than one allocation per submesh.
          Alexander Cooper . unresolved

          How many submeshes are we typically seeing? I'm not sure that the likely slight extra overhead is worth the more complicated logic here to make this a flat array instead of e.g. creating an array of a POD struct of vectors for a submesh (And then we could maybe even just make a typemap to abstract the conversion for mojo over the wire).

          Line 314, Patchset 9: // Point each submesh's data fields to its slice of the flat buffers, and

          // record each submesh's starting offset for use in the conversion loop.
          std::vector<uint32_t> vertex_offsets(submeshes.size());
          std::vector<uint32_t> index_offsets(submeshes.size());
          Salar Khan . unresolved

          these might not be needed because in the loop of conversion to mojo structures you can just reference the mesh data buffer of each `XrSceneSubmeshDataANDROID` element in `submeshes`.

          Alexander Cooper

          How long is the lifetime of that data buffer guaranteed? I guess creating the mojo will necessarily have to do a copy anyway, so maybe it doesn't matter.

          Line 331, Patchset 11 (Latest): UNSAFE_BUFFERS(
          submesh.vertexPositions = positions_buffer.data() + vertex_offset;
          submesh.vertexNormals = normals_buffer.data() + vertex_offset;
          submesh.vertexSemantics = semantics_buffer.data() + vertex_offset;)
          Alexander Cooper . unresolved

          I believe base::span's subspan would be beneficial here. You can simply do (for each one) something like:

          positions_buffer = positions_buffer.subspan(vertex_offset); and it'll basically advance the start of the array for you. You can then just call `positions_buffer.data()` as needed. This avoids the need for the `SAFETY` comment and adds the bounds checks.

          I think `vertex_offset` doesn't become an accumulation but where you do the accumulation now is where you'd do the subspan (or maybe for the flat array you still need it).

          Line 348, Patchset 11 (Latest): UNSAFE_BUFFERS(
          submesh.indices = indices_buffer.data() + index_offset;)
          index_offset += submesh.indexCountOutput;
          Alexander Cooper . unresolved

          Similar comment as above.

          Really we should *only* be doing `UNSAFE_BUFFERS` if we need to e.g. turn the resulting array from an OpenXR API into a bounds-checked type.

          Alexander Cooper

          In the past the security/memory safety team has not really been a fan of such casts, it'd be best to run it by one of them.

          Line 377, Patchset 11 (Latest): mesh->id = device::MeshId(GetOrCreateMeshId(submesh.submeshId));
          Alexander Cooper . unresolved

          GetOrCreateMeshId should return a mesh ID and not a uint64_t.

          Line 379, Patchset 11 (Latest): XrPosef xr_pose = submesh_poses[idx];
          gfx::Point3F position(xr_pose.position.x, xr_pose.position.y,
          xr_pose.position.z);
          gfx::Quaternion orientation(xr_pose.orientation.x, xr_pose.orientation.y,
          xr_pose.orientation.z, xr_pose.orientation.w);
          mesh->mojo_from_mesh = device::Pose(position, orientation);
          Alexander Cooper . unresolved
          Open in Gerrit

          Related details

          Attention is currently required from:
          • Adam Ren (xWF)
          • Brandon Jones
          • Tomas Oresky
          Submit Requirements:
          • requirement satisfiedCode-Coverage
          • requirement is not satisfiedCode-Owners
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: chromium/src
          Gerrit-Branch: main
          Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
          Gerrit-Change-Number: 7579917
          Gerrit-PatchSet: 11
          Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
          Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
          Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
          Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
          Gerrit-CC: David Li <dav...@google.com>
          Gerrit-CC: Nihav Jain <niha...@google.com>
          Gerrit-CC: Salar Khan <sala...@google.com>
          Gerrit-CC: Tomas Oresky <tor...@google.com>
          Gerrit-Attention: Adam Ren (xWF) <ada...@google.com>
          Gerrit-Attention: Brandon Jones <baj...@chromium.org>
          Gerrit-Attention: Tomas Oresky <tor...@google.com>
          Gerrit-Comment-Date: Thu, 12 Mar 2026 20:30:07 +0000
          satisfied_requirement
          unsatisfied_requirement
          open
          diffy

          Adam Ren (xWF) (Gerrit)

          unread,
          Mar 13, 2026, 6:38:38 AM (3 days ago) Mar 13
          to Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
          Attention needed from Alexander Cooper, Brandon Jones, David Li, Salar Khan and Tomas Oresky

          Adam Ren (xWF) added 30 comments

          File device/vr/openxr/android/openxr_mesh_manager_android.h
          Line 51, Patchset 11: uint64_t GetOrCreateMeshId(const XrUuid& uuid);
          Alexander Cooper . resolved

          We should probably be using MeshId here no? Or at the very least another strongly-typed alias.

          Adam Ren (xWF)

          Done

          File device/vr/openxr/android/openxr_mesh_manager_android.cc
          Line 47, Patchset 11:mojom::XRSemanticLabel ToMojomSemanticLabel(
          Alexander Cooper . resolved
          Adam Ren (xWF)

          Done

          Line 74, Patchset 11: XrSceneMeshingTrackerCreateInfoANDROID create_info{

          XR_TYPE_SCENE_MESHING_TRACKER_CREATE_INFO_ANDROID};
          create_info.semanticLabelSet =
          XR_SCENE_MESH_SEMANTIC_LABEL_SET_DEFAULT_ANDROID;
          create_info.enableNormals = XR_TRUE;
          Alexander Cooper . resolved

          Consider using an initializer list e.g.:

          XrSceneMeshingTrackerCreateInfoANDROID create_info {
          .type = ,
          .semanticLabelSet= ...,
          .enableNormals = XR_TRUE,
          };

          Adam Ren (xWF)

          Done

          Line 90, Patchset 11: mesh_tracker_ = XR_NULL_HANDLE;
          }
          submesh_cache_.clear();
          uuid_to_id_.clear();
          last_valid_mesh_data_.reset();
          Alexander Cooper . resolved

          None of this is probably strictly necessary in the destructor, unless there's some ordering or other concerns that should be documented.

          Adam Ren (xWF)

          Thanks. It has been removed.

          Line 103, Patchset 11: uint64_t new_id = next_mesh_id_++;
          Alexander Cooper . resolved

          We should be using hte MeshId type and a generator and returning that value.

          Adam Ren (xWF)

          Done

          Line 105, Patchset 11: DVLOG(2) << "Created new mesh ID " << new_id << " for UUID " << uuid_str;
          Alexander Cooper . resolved

          3

          Adam Ren (xWF)

          Done

          Line 109, Patchset 11:mojom::XRMeshDetectionDataPtr OpenXrMeshManagerAndroid::GetDetectedMeshesData(
          Alexander Cooper . resolved

          Based on the length, it seems like maybe this could/should be broken up somehow?

          Adam Ren (xWF)

          Done

          Line 117, Patchset 11: XrPosef bbox_center{};

          bbox_center.position = {0.0f, 0.0f, 0.0f};
          bbox_center.orientation = {0.0f, 0.0f, 0.0f, 1.0f};
          Alexander Cooper . resolved
          Adam Ren (xWF)

          Done

          Line 117, Patchset 11: XrPosef bbox_center{};

          bbox_center.position = {0.0f, 0.0f, 0.0f};
          bbox_center.orientation = {0.0f, 0.0f, 0.0f, 1.0f};
          XrBoxf bounding_box;
          bounding_box.center = bbox_center;
          bounding_box.extents = {kUnboundedSceneHalfExtent, kUnboundedSceneHalfExtent,
          kUnboundedSceneHalfExtent};
          Alexander Cooper . resolved

          Use an initializer list; only chrome style is to match the order of the members.

          Adam Ren (xWF)

          Done

          Line 194, Patchset 11: size_t start_idx = 0;

          size_t end_idx = static_cast<size_t>(submesh_count);
          if constexpr (kEnableBatchProcessing) {
          start_idx = mesh_processing_offset_;
          end_idx = std::min(start_idx + kBatchSize,
          static_cast<size_t>(submesh_count));
          mesh_processing_offset_ = (end_idx == submesh_count) ? 0 : end_idx;
          }
          DVLOG(2) << "Processing meshes [" << start_idx << ", " << end_idx
          << ") of " << submesh_count;
          Alexander Cooper . resolved

          Chrome style generally frowns on e.g. `_idx` suffixes; but I also think using base::span is cleaner here. You can do something like:

          ```
          auto states_span = base::span(states);
          if constexpr (kEnableBatchProcessing) {
          uint32_t mesh_process_count = std::min(kBatchSize, submesh_count - mesh_processing_offset_);
          states_span = states_span.subspan(mesh_processing_offset_, mesh_process_count);

          // Can do another variation on the ternary instead
          mesh_processing_offset_ += kBatchSize;
          if (mesh_processing_offset_ >= submesh_count_) {
          mesh_processing_offset_ = 0;
          }
          ```

          This allows you to flip to the preferred range-based for loop below.

          Though, if we don't think we'd ever turn on the batch-based processing it may be simpler to just remove it.

          Adam Ren (xWF)

          Sure, I have removed the batch-based processing as it doesn't give good user experience. We should use bbox-based and LOD-based query when they are available for future optimization

          Line 210, Patchset 9: std::list<size_t> indices_needing_update;
          Salar Khan . resolved

          nit: call this `state_indices_of_meshes_to_update` or something similar, because just using `indices` makes it confusing whether this refers to index of submesh states array or index buffer.

          Adam Ren (xWF)

          This variable was removed in a subsequent refactoring that simplified the loop to a single pass.

          Line 214, Patchset 11: auto cache_it = submesh_cache_.find(uuid_str);
          Alexander Cooper . resolved

          Seems like a good use for
          ```
          auto* mesh = base::FindOrNull(submesh_cache_, uuid_str);
          if (!mesh) {
          mesh = &submesh_cache_[uuid_str];
          mesh.mesh_id = GetOrCreateMeshId(state.submeshId);
          }

          if (mesh.last_updated_time != state.lastUpdatedTime) {
          ...
          }
          ```

          Adam Ren (xWF)

          Done

          Line 221, Patchset 9: indices_needing_update.push_back(i);
          Salar Khan . resolved

          nit: use emplace_back.

          Adam Ren (xWF)

          Done

          Line 228, Patchset 9: indices_needing_update.push_back(i);
          Salar Khan . resolved

          nit: use emplace_back.

          Adam Ren (xWF)

          Done

          Line 232, Patchset 11: // Second pass: now that the count is known, reserve once and populate.
          Alexander Cooper . resolved

          I don't think this is a good approach. I think it'd be more efficient to only iterate through the list once, it seems like the entire purpose of the first iteration is just to avoid vector resizes as you go along. If it truly becomes an issue you could probably do some heurisitc to estimate an initial capacity for `submeshes` and `submesh_poses` (the total size in the worst case or maybe something like 0.5-0.75)

          Adam Ren (xWF)

          I have eliminated the two-pass approach in an earlier comment. The current code is a single loop that builds submeshes and submesh_poses directly:

          Line 242, Patchset 11: submesh.vertexCapacityInput = 0;

          submesh.vertexCountOutput = 0;
          submesh.indexCapacityInput = 0;
          submesh.indexCountOutput = 0;
          submesh.vertexPositions = nullptr;
          submesh.vertexNormals = nullptr;
          submesh.vertexSemantics = nullptr;
          submesh.indices = nullptr;
          Alexander Cooper . resolved

          ISTM that these should be most of the defaults?

          Adam Ren (xWF)

          Done

          Line 255, Patchset 11: if (end_idx == submesh_count) {
          Alexander Cooper . resolved

          Not clear why only in this case?

          Adam Ren (xWF)

          the guard was removed in the same step where I removed batch processing. The stale cache cleanup now always runs unconditionally.

          Line 263, Patchset 11: for (const auto& [uuid_str, cache_entry] : submesh_cache_) {
          Alexander Cooper . resolved

          NIT: `_` if unused I believe is acceptable

          Adam Ren (xWF)

          Done

          Line 284, Patchset 11: extension_helper_->ExtensionMethods().xrDestroySceneMeshSnapshotANDROID(
          snapshot);
          Alexander Cooper . resolved

          Use https://source.chromium.org/chromium/chromium/src/+/main:device/vr/openxr/scoped_openxr_object.h or absl::Cleanup to ensure this gets cleaned up rather than this easily skippable idion.

          Adam Ren (xWF)

          Done

          Line 299, Patchset 11: // Sum total vertex and index counts so we can allocate one flat, contiguous

          // buffer per data type rather than one allocation per submesh.
          Alexander Cooper . resolved

          How many submeshes are we typically seeing? I'm not sure that the likely slight extra overhead is worth the more complicated logic here to make this a flat array instead of e.g. creating an array of a POD struct of vectors for a submesh (And then we could maybe even just make a typemap to abstract the conversion for mojo over the wire).

          Adam Ren (xWF)

          In practice submesh counts range from ~300 to 1000+, growing as the scanned region expands. Salar suggested in an earlier patchset to use flat contiguous buffers specifically to avoid fragmented allocations at that scale, so keeping the flat buffer approach seems justified here.

          Line 314, Patchset 9: // Point each submesh's data fields to its slice of the flat buffers, and
          // record each submesh's starting offset for use in the conversion loop.
          std::vector<uint32_t> vertex_offsets(submeshes.size());
          std::vector<uint32_t> index_offsets(submeshes.size());
          Salar Khan . resolved

          these might not be needed because in the loop of conversion to mojo structures you can just reference the mesh data buffer of each `XrSceneSubmeshDataANDROID` element in `submeshes`.

          Alexander Cooper

          How long is the lifetime of that data buffer guaranteed? I guess creating the mojo will necessarily have to do a copy anyway, so maybe it doesn't matter.

          Adam Ren (xWF)

          Removed vertex_offsets and index_offsets from SubmeshGeometry and updated the conversion loop to use base::span directly.

          Line 331, Patchset 11: UNSAFE_BUFFERS(

          submesh.vertexPositions = positions_buffer.data() + vertex_offset;
          submesh.vertexNormals = normals_buffer.data() + vertex_offset;
          submesh.vertexSemantics = semantics_buffer.data() + vertex_offset;)
          Alexander Cooper . resolved

          I believe base::span's subspan would be beneficial here. You can simply do (for each one) something like:

          positions_buffer = positions_buffer.subspan(vertex_offset); and it'll basically advance the start of the array for you. You can then just call `positions_buffer.data()` as needed. This avoids the need for the `SAFETY` comment and adds the bounds checks.

          I think `vertex_offset` doesn't become an accumulation but where you do the accumulation now is where you'd do the subspan (or maybe for the flat array you still need it).

          Adam Ren (xWF)

          Replaced the raw data() + offset pointer arithmetic with four base::span cursors over the flat buffers.

          Line 348, Patchset 11: UNSAFE_BUFFERS(

          submesh.indices = indices_buffer.data() + index_offset;)
          index_offset += submesh.indexCountOutput;
          Alexander Cooper . resolved

          Similar comment as above.

          Really we should *only* be doing `UNSAFE_BUFFERS` if we need to e.g. turn the resulting array from an OpenXR API into a bounds-checked type.

          Adam Ren (xWF)

          Done

          Line 348, Patchset 8: const auto& positions = positions_buffers[idx];
          mesh->vertices.clear();
          mesh->vertices.reserve(positions.size() * 3);
          for (const auto& pos : positions) {
          mesh->vertices.push_back(pos.x);
          mesh->vertices.push_back(pos.y);
          mesh->vertices.push_back(pos.z);
          }
          // Indices
          if (submesh.indexCountOutput > 0) {
          const auto& indices = indices_buffers[idx];
          mesh->indices.assign(indices.begin(), indices.end());
          } else {
          mesh->indices.clear();
          mesh->indices.reserve(positions.size());
          for (uint32_t j = 0; j < positions.size(); ++j) {
          mesh->indices.push_back(j);
          }
          }
          // Semantic label
          if (!semantics_buffers[idx].empty()) {
          uint8_t first_val = semantics_buffers[idx][0];
          auto label_enum = static_cast<XrSceneMeshSemanticLabelANDROID>(first_val);
          mesh->semantic_label = ToMojomSemanticLabel(label_enum);
          }
          Salar Khan . resolved

          Any reason you are first using a staging buffer to fetch the data and then copying to the final buffer?

          Why not first prepare mojom::XRMeshData from the submesh states, allocate the buffers, and then use the buffer pointers in xrGetSubmeshData.

          Adam Ren (xWF)

          Hi, Salar, the suggestion works for indices only, doesn't work cleanly for vertices due to type mismatch, and is structurally inapplicable to normals and semantics. I think the current buffer-based approach is the correct design given the incompatibility between OpenXR's C struct types and Mojo's flat float arrays.

          Salar Khan

          Acknowledged. Yes I know conversions can add bottlenecks. In future I suggest to copy the raw CPU buffer to GPU buffer and perform conversions using compute shader.

          David Li

          I don't quite understand the type mismatch. XrVector3f is just three floats so a vector of XrVector3f should be laid out the same as std::vector<float> right?

          Salar Khan

          yes. You can safely cast a XrVector3f to a float[3], or even an array of XrVector3f to a float array.

          Alexander Cooper

          In the past the security/memory safety team has not really been a fan of such casts, it'd be best to run it by one of them.

          Adam Ren (xWF)

          Thanks, based on the safety team's preference. I will keep the mojom conversion seperate.

          Line 377, Patchset 11: mesh->id = device::MeshId(GetOrCreateMeshId(submesh.submeshId));
          Alexander Cooper . resolved

          GetOrCreateMeshId should return a mesh ID and not a uint64_t.

          Adam Ren (xWF)

          Done

          Line 379, Patchset 11: XrPosef xr_pose = submesh_poses[idx];

          gfx::Point3F position(xr_pose.position.x, xr_pose.position.y,
          xr_pose.position.z);
          gfx::Quaternion orientation(xr_pose.orientation.x, xr_pose.orientation.y,
          xr_pose.orientation.z, xr_pose.orientation.w);
          mesh->mojo_from_mesh = device::Pose(position, orientation);
          Alexander Cooper . resolved
          Adam Ren (xWF)

          Done

          Line 390, Patchset 9: const auto& pos = positions_buffer[v_offset + j];
          Salar Khan . resolved

          you could just do
          `const XrVector3f& pos = submesh.vertexPositions[j];`

          Adam Ren (xWF)

          Done

          Line 396, Patchset 9: if (submesh.indexCountOutput > 0) {
          Salar Khan . resolved

          tbh: you don't really want to use up memory of a mesh with zero triangle. You could also add this to the skip logic

          Adam Ren (xWF)

          Done

          Line 408, Patchset 9: if (v_count > 0) {
          Salar Khan . resolved

          nit: you already have a skip logic at begining of loop if vertex count is zero. This is unnecessary.

          Adam Ren (xWF)

          Done

          Line 396, Patchset 9: if (submesh.indexCountOutput > 0) {

          const uint32_t i_offset = index_offsets[idx];
          mesh->indices.assign(indices_buffer.begin() + i_offset,
          indices_buffer.begin() + i_offset +
          submesh.indexCountOutput);
          } else {
          mesh->indices.reserve(v_count);
          for (uint32_t j = 0; j < v_count; ++j) {

          mesh->indices.push_back(j);
          }
          }
          // Semantic label
          if (v_count > 0) {
          uint8_t first_val = semantics_buffer[v_offset];
          auto label_enum = static_cast<XrSceneMeshSemanticLabelANDROID>(first_val);
          mesh->semantic_label = ToMojomSemanticLabel(label_enum);
          }
          Salar Khan . resolved

          same scenario here. No need of offset array. Each `XrSceneSubmeshDataANDROID` already has pointer to the buffer where their data exists.

          Adam Ren (xWF)

          Thanks Salar, index_offsets and vertex_offsets arrays were removed. The conversion loop now uses base::span(submesh.indices, submesh.indexCountOutput) and base::span(submesh.vertexSemantics, submesh.vertexCountOutput) directly

          Open in Gerrit

          Related details

          Attention is currently required from:
          • Alexander Cooper
          • Brandon Jones
          • David Li
          • Salar Khan
          • Tomas Oresky
          Submit Requirements:
            • requirement satisfiedCode-Coverage
            • requirement is not satisfiedCode-Owners
            • requirement is not satisfiedCode-Review
            • requirement is not satisfiedReview-Enforcement
            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
            Gerrit-MessageType: comment
            Gerrit-Project: chromium/src
            Gerrit-Branch: main
            Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
            Gerrit-Change-Number: 7579917
            Gerrit-PatchSet: 14
            Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
            Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
            Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
            Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
            Gerrit-CC: David Li <dav...@google.com>
            Gerrit-CC: Nihav Jain <niha...@google.com>
            Gerrit-CC: Salar Khan <sala...@google.com>
            Gerrit-CC: Tomas Oresky <tor...@google.com>
            Gerrit-Attention: David Li <dav...@google.com>
            Gerrit-Attention: Salar Khan <sala...@google.com>
            Gerrit-Attention: Brandon Jones <baj...@chromium.org>
            Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
            Gerrit-Attention: Tomas Oresky <tor...@google.com>
            Gerrit-Comment-Date: Fri, 13 Mar 2026 10:38:26 +0000
            Gerrit-HasComments: Yes
            Gerrit-Has-Labels: No
            Comment-In-Reply-To: David Li <dav...@google.com>
            Comment-In-Reply-To: Adam Ren (xWF) <ada...@google.com>
            Comment-In-Reply-To: Salar Khan <sala...@google.com>
            Comment-In-Reply-To: Alexander Cooper <alco...@chromium.org>
            satisfied_requirement
            unsatisfied_requirement
            open
            diffy

            Salar Khan (Gerrit)

            unread,
            Mar 13, 2026, 11:22:49 AM (3 days ago) Mar 13
            to Adam Ren (xWF), Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
            Attention needed from Adam Ren (xWF), Alexander Cooper, Brandon Jones, David Li and Tomas Oresky

            Salar Khan added 2 comments

            File device/vr/openxr/android/openxr_mesh_manager_android.cc
            Line 168, Patchset 14 (Latest): XrBoxf bounding_box = {
            Salar Khan . unresolved

            bounding box extents refers to the actual width and height of the bounding box, not the half extent (absolute vector from box origin to a corner in box local space).

            So you probably need to multiply kUnboundedSceneHalfExtent with 2. Check the definition of XrBoxf:
            https://registry.khronos.org/OpenXR/specs/1.1/man/html/XrBoxf.html

            Line 227, Patchset 14 (Latest): std::vector<XrSceneSubmeshDataANDROID> submeshes;
            std::vector<XrPosef> submesh_poses;
            for (const auto& state : states) {
            std::string uuid_str = XrUuidToString(state.submeshId);
            auto* entry = base::FindOrNull(submesh_cache_, uuid_str);
            if (!entry) {
            entry = &submesh_cache_[uuid_str];
            entry->mesh_id = GetOrCreateMeshId(state.submeshId);
            DVLOG(2) << "New submesh detected";
            } else if (entry->last_updated_time == state.lastUpdatedTime) {
            continue;
            } else {
            DVLOG(2) << "Submesh updated";
            }
            entry->last_updated_time = state.lastUpdatedTime;
            submeshes.push_back({
            .type = XR_TYPE_SCENE_SUBMESH_DATA_ANDROID,
            .submeshId = state.submeshId,
            });
            submesh_poses.push_back(state.submeshPoseInBaseSpace);
            }
            Salar Khan . unresolved

            nit: Every push_back call here reallocates the underlying vector data and copies existing data to new location which has average O(n^2) runtime.

            Please bring back the old code of multipass where you first create a needs update std::list<XrUuid> and then reserve the memory for submesh and submesh_poses data, and populate then in a second pass loop. You could also incorporate it with the uuid update below where you create current_frame_uuids in parallel with the needs_update list. Also the more appropriate way to do it is rather than creating a temporary flash_hash_set, you could just mark every entry in submesh_cache_ for delete initially before getting submesh states, and then after getting the submesh states and iterating the states uuid, you update the tag to unmark the ones that still exist in the frame. This way you can delete the entries from the cache that are marked for deletion.

            Open in Gerrit

            Related details

            Attention is currently required from:
            • Adam Ren (xWF)
            • Alexander Cooper
            • Brandon Jones
            • David Li
            • Tomas Oresky
            Submit Requirements:
              • requirement satisfiedCode-Coverage
              • requirement is not satisfiedCode-Owners
              • requirement is not satisfiedCode-Review
              • requirement is not satisfiedNo-Unresolved-Comments
              • requirement is not satisfiedReview-Enforcement
              Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
              Gerrit-MessageType: comment
              Gerrit-Project: chromium/src
              Gerrit-Branch: main
              Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
              Gerrit-Change-Number: 7579917
              Gerrit-PatchSet: 14
              Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
              Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
              Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
              Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
              Gerrit-CC: David Li <dav...@google.com>
              Gerrit-CC: Nihav Jain <niha...@google.com>
              Gerrit-CC: Salar Khan <sala...@google.com>
              Gerrit-CC: Tomas Oresky <tor...@google.com>
              Gerrit-Attention: David Li <dav...@google.com>
              Gerrit-Attention: Adam Ren (xWF) <ada...@google.com>
              Gerrit-Attention: Brandon Jones <baj...@chromium.org>
              Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
              Gerrit-Attention: Tomas Oresky <tor...@google.com>
              Gerrit-Comment-Date: Fri, 13 Mar 2026 15:22:43 +0000
              Gerrit-HasComments: Yes
              Gerrit-Has-Labels: No
              satisfied_requirement
              unsatisfied_requirement
              open
              diffy

              Alexander Cooper (Gerrit)

              unread,
              Mar 13, 2026, 2:51:36 PM (2 days ago) Mar 13
              to Adam Ren (xWF), Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, David Li, AyeAye, feature-v...@chromium.org
              Attention needed from Adam Ren (xWF), Brandon Jones, David Li and Tomas Oresky

              Alexander Cooper added 2 comments

              File device/vr/openxr/android/openxr_mesh_manager_android.cc
              Line 227, Patchset 14 (Latest): std::vector<XrSceneSubmeshDataANDROID> submeshes;
              std::vector<XrPosef> submesh_poses;
              for (const auto& state : states) {
              std::string uuid_str = XrUuidToString(state.submeshId);
              auto* entry = base::FindOrNull(submesh_cache_, uuid_str);
              if (!entry) {
              entry = &submesh_cache_[uuid_str];
              entry->mesh_id = GetOrCreateMeshId(state.submeshId);
              DVLOG(2) << "New submesh detected";
              } else if (entry->last_updated_time == state.lastUpdatedTime) {
              continue;
              } else {
              DVLOG(2) << "Submesh updated";
              }
              entry->last_updated_time = state.lastUpdatedTime;
              submeshes.push_back({
              .type = XR_TYPE_SCENE_SUBMESH_DATA_ANDROID,
              .submeshId = state.submeshId,
              });
              submesh_poses.push_back(state.submeshPoseInBaseSpace);
              }
              Salar Khan . unresolved

              nit: Every push_back call here reallocates the underlying vector data and copies existing data to new location which has average O(n^2) runtime.

              Please bring back the old code of multipass where you first create a needs update std::list<XrUuid> and then reserve the memory for submesh and submesh_poses data, and populate then in a second pass loop. You could also incorporate it with the uuid update below where you create current_frame_uuids in parallel with the needs_update list. Also the more appropriate way to do it is rather than creating a temporary flash_hash_set, you could just mark every entry in submesh_cache_ for delete initially before getting submesh states, and then after getting the submesh states and iterating the states uuid, you update the tag to unmark the ones that still exist in the frame. This way you can delete the entries from the cache that are marked for deletion.

              Alexander Cooper

              I think reading through the list twice is also going to be expensive, especially to solely store an index to do access over. I think it'd be sufficient to do some heuristic to reserve an appropriate amount of space accordingly to reduce the resize calls. (Even if that heuristic is "assume all submeshes will be updated").

              Line 299, Patchset 11: // Sum total vertex and index counts so we can allocate one flat, contiguous
              // buffer per data type rather than one allocation per submesh.
              Alexander Cooper . unresolved

              How many submeshes are we typically seeing? I'm not sure that the likely slight extra overhead is worth the more complicated logic here to make this a flat array instead of e.g. creating an array of a POD struct of vectors for a submesh (And then we could maybe even just make a typemap to abstract the conversion for mojo over the wire).

              Adam Ren (xWF)

              In practice submesh counts range from ~300 to 1000+, growing as the scanned region expands. Salar suggested in an earlier patchset to use flat contiguous buffers specifically to avoid fragmented allocations at that scale, so keeping the flat buffer approach seems justified here.

              Alexander Cooper

              Can you point me to the comment?

              Open in Gerrit

              Related details

              Attention is currently required from:
              • Adam Ren (xWF)
              Gerrit-Attention: Tomas Oresky <tor...@google.com>
              Gerrit-Comment-Date: Fri, 13 Mar 2026 18:51:27 +0000
              Gerrit-HasComments: Yes
              Gerrit-Has-Labels: No
              satisfied_requirement
              unsatisfied_requirement
              open
              diffy

              Adam Ren (xWF) (Gerrit)

              unread,
              Mar 13, 2026, 3:09:03 PM (2 days ago) Mar 13
              to Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
              Attention needed from Alexander Cooper, Brandon Jones, David Li and Tomas Oresky

              Adam Ren (xWF) added 1 comment

              File device/vr/openxr/android/openxr_mesh_manager_android.cc
              Line 299, Patchset 11: // Sum total vertex and index counts so we can allocate one flat, contiguous
              // buffer per data type rather than one allocation per submesh.
              Alexander Cooper . unresolved

              How many submeshes are we typically seeing? I'm not sure that the likely slight extra overhead is worth the more complicated logic here to make this a flat array instead of e.g. creating an array of a POD struct of vectors for a submesh (And then we could maybe even just make a typemap to abstract the conversion for mojo over the wire).

              Adam Ren (xWF)

              In practice submesh counts range from ~300 to 1000+, growing as the scanned region expands. Salar suggested in an earlier patchset to use flat contiguous buffers specifically to avoid fragmented allocations at that scale, so keeping the flat buffer approach seems justified here.

              Alexander Cooper

              Can you point me to the comment?

              Adam Ren (xWF)

              Yeah, it's this one

              #318
              Salar Khan
              Patchset 8
              Mar 04


              nit: fragmented buffers can have performance impact. You could just determine the total vertex and index count, and make each entry in submeshes point to an offset in the buffer.

              Open in Gerrit

              Related details

              Attention is currently required from:
              • Alexander Cooper
              Gerrit-Attention: Brandon Jones <baj...@chromium.org>
              Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
              Gerrit-Attention: Tomas Oresky <tor...@google.com>
              Gerrit-Comment-Date: Fri, 13 Mar 2026 19:08:53 +0000
              Gerrit-HasComments: Yes
              Gerrit-Has-Labels: No
              Comment-In-Reply-To: Adam Ren (xWF) <ada...@google.com>
              Comment-In-Reply-To: Alexander Cooper <alco...@chromium.org>
              satisfied_requirement
              unsatisfied_requirement
              open
              diffy

              Alexander Cooper (Gerrit)

              unread,
              Mar 13, 2026, 3:27:34 PM (2 days ago) Mar 13
              to Adam Ren (xWF), Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, David Li, AyeAye, feature-v...@chromium.org
              Attention needed from Adam Ren (xWF), Brandon Jones, David Li and Tomas Oresky

              Alexander Cooper added 1 comment

              File device/vr/openxr/android/openxr_mesh_manager_android.cc
              Line 299, Patchset 11: // Sum total vertex and index counts so we can allocate one flat, contiguous
              // buffer per data type rather than one allocation per submesh.
              Alexander Cooper . unresolved

              How many submeshes are we typically seeing? I'm not sure that the likely slight extra overhead is worth the more complicated logic here to make this a flat array instead of e.g. creating an array of a POD struct of vectors for a submesh (And then we could maybe even just make a typemap to abstract the conversion for mojo over the wire).

              Adam Ren (xWF)

              In practice submesh counts range from ~300 to 1000+, growing as the scanned region expands. Salar suggested in an earlier patchset to use flat contiguous buffers specifically to avoid fragmented allocations at that scale, so keeping the flat buffer approach seems justified here.

              Alexander Cooper

              Can you point me to the comment?

              Adam Ren (xWF)

              Yeah, it's this one

              #318
              Salar Khan
              Patchset 8
              Mar 04
              nit: fragmented buffers can have performance impact. You could just determine the total vertex and index count, and make each entry in submeshes point to an offset in the buffer.

              Alexander Cooper

              TBH that's a lot weaker of a statement than I would think. Did you see a noticeable performance improvement when you switched to this or was it done in conjunction with a bunch of other changes? How is it consumed on the blink side?

              Open in Gerrit

              Related details

              Attention is currently required from:
              • Adam Ren (xWF)
              Gerrit-Attention: Adam Ren (xWF) <ada...@google.com>
              Gerrit-Attention: Brandon Jones <baj...@chromium.org>
              Gerrit-Attention: Tomas Oresky <tor...@google.com>
              Gerrit-Comment-Date: Fri, 13 Mar 2026 19:27:25 +0000
              satisfied_requirement
              unsatisfied_requirement
              open
              diffy

              Salar Khan (Gerrit)

              unread,
              Mar 13, 2026, 3:46:31 PM (2 days ago) Mar 13
              to Adam Ren (xWF), Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
              Attention needed from Adam Ren (xWF), Brandon Jones, David Li and Tomas Oresky

              Salar Khan added 1 comment

              File device/vr/openxr/android/openxr_mesh_manager_android.cc
              Line 227, Patchset 14 (Latest): std::vector<XrSceneSubmeshDataANDROID> submeshes;
              std::vector<XrPosef> submesh_poses;
              for (const auto& state : states) {
              std::string uuid_str = XrUuidToString(state.submeshId);
              auto* entry = base::FindOrNull(submesh_cache_, uuid_str);
              if (!entry) {
              entry = &submesh_cache_[uuid_str];
              entry->mesh_id = GetOrCreateMeshId(state.submeshId);
              DVLOG(2) << "New submesh detected";
              } else if (entry->last_updated_time == state.lastUpdatedTime) {
              continue;
              } else {
              DVLOG(2) << "Submesh updated";
              }
              entry->last_updated_time = state.lastUpdatedTime;
              submeshes.push_back({
              .type = XR_TYPE_SCENE_SUBMESH_DATA_ANDROID,
              .submeshId = state.submeshId,
              });
              submesh_poses.push_back(state.submeshPoseInBaseSpace);
              }
              Salar Khan . unresolved

              nit: Every push_back call here reallocates the underlying vector data and copies existing data to new location which has average O(n^2) runtime.

              Please bring back the old code of multipass where you first create a needs update std::list<XrUuid> and then reserve the memory for submesh and submesh_poses data, and populate then in a second pass loop. You could also incorporate it with the uuid update below where you create current_frame_uuids in parallel with the needs_update list. Also the more appropriate way to do it is rather than creating a temporary flash_hash_set, you could just mark every entry in submesh_cache_ for delete initially before getting submesh states, and then after getting the submesh states and iterating the states uuid, you update the tag to unmark the ones that still exist in the frame. This way you can delete the entries from the cache that are marked for deletion.

              Alexander Cooper

              I think reading through the list twice is also going to be expensive, especially to solely store an index to do access over. I think it'd be sufficient to do some heuristic to reserve an appropriate amount of space accordingly to reduce the resize calls. (Even if that heuristic is "assume all submeshes will be updated").

              Salar Khan

              Is not the double read that will be expensive, but the heap allocation for every push to std::list. What if we just use std::vector<uint32_t> and reserve it with states.size(), this way we are not wasting too much memory, and we push index of states elements that need new data. And in second pass we iterate this indices vector and use that to get the state which is useful for populating the submeshes and submesh_poses vector?

              reserving states.size() for submeshes and submesh_poses will be very expensive and memory wasting since those structs use more memory than simple uint32_t?

              Btw the maximum number of submeshes this API will provide is 6250.

              Gerrit-Comment-Date: Fri, 13 Mar 2026 19:46:25 +0000
              Gerrit-HasComments: Yes
              Gerrit-Has-Labels: No
              satisfied_requirement
              unsatisfied_requirement
              open
              diffy

              Salar Khan (Gerrit)

              unread,
              Mar 13, 2026, 3:49:49 PM (2 days ago) Mar 13
              to Adam Ren (xWF), Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
              Attention needed from Adam Ren (xWF), Brandon Jones, David Li and Tomas Oresky

              Salar Khan added 1 comment

              File device/vr/openxr/android/openxr_mesh_manager_android.cc
              Line 237, Patchset 14 (Latest): continue;
              Salar Khan . unresolved

              VERY IMPORTANT NOTE:

              This only tells you the last time buffers were updated, not the pose. The poses of literally all of the submeshes in the base space can change when you recenter your device. So you need to give latest poses as well, otherwise pressing recenter will cause all of the updated rendered mesh to shift since they will be using old pose values.

              Gerrit-Comment-Date: Fri, 13 Mar 2026 19:49:45 +0000
              Gerrit-HasComments: Yes
              Gerrit-Has-Labels: No
              satisfied_requirement
              unsatisfied_requirement
              open
              diffy

              Salar Khan (Gerrit)

              unread,
              Mar 13, 2026, 4:17:04 PM (2 days ago) Mar 13
              to Adam Ren (xWF), Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
              Attention needed from Adam Ren (xWF), Brandon Jones, David Li and Tomas Oresky

              Salar Khan added 1 comment

              File device/vr/openxr/android/openxr_mesh_manager_android.cc
              Line 299, Patchset 11: // Sum total vertex and index counts so we can allocate one flat, contiguous
              // buffer per data type rather than one allocation per submesh.
              Alexander Cooper . unresolved

              How many submeshes are we typically seeing? I'm not sure that the likely slight extra overhead is worth the more complicated logic here to make this a flat array instead of e.g. creating an array of a POD struct of vectors for a submesh (And then we could maybe even just make a typemap to abstract the conversion for mojo over the wire).

              Adam Ren (xWF)

              In practice submesh counts range from ~300 to 1000+, growing as the scanned region expands. Salar suggested in an earlier patchset to use flat contiguous buffers specifically to avoid fragmented allocations at that scale, so keeping the flat buffer approach seems justified here.

              Alexander Cooper

              Can you point me to the comment?

              Adam Ren (xWF)

              Yeah, it's this one

              #318
              Salar Khan
              Patchset 8
              Mar 04
              nit: fragmented buffers can have performance impact. You could just determine the total vertex and index count, and make each entry in submeshes point to an offset in the buffer.

              Alexander Cooper

              TBH that's a lot weaker of a statement than I would think. Did you see a noticeable performance improvement when you switched to this or was it done in conjunction with a bunch of other changes? How is it consumed on the blink side?

              Salar Khan

              calling new multiple times is expensive since the OS has to do work on allocating memory. The maximum total vertices this API will provide is 800000 and total indices 2000000 and total submeshes 6250.
              So we can expect maximum 128 vertices and 320 indices per submesh. On average it is much lower though.

              However I do agree there is not a huge difference in performance right now so I wouldn't mind use of separate buffers for each submesh data. But there can be performance bottlenecks in the future when this API is scaled to provide higher level of detail meshes and more submeshes.

              Gerrit-Comment-Date: Fri, 13 Mar 2026 20:16:59 +0000
              Gerrit-HasComments: Yes
              Gerrit-Has-Labels: No
              satisfied_requirement
              unsatisfied_requirement
              open
              diffy

              Alexander Cooper (Gerrit)

              unread,
              Mar 13, 2026, 5:06:10 PM (2 days ago) Mar 13
              to Adam Ren (xWF), Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, David Li, AyeAye, feature-v...@chromium.org
              Attention needed from Adam Ren (xWF), Brandon Jones, David Li and Tomas Oresky

              Alexander Cooper added 6 comments

              File device/vr/openxr/android/openxr_mesh_manager_android.cc
              Line 236, Patchset 14 (Latest): } else if (entry->last_updated_time == state.lastUpdatedTime) {

              continue;
              } else {
              DVLOG(2) << "Submesh updated";
              }
              entry->last_updated_time = state.lastUpdatedTime;
              submeshes.push_back({
              .type = XR_TYPE_SCENE_SUBMESH_DATA_ANDROID,
              .submeshId = state.submeshId,
              });
              Alexander Cooper . unresolved

              NIT: A bit more legible IMO if you don't make an if/else chain with the continue and just make a separate statement check the update time once you've guaranteed entry is valid.

              Line 227, Patchset 14 (Latest): std::vector<XrSceneSubmeshDataANDROID> submeshes;
              std::vector<XrPosef> submesh_poses;
              for (const auto& state : states) {
              std::string uuid_str = XrUuidToString(state.submeshId);
              auto* entry = base::FindOrNull(submesh_cache_, uuid_str);
              if (!entry) {
              entry = &submesh_cache_[uuid_str];
              entry->mesh_id = GetOrCreateMeshId(state.submeshId);
              DVLOG(2) << "New submesh detected";
              } else if (entry->last_updated_time == state.lastUpdatedTime) {
              continue;
              } else {
              DVLOG(2) << "Submesh updated";
              }
              entry->last_updated_time = state.lastUpdatedTime;
              submeshes.push_back({
              .type = XR_TYPE_SCENE_SUBMESH_DATA_ANDROID,
              .submeshId = state.submeshId,
              });
              submesh_poses.push_back(state.submeshPoseInBaseSpace);
              }
              Salar Khan . unresolved

              nit: Every push_back call here reallocates the underlying vector data and copies existing data to new location which has average O(n^2) runtime.

              Please bring back the old code of multipass where you first create a needs update std::list<XrUuid> and then reserve the memory for submesh and submesh_poses data, and populate then in a second pass loop. You could also incorporate it with the uuid update below where you create current_frame_uuids in parallel with the needs_update list. Also the more appropriate way to do it is rather than creating a temporary flash_hash_set, you could just mark every entry in submesh_cache_ for delete initially before getting submesh states, and then after getting the submesh states and iterating the states uuid, you update the tag to unmark the ones that still exist in the frame. This way you can delete the entries from the cache that are marked for deletion.

              Alexander Cooper

              I think reading through the list twice is also going to be expensive, especially to solely store an index to do access over. I think it'd be sufficient to do some heuristic to reserve an appropriate amount of space accordingly to reduce the resize calls. (Even if that heuristic is "assume all submeshes will be updated").

              Alexander Cooper

              TBH, I think we're in the realm of micro-optimizations/trade-offs here. Creating a list of indices leads to random access of the array which can lead to cache misses and spinning processing time, allocating a full object can take up extra memory that we don't need temporarily (and we can always resize down). In this case, I have a slight preference for what's more readable unless we have concrete benchmarks showing one way is better than the other.

              Line 299, Patchset 11: // Sum total vertex and index counts so we can allocate one flat, contiguous
              // buffer per data type rather than one allocation per submesh.
              Alexander Cooper . unresolved

              How many submeshes are we typically seeing? I'm not sure that the likely slight extra overhead is worth the more complicated logic here to make this a flat array instead of e.g. creating an array of a POD struct of vectors for a submesh (And then we could maybe even just make a typemap to abstract the conversion for mojo over the wire).

              Adam Ren (xWF)

              In practice submesh counts range from ~300 to 1000+, growing as the scanned region expands. Salar suggested in an earlier patchset to use flat contiguous buffers specifically to avoid fragmented allocations at that scale, so keeping the flat buffer approach seems justified here.

              Alexander Cooper

              Can you point me to the comment?

              Adam Ren (xWF)

              Yeah, it's this one

              #318
              Salar Khan
              Patchset 8
              Mar 04
              nit: fragmented buffers can have performance impact. You could just determine the total vertex and index count, and make each entry in submeshes point to an offset in the buffer.

              Alexander Cooper

              TBH that's a lot weaker of a statement than I would think. Did you see a noticeable performance improvement when you switched to this or was it done in conjunction with a bunch of other changes? How is it consumed on the blink side?

              Salar Khan

              calling new multiple times is expensive since the OS has to do work on allocating memory. The maximum total vertices this API will provide is 800000 and total indices 2000000 and total submeshes 6250.
              So we can expect maximum 128 vertices and 320 indices per submesh. On average it is much lower though.

              However I do agree there is not a huge difference in performance right now so I wouldn't mind use of separate buffers for each submesh data. But there can be performance bottlenecks in the future when this API is scaled to provide higher level of detail meshes and more submeshes.

              Alexander Cooper

              Ultimately though I haven't double checked, it's going to depend on how this data is surfaced to the page as well. If it's all submeshes/independent objects to the page, then we may as well allocate it as such here and now and only pay that cost once, and in a spot where it's easier to try to optimize.

              File device/vr/openxr/openxr_util.h
              Line 81, Patchset 14 (Latest): XrSceneMeshSemanticLabelANDROID label);
              Alexander Cooper . resolved

              I missed in my earlier comment that these were technically two different enums, in that case it'd be fine to leave them as they were; but I'm not going to ask you to flip back at this point.

              Line 79, Patchset 14 (Latest):#if BUILDFLAG(IS_ANDROID)
              Alexander Cooper . unresolved

              I don't think this needs to be in an `IS_ANDROID` block.

              File device/vr/openxr/scoped_openxr_object.h
              Line 12, Patchset 14 (Latest):#if BUILDFLAG(IS_ANDROID)
              Alexander Cooper . unresolved

              Doesn't need to be in an `is_android` block. the file name "xr_android.h" is because the extensions are named "XR_ANDROID_FOO" etc; not because they are necessarily android specific, but because "Android" is the vendor name that they are proposed under.

              Gerrit-Comment-Date: Fri, 13 Mar 2026 21:06:01 +0000
              Gerrit-HasComments: Yes
              Gerrit-Has-Labels: No
              Comment-In-Reply-To: Adam Ren (xWF) <ada...@google.com>
              satisfied_requirement
              unsatisfied_requirement
              open
              diffy

              Adam Ren (xWF) (Gerrit)

              unread,
              Mar 15, 2026, 3:33:38 AM (21 hours ago) Mar 15
              to Salar Khan, Nihav Jain, Tomas Oresky, Brandon Jones, Code Review Nudger, Alexander Cooper, David Li, AyeAye, feature-v...@chromium.org
              Attention needed from Alexander Cooper, Brandon Jones, David Li, Salar Khan and Tomas Oresky

              Adam Ren (xWF) added 7 comments

              File device/vr/openxr/android/openxr_mesh_manager_android.cc
              Line 168, Patchset 14: XrBoxf bounding_box = {
              Salar Khan . resolved

              bounding box extents refers to the actual width and height of the bounding box, not the half extent (absolute vector from box origin to a corner in box local space).

              So you probably need to multiply kUnboundedSceneHalfExtent with 2. Check the definition of XrBoxf:
              https://registry.khronos.org/OpenXR/specs/1.1/man/html/XrBoxf.html

              Adam Ren (xWF)

              Done

              Line 237, Patchset 14: continue;
              Salar Khan . resolved

              VERY IMPORTANT NOTE:

              This only tells you the last time buffers were updated, not the pose. The poses of literally all of the submeshes in the base space can change when you recenter your device. So you need to give latest poses as well, otherwise pressing recenter will cause all of the updated rendered mesh to shift since they will be using old pose values.

              Adam Ren (xWF)

              Thanks Salar, it has been corrected.

              Line 236, Patchset 14: } else if (entry->last_updated_time == state.lastUpdatedTime) {

              continue;
              } else {
              DVLOG(2) << "Submesh updated";
              }
              entry->last_updated_time = state.lastUpdatedTime;
              submeshes.push_back({
              .type = XR_TYPE_SCENE_SUBMESH_DATA_ANDROID,
              .submeshId = state.submeshId,
              });
              Alexander Cooper . resolved

              NIT: A bit more legible IMO if you don't make an if/else chain with the continue and just make a separate statement check the update time once you've guaranteed entry is valid.

              Adam Ren (xWF)

              Done

              Line 227, Patchset 14: std::vector<XrSceneSubmeshDataANDROID> submeshes;

              std::vector<XrPosef> submesh_poses;
              for (const auto& state : states) {
              std::string uuid_str = XrUuidToString(state.submeshId);
              auto* entry = base::FindOrNull(submesh_cache_, uuid_str);
              if (!entry) {
              entry = &submesh_cache_[uuid_str];
              entry->mesh_id = GetOrCreateMeshId(state.submeshId);
              DVLOG(2) << "New submesh detected";
              } else if (entry->last_updated_time == state.lastUpdatedTime) {
              continue;
              } else {
              DVLOG(2) << "Submesh updated";
              }
              entry->last_updated_time = state.lastUpdatedTime;
              submeshes.push_back({
              .type = XR_TYPE_SCENE_SUBMESH_DATA_ANDROID,
              .submeshId = state.submeshId,
              });
              submesh_poses.push_back(state.submeshPoseInBaseSpace);
              }
              Salar Khan . resolved

              nit: Every push_back call here reallocates the underlying vector data and copies existing data to new location which has average O(n^2) runtime.

              Please bring back the old code of multipass where you first create a needs update std::list<XrUuid> and then reserve the memory for submesh and submesh_poses data, and populate then in a second pass loop. You could also incorporate it with the uuid update below where you create current_frame_uuids in parallel with the needs_update list. Also the more appropriate way to do it is rather than creating a temporary flash_hash_set, you could just mark every entry in submesh_cache_ for delete initially before getting submesh states, and then after getting the submesh states and iterating the states uuid, you update the tag to unmark the ones that still exist in the frame. This way you can delete the entries from the cache that are marked for deletion.

              Alexander Cooper

              I think reading through the list twice is also going to be expensive, especially to solely store an index to do access over. I think it'd be sufficient to do some heuristic to reserve an appropriate amount of space accordingly to reduce the resize calls. (Even if that heuristic is "assume all submeshes will be updated").

              Alexander Cooper

              TBH, I think we're in the realm of micro-optimizations/trade-offs here. Creating a list of indices leads to random access of the array which can lead to cache misses and spinning processing time, allocating a full object can take up extra memory that we don't need temporarily (and we can always resize down). In this case, I have a slight preference for what's more readable unless we have concrete benchmarks showing one way is better than the other.

              Adam Ren (xWF)

              Thanks for the detailed discussion. I don’t see a clear advantage of one approach over the other in the on-device test, so I’ll keep the current design for now for better readability

              Line 299, Patchset 11: // Sum total vertex and index counts so we can allocate one flat, contiguous
              // buffer per data type rather than one allocation per submesh.
              Alexander Cooper . resolved

              How many submeshes are we typically seeing? I'm not sure that the likely slight extra overhead is worth the more complicated logic here to make this a flat array instead of e.g. creating an array of a POD struct of vectors for a submesh (And then we could maybe even just make a typemap to abstract the conversion for mojo over the wire).

              Adam Ren (xWF)

              In practice submesh counts range from ~300 to 1000+, growing as the scanned region expands. Salar suggested in an earlier patchset to use flat contiguous buffers specifically to avoid fragmented allocations at that scale, so keeping the flat buffer approach seems justified here.

              Alexander Cooper

              Can you point me to the comment?

              Adam Ren (xWF)

              Yeah, it's this one

              #318
              Salar Khan
              Patchset 8
              Mar 04
              nit: fragmented buffers can have performance impact. You could just determine the total vertex and index count, and make each entry in submeshes point to an offset in the buffer.

              Alexander Cooper

              TBH that's a lot weaker of a statement than I would think. Did you see a noticeable performance improvement when you switched to this or was it done in conjunction with a bunch of other changes? How is it consumed on the blink side?

              Salar Khan

              calling new multiple times is expensive since the OS has to do work on allocating memory. The maximum total vertices this API will provide is 800000 and total indices 2000000 and total submeshes 6250.
              So we can expect maximum 128 vertices and 320 indices per submesh. On average it is much lower though.

              However I do agree there is not a huge difference in performance right now so I wouldn't mind use of separate buffers for each submesh data. But there can be performance bottlenecks in the future when this API is scaled to provide higher level of detail meshes and more submeshes.

              Alexander Cooper

              Ultimately though I haven't double checked, it's going to depend on how this data is surfaced to the page as well. If it's all submeshes/independent objects to the page, then we may as well allocate it as such here and now and only pay that cost once, and in a spot where it's easier to try to optimize.

              Adam Ren (xWF)

              Thanks for the discussion. I’ll keep the current flat buffer design for generalization and to scale better if the API later provides more submeshes or higher LOD meshes. The Blink side already consumes this as per-submesh XRMeshData objects after conversion.

              File device/vr/openxr/openxr_util.h
              Line 79, Patchset 14:#if BUILDFLAG(IS_ANDROID)
              Alexander Cooper . resolved

              I don't think this needs to be in an `IS_ANDROID` block.

              Adam Ren (xWF)

              Done

              File device/vr/openxr/scoped_openxr_object.h
              Line 12, Patchset 14:#if BUILDFLAG(IS_ANDROID)
              Alexander Cooper . resolved

              Doesn't need to be in an `is_android` block. the file name "xr_android.h" is because the extensions are named "XR_ANDROID_FOO" etc; not because they are necessarily android specific, but because "Android" is the vendor name that they are proposed under.

              Adam Ren (xWF)

              Thanks for the explanation. It has been removed.

              Open in Gerrit

              Related details

              Attention is currently required from:
              • Alexander Cooper
              • Brandon Jones
              • David Li
              • Salar Khan
              • Tomas Oresky
              Submit Requirements:
                • requirement satisfiedCode-Coverage
                • requirement is not satisfiedCode-Owners
                • requirement is not satisfiedCode-Review
                • requirement is not satisfiedReview-Enforcement
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: comment
                Gerrit-Project: chromium/src
                Gerrit-Branch: main
                Gerrit-Change-Id: If8d5399d279a9404c2f13722e88c3eca00f37c71
                Gerrit-Change-Number: 7579917
                Gerrit-PatchSet: 17
                Gerrit-Owner: Adam Ren (xWF) <ada...@google.com>
                Gerrit-Reviewer: Alexander Cooper <alco...@chromium.org>
                Gerrit-Reviewer: Brandon Jones <baj...@chromium.org>
                Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
                Gerrit-CC: David Li <dav...@google.com>
                Gerrit-CC: Nihav Jain <niha...@google.com>
                Gerrit-CC: Salar Khan <sala...@google.com>
                Gerrit-CC: Tomas Oresky <tor...@google.com>
                Gerrit-Attention: David Li <dav...@google.com>
                Gerrit-Attention: Salar Khan <sala...@google.com>
                Gerrit-Attention: Brandon Jones <baj...@chromium.org>
                Gerrit-Attention: Alexander Cooper <alco...@chromium.org>
                Gerrit-Attention: Tomas Oresky <tor...@google.com>
                Gerrit-Comment-Date: Sun, 15 Mar 2026 07:33:28 +0000
                satisfied_requirement
                unsatisfied_requirement
                open
                diffy
                Reply all
                Reply to author
                Forward
                0 new messages