chromium/device/vr/openxr/android/openxr_stage_bounds_provider_android.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/openxr/android/openxr_stage_bounds_provider_android.h"

#include <set>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/no_destructor.h"
#include "base/ranges/algorithm.h"
#include "device/vr/openxr/openxr_extension_helper.h"
#include "device/vr/public/mojom/xr_session.mojom-shared.h"
#include "third_party/openxr/dev/xr_android.h"
#include "ui/gfx/geometry/point3_f.h"

namespace device {

OpenXrStageBoundsProviderAndroid::OpenXrStageBoundsProviderAndroid(
    const OpenXrExtensionHelper& extension_helper,
    XrSession session)
    : extension_helper_(extension_helper), session_(session) {}

OpenXrStageBoundsProviderAndroid::~OpenXrStageBoundsProviderAndroid() = default;

std::vector<gfx::Point3F> OpenXrStageBoundsProviderAndroid::GetStageBounds() {
  uint32_t vertices_count_output = 0;
  XrResult result = extension_helper_->ExtensionMethods()
                        .xrGetReferenceSpaceBoundsPolygonANDROID(
                            session_, XR_REFERENCE_SPACE_TYPE_STAGE, 0,
                            &vertices_count_output, nullptr);

  if (!XR_SUCCEEDED(result) || vertices_count_output == 0) {
    return {};
  }

  std::vector<XrVector2f> boundary_vertices(vertices_count_output);
  result =
      extension_helper_->ExtensionMethods()
          .xrGetReferenceSpaceBoundsPolygonANDROID(
              session_, XR_REFERENCE_SPACE_TYPE_STAGE, boundary_vertices.size(),
              &vertices_count_output, boundary_vertices.data());

  // In the (unlikely) event that the array is differently sized, there should
  // be a pending XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING which will
  // cause us to update the bounds. They'll be stale for at most a frame.
  if (!XR_SUCCEEDED(result) ||
      boundary_vertices.size() != vertices_count_output) {
    return {};
  }

  // `xrGetReferenceSpaceBoundsPolygonANDROID` returns the bounds in counter-
  // clockwise order, but we need them in clock-wise order, so iterate through
  // the vector backwards.
  std::vector<gfx::Point3F> stage_bounds;
  stage_bounds.reserve(vertices_count_output);
  base::ranges::transform(boundary_vertices.rbegin(), boundary_vertices.rend(),
                          std::back_inserter(stage_bounds),
                          [](const XrVector2f& xr_coord) {
                            return gfx::Point3F(xr_coord.x, 0.0, xr_coord.y);
                          });

  return stage_bounds;
}

OpenXrStageBoundsProviderAndroidFactory::
    OpenXrStageBoundsProviderAndroidFactory() = default;
OpenXrStageBoundsProviderAndroidFactory::
    ~OpenXrStageBoundsProviderAndroidFactory() = default;

const base::flat_set<std::string_view>&
OpenXrStageBoundsProviderAndroidFactory::GetRequestedExtensions() const {
  static base::NoDestructor<base::flat_set<std::string_view>> kExtensions(
      {XR_ANDROID_REFERENCE_SPACE_BOUNDS_POLYGON_EXTENSION_NAME});
  return *kExtensions;
}

std::set<device::mojom::XRSessionFeature>
OpenXrStageBoundsProviderAndroidFactory::GetSupportedFeatures(
    const OpenXrExtensionEnumeration* extension_enum) const {
  if (!IsEnabled(extension_enum)) {
    return {};
  }

  return {device::mojom::XRSessionFeature::REF_SPACE_BOUNDED_FLOOR};
}

std::unique_ptr<OpenXrStageBoundsProvider>
OpenXrStageBoundsProviderAndroidFactory::CreateStageBoundsProvider(
    const OpenXrExtensionHelper& extension_helper,
    XrSession session) const {
  bool is_supported = IsEnabled(extension_helper.ExtensionEnumeration());
  DVLOG(2) << __func__ << " is_supported=" << is_supported;
  if (is_supported) {
    return std::make_unique<OpenXrStageBoundsProviderAndroid>(extension_helper,
                                                              session);
  }

  return nullptr;
}

}  // namespace device