diff options
Diffstat (limited to 'thirdparty/openxr/src/common')
-rw-r--r-- | thirdparty/openxr/src/common/extra_algorithms.h | 47 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/filesystem_utils.cpp | 322 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/filesystem_utils.hpp | 46 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/hex_and_handles.h | 108 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/loader_interfaces.h | 114 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/object_info.cpp | 276 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/object_info.h | 229 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/platform_utils.hpp | 345 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/stdfs_conditions.h | 45 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/xr_dependencies.h | 89 | ||||
-rw-r--r-- | thirdparty/openxr/src/common/xr_linear.h | 787 |
11 files changed, 2408 insertions, 0 deletions
diff --git a/thirdparty/openxr/src/common/extra_algorithms.h b/thirdparty/openxr/src/common/extra_algorithms.h new file mode 100644 index 0000000000..64af4d08ff --- /dev/null +++ b/thirdparty/openxr/src/common/extra_algorithms.h @@ -0,0 +1,47 @@ +// Copyright (c) 2017-2022, The Khronos Group Inc. +// Copyright (c) 2017-2019 Valve Corporation +// Copyright (c) 2017-2019 LunarG, Inc. +// Copyright (c) 2019 Collabora, Ltd. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com> +// + +/*! + * @file + * + * Additional functions along the lines of the standard library algorithms. + */ + +#pragma once + +#include <algorithm> +#include <vector> + +/// Like std::remove_if, except it works on associative containers and it actually removes this. +/// +/// The iterator stuff in here is subtle - .erase() invalidates only that iterator, but it returns a non-invalidated iterator to the +/// next valid element which we can use instead of incrementing. +template <typename T, typename Pred> +static inline void map_erase_if(T &container, Pred &&predicate) { + for (auto it = container.begin(); it != container.end();) { + if (predicate(*it)) { + it = container.erase(it); + } else { + ++it; + } + } +} + +/*! + * Moves all elements matching the predicate to the end of the vector then erases them. + * + * Combines the two parts of the erase-remove idiom to simplify things and avoid accidentally using the wrong erase overload. + */ +template <typename T, typename Alloc, typename Pred> +static inline void vector_remove_if_and_erase(std::vector<T, Alloc> &vec, Pred &&predicate) { + auto b = vec.begin(); + auto e = vec.end(); + vec.erase(std::remove_if(b, e, std::forward<Pred>(predicate)), e); +} diff --git a/thirdparty/openxr/src/common/filesystem_utils.cpp b/thirdparty/openxr/src/common/filesystem_utils.cpp new file mode 100644 index 0000000000..d3d4182fb9 --- /dev/null +++ b/thirdparty/openxr/src/common/filesystem_utils.cpp @@ -0,0 +1,322 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG, Inc. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Authors: Mark Young <marky@lunarg.com> +// Nat Brown <natb@valvesoftware.com> +// + +#include "filesystem_utils.hpp" + +#include "platform_utils.hpp" + +#include <cstring> +#include <string> + +#if defined DISABLE_STD_FILESYSTEM +#define USE_EXPERIMENTAL_FS 0 +#define USE_FINAL_FS 0 + +#else +#include "stdfs_conditions.h" +#endif + +#if USE_FINAL_FS == 1 +#include <filesystem> +#define FS_PREFIX std::filesystem +#elif USE_EXPERIMENTAL_FS == 1 +#include <experimental/filesystem> +#define FS_PREFIX std::experimental::filesystem +#elif defined(XR_USE_PLATFORM_WIN32) +// Windows fallback includes +#include <stdint.h> +#include <direct.h> +#else +// Linux/Apple fallback includes +#include <sys/stat.h> +#include <unistd.h> +#include <limits.h> +#include <stdlib.h> +#include <dirent.h> +#endif + +#if defined(XR_USE_PLATFORM_WIN32) +#define PATH_SEPARATOR ';' +#define DIRECTORY_SYMBOL '\\' +#define ALTERNATE_DIRECTORY_SYMBOL '/' +#else +#define PATH_SEPARATOR ':' +#define DIRECTORY_SYMBOL '/' +#endif + +#if (USE_FINAL_FS == 1) || (USE_EXPERIMENTAL_FS == 1) +// We can use one of the C++ filesystem packages + +bool FileSysUtilsIsRegularFile(const std::string& path) { return FS_PREFIX::is_regular_file(path); } + +bool FileSysUtilsIsDirectory(const std::string& path) { return FS_PREFIX::is_directory(path); } + +bool FileSysUtilsPathExists(const std::string& path) { return FS_PREFIX::exists(path); } + +bool FileSysUtilsIsAbsolutePath(const std::string& path) { + FS_PREFIX::path file_path(path); + return file_path.is_absolute(); +} + +bool FileSysUtilsGetCurrentPath(std::string& path) { + FS_PREFIX::path cur_path = FS_PREFIX::current_path(); + path = cur_path.string(); + return true; +} + +bool FileSysUtilsGetParentPath(const std::string& file_path, std::string& parent_path) { + FS_PREFIX::path path_var(file_path); + parent_path = path_var.parent_path().string(); + return true; +} + +bool FileSysUtilsGetAbsolutePath(const std::string& path, std::string& absolute) { + absolute = FS_PREFIX::absolute(path).string(); + return true; +} + +bool FileSysUtilsGetCanonicalPath(const std::string& path, std::string& canonical) { +#if defined(XR_USE_PLATFORM_WIN32) + // std::filesystem::canonical fails on UWP and must be avoided. Further, PathCchCanonicalize is not available on Windows 7 and + // PathCanonicalizeW is not available on UWP. However, symbolic links are not important on Windows since the loader uses the + // registry for indirection instead, and so this function can be a no-op on Windows. + canonical = path; +#else + canonical = FS_PREFIX::canonical(path).string(); +#endif + return true; +} + +bool FileSysUtilsCombinePaths(const std::string& parent, const std::string& child, std::string& combined) { + FS_PREFIX::path parent_path(parent); + FS_PREFIX::path child_path(child); + FS_PREFIX::path full_path = parent_path / child_path; + combined = full_path.string(); + return true; +} + +bool FileSysUtilsParsePathList(std::string& path_list, std::vector<std::string>& paths) { + std::string::size_type start = 0; + std::string::size_type location = path_list.find(PATH_SEPARATOR); + while (location != std::string::npos) { + paths.push_back(path_list.substr(start, location)); + start = location + 1; + location = path_list.find(PATH_SEPARATOR, start); + } + paths.push_back(path_list.substr(start, location)); + return true; +} + +bool FileSysUtilsFindFilesInPath(const std::string& path, std::vector<std::string>& files) { + for (auto& dir_iter : FS_PREFIX::directory_iterator(path)) { + files.push_back(dir_iter.path().filename().string()); + } + return true; +} + +#elif defined(XR_OS_WINDOWS) + +// For pre C++17 compiler that doesn't support experimental filesystem + +bool FileSysUtilsIsRegularFile(const std::string& path) { + const DWORD attr = GetFileAttributesW(utf8_to_wide(path).c_str()); + return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY); +} + +bool FileSysUtilsIsDirectory(const std::string& path) { + const DWORD attr = GetFileAttributesW(utf8_to_wide(path).c_str()); + return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY); +} + +bool FileSysUtilsPathExists(const std::string& path) { + return (GetFileAttributesW(utf8_to_wide(path).c_str()) != INVALID_FILE_ATTRIBUTES); +} + +bool FileSysUtilsIsAbsolutePath(const std::string& path) { + bool pathStartsWithDir = (path.size() >= 1) && ((path[0] == DIRECTORY_SYMBOL) || (path[0] == ALTERNATE_DIRECTORY_SYMBOL)); + + bool pathStartsWithDrive = + (path.size() >= 3) && (path[1] == ':' && (path[2] == DIRECTORY_SYMBOL || path[2] == ALTERNATE_DIRECTORY_SYMBOL)); + + return pathStartsWithDir || pathStartsWithDrive; +} + +bool FileSysUtilsGetCurrentPath(std::string& path) { + wchar_t tmp_path[MAX_PATH]; + if (nullptr != _wgetcwd(tmp_path, MAX_PATH - 1)) { + path = wide_to_utf8(tmp_path); + return true; + } + return false; +} + +bool FileSysUtilsGetParentPath(const std::string& file_path, std::string& parent_path) { + std::string full_path; + if (FileSysUtilsGetAbsolutePath(file_path, full_path)) { + std::string::size_type lastSeparator = full_path.find_last_of(DIRECTORY_SYMBOL); + parent_path = (lastSeparator == 0) ? full_path : full_path.substr(0, lastSeparator); + return true; + } + return false; +} + +bool FileSysUtilsGetAbsolutePath(const std::string& path, std::string& absolute) { + wchar_t tmp_path[MAX_PATH]; + if (0 != GetFullPathNameW(utf8_to_wide(path).c_str(), MAX_PATH, tmp_path, NULL)) { + absolute = wide_to_utf8(tmp_path); + return true; + } + return false; +} + +bool FileSysUtilsGetCanonicalPath(const std::string& path, std::string& absolute) { + // PathCchCanonicalize is not available on Windows 7 and PathCanonicalizeW is not available on UWP. However, symbolic links are + // not important on Windows since the loader uses the registry for indirection instead, and so this function can be a no-op on + // Windows. + absolute = path; + return true; +} + +bool FileSysUtilsCombinePaths(const std::string& parent, const std::string& child, std::string& combined) { + std::string::size_type parent_len = parent.length(); + if (0 == parent_len || "." == parent || ".\\" == parent || "./" == parent) { + combined = child; + return true; + } + char last_char = parent[parent_len - 1]; + if ((last_char == DIRECTORY_SYMBOL) || (last_char == ALTERNATE_DIRECTORY_SYMBOL)) { + parent_len--; + } + combined = parent.substr(0, parent_len) + DIRECTORY_SYMBOL + child; + return true; +} + +bool FileSysUtilsParsePathList(std::string& path_list, std::vector<std::string>& paths) { + std::string::size_type start = 0; + std::string::size_type location = path_list.find(PATH_SEPARATOR); + while (location != std::string::npos) { + paths.push_back(path_list.substr(start, location)); + start = location + 1; + location = path_list.find(PATH_SEPARATOR, start); + } + paths.push_back(path_list.substr(start, location)); + return true; +} + +bool FileSysUtilsFindFilesInPath(const std::string& path, std::vector<std::string>& files) { + std::string searchPath; + FileSysUtilsCombinePaths(path, "*", searchPath); + + WIN32_FIND_DATAW file_data; + HANDLE file_handle = FindFirstFileW(utf8_to_wide(searchPath).c_str(), &file_data); + if (file_handle != INVALID_HANDLE_VALUE) { + do { + if (!(file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + files.push_back(wide_to_utf8(file_data.cFileName)); + } + } while (FindNextFileW(file_handle, &file_data)); + return true; + } + return false; +} + +#else // XR_OS_LINUX/XR_OS_APPLE fallback + +// simple POSIX-compatible implementation of the <filesystem> pieces used by OpenXR + +bool FileSysUtilsIsRegularFile(const std::string& path) { + struct stat path_stat; + stat(path.c_str(), &path_stat); + return S_ISREG(path_stat.st_mode); +} + +bool FileSysUtilsIsDirectory(const std::string& path) { + struct stat path_stat; + stat(path.c_str(), &path_stat); + return S_ISDIR(path_stat.st_mode); +} + +bool FileSysUtilsPathExists(const std::string& path) { return (access(path.c_str(), F_OK) != -1); } + +bool FileSysUtilsIsAbsolutePath(const std::string& path) { return (path[0] == DIRECTORY_SYMBOL); } + +bool FileSysUtilsGetCurrentPath(std::string& path) { + char tmp_path[PATH_MAX]; + if (nullptr != getcwd(tmp_path, PATH_MAX - 1)) { + path = tmp_path; + return true; + } + return false; +} + +bool FileSysUtilsGetParentPath(const std::string& file_path, std::string& parent_path) { + std::string full_path; + if (FileSysUtilsGetAbsolutePath(file_path, full_path)) { + std::string::size_type lastSeparator = full_path.find_last_of(DIRECTORY_SYMBOL); + parent_path = (lastSeparator == 0) ? full_path : full_path.substr(0, lastSeparator); + return true; + } + return false; +} + +bool FileSysUtilsGetAbsolutePath(const std::string& path, std::string& absolute) { + // canonical path is absolute + return FileSysUtilsGetCanonicalPath(path, absolute); +} + +bool FileSysUtilsGetCanonicalPath(const std::string& path, std::string& canonical) { + char buf[PATH_MAX]; + if (nullptr != realpath(path.c_str(), buf)) { + canonical = buf; + return true; + } + return false; +} + +bool FileSysUtilsCombinePaths(const std::string& parent, const std::string& child, std::string& combined) { + std::string::size_type parent_len = parent.length(); + if (0 == parent_len || "." == parent || "./" == parent) { + combined = child; + return true; + } + char last_char = parent[parent_len - 1]; + if (last_char == DIRECTORY_SYMBOL) { + parent_len--; + } + combined = parent.substr(0, parent_len) + DIRECTORY_SYMBOL + child; + return true; +} + +bool FileSysUtilsParsePathList(std::string& path_list, std::vector<std::string>& paths) { + std::string::size_type start = 0; + std::string::size_type location = path_list.find(PATH_SEPARATOR); + while (location != std::string::npos) { + paths.push_back(path_list.substr(start, location)); + start = location + 1; + location = path_list.find(PATH_SEPARATOR, start); + } + paths.push_back(path_list.substr(start, location)); + return true; +} + +bool FileSysUtilsFindFilesInPath(const std::string& path, std::vector<std::string>& files) { + DIR* dir = opendir(path.c_str()); + if (dir == nullptr) { + return false; + } + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + files.emplace_back(entry->d_name); + } + closedir(dir); + return true; +} + +#endif diff --git a/thirdparty/openxr/src/common/filesystem_utils.hpp b/thirdparty/openxr/src/common/filesystem_utils.hpp new file mode 100644 index 0000000000..4a5c987e7b --- /dev/null +++ b/thirdparty/openxr/src/common/filesystem_utils.hpp @@ -0,0 +1,46 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG, Inc. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Author: Mark Young <marky@lunarg.com> +// + +#pragma once + +#include <string> +#include <vector> + +// Determine if the path indicates a regular file (not a directory or symbolic link) +bool FileSysUtilsIsRegularFile(const std::string& path); + +// Determine if the path indicates a directory +bool FileSysUtilsIsDirectory(const std::string& path); + +// Determine if the provided path exists on the filesystem +bool FileSysUtilsPathExists(const std::string& path); + +// Get the current directory +bool FileSysUtilsGetCurrentPath(std::string& path); + +// Get the parent path of a file +bool FileSysUtilsGetParentPath(const std::string& file_path, std::string& parent_path); + +// Determine if the provided path is an absolute path +bool FileSysUtilsIsAbsolutePath(const std::string& path); + +// Get the absolute path for a provided file +bool FileSysUtilsGetAbsolutePath(const std::string& path, std::string& absolute); + +// Get the absolute path for a provided file +bool FileSysUtilsGetCanonicalPath(const std::string& path, std::string& canonical); + +// Combine a parent and child directory +bool FileSysUtilsCombinePaths(const std::string& parent, const std::string& child, std::string& combined); + +// Parse out individual paths in a path list +bool FileSysUtilsParsePathList(std::string& path_list, std::vector<std::string>& paths); + +// Record all the filenames for files found in the provided path. +bool FileSysUtilsFindFilesInPath(const std::string& path, std::vector<std::string>& files); diff --git a/thirdparty/openxr/src/common/hex_and_handles.h b/thirdparty/openxr/src/common/hex_and_handles.h new file mode 100644 index 0000000000..341013d32b --- /dev/null +++ b/thirdparty/openxr/src/common/hex_and_handles.h @@ -0,0 +1,108 @@ +// Copyright (c) 2017-2022, The Khronos Group Inc. +// Copyright (c) 2017-2019 Valve Corporation +// Copyright (c) 2017-2019 LunarG, Inc. +// Copyright (c) 2019 Collabora, Ltd. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com> +// + +/*! + * @file + * + * Some utilities, primarily for working with OpenXR handles in a generic way. + */ + +#pragma once + +#include <openxr/openxr.h> + +#include <string> +#include <stdint.h> + +inline std::string to_hex(const uint8_t* const data, size_t bytes) { + std::string out(2 + bytes * 2, '?'); + out[0] = '0'; + out[1] = 'x'; + static const char* hex = "0123456789abcdef"; + auto ch = out.end(); + for (size_t i = 0; i < bytes; ++i) { + auto b = data[i]; + *--ch = hex[(b >> 0) & 0xf]; + *--ch = hex[(b >> 4) & 0xf]; + } + return out; +} + +template <typename T> +inline std::string to_hex(const T& data) { + return to_hex(reinterpret_cast<const uint8_t* const>(&data), sizeof(data)); +} + +#if XR_PTR_SIZE == 8 +/// Convert a handle into a same-sized integer. +template <typename T> +static inline uint64_t MakeHandleGeneric(T handle) { + return reinterpret_cast<uint64_t>(handle); +} + +/// Treat an integer as a handle +template <typename T> +static inline T& TreatIntegerAsHandle(uint64_t& handle) { + return reinterpret_cast<T&>(handle); +} + +/// @overload +template <typename T> +static inline T const& TreatIntegerAsHandle(uint64_t const& handle) { + return reinterpret_cast<T const&>(handle); +} + +/// Does a correctly-sized integer represent a null handle? +static inline bool IsIntegerNullHandle(uint64_t handle) { return XR_NULL_HANDLE == reinterpret_cast<void*>(handle); } + +#else + +/// Convert a handle into a same-sized integer: no-op on 32-bit systems +static inline uint64_t MakeHandleGeneric(uint64_t handle) { return handle; } + +/// Treat an integer as a handle: no-op on 32-bit systems +template <typename T> +static inline T& TreatIntegerAsHandle(uint64_t& handle) { + return handle; +} + +/// @overload +template <typename T> +static inline T const& TreatIntegerAsHandle(uint64_t const& handle) { + return handle; +} + +/// Does a correctly-sized integer represent a null handle? +static inline bool IsIntegerNullHandle(uint64_t handle) { return XR_NULL_HANDLE == handle; } + +#endif + +/// Turns a uint64_t into a string formatted as hex. +/// +/// The core of the HandleToHexString implementation is in here. +inline std::string Uint64ToHexString(uint64_t val) { return to_hex(val); } + +/// Turns a uint32_t into a string formatted as hex. +inline std::string Uint32ToHexString(uint32_t val) { return to_hex(val); } + +/// Turns an OpenXR handle into a string formatted as hex. +template <typename T> +inline std::string HandleToHexString(T handle) { + return to_hex(handle); +} + +/// Turns a pointer-sized integer into a string formatted as hex. +inline std::string UintptrToHexString(uintptr_t val) { return to_hex(val); } + +/// Convert a pointer to a string formatted as hex. +template <typename T> +inline std::string PointerToHexString(T const* ptr) { + return to_hex(ptr); +} diff --git a/thirdparty/openxr/src/common/loader_interfaces.h b/thirdparty/openxr/src/common/loader_interfaces.h new file mode 100644 index 0000000000..9c74ed16f3 --- /dev/null +++ b/thirdparty/openxr/src/common/loader_interfaces.h @@ -0,0 +1,114 @@ +// Copyright (c) 2017-2022, The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG, Inc. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Author: Mark Young <marky@lunarg.com> +// + +#pragma once + +#include <openxr/openxr.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// Forward declare. +typedef struct XrApiLayerCreateInfo XrApiLayerCreateInfo; + +// Function pointer prototype for the xrCreateApiLayerInstance function used in place of xrCreateInstance. +// This function allows us to pass special API layer information to each layer during the process of creating an Instance. +typedef XrResult(XRAPI_PTR *PFN_xrCreateApiLayerInstance)(const XrInstanceCreateInfo *info, + const XrApiLayerCreateInfo *apiLayerInfo, XrInstance *instance); + +// Loader/API Layer Interface versions +// 1 - First version, introduces negotiation structure and functions +#define XR_CURRENT_LOADER_API_LAYER_VERSION 1 + +// Loader/Runtime Interface versions +// 1 - First version, introduces negotiation structure and functions +#define XR_CURRENT_LOADER_RUNTIME_VERSION 1 + +// Version negotiation values +typedef enum XrLoaderInterfaceStructs { + XR_LOADER_INTERFACE_STRUCT_UNINTIALIZED = 0, + XR_LOADER_INTERFACE_STRUCT_LOADER_INFO, + XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST, + XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST, + XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO, + XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO, +} XrLoaderInterfaceStructs; + +#define XR_LOADER_INFO_STRUCT_VERSION 1 +typedef struct XrNegotiateLoaderInfo { + XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_LOADER_INFO + uint32_t structVersion; // XR_LOADER_INFO_STRUCT_VERSION + size_t structSize; // sizeof(XrNegotiateLoaderInfo) + uint32_t minInterfaceVersion; + uint32_t maxInterfaceVersion; + XrVersion minApiVersion; + XrVersion maxApiVersion; +} XrNegotiateLoaderInfo; + +#define XR_API_LAYER_INFO_STRUCT_VERSION 1 +typedef struct XrNegotiateApiLayerRequest { + XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST + uint32_t structVersion; // XR_API_LAYER_INFO_STRUCT_VERSION + size_t structSize; // sizeof(XrNegotiateApiLayerRequest) + uint32_t layerInterfaceVersion; // CURRENT_LOADER_API_LAYER_VERSION + XrVersion layerApiVersion; + PFN_xrGetInstanceProcAddr getInstanceProcAddr; + PFN_xrCreateApiLayerInstance createApiLayerInstance; +} XrNegotiateApiLayerRequest; + +#define XR_RUNTIME_INFO_STRUCT_VERSION 1 +typedef struct XrNegotiateRuntimeRequest { + XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST + uint32_t structVersion; // XR_RUNTIME_INFO_STRUCT_VERSION + size_t structSize; // sizeof(XrNegotiateRuntimeRequest) + uint32_t runtimeInterfaceVersion; // CURRENT_LOADER_RUNTIME_VERSION + XrVersion runtimeApiVersion; + PFN_xrGetInstanceProcAddr getInstanceProcAddr; +} XrNegotiateRuntimeRequest; + +// 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. +typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderApiLayerInterface)(const XrNegotiateLoaderInfo *loaderInfo, + const char *apiLayerName, + XrNegotiateApiLayerRequest *apiLayerRequest); + +// Function used to negotiate an interface betewen the loader and a runtime. Each runtime should expose +// at least this function. +typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderRuntimeInterface)(const XrNegotiateLoaderInfo *loaderInfo, + XrNegotiateRuntimeRequest *runtimeRequest); + +// Forward declare. +typedef struct XrApiLayerNextInfo XrApiLayerNextInfo; + +#define XR_API_LAYER_NEXT_INFO_STRUCT_VERSION 1 +struct XrApiLayerNextInfo { + XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO + uint32_t structVersion; // XR_API_LAYER_NEXT_INFO_STRUCT_VERSION + size_t structSize; // sizeof(XrApiLayerNextInfo) + char layerName[XR_MAX_API_LAYER_NAME_SIZE]; // Name of API layer which should receive this info + PFN_xrGetInstanceProcAddr nextGetInstanceProcAddr; // Pointer to next API layer's xrGetInstanceProcAddr + PFN_xrCreateApiLayerInstance nextCreateApiLayerInstance; // Pointer to next API layer's xrCreateApiLayerInstance + XrApiLayerNextInfo *next; // Pointer to the next API layer info in the sequence +}; + +#define XR_API_LAYER_MAX_SETTINGS_PATH_SIZE 512 +#define XR_API_LAYER_CREATE_INFO_STRUCT_VERSION 1 +typedef struct XrApiLayerCreateInfo { + XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO + uint32_t structVersion; // XR_API_LAYER_CREATE_INFO_STRUCT_VERSION + size_t structSize; // sizeof(XrApiLayerCreateInfo) + void *loaderInstance; // Pointer to the LoaderInstance class + char settings_file_location[XR_API_LAYER_MAX_SETTINGS_PATH_SIZE]; // Location to the found settings file (or empty '\0') + XrApiLayerNextInfo *nextInfo; // Pointer to the next API layer's Info +} XrApiLayerCreateInfo; + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/thirdparty/openxr/src/common/object_info.cpp b/thirdparty/openxr/src/common/object_info.cpp new file mode 100644 index 0000000000..95b5aaf404 --- /dev/null +++ b/thirdparty/openxr/src/common/object_info.cpp @@ -0,0 +1,276 @@ +// Copyright (c) 2017-2022, The Khronos Group Inc. +// Copyright (c) 2017-2019 Valve Corporation +// Copyright (c) 2017-2019 LunarG, Inc. +// Copyright (c) 2019 Collabora, Ltd. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Authors: Mark Young <marky@lunarg.com> +// Ryan Pavlik <ryan.pavlik@collabora.com> +// Dave Houlton <daveh@lunarg.com> +// + +#include "object_info.h" + +#include "extra_algorithms.h" +#include "hex_and_handles.h" + +#include <openxr/openxr.h> + +#include <algorithm> +#include <iterator> +#include <memory> +#include <sstream> +#include <string> +#include <vector> + +#include "memory.h" + +std::string XrSdkLogObjectInfo::ToString() const { + std::ostringstream oss; + oss << Uint64ToHexString(handle); + if (!name.empty()) { + oss << " (" << name << ")"; + } + return oss.str(); +} + +void ObjectInfoCollection::AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name) { + // If name is empty, we should erase it + if (object_name.empty()) { + RemoveObject(object_handle, object_type); + return; + } + + // Otherwise, add it or update the name + XrSdkLogObjectInfo new_obj = {object_handle, object_type}; + + // If it already exists, update the name + auto lookup_info = LookUpStoredObjectInfo(new_obj); + if (lookup_info != nullptr) { + lookup_info->name = object_name; + return; + } + + // It doesn't exist, so add a new info block + new_obj.name = object_name; + object_info_.push_back(new_obj); +} + +void ObjectInfoCollection::RemoveObject(uint64_t object_handle, XrObjectType object_type) { + vector_remove_if_and_erase( + object_info_, [=](XrSdkLogObjectInfo const& info) { return info.handle == object_handle && info.type == object_type; }); +} + +XrSdkLogObjectInfo const* ObjectInfoCollection::LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info) const { + auto e = object_info_.end(); + auto it = std::find_if(object_info_.begin(), e, [&](XrSdkLogObjectInfo const& stored) { return Equivalent(stored, info); }); + if (it != e) { + return &(*it); + } + return nullptr; +} + +XrSdkLogObjectInfo* ObjectInfoCollection::LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info) { + auto e = object_info_.end(); + auto it = std::find_if(object_info_.begin(), e, [&](XrSdkLogObjectInfo const& stored) { return Equivalent(stored, info); }); + if (it != e) { + return &(*it); + } + return nullptr; +} + +bool ObjectInfoCollection::LookUpObjectName(XrDebugUtilsObjectNameInfoEXT& info) const { + auto info_lookup = LookUpStoredObjectInfo(info.objectHandle, info.objectType); + if (info_lookup != nullptr) { + info.objectName = info_lookup->name.c_str(); + return true; + } + return false; +} + +bool ObjectInfoCollection::LookUpObjectName(XrSdkLogObjectInfo& info) const { + auto info_lookup = LookUpStoredObjectInfo(info); + if (info_lookup != nullptr) { + info.name = info_lookup->name; + return true; + } + return false; +} + +static std::vector<XrDebugUtilsObjectNameInfoEXT> PopulateObjectNameInfo(std::vector<XrSdkLogObjectInfo> const& obj) { + std::vector<XrDebugUtilsObjectNameInfoEXT> ret; + ret.reserve(obj.size()); + std::transform(obj.begin(), obj.end(), std::back_inserter(ret), [](XrSdkLogObjectInfo const& info) { + return XrDebugUtilsObjectNameInfoEXT{XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, nullptr, info.type, info.handle, + info.name.c_str()}; + }); + return ret; +} + +NamesAndLabels::NamesAndLabels(std::vector<XrSdkLogObjectInfo> obj, std::vector<XrDebugUtilsLabelEXT> lab) + : sdk_objects(std::move(obj)), objects(PopulateObjectNameInfo(sdk_objects)), labels(std::move(lab)) {} + +void NamesAndLabels::PopulateCallbackData(XrDebugUtilsMessengerCallbackDataEXT& callback_data) const { + callback_data.objects = objects.empty() ? nullptr : const_cast<XrDebugUtilsObjectNameInfoEXT*>(objects.data()); + callback_data.objectCount = static_cast<uint32_t>(objects.size()); + callback_data.sessionLabels = labels.empty() ? nullptr : const_cast<XrDebugUtilsLabelEXT*>(labels.data()); + callback_data.sessionLabelCount = static_cast<uint32_t>(labels.size()); +} + +void DebugUtilsData::LookUpSessionLabels(XrSession session, std::vector<XrDebugUtilsLabelEXT>& labels) const { + auto session_label_iterator = session_labels_.find(session); + if (session_label_iterator != session_labels_.end()) { + auto& XrSdkSessionLabels = *session_label_iterator->second; + // Copy the debug utils labels in reverse order in the the labels vector. + std::transform(XrSdkSessionLabels.rbegin(), XrSdkSessionLabels.rend(), std::back_inserter(labels), + [](XrSdkSessionLabelPtr const& label) { return label->debug_utils_label; }); + } +} + +XrSdkSessionLabel::XrSdkSessionLabel(const XrDebugUtilsLabelEXT& label_info, bool individual) + : label_name(label_info.labelName), debug_utils_label(label_info), is_individual_label(individual) { + // Update the c string pointer to the one we hold. + debug_utils_label.labelName = label_name.c_str(); +} + +XrSdkSessionLabelPtr XrSdkSessionLabel::make(const XrDebugUtilsLabelEXT& label_info, bool individual) { + XrSdkSessionLabelPtr ret(new XrSdkSessionLabel(label_info, individual)); + return ret; +} +void DebugUtilsData::AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name) { + object_info_.AddObjectName(object_handle, object_type, object_name); +} + +// We always want to remove the old individual label before we do anything else. +// So, do that in it's own method +void DebugUtilsData::RemoveIndividualLabel(XrSdkSessionLabelList& label_vec) { + if (!label_vec.empty() && label_vec.back()->is_individual_label) { + label_vec.pop_back(); + } +} + +XrSdkSessionLabelList* DebugUtilsData::GetSessionLabelList(XrSession session) { + auto session_label_iterator = session_labels_.find(session); + if (session_label_iterator == session_labels_.end()) { + return nullptr; + } + return session_label_iterator->second.get(); +} + +XrSdkSessionLabelList& DebugUtilsData::GetOrCreateSessionLabelList(XrSession session) { + XrSdkSessionLabelList* vec_ptr = GetSessionLabelList(session); + if (vec_ptr == nullptr) { + std::unique_ptr<XrSdkSessionLabelList> vec(new XrSdkSessionLabelList); + vec_ptr = vec.get(); + session_labels_[session] = std::move(vec); + } + return *vec_ptr; +} + +void DebugUtilsData::BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT& label_info) { + auto& vec = GetOrCreateSessionLabelList(session); + + // Individual labels do not stay around in the transition into a new label region + RemoveIndividualLabel(vec); + + // Start the new label region + vec.emplace_back(XrSdkSessionLabel::make(label_info, false)); +} + +void DebugUtilsData::EndLabelRegion(XrSession session) { + XrSdkSessionLabelList* vec_ptr = GetSessionLabelList(session); + if (vec_ptr == nullptr) { + return; + } + + // Individual labels do not stay around in the transition out of label region + RemoveIndividualLabel(*vec_ptr); + + // Remove the last label region + if (!vec_ptr->empty()) { + vec_ptr->pop_back(); + } +} + +void DebugUtilsData::InsertLabel(XrSession session, const XrDebugUtilsLabelEXT& label_info) { + auto& vec = GetOrCreateSessionLabelList(session); + + // Remove any individual layer that might already be there + RemoveIndividualLabel(vec); + + // Insert a new individual label + vec.emplace_back(XrSdkSessionLabel::make(label_info, true)); +} + +void DebugUtilsData::DeleteObject(uint64_t object_handle, XrObjectType object_type) { + object_info_.RemoveObject(object_handle, object_type); + + if (object_type == XR_OBJECT_TYPE_SESSION) { + auto session = TreatIntegerAsHandle<XrSession>(object_handle); + XrSdkSessionLabelList* vec_ptr = GetSessionLabelList(session); + if (vec_ptr != nullptr) { + session_labels_.erase(session); + } + } +} + +void DebugUtilsData::DeleteSessionLabels(XrSession session) { session_labels_.erase(session); } + +NamesAndLabels DebugUtilsData::PopulateNamesAndLabels(std::vector<XrSdkLogObjectInfo> objects) const { + std::vector<XrDebugUtilsLabelEXT> labels; + for (auto& obj : objects) { + // Check for any names that have been associated with the objects and set them up here + object_info_.LookUpObjectName(obj); + // If this is a session, see if there are any labels associated with it for us to add + // to the callback content. + if (XR_OBJECT_TYPE_SESSION == obj.type) { + LookUpSessionLabels(obj.GetTypedHandle<XrSession>(), labels); + } + } + + return {objects, labels}; +} + +void DebugUtilsData::WrapCallbackData(AugmentedCallbackData* aug_data, + const XrDebugUtilsMessengerCallbackDataEXT* callback_data) const { + // If there's nothing to add, just return the original data as the augmented copy + aug_data->exported_data = callback_data; + if (object_info_.Empty() || callback_data->objectCount == 0) { + return; + } + + // Inspect each of the callback objects + bool name_found = false; + for (uint32_t obj = 0; obj < callback_data->objectCount; ++obj) { + auto& current_obj = callback_data->objects[obj]; + name_found |= (nullptr != object_info_.LookUpStoredObjectInfo(current_obj.objectHandle, current_obj.objectType)); + + // If this is a session, record any labels associated with it + if (XR_OBJECT_TYPE_SESSION == current_obj.objectType) { + XrSession session = TreatIntegerAsHandle<XrSession>(current_obj.objectHandle); + LookUpSessionLabels(session, aug_data->labels); + } + } + + // If we found nothing to add, return the original data + if (!name_found && aug_data->labels.empty()) { + return; + } + + // Found additional data - modify an internal copy and return that as the exported data + memcpy(&aug_data->modified_data, callback_data, sizeof(XrDebugUtilsMessengerCallbackDataEXT)); + aug_data->new_objects.assign(callback_data->objects, callback_data->objects + callback_data->objectCount); + + // Record (overwrite) the names of all incoming objects provided in our internal list + for (auto& obj : aug_data->new_objects) { + object_info_.LookUpObjectName(obj); + } + + // Update local copy & point export to it + aug_data->modified_data.objects = aug_data->new_objects.data(); + aug_data->modified_data.sessionLabelCount = static_cast<uint32_t>(aug_data->labels.size()); + aug_data->modified_data.sessionLabels = aug_data->labels.empty() ? nullptr : aug_data->labels.data(); + aug_data->exported_data = &aug_data->modified_data; + return; +} diff --git a/thirdparty/openxr/src/common/object_info.h b/thirdparty/openxr/src/common/object_info.h new file mode 100644 index 0000000000..8e9742b605 --- /dev/null +++ b/thirdparty/openxr/src/common/object_info.h @@ -0,0 +1,229 @@ +// Copyright (c) 2017-2022, The Khronos Group Inc. +// Copyright (c) 2017-2019 Valve Corporation +// Copyright (c) 2017-2019 LunarG, Inc. +// Copyright (c) 2019 Collabora, Ltd. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Authors: Mark Young <marky@lunarg.com>, Ryan Pavlik <ryan.pavlik@collabora.com +// +/*! + * @file + * + * The core of an XR_EXT_debug_utils implementation, used/shared by the loader and several SDK layers. + */ + +#pragma once + +#include "hex_and_handles.h" + +#include <openxr/openxr.h> + +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +struct XrSdkGenericObject { + //! Type-erased handle value + uint64_t handle; + + //! Kind of object this handle refers to + XrObjectType type; + /// Un-erase the type of the handle and get it properly typed again. + /// + /// Note: Does not check the type before doing it! + template <typename HandleType> + HandleType& GetTypedHandle() { + return TreatIntegerAsHandle<HandleType&>(handle); + } + + //! @overload + template <typename HandleType> + HandleType const& GetTypedHandle() const { + return TreatIntegerAsHandle<HandleType&>(handle); + } + + //! Create from a typed handle and object type + template <typename T> + XrSdkGenericObject(T h, XrObjectType t) : handle(MakeHandleGeneric(h)), type(t) {} + + //! Create from an untyped handle value (integer) and object type + XrSdkGenericObject(uint64_t h, XrObjectType t) : handle(h), type(t) {} +}; + +struct XrSdkLogObjectInfo { + //! Type-erased handle value + uint64_t handle; + + //! Kind of object this handle refers to + XrObjectType type; + + //! To be assigned by the application - not part of this object's identity + std::string name; + + /// Un-erase the type of the handle and get it properly typed again. + /// + /// Note: Does not check the type before doing it! + template <typename HandleType> + HandleType& GetTypedHandle() { + return TreatIntegerAsHandle<HandleType&>(handle); + } + + //! @overload + template <typename HandleType> + HandleType const& GetTypedHandle() const { + return TreatIntegerAsHandle<HandleType&>(handle); + } + + XrSdkLogObjectInfo() = default; + + //! Create from a typed handle and object type + template <typename T> + XrSdkLogObjectInfo(T h, XrObjectType t) : handle(MakeHandleGeneric(h)), type(t) {} + + //! Create from an untyped handle value (integer) and object type + XrSdkLogObjectInfo(uint64_t h, XrObjectType t) : handle(h), type(t) {} + //! Create from an untyped handle value (integer), object type, and name + XrSdkLogObjectInfo(uint64_t h, XrObjectType t, const char* n) : handle(h), type(t), name(n == nullptr ? "" : n) {} + + std::string ToString() const; +}; + +//! True if the two object infos have the same handle value and handle type +static inline bool Equivalent(XrSdkLogObjectInfo const& a, XrSdkLogObjectInfo const& b) { + return a.handle == b.handle && a.type == b.type; +} + +//! @overload +static inline bool Equivalent(XrDebugUtilsObjectNameInfoEXT const& a, XrSdkLogObjectInfo const& b) { + return a.objectHandle == b.handle && a.objectType == b.type; +} + +//! @overload +static inline bool Equivalent(XrSdkLogObjectInfo const& a, XrDebugUtilsObjectNameInfoEXT const& b) { return Equivalent(b, a); } + +/// Object info registered with calls to xrSetDebugUtilsObjectNameEXT +class ObjectInfoCollection { + public: + void AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name); + + void RemoveObject(uint64_t object_handle, XrObjectType object_type); + + //! Find the stored object info, if any, matching handle and type. + //! Return nullptr if not found. + XrSdkLogObjectInfo const* LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info) const; + + //! Find the stored object info, if any, matching handle and type. + //! Return nullptr if not found. + XrSdkLogObjectInfo* LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info); + + //! Find the stored object info, if any. + //! Return nullptr if not found. + XrSdkLogObjectInfo const* LookUpStoredObjectInfo(uint64_t handle, XrObjectType type) const { + return LookUpStoredObjectInfo({handle, type}); + } + + //! Find the object name, if any, and update debug utils info accordingly. + //! Return true if found and updated. + bool LookUpObjectName(XrDebugUtilsObjectNameInfoEXT& info) const; + + //! Find the object name, if any, and update logging info accordingly. + //! Return true if found and updated. + bool LookUpObjectName(XrSdkLogObjectInfo& info) const; + + //! Is the collection empty? + bool Empty() const { return object_info_.empty(); } + + private: + // Object names that have been set for given objects + std::vector<XrSdkLogObjectInfo> object_info_; +}; + +struct XrSdkSessionLabel; +using XrSdkSessionLabelPtr = std::unique_ptr<XrSdkSessionLabel>; +using XrSdkSessionLabelList = std::vector<XrSdkSessionLabelPtr>; + +struct XrSdkSessionLabel { + static XrSdkSessionLabelPtr make(const XrDebugUtilsLabelEXT& label_info, bool individual); + + std::string label_name; + XrDebugUtilsLabelEXT debug_utils_label; + bool is_individual_label; + + private: + XrSdkSessionLabel(const XrDebugUtilsLabelEXT& label_info, bool individual); +}; + +/// The metadata for a collection of objects. Must persist unmodified during the entire debug messenger call! +struct NamesAndLabels { + NamesAndLabels() = default; + NamesAndLabels(std::vector<XrSdkLogObjectInfo> obj, std::vector<XrDebugUtilsLabelEXT> lab); + /// C++ structure owning the data (strings) backing the objects vector. + std::vector<XrSdkLogObjectInfo> sdk_objects; + + std::vector<XrDebugUtilsObjectNameInfoEXT> objects; + std::vector<XrDebugUtilsLabelEXT> labels; + + /// Populate the debug utils callback data structure. + void PopulateCallbackData(XrDebugUtilsMessengerCallbackDataEXT& data) const; + // XrDebugUtilsMessengerCallbackDataEXT MakeCallbackData() const; +}; + +struct AugmentedCallbackData { + std::vector<XrDebugUtilsLabelEXT> labels; + std::vector<XrDebugUtilsObjectNameInfoEXT> new_objects; + XrDebugUtilsMessengerCallbackDataEXT modified_data; + const XrDebugUtilsMessengerCallbackDataEXT* exported_data; +}; + +/// Tracks all the data (handle names and session labels) required to fully augment XR_EXT_debug_utils-related calls. +class DebugUtilsData { + public: + DebugUtilsData() = default; + + DebugUtilsData(const DebugUtilsData&) = delete; + DebugUtilsData& operator=(const DebugUtilsData&) = delete; + + bool Empty() const { return object_info_.Empty() && session_labels_.empty(); } + + //! Core of implementation for xrSetDebugUtilsObjectNameEXT + void AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name); + + /// Core of implementation for xrSessionBeginDebugUtilsLabelRegionEXT + void BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT& label_info); + + /// Core of implementation for xrSessionEndDebugUtilsLabelRegionEXT + void EndLabelRegion(XrSession session); + + /// Core of implementation for xrSessionInsertDebugUtilsLabelEXT + void InsertLabel(XrSession session, const XrDebugUtilsLabelEXT& label_info); + + /// Removes all labels associated with a session - call in xrDestroySession and xrDestroyInstance (for all child sessions) + void DeleteSessionLabels(XrSession session); + + /// Retrieve labels for the given session, if any, and push them in reverse order on the vector. + void LookUpSessionLabels(XrSession session, std::vector<XrDebugUtilsLabelEXT>& labels) const; + + /// Removes all data related to this object - including session labels if it's a session. + /// + /// Does not take care of handling child objects - you must do this yourself. + void DeleteObject(uint64_t object_handle, XrObjectType object_type); + + /// Given the collection of objects, populate their names and list of labels + NamesAndLabels PopulateNamesAndLabels(std::vector<XrSdkLogObjectInfo> objects) const; + + void WrapCallbackData(AugmentedCallbackData* aug_data, + const XrDebugUtilsMessengerCallbackDataEXT* provided_callback_data) const; + + private: + void RemoveIndividualLabel(XrSdkSessionLabelList& label_vec); + XrSdkSessionLabelList* GetSessionLabelList(XrSession session); + XrSdkSessionLabelList& GetOrCreateSessionLabelList(XrSession session); + + // Session labels: one vector of them per session. + std::unordered_map<XrSession, std::unique_ptr<XrSdkSessionLabelList>> session_labels_; + + // Names for objects. + ObjectInfoCollection object_info_; +}; diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp new file mode 100644 index 0000000000..85d5cdab10 --- /dev/null +++ b/thirdparty/openxr/src/common/platform_utils.hpp @@ -0,0 +1,345 @@ +// Copyright (c) 2017-2022, The Khronos Group Inc. +// Copyright (c) 2017-2019 Valve Corporation +// Copyright (c) 2017-2019 LunarG, Inc. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Authors: Mark Young <marky@lunarg.com>, Dave Houlton <daveh@lunarg.com> +// + +#pragma once + +#include "xr_dependencies.h" +#include <string> +#include <stdlib.h> + +// OpenXR paths and registry key locations +#define OPENXR_RELATIVE_PATH "openxr/" +#define OPENXR_IMPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/implicit.d" +#define OPENXR_EXPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/explicit.d" +#ifdef XR_OS_WINDOWS +#define OPENXR_REGISTRY_LOCATION "SOFTWARE\\Khronos\\OpenXR\\" +#define OPENXR_IMPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Implicit" +#define OPENXR_EXPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Explicit" +#endif + +// OpenXR Loader environment variables of interest +#define OPENXR_RUNTIME_JSON_ENV_VAR "XR_RUNTIME_JSON" +#define OPENXR_API_LAYER_PATH_ENV_VAR "XR_API_LAYER_PATH" + +// This is a CMake generated file with #defines for any functions/includes +// that it found present and build-time configuration. +// If you don't have this file, on non-Windows you'll need to define +// one of HAVE_SECURE_GETENV or HAVE___SECURE_GETENV depending on which +// of secure_getenv or __secure_getenv are present +#ifdef OPENXR_HAVE_COMMON_CONFIG +#include "common_config.h" +#endif // OPENXR_HAVE_COMMON_CONFIG + +// Environment variables +#if defined(XR_OS_LINUX) || defined(XR_OS_APPLE) + +#include <unistd.h> +#include <fcntl.h> +#include <iostream> + +namespace detail { + +static inline char* ImplGetEnv(const char* name) { return getenv(name); } + +static inline int ImplSetEnv(const char* name, const char* value, int overwrite) { return setenv(name, value, overwrite); } + +static inline char* ImplGetSecureEnv(const char* name) { +#ifdef HAVE_SECURE_GETENV + return secure_getenv(name); +#elif defined(HAVE___SECURE_GETENV) + return __secure_getenv(name); +#else +#pragma message( \ + "Warning: Falling back to non-secure getenv for environmental" \ + "lookups! Consider updating to a different libc.") + + return ImplGetEnv(name); +#endif +} +} // namespace detail + +#endif // defined(XR_OS_LINUX) || defined(XR_OS_APPLE) +#if defined(XR_OS_LINUX) + +static inline std::string PlatformUtilsGetEnv(const char* name) { + auto str = detail::ImplGetEnv(name); + if (str == nullptr) { + return {}; + } + return str; +} + +static inline std::string PlatformUtilsGetSecureEnv(const char* name) { + auto str = detail::ImplGetSecureEnv(name); + if (str == nullptr) { + return {}; + } + return str; +} + +static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; } + +static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { + const int shouldOverwrite = 1; + int result = detail::ImplSetEnv(name, value, shouldOverwrite); + return (result == 0); +} + +#elif defined(XR_OS_APPLE) + +static inline std::string PlatformUtilsGetEnv(const char* name) { + auto str = detail::ImplGetEnv(name); + if (str == nullptr) { + return {}; + } + return str; +} + +static inline std::string PlatformUtilsGetSecureEnv(const char* name) { + auto str = detail::ImplGetSecureEnv(name); + if (str == nullptr) { + return {}; + } + return str; +} + +static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; } + +static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { + const int shouldOverwrite = 1; + int result = detail::ImplSetEnv(name, value, shouldOverwrite); + return (result == 0); +} + +// Prefix for the Apple global runtime JSON file name +static const std::string rt_dir_prefix = "/usr/local/share/openxr/"; +static const std::string rt_filename = "/active_runtime.json"; + +static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { + file_name = rt_dir_prefix; + file_name += std::to_string(major_version); + file_name += rt_filename; + return true; +} + +#elif defined(XR_OS_WINDOWS) + +#if !defined(NDEBUG) +inline void LogError(const std::string& error) { OutputDebugStringA(error.c_str()); } +#else +#define LogError(x) +#endif + +inline std::wstring utf8_to_wide(const std::string& utf8Text) { + if (utf8Text.empty()) { + return {}; + } + + std::wstring wideText; + const int wideLength = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), nullptr, 0); + if (wideLength == 0) { + LogError("utf8_to_wide get size error: " + std::to_string(::GetLastError())); + return {}; + } + + // MultiByteToWideChar returns number of chars of the input buffer, regardless of null terminitor + wideText.resize(wideLength, 0); + wchar_t* wideString = const_cast<wchar_t*>(wideText.data()); // mutable data() only exists in c++17 + const int length = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), wideString, wideLength); + if (length != wideLength) { + LogError("utf8_to_wide convert string error: " + std::to_string(::GetLastError())); + return {}; + } + + return wideText; +} + +inline std::string wide_to_utf8(const std::wstring& wideText) { + if (wideText.empty()) { + return {}; + } + + std::string narrowText; + int narrowLength = ::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), nullptr, 0, nullptr, nullptr); + if (narrowLength == 0) { + LogError("wide_to_utf8 get size error: " + std::to_string(::GetLastError())); + return {}; + } + + // WideCharToMultiByte returns number of chars of the input buffer, regardless of null terminitor + narrowText.resize(narrowLength, 0); + char* narrowString = const_cast<char*>(narrowText.data()); // mutable data() only exists in c++17 + const int length = + ::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), narrowString, narrowLength, nullptr, nullptr); + if (length != narrowLength) { + LogError("wide_to_utf8 convert string error: " + std::to_string(::GetLastError())); + return {}; + } + + return narrowText; +} + +// Returns true if the current process has an integrity level > SECURITY_MANDATORY_MEDIUM_RID. +static inline bool IsHighIntegrityLevel() { + // Execute this check once and save the value as a static bool. + static bool isHighIntegrityLevel = ([] { + HANDLE processToken; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &processToken)) { + // Maximum possible size of SID_AND_ATTRIBUTES is maximum size of a SID + size of attributes DWORD. + uint8_t mandatoryLabelBuffer[SECURITY_MAX_SID_SIZE + sizeof(DWORD)]{}; + DWORD bufferSize; + if (GetTokenInformation(processToken, TokenIntegrityLevel, mandatoryLabelBuffer, sizeof(mandatoryLabelBuffer), + &bufferSize) != 0) { + const auto mandatoryLabel = reinterpret_cast<const TOKEN_MANDATORY_LABEL*>(mandatoryLabelBuffer); + if (mandatoryLabel->Label.Sid != 0) { + const DWORD subAuthorityCount = *GetSidSubAuthorityCount(mandatoryLabel->Label.Sid); + const DWORD integrityLevel = *GetSidSubAuthority(mandatoryLabel->Label.Sid, subAuthorityCount - 1); + CloseHandle(processToken); + return integrityLevel > SECURITY_MANDATORY_MEDIUM_RID; + } + } + + CloseHandle(processToken); + } + + return false; + })(); + + return isHighIntegrityLevel; +} + +// Returns true if the given environment variable exists. +// The name is a case-sensitive UTF8 string. +static inline bool PlatformUtilsGetEnvSet(const char* name) { + const std::wstring wname = utf8_to_wide(name); + const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0); + // GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error. + return 0 != valSize; +} + +// Returns the environment variable value for the given name. +// Returns an empty string if the environment variable doesn't exist or if it exists but is empty. +// Use PlatformUtilsGetEnvSet to tell if it exists. +// The name is a case-sensitive UTF8 string. +static inline std::string PlatformUtilsGetEnv(const char* name) { + const std::wstring wname = utf8_to_wide(name); + const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0); + // GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error. + // The size includes the null-terminator, so a size of 1 is means the variable was explicitly set to empty. + if (valSize == 0 || valSize == 1) { + return {}; + } + + // GetEnvironmentVariable returns size including null terminator for "query size" call. + std::wstring wValue(valSize, 0); + wchar_t* wValueData = &wValue[0]; + + // GetEnvironmentVariable returns string length, excluding null terminator for "get value" + // call if there was enough capacity. Else it returns the required capacity (including null terminator). + const DWORD length = ::GetEnvironmentVariableW(wname.c_str(), wValueData, (DWORD)wValue.size()); + if ((length == 0) || (length >= wValue.size())) { // If error or the variable increased length between calls... + LogError("GetEnvironmentVariable get value error: " + std::to_string(::GetLastError())); + return {}; + } + + wValue.resize(length); // Strip the null terminator. + + return wide_to_utf8(wValue); +} + +// Acts the same as PlatformUtilsGetEnv except returns an empty string if IsHighIntegrityLevel. +static inline std::string PlatformUtilsGetSecureEnv(const char* name) { + // Do not allow high integrity processes to act on data that can be controlled by medium integrity processes. + if (IsHighIntegrityLevel()) { + return {}; + } + + // No secure version for Windows so the above integrity check is needed. + return PlatformUtilsGetEnv(name); +} + +// Sets an environment variable via UTF8 strings. +// The name is case-sensitive. +// Overwrites the variable if it already exists. +// Returns true if it could be set. +static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { + const std::wstring wname = utf8_to_wide(name); + const std::wstring wvalue = utf8_to_wide(value); + BOOL result = ::SetEnvironmentVariableW(wname.c_str(), wvalue.c_str()); + return (result != 0); +} + +#elif defined(XR_OS_ANDROID) + +static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { + // Stub func + return false; +} + +static inline std::string PlatformUtilsGetEnv(const char* /* name */) { + // Stub func + return {}; +} + +static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) { + // Stub func + return {}; +} + +static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) { + // Stub func + return false; +} + +#include <sys/stat.h> + +// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases +static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { + // Prefix for the runtime JSON file name + static const char* rt_dir_prefixes[] = {"/oem", "/vendor", "/system"}; + static const std::string rt_filename = "/active_runtime.json"; + static const std::string subdir = "/etc/openxr/"; + for (const auto prefix : rt_dir_prefixes) { + auto path = prefix + subdir + std::to_string(major_version) + rt_filename; + struct stat buf; + if (0 == stat(path.c_str(), &buf)) { + file_name = path; + return true; + } + } + return false; +} +#else // Not Linux, Apple, nor Windows + +static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { + // Stub func + return false; +} + +static inline std::string PlatformUtilsGetEnv(const char* /* name */) { + // Stub func + return {}; +} + +static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) { + // Stub func + return {}; +} + +static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) { + // Stub func + return false; +} + +static inline bool PlatformGetGlobalRuntimeFileName(uint16_t /* major_version */, std::string const& /* file_name */) { + // Stub func + return false; +} + +#endif diff --git a/thirdparty/openxr/src/common/stdfs_conditions.h b/thirdparty/openxr/src/common/stdfs_conditions.h new file mode 100644 index 0000000000..6dc18cc620 --- /dev/null +++ b/thirdparty/openxr/src/common/stdfs_conditions.h @@ -0,0 +1,45 @@ +// Copyright (c) 2017-2022, The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG, Inc. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#ifndef _STDFS_CONDITIONS_H +#define _STDFS_CONDITIONS_H + +// If the C++ macro is set to the version containing C++17, it must support +// the final C++17 package +#if __cplusplus >= 201703L +#define USE_EXPERIMENTAL_FS 0 +#define USE_FINAL_FS 1 + +#elif defined(_MSC_VER) && _MSC_VER >= 1900 + +#if defined(_HAS_CXX17) && _HAS_CXX17 +// When MSC supports c++17 use <filesystem> package. +#define USE_EXPERIMENTAL_FS 0 +#define USE_FINAL_FS 1 +#endif // !_HAS_CXX17 + +// GCC supports the experimental filesystem items starting in GCC 6 +#elif (__GNUC__ >= 6) +#define USE_EXPERIMENTAL_FS 1 +#define USE_FINAL_FS 0 + +// If Clang, check for feature support +#elif defined(__clang__) && (__cpp_lib_filesystem || __cpp_lib_experimental_filesystem) +#if __cpp_lib_filesystem +#define USE_EXPERIMENTAL_FS 0 +#define USE_FINAL_FS 1 +#else +#define USE_EXPERIMENTAL_FS 1 +#define USE_FINAL_FS 0 +#endif + +// If all above fails, fall back to standard C++ and OS-specific items +#else +#define USE_EXPERIMENTAL_FS 0 +#define USE_FINAL_FS 0 +#endif + +#endif // !_STDFS_CONDITIONS_H diff --git a/thirdparty/openxr/src/common/xr_dependencies.h b/thirdparty/openxr/src/common/xr_dependencies.h new file mode 100644 index 0000000000..e34527abc3 --- /dev/null +++ b/thirdparty/openxr/src/common/xr_dependencies.h @@ -0,0 +1,89 @@ +// Copyright (c) 2018-2022, The Khronos Group Inc. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// This file includes headers with types which openxr.h depends on in order +// to compile when platforms, graphics apis, and the like are enabled. + +#pragma once + +#ifdef XR_USE_PLATFORM_ANDROID +#include <android/native_window.h> +#include <android/window.h> +#include <android/native_window_jni.h> +#endif // XR_USE_PLATFORM_ANDROID + +#ifdef XR_USE_PLATFORM_WIN32 + +#include <winapifamily.h> +#if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)) +// Enable desktop partition APIs, such as RegOpenKeyEx, LoadLibraryEx, PathFileExists etc. +#undef WINAPI_PARTITION_DESKTOP +#define WINAPI_PARTITION_DESKTOP 1 +#endif + +#ifndef NOMINMAX +#define NOMINMAX +#endif // !NOMINMAX + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif // !WIN32_LEAN_AND_MEAN + +#include <windows.h> +#include <unknwn.h> + +#endif // XR_USE_PLATFORM_WIN32 + +#ifdef XR_USE_GRAPHICS_API_D3D11 +#include <d3d11.h> +#endif // XR_USE_GRAPHICS_API_D3D11 + +#ifdef XR_USE_GRAPHICS_API_D3D12 +#include <d3d12.h> +#endif // XR_USE_GRAPHICS_API_D3D12 + +#ifdef XR_USE_PLATFORM_XLIB +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#ifdef Success +#undef Success +#endif // Success + +#ifdef Always +#undef Always +#endif // Always + +#ifdef None +#undef None +#endif // None +#endif // XR_USE_PLATFORM_XLIB + +#ifdef XR_USE_PLATFORM_XCB +#include <xcb/xcb.h> +#endif // XR_USE_PLATFORM_XCB + +#ifdef XR_USE_GRAPHICS_API_OPENGL +#if defined(XR_USE_PLATFORM_XLIB) || defined(XR_USE_PLATFORM_XCB) +#include <GL/glx.h> +#endif // (XR_USE_PLATFORM_XLIB || XR_USE_PLATFORM_XCB) +#ifdef XR_USE_PLATFORM_XCB +#include <xcb/glx.h> +#endif // XR_USE_PLATFORM_XCB +#ifdef XR_USE_PLATFORM_MACOS +#include <CL/cl_gl_ext.h> +#endif // XR_USE_PLATFORM_MACOS +#endif // XR_USE_GRAPHICS_API_OPENGL + +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES +#include <EGL/egl.h> +#endif // XR_USE_GRAPHICS_API_OPENGL_ES + +#ifdef XR_USE_GRAPHICS_API_VULKAN +#include <vulkan/vulkan.h> +#endif // XR_USE_GRAPHICS_API_VULKAN + +#ifdef XR_USE_PLATFORM_WAYLAND +#include "wayland-client.h" +#endif // XR_USE_PLATFORM_WAYLAND diff --git a/thirdparty/openxr/src/common/xr_linear.h b/thirdparty/openxr/src/common/xr_linear.h new file mode 100644 index 0000000000..9ffb49a4b6 --- /dev/null +++ b/thirdparty/openxr/src/common/xr_linear.h @@ -0,0 +1,787 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2016 Oculus VR, LLC. +// +// 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. +// +// Author: J.M.P. van Waveren +// + +#ifndef XR_LINEAR_H_ +#define XR_LINEAR_H_ + +#if defined(OS_LINUX_XCB) || defined(OS_LINUX_XCB_GLX) || defined(OS_LINUX_WAYLAND) +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wunused-function" +#endif + +#include <openxr/openxr.h> + +/* +================================================================================================ + +Description : Vector, matrix and quaternion math. +Author : J.M.P. van Waveren +Date : 12/10/2016 +Language : C99 +Format : Indent 4 spaces - no tabs. +Copyright : Copyright (c) 2016 Oculus VR, LLC. All Rights reserved. + + +DESCRIPTION +=========== + +All matrices are column-major. + +INTERFACE +========= + +XrVector2f +XrVector3f +XrVector4f +XrQuaternionf +XrMatrix4x4f + +inline static void XrVector3f_Set(XrVector3f* v, const float value); +inline static void XrVector3f_Add(XrVector3f* result, const XrVector3f* a, const XrVector3f* b); +inline static void XrVector3f_Sub(XrVector3f* result, const XrVector3f* a, const XrVector3f* b); +inline static void XrVector3f_Min(XrVector3f* result, const XrVector3f* a, const XrVector3f* b); +inline static void XrVector3f_Max(XrVector3f* result, const XrVector3f* a, const XrVector3f* b); +inline static void XrVector3f_Decay(XrVector3f* result, const XrVector3f* a, const float value); +inline static void XrVector3f_Lerp(XrVector3f* result, const XrVector3f* a, const XrVector3f* b, const float fraction); +inline static void XrVector3f_Scale(XrVector3f* result, const XrVector3f* a, const float scaleFactor); +inline static void XrVector3f_Normalize(XrVector3f* v); +inline static float XrVector3f_Length(const XrVector3f* v); + +inline static void XrQuaternionf_Lerp(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b, const float fraction); +inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b; + +inline static void XrMatrix4x4f_CreateIdentity(XrMatrix4x4f* result); +inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const float x, const float y, const float z); +inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY, + const float degreesZ); +inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z); +inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* result, const XrVector3f* translation, + const XrQuaternionf* rotation, const XrVector3f* scale); +inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, const float tanAngleLeft, const float tanAngleRight, + const float tanAngleUp, float const tanAngleDown, const float nearZ, + const float farZ); +inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, const float fovDegreesLeft, const float fovDegreesRight, + const float fovDegreeUp, const float fovDegreesDown, const float nearZ, + const float farZ); +inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* src); +inline static void XrMatrix4x4f_CreateOffsetScaleForBounds(XrMatrix4x4f* result, const XrMatrix4x4f* matrix, const XrVector3f* mins, + const XrVector3f* maxs); + +inline static bool XrMatrix4x4f_IsAffine(const XrMatrix4x4f* matrix, const float epsilon); +inline static bool XrMatrix4x4f_IsOrthogonal(const XrMatrix4x4f* matrix, const float epsilon); +inline static bool XrMatrix4x4f_IsOrthonormal(const XrMatrix4x4f* matrix, const float epsilon); +inline static bool XrMatrix4x4f_IsRigidBody(const XrMatrix4x4f* matrix, const float epsilon); + +inline static void XrMatrix4x4f_GetTranslation(XrVector3f* result, const XrMatrix4x4f* src); +inline static void XrMatrix4x4f_GetRotation(XrQuaternionf* result, const XrMatrix4x4f* src); +inline static void XrMatrix4x4f_GetScale(XrVector3f* result, const XrMatrix4x4f* src); + +inline static void XrMatrix4x4f_Multiply(XrMatrix4x4f* result, const XrMatrix4x4f* a, const XrMatrix4x4f* b); +inline static void XrMatrix4x4f_Transpose(XrMatrix4x4f* result, const XrMatrix4x4f* src); +inline static void XrMatrix4x4f_Invert(XrMatrix4x4f* result, const XrMatrix4x4f* src); +inline static void XrMatrix4x4f_InvertRigidBody(XrMatrix4x4f* result, const XrMatrix4x4f* src); + +inline static void XrMatrix4x4f_TransformVector3f(XrVector3f* result, const XrMatrix4x4f* m, const XrVector3f* v); +inline static void XrMatrix4x4f_TransformVector4f(XrVector4f* result, const XrMatrix4x4f* m, const XrVector4f* v); + +inline static void XrMatrix4x4f_TransformBounds(XrVector3f* resultMins, XrVector3f* resultMaxs, const XrMatrix4x4f* matrix, + const XrVector3f* mins, const XrVector3f* maxs); +inline static bool XrMatrix4x4f_CullBounds(const XrMatrix4x4f* mvp, const XrVector3f* mins, const XrVector3f* maxs); + +================================================================================================ +*/ + +#include <assert.h> +#include <math.h> +#include <stdbool.h> + +#define MATH_PI 3.14159265358979323846f + +#define DEFAULT_NEAR_Z 0.015625f // exact floating point representation +#define INFINITE_FAR_Z 0.0f + +static const XrColor4f XrColorRed = {1.0f, 0.0f, 0.0f, 1.0f}; +static const XrColor4f XrColorGreen = {0.0f, 1.0f, 0.0f, 1.0f}; +static const XrColor4f XrColorBlue = {0.0f, 0.0f, 1.0f, 1.0f}; +static const XrColor4f XrColorYellow = {1.0f, 1.0f, 0.0f, 1.0f}; +static const XrColor4f XrColorPurple = {1.0f, 0.0f, 1.0f, 1.0f}; +static const XrColor4f XrColorCyan = {0.0f, 1.0f, 1.0f, 1.0f}; +static const XrColor4f XrColorLightGrey = {0.7f, 0.7f, 0.7f, 1.0f}; +static const XrColor4f XrColorDarkGrey = {0.3f, 0.3f, 0.3f, 1.0f}; + +enum GraphicsAPI { GRAPHICS_VULKAN, GRAPHICS_OPENGL, GRAPHICS_OPENGL_ES, GRAPHICS_D3D }; + +// Column-major, pre-multiplied. This type does not exist in the OpenXR API and is provided for convenience. +struct XrMatrix4x4f { + float m[16]; +}; + +inline static float XrRcpSqrt(const float x) { + const float SMALLEST_NON_DENORMAL = 1.1754943508222875e-038f; // ( 1U << 23 ) + const float rcp = (x >= SMALLEST_NON_DENORMAL) ? 1.0f / sqrtf(x) : 1.0f; + return rcp; +} + +inline static void XrVector3f_Set(XrVector3f* v, const float value) { + v->x = value; + v->y = value; + v->z = value; +} + +inline static void XrVector3f_Add(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = a->x + b->x; + result->y = a->y + b->y; + result->z = a->z + b->z; +} + +inline static void XrVector3f_Sub(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = a->x - b->x; + result->y = a->y - b->y; + result->z = a->z - b->z; +} + +inline static void XrVector3f_Min(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = (a->x < b->x) ? a->x : b->x; + result->y = (a->y < b->y) ? a->y : b->y; + result->z = (a->z < b->z) ? a->z : b->z; +} + +inline static void XrVector3f_Max(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = (a->x > b->x) ? a->x : b->x; + result->y = (a->y > b->y) ? a->y : b->y; + result->z = (a->z > b->z) ? a->z : b->z; +} + +inline static void XrVector3f_Decay(XrVector3f* result, const XrVector3f* a, const float value) { + result->x = (fabsf(a->x) > value) ? ((a->x > 0.0f) ? (a->x - value) : (a->x + value)) : 0.0f; + result->y = (fabsf(a->y) > value) ? ((a->y > 0.0f) ? (a->y - value) : (a->y + value)) : 0.0f; + result->z = (fabsf(a->z) > value) ? ((a->z > 0.0f) ? (a->z - value) : (a->z + value)) : 0.0f; +} + +inline static void XrVector3f_Lerp(XrVector3f* result, const XrVector3f* a, const XrVector3f* b, const float fraction) { + result->x = a->x + fraction * (b->x - a->x); + result->y = a->y + fraction * (b->y - a->y); + result->z = a->z + fraction * (b->z - a->z); +} + +inline static void XrVector3f_Scale(XrVector3f* result, const XrVector3f* a, const float scaleFactor) { + result->x = a->x * scaleFactor; + result->y = a->y * scaleFactor; + result->z = a->z * scaleFactor; +} + +inline static float XrVector3f_Dot(const XrVector3f* a, const XrVector3f* b) { return a->x * b->x + a->y * b->y + a->z * b->z; } + +// Compute cross product, which generates a normal vector. +// Direction vector can be determined by right-hand rule: Pointing index finder in +// direction a and middle finger in direction b, thumb will point in Cross(a, b). +inline static void XrVector3f_Cross(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = a->y * b->z - a->z * b->y; + result->y = a->z * b->x - a->x * b->z; + result->z = a->x * b->y - a->y * b->x; +} + +inline static void XrVector3f_Normalize(XrVector3f* v) { + const float lengthRcp = XrRcpSqrt(v->x * v->x + v->y * v->y + v->z * v->z); + v->x *= lengthRcp; + v->y *= lengthRcp; + v->z *= lengthRcp; +} + +inline static float XrVector3f_Length(const XrVector3f* v) { return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z); } + +inline static void XrQuaternionf_CreateFromAxisAngle(XrQuaternionf* result, const XrVector3f* axis, const float angleInRadians) { + float s = sinf(angleInRadians / 2.0f); + float lengthRcp = XrRcpSqrt(axis->x * axis->x + axis->y * axis->y + axis->z * axis->z); + result->x = s * axis->x * lengthRcp; + result->y = s * axis->y * lengthRcp; + result->z = s * axis->z * lengthRcp; + result->w = cosf(angleInRadians / 2.0f); +} + +inline static void XrQuaternionf_Lerp(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b, const float fraction) { + const float s = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w; + const float fa = 1.0f - fraction; + const float fb = (s < 0.0f) ? -fraction : fraction; + const float x = a->x * fa + b->x * fb; + const float y = a->y * fa + b->y * fb; + const float z = a->z * fa + b->z * fb; + const float w = a->w * fa + b->w * fb; + const float lengthRcp = XrRcpSqrt(x * x + y * y + z * z + w * w); + result->x = x * lengthRcp; + result->y = y * lengthRcp; + result->z = z * lengthRcp; + result->w = w * lengthRcp; +} + +inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b) { + result->x = (b->w * a->x) + (b->x * a->w) + (b->y * a->z) - (b->z * a->y); + result->y = (b->w * a->y) - (b->x * a->z) + (b->y * a->w) + (b->z * a->x); + result->z = (b->w * a->z) + (b->x * a->y) - (b->y * a->x) + (b->z * a->w); + result->w = (b->w * a->w) - (b->x * a->x) - (b->y * a->y) - (b->z * a->z); +} + +// Use left-multiplication to accumulate transformations. +inline static void XrMatrix4x4f_Multiply(XrMatrix4x4f* result, const XrMatrix4x4f* a, const XrMatrix4x4f* b) { + result->m[0] = a->m[0] * b->m[0] + a->m[4] * b->m[1] + a->m[8] * b->m[2] + a->m[12] * b->m[3]; + result->m[1] = a->m[1] * b->m[0] + a->m[5] * b->m[1] + a->m[9] * b->m[2] + a->m[13] * b->m[3]; + result->m[2] = a->m[2] * b->m[0] + a->m[6] * b->m[1] + a->m[10] * b->m[2] + a->m[14] * b->m[3]; + result->m[3] = a->m[3] * b->m[0] + a->m[7] * b->m[1] + a->m[11] * b->m[2] + a->m[15] * b->m[3]; + + result->m[4] = a->m[0] * b->m[4] + a->m[4] * b->m[5] + a->m[8] * b->m[6] + a->m[12] * b->m[7]; + result->m[5] = a->m[1] * b->m[4] + a->m[5] * b->m[5] + a->m[9] * b->m[6] + a->m[13] * b->m[7]; + result->m[6] = a->m[2] * b->m[4] + a->m[6] * b->m[5] + a->m[10] * b->m[6] + a->m[14] * b->m[7]; + result->m[7] = a->m[3] * b->m[4] + a->m[7] * b->m[5] + a->m[11] * b->m[6] + a->m[15] * b->m[7]; + + result->m[8] = a->m[0] * b->m[8] + a->m[4] * b->m[9] + a->m[8] * b->m[10] + a->m[12] * b->m[11]; + result->m[9] = a->m[1] * b->m[8] + a->m[5] * b->m[9] + a->m[9] * b->m[10] + a->m[13] * b->m[11]; + result->m[10] = a->m[2] * b->m[8] + a->m[6] * b->m[9] + a->m[10] * b->m[10] + a->m[14] * b->m[11]; + result->m[11] = a->m[3] * b->m[8] + a->m[7] * b->m[9] + a->m[11] * b->m[10] + a->m[15] * b->m[11]; + + result->m[12] = a->m[0] * b->m[12] + a->m[4] * b->m[13] + a->m[8] * b->m[14] + a->m[12] * b->m[15]; + result->m[13] = a->m[1] * b->m[12] + a->m[5] * b->m[13] + a->m[9] * b->m[14] + a->m[13] * b->m[15]; + result->m[14] = a->m[2] * b->m[12] + a->m[6] * b->m[13] + a->m[10] * b->m[14] + a->m[14] * b->m[15]; + result->m[15] = a->m[3] * b->m[12] + a->m[7] * b->m[13] + a->m[11] * b->m[14] + a->m[15] * b->m[15]; +} + +// Creates the transpose of the given matrix. +inline static void XrMatrix4x4f_Transpose(XrMatrix4x4f* result, const XrMatrix4x4f* src) { + result->m[0] = src->m[0]; + result->m[1] = src->m[4]; + result->m[2] = src->m[8]; + result->m[3] = src->m[12]; + + result->m[4] = src->m[1]; + result->m[5] = src->m[5]; + result->m[6] = src->m[9]; + result->m[7] = src->m[13]; + + result->m[8] = src->m[2]; + result->m[9] = src->m[6]; + result->m[10] = src->m[10]; + result->m[11] = src->m[14]; + + result->m[12] = src->m[3]; + result->m[13] = src->m[7]; + result->m[14] = src->m[11]; + result->m[15] = src->m[15]; +} + +// Returns a 3x3 minor of a 4x4 matrix. +inline static float XrMatrix4x4f_Minor(const XrMatrix4x4f* matrix, int r0, int r1, int r2, int c0, int c1, int c2) { + return matrix->m[4 * r0 + c0] * + (matrix->m[4 * r1 + c1] * matrix->m[4 * r2 + c2] - matrix->m[4 * r2 + c1] * matrix->m[4 * r1 + c2]) - + matrix->m[4 * r0 + c1] * + (matrix->m[4 * r1 + c0] * matrix->m[4 * r2 + c2] - matrix->m[4 * r2 + c0] * matrix->m[4 * r1 + c2]) + + matrix->m[4 * r0 + c2] * + (matrix->m[4 * r1 + c0] * matrix->m[4 * r2 + c1] - matrix->m[4 * r2 + c0] * matrix->m[4 * r1 + c1]); +} + +// Calculates the inverse of a 4x4 matrix. +inline static void XrMatrix4x4f_Invert(XrMatrix4x4f* result, const XrMatrix4x4f* src) { + const float rcpDet = + 1.0f / (src->m[0] * XrMatrix4x4f_Minor(src, 1, 2, 3, 1, 2, 3) - src->m[1] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 2, 3) + + src->m[2] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 3) - src->m[3] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 2)); + + result->m[0] = XrMatrix4x4f_Minor(src, 1, 2, 3, 1, 2, 3) * rcpDet; + result->m[1] = -XrMatrix4x4f_Minor(src, 0, 2, 3, 1, 2, 3) * rcpDet; + result->m[2] = XrMatrix4x4f_Minor(src, 0, 1, 3, 1, 2, 3) * rcpDet; + result->m[3] = -XrMatrix4x4f_Minor(src, 0, 1, 2, 1, 2, 3) * rcpDet; + result->m[4] = -XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 2, 3) * rcpDet; + result->m[5] = XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 2, 3) * rcpDet; + result->m[6] = -XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 2, 3) * rcpDet; + result->m[7] = XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 2, 3) * rcpDet; + result->m[8] = XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 3) * rcpDet; + result->m[9] = -XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 1, 3) * rcpDet; + result->m[10] = XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 1, 3) * rcpDet; + result->m[11] = -XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 1, 3) * rcpDet; + result->m[12] = -XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 2) * rcpDet; + result->m[13] = XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 1, 2) * rcpDet; + result->m[14] = -XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 1, 2) * rcpDet; + result->m[15] = XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 1, 2) * rcpDet; +} + +// Calculates the inverse of a rigid body transform. +inline static void XrMatrix4x4f_InvertRigidBody(XrMatrix4x4f* result, const XrMatrix4x4f* src) { + result->m[0] = src->m[0]; + result->m[1] = src->m[4]; + result->m[2] = src->m[8]; + result->m[3] = 0.0f; + result->m[4] = src->m[1]; + result->m[5] = src->m[5]; + result->m[6] = src->m[9]; + result->m[7] = 0.0f; + result->m[8] = src->m[2]; + result->m[9] = src->m[6]; + result->m[10] = src->m[10]; + result->m[11] = 0.0f; + result->m[12] = -(src->m[0] * src->m[12] + src->m[1] * src->m[13] + src->m[2] * src->m[14]); + result->m[13] = -(src->m[4] * src->m[12] + src->m[5] * src->m[13] + src->m[6] * src->m[14]); + result->m[14] = -(src->m[8] * src->m[12] + src->m[9] * src->m[13] + src->m[10] * src->m[14]); + result->m[15] = 1.0f; +} + +// Creates an identity matrix. +inline static void XrMatrix4x4f_CreateIdentity(XrMatrix4x4f* result) { + result->m[0] = 1.0f; + result->m[1] = 0.0f; + result->m[2] = 0.0f; + result->m[3] = 0.0f; + result->m[4] = 0.0f; + result->m[5] = 1.0f; + result->m[6] = 0.0f; + result->m[7] = 0.0f; + result->m[8] = 0.0f; + result->m[9] = 0.0f; + result->m[10] = 1.0f; + result->m[11] = 0.0f; + result->m[12] = 0.0f; + result->m[13] = 0.0f; + result->m[14] = 0.0f; + result->m[15] = 1.0f; +} + +// Creates a translation matrix. +inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const float x, const float y, const float z) { + result->m[0] = 1.0f; + result->m[1] = 0.0f; + result->m[2] = 0.0f; + result->m[3] = 0.0f; + result->m[4] = 0.0f; + result->m[5] = 1.0f; + result->m[6] = 0.0f; + result->m[7] = 0.0f; + result->m[8] = 0.0f; + result->m[9] = 0.0f; + result->m[10] = 1.0f; + result->m[11] = 0.0f; + result->m[12] = x; + result->m[13] = y; + result->m[14] = z; + result->m[15] = 1.0f; +} + +// Creates a rotation matrix. +// If -Z=forward, +Y=up, +X=right, then degreesX=pitch, degreesY=yaw, degreesZ=roll. +inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY, + const float degreesZ) { + const float sinX = sinf(degreesX * (MATH_PI / 180.0f)); + const float cosX = cosf(degreesX * (MATH_PI / 180.0f)); + const XrMatrix4x4f rotationX = {{1, 0, 0, 0, 0, cosX, sinX, 0, 0, -sinX, cosX, 0, 0, 0, 0, 1}}; + const float sinY = sinf(degreesY * (MATH_PI / 180.0f)); + const float cosY = cosf(degreesY * (MATH_PI / 180.0f)); + const XrMatrix4x4f rotationY = {{cosY, 0, -sinY, 0, 0, 1, 0, 0, sinY, 0, cosY, 0, 0, 0, 0, 1}}; + const float sinZ = sinf(degreesZ * (MATH_PI / 180.0f)); + const float cosZ = cosf(degreesZ * (MATH_PI / 180.0f)); + const XrMatrix4x4f rotationZ = {{cosZ, sinZ, 0, 0, -sinZ, cosZ, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}}; + XrMatrix4x4f rotationXY; + XrMatrix4x4f_Multiply(&rotationXY, &rotationY, &rotationX); + XrMatrix4x4f_Multiply(result, &rotationZ, &rotationXY); +} + +// Creates a scale matrix. +inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z) { + result->m[0] = x; + result->m[1] = 0.0f; + result->m[2] = 0.0f; + result->m[3] = 0.0f; + result->m[4] = 0.0f; + result->m[5] = y; + result->m[6] = 0.0f; + result->m[7] = 0.0f; + result->m[8] = 0.0f; + result->m[9] = 0.0f; + result->m[10] = z; + result->m[11] = 0.0f; + result->m[12] = 0.0f; + result->m[13] = 0.0f; + result->m[14] = 0.0f; + result->m[15] = 1.0f; +} + +// Creates a matrix from a quaternion. +inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* quat) { + const float x2 = quat->x + quat->x; + const float y2 = quat->y + quat->y; + const float z2 = quat->z + quat->z; + + const float xx2 = quat->x * x2; + const float yy2 = quat->y * y2; + const float zz2 = quat->z * z2; + + const float yz2 = quat->y * z2; + const float wx2 = quat->w * x2; + const float xy2 = quat->x * y2; + const float wz2 = quat->w * z2; + const float xz2 = quat->x * z2; + const float wy2 = quat->w * y2; + + result->m[0] = 1.0f - yy2 - zz2; + result->m[1] = xy2 + wz2; + result->m[2] = xz2 - wy2; + result->m[3] = 0.0f; + + result->m[4] = xy2 - wz2; + result->m[5] = 1.0f - xx2 - zz2; + result->m[6] = yz2 + wx2; + result->m[7] = 0.0f; + + result->m[8] = xz2 + wy2; + result->m[9] = yz2 - wx2; + result->m[10] = 1.0f - xx2 - yy2; + result->m[11] = 0.0f; + + result->m[12] = 0.0f; + result->m[13] = 0.0f; + result->m[14] = 0.0f; + result->m[15] = 1.0f; +} + +// Creates a combined translation(rotation(scale(object))) matrix. +inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* result, const XrVector3f* translation, + const XrQuaternionf* rotation, const XrVector3f* scale) { + XrMatrix4x4f scaleMatrix; + XrMatrix4x4f_CreateScale(&scaleMatrix, scale->x, scale->y, scale->z); + + XrMatrix4x4f rotationMatrix; + XrMatrix4x4f_CreateFromQuaternion(&rotationMatrix, rotation); + + XrMatrix4x4f translationMatrix; + XrMatrix4x4f_CreateTranslation(&translationMatrix, translation->x, translation->y, translation->z); + + XrMatrix4x4f combinedMatrix; + XrMatrix4x4f_Multiply(&combinedMatrix, &rotationMatrix, &scaleMatrix); + XrMatrix4x4f_Multiply(result, &translationMatrix, &combinedMatrix); +} + +// Creates a projection matrix based on the specified dimensions. +// The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for the graphics API. +// The far plane is placed at infinity if farZ <= nearZ. +// An infinite projection matrix is preferred for rasterization because, except for +// things *right* up against the near plane, it always provides better precision: +// "Tightening the Precision of Perspective Rendering" +// Paul Upchurch, Mathieu Desbrun +// Journal of Graphics Tools, Volume 16, Issue 1, 2012 +inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const float tanAngleLeft, + const float tanAngleRight, const float tanAngleUp, float const tanAngleDown, + const float nearZ, const float farZ) { + const float tanAngleWidth = tanAngleRight - tanAngleLeft; + + // Set to tanAngleDown - tanAngleUp for a clip space with positive Y down (Vulkan). + // Set to tanAngleUp - tanAngleDown for a clip space with positive Y up (OpenGL / D3D / Metal). + const float tanAngleHeight = graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown); + + // Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES). + // Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal). + const float offsetZ = (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0; + + if (farZ <= nearZ) { + // place the far plane at infinity + result->m[0] = 2.0f / tanAngleWidth; + result->m[4] = 0.0f; + result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result->m[12] = 0.0f; + + result->m[1] = 0.0f; + result->m[5] = 2.0f / tanAngleHeight; + result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result->m[13] = 0.0f; + + result->m[2] = 0.0f; + result->m[6] = 0.0f; + result->m[10] = -1.0f; + result->m[14] = -(nearZ + offsetZ); + + result->m[3] = 0.0f; + result->m[7] = 0.0f; + result->m[11] = -1.0f; + result->m[15] = 0.0f; + } else { + // normal projection + result->m[0] = 2.0f / tanAngleWidth; + result->m[4] = 0.0f; + result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result->m[12] = 0.0f; + + result->m[1] = 0.0f; + result->m[5] = 2.0f / tanAngleHeight; + result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result->m[13] = 0.0f; + + result->m[2] = 0.0f; + result->m[6] = 0.0f; + result->m[10] = -(farZ + offsetZ) / (farZ - nearZ); + result->m[14] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ); + + result->m[3] = 0.0f; + result->m[7] = 0.0f; + result->m[11] = -1.0f; + result->m[15] = 0.0f; + } +} + +// Creates a projection matrix based on the specified FOV. +inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const XrFovf fov, + const float nearZ, const float farZ) { + const float tanLeft = tanf(fov.angleLeft); + const float tanRight = tanf(fov.angleRight); + + const float tanDown = tanf(fov.angleDown); + const float tanUp = tanf(fov.angleUp); + + XrMatrix4x4f_CreateProjection(result, graphicsApi, tanLeft, tanRight, tanUp, tanDown, nearZ, farZ); +} + +// Creates a matrix that transforms the -1 to 1 cube to cover the given 'mins' and 'maxs' transformed with the given 'matrix'. +inline static void XrMatrix4x4f_CreateOffsetScaleForBounds(XrMatrix4x4f* result, const XrMatrix4x4f* matrix, const XrVector3f* mins, + const XrVector3f* maxs) { + const XrVector3f offset = {(maxs->x + mins->x) * 0.5f, (maxs->y + mins->y) * 0.5f, (maxs->z + mins->z) * 0.5f}; + const XrVector3f scale = {(maxs->x - mins->x) * 0.5f, (maxs->y - mins->y) * 0.5f, (maxs->z - mins->z) * 0.5f}; + + result->m[0] = matrix->m[0] * scale.x; + result->m[1] = matrix->m[1] * scale.x; + result->m[2] = matrix->m[2] * scale.x; + result->m[3] = matrix->m[3] * scale.x; + + result->m[4] = matrix->m[4] * scale.y; + result->m[5] = matrix->m[5] * scale.y; + result->m[6] = matrix->m[6] * scale.y; + result->m[7] = matrix->m[7] * scale.y; + + result->m[8] = matrix->m[8] * scale.z; + result->m[9] = matrix->m[9] * scale.z; + result->m[10] = matrix->m[10] * scale.z; + result->m[11] = matrix->m[11] * scale.z; + + result->m[12] = matrix->m[12] + matrix->m[0] * offset.x + matrix->m[4] * offset.y + matrix->m[8] * offset.z; + result->m[13] = matrix->m[13] + matrix->m[1] * offset.x + matrix->m[5] * offset.y + matrix->m[9] * offset.z; + result->m[14] = matrix->m[14] + matrix->m[2] * offset.x + matrix->m[6] * offset.y + matrix->m[10] * offset.z; + result->m[15] = matrix->m[15] + matrix->m[3] * offset.x + matrix->m[7] * offset.y + matrix->m[11] * offset.z; +} + +// Returns true if the given matrix is affine. +inline static bool XrMatrix4x4f_IsAffine(const XrMatrix4x4f* matrix, const float epsilon) { + return fabsf(matrix->m[3]) <= epsilon && fabsf(matrix->m[7]) <= epsilon && fabsf(matrix->m[11]) <= epsilon && + fabsf(matrix->m[15] - 1.0f) <= epsilon; +} + +// Returns true if the given matrix is orthogonal. +inline static bool XrMatrix4x4f_IsOrthogonal(const XrMatrix4x4f* matrix, const float epsilon) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (i != j) { + if (fabsf(matrix->m[4 * i + 0] * matrix->m[4 * j + 0] + matrix->m[4 * i + 1] * matrix->m[4 * j + 1] + + matrix->m[4 * i + 2] * matrix->m[4 * j + 2]) > epsilon) { + return false; + } + if (fabsf(matrix->m[4 * 0 + i] * matrix->m[4 * 0 + j] + matrix->m[4 * 1 + i] * matrix->m[4 * 1 + j] + + matrix->m[4 * 2 + i] * matrix->m[4 * 2 + j]) > epsilon) { + return false; + } + } + } + } + return true; +} + +// Returns true if the given matrix is orthonormal. +inline static bool XrMatrix4x4f_IsOrthonormal(const XrMatrix4x4f* matrix, const float epsilon) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + const float kd = (i == j) ? 1.0f : 0.0f; // Kronecker delta + if (fabsf(kd - (matrix->m[4 * i + 0] * matrix->m[4 * j + 0] + matrix->m[4 * i + 1] * matrix->m[4 * j + 1] + + matrix->m[4 * i + 2] * matrix->m[4 * j + 2])) > epsilon) { + return false; + } + if (fabsf(kd - (matrix->m[4 * 0 + i] * matrix->m[4 * 0 + j] + matrix->m[4 * 1 + i] * matrix->m[4 * 1 + j] + + matrix->m[4 * 2 + i] * matrix->m[4 * 2 + j])) > epsilon) { + return false; + } + } + } + return true; +} + +// Returns true if the given matrix is a rigid body transform. +inline static bool XrMatrix4x4f_IsRigidBody(const XrMatrix4x4f* matrix, const float epsilon) { + return XrMatrix4x4f_IsAffine(matrix, epsilon) && XrMatrix4x4f_IsOrthonormal(matrix, epsilon); +} + +// Get the translation from a combined translation(rotation(scale(object))) matrix. +inline static void XrMatrix4x4f_GetTranslation(XrVector3f* result, const XrMatrix4x4f* src) { + assert(XrMatrix4x4f_IsAffine(src, 1e-4f)); + assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f)); + + result->x = src->m[12]; + result->y = src->m[13]; + result->z = src->m[14]; +} + +// Get the rotation from a combined translation(rotation(scale(object))) matrix. +inline static void XrMatrix4x4f_GetRotation(XrQuaternionf* result, const XrMatrix4x4f* src) { + assert(XrMatrix4x4f_IsAffine(src, 1e-4f)); + assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f)); + + const float rcpScaleX = XrRcpSqrt(src->m[0] * src->m[0] + src->m[1] * src->m[1] + src->m[2] * src->m[2]); + const float rcpScaleY = XrRcpSqrt(src->m[4] * src->m[4] + src->m[5] * src->m[5] + src->m[6] * src->m[6]); + const float rcpScaleZ = XrRcpSqrt(src->m[8] * src->m[8] + src->m[9] * src->m[9] + src->m[10] * src->m[10]); + const float m[9] = {src->m[0] * rcpScaleX, src->m[1] * rcpScaleX, src->m[2] * rcpScaleX, + src->m[4] * rcpScaleY, src->m[5] * rcpScaleY, src->m[6] * rcpScaleY, + src->m[8] * rcpScaleZ, src->m[9] * rcpScaleZ, src->m[10] * rcpScaleZ}; + if (m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] > 0.0f) { + float t = +m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f; + float s = XrRcpSqrt(t) * 0.5f; + result->w = s * t; + result->z = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s; + result->y = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s; + result->x = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s; + } else if (m[0 * 3 + 0] > m[1 * 3 + 1] && m[0 * 3 + 0] > m[2 * 3 + 2]) { + float t = +m[0 * 3 + 0] - m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f; + float s = XrRcpSqrt(t) * 0.5f; + result->x = s * t; + result->y = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s; + result->z = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s; + result->w = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s; + } else if (m[1 * 3 + 1] > m[2 * 3 + 2]) { + float t = -m[0 * 3 + 0] + m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f; + float s = XrRcpSqrt(t) * 0.5f; + result->y = s * t; + result->x = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s; + result->w = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s; + result->z = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s; + } else { + float t = -m[0 * 3 + 0] - m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f; + float s = XrRcpSqrt(t) * 0.5f; + result->z = s * t; + result->w = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s; + result->x = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s; + result->y = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s; + } +} + +// Get the scale from a combined translation(rotation(scale(object))) matrix. +inline static void XrMatrix4x4f_GetScale(XrVector3f* result, const XrMatrix4x4f* src) { + assert(XrMatrix4x4f_IsAffine(src, 1e-4f)); + assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f)); + + result->x = sqrtf(src->m[0] * src->m[0] + src->m[1] * src->m[1] + src->m[2] * src->m[2]); + result->y = sqrtf(src->m[4] * src->m[4] + src->m[5] * src->m[5] + src->m[6] * src->m[6]); + result->z = sqrtf(src->m[8] * src->m[8] + src->m[9] * src->m[9] + src->m[10] * src->m[10]); +} + +// Transforms a 3D vector. +inline static void XrMatrix4x4f_TransformVector3f(XrVector3f* result, const XrMatrix4x4f* m, const XrVector3f* v) { + const float w = m->m[3] * v->x + m->m[7] * v->y + m->m[11] * v->z + m->m[15]; + const float rcpW = 1.0f / w; + result->x = (m->m[0] * v->x + m->m[4] * v->y + m->m[8] * v->z + m->m[12]) * rcpW; + result->y = (m->m[1] * v->x + m->m[5] * v->y + m->m[9] * v->z + m->m[13]) * rcpW; + result->z = (m->m[2] * v->x + m->m[6] * v->y + m->m[10] * v->z + m->m[14]) * rcpW; +} + +// Transforms a 4D vector. +inline static void XrMatrix4x4f_TransformVector4f(XrVector4f* result, const XrMatrix4x4f* m, const XrVector4f* v) { + result->x = m->m[0] * v->x + m->m[4] * v->y + m->m[8] * v->z + m->m[12] * v->w; + result->y = m->m[1] * v->x + m->m[5] * v->y + m->m[9] * v->z + m->m[13] * v->w; + result->z = m->m[2] * v->x + m->m[6] * v->y + m->m[10] * v->z + m->m[14] * v->w; + result->w = m->m[3] * v->x + m->m[7] * v->y + m->m[11] * v->z + m->m[15] * v->w; +} + +// Transforms the 'mins' and 'maxs' bounds with the given 'matrix'. +inline static void XrMatrix4x4f_TransformBounds(XrVector3f* resultMins, XrVector3f* resultMaxs, const XrMatrix4x4f* matrix, + const XrVector3f* mins, const XrVector3f* maxs) { + assert(XrMatrix4x4f_IsAffine(matrix, 1e-4f)); + + const XrVector3f center = {(mins->x + maxs->x) * 0.5f, (mins->y + maxs->y) * 0.5f, (mins->z + maxs->z) * 0.5f}; + const XrVector3f extents = {maxs->x - center.x, maxs->y - center.y, maxs->z - center.z}; + const XrVector3f newCenter = {matrix->m[0] * center.x + matrix->m[4] * center.y + matrix->m[8] * center.z + matrix->m[12], + matrix->m[1] * center.x + matrix->m[5] * center.y + matrix->m[9] * center.z + matrix->m[13], + matrix->m[2] * center.x + matrix->m[6] * center.y + matrix->m[10] * center.z + matrix->m[14]}; + const XrVector3f newExtents = { + fabsf(extents.x * matrix->m[0]) + fabsf(extents.y * matrix->m[4]) + fabsf(extents.z * matrix->m[8]), + fabsf(extents.x * matrix->m[1]) + fabsf(extents.y * matrix->m[5]) + fabsf(extents.z * matrix->m[9]), + fabsf(extents.x * matrix->m[2]) + fabsf(extents.y * matrix->m[6]) + fabsf(extents.z * matrix->m[10])}; + XrVector3f_Sub(resultMins, &newCenter, &newExtents); + XrVector3f_Add(resultMaxs, &newCenter, &newExtents); +} + +// Returns true if the 'mins' and 'maxs' bounds is completely off to one side of the projection matrix. +inline static bool XrMatrix4x4f_CullBounds(const XrMatrix4x4f* mvp, const XrVector3f* mins, const XrVector3f* maxs) { + if (maxs->x <= mins->x && maxs->y <= mins->y && maxs->z <= mins->z) { + return false; + } + + XrVector4f c[8]; + for (int i = 0; i < 8; i++) { + const XrVector4f corner = {(i & 1) != 0 ? maxs->x : mins->x, (i & 2) != 0 ? maxs->y : mins->y, + (i & 4) != 0 ? maxs->z : mins->z, 1.0f}; + XrMatrix4x4f_TransformVector4f(&c[i], mvp, &corner); + } + + int i; + for (i = 0; i < 8; i++) { + if (c[i].x > -c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + for (i = 0; i < 8; i++) { + if (c[i].x < c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + + for (i = 0; i < 8; i++) { + if (c[i].y > -c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + for (i = 0; i < 8; i++) { + if (c[i].y < c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + for (i = 0; i < 8; i++) { + if (c[i].z > -c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + for (i = 0; i < 8; i++) { + if (c[i].z < c[i].w) { + break; + } + } + return i == 8; +} + +#endif // XR_LINEAR_H_ |