chromium/device/vr/openxr/test/fake_openxr_impl_api.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "base/containers/contains.h"
#include "device/vr/openxr/openxr_util.h"
#include "device/vr/openxr/test/openxr_negotiate.h"
#include "device/vr/openxr/test/openxr_test_helper.h"

#if BUILDFLAG(IS_WIN)
#include <wrl.h>
#endif

namespace {
// Global test helper that communicates with the test and contains the mock
// OpenXR runtime state/properties. A reference to this is returned as the
// instance handle through xrCreateInstance.
OpenXrTestHelper g_test_helper;
}  // namespace

// Extension methods

// Mock implementations of openxr runtime.dll APIs.
// Please add new APIs in alphabetical order.

XrResult xrAcquireSwapchainImage(
    XrSwapchain swapchain,
    const XrSwapchainImageAcquireInfo* acquire_info,
    uint32_t* index) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSwapchain(swapchain));
  RETURN_IF(acquire_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainImageAcquireInfo is nullptr");
  RETURN_IF(acquire_info->type != XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO,
            XR_ERROR_VALIDATION_FAILURE,
            "xrAcquireSwapchainImage type invalid");
  RETURN_IF(acquire_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "xrAcquireSwapchainImage next not nullptr");

  RETURN_IF(index == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "xrAcquireSwapchainImage index is nullptr");
  *index = g_test_helper.NextSwapchainImageIndex();

  return XR_SUCCESS;
}

XrResult xrAttachSessionActionSets(
    XrSession session,
    const XrSessionActionSetsAttachInfo* attach_info) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(attach_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSessionActionSetsAttachInfo is nullptr");
  RETURN_IF_XR_FAILED(g_test_helper.AttachActionSets(*attach_info));

  return XR_SUCCESS;
}

