blob: 376085264362bbfe1cf2dd1ec30f670e5af913db [file] [log] [blame]
// Copyright 2020 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/openxr_extension_helper.h"
#include <algorithm>
#include <memory>
#include "base/compiler_specific.h"
#include "base/containers/contains.h"
#include "base/dcheck_is_on.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "device/vr/openxr/openxr_extension_handler_factories.h"
#include "device/vr/openxr/openxr_extension_handler_factory.h"
#include "device/vr/openxr/openxr_platform_helper.h"
#include "device/vr/public/mojom/xr_session.mojom.h"
namespace device {
namespace {
// A helper macro to avoid the repetitive boilerplate required to load a
// function from OpenXR. Note that these are typically extension functions and
// that the name of the method is repeated several times throughout the
// declaration. For a function named "xrCreateHandTrackerEXT" this will exapnd
// to the following:
// std::ignore = xrGetInstanceProcAddr(
// instance, "xrCreateHandTrackerEXT",
// reinterpret_cast<PFN_xrVoidFunction*>(
// const_cast<PFN_xrCreateHandTrackerEXT*>(
// &extension_methods_.xrCreateHandTrackerEXT)));
#define OPENXR_LOAD_FN(name) \
std::ignore = xrGetInstanceProcAddr( \
instance, #name, \
reinterpret_cast<PFN_xrVoidFunction*>( \
const_cast<PFN_##name*>(&extension_methods_.name)))
// The ExtensionHandlers are all created/checked in very similar ways, with the
// only difference really being what method they need called on the
// OpenXrExtensionHandler factory to be created (and thus the arguments that
// they need to have supplied). This helper abstracts the boilerplate so that
// we can simply use a factory lambda which has that knowledge, and this helper
// then takes care of the rest of the boilerplate.
template <typename T, typename FunctionType>
std::unique_ptr<T> CreateExtensionHandler(
FunctionType fn) {
for (const auto* factory : GetExtensionHandlerFactories()) {
CHECK(factory);
if (factory->IsEnabled()) {
auto ret = fn(*factory);
if (ret != nullptr) {
return ret;
}
}
}
return nullptr;
}
bool IsSceneUnderstandingFeature(mojom::XRSessionFeature feature) {
return feature == device::mojom::XRSessionFeature::ANCHORS ||
feature == device::mojom::XRSessionFeature::HIT_TEST ||
feature == device::mojom::XRSessionFeature::PLANE_DETECTION;
}
} // namespace
OpenXrExtensionMethods::OpenXrExtensionMethods() = default;
OpenXrExtensionMethods::~OpenXrExtensionMethods() = default;
OpenXrExtensionEnumeration::OpenXrExtensionEnumeration() {
uint32_t extension_count;
if (XR_SUCCEEDED(xrEnumerateInstanceExtensionProperties(
nullptr, 0, &extension_count, nullptr))) {
extension_properties_.resize(extension_count,
{XR_TYPE_EXTENSION_PROPERTIES});
xrEnumerateInstanceExtensionProperties(nullptr, extension_count,
&extension_count,
extension_properties_.data());
}
if constexpr (DCHECK_IS_ON()) {
DVLOG(1) << __func__ << ": Supported Extensions Begin";
for (const auto& extension : extension_properties_) {
DVLOG(1) << __func__ << ": " << extension.extensionName
<< " version=" << extension.extensionVersion;
}
DVLOG(1) << __func__ << ": Supported Extensions End";
}
}
OpenXrExtensionEnumeration::~OpenXrExtensionEnumeration() = default;
bool OpenXrExtensionEnumeration::ExtensionSupported(
const char* extension_name) const {
return std::ranges::any_of(
extension_properties_,
[&extension_name](const XrExtensionProperties& properties) {
return UNSAFE_TODO(strcmp(properties.extensionName, extension_name)) ==
0;
});
}
// static
std::vector<const char*>
OpenXrExtensionHelper::GetRequiredExtensionsForLayers() {
return {XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME,
XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME,
XR_KHR_COMPOSITION_LAYER_CUBE_EXTENSION_NAME};
}
OpenXrExtensionHelper::~OpenXrExtensionHelper() = default;
OpenXrExtensionHelper::OpenXrExtensionHelper(
XrInstance instance,
const OpenXrExtensionEnumeration* const extension_enumeration)
: extension_enumeration_(extension_enumeration) {
// Failure to query a method results in a nullptr
// General methods
OPENXR_LOAD_FN(xrPollFutureEXT);
// Hand tracking methods
OPENXR_LOAD_FN(xrCreateHandTrackerEXT);
OPENXR_LOAD_FN(xrDestroyHandTrackerEXT);
OPENXR_LOAD_FN(xrLocateHandJointsEXT);
// Anchors methods
OPENXR_LOAD_FN(xrCreateSpatialAnchorMSFT);
OPENXR_LOAD_FN(xrDestroySpatialAnchorMSFT);
OPENXR_LOAD_FN(xrCreateSpatialAnchorSpaceMSFT);
// MSFT Scene Understanding Methods
OPENXR_LOAD_FN(xrEnumerateSceneComputeFeaturesMSFT);
OPENXR_LOAD_FN(xrCreateSceneObserverMSFT);
OPENXR_LOAD_FN(xrDestroySceneObserverMSFT);
OPENXR_LOAD_FN(xrCreateSceneMSFT);
OPENXR_LOAD_FN(xrDestroySceneMSFT);
OPENXR_LOAD_FN(xrComputeNewSceneMSFT);
OPENXR_LOAD_FN(xrGetSceneComputeStateMSFT);
OPENXR_LOAD_FN(xrGetSceneComponentsMSFT);
OPENXR_LOAD_FN(xrLocateSceneComponentsMSFT);
OPENXR_LOAD_FN(xrGetSceneMeshBuffersMSFT);
// Spatial Entities
OPENXR_LOAD_FN(xrCreateSpatialContextAsyncEXT);
OPENXR_LOAD_FN(xrCreateSpatialContextCompleteEXT);
OPENXR_LOAD_FN(xrCreateSpatialDiscoverySnapshotAsyncEXT);
OPENXR_LOAD_FN(xrCreateSpatialDiscoverySnapshotCompleteEXT);
OPENXR_LOAD_FN(xrCreateSpatialUpdateSnapshotEXT);
OPENXR_LOAD_FN(xrDestroySpatialContextEXT);
OPENXR_LOAD_FN(xrDestroySpatialEntityEXT);
OPENXR_LOAD_FN(xrDestroySpatialSnapshotEXT);
OPENXR_LOAD_FN(xrEnumerateSpatialCapabilitiesEXT);
OPENXR_LOAD_FN(xrEnumerateSpatialCapabilityComponentTypesEXT);
OPENXR_LOAD_FN(xrQuerySpatialComponentDataEXT);
// Spatial Anchors
OPENXR_LOAD_FN(xrCreateSpatialAnchorEXT);
// Visibility Mask
OPENXR_LOAD_FN(xrGetVisibilityMaskKHR);
#if BUILDFLAG(IS_WIN)
OPENXR_LOAD_FN(xrConvertWin32PerformanceCounterToTimeKHR);
#endif
#if BUILDFLAG(IS_ANDROID)
OPENXR_LOAD_FN(xrCreateTrackableTrackerANDROID);
OPENXR_LOAD_FN(xrDestroyTrackableTrackerANDROID);
OPENXR_LOAD_FN(xrRaycastANDROID);
OPENXR_LOAD_FN(xrCreateAnchorSpaceANDROID);
OPENXR_LOAD_FN(xrCreateLightEstimatorANDROID);
OPENXR_LOAD_FN(xrDestroyLightEstimatorANDROID);
OPENXR_LOAD_FN(xrGetLightEstimateANDROID);
OPENXR_LOAD_FN(xrCreateDepthSwapchainANDROID);
OPENXR_LOAD_FN(xrDestroyDepthSwapchainANDROID);
OPENXR_LOAD_FN(xrEnumerateDepthSwapchainImagesANDROID);
OPENXR_LOAD_FN(xrEnumerateDepthResolutionsANDROID);
OPENXR_LOAD_FN(xrAcquireDepthSwapchainImagesANDROID);
#endif
}
bool OpenXrExtensionHelper::IsFeatureSupported(
device::mojom::XRSessionFeature feature) const {
switch (feature) {
case device::mojom::XRSessionFeature::ANCHORS:
case device::mojom::XRSessionFeature::DEPTH:
case device::mojom::XRSessionFeature::HAND_INPUT:
case device::mojom::XRSessionFeature::HIT_TEST:
case device::mojom::XRSessionFeature::LIGHT_ESTIMATION:
case device::mojom::XRSessionFeature::PLANE_DETECTION:
case device::mojom::XRSessionFeature::REF_SPACE_UNBOUNDED:
return std::ranges::any_of(
GetExtensionHandlerFactories(),
[feature](const auto* extension_handler_factory) {
return base::Contains(
extension_handler_factory->GetSupportedFeatures(), feature);
});
case device::mojom::XRSessionFeature::SECONDARY_VIEWS:
return IsExtensionSupported(
XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME);
case device::mojom::XRSessionFeature::LAYERS:
return std::ranges::all_of(GetRequiredExtensionsForLayers(),
[this](const char* extension) {
return IsExtensionSupported(extension);
});
default:
// By default we assume a feature doesn't need to be supported by an
// extension unless customized above.
return true;
}
}
bool OpenXrExtensionHelper::IsExtensionSupported(
const char* extension_name) const {
return extension_enumeration_->ExtensionSupported(extension_name);
}
std::unique_ptr<OpenXrDepthSensor> OpenXrExtensionHelper::CreateDepthSensor(
XrSession session,
XrSpace base_space,
const mojom::XRDepthOptions& depth_options) const {
return CreateExtensionHandler<OpenXrDepthSensor>(
[this, session, base_space,
depth_options](const OpenXrExtensionHandlerFactory& factory)
-> std::unique_ptr<OpenXrDepthSensor> {
auto sensor = factory.CreateDepthSensor(*this, session, base_space,
depth_options);
if (sensor && XR_SUCCEEDED(sensor->Initialize())) {
return sensor;
}
return nullptr;
});
}
std::unique_ptr<OpenXrHandTracker> OpenXrExtensionHelper::CreateHandTracker(
XrSession session,
OpenXrHandednessType handedness) const {
return CreateExtensionHandler<OpenXrHandTracker>(
[this, session,
handedness](const OpenXrExtensionHandlerFactory& factory) {
return factory.CreateHandTracker(*this, session, handedness);
});
}
std::unique_ptr<OpenXrLightEstimator>
OpenXrExtensionHelper::CreateLightEstimator(XrSession session,
XrSpace base_space) const {
return CreateExtensionHandler<OpenXrLightEstimator>(
[this, session,
base_space](const OpenXrExtensionHandlerFactory& factory) {
return factory.CreateLightEstimator(*this, session, base_space);
});
}
std::unique_ptr<OpenXRSceneUnderstandingManager>
OpenXrExtensionHelper::CreateSceneUnderstandingManager(
OpenXrApiWrapper* openxr,
XrSpace base_space,
const std::vector<mojom::XRSessionFeature>& required_features,
const std::vector<mojom::XRSessionFeature>& optional_features) const {
DVLOG(1) << __func__;
const OpenXrExtensionHandlerFactory* best_factory = nullptr;
size_t best_supported_optional_features_count = 0;
for (const auto* factory : GetExtensionHandlerFactories()) {
CHECK(factory);
if (!factory->IsEnabled()) {
continue;
}
const auto supported_features = factory->GetSupportedFeatures();
auto supported_function =
[&supported_features](mojom::XRSessionFeature feature) {
return IsSceneUnderstandingFeature(feature) &&
base::Contains(supported_features, feature);
};
// Get the count of how many required and optional features are scene
// understanding features.
size_t required_features_requested_count =
std::ranges::count_if(required_features, &IsSceneUnderstandingFeature);
size_t optional_features_requested_count =
std::ranges::count_if(optional_features, &IsSceneUnderstandingFeature);
CHECK(required_features_requested_count > 0 ||
optional_features_requested_count > 0)
<< "Requested a SceneUnderstandingManager, but no "
"SceneUnderstandingManager features are requested";
// Now, see how many of our supported features are scene understanding
// features.
size_t supported_required_features_count =
std::ranges::count_if(required_features, supported_function);
size_t supported_optional_features_count =
std::ranges::count_if(optional_features, supported_function);
// If all required features are not supported, we can't use this factory.
if (supported_required_features_count !=
required_features_requested_count) {
continue;
}
// If this SceneUnderstandingManager supports all of the optional features,
// then use it.
if (supported_optional_features_count ==
optional_features_requested_count) {
return factory->CreateSceneUnderstandingManager(*this, openxr,
base_space);
}
// Otherwise, if this factory supports more optional features than our
// current best choice, update the best count/factory and keep going.
if (supported_optional_features_count >
best_supported_optional_features_count) {
best_supported_optional_features_count =
supported_required_features_count;
best_factory = factory;
}
}
std::unique_ptr<OpenXRSceneUnderstandingManager> manager;
if (best_factory) {
manager = best_factory->CreateSceneUnderstandingManager(*this, openxr,
base_space);
}
UMA_HISTOGRAM_ENUMERATION("XR.OpenXR.SceneUnderstandingManagerType",
manager
? manager->GetType()
: OpenXrSceneUnderstandingManagerType::kNone);
return manager;
}
std::unique_ptr<OpenXrStageBoundsProvider>
OpenXrExtensionHelper::CreateStageBoundsProvider(XrSession session) const {
return CreateExtensionHandler<OpenXrStageBoundsProvider>(
[session](const OpenXrExtensionHandlerFactory& factory) {
return factory.CreateStageBoundsProvider(session);
});
}
std::unique_ptr<OpenXrUnboundedSpaceProvider>
OpenXrExtensionHelper::CreateUnboundedSpaceProvider() const {
return CreateExtensionHandler<OpenXrUnboundedSpaceProvider>(
[](const OpenXrExtensionHandlerFactory& factory) {
return factory.CreateUnboundedSpaceProvider();
});
}
} // namespace device