chromium/device/vr/openxr/openxr_platform_helper.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/openxr_platform_helper.h"

#include <memory>
#include <set>
#include <utility>

#include "base/containers/contains.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/version.h"
#include "build/build_config.h"
#include "components/version_info/version_info.h"
#include "device/vr/openxr/openxr_api_wrapper.h"
#include "device/vr/openxr/openxr_extension_handler_factories.h"
#include "device/vr/openxr/openxr_extension_handler_factory.h"
#include "device/vr/openxr/openxr_extension_helper.h"
#include "device/vr/openxr/openxr_graphics_binding.h"
#include "device/vr/openxr/openxr_interaction_profiles.h"
#include "device/vr/openxr/openxr_util.h"

namespace device {

OpenXrPlatformHelper::OpenXrPlatformHelper() = default;
OpenXrPlatformHelper::~OpenXrPlatformHelper() = default;

bool OpenXrPlatformHelper::EnsureInitialized() {
  if (initialized_) {
    return true;
  }

  if (!Initialize()) {
    return false;
  }

  // The subclass may have already created the extension enumeration.
  if (!extension_enumeration_) {
    extension_enumeration_ = std::make_unique<OpenXrExtensionEnumeration>();
  }

  initialized_ = true;
  return true;
}

// Gets the ExtensionEnumeration which is the list of extensions supported by
// the platform.
const OpenXrExtensionEnumeration*
OpenXrPlatformHelper::GetExtensionEnumeration() const {
  CHECK(initialized_);
  return extension_enumeration_.get();
}

XrResult OpenXrPlatformHelper::CreateInstance(XrInstance* instance) {
  return CreateInstance(instance, nullptr);
}

void OpenXrPlatformHelper::CreateInstanceWithCreateInfo(
    std::optional<OpenXrCreateInfo> create_info,
    CreateInstanceCallback instance_ready_callback,
    PlatormInitiatedShutdownCallback shutdown_callback) {
  DVLOG(1) << __func__;
  CHECK(initialized_);

  if (create_info.has_value()) {
    auto create_info_result_callback = base::BindOnce(
        &OpenXrPlatformHelper::OnPlatformCreateInfoResult,
        base::Unretained(this), std::move(instance_ready_callback));
    GetPlatformCreateInfo(create_info.value(),
                          std::move(create_info_result_callback),
                          std::move(shutdown_callback));
  } else {
    OnPlatformCreateInfoResult(std::move(instance_ready_callback), nullptr);
  }
}

void OpenXrPlatformHelper::OnPlatformCreateInfoResult(
    CreateInstanceCallback callback,
    void* instance_create_info) {
  DVLOG(1) << __func__;
  XrInstance instance;
  XrResult result = CreateInstance(&instance, instance_create_info);
  std::move(callback).Run(result, instance);
}

XrResult OpenXrPlatformHelper::CreateInstance(XrInstance* instance,
                                              void* create_info) {
  DVLOG(1) << __func__;
  CHECK(initialized_);
  CHECK(xr_instance_ == XR_NULL_HANDLE)
      << "Each Process is only allowed one XrInstance at a time";
  XrInstanceCreateInfo instance_create_info = {XR_TYPE_INSTANCE_CREATE_INFO};

  std::string application_name =
      base::StrCat({version_info::GetProductName(), " ",
                    version_info::GetMajorVersionNumber()});
  size_t dest_size =
      std::size(instance_create_info.applicationInfo.applicationName);
  size_t src_size =
      base::strlcpy(instance_create_info.applicationInfo.applicationName,
                    application_name.c_str(), dest_size);
  DCHECK_LT(src_size, dest_size);

  base::Version version = version_info::GetVersion();
  DCHECK_EQ(version.components().size(), 4uLL);
  uint32_t build = version.components()[2];

  // application version will be the build number of each vendor
  instance_create_info.applicationInfo.applicationVersion = build;

  dest_size = std::size(instance_create_info.applicationInfo.engineName);
  src_size = base::strlcpy(instance_create_info.applicationInfo.engineName,
                           "Chromium", dest_size);
  DCHECK_LT(src_size, dest_size);

  // engine version should be the build number of chromium
  instance_create_info.applicationInfo.engineVersion = build;

  instance_create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;

  // xrCreateInstance validates the list of extensions and returns
  // XR_ERROR_EXTENSION_NOT_PRESENT if an extension is not supported,
  // so we don't need to call xrEnumerateInstanceExtensionProperties
  // to validate these extensions.
  std::vector<const char*> extensions;
  GetRequiredExtensions(extensions);
  OpenXrGraphicsBinding::GetRequiredExtensions(extensions);

  // Create a local variable for the lambda to capture. This is okay since we
  // aren't passing the lambda anywhere.
  auto* extension_enumeration = GetExtensionEnumeration();
  auto EnableExtensionIfSupported = [&extension_enumeration,
                                     &extensions](const char* extension) {
    if (extension_enumeration->ExtensionSupported(extension)) {
      extensions.push_back(extension);
    }
  };

  std::set<std::string> handled_extensions;
  for (const auto* factory : GetExtensionHandlerFactories()) {
    auto factory_extensions = factory->GetRequestedExtensions();
    handled_extensions.insert(factory_extensions.begin(),
                              factory_extensions.end());
  }

  // Enable the required extensions for any controllers that both we and the
  // runtime support.
  for (const auto& interaction_profile :
       GetOpenXrControllerInteractionProfiles()) {
    if (!interaction_profile.required_extension.empty()) {
      handled_extensions.insert(interaction_profile.required_extension);
    }
  }

  for (const auto& extension : handled_extensions) {
    EnableExtensionIfSupported(extension.c_str());
  }

  EnableExtensionIfSupported(
      XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME);
  if (GetExtensionEnumeration()->ExtensionSupported(
          XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME)) {
    EnableExtensionIfSupported(XR_MSFT_FIRST_PERSON_OBSERVER_EXTENSION_NAME);
  }

  // Enable any other platform-specific extensions that we don't just enable or
  // try to enable across the board.
  for (const auto* extension : GetOptionalExtensions()) {
    EnableExtensionIfSupported(extension);
  }

  instance_create_info.enabledExtensionCount =
      static_cast<uint32_t>(extensions.size());
  instance_create_info.enabledExtensionNames = extensions.data();

#if BUILDFLAG(IS_ANDROID)
  if (create_info == nullptr) {
    LOG(ERROR) << "Android was missing CreateInfo";
  }
#endif

  instance_create_info.next = create_info;

  XrResult result = xrCreateInstance(&instance_create_info, instance);
  if (XR_SUCCEEDED(result)) {
    xr_instance_ = *instance;
    UpdateExtensionFactorySupport();
  } else {
    DLOG(ERROR) << __func__ << " Failed to create instance: " << result;
    OnInstanceCreateFailure();
  }

  return result;
}

void OpenXrPlatformHelper::UpdateExtensionFactorySupport() {
  CHECK(xr_instance_ != XR_NULL_HANDLE);
  auto* extension_enumeration = GetExtensionEnumeration();

  // If we can't get the System, then the worst case here is that any extensions
  // that need XrSystemProperties will just stay disabled and that can be
  // handled later.
  XrSystemId system;
  OpenXrApiWrapper::GetSystem(xr_instance_, &system);

  for (auto* extension_factory : GetExtensionHandlerFactories()) {
    extension_factory->ProcessSystemProperties(extension_enumeration,
                                               xr_instance_, system);
  }
}

XrResult OpenXrPlatformHelper::DestroyInstance(XrInstance& instance) {
  CHECK(instance != XR_NULL_HANDLE);
  CHECK(instance == xr_instance_);
  XrResult result = xrDestroyInstance(instance);
  if (XR_SUCCEEDED(result)) {
    instance = XR_NULL_HANDLE;
    xr_instance_ = XR_NULL_HANDLE;
  }
  return result;
}

bool OpenXrPlatformHelper::IsArBlendModeSupported(XrInstance instance) {
  XrSystemId system;

  if (XR_FAILED(OpenXrApiWrapper::GetSystem(instance, &system))) {
    return false;
  }

  std::vector<XrEnvironmentBlendMode> environment_blend_modes =
      OpenXrApiWrapper::GetSupportedBlendModes(instance, system);

  return base::Contains(environment_blend_modes,
                        XR_ENVIRONMENT_BLEND_MODE_ADDITIVE) ||
         base::Contains(environment_blend_modes,
                        XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND);
}

}  // namespace device