XrResult xrBeginFrame(XrSession session,
                      const XrFrameBeginInfo* frame_begin_info) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(frame_begin_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrFrameBeginInfo is nullptr");
  RETURN_IF(frame_begin_info->type != XR_TYPE_FRAME_BEGIN_INFO,
            XR_ERROR_VALIDATION_FAILURE, "XrFrameBeginInfo type invalid");
  RETURN_IF(frame_begin_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrFrameBeginInfo next is not nullptr");
  return g_test_helper.BeginFrame();
}

XrResult xrBeginSession(XrSession session,
                        const XrSessionBeginInfo* begin_info) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(begin_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSessionBeginInfo is nullptr");
  RETURN_IF(begin_info->type != XR_TYPE_SESSION_BEGIN_INFO,
            XR_ERROR_VALIDATION_FAILURE, "XrSessionBeginInfo type invalid");

  // Create a list of all the view configurations requested by the client.
  std::vector<XrViewConfigurationType> view_configs = {
      begin_info->primaryViewConfigurationType};
  if (begin_info->next != nullptr) {
    // Secondary views are requested if a next pointer is specified.
    const XrSecondaryViewConfigurationSessionBeginInfoMSFT* second_begin_info =
        reinterpret_cast<
            const XrSecondaryViewConfigurationSessionBeginInfoMSFT*>(
            begin_info->next);
    RETURN_IF(second_begin_info->type !=
                  XR_TYPE_SECONDARY_VIEW_CONFIGURATION_SESSION_BEGIN_INFO_MSFT,
              XR_ERROR_VALIDATION_FAILURE,
              "XrSecondaryViewConfigurationSessionBeginInfoMSFT type invalid");
    RETURN_IF(
        second_begin_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
        "XrSecondaryViewConfigurationSessionBeginInfoMSFT next is not nullptr");

    for (uint32_t i = 0; i < second_begin_info->viewConfigurationCount; i++) {
      view_configs.push_back(
          second_begin_info->enabledViewConfigurationTypes[i]);
    }
  }

  RETURN_IF_XR_FAILED(g_test_helper.BeginSession(view_configs));

  return XR_SUCCESS;
}

XrResult xrCreateAction(XrActionSet action_set,
                        const XrActionCreateInfo* create_info,
                        XrAction* action) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF(create_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionCreateInfo is nullptr");
  RETURN_IF_XR_FAILED(
      g_test_helper.CreateAction(action_set, *create_info, action));

  return XR_SUCCESS;
}

XrResult xrCreateActionSet(XrInstance instance,
                           const XrActionSetCreateInfo* create_info,
                           XrActionSet* action_set) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF(create_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionSetCreateInfo is nullptr");
  RETURN_IF_XR_FAILED(g_test_helper.ValidateActionSetCreateInfo(*create_info));
  RETURN_IF(action_set == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionSet is nullptr");
  *action_set = g_test_helper.CreateActionSet(*create_info);

  return XR_SUCCESS;
}

XrResult xrCreateActionSpace(XrSession session,
                             const XrActionSpaceCreateInfo* create_info,
                             XrSpace* space) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(create_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionSpaceCreateInfo is nullptr");
  RETURN_IF(space == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSpace is nullptr");
  RETURN_IF_XR_FAILED(g_test_helper.CreateActionSpace(*create_info, space));

  return XR_SUCCESS;
}

XrResult xrCreateHandTrackerEXT(XrSession session,
                                const XrHandTrackerCreateInfoEXT* create_info,
                                XrHandTrackerEXT* hand_tracker) {
  DVLOG(2) << __func__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(create_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrHandTrackerCreateInfoEXT is nullptr");
  RETURN_IF(create_info->hand == XR_HAND_MAX_ENUM_EXT,
            XR_ERROR_VALIDATION_FAILURE, "XrHand is unsupported");
  RETURN_IF(hand_tracker == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrHandTrackerEXT is null");
  *hand_tracker = g_test_helper.CreateHandTracker(create_info->hand);
  return XR_SUCCESS;
}

XrResult xrCreateInstance(const XrInstanceCreateInfo* create_info,
                          XrInstance* instance) {
  DVLOG(2) << __FUNCTION__;

  RETURN_IF(create_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrInstanceCreateInfo is nullptr");
  RETURN_IF(create_info->applicationInfo.apiVersion != XR_CURRENT_API_VERSION,
            XR_ERROR_API_VERSION_UNSUPPORTED, "apiVersion unsupported");

  RETURN_IF(create_info->type != XR_TYPE_INSTANCE_CREATE_INFO,
            XR_ERROR_VALIDATION_FAILURE, "XrInstanceCreateInfo type invalid");

  RETURN_IF(create_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrInstanceCreateInfo next is not nullptr");

  RETURN_IF(create_info->createFlags != 0, XR_ERROR_VALIDATION_FAILURE,
            "XrInstanceCreateInfo createFlags is not 0");

  RETURN_IF(
      create_info->enabledApiLayerCount != 0 ||
          create_info->enabledApiLayerNames != nullptr,
      XR_ERROR_VALIDATION_FAILURE,
      "XrInstanceCreateInfo ApiLayer is not supported by this version of test");

  for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
    bool valid_extension = false;
    for (size_t j = 0; j < OpenXrTestHelper::kNumExtensionsSupported; j++) {
      if (strcmp(create_info->enabledExtensionNames[i],
                 OpenXrTestHelper::kExtensions[j]) == 0) {
        valid_extension = true;
        break;
      }
    }

    RETURN_IF_FALSE(valid_extension, XR_ERROR_VALIDATION_FAILURE,
                    "enabledExtensionNames contains invalid extensions");
  }

  RETURN_IF(instance == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrInstance is nullptr");
  *instance = g_test_helper.CreateInstance();

  return XR_SUCCESS;
}

XrResult xrCreateReferenceSpace(XrSession session,
                                const XrReferenceSpaceCreateInfo* create_info,
                                XrSpace* space) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(create_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrReferenceSpaceCreateInfo is nullptr");
  RETURN_IF(create_info->type != XR_TYPE_REFERENCE_SPACE_CREATE_INFO,
            XR_ERROR_VALIDATION_FAILURE,
            "XrReferenceSpaceCreateInfo type invalid");
  RETURN_IF(create_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrReferenceSpaceCreateInfo next is not nullptr");
  RETURN_IF(
      create_info->referenceSpaceType != XR_REFERENCE_SPACE_TYPE_LOCAL &&
          create_info->referenceSpaceType != XR_REFERENCE_SPACE_TYPE_VIEW &&
          create_info->referenceSpaceType != XR_REFERENCE_SPACE_TYPE_STAGE &&
          create_info->referenceSpaceType !=
              XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT,
      XR_ERROR_REFERENCE_SPACE_UNSUPPORTED,
      "XrReferenceSpaceCreateInfo referenceSpaceType invalid");
  RETURN_IF_XR_FAILED(g_test_helper.ValidateXrPosefIsIdentity(
      create_info->poseInReferenceSpace));
  RETURN_IF(space == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSpace is nullptr");
  *space = g_test_helper.CreateReferenceSpace(create_info->referenceSpaceType);

  return XR_SUCCESS;
}

XrResult xrCreateSession(XrInstance instance,
                         const XrSessionCreateInfo* create_info,
                         XrSession* session) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF(create_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSessionCreateInfo is nullptr");
  RETURN_IF(create_info->type != XR_TYPE_SESSION_CREATE_INFO,
            XR_ERROR_VALIDATION_FAILURE, "XrSessionCreateInfo type invalid");
  RETURN_IF(create_info->createFlags != 0, XR_ERROR_VALIDATION_FAILURE,
            "XrSessionCreateInfo createFlags is not 0");
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSystemId(create_info->systemId));

  const XrGraphicsBindingD3D11KHR* binding =
      static_cast<const XrGraphicsBindingD3D11KHR*>(create_info->next);
  RETURN_IF(binding->type != XR_TYPE_GRAPHICS_BINDING_D3D11_KHR,
            XR_ERROR_VALIDATION_FAILURE,
            "XrGraphicsBindingD3D11KHR type invalid");
  RETURN_IF(binding->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrGraphicsBindingD3D11KHR next is not nullptr");
  RETURN_IF(binding->device == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "D3D11Device is nullptr");

  g_test_helper.SetD3DDevice(binding->device);
  RETURN_IF(session == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSession is nullptr");
  RETURN_IF_XR_FAILED(g_test_helper.CreateSession(session));

  return XR_SUCCESS;
}

XrResult xrCreateSwapchain(XrSession session,
                           const XrSwapchainCreateInfo* create_info,
                           XrSwapchain* swapchain) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(create_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo is nullptr");
  RETURN_IF(create_info->type != XR_TYPE_SWAPCHAIN_CREATE_INFO,
            XR_ERROR_VALIDATION_FAILURE, "XrSwapchainCreateInfo type invalid");
  RETURN_IF(create_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo next is not nullptr");
  RETURN_IF(create_info->createFlags != 0, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo createFlags is not 0");
  RETURN_IF(create_info->usageFlags != XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT,
            XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo usageFlags is not "
            "XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT");
  RETURN_IF(create_info->format != DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
            XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED,
            "XrSwapchainCreateInfo format unsupported");
  RETURN_IF(create_info->sampleCount != OpenXrTestHelper::kSwapCount,
            XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo sampleCount invalid");
  RETURN_IF(create_info->width == 0, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo width is zero");
  RETURN_IF(create_info->height == 0, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo height is zero");
  RETURN_IF(create_info->faceCount != 1, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo faceCount is not 1");
  RETURN_IF(create_info->arraySize != 1, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo arraySize invalid");
  RETURN_IF(create_info->mipCount != 1, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainCreateInfo mipCount is not 1");

  RETURN_IF(swapchain == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchain is nullptr");
  *swapchain = g_test_helper.CreateSwapchain();

  return XR_SUCCESS;
}

XrResult xrDestroyActionSet(XrActionSet action_set) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.DestroyActionSet(action_set));
  return XR_SUCCESS;
}

XrResult xrDestroyHandTrackerEXT(XrHandTrackerEXT hand_tracker) {
  DVLOG(2) << __func__;
  RETURN_IF_XR_FAILED(g_test_helper.DestroyHandTracker(hand_tracker));
  return XR_SUCCESS;
}

XrResult xrDestroyInstance(XrInstance instance) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.DestroyInstance(instance));
  return XR_SUCCESS;
}

XrResult xrDestroySession(XrSession session) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.DestroySession(session));
  return XR_SUCCESS;
}

XrResult xrDestroySpace(XrSpace space) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.DestroySpace(space));
  return XR_SUCCESS;
}

XrResult xrDestroySwapchain(XrSwapchain swapchain) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.DestroySwapchain(swapchain));
  return XR_SUCCESS;
}

XrResult xrEndFrame(XrSession session, const XrFrameEndInfo* frame_end_info) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(frame_end_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrFrameEndInfo is nullptr");
  RETURN_IF(frame_end_info->type != XR_TYPE_FRAME_END_INFO,
            XR_ERROR_VALIDATION_FAILURE, "XrFrameEndInfo type invalid");
  RETURN_IF_XR_FAILED(
      g_test_helper.ValidatePredictedDisplayTime(frame_end_info->displayTime));
  RETURN_IF(frame_end_info->environmentBlendMode !=
                OpenXrTestHelper::kEnvironmentBlendMode,
            XR_ERROR_VALIDATION_FAILURE,
            "XrFrameEndInfo environmentBlendMode invalid");
  // We currently only support one layer per view configuration.
  RETURN_IF(frame_end_info->layerCount != 1, XR_ERROR_VALIDATION_FAILURE,
            "XrFrameEndInfo layerCount invalid");
  RETURN_IF(frame_end_info->layers == nullptr, XR_ERROR_LAYER_INVALID,
            "XrFrameEndInfo has nullptr layers");

  for (uint32_t i = 0; i < frame_end_info->layerCount; i++) {
    const XrCompositionLayerProjection* primary_layer_ptr =
        reinterpret_cast<const XrCompositionLayerProjection*>(
            frame_end_info->layers[i]);
    RETURN_IF_XR_FAILED(g_test_helper.ValidateXrCompositionLayerProjection(
        g_test_helper.PrimaryViewConfig(), *primary_layer_ptr));
  }

  if (frame_end_info->next != nullptr) {
    // If the next pointer is specified, secondary views are active.
    const XrSecondaryViewConfigurationFrameEndInfoMSFT* second_end_info =
        reinterpret_cast<const XrSecondaryViewConfigurationFrameEndInfoMSFT*>(
            frame_end_info->next);

    RETURN_IF(second_end_info->type !=
                  XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_END_INFO_MSFT,
              XR_ERROR_VALIDATION_FAILURE,
              "XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_END_INFO_MSFT type "
              "invalid");
    RETURN_IF(
        second_end_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
        "XrSecondaryViewConfigurationFrameEndInfoMSFT next is not nullptr");

    for (uint32_t i = 0; i < second_end_info->viewConfigurationCount; i++) {
      XrSecondaryViewConfigurationLayerInfoMSFT layer_info =
          second_end_info->viewConfigurationLayersInfo[i];

      RETURN_IF(layer_info.type !=
                    XR_TYPE_SECONDARY_VIEW_CONFIGURATION_LAYER_INFO_MSFT,
                XR_ERROR_VALIDATION_FAILURE,
                "XrSecondaryViewConfigurationLayerInfoMSFT type invalid");
      RETURN_IF(
          layer_info.next != nullptr, XR_ERROR_VALIDATION_FAILURE,
          "XrSecondaryViewConfigurationLayerInfoMSFT next is not nullptr");
      RETURN_IF(
          layer_info.viewConfigurationType == g_test_helper.PrimaryViewConfig(),
          XR_ERROR_LAYER_INVALID,
          "XrSecondaryViewConfigurationLayerInfoMSFT cannot have a "
          "primary view configuration");
      RETURN_IF_XR_FAILED(g_test_helper.ValidateViewConfigType(
          layer_info.viewConfigurationType));
      RETURN_IF(layer_info.environmentBlendMode !=
                    OpenXrTestHelper::kEnvironmentBlendMode,
                XR_ERROR_VALIDATION_FAILURE,
                "XrSecondaryViewConfigurationLayerInfoMSFT "
                "environmentBlendMode invalid");
      // We currently only support one layer per view configuration.
      RETURN_IF(layer_info.layerCount != 1, XR_ERROR_VALIDATION_FAILURE,
                "XrSecondaryViewConfigurationLayerInfoMSFT layerCount invalid");
      RETURN_IF(layer_info.layers == nullptr, XR_ERROR_LAYER_INVALID,
                "XrSecondaryViewConfigurationLayerInfoMSFT has nullptr layers");

      for (uint32_t j = 0; j < layer_info.layerCount; j++) {
        const XrCompositionLayerProjection* secondary_layer_ptr =
            reinterpret_cast<const XrCompositionLayerProjection*>(
                layer_info.layers[j]);
        RETURN_IF_XR_FAILED(g_test_helper.ValidateXrCompositionLayerProjection(
            layer_info.viewConfigurationType, *secondary_layer_ptr));
      }
    }
  }

  RETURN_IF_XR_FAILED(g_test_helper.EndFrame());
  g_test_helper.OnPresentedFrame();
  return XR_SUCCESS;
}

XrResult xrEndSession(XrSession session) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF_XR_FAILED(g_test_helper.EndSession());

  return XR_SUCCESS;
}

XrResult xrEnumerateEnvironmentBlendModes(
    XrInstance instance,
    XrSystemId system_id,
    XrViewConfigurationType view_configuration_type,
    uint32_t environment_blend_mode_capacity_input,
    uint32_t* environment_blend_mode_count_output,
    XrEnvironmentBlendMode* environment_blend_modes) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSystemId(system_id));
  RETURN_IF_XR_FAILED(
      g_test_helper.ValidateViewConfigType(view_configuration_type));

  RETURN_IF(environment_blend_mode_count_output == nullptr,
            XR_ERROR_VALIDATION_FAILURE,
            "environment_blend_mode_count_output is nullptr");
  *environment_blend_mode_count_output = 1;
  if (environment_blend_mode_capacity_input == 0) {
    return XR_SUCCESS;
  }

  RETURN_IF(environment_blend_mode_capacity_input != 1,
            XR_ERROR_VALIDATION_FAILURE,
            "environment_blend_mode_capacity_input is neither 0 or 1");
  RETURN_IF(environment_blend_modes == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrEnvironmentBlendMode is nullptr");
  *environment_blend_modes = OpenXrTestHelper::kEnvironmentBlendMode;

  return XR_SUCCESS;
}

// Even thought xrEnumerateInstanceExtensionProperties is not directly called
// in our implementation, it is used inside loader so this function mock is
// needed
XrResult xrEnumerateInstanceExtensionProperties(
    const char* layer_name,
    uint32_t property_capacity_input,
    uint32_t* property_count_output,
    XrExtensionProperties* properties) {
  DVLOG(2) << __FUNCTION__;

  RETURN_IF(
      property_capacity_input < OpenXrTestHelper::kNumExtensionsSupported &&
          property_capacity_input != 0,
      XR_ERROR_SIZE_INSUFFICIENT, "XrExtensionProperties array is too small");

  RETURN_IF(property_count_output == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "property_count_output is nullptr");
  *property_count_output = OpenXrTestHelper::kNumExtensionsSupported;
  if (property_capacity_input == 0) {
    return XR_SUCCESS;
  }

  RETURN_IF(
      property_capacity_input != OpenXrTestHelper::kNumExtensionsSupported,
      XR_ERROR_VALIDATION_FAILURE,
      "property_capacity_input is neither 0 or kNumExtensionsSupported");
  RETURN_IF(properties == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrExtensionProperties is nullptr");
  for (uint32_t i = 0; i < OpenXrTestHelper::kNumExtensionsSupported; i++) {
    size_t dest_size = std::size(properties[i].extensionName);
    DCHECK(dest_size > 0);
    properties[i].type = XR_TYPE_EXTENSION_PROPERTIES;
    size_t copy_length =
        base::strlcpy(properties[i].extensionName,
                      OpenXrTestHelper::kExtensions[i], dest_size);
    DCHECK(copy_length < dest_size);
    properties[i].extensionVersion = 1;
  }

  return XR_SUCCESS;
}

XrResult xrEnumerateViewConfigurations(
    XrInstance instance,
    XrSystemId system_id,
    uint32_t view_configuration_type_capacity_input,
    uint32_t* view_configuration_type_count_output,
    XrViewConfigurationType* view_configuration_types) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSystemId(system_id));
  RETURN_IF(view_configuration_type_count_output == nullptr,
            XR_ERROR_VALIDATION_FAILURE,
            "view_configuration_type_count_output is nullptr");

  std::vector<XrViewConfigurationType> view_configs =
      g_test_helper.SupportedViewConfigs();
  *view_configuration_type_count_output = view_configs.size();
  if (view_configuration_type_capacity_input == 0) {
    return XR_SUCCESS;
  }

  RETURN_IF(view_configuration_types == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "view_configuration_types is nullptr");
  RETURN_IF(view_configuration_type_capacity_input != view_configs.size(),
            XR_ERROR_SIZE_INSUFFICIENT,
            "view_configuration_type_capacity_input size is insufficient");

  for (uint32_t i = 0; i < view_configs.size(); i++) {
    view_configuration_types[i] = view_configs[i];
  }

  return XR_SUCCESS;
}

XrResult xrEnumerateViewConfigurationViews(
    XrInstance instance,
    XrSystemId system_id,
    XrViewConfigurationType view_configuration_type,
    uint32_t view_capacity_input,
    uint32_t* view_count_output,
    XrViewConfigurationView* views) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSystemId(system_id));
  RETURN_IF_XR_FAILED(
      g_test_helper.ValidateViewConfigType(view_configuration_type));
  RETURN_IF(view_count_output == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "view_count_output is nullptr");

  const std::vector<device::OpenXrViewProperties>& view_properties =
      g_test_helper.GetViewConfigInfo(view_configuration_type).Properties();
  *view_count_output = view_properties.size();
  if (view_capacity_input == 0) {
    return XR_SUCCESS;
  }

  RETURN_IF(view_capacity_input != view_properties.size(),
            XR_ERROR_SIZE_INSUFFICIENT, "view_capacity_input is insufficient");
  RETURN_IF(views == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrViewConfigurationView is nullptr");
  for (uint32_t i = 0; i < view_properties.size(); i++) {
    views[i] = view_properties[i].GetPropertiesForTest();
  }

  return XR_SUCCESS;
}

XrResult xrEnumerateSwapchainFormats(XrSession session,
                                     uint32_t format_capacity_input,
                                     uint32_t* format_count_output,
                                     int64_t* formats) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(format_capacity_input != 1 && format_capacity_input != 0,
            XR_ERROR_SIZE_INSUFFICIENT,
            "xrEnumerateSwapchainFormats does not equal length returned by "
            "previous call");
  RETURN_IF(format_count_output == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "format_count_output is nullptr");
  *format_count_output = 1;
  if (format_capacity_input == 0) {
    return XR_SUCCESS;
  }

  RETURN_IF(format_capacity_input < *format_count_output,
            XR_ERROR_SIZE_INSUFFICIENT,
            "format_capacity_input is less than required size");
  RETURN_IF(formats == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "Formats Array is nullptr");
  // This is what is hardcoded in `OpenXrGraphicsBindingD3D11`.
  formats[0] = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;

  return XR_SUCCESS;
}

XrResult xrEnumerateSwapchainImages(XrSwapchain swapchain,
                                    uint32_t image_capacity_input,
                                    uint32_t* image_count_output,
                                    XrSwapchainImageBaseHeader* images) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSwapchain(swapchain));
  RETURN_IF(image_capacity_input != OpenXrTestHelper::kMinSwapchainBuffering &&
                image_capacity_input != 0,
            XR_ERROR_SIZE_INSUFFICIENT,
            "xrEnumerateSwapchainImages does not equal length returned by "
            "xrCreateSwapchain");

  RETURN_IF(image_count_output == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "image_capacity_input is nullptr");
  *image_count_output = OpenXrTestHelper::kMinSwapchainBuffering;
  if (image_capacity_input == 0) {
    return XR_SUCCESS;
  }

  RETURN_IF(image_capacity_input != OpenXrTestHelper::kMinSwapchainBuffering,
            XR_ERROR_VALIDATION_FAILURE,
            "image_capacity_input is neither 0 or kMinSwapchainBuffering");
  RETURN_IF(images == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainImageBaseHeader is nullptr");
  const std::vector<Microsoft::WRL::ComPtr<ID3D11Texture2D>>& textures =
      g_test_helper.GetSwapchainTextures();
  DCHECK_EQ(textures.size(), image_capacity_input);

  for (uint32_t i = 0; i < image_capacity_input; i++) {
    XrSwapchainImageD3D11KHR& image =
        reinterpret_cast<XrSwapchainImageD3D11KHR*>(images)[i];

    RETURN_IF(image.type != XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR,
              XR_ERROR_VALIDATION_FAILURE,
              "XrSwapchainImageD3D11KHR type invalid");
    RETURN_IF(image.next != nullptr, XR_ERROR_VALIDATION_FAILURE,
              "XrSwapchainImageD3D11KHR next is not nullptr");

    image.texture = textures[i].Get();
  }

  return XR_SUCCESS;
}

#if BUILDFLAG(IS_WIN)
__stdcall XrResult xrGetD3D11GraphicsRequirementsKHR(
    XrInstance instance,
    XrSystemId system_id,
    XrGraphicsRequirementsD3D11KHR* graphics_requirements) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSystemId(system_id));
  RETURN_IF(graphics_requirements == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrGraphicsRequirementsD3D11KHR is nullptr");
  RETURN_IF(
      graphics_requirements->type != XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR,
      XR_ERROR_VALIDATION_FAILURE,
      "XrGraphicsRequirementsD3D11KHR type invalid");
  RETURN_IF(graphics_requirements->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrGraphicsRequirementsD3D11KHR next is not nullptr");

  Microsoft::WRL::ComPtr<IDXGIFactory1> dxgi_factory;
  Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
  HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory));
  DCHECK(SUCCEEDED(hr));
  if (SUCCEEDED(dxgi_factory->EnumAdapters(0, &adapter))) {
    DXGI_ADAPTER_DESC desc;
    adapter->GetDesc(&desc);
    graphics_requirements->adapterLuid = desc.AdapterLuid;

    // Require D3D11.1 to support shared NT handles.
    graphics_requirements->minFeatureLevel = D3D_FEATURE_LEVEL_11_1;

    return XR_SUCCESS;
  }

  RETURN_IF_FALSE(false, XR_ERROR_VALIDATION_FAILURE,
                  "Unable to create query DXGI Adapter");
}
#endif

XrResult xrGetActionStateFloat(XrSession session,
                               const XrActionStateGetInfo* get_info,
                               XrActionStateFloat* state) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(get_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionStateGetInfo is nullptr");
  RETURN_IF(get_info->type != XR_TYPE_ACTION_STATE_GET_INFO,
            XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStateFloat has wrong type");
  RETURN_IF(get_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStateFloat next is not nullptr");
  RETURN_IF_XR_FAILED(g_test_helper.ValidateAction(get_info->action));
  RETURN_IF(get_info->subactionPath != XR_NULL_PATH,
            XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStateFloat has subactionPath != nullptr which is not "
            "supported by current version of test.");
  RETURN_IF_XR_FAILED(
      g_test_helper.GetActionStateFloat(get_info->action, state));

  return XR_SUCCESS;
}

XrResult xrGetActionStateBoolean(XrSession session,
                                 const XrActionStateGetInfo* get_info,
                                 XrActionStateBoolean* state) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(get_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionStateGetInfo is nullptr");
  RETURN_IF(get_info->type != XR_TYPE_ACTION_STATE_GET_INFO,
            XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStateBoolean get_info has wrong type");
  RETURN_IF(get_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStateBoolean next is not nullptr");
  RETURN_IF_XR_FAILED(g_test_helper.ValidateAction(get_info->action));
  RETURN_IF(get_info->subactionPath != XR_NULL_PATH,
            XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStateBoolean has subactionPath != nullptr which is not "
            "supported by current version of test.");
  RETURN_IF_XR_FAILED(
      g_test_helper.GetActionStateBoolean(get_info->action, state));

  return XR_SUCCESS;
}

XrResult xrGetActionStateVector2f(XrSession session,
                                  const XrActionStateGetInfo* get_info,
                                  XrActionStateVector2f* state) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(get_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionStateGetInfo is nullptr");
  RETURN_IF(get_info->type != XR_TYPE_ACTION_STATE_GET_INFO,
            XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStateVector2f get_info has wrong type");
  RETURN_IF(get_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStateVector2f next is not nullptr");
  RETURN_IF_XR_FAILED(g_test_helper.ValidateAction(get_info->action));
  RETURN_IF(
      get_info->subactionPath != XR_NULL_PATH, XR_ERROR_VALIDATION_FAILURE,
      "xrGetActionStateVector2f has subactionPath != nullptr which is not "
      "supported by current version of test.");
  RETURN_IF_XR_FAILED(
      g_test_helper.GetActionStateVector2f(get_info->action, state));

  return XR_SUCCESS;
}

XrResult xrGetActionStatePose(XrSession session,
                              const XrActionStateGetInfo* get_info,
                              XrActionStatePose* state) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(get_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionStateGetInfo is nullptr");
  RETURN_IF(get_info->type != XR_TYPE_ACTION_STATE_GET_INFO,
            XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStatePose get_info has wrong type");
  RETURN_IF(get_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStatePose next is not nullptr");
  RETURN_IF_XR_FAILED(g_test_helper.ValidateAction(get_info->action));
  RETURN_IF(get_info->subactionPath != XR_NULL_PATH,
            XR_ERROR_VALIDATION_FAILURE,
            "xrGetActionStatePose has subactionPath != nullptr which is not "
            "supported by current version of test.");
  RETURN_IF_XR_FAILED(
      g_test_helper.GetActionStatePose(get_info->action, state));

  return XR_SUCCESS;
}

XrResult xrGetCurrentInteractionProfile(
    XrSession session,
    XrPath top_level_user_path,
    XrInteractionProfileState* interaction_profile) {
  DVLOG(1) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(
      g_test_helper.AttachedActionSetsSize() == 0,
      XR_ERROR_ACTIONSET_NOT_ATTACHED,
      "xrGetCurrentInteractionProfile action sets have not been attached yet");
  RETURN_IF_XR_FAILED(g_test_helper.ValidatePath(top_level_user_path));
  RETURN_IF(interaction_profile == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrInteractionProfileState is nullptr");
  RETURN_IF(interaction_profile->type != XR_TYPE_INTERACTION_PROFILE_STATE,
            XR_ERROR_VALIDATION_FAILURE,
            "xrGetCurrentInteractionProfile type is not "
            "XR_TYPE_INTERACTION_PROFILE_STATE");
  RETURN_IF(interaction_profile->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "xrGetCurrentInteractionProfile next is not "
            "nullptr");
  interaction_profile->interactionProfile =
      g_test_helper.GetCurrentInteractionProfile();
  return XR_SUCCESS;
}

XrResult xrGetReferenceSpaceBoundsRect(
    XrSession session,
    XrReferenceSpaceType refernece_space_type,
    XrExtent2Df* bounds) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(refernece_space_type != XR_REFERENCE_SPACE_TYPE_STAGE,
            XR_ERROR_REFERENCE_SPACE_UNSUPPORTED,
            "xrGetReferenceSpaceBoundsRect type is not stage");
  RETURN_IF(bounds == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrExtent2Df is nullptr");
  bounds->width = 0;
  bounds->height = 0;
  return XR_SUCCESS;
}

XrResult xrGetViewConfigurationProperties(
    XrInstance instance,
    XrSystemId system_id,
    XrViewConfigurationType view_configuration_type,
    XrViewConfigurationProperties* configuration_properties) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSystemId(system_id));
  RETURN_IF(
      view_configuration_type != XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO,
      XR_ERROR_VALIDATION_FAILURE, "viewConfigurationType must be stereo");
  RETURN_IF(
      configuration_properties->type == XR_TYPE_VIEW_CONFIGURATION_PROPERTIES,
      XR_ERROR_VALIDATION_FAILURE,
      "XrViewConfigurationProperties.type must be "
      "XR_TYPE_VIEW_CONFIGURATION_PROPERTIES");
  RETURN_IF(configuration_properties->next == nullptr,
            XR_ERROR_VALIDATION_FAILURE,
            "XrViewConfigurationProperties.next must be nullptr");
  configuration_properties->viewConfigurationType =
      XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
  configuration_properties->fovMutable = XR_TRUE;
  return XR_SUCCESS;
}

XrResult xrGetSystem(XrInstance instance,
                     const XrSystemGetInfo* get_info,
                     XrSystemId* system_id) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF(get_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSystemGetInfo is nullptr");
  RETURN_IF(get_info->type != XR_TYPE_SYSTEM_GET_INFO,
            XR_ERROR_VALIDATION_FAILURE, "XrSystemGetInfo type invalid");
  RETURN_IF(get_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSystemGetInfo next is not nullptr");
  RETURN_IF(get_info->formFactor != XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY,
            XR_ERROR_VALIDATION_FAILURE, "XrSystemGetInfo formFactor invalid");

  RETURN_IF(system_id == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSystemId is nullptr");
  *system_id = g_test_helper.GetSystemId();

  return XR_SUCCESS;
}

XrResult xrGetSystemProperties(XrInstance instance,
                               XrSystemId system_id,
                               XrSystemProperties* system_properties) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSystemId(system_id));
  RETURN_IF(system_properties == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSystemProperties is nullptr");
  RETURN_IF(system_properties->type != XR_TYPE_SYSTEM_PROPERTIES,
            XR_ERROR_VALIDATION_FAILURE, "XrSystemProperties type invalid");
  RETURN_IF(system_properties->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSystemProperties next is not nullptr");

  *system_properties = g_test_helper.GetSystemProperties();
  system_properties->systemId = system_id;

  return XR_SUCCESS;
}

XrResult xrLocateHandJointsEXT(XrHandTrackerEXT hand_tracker,
                               const XrHandJointsLocateInfoEXT* locate_info,
                               XrHandJointLocationsEXT* locations) {
  DVLOG(2) << __func__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateHandTracker(hand_tracker));
  RETURN_IF(locate_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrHandJointsLocateInfoEXT is nullptr");
  RETURN_IF(locations == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrHandJointLocationsEXT is nullptr");
  // No tests actually use hand joint data, so we leave them unpopulated at this
  // time.
  return XR_SUCCESS;
}

XrResult xrLocateSpace(XrSpace space,
                       XrSpace base_space,
                       XrTime time,
                       XrSpaceLocation* location) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSpace(space));
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSpace(base_space));
  RETURN_IF_XR_FAILED(g_test_helper.ValidatePredictedDisplayTime(time));

  RETURN_IF(location == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSpaceLocation is nullptr");
  g_test_helper.LocateSpace(space, &(location->pose));

  location->locationFlags = XR_SPACE_LOCATION_ORIENTATION_VALID_BIT |
                            XR_SPACE_LOCATION_POSITION_VALID_BIT |
                            XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT |
                            XR_SPACE_LOCATION_POSITION_TRACKED_BIT;

  return XR_SUCCESS;
}

XrResult xrLocateViews(XrSession session,
                       const XrViewLocateInfo* view_locate_info,
                       XrViewState* view_state,
                       uint32_t view_capacity_input,
                       uint32_t* view_count_output,
                       XrView* views) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(view_locate_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrViewLocateInfo is nullptr");
  RETURN_IF(view_locate_info->type != XR_TYPE_VIEW_LOCATE_INFO,
            XR_ERROR_VALIDATION_FAILURE,
            "xrLocateViews view_locate_info type invalid");
  RETURN_IF(view_locate_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrViewLocateInfo next is not nullptr");
  RETURN_IF_XR_FAILED(g_test_helper.ValidateViewConfigType(
      view_locate_info->viewConfigurationType));
  RETURN_IF_XR_FAILED(g_test_helper.ValidatePredictedDisplayTime(
      view_locate_info->displayTime));
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSpace(view_locate_info->space));
  RETURN_IF(view_state == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrViewState is nullptr");
  RETURN_IF(view_count_output == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "view_count_output is nullptr");

  const device::OpenXrViewConfiguration& view_config =
      g_test_helper.GetViewConfigInfo(view_locate_info->viewConfigurationType);
  const std::vector<XrView>& view_config_views = view_config.Views();
  *view_count_output = view_config_views.size();
  if (view_capacity_input == 0) {
    return XR_SUCCESS;
  }

  RETURN_IF(view_capacity_input != view_config_views.size(),
            XR_ERROR_SIZE_INSUFFICIENT,
            "view_capacity_input is neither 0 or OpenXrTestHelper::kViewCount");
  RETURN_IF_XR_FAILED(g_test_helper.ValidateViews(view_capacity_input, views));
  RETURN_IF_FALSE(
      g_test_helper.UpdateViews(view_locate_info->viewConfigurationType, views,
                                view_capacity_input),
      XR_ERROR_VALIDATION_FAILURE, "xrLocateViews UpdateViews failed");
  view_state->viewStateFlags =
      XR_VIEW_STATE_POSITION_VALID_BIT | XR_VIEW_STATE_ORIENTATION_VALID_BIT;

  return XR_SUCCESS;
}

XrResult xrPollEvent(XrInstance instance, XrEventDataBuffer* event_data) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));

  return g_test_helper.PollEvent(event_data);
}

XrResult xrReleaseSwapchainImage(
    XrSwapchain swapchain,
    const XrSwapchainImageReleaseInfo* release_info) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSwapchain(swapchain));
  RETURN_IF(release_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainImageReleaseInfo is nullptr");
  RETURN_IF(release_info->type != XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO,
            XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainImageReleaseInfo type invalid");
  RETURN_IF(release_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainImageReleaseInfo next is not nullptr");

  return XR_SUCCESS;
}

XrResult xrSuggestInteractionProfileBindings(
    XrInstance instance,
    const XrInteractionProfileSuggestedBinding* suggested_bindings) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF(suggested_bindings == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrInteractionProfileSuggestedBinding is nullptr");
  RETURN_IF(
      suggested_bindings->type != XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING,
      XR_ERROR_VALIDATION_FAILURE,
      "xrSetInteractionProfileSuggestedBindings type invalid");
  RETURN_IF(suggested_bindings->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "xrSetInteractionProfileSuggestedBindings next is not nullptr");
  RETURN_IF_XR_FAILED(
      g_test_helper.ValidatePath(suggested_bindings->interactionProfile));
  std::string interaction_profile =
      g_test_helper.PathToString(suggested_bindings->interactionProfile);

  RETURN_IF(suggested_bindings->suggestedBindings == nullptr,
            XR_ERROR_VALIDATION_FAILURE,
            "XrInteractionProfileSuggestedBinding has nullptr "
            "XrActionSuggestedBinding");
  RETURN_IF(g_test_helper.AttachedActionSetsSize() != 0,
            XR_ERROR_ACTIONSETS_ALREADY_ATTACHED,
            "xrSuggestInteractionProfileBindings called after "
            "xrAttachSessionActionSets");
  for (uint32_t i = 0; i < suggested_bindings->countSuggestedBindings; i++) {
    XrActionSuggestedBinding suggestedBinding =
        suggested_bindings->suggestedBindings[i];
    RETURN_IF_XR_FAILED(g_test_helper.BindActionAndPath(
        suggested_bindings->interactionProfile, suggestedBinding));
  }

  return XR_SUCCESS;
}

XrResult xrStringToPath(XrInstance instance,
                        const char* path_string,
                        XrPath* path) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF(path == nullptr, XR_ERROR_VALIDATION_FAILURE, "path is nullptr");
  *path = g_test_helper.GetPath(path_string);

  return XR_SUCCESS;
}

XrResult xrPathToString(XrInstance instance,
                        XrPath path,
                        uint32_t buffer_capacity_input,
                        uint32_t* buffer_count_output,
                        char* buffer) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
  RETURN_IF_XR_FAILED(g_test_helper.ValidatePath(path));

  std::string path_string = g_test_helper.PathToString(path);
  RETURN_IF(buffer_count_output == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "buffer_count_output is nullptr");
  // OpenXR spec counts terminating '\0'
  *buffer_count_output = path_string.size() + 1;
  if (buffer_capacity_input == 0) {
    return XR_SUCCESS;
  }

  RETURN_IF(
      buffer_capacity_input <= path_string.size(), XR_ERROR_SIZE_INSUFFICIENT,
      "xrPathToString inputsize is not large enough to hold the output string");
  size_t copied_size =
      base::strlcpy(buffer, path_string.data(), *buffer_count_output);
  DCHECK_LT(copied_size, *buffer_count_output);

  return XR_SUCCESS;
}

XrResult xrSyncActions(XrSession session, const XrActionsSyncInfo* sync_info) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF_FALSE(g_test_helper.UpdateData(), XR_ERROR_VALIDATION_FAILURE,
                  "xrSyncActionData can't receive data from test");
  RETURN_IF(sync_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionsSyncInfo is nullptr");
  RETURN_IF(sync_info->type != XR_TYPE_ACTIONS_SYNC_INFO,
            XR_ERROR_VALIDATION_FAILURE, "XrActionsSyncInfo type invalid");
  RETURN_IF(sync_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionsSyncInfo next is not nullptr");
  RETURN_IF(sync_info->activeActionSets == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrActionsSyncInfo activeActionSets is nullptr");

  for (uint32_t i = 0; i < sync_info->countActiveActionSets; i++) {
    RETURN_IF(
        sync_info->activeActionSets[i].subactionPath != XR_NULL_PATH,
        XR_ERROR_VALIDATION_FAILURE,
        "xrSyncActionData does not support use of subactionPath for test yet");
    RETURN_IF_XR_FAILED(
        g_test_helper.SyncActionData(sync_info->activeActionSets[i].actionSet));
  }

  return XR_SUCCESS;
}

XrResult xrWaitFrame(XrSession session,
                     const XrFrameWaitInfo* frame_wait_info,
                     XrFrameState* frame_state) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
  RETURN_IF(frame_wait_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrFrameWaitInfo is nullptr");
  RETURN_IF(frame_wait_info->type != XR_TYPE_FRAME_WAIT_INFO,
            XR_ERROR_VALIDATION_FAILURE, "frame_wait_info type invalid");
  RETURN_IF(frame_state == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrFrameState is nullptr");
  RETURN_IF(frame_state->type != XR_TYPE_FRAME_STATE,
            XR_ERROR_VALIDATION_FAILURE, "XR_TYPE_FRAME_STATE type invalid");

  if (frame_state->next != nullptr) {
    // If a next pointer is specified, secondary views are active.
    XrSecondaryViewConfigurationFrameStateMSFT* secondary_frame_state =
        reinterpret_cast<XrSecondaryViewConfigurationFrameStateMSFT*>(
            frame_state->next);
    RETURN_IF(secondary_frame_state->type !=
                  XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_STATE_MSFT,
              XR_ERROR_VALIDATION_FAILURE,
              "XrSecondaryViewConfigurationFrameStateMSFT type invalid");
    RETURN_IF(secondary_frame_state->next != nullptr,
              XR_ERROR_VALIDATION_FAILURE,
              "XrSecondaryViewConfigurationFrameStateMSFT next is not nullptr");
    RETURN_IF(secondary_frame_state->viewConfigurationStates == nullptr,
              XR_ERROR_VALIDATION_FAILURE,
              "XrSecondaryViewConfigurationFrameStateMSFT "
              "viewConfigurationStates must point to an array");

    RETURN_IF_XR_FAILED(g_test_helper.GetSecondaryConfigStates(
        secondary_frame_state->viewConfigurationCount,
        secondary_frame_state->viewConfigurationStates));
  }

  frame_state->predictedDisplayTime = g_test_helper.NextPredictedDisplayTime();

  return XR_SUCCESS;
}

XrResult xrWaitSwapchainImage(XrSwapchain swapchain,
                              const XrSwapchainImageWaitInfo* wait_info) {
  DVLOG(2) << __FUNCTION__;
  RETURN_IF_XR_FAILED(g_test_helper.ValidateSwapchain(swapchain));
  RETURN_IF(wait_info == nullptr, XR_ERROR_VALIDATION_FAILURE,
            "XrSwapchainImageWaitInfo is nullptr");
  RETURN_IF(wait_info->type != XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO,
            XR_ERROR_VALIDATION_FAILURE, "xrWaitSwapchainImage type invalid");
  RETURN_IF(wait_info->type != XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO,
            XR_ERROR_VALIDATION_FAILURE,
            "xrWaitSwapchainImage next is nullptr");
  RETURN_IF(wait_info->timeout != XR_INFINITE_DURATION,
            XR_ERROR_VALIDATION_FAILURE,
            "xrWaitSwapchainImage timeout not XR_INFINITE_DURATION");

  return XR_SUCCESS;
}

// Getter for extension methods. Casts the correct function dynamically based on
// the method name provided.
// Please add new OpenXR APIs below in alphabetical order.
#define TRY_LOAD_METHOD(method_name)                                 \
  do {                                                               \
    if (strcmp(name, #method_name) == 0) {                           \
      *function = reinterpret_cast<PFN_xrVoidFunction>(method_name); \
      return XR_SUCCESS;                                             \
    }                                                                \
  } while (false)

XrResult XRAPI_PTR xrGetInstanceProcAddr(XrInstance instance,
                                         const char* name,
                                         PFN_xrVoidFunction* function) {
  TRY_LOAD_METHOD(xrAcquireSwapchainImage);
  TRY_LOAD_METHOD(xrAttachSessionActionSets);
  TRY_LOAD_METHOD(xrBeginFrame);
  TRY_LOAD_METHOD(xrBeginSession);
  TRY_LOAD_METHOD(xrCreateAction);
  TRY_LOAD_METHOD(xrCreateActionSet);
  TRY_LOAD_METHOD(xrCreateActionSpace);
  TRY_LOAD_METHOD(xrCreateHandTrackerEXT);
  TRY_LOAD_METHOD(xrCreateInstance);
  TRY_LOAD_METHOD(xrCreateReferenceSpace);
  TRY_LOAD_METHOD(xrCreateSession);
  TRY_LOAD_METHOD(xrCreateSwapchain);
  TRY_LOAD_METHOD(xrDestroyActionSet);
  TRY_LOAD_METHOD(xrDestroyHandTrackerEXT);
  TRY_LOAD_METHOD(xrDestroyInstance);
  TRY_LOAD_METHOD(xrDestroySession);
  TRY_LOAD_METHOD(xrDestroySpace);
  TRY_LOAD_METHOD(xrDestroySwapchain);
  TRY_LOAD_METHOD(xrEndFrame);
  TRY_LOAD_METHOD(xrEndSession);
  TRY_LOAD_METHOD(xrEnumerateEnvironmentBlendModes);
  TRY_LOAD_METHOD(xrEnumerateInstanceExtensionProperties);
  TRY_LOAD_METHOD(xrEnumerateSwapchainFormats);
  TRY_LOAD_METHOD(xrEnumerateSwapchainImages);
  TRY_LOAD_METHOD(xrEnumerateViewConfigurations);
  TRY_LOAD_METHOD(xrEnumerateViewConfigurationViews);
#if BUILDFLAG(IS_WIN)
  TRY_LOAD_METHOD(xrGetD3D11GraphicsRequirementsKHR);
#endif
  TRY_LOAD_METHOD(xrGetActionStateFloat);
  TRY_LOAD_METHOD(xrGetActionStateBoolean);
  TRY_LOAD_METHOD(xrGetActionStateVector2f);
  TRY_LOAD_METHOD(xrGetActionStatePose);
  TRY_LOAD_METHOD(xrGetCurrentInteractionProfile);
  TRY_LOAD_METHOD(xrGetReferenceSpaceBoundsRect);
  TRY_LOAD_METHOD(xrGetViewConfigurationProperties);
  TRY_LOAD_METHOD(xrGetSystem);
  TRY_LOAD_METHOD(xrGetSystemProperties);
  TRY_LOAD_METHOD(xrLocateHandJointsEXT);
  TRY_LOAD_METHOD(xrLocateSpace);
  TRY_LOAD_METHOD(xrLocateViews);
  TRY_LOAD_METHOD(xrPollEvent);
  TRY_LOAD_METHOD(xrReleaseSwapchainImage);
  TRY_LOAD_METHOD(xrSuggestInteractionProfileBindings);
  TRY_LOAD_METHOD(xrStringToPath);
  TRY_LOAD_METHOD(xrPathToString);
  TRY_LOAD_METHOD(xrSyncActions);
  TRY_LOAD_METHOD(xrWaitFrame);
  TRY_LOAD_METHOD(xrWaitSwapchainImage);

  return XR_ERROR_FUNCTION_UNSUPPORTED;
}

#undef TRY_LOAD_METHOD