From 8f3b9d9d2398277b3b002a895bc556a297e03eb7 Mon Sep 17 00:00:00 2001 From: John Kearney Date: Tue, 6 Aug 2024 14:35:59 -0700 Subject: [PATCH] Add OpenXR CTS test for API layer pre-create operations OpenXR API Layers may need to query other layers or the runtime before the instance has been created - add an explicit test for this case. --- changes/conformance/mr.3415.gl.md | 5 + src/conformance/CMakeLists.txt | 1 + src/conformance/build.gradle | 2 +- .../conformance_layer/Negotiate.cpp | 12 +- ...ILAYER_KHRONOS_conformance_test_layer.json | 11 + ...HRONOS_conformance_test_layer.json.license | 3 + .../conformance_test/test_apiLayer.cpp | 68 ++++++ .../test_xrCreateInstance.cpp | 3 +- src/conformance/test_layer/CMakeLists.txt | 129 +++++++++++ src/conformance/test_layer/Negotiate.cpp | 218 ++++++++++++++++++ .../XrApiLayer_conformance_test_layer.def | 6 + .../XrApiLayer_conformance_test_layer.expsym | 5 + .../XrApiLayer_conformance_test_layer.map | 12 + .../test_layer/conformance_test_layer.json | 11 + .../conformance_test_layer.json.license | 3 + 15 files changed, 486 insertions(+), 3 deletions(-) create mode 100644 changes/conformance/mr.3415.gl.md create mode 100644 src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json create mode 100644 src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json.license create mode 100644 src/conformance/conformance_test/test_apiLayer.cpp create mode 100644 src/conformance/test_layer/CMakeLists.txt create mode 100644 src/conformance/test_layer/Negotiate.cpp create mode 100644 src/conformance/test_layer/XrApiLayer_conformance_test_layer.def create mode 100644 src/conformance/test_layer/XrApiLayer_conformance_test_layer.expsym create mode 100644 src/conformance/test_layer/XrApiLayer_conformance_test_layer.map create mode 100644 src/conformance/test_layer/conformance_test_layer.json create mode 100644 src/conformance/test_layer/conformance_test_layer.json.license diff --git a/changes/conformance/mr.3415.gl.md b/changes/conformance/mr.3415.gl.md new file mode 100644 index 00000000..b2edf034 --- /dev/null +++ b/changes/conformance/mr.3415.gl.md @@ -0,0 +1,5 @@ +--- +- mr.3415.gl +- issues.2333.gl +--- +Add conformance test to validate xrCreateInstance patterns required by API layers. \ No newline at end of file diff --git a/src/conformance/CMakeLists.txt b/src/conformance/CMakeLists.txt index 6244ebbc..d9500078 100644 --- a/src/conformance/CMakeLists.txt +++ b/src/conformance/CMakeLists.txt @@ -31,6 +31,7 @@ set_target_properties( add_subdirectory(conformance_layer) add_subdirectory(utilities) add_subdirectory(framework) +add_subdirectory(test_layer) add_subdirectory(conformance_test) if(NOT ANDROID) add_subdirectory(conformance_cli) diff --git a/src/conformance/build.gradle b/src/conformance/build.gradle index 6897d202..c7da1b4f 100644 --- a/src/conformance/build.gradle +++ b/src/conformance/build.gradle @@ -68,7 +68,7 @@ android { '-DBUILD_LOADER=ON', '-DBUILD_CONFORMANCE_TESTS=ON', '-DBUILD_ALL_EXTENSIONS=ON' - targets 'conformance_test', 'openxr_loader', 'XrApiLayer_runtime_conformance' + targets 'conformance_test', 'openxr_loader', 'XrApiLayer_runtime_conformance', 'XrApiLayer_conformance_test_layer' } } preBuild.dependsOn(copyAssets) diff --git a/src/conformance/conformance_layer/Negotiate.cpp b/src/conformance/conformance_layer/Negotiate.cpp index 21ab0bb4..8c6e541f 100644 --- a/src/conformance/conformance_layer/Negotiate.cpp +++ b/src/conformance/conformance_layer/Negotiate.cpp @@ -18,8 +18,12 @@ #include "ConformanceHooks.h" #include "gen_dispatch.h" +#include + namespace { + static const char* LAYER_NAME = "XR_APILAYER_KHRONOS_runtime_conformance"; + XRAPI_ATTR XrResult XRAPI_CALL ConformanceLayer_RegisterInstance(const XrInstanceCreateInfo* createInfo, const XrApiLayerCreateInfo* apiLayerInfo, XrInstance* instance) { @@ -68,7 +72,7 @@ namespace // Function used to negotiate an interface betewen the loader and an API layer. Each library exposing one or // more API layers needs to expose at least this function. extern "C" LAYER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrNegotiateLoaderApiLayerInterface(const XrNegotiateLoaderInfo* loaderInfo, - const char* /*apiLayerName*/, + const char* apiLayerName, XrNegotiateApiLayerRequest* apiLayerRequest) { if (loaderInfo == nullptr || loaderInfo->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO || @@ -91,6 +95,12 @@ extern "C" LAYER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrNegotiateLoaderApiLayer return XR_ERROR_INITIALIZATION_FAILED; } + if (strcmp(apiLayerName, LAYER_NAME) != 0) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader layer name does not match expected name"); + return XR_ERROR_INITIALIZATION_FAILED; + } + if (apiLayerRequest == nullptr || apiLayerRequest->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST || apiLayerRequest->structVersion != XR_API_LAYER_INFO_STRUCT_VERSION || apiLayerRequest->structSize != sizeof(XrNegotiateApiLayerRequest)) { diff --git a/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json b/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json new file mode 100644 index 00000000..9dc3ead1 --- /dev/null +++ b/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json @@ -0,0 +1,11 @@ +{ + "file_format_version": "1.0.0", + "api_layer": { + "name": "XR_APILAYER_KHRONOS_conformance_test_layer", + "library_path": "libXrApiLayer_conformance_test_layer.so", + "api_version": "1.1", + "implementation_version": "1", + "description": "API Layer to validate OpenXR runtime conformance", + "disable_environment": "KHRONOS_conformance_test_layer_disabled" + } +} diff --git a/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json.license b/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/test_apiLayer.cpp b/src/conformance/conformance_test/test_apiLayer.cpp new file mode 100644 index 00000000..70304aa9 --- /dev/null +++ b/src/conformance/conformance_test/test_apiLayer.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2019-2024, The Khronos Group Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "conformance_framework.h" +#include "conformance_utils.h" +#include "utilities/types_and_constants.h" +#include "utilities/utils.h" + +#include +#include + +#include +#include +#include + +namespace Conformance +{ + TEST_CASE("validApiLayer", "") + { + GlobalData& globalData = GetGlobalData(); + + // Layer for run-time conformance (and anything else global) + StringVec enabledApiLayers = globalData.enabledAPILayerNames; + // plus our test layer + enabledApiLayers.push_back("XR_APILAYER_KHRONOS_conformance_test_layer"); + + // Enable only the required platform extensions by default + auto enabledExtensions = StringVec(globalData.requiredPlatformInstanceExtensions); + + XrInstance instance = XR_NULL_HANDLE_CPP; + CleanupInstanceOnScopeExit cleanup(instance); + + XrInstanceCreateInfo createInfo{XR_TYPE_INSTANCE_CREATE_INFO}; + + strcpy(createInfo.applicationInfo.applicationName, "conformance test"); + createInfo.applicationInfo.applicationVersion = 1; + // Leave engineName and engineVersion empty, which is valid usage. + createInfo.applicationInfo.apiVersion = globalData.options.desiredApiVersionValue; + + if (globalData.requiredPlatformInstanceCreateStruct) { + createInfo.next = globalData.requiredPlatformInstanceCreateStruct; + } + + createInfo.enabledApiLayerCount = (uint32_t)enabledApiLayers.size(); + createInfo.enabledApiLayerNames = enabledApiLayers.data(); + createInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size(); + createInfo.enabledExtensionNames = enabledExtensions.data(); + + SECTION("XR_SUCCESS, only platform-required extensions enabled") + { + REQUIRE(XR_SUCCESS == xrCreateInstance(&createInfo, &instance)); + } + } + +} // namespace Conformance diff --git a/src/conformance/conformance_test/test_xrCreateInstance.cpp b/src/conformance/conformance_test/test_xrCreateInstance.cpp index 80ec6687..dbb41916 100644 --- a/src/conformance/conformance_test/test_xrCreateInstance.cpp +++ b/src/conformance/conformance_test/test_xrCreateInstance.cpp @@ -46,8 +46,9 @@ namespace Conformance // Leave engineName and engineVersion empty, which is valid usage. createInfo.applicationInfo.apiVersion = globalData.options.desiredApiVersionValue; - if (globalData.requiredPlatformInstanceCreateStruct) + if (globalData.requiredPlatformInstanceCreateStruct) { createInfo.next = globalData.requiredPlatformInstanceCreateStruct; + } // Layers enabled at least for run-time conformance StringVec enabledApiLayers = globalData.enabledAPILayerNames; diff --git a/src/conformance/test_layer/CMakeLists.txt b/src/conformance/test_layer/CMakeLists.txt new file mode 100644 index 00000000..5f12aec5 --- /dev/null +++ b/src/conformance/test_layer/CMakeLists.txt @@ -0,0 +1,129 @@ +# Copyright (c) 2019-2024, The Khronos Group Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +file( + GLOB + LOCAL_HEADERS + CONFIGURE_DEPENDS + "*.h" +) +file( + GLOB + LOCAL_SOURCE + CONFIGURE_DEPENDS + "*.cpp" +) + +configure_file( + conformance_test_layer.json + ${CMAKE_CURRENT_BINARY_DIR}/XrApiLayer_conformance_test_layer.json @ONLY +) + +add_library( + XrApiLayer_conformance_test_layer MODULE + ${LOCAL_SOURCE} + ${LOCAL_HEADERS} +) +target_link_libraries( + XrApiLayer_conformance_test_layer PRIVATE OpenXR::headers +) + +source_group("Headers" FILES ${LOCAL_HEADERS}) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_compile_options(XrApiLayer_conformance_test_layer PRIVATE -Wall) + target_link_libraries(XrApiLayer_conformance_test_layer PRIVATE m) +endif() + +if(ANDROID) + target_link_libraries( + XrApiLayer_conformance_test_layer PRIVATE ${ANDROID_LOG_LIBRARY} + ) +endif() + +# Dynamic Library: +# - Make build depend on the module definition/version script/export map +# - Add the linker flag (except windows) +if(WIN32) + target_sources( + XrApiLayer_conformance_test_layer + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_test_layer.def" + ) +elseif(APPLE) + set_target_properties( + XrApiLayer_conformance_test_layer + PROPERTIES + LINK_FLAGS + "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_test_layer.expsym" + ) + target_sources( + XrApiLayer_conformance_test_layer + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_test_layer.expsym" + ) +else() + set_target_properties( + XrApiLayer_conformance_test_layer + PROPERTIES + LINK_FLAGS + "-Wl,--version-script=\"${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_test_layer.map\"" + ) + target_sources( + XrApiLayer_conformance_test_layer + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_test_layer.map" + ) +endif() + +if(BUILD_CONFORMANCE_CLI) + # Copy conformance layer files to conformance_cli binary folder + add_custom_command( + TARGET XrApiLayer_conformance_test_layer + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy + $ + $ + COMMAND + ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/XrApiLayer_conformance_test_layer.json + $ + ) +endif() + +set_target_properties( + XrApiLayer_conformance_test_layer PROPERTIES FOLDER + ${CONFORMANCE_TESTS_FOLDER} +) + +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/XrApiLayer_conformance_test_layer.json + DESTINATION conformance +) + +install( + TARGETS XrApiLayer_conformance_test_layer + LIBRARY DESTINATION conformance + ARCHIVE DESTINATION conformance + RUNTIME DESTINATION conformance +) + +if(MSVC) + install( + FILES $ + DESTINATION conformance + OPTIONAL + ) +endif() diff --git a/src/conformance/test_layer/Negotiate.cpp b/src/conformance/test_layer/Negotiate.cpp new file mode 100644 index 00000000..85cea1cd --- /dev/null +++ b/src/conformance/test_layer/Negotiate.cpp @@ -0,0 +1,218 @@ +// Copyright (c) 2019-2024, The Khronos Group Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include +#include + +#if defined(__GNUC__) && __GNUC__ >= 4 +#define LAYER_EXPORT __attribute__((visibility("default"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define LAYER_EXPORT __attribute__((visibility("default"))) +#else +#define LAYER_EXPORT +#endif + +std::mutex g_instance_dispatch_mutex; +std::unordered_map g_instance_dispatch_map; + +namespace +{ + static const char* LAYER_NAME = "XR_APILAYER_KHRONOS_conformance_test_layer"; + + XRAPI_ATTR XrResult XRAPI_CALL TestLayer_CreateApiLayerInstance(const XrInstanceCreateInfo* createInfo, + const XrApiLayerCreateInfo* apiLayerInfo, XrInstance* instance) + { + // + // 1. Validate input data from Loader / previous layer + // + { + if (apiLayerInfo == nullptr || apiLayerInfo->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO || + apiLayerInfo->structVersion != XR_API_LAYER_CREATE_INFO_STRUCT_VERSION || + apiLayerInfo->structSize != sizeof(XrApiLayerCreateInfo)) { + return XR_ERROR_INITIALIZATION_FAILED; + } + + // apiLayerInfo->loaderInstance is deprecated and must be ignored + // apiLayerInfo->settings_file_location is currently unused. + + if (apiLayerInfo->nextInfo == nullptr || apiLayerInfo->nextInfo->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO || + apiLayerInfo->nextInfo->structVersion != XR_API_LAYER_NEXT_INFO_STRUCT_VERSION || + apiLayerInfo->nextInfo->structSize != sizeof(XrApiLayerNextInfo)) { + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (strcmp(apiLayerInfo->nextInfo->layerName, LAYER_NAME) != 0) { + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (apiLayerInfo->nextInfo->nextGetInstanceProcAddr == nullptr || + apiLayerInfo->nextInfo->nextCreateApiLayerInstance == nullptr) { + return XR_ERROR_INITIALIZATION_FAILED; + } + } + + // + // 2.A. Checks associated with https://gitlab.khronos.org/openxr/openxr/-/issues/2333 + // API Layers may need to know information about the other layers or the runtime, + // and to do so has to query information from the next chain. + // + { + PFN_xrEnumerateInstanceExtensionProperties xrEnumerateInstanceExtensionProperties{nullptr}; + XrResult res = apiLayerInfo->nextInfo->nextGetInstanceProcAddr( + XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties", + reinterpret_cast(&xrEnumerateInstanceExtensionProperties)); + if (res != XR_SUCCESS) { + return res; + } + + uint32_t extensionsCount = 0; + res = xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionsCount, nullptr); + if (res != XR_SUCCESS) { + return res; + } + + std::vector extensions(extensionsCount, {XR_TYPE_EXTENSION_PROPERTIES}); + res = xrEnumerateInstanceExtensionProperties(nullptr, extensionsCount, &extensionsCount, extensions.data()); + if (res != XR_SUCCESS) { + return res; + } + + // The API layer would now cache extensions vector or use it for validation. + } + + // + // 2.B. Checks for feature availability in instances. + // + { + XrInstance temporaryInstance{XR_NULL_HANDLE}; + + XrApiLayerCreateInfo temporaryNextApiLayerInfo = *apiLayerInfo; + temporaryNextApiLayerInfo.nextInfo = apiLayerInfo->nextInfo->next; + + XrResult res = apiLayerInfo->nextInfo->nextCreateApiLayerInstance(createInfo, &temporaryNextApiLayerInfo, &temporaryInstance); + + if (res != XR_SUCCESS) { + return res; + } + + // The API layer would now query the instance using other functions to validate feature availability. + + PFN_xrDestroyInstance xrDestroyInstance{nullptr}; + res = apiLayerInfo->nextInfo->nextGetInstanceProcAddr(temporaryInstance, "xrDestroyInstance", + reinterpret_cast(&xrDestroyInstance)); + res = xrDestroyInstance(temporaryInstance); + if (res != XR_SUCCESS) { + return res; + } + } + + // + // 3. Call down to the next layer's xrCreateApiLayerInstance and record next gipa. + // + { + + // Clone the XrApiLayerCreateInfo, but move to the next XrApiLayerNextInfo in the chain. nextInfo will be null + // if the loader's terminator function is going to be called (between the layer and the runtime) but this is OK + // because the loader's terminator function won't use it. + XrApiLayerCreateInfo newApiLayerInfo = *apiLayerInfo; + newApiLayerInfo.nextInfo = apiLayerInfo->nextInfo->next; + + XrResult nextLayerCreateRes = apiLayerInfo->nextInfo->nextCreateApiLayerInstance(createInfo, &newApiLayerInfo, instance); + if (XR_FAILED(nextLayerCreateRes)) { + // Some layer higher the chain failed - we return the error. + return nextLayerCreateRes; + } + + { + // Record the get instance proc addr for the next layer in the chain. + std::unique_lock lock(g_instance_dispatch_mutex); + g_instance_dispatch_map[*instance] = apiLayerInfo->nextInfo->nextGetInstanceProcAddr; + } + + return XR_SUCCESS; + } + } + + XRAPI_ATTR XrResult XRAPI_CALL TestLayer_GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) + { + if (instance == XR_NULL_HANDLE) { + // assert(false); + return XR_SUCCESS; + } + + PFN_xrGetInstanceProcAddr nextGetProcAddr = nullptr; + { + std::unique_lock lock(g_instance_dispatch_mutex); + auto it = g_instance_dispatch_map.find(instance); + if (it == g_instance_dispatch_map.end()) { + return XR_ERROR_HANDLE_INVALID; + } + nextGetProcAddr = it->second; + } + return nextGetProcAddr(instance, name, function); + } +} // namespace + +extern "C" LAYER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrNegotiateLoaderApiLayerInterface(const XrNegotiateLoaderInfo* loaderInfo, + const char* apiLayerName, + XrNegotiateApiLayerRequest* apiLayerRequest) +{ + if (loaderInfo == nullptr || loaderInfo->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO || + loaderInfo->structVersion != XR_LOADER_INFO_STRUCT_VERSION || loaderInfo->structSize != sizeof(XrNegotiateLoaderInfo)) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loaderInfo struct is not valid"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (loaderInfo->minInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION || + loaderInfo->maxInterfaceVersion < XR_CURRENT_LOADER_API_LAYER_VERSION) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader interface version is not in the range [minInterfaceVersion, maxInterfaceVersion]"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (loaderInfo->minApiVersion > XR_CURRENT_API_VERSION || loaderInfo->maxApiVersion < XR_CURRENT_API_VERSION) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader api version is not in the range [minApiVersion, maxApiVersion]"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (strcmp(apiLayerName, LAYER_NAME) != 0) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader layer name does not match expected name"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (apiLayerRequest == nullptr || apiLayerRequest->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST || + apiLayerRequest->structVersion != XR_API_LAYER_INFO_STRUCT_VERSION || + apiLayerRequest->structSize != sizeof(XrNegotiateApiLayerRequest)) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("apiLayerRequest is not valid"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + apiLayerRequest->layerInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION; + apiLayerRequest->layerApiVersion = XR_CURRENT_API_VERSION; + apiLayerRequest->getInstanceProcAddr = TestLayer_GetInstanceProcAddr; + apiLayerRequest->createApiLayerInstance = TestLayer_CreateApiLayerInstance; + + return XR_SUCCESS; +} diff --git a/src/conformance/test_layer/XrApiLayer_conformance_test_layer.def b/src/conformance/test_layer/XrApiLayer_conformance_test_layer.def new file mode 100644 index 00000000..f2874f18 --- /dev/null +++ b/src/conformance/test_layer/XrApiLayer_conformance_test_layer.def @@ -0,0 +1,6 @@ +; Copyright (c) 2019-2024, The Khronos Group Inc. +; +; SPDX-License-Identifier: Apache-2.0 + +EXPORTS + xrNegotiateLoaderApiLayerInterface \ No newline at end of file diff --git a/src/conformance/test_layer/XrApiLayer_conformance_test_layer.expsym b/src/conformance/test_layer/XrApiLayer_conformance_test_layer.expsym new file mode 100644 index 00000000..c7892e0c --- /dev/null +++ b/src/conformance/test_layer/XrApiLayer_conformance_test_layer.expsym @@ -0,0 +1,5 @@ +# Copyright (c) 2019-2024, The Khronos Group Inc. +# +# SPDX-License-Identifier: Apache-2.0 + +_xrNegotiateLoaderApiLayerInterface diff --git a/src/conformance/test_layer/XrApiLayer_conformance_test_layer.map b/src/conformance/test_layer/XrApiLayer_conformance_test_layer.map new file mode 100644 index 00000000..5fc8ab24 --- /dev/null +++ b/src/conformance/test_layer/XrApiLayer_conformance_test_layer.map @@ -0,0 +1,12 @@ +/* +Copyright (c) 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 +*/ + +{ + global: + xrNegotiateLoaderApiLayerInterface; + local: + *; +}; diff --git a/src/conformance/test_layer/conformance_test_layer.json b/src/conformance/test_layer/conformance_test_layer.json new file mode 100644 index 00000000..7ad78f78 --- /dev/null +++ b/src/conformance/test_layer/conformance_test_layer.json @@ -0,0 +1,11 @@ +{ + "file_format_version": "1.0.0", + "api_layer": { + "name": "XR_APILAYER_KHRONOS_conformance_test_layer", + "library_path": "./@CMAKE_SHARED_MODULE_PREFIX@XrApiLayer_conformance_test_layer@CMAKE_SHARED_MODULE_SUFFIX@", + "api_version": "1.0", + "implementation_version": "1", + "description": "API Layer to validate OpenXR runtime conformance", + "disable_environment": "KHRONOS_conformance_test_layer_disabled" + } +} diff --git a/src/conformance/test_layer/conformance_test_layer.json.license b/src/conformance/test_layer/conformance_test_layer.json.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/test_layer/conformance_test_layer.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0