1
0
mirror of https://github.com/godotengine/godot.git synced 2025-12-03 16:55:53 +00:00

Add OpenXR 1.0.22 to thirdparty libraries

Will be compiled and used in the next commit.

Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
This commit is contained in:
Bastiaan Olij
2021-12-08 18:31:06 +11:00
committed by Rémi Verschelde
parent fcf8c2006d
commit 65bae5a341
59 changed files with 23956 additions and 1 deletions

View File

@@ -0,0 +1,5 @@
# Copyright (c) 2020 The Khronos Group Inc.
#
# SPDX-License-Identifier: Apache-2.0
!openxr_loader_for_android.pom

View File

@@ -0,0 +1,319 @@
// Copyright (c) 2020-2022, The Khronos Group Inc.
// Copyright (c) 2020-2021, Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com>
#include "android_utilities.h"
#ifdef __ANDROID__
#include <wrap/android.net.h>
#include <wrap/android.content.h>
#include <wrap/android.database.h>
#include <json/value.h>
#include <openxr/openxr.h>
#include <sstream>
#include <vector>
#include <android/log.h>
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "openxr_loader", __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, "openxr_loader", __VA_ARGS__)
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "openxr_loader", __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, "openxr_loader", __VA_ARGS__)
namespace openxr_android {
using wrap::android::content::ContentUris;
using wrap::android::content::Context;
using wrap::android::database::Cursor;
using wrap::android::net::Uri;
using wrap::android::net::Uri_Builder;
// Code in here corresponds roughly to the Java "BrokerContract" class and subclasses.
namespace {
constexpr auto AUTHORITY = "org.khronos.openxr.runtime_broker";
constexpr auto SYSTEM_AUTHORITY = "org.khronos.openxr.system_runtime_broker";
constexpr auto BASE_PATH = "openxr";
constexpr auto ABI_PATH = "abi";
constexpr auto RUNTIMES_PATH = "runtimes";
constexpr const char *getBrokerAuthority(bool systemBroker) { return systemBroker ? SYSTEM_AUTHORITY : AUTHORITY; }
struct BaseColumns {
/**
* The unique ID for a row.
*/
[[maybe_unused]] static constexpr auto ID = "_id";
};
/**
* Contains details for the /openxr/[major_ver]/abi/[abi]/runtimes/active URI.
* <p>
* This URI represents a "table" containing at most one item, the currently active runtime. The
* policy of which runtime is chosen to be active (if more than one is installed) is left to the
* content provider.
* <p>
* No sort order is required to be honored by the content provider.
*/
namespace active_runtime {
/**
* Final path component to this URI.
*/
static constexpr auto TABLE_PATH = "active";
/**
* Create a content URI for querying the data on the active runtime for a
* given major version of OpenXR.
*
* @param systemBroker If the system runtime broker (instead of the installable one) should be queried.
* @param majorVer The major version of OpenXR.
* @param abi The Android ABI name in use.
* @return A content URI for a single item: the active runtime.
*/
static Uri makeContentUri(bool systemBroker, int majorVersion, const char *abi) {
auto builder = Uri_Builder::construct();
builder.scheme("content")
.authority(getBrokerAuthority(systemBroker))
.appendPath(BASE_PATH)
.appendPath(std::to_string(majorVersion))
.appendPath(ABI_PATH)
.appendPath(abi)
.appendPath(RUNTIMES_PATH)
.appendPath(TABLE_PATH);
ContentUris::appendId(builder, 0);
return builder.build();
}
struct Columns : BaseColumns {
/**
* Constant for the PACKAGE_NAME column name
*/
static constexpr auto PACKAGE_NAME = "package_name";
/**
* Constant for the NATIVE_LIB_DIR column name
*/
static constexpr auto NATIVE_LIB_DIR = "native_lib_dir";
/**
* Constant for the SO_FILENAME column name
*/
static constexpr auto SO_FILENAME = "so_filename";
/**
* Constant for the HAS_FUNCTIONS column name.
* <p>
* If this column contains true, you should check the /functions/ URI for that runtime.
*/
static constexpr auto HAS_FUNCTIONS = "has_functions";
};
} // namespace active_runtime
/**
* Contains details for the /openxr/[major_ver]/abi/[abi]/runtimes/[package]/functions URI.
* <p>
* This URI is for package-specific function name remapping. Since this is an optional field in
* the corresponding JSON manifests for OpenXR, it is optional here as well. If the active
* runtime contains "true" in its "has_functions" column, then this table must exist and be
* queryable.
* <p>
* No sort order is required to be honored by the content provider.
*/
namespace functions {
/**
* Final path component to this URI.
*/
static constexpr auto TABLE_PATH = "functions";
/**
* Create a content URI for querying all rows of the function remapping data for a given
* runtime package and major version of OpenXR.
*
* @param systemBroker If the system runtime broker (instead of the installable one) should be queried.
* @param majorVer The major version of OpenXR.
* @param packageName The package name of the runtime.
* @param abi The Android ABI name in use.
* @return A content URI for the entire table: the function remapping for that runtime.
*/
static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const &packageName, const char *abi) {
auto builder = Uri_Builder::construct();
builder.scheme("content")
.authority(getBrokerAuthority(systemBroker))
.appendPath(BASE_PATH)
.appendPath(std::to_string(majorVersion))
.appendPath(ABI_PATH)
.appendPath(abi)
.appendPath(RUNTIMES_PATH)
.appendPath(packageName)
.appendPath(TABLE_PATH);
return builder.build();
}
struct Columns : BaseColumns {
/**
* Constant for the FUNCTION_NAME column name
*/
static constexpr auto FUNCTION_NAME = "function_name";
/**
* Constant for the SYMBOL_NAME column name
*/
static constexpr auto SYMBOL_NAME = "symbol_name";
};
} // namespace functions
} // namespace
static inline jni::Array<std::string> makeArray(std::initializer_list<const char *> &&list) {
auto ret = jni::Array<std::string>{(long)list.size()};
long i = 0;
for (auto &&elt : list) {
ret.setElement(i, elt);
++i;
}
return ret;
}
static constexpr auto TAG = "OpenXR-Loader";
#if defined(__arm__)
static constexpr auto ABI = "armeabi-v7l";
#elif defined(__aarch64__)
static constexpr auto ABI = "arm64-v8a";
#elif defined(__i386__)
static constexpr auto ABI = "x86";
#elif defined(__x86_64__)
static constexpr auto ABI = "x86_64";
#else
#error "Unknown ABI!"
#endif
/// Helper class to generate the jsoncpp object corresponding to a synthetic runtime manifest.
class JsonManifestBuilder {
public:
JsonManifestBuilder(const std::string &libraryPathParent, const std::string &libraryPath);
JsonManifestBuilder &function(const std::string &functionName, const std::string &symbolName);
Json::Value build() const { return root_node; }
private:
Json::Value root_node;
};
inline JsonManifestBuilder::JsonManifestBuilder(const std::string &libraryPathParent, const std::string &libraryPath)
: root_node(Json::objectValue) {
root_node["file_format_version"] = "1.0.0";
root_node["instance_extensions"] = Json::Value(Json::arrayValue);
root_node["functions"] = Json::Value(Json::objectValue);
root_node[libraryPathParent] = Json::objectValue;
root_node[libraryPathParent]["library_path"] = libraryPath;
}
inline JsonManifestBuilder &JsonManifestBuilder::function(const std::string &functionName, const std::string &symbolName) {
root_node["functions"][functionName] = symbolName;
return *this;
}
static constexpr const char *getBrokerTypeName(bool systemBroker) { return systemBroker ? "system" : "installable"; }
static int populateFunctions(wrap::android::content::Context const &context, bool systemBroker, const std::string &packageName,
JsonManifestBuilder &builder) {
jni::Array<std::string> projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME});
auto uri = functions::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI);
ALOGI("populateFunctions: Querying URI: %s", uri.toString().c_str());
Cursor cursor = context.getContentResolver().query(uri, projection);
if (cursor.isNull()) {
ALOGE("Null cursor when querying content resolver for functions.");
return -1;
}
if (cursor.getCount() < 1) {
ALOGE("Non-null but empty cursor when querying content resolver for functions.");
cursor.close();
return -1;
}
auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME);
auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME);
while (cursor.moveToNext()) {
builder.function(cursor.getString(functionIndex), cursor.getString(symbolIndex));
}
cursor.close();
return 0;
}
/// Get cursor for active runtime, parameterized by whether or not we use the system broker
static bool getActiveRuntimeCursor(wrap::android::content::Context const &context, jni::Array<std::string> const &projection,
bool systemBroker, Cursor &cursor) {
auto uri = active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI);
ALOGI("getActiveRuntimeCursor: Querying URI: %s", uri.toString().c_str());
try {
cursor = context.getContentResolver().query(uri, projection);
} catch (const std::exception &e) {
ALOGW("Exception when querying %s content resolver: %s", getBrokerTypeName(systemBroker), e.what());
cursor = {};
return false;
}
if (cursor.isNull()) {
ALOGW("Null cursor when querying %s content resolver.", getBrokerTypeName(systemBroker));
cursor = {};
return false;
}
if (cursor.getCount() < 1) {
ALOGW("Non-null but empty cursor when querying %s content resolver.", getBrokerTypeName(systemBroker));
cursor.close();
cursor = {};
return false;
}
return true;
}
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest) {
jni::Array<std::string> projection = makeArray({active_runtime::Columns::PACKAGE_NAME, active_runtime::Columns::NATIVE_LIB_DIR,
active_runtime::Columns::SO_FILENAME, active_runtime::Columns::HAS_FUNCTIONS});
// First, try getting the installable broker's provider
bool systemBroker = false;
Cursor cursor;
if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) {
// OK, try the system broker as a fallback.
systemBroker = true;
getActiveRuntimeCursor(context, projection, systemBroker, cursor);
}
if (cursor.isNull()) {
// Couldn't find either broker
ALOGE("Could access neither the installable nor system runtime broker.");
return -1;
}
cursor.moveToFirst();
auto filename = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::SO_FILENAME));
auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR));
auto packageName = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::PACKAGE_NAME));
auto hasFunctions = cursor.getInt(cursor.getColumnIndex(active_runtime::Columns::HAS_FUNCTIONS)) == 1;
__android_log_print(ANDROID_LOG_INFO, TAG, "Got runtime: package: %s, so filename: %s, native lib dir: %s, has functions: %s",
packageName.c_str(), libDir.c_str(), filename.c_str(), (hasFunctions ? "yes" : "no"));
auto lib_path = libDir + "/" + filename;
cursor.close();
JsonManifestBuilder builder{"runtime", lib_path};
if (hasFunctions) {
int result = populateFunctions(context, systemBroker, packageName, builder);
if (result != 0) {
return result;
}
}
virtualManifest = builder.build();
return 0;
}
} // namespace openxr_android
#endif // __ANDROID__

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2020-2022, The Khronos Group Inc.
// Copyright (c) 2020-2021, Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com>
#pragma once
#ifdef __ANDROID__
#include "wrap/android.content.h"
#include <string>
namespace Json {
class Value;
} // namespace Json
namespace openxr_android {
using wrap::android::content::Context;
/*!
* Find the single active OpenXR runtime on the system, and return a constructed JSON object representing it.
*
* @param context An Android context, preferably an Activity Context.
* @param[out] virtualManifest The Json::Value to fill with the virtual manifest.
*
* @return 0 on success, something else on failure.
*/
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest);
} // namespace openxr_android
#endif // __ANDROID__

View File

@@ -0,0 +1,399 @@
// 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 Author: Mark Young <marky@lunarg.com>
//
#include "api_layer_interface.hpp"
#include "loader_interfaces.h"
#include "loader_logger.hpp"
#include "loader_platform.hpp"
#include "manifest_file.hpp"
#include "platform_utils.hpp"
#include <openxr/openxr.h>
#include <cstring>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#define OPENXR_ENABLE_LAYERS_ENV_VAR "XR_ENABLE_API_LAYERS"
// Add any layers defined in the loader layer environment variable.
static void AddEnvironmentApiLayers(std::vector<std::string>& enabled_layers) {
std::string layers = PlatformUtilsGetEnv(OPENXR_ENABLE_LAYERS_ENV_VAR);
std::size_t last_found = 0;
std::size_t found = layers.find_first_of(PATH_SEPARATOR);
std::string cur_search;
// Handle any path listings in the string (separated by the appropriate path separator)
while (found != std::string::npos) {
cur_search = layers.substr(last_found, found - last_found);
enabled_layers.push_back(cur_search);
last_found = found + 1;
found = layers.find_first_of(PATH_SEPARATOR, last_found);
}
// If there's something remaining in the string, copy it over
if (last_found < layers.size()) {
cur_search = layers.substr(last_found);
enabled_layers.push_back(cur_search);
}
}
XrResult ApiLayerInterface::GetApiLayerProperties(const std::string& openxr_command, uint32_t incoming_count,
uint32_t* outgoing_count, XrApiLayerProperties* api_layer_properties) {
std::vector<std::unique_ptr<ApiLayerManifestFile>> manifest_files;
uint32_t manifest_count = 0;
// Validate props struct before proceeding
if (0 < incoming_count && nullptr != api_layer_properties) {
for (uint32_t i = 0; i < incoming_count; i++) {
if (XR_TYPE_API_LAYER_PROPERTIES != api_layer_properties[i].type) {
LoaderLogger::LogErrorMessage(openxr_command,
"VUID-XrApiLayerProperties-type-type: unknown type in api_layer_properties");
return XR_ERROR_VALIDATION_FAILURE;
}
}
}
// "Independent of elementCapacityInput or elements parameters, elementCountOutput must be a valid pointer,
// and the function sets elementCountOutput." - 2.11
if (nullptr == outgoing_count) {
return XR_ERROR_VALIDATION_FAILURE;
}
// Find any implicit layers which we may need to report information for.
XrResult result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, manifest_files);
if (XR_SUCCEEDED(result)) {
// Find any explicit layers which we may need to report information for.
result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, manifest_files);
}
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage(openxr_command,
"ApiLayerInterface::GetApiLayerProperties - failed searching for API layer manifest files");
return result;
}
manifest_count = static_cast<uint32_t>(manifest_files.size());
if (nullptr == outgoing_count) {
LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties",
"VUID-xrEnumerateApiLayerProperties-propertyCountOutput-parameter: null propertyCountOutput");
return XR_ERROR_VALIDATION_FAILURE;
}
*outgoing_count = manifest_count;
if (0 == incoming_count) {
// capacity check only
return XR_SUCCESS;
}
if (nullptr == api_layer_properties) {
// incoming_count is not 0 BUT the api_layer_properties is NULL
LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties",
"VUID-xrEnumerateApiLayerProperties-properties-parameter: non-zero capacity but null array");
return XR_ERROR_VALIDATION_FAILURE;
}
if (incoming_count < manifest_count) {
LoaderLogger::LogErrorMessage(
"xrEnumerateInstanceExtensionProperties",
"VUID-xrEnumerateApiLayerProperties-propertyCapacityInput-parameter: insufficient space in array");
return XR_ERROR_SIZE_INSUFFICIENT;
}
for (uint32_t prop = 0; prop < incoming_count && prop < manifest_count; ++prop) {
manifest_files[prop]->PopulateApiLayerProperties(api_layer_properties[prop]);
}
return XR_SUCCESS;
}
XrResult ApiLayerInterface::GetInstanceExtensionProperties(const std::string& openxr_command, const char* layer_name,
std::vector<XrExtensionProperties>& extension_properties) {
std::vector<std::unique_ptr<ApiLayerManifestFile>> manifest_files;
// If a layer name is supplied, only use the information out of that one layer
if (nullptr != layer_name && 0 != strlen(layer_name)) {
XrResult result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, manifest_files);
if (XR_SUCCEEDED(result)) {
// Find any explicit layers which we may need to report information for.
result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, manifest_files);
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage(
openxr_command,
"ApiLayerInterface::GetInstanceExtensionProperties - failed searching for API layer manifest files");
return result;
}
bool found = false;
auto num_files = static_cast<uint32_t>(manifest_files.size());
for (uint32_t man_file = 0; man_file < num_files; ++man_file) {
// If a layer with the provided name exists, get it's instance extension information.
if (manifest_files[man_file]->LayerName() == layer_name) {
manifest_files[man_file]->GetInstanceExtensionProperties(extension_properties);
found = true;
break;
}
}
// If nothing found, report 0
if (!found) {
return XR_ERROR_API_LAYER_NOT_PRESENT;
}
}
// Otherwise, we want to add only implicit API layers and explicit API layers enabled using the environment variables
} else {
XrResult result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, manifest_files);
if (XR_SUCCEEDED(result)) {
// Find any environmentally enabled explicit layers. If they're present, treat them like implicit layers
// since we know that they're going to be enabled.
std::vector<std::string> env_enabled_layers;
AddEnvironmentApiLayers(env_enabled_layers);
if (!env_enabled_layers.empty()) {
std::vector<std::unique_ptr<ApiLayerManifestFile>> exp_layer_man_files = {};
result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, exp_layer_man_files);
if (XR_SUCCEEDED(result)) {
for (auto& exp_layer_man_file : exp_layer_man_files) {
for (std::string& enabled_layer : env_enabled_layers) {
// If this is an enabled layer, transfer it over to the manifest list.
if (enabled_layer == exp_layer_man_file->LayerName()) {
manifest_files.push_back(std::move(exp_layer_man_file));
break;
}
}
}
}
}
}
// Grab the layer instance extensions information
auto num_files = static_cast<uint32_t>(manifest_files.size());
for (uint32_t man_file = 0; man_file < num_files; ++man_file) {
manifest_files[man_file]->GetInstanceExtensionProperties(extension_properties);
}
}
return XR_SUCCESS;
}
XrResult ApiLayerInterface::LoadApiLayers(const std::string& openxr_command, uint32_t enabled_api_layer_count,
const char* const* enabled_api_layer_names,
std::vector<std::unique_ptr<ApiLayerInterface>>& api_layer_interfaces) {
XrResult last_error = XR_SUCCESS;
std::unordered_set<std::string> layers_already_found;
bool any_loaded = false;
std::vector<std::unique_ptr<ApiLayerManifestFile>> enabled_layer_manifest_files_in_init_order = {};
// Find any implicit layers.
XrResult result =
ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, enabled_layer_manifest_files_in_init_order);
for (const auto& enabled_layer_manifest_file : enabled_layer_manifest_files_in_init_order) {
layers_already_found.insert(enabled_layer_manifest_file->LayerName());
}
// Find any explicit layers.
std::vector<std::unique_ptr<ApiLayerManifestFile>> explicit_layer_manifest_files = {};
if (XR_SUCCEEDED(result)) {
result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, explicit_layer_manifest_files);
}
bool found_all_layers = true;
if (XR_SUCCEEDED(result)) {
// Put all explicit and then xrCreateInstance enabled layers into a string vector
std::vector<std::string> enabled_explicit_api_layer_names = {};
AddEnvironmentApiLayers(enabled_explicit_api_layer_names);
if (enabled_api_layer_count > 0) {
if (nullptr == enabled_api_layer_names) {
LoaderLogger::LogErrorMessage(
"xrCreateInstance",
"VUID-XrInstanceCreateInfo-enabledApiLayerNames-parameter: enabledApiLayerCount is non-0 but array is NULL");
LoaderLogger::LogErrorMessage(
"xrCreateInstance", "VUID-xrCreateInstance-info-parameter: something wrong with XrInstanceCreateInfo contents");
return XR_ERROR_VALIDATION_FAILURE;
}
std::copy(enabled_api_layer_names, enabled_api_layer_names + enabled_api_layer_count,
std::back_inserter(enabled_explicit_api_layer_names));
}
// add explicit layers to list of layers to enable
for (const auto& layer_name : enabled_explicit_api_layer_names) {
bool found_this_layer = false;
for (auto it = explicit_layer_manifest_files.begin(); it != explicit_layer_manifest_files.end();) {
bool erased_layer_manifest_file = false;
if (layers_already_found.count(layer_name) > 0) {
found_this_layer = true;
} else if (layer_name == (*it)->LayerName()) {
found_this_layer = true;
layers_already_found.insert(layer_name);
enabled_layer_manifest_files_in_init_order.push_back(std::move(*it));
it = explicit_layer_manifest_files.erase(it);
erased_layer_manifest_file = true;
}
if (!erased_layer_manifest_file) {
it++;
}
}
// If even one of the layers wasn't found, we want to return an error
if (!found_this_layer) {
found_all_layers = false;
std::string error_message = "ApiLayerInterface::LoadApiLayers - failed to find layer ";
error_message += layer_name;
LoaderLogger::LogErrorMessage(openxr_command, error_message);
}
}
}
for (std::unique_ptr<ApiLayerManifestFile>& manifest_file : enabled_layer_manifest_files_in_init_order) {
LoaderPlatformLibraryHandle layer_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());
if (nullptr == layer_library) {
if (!any_loaded) {
last_error = XR_ERROR_FILE_ACCESS_ERROR;
}
std::string library_message = LoaderPlatformLibraryOpenError(manifest_file->LibraryPath());
std::string warning_message = "ApiLayerInterface::LoadApiLayers skipping layer ";
warning_message += manifest_file->LayerName();
warning_message += ", failed to load with message \"";
warning_message += library_message;
warning_message += "\"";
LoaderLogger::LogWarningMessage(openxr_command, warning_message);
continue;
}
// Get and settle on an layer interface version (using any provided name if required).
std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderApiLayerInterface");
auto negotiate = reinterpret_cast<PFN_xrNegotiateLoaderApiLayerInterface>(
LoaderPlatformLibraryGetProcAddr(layer_library, function_name));
if (nullptr == negotiate) {
std::ostringstream oss;
oss << "ApiLayerInterface::LoadApiLayers skipping layer " << manifest_file->LayerName()
<< " because negotiation function " << function_name << " was not found";
LoaderLogger::LogErrorMessage(openxr_command, oss.str());
LoaderPlatformLibraryClose(layer_library);
last_error = XR_ERROR_API_LAYER_NOT_PRESENT;
continue;
}
// Loader info for negotiation
XrNegotiateLoaderInfo loader_info = {};
loader_info.structType = XR_LOADER_INTERFACE_STRUCT_LOADER_INFO;
loader_info.structVersion = XR_LOADER_INFO_STRUCT_VERSION;
loader_info.structSize = sizeof(XrNegotiateLoaderInfo);
loader_info.minInterfaceVersion = 1;
loader_info.maxInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION;
loader_info.minApiVersion = XR_MAKE_VERSION(1, 0, 0);
loader_info.maxApiVersion = XR_MAKE_VERSION(1, 0x3ff, 0xfff); // Maximum allowed version for this major version.
// Set up the layer return structure
XrNegotiateApiLayerRequest api_layer_info = {};
api_layer_info.structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST;
api_layer_info.structVersion = XR_API_LAYER_INFO_STRUCT_VERSION;
api_layer_info.structSize = sizeof(XrNegotiateApiLayerRequest);
XrResult res = negotiate(&loader_info, manifest_file->LayerName().c_str(), &api_layer_info);
// If we supposedly succeeded, but got a nullptr for getInstanceProcAddr
// then something still went wrong, so return with an error.
if (XR_SUCCEEDED(res) && nullptr == api_layer_info.getInstanceProcAddr) {
std::string warning_message = "ApiLayerInterface::LoadApiLayers skipping layer ";
warning_message += manifest_file->LayerName();
warning_message += ", negotiation did not return a valid getInstanceProcAddr";
LoaderLogger::LogWarningMessage(openxr_command, warning_message);
res = XR_ERROR_FILE_CONTENTS_INVALID;
}
if (XR_FAILED(res)) {
if (!any_loaded) {
last_error = res;
}
std::ostringstream oss;
oss << "ApiLayerInterface::LoadApiLayers skipping layer " << manifest_file->LayerName()
<< " due to failed negotiation with error " << res;
LoaderLogger::LogWarningMessage(openxr_command, oss.str());
LoaderPlatformLibraryClose(layer_library);
continue;
}
{
std::ostringstream oss;
oss << "ApiLayerInterface::LoadApiLayers succeeded loading layer " << manifest_file->LayerName()
<< " using interface version " << api_layer_info.layerInterfaceVersion << " and OpenXR API version "
<< XR_VERSION_MAJOR(api_layer_info.layerApiVersion) << "." << XR_VERSION_MINOR(api_layer_info.layerApiVersion);
LoaderLogger::LogInfoMessage(openxr_command, oss.str());
}
// Grab the list of extensions this layer supports for easy filtering after the
// xrCreateInstance call
std::vector<std::string> supported_extensions;
std::vector<XrExtensionProperties> extension_properties;
manifest_file->GetInstanceExtensionProperties(extension_properties);
supported_extensions.reserve(extension_properties.size());
for (XrExtensionProperties& ext_prop : extension_properties) {
supported_extensions.emplace_back(ext_prop.extensionName);
}
// Add this API layer to the vector
api_layer_interfaces.emplace_back(new ApiLayerInterface(manifest_file->LayerName(), layer_library, supported_extensions,
api_layer_info.getInstanceProcAddr,
api_layer_info.createApiLayerInstance));
// If we load one, clear all errors.
any_loaded = true;
last_error = XR_SUCCESS;
}
// Set error here to preserve prior error behavior
if (!found_all_layers) {
last_error = XR_ERROR_API_LAYER_NOT_PRESENT;
}
// If we failed catastrophically for some reason, clean up everything.
if (XR_FAILED(last_error)) {
api_layer_interfaces.clear();
}
return last_error;
}
ApiLayerInterface::ApiLayerInterface(const std::string& layer_name, LoaderPlatformLibraryHandle layer_library,
std::vector<std::string>& supported_extensions,
PFN_xrGetInstanceProcAddr get_instance_proc_addr,
PFN_xrCreateApiLayerInstance create_api_layer_instance)
: _layer_name(layer_name),
_layer_library(layer_library),
_get_instance_proc_addr(get_instance_proc_addr),
_create_api_layer_instance(create_api_layer_instance),
_supported_extensions(supported_extensions) {}
ApiLayerInterface::~ApiLayerInterface() {
std::string info_message = "ApiLayerInterface being destroyed for layer ";
info_message += _layer_name;
LoaderLogger::LogInfoMessage("", info_message);
LoaderPlatformLibraryClose(_layer_library);
}
bool ApiLayerInterface::SupportsExtension(const std::string& extension_name) const {
bool found_prop = false;
for (const std::string& supported_extension : _supported_extensions) {
if (supported_extension == extension_name) {
found_prop = true;
break;
}
}
return found_prop;
}

View File

@@ -0,0 +1,54 @@
// 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 Author: Mark Young <marky@lunarg.com>
//
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <openxr/openxr.h>
#include "loader_platform.hpp"
#include "loader_interfaces.h"
struct XrGeneratedDispatchTable;
class ApiLayerInterface {
public:
// Factory method
static XrResult LoadApiLayers(const std::string& openxr_command, uint32_t enabled_api_layer_count,
const char* const* enabled_api_layer_names,
std::vector<std::unique_ptr<ApiLayerInterface>>& api_layer_interfaces);
// Static queries
static XrResult GetApiLayerProperties(const std::string& openxr_command, uint32_t incoming_count, uint32_t* outgoing_count,
XrApiLayerProperties* api_layer_properties);
static XrResult GetInstanceExtensionProperties(const std::string& openxr_command, const char* layer_name,
std::vector<XrExtensionProperties>& extension_properties);
ApiLayerInterface(const std::string& layer_name, LoaderPlatformLibraryHandle layer_library,
std::vector<std::string>& supported_extensions, PFN_xrGetInstanceProcAddr get_instance_proc_addr,
PFN_xrCreateApiLayerInstance create_api_layer_instance);
virtual ~ApiLayerInterface();
PFN_xrGetInstanceProcAddr GetInstanceProcAddrFuncPointer() { return _get_instance_proc_addr; }
PFN_xrCreateApiLayerInstance GetCreateApiLayerInstanceFuncPointer() { return _create_api_layer_instance; }
std::string LayerName() { return _layer_name; }
// Generated methods
bool SupportsExtension(const std::string& extension_name) const;
private:
std::string _layer_name;
LoaderPlatformLibraryHandle _layer_library;
PFN_xrGetInstanceProcAddr _get_instance_proc_addr;
PFN_xrCreateApiLayerInstance _create_api_layer_instance;
std::vector<std::string> _supported_extensions;
};

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2019-2022, The Khronos Group Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com>
//
// Provides protection for C ABI functions if standard library functions may throw.
#pragma once
#ifdef OPENXR_HAVE_COMMON_CONFIG
#include "common_config.h"
#endif // OPENXR_HAVE_COMMON_CONFIG
#ifdef XRLOADER_DISABLE_EXCEPTION_HANDLING
#define XRLOADER_ABI_TRY
#define XRLOADER_ABI_CATCH_BAD_ALLOC_OOM
#define XRLOADER_ABI_CATCH_FALLBACK
#else
#include <stdexcept>
#define XRLOADER_ABI_TRY try
#define XRLOADER_ABI_CATCH_BAD_ALLOC_OOM \
catch (const std::bad_alloc&) { \
LoaderLogger::LogErrorMessage("", "failed allocating memory"); \
return XR_ERROR_OUT_OF_MEMORY; \
}
#define XRLOADER_ABI_CATCH_FALLBACK \
catch (const std::exception& e) { \
LoaderLogger::LogErrorMessage("", "Unknown failure: " + std::string(e.what())); \
return XR_ERROR_RUNTIME_FAILURE; \
} \
catch (...) { \
LoaderLogger::LogErrorMessage("", "Unknown failure"); \
return XR_ERROR_RUNTIME_FAILURE; \
}
#endif

View File

@@ -0,0 +1,847 @@
// 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>
//
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#include "api_layer_interface.hpp"
#include "exception_handling.hpp"
#include "hex_and_handles.h"
#include "loader_instance.hpp"
#include "loader_logger_recorders.hpp"
#include "loader_logger.hpp"
#include "loader_platform.hpp"
#include "runtime_interface.hpp"
#include "xr_generated_dispatch_table.h"
#include "xr_generated_loader.hpp"
#include <openxr/openxr.h>
#include <cstring>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
// Global loader lock to:
// 1. Ensure ActiveLoaderInstance get and set operations are done atomically.
// 2. Ensure RuntimeInterface isn't used to unload the runtime while the runtime is in use.
std::mutex &GetGlobalLoaderMutex() {
static std::mutex loader_mutex;
return loader_mutex;
}
// Prototypes for the debug utils calls used internally.
static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineCreateDebugUtilsMessengerEXT(
XrInstance instance, const XrDebugUtilsMessengerCreateInfoEXT *createInfo, XrDebugUtilsMessengerEXT *messenger);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger);
// Terminal functions needed by xrCreateInstance.
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermGetInstanceProcAddr(XrInstance, const char *, PFN_xrVoidFunction *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateInstance(const XrInstanceCreateInfo *, XrInstance *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateApiLayerInstance(const XrInstanceCreateInfo *,
const struct XrApiLayerCreateInfo *, XrInstance *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSetDebugUtilsObjectNameEXT(XrInstance, const XrDebugUtilsObjectNameInfoEXT *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateDebugUtilsMessengerEXT(XrInstance,
const XrDebugUtilsMessengerCreateInfoEXT *,
XrDebugUtilsMessengerEXT *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSubmitDebugUtilsMessageEXT(
XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
const XrDebugUtilsMessengerCallbackDataEXT *callbackData);
// Utility template function meant to validate if a fixed size string contains
// a null-terminator.
template <size_t max_length>
inline bool IsMissingNullTerminator(const char (&str)[max_length]) {
for (size_t index = 0; index < max_length; ++index) {
if (str[index] == '\0') {
return false;
}
}
return true;
}
// ---- Core 1.0 manual loader trampoline functions
#ifdef XR_KHR_LOADER_INIT_SUPPORT // platforms that support XR_KHR_loader_init.
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrInitializeLoaderKHR(const XrLoaderInitInfoBaseHeaderKHR *loaderInitInfo) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrInitializeLoaderKHR", "Entering loader trampoline");
return InitializeLoader(loaderInitInfo);
}
XRLOADER_ABI_CATCH_FALLBACK
#endif
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrEnumerateApiLayerProperties(uint32_t propertyCapacityInput,
uint32_t *propertyCountOutput,
XrApiLayerProperties *properties) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrEnumerateApiLayerProperties", "Entering loader trampoline");
// Make sure only one thread is attempting to read the JSON files at a time.
std::unique_lock<std::mutex> loader_lock(GetGlobalLoaderMutex());
XrResult result = ApiLayerInterface::GetApiLayerProperties("xrEnumerateApiLayerProperties", propertyCapacityInput,
propertyCountOutput, properties);
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage("xrEnumerateApiLayerProperties", "Failed ApiLayerInterface::GetApiLayerProperties");
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderXrEnumerateInstanceExtensionProperties(const char *layerName, uint32_t propertyCapacityInput, uint32_t *propertyCountOutput,
XrExtensionProperties *properties) XRLOADER_ABI_TRY {
bool just_layer_properties = false;
LoaderLogger::LogVerboseMessage("xrEnumerateInstanceExtensionProperties", "Entering loader trampoline");
// "Independent of elementCapacityInput or elements parameters, elementCountOutput must be a valid pointer,
// and the function sets elementCountOutput." - 2.11
if (nullptr == propertyCountOutput) {
return XR_ERROR_VALIDATION_FAILURE;
}
if (nullptr != layerName && 0 != strlen(layerName)) {
// Application is only interested in layer's properties, not all of them.
just_layer_properties = true;
}
std::vector<XrExtensionProperties> extension_properties = {};
XrResult result;
{
// Make sure the runtime isn't unloaded while this call is in progress.
std::unique_lock<std::mutex> loader_lock(GetGlobalLoaderMutex());
// Get the layer extension properties
result = ApiLayerInterface::GetInstanceExtensionProperties("xrEnumerateInstanceExtensionProperties", layerName,
extension_properties);
if (XR_SUCCEEDED(result) && !just_layer_properties) {
// If not specific to a layer, get the runtime extension properties
result = RuntimeInterface::LoadRuntime("xrEnumerateInstanceExtensionProperties");
if (XR_SUCCEEDED(result)) {
RuntimeInterface::GetRuntime().GetInstanceExtensionProperties(extension_properties);
} else {
LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties",
"Failed to find default runtime with RuntimeInterface::LoadRuntime()");
}
}
}
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties", "Failed querying extension properties");
return result;
}
// If this is not in reference to a specific layer, then add the loader-specific extension properties as well.
// These are extensions that the loader directly supports.
if (!just_layer_properties) {
for (const XrExtensionProperties &loader_prop : LoaderInstance::LoaderSpecificExtensions()) {
bool found_prop = false;
for (XrExtensionProperties &existing_prop : extension_properties) {
if (0 == strcmp(existing_prop.extensionName, loader_prop.extensionName)) {
found_prop = true;
// Use the loader version if it is newer
if (existing_prop.extensionVersion < loader_prop.extensionVersion) {
existing_prop.extensionVersion = loader_prop.extensionVersion;
}
break;
}
}
// Only add extensions not supported by the loader
if (!found_prop) {
extension_properties.push_back(loader_prop);
}
}
}
auto num_extension_properties = static_cast<uint32_t>(extension_properties.size());
if (propertyCapacityInput == 0) {
*propertyCountOutput = num_extension_properties;
} else if (nullptr != properties) {
if (propertyCapacityInput < num_extension_properties) {
*propertyCountOutput = num_extension_properties;
LoaderLogger::LogValidationErrorMessage("VUID-xrEnumerateInstanceExtensionProperties-propertyCountOutput-parameter",
"xrEnumerateInstanceExtensionProperties", "insufficient space in array");
return XR_ERROR_SIZE_INSUFFICIENT;
}
uint32_t num_to_copy = num_extension_properties;
// Determine how many extension properties we can copy over
if (propertyCapacityInput < num_to_copy) {
num_to_copy = propertyCapacityInput;
}
bool properties_valid = true;
for (uint32_t prop = 0; prop < propertyCapacityInput && prop < extension_properties.size(); ++prop) {
if (XR_TYPE_EXTENSION_PROPERTIES != properties[prop].type) {
properties_valid = false;
LoaderLogger::LogValidationErrorMessage("VUID-XrExtensionProperties-type-type",
"xrEnumerateInstanceExtensionProperties", "unknown type in properties");
}
if (properties_valid) {
properties[prop] = extension_properties[prop];
}
}
if (!properties_valid) {
LoaderLogger::LogValidationErrorMessage("VUID-xrEnumerateInstanceExtensionProperties-properties-parameter",
"xrEnumerateInstanceExtensionProperties", "invalid properties");
return XR_ERROR_VALIDATION_FAILURE;
}
if (nullptr != propertyCountOutput) {
*propertyCountOutput = num_to_copy;
}
} else {
// incoming_count is not 0 BUT the properties is NULL
return XR_ERROR_VALIDATION_FAILURE;
}
LoaderLogger::LogVerboseMessage("xrEnumerateInstanceExtensionProperties", "Completed loader trampoline");
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrCreateInstance(const XrInstanceCreateInfo *info,
XrInstance *instance) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Entering loader trampoline");
if (nullptr == info) {
LoaderLogger::LogValidationErrorMessage("VUID-xrCreateInstance-info-parameter", "xrCreateInstance", "must be non-NULL");
return XR_ERROR_VALIDATION_FAILURE;
}
// If application requested OpenXR API version is higher than the loader version, then we need to throw
// an error.
uint16_t app_major = XR_VERSION_MAJOR(info->applicationInfo.apiVersion); // NOLINT
uint16_t app_minor = XR_VERSION_MINOR(info->applicationInfo.apiVersion); // NOLINT
uint16_t loader_major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION); // NOLINT
uint16_t loader_minor = XR_VERSION_MINOR(XR_CURRENT_API_VERSION); // NOLINT
if (app_major > loader_major || (app_major == loader_major && app_minor > loader_minor)) {
std::ostringstream oss;
oss << "xrCreateInstance called with invalid API version " << app_major << "." << app_minor
<< ". Max supported version is " << loader_major << "." << loader_minor;
LoaderLogger::LogErrorMessage("xrCreateInstance", oss.str());
return XR_ERROR_API_VERSION_UNSUPPORTED;
}
if (nullptr == instance) {
LoaderLogger::LogValidationErrorMessage("VUID-xrCreateInstance-instance-parameter", "xrCreateInstance", "must be non-NULL");
return XR_ERROR_VALIDATION_FAILURE;
}
// Make sure the ActiveLoaderInstance::IsAvailable check is done atomically with RuntimeInterface::LoadRuntime.
std::unique_lock<std::mutex> instance_lock(GetGlobalLoaderMutex());
// Check if there is already an XrInstance that is alive. If so, another instance cannot be created.
// The loader does not support multiple simultaneous instances because the loader is intended to be
// usable by apps using future OpenXR APIs (through xrGetInstanceProcAddr). Because the loader would
// not be aware of new handle types, it would not be able to look up the appropriate dispatch table
// in some cases.
if (ActiveLoaderInstance::IsAvailable()) { // If there is an XrInstance already alive.
LoaderLogger::LogErrorMessage("xrCreateInstance", "Loader does not support simultaneous XrInstances");
return XR_ERROR_LIMIT_REACHED;
}
std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces;
XrResult result;
// Make sure only one thread is attempting to read the JSON files and use the instance.
{
// Load the available runtime
result = RuntimeInterface::LoadRuntime("xrCreateInstance");
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage("xrCreateInstance", "Failed loading runtime information");
} else {
// Load the appropriate layers
result = ApiLayerInterface::LoadApiLayers("xrCreateInstance", info->enabledApiLayerCount, info->enabledApiLayerNames,
api_layer_interfaces);
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage("xrCreateInstance", "Failed loading layer information");
}
}
}
// Create the loader instance (only send down first runtime interface)
LoaderInstance *loader_instance = nullptr;
if (XR_SUCCEEDED(result)) {
std::unique_ptr<LoaderInstance> owned_loader_instance;
result = LoaderInstance::CreateInstance(LoaderXrTermGetInstanceProcAddr, LoaderXrTermCreateInstance,
LoaderXrTermCreateApiLayerInstance, std::move(api_layer_interfaces), info,
&owned_loader_instance);
if (XR_SUCCEEDED(result)) {
loader_instance = owned_loader_instance.get();
result = ActiveLoaderInstance::Set(std::move(owned_loader_instance), "xrCreateInstance");
}
}
if (XR_SUCCEEDED(result)) {
// Create a debug utils messenger if the create structure is in the "next" chain
const auto *next_header = reinterpret_cast<const XrBaseInStructure *>(info->next);
const XrDebugUtilsMessengerCreateInfoEXT *dbg_utils_create_info = nullptr;
while (next_header != nullptr) {
if (next_header->type == XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) {
LoaderLogger::LogInfoMessage("xrCreateInstance", "Found XrDebugUtilsMessengerCreateInfoEXT in \'next\' chain.");
dbg_utils_create_info = reinterpret_cast<const XrDebugUtilsMessengerCreateInfoEXT *>(next_header);
XrDebugUtilsMessengerEXT messenger;
result = LoaderTrampolineCreateDebugUtilsMessengerEXT(loader_instance->GetInstanceHandle(), dbg_utils_create_info,
&messenger);
if (XR_FAILED(result)) {
return XR_ERROR_VALIDATION_FAILURE;
}
loader_instance->SetDefaultDebugUtilsMessenger(messenger);
break;
}
next_header = reinterpret_cast<const XrBaseInStructure *>(next_header->next);
}
}
if (XR_FAILED(result)) {
// Ensure the global loader instance and runtime are destroyed if something went wrong.
ActiveLoaderInstance::Remove();
RuntimeInterface::UnloadRuntime("xrCreateInstance");
LoaderLogger::LogErrorMessage("xrCreateInstance", "xrCreateInstance failed");
} else {
*instance = loader_instance->GetInstanceHandle();
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Completed loader trampoline");
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrDestroyInstance(XrInstance instance) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Entering loader trampoline");
// Runtimes may detect XR_NULL_HANDLE provided as a required handle parameter and return XR_ERROR_HANDLE_INVALID. - 2.9
if (XR_NULL_HANDLE == instance) {
LoaderLogger::LogErrorMessage("xrDestroyInstance", "Instance handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
// Make sure the runtime isn't unloaded while it is being used by xrEnumerateInstanceExtensionProperties.
std::unique_lock<std::mutex> loader_lock(GetGlobalLoaderMutex());
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroyInstance");
if (XR_FAILED(result)) {
return result;
}
const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
// If we allocated a default debug utils messenger, free it
XrDebugUtilsMessengerEXT messenger = loader_instance->DefaultDebugUtilsMessenger();
if (messenger != XR_NULL_HANDLE) {
LoaderTrampolineDestroyDebugUtilsMessengerEXT(messenger);
}
// Now destroy the instance
if (XR_FAILED(dispatch_table->DestroyInstance(instance))) {
LoaderLogger::LogErrorMessage("xrDestroyInstance", "Unknown error occurred calling down chain");
}
// Get rid of the loader instance. This will make it possible to create another instance in the future.
ActiveLoaderInstance::Remove();
// Lock the instance create/destroy mutex
LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Completed loader trampoline");
// Finally, unload the runtime if necessary
RuntimeInterface::UnloadRuntime("xrDestroyInstance");
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
// ---- Core 1.0 manual loader terminator functions
// Validate that the applicationInfo structure in the XrInstanceCreateInfo is valid.
static XrResult ValidateApplicationInfo(const XrApplicationInfo &info) {
if (IsMissingNullTerminator<XR_MAX_APPLICATION_NAME_SIZE>(info.applicationName)) {
LoaderLogger::LogValidationErrorMessage("VUID-XrApplicationInfo-applicationName-parameter", "xrCreateInstance",
"application name missing NULL terminator.");
return XR_ERROR_NAME_INVALID;
}
if (IsMissingNullTerminator<XR_MAX_ENGINE_NAME_SIZE>(info.engineName)) {
LoaderLogger::LogValidationErrorMessage("VUID-XrApplicationInfo-engineName-parameter", "xrCreateInstance",
"engine name missing NULL terminator.");
return XR_ERROR_NAME_INVALID;
}
if (strlen(info.applicationName) == 0) {
LoaderLogger::LogErrorMessage("xrCreateInstance",
"VUID-XrApplicationInfo-engineName-parameter: application name can not be empty.");
return XR_ERROR_NAME_INVALID;
}
return XR_SUCCESS;
}
// Validate that the XrInstanceCreateInfo is valid
static XrResult ValidateInstanceCreateInfo(const XrInstanceCreateInfo *info) {
// Should have a valid 'type'
if (XR_TYPE_INSTANCE_CREATE_INFO != info->type) {
LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-type-type", "xrCreateInstance",
"expected XR_TYPE_INSTANCE_CREATE_INFO.");
return XR_ERROR_VALIDATION_FAILURE;
}
// Flags must be 0
if (0 != info->createFlags) {
LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-createFlags-zerobitmask", "xrCreateInstance",
"flags must be 0.");
return XR_ERROR_VALIDATION_FAILURE;
}
// ApplicationInfo struct must be valid
XrResult result = ValidateApplicationInfo(info->applicationInfo);
if (XR_FAILED(result)) {
LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-applicationInfo-parameter", "xrCreateInstance",
"info->applicationInfo is not valid.");
return result;
}
// VUID-XrInstanceCreateInfo-enabledApiLayerNames-parameter already tested in LoadApiLayers()
if ((info->enabledExtensionCount != 0u) && nullptr == info->enabledExtensionNames) {
LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-enabledExtensionNames-parameter", "xrCreateInstance",
"enabledExtensionCount is non-0 but array is NULL");
return XR_ERROR_VALIDATION_FAILURE;
}
return XR_SUCCESS;
}
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateInstance(const XrInstanceCreateInfo *createInfo,
XrInstance *instance) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Entering loader terminator");
XrResult result = ValidateInstanceCreateInfo(createInfo);
if (XR_FAILED(result)) {
LoaderLogger::LogValidationErrorMessage("VUID-xrCreateInstance-info-parameter", "xrCreateInstance",
"something wrong with XrInstanceCreateInfo contents");
return result;
}
result = RuntimeInterface::GetRuntime().CreateInstance(createInfo, instance);
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateApiLayerInstance(const XrInstanceCreateInfo *info,
const struct XrApiLayerCreateInfo * /*apiLayerInfo*/,
XrInstance *instance) {
return LoaderXrTermCreateInstance(info, instance);
}
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyInstance(XrInstance instance) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Entering loader terminator");
LoaderLogger::GetInstance().RemoveLogRecordersForXrInstance(instance);
XrResult result = RuntimeInterface::GetRuntime().DestroyInstance(instance);
LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermGetInstanceProcAddr(XrInstance instance, const char *name,
PFN_xrVoidFunction *function) XRLOADER_ABI_TRY {
// A few instance commands need to go through a loader terminator.
// Otherwise, go directly to the runtime version of the command if it exists.
// But first set the function pointer to NULL so that the fall-through below actually works.
*function = nullptr;
// NOTE: ActiveLoaderInstance cannot be used in this function because it is called before an instance is made active.
if (0 == strcmp(name, "xrGetInstanceProcAddr")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermGetInstanceProcAddr);
} else if (0 == strcmp(name, "xrCreateInstance")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermCreateInstance);
} else if (0 == strcmp(name, "xrDestroyInstance")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermDestroyInstance);
} else if (0 == strcmp(name, "xrSetDebugUtilsObjectNameEXT")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermSetDebugUtilsObjectNameEXT);
} else if (0 == strcmp(name, "xrCreateDebugUtilsMessengerEXT")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermCreateDebugUtilsMessengerEXT);
} else if (0 == strcmp(name, "xrDestroyDebugUtilsMessengerEXT")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermDestroyDebugUtilsMessengerEXT);
} else if (0 == strcmp(name, "xrSubmitDebugUtilsMessageEXT")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermSubmitDebugUtilsMessageEXT);
} else if (0 == strcmp(name, "xrCreateApiLayerInstance")) {
// Special layer version of xrCreateInstance terminator. If we get called this by a layer,
// we simply re-direct the information back into the standard xrCreateInstance terminator.
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermCreateApiLayerInstance);
}
if (nullptr != *function) {
return XR_SUCCESS;
}
return RuntimeInterface::GetInstanceProcAddr(instance, name, function);
}
XRLOADER_ABI_CATCH_FALLBACK
// ---- Extension manual loader trampoline functions
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineCreateDebugUtilsMessengerEXT(XrInstance instance, const XrDebugUtilsMessengerCreateInfoEXT *createInfo,
XrDebugUtilsMessengerEXT *messenger) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Entering loader trampoline");
if (instance == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrCreateDebugUtilsMessengerEXT", "Instance handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrCreateDebugUtilsMessengerEXT");
if (XR_FAILED(result)) {
return result;
}
result = loader_instance->DispatchTable()->CreateDebugUtilsMessengerEXT(instance, createInfo, messenger);
LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Completed loader trampoline");
return result;
}
XRLOADER_ABI_CATCH_BAD_ALLOC_OOM XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger) XRLOADER_ABI_TRY {
// TODO: get instance from messenger in loader
// Also, is the loader really doing all this every call?
LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Entering loader trampoline");
if (messenger == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrDestroyDebugUtilsMessengerEXT", "Messenger handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroyDebugUtilsMessengerEXT");
if (XR_FAILED(result)) {
return result;
}
result = loader_instance->DispatchTable()->DestroyDebugUtilsMessengerEXT(messenger);
LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Completed loader trampoline");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineSessionBeginDebugUtilsLabelRegionEXT(XrSession session, const XrDebugUtilsLabelEXT *labelInfo) XRLOADER_ABI_TRY {
if (session == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrSessionBeginDebugUtilsLabelRegionEXT", "Session handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
if (nullptr == labelInfo) {
LoaderLogger::LogValidationErrorMessage("VUID-xrSessionBeginDebugUtilsLabelRegionEXT-labelInfo-parameter",
"xrSessionBeginDebugUtilsLabelRegionEXT", "labelInfo must be non-NULL",
{XrSdkLogObjectInfo{session, XR_OBJECT_TYPE_SESSION}});
return XR_ERROR_VALIDATION_FAILURE;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSessionBeginDebugUtilsLabelRegionEXT");
if (XR_FAILED(result)) {
return result;
}
LoaderLogger::GetInstance().BeginLabelRegion(session, labelInfo);
const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
if (nullptr != dispatch_table->SessionBeginDebugUtilsLabelRegionEXT) {
return dispatch_table->SessionBeginDebugUtilsLabelRegionEXT(session, labelInfo);
}
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineSessionEndDebugUtilsLabelRegionEXT(XrSession session) XRLOADER_ABI_TRY {
if (session == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrSessionEndDebugUtilsLabelRegionEXT", "Session handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSessionEndDebugUtilsLabelRegionEXT");
if (XR_FAILED(result)) {
return result;
}
LoaderLogger::GetInstance().EndLabelRegion(session);
const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
if (nullptr != dispatch_table->SessionEndDebugUtilsLabelRegionEXT) {
return dispatch_table->SessionEndDebugUtilsLabelRegionEXT(session);
}
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineSessionInsertDebugUtilsLabelEXT(XrSession session, const XrDebugUtilsLabelEXT *labelInfo) XRLOADER_ABI_TRY {
if (session == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrSessionInsertDebugUtilsLabelEXT", "Session handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSessionInsertDebugUtilsLabelEXT");
if (XR_FAILED(result)) {
return result;
}
if (nullptr == labelInfo) {
LoaderLogger::LogValidationErrorMessage("VUID-xrSessionInsertDebugUtilsLabelEXT-labelInfo-parameter",
"xrSessionInsertDebugUtilsLabelEXT", "labelInfo must be non-NULL",
{XrSdkLogObjectInfo{session, XR_OBJECT_TYPE_SESSION}});
return XR_ERROR_VALIDATION_FAILURE;
}
LoaderLogger::GetInstance().InsertLabel(session, labelInfo);
const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
if (nullptr != dispatch_table->SessionInsertDebugUtilsLabelEXT) {
return dispatch_table->SessionInsertDebugUtilsLabelEXT(session, labelInfo);
}
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
// No-op trampoline needed for xrGetInstanceProcAddr. Work done in terminator.
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineSetDebugUtilsObjectNameEXT(XrInstance instance, const XrDebugUtilsObjectNameInfoEXT *nameInfo) XRLOADER_ABI_TRY {
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSetDebugUtilsObjectNameEXT");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->SetDebugUtilsObjectNameEXT(instance, nameInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
// No-op trampoline needed for xrGetInstanceProcAddr. Work done in terminator.
static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineSubmitDebugUtilsMessageEXT(
XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
const XrDebugUtilsMessengerCallbackDataEXT *callbackData) XRLOADER_ABI_TRY {
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSubmitDebugUtilsMessageEXT");
if (XR_SUCCEEDED(result)) {
result =
loader_instance->DispatchTable()->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, callbackData);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
// ---- Extension manual loader terminator functions
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateDebugUtilsMessengerEXT(XrInstance instance,
const XrDebugUtilsMessengerCreateInfoEXT *createInfo,
XrDebugUtilsMessengerEXT *messenger) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Entering loader terminator");
if (nullptr == messenger) {
LoaderLogger::LogValidationErrorMessage("VUID-xrCreateDebugUtilsMessengerEXT-messenger-parameter",
"xrCreateDebugUtilsMessengerEXT", "invalid messenger pointer");
return XR_ERROR_VALIDATION_FAILURE;
}
const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
XrResult result = XR_SUCCESS;
// This extension is supported entirely by the loader which means the runtime may or may not support it.
if (nullptr != dispatch_table->CreateDebugUtilsMessengerEXT) {
result = dispatch_table->CreateDebugUtilsMessengerEXT(instance, createInfo, messenger);
} else {
// Just allocate a character so we have a unique value
char *temp_mess_ptr = new char;
*messenger = reinterpret_cast<XrDebugUtilsMessengerEXT>(temp_mess_ptr);
}
if (XR_SUCCEEDED(result)) {
LoaderLogger::GetInstance().AddLogRecorderForXrInstance(instance, MakeDebugUtilsLoaderLogRecorder(createInfo, *messenger));
RuntimeInterface::GetRuntime().TrackDebugMessenger(instance, *messenger);
}
LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Entering loader terminator");
const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDebugUtilsMessengerDispatchTable(messenger);
XrResult result = XR_SUCCESS;
LoaderLogger::GetInstance().RemoveLogRecorder(MakeHandleGeneric(messenger));
RuntimeInterface::GetRuntime().ForgetDebugMessenger(messenger);
// This extension is supported entirely by the loader which means the runtime may or may not support it.
if (nullptr != dispatch_table->DestroyDebugUtilsMessengerEXT) {
result = dispatch_table->DestroyDebugUtilsMessengerEXT(messenger);
} else {
// Delete the character we would've created
delete (reinterpret_cast<char *>(MakeHandleGeneric(messenger)));
}
LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSubmitDebugUtilsMessageEXT(
XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
const XrDebugUtilsMessengerCallbackDataEXT *callbackData) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrSubmitDebugUtilsMessageEXT", "Entering loader terminator");
const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
XrResult result = XR_SUCCESS;
if (nullptr != dispatch_table->SubmitDebugUtilsMessageEXT) {
result = dispatch_table->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, callbackData);
} else {
// Only log the message from the loader if the runtime doesn't support this extension. If we did,
// then the user would receive multiple instances of the same message.
LoaderLogger::GetInstance().LogDebugUtilsMessage(messageSeverity, messageTypes, callbackData);
}
LoaderLogger::LogVerboseMessage("xrSubmitDebugUtilsMessageEXT", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL
LoaderXrTermSetDebugUtilsObjectNameEXT(XrInstance instance, const XrDebugUtilsObjectNameInfoEXT *nameInfo) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrSetDebugUtilsObjectNameEXT", "Entering loader terminator");
const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
XrResult result = XR_SUCCESS;
if (nullptr != dispatch_table->SetDebugUtilsObjectNameEXT) {
result = dispatch_table->SetDebugUtilsObjectNameEXT(instance, nameInfo);
}
LoaderLogger::GetInstance().AddObjectName(nameInfo->objectHandle, nameInfo->objectType, nameInfo->objectName);
LoaderLogger::LogVerboseMessage("xrSetDebugUtilsObjectNameEXT", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrGetInstanceProcAddr(XrInstance instance, const char *name,
PFN_xrVoidFunction *function) XRLOADER_ABI_TRY {
// Initialize the function to nullptr in case it does not get caught in a known case
*function = nullptr;
if (nullptr == function) {
LoaderLogger::LogValidationErrorMessage("VUID-xrGetInstanceProcAddr-function-parameter", "xrGetInstanceProcAddr",
"Invalid Function pointer");
return XR_ERROR_VALIDATION_FAILURE;
}
if (nullptr == name) {
LoaderLogger::LogValidationErrorMessage("VUID-xrGetInstanceProcAddr-function-parameter", "xrGetInstanceProcAddr",
"Invalid Name pointer");
return XR_ERROR_VALIDATION_FAILURE;
}
LoaderInstance *loader_instance = nullptr;
if (instance == XR_NULL_HANDLE) {
// Null instance is allowed for a few specific API entry points, otherwise return error
if (strcmp(name, "xrCreateInstance") != 0 && strcmp(name, "xrEnumerateApiLayerProperties") != 0 &&
strcmp(name, "xrEnumerateInstanceExtensionProperties") != 0 && strcmp(name, "xrInitializeLoaderKHR") != 0) {
// TODO why is xrGetInstanceProcAddr not listed in here?
std::string error_str = "XR_NULL_HANDLE for instance but query for ";
error_str += name;
error_str += " requires a valid instance";
LoaderLogger::LogValidationErrorMessage("VUID-xrGetInstanceProcAddr-instance-parameter", "xrGetInstanceProcAddr",
error_str);
return XR_ERROR_HANDLE_INVALID;
}
} else {
// non null instance passed in, it should be our current instance
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetInstanceProcAddr");
if (XR_FAILED(result)) {
return result;
}
if (loader_instance->GetInstanceHandle() != instance) {
return XR_ERROR_HANDLE_INVALID;
}
}
// These functions must always go through the loader's implementation (trampoline).
if (strcmp(name, "xrGetInstanceProcAddr") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrGetInstanceProcAddr);
return XR_SUCCESS;
} else if (strcmp(name, "xrInitializeLoaderKHR") == 0) {
#ifdef XR_KHR_LOADER_INIT_SUPPORT
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrInitializeLoaderKHR);
return XR_SUCCESS;
#else
return XR_ERROR_FUNCTION_UNSUPPORTED;
#endif
} else if (strcmp(name, "xrEnumerateApiLayerProperties") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrEnumerateApiLayerProperties);
return XR_SUCCESS;
} else if (strcmp(name, "xrEnumerateInstanceExtensionProperties") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrEnumerateInstanceExtensionProperties);
return XR_SUCCESS;
} else if (strcmp(name, "xrCreateInstance") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrCreateInstance);
return XR_SUCCESS;
} else if (strcmp(name, "xrDestroyInstance") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrDestroyInstance);
return XR_SUCCESS;
}
// XR_EXT_debug_utils is built into the loader and handled partly through the xrGetInstanceProcAddress terminator,
// but the check to see if the extension is enabled must be done here where ActiveLoaderInstance is safe to use.
if (*function == nullptr) {
if (strcmp(name, "xrCreateDebugUtilsMessengerEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineCreateDebugUtilsMessengerEXT);
} else if (strcmp(name, "xrDestroyDebugUtilsMessengerEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineDestroyDebugUtilsMessengerEXT);
} else if (strcmp(name, "xrSessionBeginDebugUtilsLabelRegionEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSessionBeginDebugUtilsLabelRegionEXT);
} else if (strcmp(name, "xrSessionEndDebugUtilsLabelRegionEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSessionEndDebugUtilsLabelRegionEXT);
} else if (strcmp(name, "xrSessionInsertDebugUtilsLabelEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSessionInsertDebugUtilsLabelEXT);
} else if (strcmp(name, "xrSetDebugUtilsObjectNameEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSetDebugUtilsObjectNameEXT);
} else if (strcmp(name, "xrSubmitDebugUtilsMessageEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSubmitDebugUtilsMessageEXT);
}
if (*function != nullptr && !loader_instance->ExtensionIsEnabled("XR_EXT_debug_utils")) {
// The function matches one of the XR_EXT_debug_utils functions but the extension is not enabled.
*function = nullptr;
return XR_ERROR_FUNCTION_UNSUPPORTED;
}
}
if (*function != nullptr) {
// The loader has a trampoline or implementation of this function.
return XR_SUCCESS;
}
// If the function is not supported by the loader, call down to the next layer.
return loader_instance->GetInstanceProcAddr(name, function);
}
XRLOADER_ABI_CATCH_FALLBACK
// Exported loader functions
//
// The application might override these by exporting the same symbols and so we can't use these
// symbols anywhere in the loader code, and instead the internal non exported functions that these
// stubs call should be used internally.
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateApiLayerProperties(uint32_t propertyCapacityInput,
uint32_t *propertyCountOutput,
XrApiLayerProperties *properties) {
return LoaderXrEnumerateApiLayerProperties(propertyCapacityInput, propertyCountOutput, properties);
}
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateInstanceExtensionProperties(const char *layerName,
uint32_t propertyCapacityInput,
uint32_t *propertyCountOutput,
XrExtensionProperties *properties) {
return LoaderXrEnumerateInstanceExtensionProperties(layerName, propertyCapacityInput, propertyCountOutput, properties);
}
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateInstance(const XrInstanceCreateInfo *info, XrInstance *instance) {
return LoaderXrCreateInstance(info, instance);
}
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroyInstance(XrInstance instance) { return LoaderXrDestroyInstance(instance); }
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetInstanceProcAddr(XrInstance instance, const char *name,
PFN_xrVoidFunction *function) {
return LoaderXrGetInstanceProcAddr(instance, name, function);
}
#ifdef XR_KHR_LOADER_INIT_SUPPORT
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrInitializeLoaderKHR(const XrLoaderInitInfoBaseHeaderKHR *loaderInitInfo) {
return LoaderXrInitializeLoaderKHR(loaderInitInfo);
}
#endif

View File

@@ -0,0 +1,303 @@
// 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 Author: Mark Young <marky@lunarg.com>
//
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#include "loader_instance.hpp"
#include "api_layer_interface.hpp"
#include "hex_and_handles.h"
#include "loader_interfaces.h"
#include "loader_logger.hpp"
#include "runtime_interface.hpp"
#include "xr_generated_dispatch_table.h"
#include "xr_generated_loader.hpp"
#include <openxr/openxr.h>
#include <cstring>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
namespace {
std::unique_ptr<LoaderInstance>& GetSetCurrentLoaderInstance() {
static std::unique_ptr<LoaderInstance> current_loader_instance;
return current_loader_instance;
}
} // namespace
namespace ActiveLoaderInstance {
XrResult Set(std::unique_ptr<LoaderInstance> loader_instance, const char* log_function_name) {
if (GetSetCurrentLoaderInstance() != nullptr) {
LoaderLogger::LogErrorMessage(log_function_name, "Active XrInstance handle already exists");
return XR_ERROR_LIMIT_REACHED;
}
GetSetCurrentLoaderInstance() = std::move(loader_instance);
return XR_SUCCESS;
}
XrResult Get(LoaderInstance** loader_instance, const char* log_function_name) {
*loader_instance = GetSetCurrentLoaderInstance().get();
if (*loader_instance == nullptr) {
LoaderLogger::LogErrorMessage(log_function_name, "No active XrInstance handle.");
return XR_ERROR_HANDLE_INVALID;
}
return XR_SUCCESS;
}
bool IsAvailable() { return GetSetCurrentLoaderInstance() != nullptr; }
void Remove() { GetSetCurrentLoaderInstance().release(); }
} // namespace ActiveLoaderInstance
// Extensions that are supported by the loader, but may not be supported
// the the runtime.
const std::array<XrExtensionProperties, 1>& LoaderInstance::LoaderSpecificExtensions() {
static const std::array<XrExtensionProperties, 1> extensions = {XrExtensionProperties{
XR_TYPE_EXTENSION_PROPERTIES, nullptr, XR_EXT_DEBUG_UTILS_EXTENSION_NAME, XR_EXT_debug_utils_SPEC_VERSION}};
return extensions;
}
namespace {
class InstanceCreateInfoManager {
public:
explicit InstanceCreateInfoManager(const XrInstanceCreateInfo* info) : original_create_info(info), modified_create_info(*info) {
Reset();
}
// Reset the "modified" state to match the original state.
void Reset() {
enabled_extensions_cstr.clear();
enabled_extensions_cstr.reserve(original_create_info->enabledExtensionCount);
for (uint32_t i = 0; i < original_create_info->enabledExtensionCount; ++i) {
enabled_extensions_cstr.push_back(original_create_info->enabledExtensionNames[i]);
}
Update();
}
// Remove extensions named in the parameter and return a pointer to the current state.
const XrInstanceCreateInfo* FilterOutExtensions(const std::vector<const char*>& extensions_to_skip) {
if (enabled_extensions_cstr.empty()) {
return Get();
}
if (extensions_to_skip.empty()) {
return Get();
}
for (auto& ext : extensions_to_skip) {
FilterOutExtension(ext);
}
return Update();
}
// Remove the extension named in the parameter and return a pointer to the current state.
const XrInstanceCreateInfo* FilterOutExtension(const char* extension_to_skip) {
if (enabled_extensions_cstr.empty()) {
return &modified_create_info;
}
auto b = enabled_extensions_cstr.begin();
auto e = enabled_extensions_cstr.end();
auto it = std::find_if(b, e, [&](const char* extension) { return strcmp(extension_to_skip, extension) == 0; });
if (it != e) {
// Just that one element goes away
enabled_extensions_cstr.erase(it);
}
return Update();
}
// Get the current modified XrInstanceCreateInfo
const XrInstanceCreateInfo* Get() const { return &modified_create_info; }
private:
const XrInstanceCreateInfo* Update() {
modified_create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions_cstr.size());
modified_create_info.enabledExtensionNames = enabled_extensions_cstr.empty() ? nullptr : enabled_extensions_cstr.data();
return &modified_create_info;
}
const XrInstanceCreateInfo* original_create_info;
XrInstanceCreateInfo modified_create_info;
std::vector<const char*> enabled_extensions_cstr;
};
} // namespace
// Factory method
XrResult LoaderInstance::CreateInstance(PFN_xrGetInstanceProcAddr get_instance_proc_addr_term,
PFN_xrCreateInstance create_instance_term,
PFN_xrCreateApiLayerInstance create_api_layer_instance_term,
std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces,
const XrInstanceCreateInfo* info, std::unique_ptr<LoaderInstance>* loader_instance) {
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Entering LoaderInstance::CreateInstance");
// Check the list of enabled extensions to make sure something supports them, and, if we do,
// add it to the list of enabled extensions
XrResult last_error = XR_SUCCESS;
for (uint32_t ext = 0; ext < info->enabledExtensionCount; ++ext) {
bool found = false;
// First check the runtime
if (RuntimeInterface::GetRuntime().SupportsExtension(info->enabledExtensionNames[ext])) {
found = true;
}
// Next check the loader
if (!found) {
for (auto& loader_extension : LoaderInstance::LoaderSpecificExtensions()) {
if (strcmp(loader_extension.extensionName, info->enabledExtensionNames[ext]) == 0) {
found = true;
break;
}
}
}
// Finally, check the enabled layers
if (!found) {
for (auto& layer_interface : api_layer_interfaces) {
if (layer_interface->SupportsExtension(info->enabledExtensionNames[ext])) {
found = true;
break;
}
}
}
if (!found) {
std::string msg = "LoaderInstance::CreateInstance, no support found for requested extension: ";
msg += info->enabledExtensionNames[ext];
LoaderLogger::LogErrorMessage("xrCreateInstance", msg);
last_error = XR_ERROR_EXTENSION_NOT_PRESENT;
break;
}
}
// Topmost means "closest to the application"
PFN_xrGetInstanceProcAddr topmost_gipa = get_instance_proc_addr_term;
XrInstance instance{XR_NULL_HANDLE};
if (XR_SUCCEEDED(last_error)) {
// Remove the loader-supported-extensions (debug utils), if it's in the list of enabled extensions but not supported by
// the runtime.
InstanceCreateInfoManager create_info_manager{info};
const XrInstanceCreateInfo* modified_create_info = info;
if (info->enabledExtensionCount > 0) {
std::vector<const char*> extensions_to_skip;
for (const auto& ext : LoaderInstance::LoaderSpecificExtensions()) {
if (!RuntimeInterface::GetRuntime().SupportsExtension(ext.extensionName)) {
extensions_to_skip.emplace_back(ext.extensionName);
}
}
modified_create_info = create_info_manager.FilterOutExtensions(extensions_to_skip);
}
// Only start the xrCreateApiLayerInstance stack if we have layers.
if (!api_layer_interfaces.empty()) {
// Initialize an array of ApiLayerNextInfo structs
std::unique_ptr<XrApiLayerNextInfo[]> next_info_list(new XrApiLayerNextInfo[api_layer_interfaces.size()]);
auto ni_index = static_cast<uint32_t>(api_layer_interfaces.size() - 1);
for (uint32_t i = 0; i <= ni_index; i++) {
next_info_list[i].structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO;
next_info_list[i].structVersion = XR_API_LAYER_NEXT_INFO_STRUCT_VERSION;
next_info_list[i].structSize = sizeof(XrApiLayerNextInfo);
}
// Go through all layers, and override the instance pointers with the layer version. However,
// go backwards through the layer list so we replace in reverse order so the layers can call their next function
// appropriately.
PFN_xrCreateApiLayerInstance topmost_cali_fp = create_api_layer_instance_term;
XrApiLayerNextInfo* topmost_nextinfo = nullptr;
for (auto layer_interface = api_layer_interfaces.rbegin(); layer_interface != api_layer_interfaces.rend();
++layer_interface) {
// Collect current layer's function pointers
PFN_xrGetInstanceProcAddr cur_gipa_fp = (*layer_interface)->GetInstanceProcAddrFuncPointer();
PFN_xrCreateApiLayerInstance cur_cali_fp = (*layer_interface)->GetCreateApiLayerInstanceFuncPointer();
// Fill in layer info and link previous (lower) layer fxn pointers
strncpy(next_info_list[ni_index].layerName, (*layer_interface)->LayerName().c_str(),
XR_MAX_API_LAYER_NAME_SIZE - 1);
next_info_list[ni_index].layerName[XR_MAX_API_LAYER_NAME_SIZE - 1] = '\0';
next_info_list[ni_index].next = topmost_nextinfo;
next_info_list[ni_index].nextGetInstanceProcAddr = topmost_gipa;
next_info_list[ni_index].nextCreateApiLayerInstance = topmost_cali_fp;
// Update saved pointers for next iteration
topmost_nextinfo = &next_info_list[ni_index];
topmost_gipa = cur_gipa_fp;
topmost_cali_fp = cur_cali_fp;
ni_index--;
}
// Populate the ApiLayerCreateInfo struct and pass to topmost CreateApiLayerInstance()
XrApiLayerCreateInfo api_layer_ci = {};
api_layer_ci.structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO;
api_layer_ci.structVersion = XR_API_LAYER_CREATE_INFO_STRUCT_VERSION;
api_layer_ci.structSize = sizeof(XrApiLayerCreateInfo);
api_layer_ci.loaderInstance = nullptr; // Not used.
api_layer_ci.settings_file_location[0] = '\0';
api_layer_ci.nextInfo = next_info_list.get();
//! @todo do we filter our create info extension list here?
//! Think that actually each layer might need to filter...
last_error = topmost_cali_fp(modified_create_info, &api_layer_ci, &instance);
} else {
// The loader's terminator is the topmost CreateInstance if there are no layers.
last_error = create_instance_term(modified_create_info, &instance);
}
if (XR_FAILED(last_error)) {
LoaderLogger::LogErrorMessage("xrCreateInstance", "LoaderInstance::CreateInstance chained CreateInstance call failed");
}
}
if (XR_SUCCEEDED(last_error)) {
loader_instance->reset(new LoaderInstance(instance, info, topmost_gipa, std::move(api_layer_interfaces)));
std::ostringstream oss;
oss << "LoaderInstance::CreateInstance succeeded with ";
oss << (*loader_instance)->LayerInterfaces().size();
oss << " layers enabled and runtime interface - created instance = ";
oss << HandleToHexString((*loader_instance)->GetInstanceHandle());
LoaderLogger::LogInfoMessage("xrCreateInstance", oss.str());
}
return last_error;
}
XrResult LoaderInstance::GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function) {
return _topmost_gipa(_runtime_instance, name, function);
}
LoaderInstance::LoaderInstance(XrInstance instance, const XrInstanceCreateInfo* create_info, PFN_xrGetInstanceProcAddr topmost_gipa,
std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces)
: _runtime_instance(instance),
_topmost_gipa(topmost_gipa),
_api_layer_interfaces(std::move(api_layer_interfaces)),
_dispatch_table(new XrGeneratedDispatchTable{}) {
for (uint32_t ext = 0; ext < create_info->enabledExtensionCount; ++ext) {
_enabled_extensions.push_back(create_info->enabledExtensionNames[ext]);
}
GeneratedXrPopulateDispatchTable(_dispatch_table.get(), instance, topmost_gipa);
}
LoaderInstance::~LoaderInstance() {
std::ostringstream oss;
oss << "Destroying LoaderInstance = ";
oss << PointerToHexString(this);
LoaderLogger::LogInfoMessage("xrDestroyInstance", oss.str());
}
bool LoaderInstance::ExtensionIsEnabled(const std::string& extension) {
for (std::string& cur_enabled : _enabled_extensions) {
if (cur_enabled == extension) {
return true;
}
}
return false;
}

View File

@@ -0,0 +1,77 @@
// 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 Author: Mark Young <marky@lunarg.com>
//
#pragma once
#include "extra_algorithms.h"
#include "loader_interfaces.h"
#include <openxr/openxr.h>
#include <array>
#include <cmath>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
class ApiLayerInterface;
struct XrGeneratedDispatchTable;
class LoaderInstance;
// Manage the single loader instance that is available.
namespace ActiveLoaderInstance {
// Set the active loader instance. This will fail if there is already an active loader instance.
XrResult Set(std::unique_ptr<LoaderInstance> loader_instance, const char* log_function_name);
// Returns true if there is an active loader instance.
bool IsAvailable();
// Get the active LoaderInstance.
XrResult Get(LoaderInstance** loader_instance, const char* log_function_name);
// Destroy the currently active LoaderInstance if there is one. This will make the loader able to create a new XrInstance if needed.
void Remove();
}; // namespace ActiveLoaderInstance
// Manages information needed by the loader for an XrInstance, such as what extensions are available and the dispatch table.
class LoaderInstance {
public:
// Factory method
static XrResult CreateInstance(PFN_xrGetInstanceProcAddr get_instance_proc_addr_term, PFN_xrCreateInstance create_instance_term,
PFN_xrCreateApiLayerInstance create_api_layer_instance_term,
std::vector<std::unique_ptr<ApiLayerInterface>> layer_interfaces,
const XrInstanceCreateInfo* createInfo, std::unique_ptr<LoaderInstance>* loader_instance);
static const std::array<XrExtensionProperties, 1>& LoaderSpecificExtensions();
virtual ~LoaderInstance();
XrInstance GetInstanceHandle() { return _runtime_instance; }
const std::unique_ptr<XrGeneratedDispatchTable>& DispatchTable() { return _dispatch_table; }
std::vector<std::unique_ptr<ApiLayerInterface>>& LayerInterfaces() { return _api_layer_interfaces; }
bool ExtensionIsEnabled(const std::string& extension);
XrDebugUtilsMessengerEXT DefaultDebugUtilsMessenger() { return _messenger; }
void SetDefaultDebugUtilsMessenger(XrDebugUtilsMessengerEXT messenger) { _messenger = messenger; }
XrResult GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function);
private:
LoaderInstance(XrInstance instance, const XrInstanceCreateInfo* createInfo, PFN_xrGetInstanceProcAddr topmost_gipa,
std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces);
private:
XrInstance _runtime_instance{XR_NULL_HANDLE};
PFN_xrGetInstanceProcAddr _topmost_gipa{nullptr};
std::vector<std::string> _enabled_extensions;
std::vector<std::unique_ptr<ApiLayerInterface>> _api_layer_interfaces;
std::unique_ptr<XrGeneratedDispatchTable> _dispatch_table;
// Internal debug messenger created during xrCreateInstance
XrDebugUtilsMessengerEXT _messenger{XR_NULL_HANDLE};
};

View File

@@ -0,0 +1,239 @@
// 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 Author: Mark Young <marky@lunarg.com>
//
#include "loader_logger.hpp"
#include "extra_algorithms.h"
#include "hex_and_handles.h"
#include "loader_logger_recorders.hpp"
#include "platform_utils.hpp"
#include <openxr/openxr.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
bool LoaderLogRecorder::LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT /*message_severity*/,
XrDebugUtilsMessageTypeFlagsEXT /*message_type*/,
const XrDebugUtilsMessengerCallbackDataEXT* /*callback_data*/) {
return false;
}
// Utility functions for converting to/from XR_EXT_debug_utils values
XrLoaderLogMessageSeverityFlags DebugUtilsSeveritiesToLoaderLogMessageSeverities(
XrDebugUtilsMessageSeverityFlagsEXT utils_severities) {
XrLoaderLogMessageSeverityFlags log_severities = 0UL;
if ((utils_severities & XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) != 0u) {
log_severities |= XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT;
}
if ((utils_severities & XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) != 0u) {
log_severities |= XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT;
}
if ((utils_severities & XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) != 0u) {
log_severities |= XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT;
}
if ((utils_severities & XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0u) {
log_severities |= XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT;
}
return log_severities;
}
XrDebugUtilsMessageSeverityFlagsEXT LoaderLogMessageSeveritiesToDebugUtilsMessageSeverities(
XrLoaderLogMessageSeverityFlags log_severities) {
XrDebugUtilsMessageSeverityFlagsEXT utils_severities = 0UL;
if ((log_severities & XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT) != 0u) {
utils_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
}
if ((log_severities & XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT) != 0u) {
utils_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
}
if ((log_severities & XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT) != 0u) {
utils_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
}
if ((log_severities & XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT) != 0u) {
utils_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
}
return utils_severities;
}
XrLoaderLogMessageTypeFlagBits DebugUtilsMessageTypesToLoaderLogMessageTypes(XrDebugUtilsMessageTypeFlagsEXT utils_types) {
XrLoaderLogMessageTypeFlagBits log_types = 0UL;
if ((utils_types & XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) != 0u) {
log_types |= XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT;
}
if ((utils_types & XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) != 0u) {
log_types |= XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT;
}
if ((utils_types & XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) != 0u) {
log_types |= XR_LOADER_LOG_MESSAGE_TYPE_PERFORMANCE_BIT;
}
return log_types;
}
XrDebugUtilsMessageTypeFlagsEXT LoaderLogMessageTypesToDebugUtilsMessageTypes(XrLoaderLogMessageTypeFlagBits log_types) {
XrDebugUtilsMessageTypeFlagsEXT utils_types = 0UL;
if ((log_types & XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT) != 0u) {
utils_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
}
if ((log_types & XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT) != 0u) {
utils_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
}
if ((log_types & XR_LOADER_LOG_MESSAGE_TYPE_PERFORMANCE_BIT) != 0u) {
utils_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
}
return utils_types;
}
LoaderLogger::LoaderLogger() {
std::string debug_string = PlatformUtilsGetEnv("XR_LOADER_DEBUG");
// Add an error logger by default so that we at least get errors out to std::cerr.
// Normally we enable stderr output. But if the XR_LOADER_DEBUG environment variable is
// present as "none" then we don't.
if (debug_string != "none") {
AddLogRecorder(MakeStdErrLoaderLogRecorder(nullptr));
#ifdef __ANDROID__
// Add a logcat logger by default.
AddLogRecorder(MakeLogcatLoaderLogRecorder());
#endif // __ANDROID__
}
#ifdef _WIN32
// Add an debugger logger by default so that we at least get errors out to the debugger.
AddLogRecorder(MakeDebuggerLoaderLogRecorder(nullptr));
#endif
// If the environment variable to enable loader debugging is set, then enable the
// appropriate logging out to std::cout.
if (!debug_string.empty()) {
XrLoaderLogMessageSeverityFlags debug_flags = {};
if (debug_string == "error") {
debug_flags = XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT;
} else if (debug_string == "warn") {
debug_flags = XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT;
} else if (debug_string == "info") {
debug_flags = XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT |
XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT;
} else if (debug_string == "all" || debug_string == "verbose") {
debug_flags = XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT |
XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT;
}
AddLogRecorder(MakeStdOutLoaderLogRecorder(nullptr, debug_flags));
}
}
void LoaderLogger::AddLogRecorder(std::unique_ptr<LoaderLogRecorder>&& recorder) {
std::unique_lock<std::shared_timed_mutex> lock(_mutex);
_recorders.push_back(std::move(recorder));
}
void LoaderLogger::AddLogRecorderForXrInstance(XrInstance instance, std::unique_ptr<LoaderLogRecorder>&& recorder) {
std::unique_lock<std::shared_timed_mutex> lock(_mutex);
_recordersByInstance[instance].insert(recorder->UniqueId());
_recorders.emplace_back(std::move(recorder));
}
void LoaderLogger::RemoveLogRecorder(uint64_t unique_id) {
std::unique_lock<std::shared_timed_mutex> lock(_mutex);
vector_remove_if_and_erase(
_recorders, [=](std::unique_ptr<LoaderLogRecorder> const& recorder) { return recorder->UniqueId() == unique_id; });
for (auto& recorders : _recordersByInstance) {
auto& messengersForInstance = recorders.second;
if (messengersForInstance.count(unique_id) > 0) {
messengersForInstance.erase(unique_id);
}
}
}
void LoaderLogger::RemoveLogRecordersForXrInstance(XrInstance instance) {
std::unique_lock<std::shared_timed_mutex> lock(_mutex);
if (_recordersByInstance.find(instance) != _recordersByInstance.end()) {
auto recorders = _recordersByInstance[instance];
vector_remove_if_and_erase(_recorders, [=](std::unique_ptr<LoaderLogRecorder> const& recorder) {
return recorders.find(recorder->UniqueId()) != recorders.end();
});
_recordersByInstance.erase(instance);
}
}
bool LoaderLogger::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
const std::string& message_id, const std::string& command_name, const std::string& message,
const std::vector<XrSdkLogObjectInfo>& objects) {
XrLoaderLogMessengerCallbackData callback_data = {};
callback_data.message_id = message_id.c_str();
callback_data.command_name = command_name.c_str();
callback_data.message = message.c_str();
auto names_and_labels = data_.PopulateNamesAndLabels(objects);
callback_data.objects = names_and_labels.sdk_objects.empty() ? nullptr : names_and_labels.sdk_objects.data();
callback_data.object_count = static_cast<uint8_t>(names_and_labels.objects.size());
callback_data.session_labels = names_and_labels.labels.empty() ? nullptr : names_and_labels.labels.data();
callback_data.session_labels_count = static_cast<uint8_t>(names_and_labels.labels.size());
std::shared_lock<std::shared_timed_mutex> lock(_mutex);
bool exit_app = false;
for (std::unique_ptr<LoaderLogRecorder>& recorder : _recorders) {
if ((recorder->MessageSeverities() & message_severity) == message_severity &&
(recorder->MessageTypes() & message_type) == message_type) {
exit_app |= recorder->LogMessage(message_severity, message_type, &callback_data);
}
}
return exit_app;
}
// Extension-specific logging functions
bool LoaderLogger::LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity,
XrDebugUtilsMessageTypeFlagsEXT message_type,
const XrDebugUtilsMessengerCallbackDataEXT* callback_data) {
bool exit_app = false;
XrLoaderLogMessageSeverityFlags log_message_severity = DebugUtilsSeveritiesToLoaderLogMessageSeverities(message_severity);
XrLoaderLogMessageTypeFlags log_message_type = DebugUtilsMessageTypesToLoaderLogMessageTypes(message_type);
AugmentedCallbackData augmented_data;
data_.WrapCallbackData(&augmented_data, callback_data);
// Loop through the recorders
std::shared_lock<std::shared_timed_mutex> lock(_mutex);
for (std::unique_ptr<LoaderLogRecorder>& recorder : _recorders) {
// Only send the message if it's a debug utils recorder and of the type the recorder cares about.
if (recorder->Type() != XR_LOADER_LOG_DEBUG_UTILS ||
(recorder->MessageSeverities() & log_message_severity) != log_message_severity ||
(recorder->MessageTypes() & log_message_type) != log_message_type) {
continue;
}
exit_app |= recorder->LogDebugUtilsMessage(message_severity, message_type, augmented_data.exported_data);
}
return exit_app;
}
void LoaderLogger::AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name) {
data_.AddObjectName(object_handle, object_type, object_name);
}
void LoaderLogger::BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT* label_info) {
data_.BeginLabelRegion(session, *label_info);
}
void LoaderLogger::EndLabelRegion(XrSession session) { data_.EndLabelRegion(session); }
void LoaderLogger::InsertLabel(XrSession session, const XrDebugUtilsLabelEXT* label_info) {
data_.InsertLabel(session, *label_info);
}
void LoaderLogger::DeleteSessionLabels(XrSession session) { data_.DeleteSessionLabels(session); }

View File

@@ -0,0 +1,194 @@
// 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 Author: Mark Young <marky@lunarg.com>
//
#pragma once
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <set>
#include <map>
#include <shared_mutex>
#include <openxr/openxr.h>
#include "hex_and_handles.h"
#include "object_info.h"
// Use internal versions of flags similar to XR_EXT_debug_utils so that
// we're not tightly coupled to that extension. This way, if the extension
// changes or gets replaced, we can be flexible in the loader.
#define XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT 0x00000001
#define XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT 0x00000010
#define XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT 0x00000100
#define XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT 0x00001000
#define XR_LOADER_LOG_MESSAGE_SEVERITY_DEFAULT_BITS 0x00000000
typedef XrFlags64 XrLoaderLogMessageSeverityFlagBits;
typedef XrFlags64 XrLoaderLogMessageSeverityFlags;
#define XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT 0x00000001
#define XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT 0x00000002
#define XR_LOADER_LOG_MESSAGE_TYPE_PERFORMANCE_BIT 0x00000004
#define XR_LOADER_LOG_MESSAGE_TYPE_DEFAULT_BITS 0xffffffff
typedef XrFlags64 XrLoaderLogMessageTypeFlagBits;
typedef XrFlags64 XrLoaderLogMessageTypeFlags;
struct XrLoaderLogMessengerCallbackData {
const char* message_id;
const char* command_name;
const char* message;
uint8_t object_count;
XrSdkLogObjectInfo* objects;
uint8_t session_labels_count;
XrDebugUtilsLabelEXT* session_labels;
};
enum XrLoaderLogType {
XR_LOADER_LOG_UNKNOWN = 0,
XR_LOADER_LOG_STDERR,
XR_LOADER_LOG_STDOUT,
XR_LOADER_LOG_DEBUG_UTILS,
XR_LOADER_LOG_DEBUGGER,
XR_LOADER_LOG_LOGCAT,
};
class LoaderLogRecorder {
public:
LoaderLogRecorder(XrLoaderLogType type, void* user_data, XrLoaderLogMessageSeverityFlags message_severities,
XrLoaderLogMessageTypeFlags message_types) {
_active = false;
_user_data = user_data;
_type = type;
_unique_id = 0;
_message_severities = message_severities;
_message_types = message_types;
}
virtual ~LoaderLogRecorder() = default;
XrLoaderLogType Type() { return _type; }
uint64_t UniqueId() { return _unique_id; }
XrLoaderLogMessageSeverityFlags MessageSeverities() { return _message_severities; }
XrLoaderLogMessageTypeFlags MessageTypes() { return _message_types; }
virtual void Start() { _active = true; }
bool IsPaused() { return _active; }
virtual void Pause() { _active = false; }
virtual void Resume() { _active = true; }
virtual void Stop() { _active = false; }
virtual bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
const XrLoaderLogMessengerCallbackData* callback_data) = 0;
// Extension-specific logging functions - defaults to do nothing.
virtual bool LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity,
XrDebugUtilsMessageTypeFlagsEXT message_type,
const XrDebugUtilsMessengerCallbackDataEXT* callback_data);
protected:
bool _active;
XrLoaderLogType _type;
uint64_t _unique_id;
void* _user_data;
XrLoaderLogMessageSeverityFlags _message_severities;
XrLoaderLogMessageTypeFlags _message_types;
};
class LoaderLogger {
public:
static LoaderLogger& GetInstance() {
static LoaderLogger instance;
return instance;
}
void AddLogRecorder(std::unique_ptr<LoaderLogRecorder>&& recorder);
void RemoveLogRecorder(uint64_t unique_id);
void AddLogRecorderForXrInstance(XrInstance instance, std::unique_ptr<LoaderLogRecorder>&& recorder);
void RemoveLogRecordersForXrInstance(XrInstance instance);
//! Called from LoaderXrTermSetDebugUtilsObjectNameEXT - an empty name means remove
void AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name);
void BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT* label_info);
void EndLabelRegion(XrSession session);
void InsertLabel(XrSession session, const XrDebugUtilsLabelEXT* label_info);
void DeleteSessionLabels(XrSession session);
bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
const std::string& message_id, const std::string& command_name, const std::string& message,
const std::vector<XrSdkLogObjectInfo>& objects = {});
static bool LogErrorMessage(const std::string& command_name, const std::string& message,
const std::vector<XrSdkLogObjectInfo>& objects = {}) {
return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
"OpenXR-Loader", command_name, message, objects);
}
static bool LogWarningMessage(const std::string& command_name, const std::string& message,
const std::vector<XrSdkLogObjectInfo>& objects = {}) {
return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
"OpenXR-Loader", command_name, message, objects);
}
static bool LogInfoMessage(const std::string& command_name, const std::string& message,
const std::vector<XrSdkLogObjectInfo>& objects = {}) {
return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
"OpenXR-Loader", command_name, message, objects);
}
static bool LogVerboseMessage(const std::string& command_name, const std::string& message,
const std::vector<XrSdkLogObjectInfo>& objects = {}) {
return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
"OpenXR-Loader", command_name, message, objects);
}
static bool LogValidationErrorMessage(const std::string& vuid, const std::string& command_name, const std::string& message,
const std::vector<XrSdkLogObjectInfo>& objects = {}) {
return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT, XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT,
vuid, command_name, message, objects);
}
static bool LogValidationWarningMessage(const std::string& vuid, const std::string& command_name, const std::string& message,
const std::vector<XrSdkLogObjectInfo>& objects = {}) {
return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT, XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT,
vuid, command_name, message, objects);
}
// Extension-specific logging functions
bool LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity, XrDebugUtilsMessageTypeFlagsEXT message_type,
const XrDebugUtilsMessengerCallbackDataEXT* callback_data);
// Non-copyable
LoaderLogger(const LoaderLogger&) = delete;
LoaderLogger& operator=(const LoaderLogger&) = delete;
private:
LoaderLogger();
std::shared_timed_mutex _mutex;
// List of *all* available recorder objects (including created specifically for an Instance)
std::vector<std::unique_ptr<LoaderLogRecorder>> _recorders;
// List of recorder objects only created specifically for an XrInstance
std::unordered_map<XrInstance, std::unordered_set<uint64_t>> _recordersByInstance;
DebugUtilsData data_;
};
// Utility functions for converting to/from XR_EXT_debug_utils values
XrLoaderLogMessageSeverityFlags DebugUtilsSeveritiesToLoaderLogMessageSeverities(
XrDebugUtilsMessageSeverityFlagsEXT utils_severities);
XrDebugUtilsMessageSeverityFlagsEXT LoaderLogMessageSeveritiesToDebugUtilsMessageSeverities(
XrLoaderLogMessageSeverityFlags log_severities);
XrLoaderLogMessageTypeFlagBits DebugUtilsMessageTypesToLoaderLogMessageTypes(XrDebugUtilsMessageTypeFlagsEXT utils_types);
XrDebugUtilsMessageTypeFlagsEXT LoaderLogMessageTypesToDebugUtilsMessageTypes(XrLoaderLogMessageTypeFlagBits log_types);

View File

@@ -0,0 +1,291 @@
// 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 Author: Mark Young <marky@lunarg.com>
//
#include "loader_logger_recorders.hpp"
#include "hex_and_handles.h"
#include "loader_logger.hpp"
#include <openxr/openxr.h>
#include <memory>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#ifdef __ANDROID__
#include "android/log.h"
#endif
#ifdef _WIN32
#include <windows.h>
#endif
// Anonymous namespace to keep these types private
namespace {
void OutputMessageToStream(std::ostream& os, XrLoaderLogMessageSeverityFlagBits message_severity,
XrLoaderLogMessageTypeFlags message_type, const XrLoaderLogMessengerCallbackData* callback_data) {
if (XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT > message_severity) {
os << "Verbose [";
} else if (XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT > message_severity) {
os << "Info [";
} else if (XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT > message_severity) {
os << "Warning [";
} else {
os << "Error [";
}
switch (message_type) {
case XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT:
os << "GENERAL";
break;
case XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT:
os << "SPEC";
break;
case XR_LOADER_LOG_MESSAGE_TYPE_PERFORMANCE_BIT:
os << "PERF";
break;
default:
os << "UNKNOWN";
break;
}
os << " | " << callback_data->command_name << " | " << callback_data->message_id << "] : " << callback_data->message
<< std::endl;
for (uint32_t obj = 0; obj < callback_data->object_count; ++obj) {
os << " Object[" << obj << "] = " << callback_data->objects[obj].ToString();
os << std::endl;
}
for (uint32_t label = 0; label < callback_data->session_labels_count; ++label) {
os << " SessionLabel[" << std::to_string(label) << "] = " << callback_data->session_labels[label].labelName;
os << std::endl;
}
}
// With std::cerr: Standard Error logger, always on for now
// With std::cout: Standard Output logger used with XR_LOADER_DEBUG
class OstreamLoaderLogRecorder : public LoaderLogRecorder {
public:
OstreamLoaderLogRecorder(std::ostream& os, void* user_data, XrLoaderLogMessageSeverityFlags flags);
bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
const XrLoaderLogMessengerCallbackData* callback_data) override;
private:
std::ostream& os_;
};
// Debug Utils logger used with XR_EXT_debug_utils
class DebugUtilsLogRecorder : public LoaderLogRecorder {
public:
DebugUtilsLogRecorder(const XrDebugUtilsMessengerCreateInfoEXT* create_info, XrDebugUtilsMessengerEXT debug_messenger);
bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
const XrLoaderLogMessengerCallbackData* callback_data) override;
// Extension-specific logging functions
bool LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity, XrDebugUtilsMessageTypeFlagsEXT message_type,
const XrDebugUtilsMessengerCallbackDataEXT* callback_data) override;
private:
PFN_xrDebugUtilsMessengerCallbackEXT _user_callback;
};
#ifdef __ANDROID__
class LogcatLoaderLogRecorder : public LoaderLogRecorder {
public:
LogcatLoaderLogRecorder();
bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
const XrLoaderLogMessengerCallbackData* callback_data) override;
};
#endif
#ifdef _WIN32
// Output to debugger
class DebuggerLoaderLogRecorder : public LoaderLogRecorder {
public:
DebuggerLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags);
bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
const XrLoaderLogMessengerCallbackData* callback_data) override;
};
#endif
// Unified stdout/stderr logger
OstreamLoaderLogRecorder::OstreamLoaderLogRecorder(std::ostream& os, void* user_data, XrLoaderLogMessageSeverityFlags flags)
: LoaderLogRecorder(XR_LOADER_LOG_STDOUT, user_data, flags, 0xFFFFFFFFUL), os_(os) {
// Automatically start
Start();
}
bool OstreamLoaderLogRecorder::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity,
XrLoaderLogMessageTypeFlags message_type,
const XrLoaderLogMessengerCallbackData* callback_data) {
if (_active && 0 != (_message_severities & message_severity) && 0 != (_message_types & message_type)) {
OutputMessageToStream(os_, message_severity, message_type, callback_data);
}
// Return of "true" means that we should exit the application after the logged message. We
// don't want to do that for our internal logging. Only let a user return true.
return false;
}
// A logger associated with the XR_EXT_debug_utils extension
DebugUtilsLogRecorder::DebugUtilsLogRecorder(const XrDebugUtilsMessengerCreateInfoEXT* create_info,
XrDebugUtilsMessengerEXT debug_messenger)
: LoaderLogRecorder(XR_LOADER_LOG_DEBUG_UTILS, static_cast<void*>(create_info->userData),
DebugUtilsSeveritiesToLoaderLogMessageSeverities(create_info->messageSeverities),
DebugUtilsMessageTypesToLoaderLogMessageTypes(create_info->messageTypes)),
_user_callback(create_info->userCallback) {
// Use the debug messenger value to uniquely identify this logger with that messenger
_unique_id = MakeHandleGeneric(debug_messenger);
Start();
}
// Extension-specific logging functions
bool DebugUtilsLogRecorder::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity,
XrLoaderLogMessageTypeFlags message_type,
const XrLoaderLogMessengerCallbackData* callback_data) {
bool should_exit = false;
if (_active && 0 != (_message_severities & message_severity) && 0 != (_message_types & message_type)) {
XrDebugUtilsMessageSeverityFlagsEXT utils_severity = DebugUtilsSeveritiesToLoaderLogMessageSeverities(message_severity);
XrDebugUtilsMessageTypeFlagsEXT utils_type = LoaderLogMessageTypesToDebugUtilsMessageTypes(message_type);
// Convert the loader log message into the debug utils log message information
XrDebugUtilsMessengerCallbackDataEXT utils_callback_data = {};
utils_callback_data.type = XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT;
utils_callback_data.messageId = callback_data->message_id;
utils_callback_data.functionName = callback_data->command_name;
utils_callback_data.message = callback_data->message;
std::vector<XrDebugUtilsObjectNameInfoEXT> utils_objects;
utils_objects.resize(callback_data->object_count);
for (uint8_t object = 0; object < callback_data->object_count; ++object) {
utils_objects[object].type = XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
utils_objects[object].next = nullptr;
utils_objects[object].objectHandle = callback_data->objects[object].handle;
utils_objects[object].objectType = callback_data->objects[object].type;
utils_objects[object].objectName = callback_data->objects[object].name.c_str();
}
utils_callback_data.objectCount = callback_data->object_count;
utils_callback_data.objects = utils_objects.data();
utils_callback_data.sessionLabelCount = callback_data->session_labels_count;
utils_callback_data.sessionLabels = callback_data->session_labels;
// Call the user callback with the appropriate info
// Return of "true" means that we should exit the application after the logged message.
should_exit = (_user_callback(utils_severity, utils_type, &utils_callback_data, _user_data) == XR_TRUE);
}
return should_exit;
}
bool DebugUtilsLogRecorder::LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity,
XrDebugUtilsMessageTypeFlagsEXT message_type,
const XrDebugUtilsMessengerCallbackDataEXT* callback_data) {
// Call the user callback with the appropriate info
// Return of "true" means that we should exit the application after the logged message.
return (_user_callback(message_severity, message_type, callback_data, _user_data) == XR_TRUE);
}
#ifdef __ANDROID__
static inline android_LogPriority LoaderToAndroidLogPriority(XrLoaderLogMessageSeverityFlags message_severity) {
if (0 != (message_severity & XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT)) {
return ANDROID_LOG_ERROR;
}
if (0 != (message_severity & XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT)) {
return ANDROID_LOG_WARN;
}
if (0 != (message_severity & XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT)) {
return ANDROID_LOG_INFO;
}
return ANDROID_LOG_VERBOSE;
}
LogcatLoaderLogRecorder::LogcatLoaderLogRecorder()
: LoaderLogRecorder(XR_LOADER_LOG_LOGCAT, nullptr,
XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT |
XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT,
0xFFFFFFFFUL) {
// Automatically start
Start();
}
bool LogcatLoaderLogRecorder::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity,
XrLoaderLogMessageTypeFlags message_type,
const XrLoaderLogMessengerCallbackData* callback_data) {
if (_active && 0 != (_message_severities & message_severity) && 0 != (_message_types & message_type)) {
std::stringstream ss;
OutputMessageToStream(ss, message_severity, message_type, callback_data);
__android_log_write(LoaderToAndroidLogPriority(message_severity), "OpenXR-Loader", ss.str().c_str());
}
// Return of "true" means that we should exit the application after the logged message. We
// don't want to do that for our internal logging. Only let a user return true.
return false;
}
#endif // __ANDROID__
#ifdef _WIN32
// Unified stdout/stderr logger
DebuggerLoaderLogRecorder::DebuggerLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags)
: LoaderLogRecorder(XR_LOADER_LOG_DEBUGGER, user_data, flags, 0xFFFFFFFFUL) {
// Automatically start
Start();
}
bool DebuggerLoaderLogRecorder::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity,
XrLoaderLogMessageTypeFlags message_type,
const XrLoaderLogMessengerCallbackData* callback_data) {
if (_active && 0 != (_message_severities & message_severity) && 0 != (_message_types & message_type)) {
std::stringstream ss;
OutputMessageToStream(ss, message_severity, message_type, callback_data);
OutputDebugStringA(ss.str().c_str());
}
// Return of "true" means that we should exit the application after the logged message. We
// don't want to do that for our internal logging. Only let a user return true.
return false;
}
#endif
} // namespace
std::unique_ptr<LoaderLogRecorder> MakeStdOutLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags) {
std::unique_ptr<LoaderLogRecorder> recorder(new OstreamLoaderLogRecorder(std::cout, user_data, flags));
return recorder;
}
std::unique_ptr<LoaderLogRecorder> MakeStdErrLoaderLogRecorder(void* user_data) {
std::unique_ptr<LoaderLogRecorder> recorder(
new OstreamLoaderLogRecorder(std::cerr, user_data, XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT));
return recorder;
}
std::unique_ptr<LoaderLogRecorder> MakeDebugUtilsLoaderLogRecorder(const XrDebugUtilsMessengerCreateInfoEXT* create_info,
XrDebugUtilsMessengerEXT debug_messenger) {
std::unique_ptr<LoaderLogRecorder> recorder(new DebugUtilsLogRecorder(create_info, debug_messenger));
return recorder;
}
#ifdef __ANDROID__
std::unique_ptr<LoaderLogRecorder> MakeLogcatLoaderLogRecorder() {
std::unique_ptr<LoaderLogRecorder> recorder(new LogcatLoaderLogRecorder());
return recorder;
}
#endif
#ifdef _WIN32
std::unique_ptr<LoaderLogRecorder> MakeDebuggerLoaderLogRecorder(void* user_data) {
std::unique_ptr<LoaderLogRecorder> recorder(new DebuggerLoaderLogRecorder(user_data, XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT));
return recorder;
}
#endif

View File

@@ -0,0 +1,40 @@
// 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 Author: Ryan Pavlik <ryan.pavlik@collabora.com>
//
#pragma once
#include "loader_logger.hpp"
#include <openxr/openxr.h>
#include <memory>
//! Standard Error logger, on by default. Disabled with environment variable XR_LOADER_DEBUG = "none".
std::unique_ptr<LoaderLogRecorder> MakeStdErrLoaderLogRecorder(void* user_data);
//! Standard Output logger used with XR_LOADER_DEBUG environment variable.
std::unique_ptr<LoaderLogRecorder> MakeStdOutLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags);
#ifdef __ANDROID__
//! Android liblog ("logcat") logger
std::unique_ptr<LoaderLogRecorder> MakeLogcatLoaderLogRecorder();
#endif
// Debug Utils logger used with XR_EXT_debug_utils
std::unique_ptr<LoaderLogRecorder> MakeDebugUtilsLoaderLogRecorder(const XrDebugUtilsMessengerCreateInfoEXT* create_info,
XrDebugUtilsMessengerEXT debug_messenger);
#ifdef _WIN32
//! Win32 debugger output
std::unique_ptr<LoaderLogRecorder> MakeDebuggerLoaderLogRecorder(void* user_data);
#endif
// TODO: Add other Derived classes:
// - FileLoaderLogRecorder - During/after xrCreateInstance
// - PipeLoaderLogRecorder? - During/after xrCreateInstance

View File

@@ -0,0 +1,204 @@
// 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 <cassert>
#include <sstream>
#include <string>
#include "xr_dependencies.h"
#include "platform_utils.hpp"
#if defined(__GNUC__) && __GNUC__ >= 4
#define LOADER_EXPORT __attribute__((visibility("default")))
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
#define LOADER_EXPORT __attribute__((visibility("default")))
#else
#define LOADER_EXPORT
#endif
// Environment variables
#if defined(XR_OS_LINUX) || defined(XR_OS_APPLE) || defined(XR_OS_ANDROID)
#include <sys/types.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#define PATH_SEPARATOR ':'
#define DIRECTORY_SYMBOL '/'
// Dynamic Loading of libraries:
typedef void *LoaderPlatformLibraryHandle;
static inline LoaderPlatformLibraryHandle LoaderPlatformLibraryOpen(const std::string &path) {
// When loading the library, we use RTLD_LAZY so that not all symbols have to be
// resolved at this time (which improves performance). Note that if not all symbols
// can be resolved, this could cause crashes later.
// For experimenting/debugging: Define the LD_BIND_NOW environment variable to force all
// symbols to be resolved here.
return dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
}
static inline const char *LoaderPlatformLibraryOpenError(const std::string &path) {
(void)path;
return dlerror();
}
static inline void LoaderPlatformLibraryClose(LoaderPlatformLibraryHandle library) { dlclose(library); }
static inline void *LoaderPlatformLibraryGetProcAddr(LoaderPlatformLibraryHandle library, const std::string &name) {
assert(library);
assert(!name.empty());
return dlsym(library, name.c_str());
}
static inline const char *LoaderPlatformLibraryGetProcAddrError(const std::string &name) {
(void)name;
return dlerror();
}
#elif defined(XR_OS_WINDOWS)
#define PATH_SEPARATOR ';'
#define DIRECTORY_SYMBOL '\\'
// Workaround for MS VS 2010/2013 missing snprintf and vsnprintf
#if defined(_MSC_VER) && _MSC_VER < 1900
#include <stdint.h>
static inline int32_t xr_vsnprintf(char *result_buffer, size_t buffer_size, const char *print_format, va_list varying_list) {
int32_t copy_count = -1;
if (buffer_size != 0) {
copy_count = _vsnprintf_s(result_buffer, buffer_size, _TRUNCATE, print_format, varying_list);
}
if (copy_count == -1) {
copy_count = _vscprintf(print_format, varying_list);
}
return copy_count;
}
static inline int32_t xr_snprintf(char *result_buffer, size_t buffer_size, const char *print_format, ...) {
va_list varying_list;
va_start(varying_list, print_format);
int32_t copy_count = xr_vsnprintf(result_buffer, buffer_size, print_format, varying_list);
va_end(varying_list);
return copy_count;
}
#define snprintf xr_snprintf
#define vsnprintf xr_vsnprintf
#endif
static inline std::string DescribeError(uint32_t code, bool prefixErrorCode = true) {
std::string str;
if (prefixErrorCode) {
char prefixBuffer[64];
snprintf(prefixBuffer, sizeof(prefixBuffer), "0x%llx (%lld): ", (uint64_t)code, (int64_t)code);
str = prefixBuffer;
}
// Could use FORMAT_MESSAGE_FROM_HMODULE to specify an error source.
WCHAR errorBufferW[1024]{};
const DWORD errorBufferWCapacity = sizeof(errorBufferW) / sizeof(errorBufferW[0]);
const DWORD length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, (DWORD)code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorBufferW, errorBufferWCapacity, nullptr);
if (length) { // If errorBufferW contains what we are looking for...
str += wide_to_utf8(errorBufferW);
} else {
str = "(unknown)";
}
return str;
}
// Dynamic Loading:
typedef HMODULE LoaderPlatformLibraryHandle;
static inline LoaderPlatformLibraryHandle LoaderPlatformLibraryOpen(const std::string &path) {
const std::wstring pathW = utf8_to_wide(path);
// Try loading the library the original way first.
LoaderPlatformLibraryHandle handle = LoadLibraryW(pathW.c_str());
if (handle == NULL && GetLastError() == ERROR_MOD_NOT_FOUND) {
const DWORD dwAttrib = GetFileAttributesW(pathW.c_str());
const bool fileExists = (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
if (fileExists) {
// If that failed, then try loading it with broader search folders.
handle = LoadLibraryExW(pathW.c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
}
}
return handle;
}
static inline std::string LoaderPlatformLibraryOpenError(const std::string &path) {
std::stringstream ss;
const DWORD dwLastError = GetLastError();
const std::string strError = DescribeError(dwLastError);
ss << "Failed to open dynamic library " << path << " with error " << dwLastError << ": " << strError;
return ss.str();
}
static inline void LoaderPlatformLibraryClose(LoaderPlatformLibraryHandle library) { FreeLibrary(library); }
static inline void *LoaderPlatformLibraryGetProcAddr(LoaderPlatformLibraryHandle library, const std::string &name) {
assert(library);
assert(name.size() > 0);
return reinterpret_cast<void *>(GetProcAddress(library, name.c_str()));
}
static inline std::string LoaderPlatformLibraryGetProcAddrAddrError(const std::string &name) {
std::stringstream ss;
ss << "Failed to find function " << name << " in dynamic library";
return ss.str();
}
#else // Not Linux or Windows
#define PATH_SEPARATOR ':'
#define DIRECTORY_SYMBOL '/'
static inline LoaderPlatformLibraryHandle LoaderPlatformLibraryOpen(const std::string &path) {
// Stub func
#error("Unknown platform, undefined dynamic library routines resulting");
(void)path;
}
static inline const char *LoaderPlatformLibraryOpenError(const std::string &path) {
// Stub func
(void)path;
}
static inline void LoaderPlatformLibraryClose(LoaderPlatformLibraryHandle library) {
// Stub func
(void)library;
}
static inline void *LoaderPlatformLibraryGetProcAddr(LoaderPlatformLibraryHandle library, const std::string &name) {
// Stub func
void(library);
void(name);
}
static inline const char *LoaderPlatformLibraryGetProcAddrError(const std::string &name) {
// Stub func
(void)name;
}
#endif

View File

@@ -0,0 +1,845 @@
// 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>
//
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#include "manifest_file.hpp"
#ifdef OPENXR_HAVE_COMMON_CONFIG
#include "common_config.h"
#endif // OPENXR_HAVE_COMMON_CONFIG
#include "filesystem_utils.hpp"
#include "loader_platform.hpp"
#include "platform_utils.hpp"
#include "loader_logger.hpp"
#include <json/json.h>
#include <openxr/openxr.h>
#include <algorithm>
#include <cstring>
#include <fstream>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#ifndef FALLBACK_CONFIG_DIRS
#define FALLBACK_CONFIG_DIRS "/etc/xdg"
#endif // !FALLBACK_CONFIG_DIRS
#ifndef FALLBACK_DATA_DIRS
#define FALLBACK_DATA_DIRS "/usr/local/share:/usr/share"
#endif // !FALLBACK_DATA_DIRS
#ifndef SYSCONFDIR
#define SYSCONFDIR "/etc"
#endif // !SYSCONFDIR
#ifdef XRLOADER_DISABLE_EXCEPTION_HANDLING
#if JSON_USE_EXCEPTIONS
#error \
"Loader is configured to not catch exceptions, but jsoncpp was built with exception-throwing enabled, which could violate the C ABI. One of those two things needs to change."
#endif // JSON_USE_EXCEPTIONS
#endif // !XRLOADER_DISABLE_EXCEPTION_HANDLING
#include "runtime_interface.hpp"
// Utility functions for finding files in the appropriate paths
static inline bool StringEndsWith(const std::string &value, const std::string &ending) {
if (ending.size() > value.size()) {
return false;
}
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
// If the file found is a manifest file name, add it to the out_files manifest list.
static void AddIfJson(const std::string &full_file, std::vector<std::string> &manifest_files) {
if (full_file.empty() || !StringEndsWith(full_file, ".json")) {
return;
}
manifest_files.push_back(full_file);
}
// Check the current path for any manifest files. If the provided search_path is a directory, look for
// all included JSON files in that directory. Otherwise, just check the provided search_path which should
// be a single filename.
static void CheckAllFilesInThePath(const std::string &search_path, bool is_directory_list,
std::vector<std::string> &manifest_files) {
if (FileSysUtilsPathExists(search_path)) {
std::string absolute_path;
if (!is_directory_list) {
// If the file exists, try to add it
if (FileSysUtilsIsRegularFile(search_path)) {
FileSysUtilsGetAbsolutePath(search_path, absolute_path);
AddIfJson(absolute_path, manifest_files);
}
} else {
std::vector<std::string> files;
if (FileSysUtilsFindFilesInPath(search_path, files)) {
for (std::string &cur_file : files) {
std::string relative_path;
FileSysUtilsCombinePaths(search_path, cur_file, relative_path);
if (!FileSysUtilsGetAbsolutePath(relative_path, absolute_path)) {
continue;
}
AddIfJson(absolute_path, manifest_files);
}
}
}
}
}
// Add all manifest files in the provided paths to the manifest_files list. If search_path
// is made up of directory listings (versus direct manifest file names) search each path for
// any manifest files.
static void AddFilesInPath(const std::string &search_path, bool is_directory_list, std::vector<std::string> &manifest_files) {
std::size_t last_found = 0;
std::size_t found = search_path.find_first_of(PATH_SEPARATOR);
std::string cur_search;
// Handle any path listings in the string (separated by the appropriate path separator)
while (found != std::string::npos) {
// substr takes a start index and length.
std::size_t length = found - last_found;
cur_search = search_path.substr(last_found, length);
CheckAllFilesInThePath(cur_search, is_directory_list, manifest_files);
// This works around issue if multiple path separator follow each other directly.
last_found = found;
while (found == last_found) {
last_found = found + 1;
found = search_path.find_first_of(PATH_SEPARATOR, last_found);
}
}
// If there's something remaining in the string, copy it over
if (last_found < search_path.size()) {
cur_search = search_path.substr(last_found);
CheckAllFilesInThePath(cur_search, is_directory_list, manifest_files);
}
}
// Copy all paths listed in the cur_path string into output_path and append the appropriate relative_path onto the end of each.
static void CopyIncludedPaths(bool is_directory_list, const std::string &cur_path, const std::string &relative_path,
std::string &output_path) {
if (!cur_path.empty()) {
std::size_t last_found = 0;
std::size_t found = cur_path.find_first_of(PATH_SEPARATOR);
// Handle any path listings in the string (separated by the appropriate path separator)
while (found != std::string::npos) {
std::size_t length = found - last_found;
output_path += cur_path.substr(last_found, length);
if (is_directory_list && (cur_path[found - 1] != '\\' && cur_path[found - 1] != '/')) {
output_path += DIRECTORY_SYMBOL;
}
output_path += relative_path;
output_path += PATH_SEPARATOR;
last_found = found;
found = cur_path.find_first_of(PATH_SEPARATOR, found + 1);
}
// If there's something remaining in the string, copy it over
size_t last_char = cur_path.size() - 1;
if (last_found != last_char) {
output_path += cur_path.substr(last_found);
if (is_directory_list && (cur_path[last_char] != '\\' && cur_path[last_char] != '/')) {
output_path += DIRECTORY_SYMBOL;
}
output_path += relative_path;
output_path += PATH_SEPARATOR;
}
}
}
// Look for data files in the provided paths, but first check the environment override to determine if we should use that instead.
static void ReadDataFilesInSearchPaths(const std::string &override_env_var, const std::string &relative_path, bool &override_active,
std::vector<std::string> &manifest_files) {
std::string override_path;
std::string search_path;
if (!override_env_var.empty()) {
bool permit_override = true;
#ifndef XR_OS_WINDOWS
if (geteuid() != getuid() || getegid() != getgid()) {
// Don't allow setuid apps to use the env var
permit_override = false;
}
#endif
if (permit_override) {
override_path = PlatformUtilsGetSecureEnv(override_env_var.c_str());
}
}
if (!override_path.empty()) {
CopyIncludedPaths(true, override_path, "", search_path);
override_active = true;
} else {
override_active = false;
#if !defined(XR_OS_WINDOWS) && !defined(XR_OS_ANDROID)
const char home_additional[] = ".local/share/";
// Determine how much space is needed to generate the full search path
// for the current manifest files.
std::string xdg_conf_dirs = PlatformUtilsGetSecureEnv("XDG_CONFIG_DIRS");
std::string xdg_data_dirs = PlatformUtilsGetSecureEnv("XDG_DATA_DIRS");
std::string xdg_data_home = PlatformUtilsGetSecureEnv("XDG_DATA_HOME");
std::string home = PlatformUtilsGetSecureEnv("HOME");
if (xdg_conf_dirs.empty()) {
CopyIncludedPaths(true, FALLBACK_CONFIG_DIRS, relative_path, search_path);
} else {
CopyIncludedPaths(true, xdg_conf_dirs, relative_path, search_path);
}
CopyIncludedPaths(true, SYSCONFDIR, relative_path, search_path);
#if defined(EXTRASYSCONFDIR)
CopyIncludedPaths(true, EXTRASYSCONFDIR, relative_path, search_path);
#endif
if (xdg_data_dirs.empty()) {
CopyIncludedPaths(true, FALLBACK_DATA_DIRS, relative_path, search_path);
} else {
CopyIncludedPaths(true, xdg_data_dirs, relative_path, search_path);
}
if (!xdg_data_home.empty()) {
CopyIncludedPaths(true, xdg_data_home, relative_path, search_path);
} else if (!home.empty()) {
std::string relative_home_path = home_additional;
relative_home_path += relative_path;
CopyIncludedPaths(true, home, relative_home_path, search_path);
}
#else
(void)relative_path;
#endif
}
// Now, parse the paths and add any manifest files found in them.
AddFilesInPath(search_path, true, manifest_files);
}
#ifdef XR_OS_LINUX
// Get an XDG environment variable with a $HOME-relative default
static std::string GetXDGEnvHome(const char *name, const char *fallback_path) {
std::string result = PlatformUtilsGetSecureEnv(name);
if (!result.empty()) {
return result;
}
result = PlatformUtilsGetSecureEnv("HOME");
if (result.empty()) {
return result;
}
result += "/";
result += fallback_path;
return result;
}
// Get an XDG environment variable with absolute defaults
static std::string GetXDGEnvAbsolute(const char *name, const char *fallback_paths) {
std::string result = PlatformUtilsGetSecureEnv(name);
if (!result.empty()) {
return result;
}
return fallback_paths;
}
// Return the first instance of relative_path occurring in an XDG config dir according to standard
// precedence order.
static bool FindXDGConfigFile(const std::string &relative_path, std::string &out) {
out = GetXDGEnvHome("XDG_CONFIG_HOME", ".config");
if (!out.empty()) {
out += "/";
out += relative_path;
LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in XDG_CONFIG_HOME: " + out);
if (FileSysUtilsPathExists(out)) {
return true;
}
}
std::istringstream iss(GetXDGEnvAbsolute("XDG_CONFIG_DIRS", FALLBACK_CONFIG_DIRS));
std::string path;
while (std::getline(iss, path, PATH_SEPARATOR)) {
if (path.empty()) {
continue;
}
out = path;
out += "/";
out += relative_path;
LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in an entry of XDG_CONFIG_DIRS: " + out);
if (FileSysUtilsPathExists(out)) {
return true;
}
}
out = SYSCONFDIR;
out += "/";
out += relative_path;
LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in compiled-in SYSCONFDIR: " + out);
if (FileSysUtilsPathExists(out)) {
return true;
}
#if defined(EXTRASYSCONFDIR)
out = EXTRASYSCONFDIR;
out += "/";
out += relative_path;
LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in compiled-in EXTRASYSCONFDIR: " + out);
if (FileSysUtilsPathExists(out)) {
return true;
}
#endif
out.clear();
return false;
}
#endif
#ifdef XR_OS_WINDOWS
// Look for runtime data files in the provided paths, but first check the environment override to determine
// if we should use that instead.
static void ReadRuntimeDataFilesInRegistry(const std::string &runtime_registry_location,
const std::string &default_runtime_value_name,
std::vector<std::string> &manifest_files) {
HKEY hkey;
DWORD access_flags;
wchar_t value_w[1024];
DWORD value_size_w = sizeof(value_w); // byte size of the buffer.
// Generate the full registry location for the registry information
std::string full_registry_location = OPENXR_REGISTRY_LOCATION;
full_registry_location += std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION));
full_registry_location += runtime_registry_location;
const std::wstring full_registry_location_w = utf8_to_wide(full_registry_location);
const std::wstring default_runtime_value_name_w = utf8_to_wide(default_runtime_value_name);
// Use 64 bit regkey for 64bit application, and use 32 bit regkey in WOW for 32bit application.
access_flags = KEY_QUERY_VALUE;
LONG open_value = RegOpenKeyExW(HKEY_LOCAL_MACHINE, full_registry_location_w.c_str(), 0, access_flags, &hkey);
if (ERROR_SUCCESS != open_value) {
LoaderLogger::LogWarningMessage("",
"ReadRuntimeDataFilesInRegistry - failed to open registry key " + full_registry_location);
} else if (ERROR_SUCCESS != RegGetValueW(hkey, nullptr, default_runtime_value_name_w.c_str(),
RRF_RT_REG_SZ | REG_EXPAND_SZ | RRF_ZEROONFAILURE, NULL,
reinterpret_cast<LPBYTE>(&value_w), &value_size_w)) {
LoaderLogger::LogWarningMessage(
"", "ReadRuntimeDataFilesInRegistry - failed to read registry value " + default_runtime_value_name);
} else {
AddFilesInPath(wide_to_utf8(value_w), false, manifest_files);
}
}
// Look for layer data files in the provided paths, but first check the environment override to determine
// if we should use that instead.
static void ReadLayerDataFilesInRegistry(const std::string &registry_location, std::vector<std::string> &manifest_files) {
const std::wstring full_registry_location_w =
utf8_to_wide(OPENXR_REGISTRY_LOCATION + std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) + registry_location);
auto ReadLayerDataFilesInHive = [&](HKEY hive) {
HKEY hkey;
LONG open_value = RegOpenKeyExW(hive, full_registry_location_w.c_str(), 0, KEY_QUERY_VALUE, &hkey);
if (ERROR_SUCCESS != open_value) {
return false;
}
wchar_t name_w[1024]{};
LONG rtn_value;
DWORD name_size = 1023;
DWORD value;
DWORD value_size = sizeof(value);
DWORD key_index = 0;
while (ERROR_SUCCESS ==
(rtn_value = RegEnumValueW(hkey, key_index++, name_w, &name_size, NULL, NULL, (LPBYTE)&value, &value_size))) {
if (value_size == sizeof(value) && value == 0) {
const std::string filename = wide_to_utf8(name_w);
AddFilesInPath(filename, false, manifest_files);
}
// Reset some items for the next loop
name_size = 1023;
}
RegCloseKey(hkey);
return true;
};
// Do not allow high integrity processes to act on data that can be controlled by medium integrity processes.
const bool readFromCurrentUser = !IsHighIntegrityLevel();
bool found = ReadLayerDataFilesInHive(HKEY_LOCAL_MACHINE);
if (readFromCurrentUser) {
found |= ReadLayerDataFilesInHive(HKEY_CURRENT_USER);
}
if (!found) {
std::string warning_message = "ReadLayerDataFilesInRegistry - failed to read registry location ";
warning_message += registry_location;
warning_message += (readFromCurrentUser ? " in either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER" : " in HKEY_LOCAL_MACHINE");
LoaderLogger::LogWarningMessage("", warning_message);
}
}
#endif // XR_OS_WINDOWS
ManifestFile::ManifestFile(ManifestFileType type, const std::string &filename, const std::string &library_path)
: _filename(filename), _type(type), _library_path(library_path) {}
bool ManifestFile::IsValidJson(const Json::Value &root_node, JsonVersion &version) {
if (root_node["file_format_version"].isNull() || !root_node["file_format_version"].isString()) {
LoaderLogger::LogErrorMessage("", "ManifestFile::IsValidJson - JSON file missing \"file_format_version\"");
return false;
}
std::string file_format = root_node["file_format_version"].asString();
const int num_fields = sscanf(file_format.c_str(), "%u.%u.%u", &version.major, &version.minor, &version.patch);
// Only version 1.0.0 is defined currently. Eventually we may have more version, but
// some of the versions may only be valid for layers or runtimes specifically.
if (num_fields != 3 || version.major != 1 || version.minor != 0 || version.patch != 0) {
std::ostringstream error_ss;
error_ss << "ManifestFile::IsValidJson - JSON \"file_format_version\" " << version.major << "." << version.minor << "."
<< version.patch << " is not supported";
LoaderLogger::LogErrorMessage("", error_ss.str());
return false;
}
return true;
}
static void GetExtensionProperties(const std::vector<ExtensionListing> &extensions, std::vector<XrExtensionProperties> &props) {
for (const auto &ext : extensions) {
auto it =
std::find_if(props.begin(), props.end(), [&](XrExtensionProperties &prop) { return prop.extensionName == ext.name; });
if (it != props.end()) {
it->extensionVersion = std::max(it->extensionVersion, ext.extension_version);
} else {
XrExtensionProperties prop = {};
prop.type = XR_TYPE_EXTENSION_PROPERTIES;
prop.next = nullptr;
strncpy(prop.extensionName, ext.name.c_str(), XR_MAX_EXTENSION_NAME_SIZE - 1);
prop.extensionName[XR_MAX_EXTENSION_NAME_SIZE - 1] = '\0';
prop.extensionVersion = ext.extension_version;
props.push_back(prop);
}
}
}
// Return any instance extensions found in the manifest files in the proper form for
// OpenXR (XrExtensionProperties).
void ManifestFile::GetInstanceExtensionProperties(std::vector<XrExtensionProperties> &props) {
GetExtensionProperties(_instance_extensions, props);
}
const std::string &ManifestFile::GetFunctionName(const std::string &func_name) const {
if (!_functions_renamed.empty()) {
auto found = _functions_renamed.find(func_name);
if (found != _functions_renamed.end()) {
return found->second;
}
}
return func_name;
}
RuntimeManifestFile::RuntimeManifestFile(const std::string &filename, const std::string &library_path)
: ManifestFile(MANIFEST_TYPE_RUNTIME, filename, library_path) {}
static void ParseExtension(Json::Value const &ext, std::vector<ExtensionListing> &extensions) {
Json::Value ext_name = ext["name"];
Json::Value ext_version = ext["extension_version"];
// Allow "extension_version" as a String or a UInt to maintain backwards compatibility, even though it should be a String.
// Internal Issue 1411: https://gitlab.khronos.org/openxr/openxr/-/issues/1411
// Internal MR !1867: https://gitlab.khronos.org/openxr/openxr/-/merge_requests/1867
if (ext_name.isString() && (ext_version.isString() || ext_version.isUInt())) {
ExtensionListing ext_listing = {};
ext_listing.name = ext_name.asString();
if (ext_version.isUInt()) {
ext_listing.extension_version = ext_version.asUInt();
} else {
ext_listing.extension_version = atoi(ext_version.asString().c_str());
}
extensions.push_back(ext_listing);
}
}
void ManifestFile::ParseCommon(Json::Value const &root_node) {
const Json::Value &inst_exts = root_node["instance_extensions"];
if (!inst_exts.isNull() && inst_exts.isArray()) {
for (const auto &ext : inst_exts) {
ParseExtension(ext, _instance_extensions);
}
}
const Json::Value &funcs_renamed = root_node["functions"];
if (!funcs_renamed.isNull() && !funcs_renamed.empty()) {
for (Json::ValueConstIterator func_it = funcs_renamed.begin(); func_it != funcs_renamed.end(); ++func_it) {
if (!(*func_it).isString()) {
LoaderLogger::LogWarningMessage(
"", "ManifestFile::ParseCommon " + _filename + " \"functions\" section contains non-string values.");
continue;
}
std::string original_name = func_it.key().asString();
std::string new_name = (*func_it).asString();
_functions_renamed.emplace(original_name, new_name);
}
}
}
void RuntimeManifestFile::CreateIfValid(std::string const &filename,
std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
std::ifstream json_stream(filename, std::ifstream::in);
LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::CreateIfValid - attempting to load " + filename);
std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid ");
if (!json_stream.is_open()) {
error_ss << "failed to open " << filename << ". Does it exist?";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
Json::CharReaderBuilder builder;
std::string errors;
Json::Value root_node = Json::nullValue;
if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) {
error_ss << "failed to parse " << filename << ".";
if (!errors.empty()) {
error_ss << " (Error message: " << errors << ")";
}
error_ss << " Is it a valid runtime manifest file?";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
CreateIfValid(root_node, filename, manifest_files);
}
void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std::string &filename,
std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid ");
JsonVersion file_version = {};
if (!ManifestFile::IsValidJson(root_node, file_version)) {
error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
const Json::Value &runtime_root_node = root_node["runtime"];
// The Runtime manifest file needs the "runtime" root as well as a sub-node for "library_path". If any of those aren't there,
// fail.
if (runtime_root_node.isNull() || runtime_root_node["library_path"].isNull() || !runtime_root_node["library_path"].isString()) {
error_ss << filename << " is missing required fields. Verify all proper fields exist.";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
std::string lib_path = runtime_root_node["library_path"].asString();
// If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
// global library path.
if (lib_path.find('\\') != std::string::npos || lib_path.find('/') != std::string::npos) {
// If the library_path is an absolute path, just use that if it exists
if (FileSysUtilsIsAbsolutePath(lib_path)) {
if (!FileSysUtilsPathExists(lib_path)) {
error_ss << filename << " library " << lib_path << " does not appear to exist";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
} else {
// Otherwise, treat the library path as a relative path based on the JSON file.
std::string canonical_path;
std::string combined_path;
std::string file_parent;
// Search relative to the real manifest file, not relative to the symlink
if (!FileSysUtilsGetCanonicalPath(filename, canonical_path)) {
// Give relative to the non-canonical path a chance
canonical_path = filename;
}
if (!FileSysUtilsGetParentPath(canonical_path, file_parent) ||
!FileSysUtilsCombinePaths(file_parent, lib_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
error_ss << filename << " library " << combined_path << " does not appear to exist";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
lib_path = combined_path;
}
}
// Add this runtime manifest file
manifest_files.emplace_back(new RuntimeManifestFile(filename, lib_path));
// Add any extensions to it after the fact.
// Handle any renamed functions
manifest_files.back()->ParseCommon(runtime_root_node);
}
// Find all manifest files in the appropriate search paths/registries for the given type.
XrResult RuntimeManifestFile::FindManifestFiles(std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
XrResult result = XR_SUCCESS;
std::string filename = PlatformUtilsGetSecureEnv(OPENXR_RUNTIME_JSON_ENV_VAR);
if (!filename.empty()) {
LoaderLogger::LogInfoMessage(
"", "RuntimeManifestFile::FindManifestFiles - using environment variable override runtime file " + filename);
} else {
#ifdef XR_OS_WINDOWS
std::vector<std::string> filenames;
ReadRuntimeDataFilesInRegistry("", "ActiveRuntime", filenames);
if (filenames.size() == 0) {
LoaderLogger::LogErrorMessage(
"", "RuntimeManifestFile::FindManifestFiles - failed to find active runtime file in registry");
return XR_ERROR_RUNTIME_UNAVAILABLE;
}
if (filenames.size() > 1) {
LoaderLogger::LogWarningMessage(
"", "RuntimeManifestFile::FindManifestFiles - found too many default runtime files in registry");
}
filename = filenames[0];
LoaderLogger::LogInfoMessage("",
"RuntimeManifestFile::FindManifestFiles - using registry-specified runtime file " + filename);
#elif defined(XR_OS_LINUX)
const std::string relative_path =
"openxr/" + std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) + "/active_runtime.json";
if (!FindXDGConfigFile(relative_path, filename)) {
LoaderLogger::LogErrorMessage(
"", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment");
return XR_ERROR_RUNTIME_UNAVAILABLE;
}
#else
#if defined(XR_KHR_LOADER_INIT_SUPPORT)
Json::Value virtualManifest;
result = GetPlatformRuntimeVirtualManifest(virtualManifest);
if (XR_SUCCESS == result) {
RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files);
return result;
}
#endif // defined(XR_KHR_LOADER_INIT_SUPPORT)
if (!PlatformGetGlobalRuntimeFileName(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), filename)) {
LoaderLogger::LogErrorMessage(
"", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment");
return XR_ERROR_RUNTIME_UNAVAILABLE;
}
result = XR_SUCCESS;
LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::FindManifestFiles - using global runtime file " + filename);
#endif
}
RuntimeManifestFile::CreateIfValid(filename, manifest_files);
return result;
}
ApiLayerManifestFile::ApiLayerManifestFile(ManifestFileType type, const std::string &filename, const std::string &layer_name,
const std::string &description, const JsonVersion &api_version,
const uint32_t &implementation_version, const std::string &library_path)
: ManifestFile(type, filename, library_path),
_api_version(api_version),
_layer_name(layer_name),
_description(description),
_implementation_version(implementation_version) {}
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename,
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
std::ifstream json_stream(filename, std::ifstream::in);
std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
if (!json_stream.is_open()) {
error_ss << "failed to open " << filename << ". Does it exist?";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
Json::CharReaderBuilder builder;
std::string errors;
Json::Value root_node = Json::nullValue;
if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) {
error_ss << "failed to parse " << filename << ".";
if (!errors.empty()) {
error_ss << " (Error message: " << errors << ")";
}
error_ss << " Is it a valid layer manifest file?";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
JsonVersion file_version = {};
if (!ManifestFile::IsValidJson(root_node, file_version)) {
error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
Json::Value layer_root_node = root_node["api_layer"];
// The API Layer manifest file needs the "api_layer" root as well as other sub-nodes.
// If any of those aren't there, fail.
if (layer_root_node.isNull() || layer_root_node["name"].isNull() || !layer_root_node["name"].isString() ||
layer_root_node["api_version"].isNull() || !layer_root_node["api_version"].isString() ||
layer_root_node["library_path"].isNull() || !layer_root_node["library_path"].isString() ||
layer_root_node["implementation_version"].isNull() || !layer_root_node["implementation_version"].isString()) {
error_ss << filename << " is missing required fields. Verify all proper fields exist.";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
if (MANIFEST_TYPE_IMPLICIT_API_LAYER == type) {
bool enabled = true;
// Implicit layers require the disable environment variable.
if (layer_root_node["disable_environment"].isNull() || !layer_root_node["disable_environment"].isString()) {
error_ss << "Implicit layer " << filename << " is missing \"disable_environment\"";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
// Check if there's an enable environment variable provided
if (!layer_root_node["enable_environment"].isNull() && layer_root_node["enable_environment"].isString()) {
std::string env_var = layer_root_node["enable_environment"].asString();
// If it's not set in the environment, disable the layer
if (!PlatformUtilsGetEnvSet(env_var.c_str())) {
enabled = false;
}
}
// Check for the disable environment variable, which must be provided in the JSON
std::string env_var = layer_root_node["disable_environment"].asString();
// If the env var is set, disable the layer. Disable env var overrides enable above
if (PlatformUtilsGetEnvSet(env_var.c_str())) {
enabled = false;
}
// Not enabled, so pretend like it isn't even there.
if (!enabled) {
error_ss << "Implicit layer " << filename << " is disabled";
LoaderLogger::LogInfoMessage("", error_ss.str());
return;
}
}
std::string layer_name = layer_root_node["name"].asString();
std::string api_version_string = layer_root_node["api_version"].asString();
JsonVersion api_version = {};
const int num_fields = sscanf(api_version_string.c_str(), "%u.%u", &api_version.major, &api_version.minor);
api_version.patch = 0;
if ((num_fields != 2) || (api_version.major == 0 && api_version.minor == 0) ||
api_version.major > XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) {
error_ss << "layer " << filename << " has invalid API Version. Skipping layer.";
LoaderLogger::LogWarningMessage("", error_ss.str());
return;
}
uint32_t implementation_version = atoi(layer_root_node["implementation_version"].asString().c_str());
std::string library_path = layer_root_node["library_path"].asString();
// If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
// global library path.
if (library_path.find('\\') != std::string::npos || library_path.find('/') != std::string::npos) {
// If the library_path is an absolute path, just use that if it exists
if (FileSysUtilsIsAbsolutePath(library_path)) {
if (!FileSysUtilsPathExists(library_path)) {
error_ss << filename << " library " << library_path << " does not appear to exist";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
} else {
// Otherwise, treat the library path as a relative path based on the JSON file.
std::string combined_path;
std::string file_parent;
if (!FileSysUtilsGetParentPath(filename, file_parent) ||
!FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
error_ss << filename << " library " << combined_path << " does not appear to exist";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
library_path = combined_path;
}
}
std::string description;
if (!layer_root_node["description"].isNull() && layer_root_node["description"].isString()) {
description = layer_root_node["description"].asString();
}
// Add this layer manifest file
manifest_files.emplace_back(
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
// Add any extensions to it after the fact.
manifest_files.back()->ParseCommon(layer_root_node);
}
void ApiLayerManifestFile::PopulateApiLayerProperties(XrApiLayerProperties &props) const {
props.layerVersion = _implementation_version;
props.specVersion = XR_MAKE_VERSION(_api_version.major, _api_version.minor, _api_version.patch);
strncpy(props.layerName, _layer_name.c_str(), XR_MAX_API_LAYER_NAME_SIZE - 1);
if (_layer_name.size() >= XR_MAX_API_LAYER_NAME_SIZE - 1) {
props.layerName[XR_MAX_API_LAYER_NAME_SIZE - 1] = '\0';
}
strncpy(props.description, _description.c_str(), XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1);
if (_description.size() >= XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1) {
props.description[XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1] = '\0';
}
}
// Find all layer manifest files in the appropriate search paths/registries for the given type.
XrResult ApiLayerManifestFile::FindManifestFiles(ManifestFileType type,
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
std::string relative_path;
std::string override_env_var;
std::string registry_location;
// Add the appropriate top-level folders for the relative path. These should be
// the string "openxr/" followed by the API major version as a string.
relative_path = OPENXR_RELATIVE_PATH;
relative_path += std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION));
switch (type) {
case MANIFEST_TYPE_IMPLICIT_API_LAYER:
relative_path += OPENXR_IMPLICIT_API_LAYER_RELATIVE_PATH;
override_env_var = "";
#ifdef XR_OS_WINDOWS
registry_location = OPENXR_IMPLICIT_API_LAYER_REGISTRY_LOCATION;
#endif
break;
case MANIFEST_TYPE_EXPLICIT_API_LAYER:
relative_path += OPENXR_EXPLICIT_API_LAYER_RELATIVE_PATH;
override_env_var = OPENXR_API_LAYER_PATH_ENV_VAR;
#ifdef XR_OS_WINDOWS
registry_location = OPENXR_EXPLICIT_API_LAYER_REGISTRY_LOCATION;
#endif
break;
default:
LoaderLogger::LogErrorMessage("", "ApiLayerManifestFile::FindManifestFiles - unknown manifest file requested");
return XR_ERROR_FILE_ACCESS_ERROR;
}
bool override_active = false;
std::vector<std::string> filenames;
ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames);
#ifdef XR_OS_WINDOWS
// Read the registry if the override wasn't active.
if (!override_active) {
ReadLayerDataFilesInRegistry(registry_location, filenames);
}
#endif
for (std::string &cur_file : filenames) {
ApiLayerManifestFile::CreateIfValid(type, cur_file, manifest_files);
}
return XR_SUCCESS;
}

View File

@@ -0,0 +1,103 @@
// 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 <openxr/openxr.h>
#include <memory>
#include <string>
#include <vector>
#include <unordered_map>
namespace Json {
class Value;
}
enum ManifestFileType {
MANIFEST_TYPE_UNDEFINED = 0,
MANIFEST_TYPE_RUNTIME,
MANIFEST_TYPE_IMPLICIT_API_LAYER,
MANIFEST_TYPE_EXPLICIT_API_LAYER,
};
struct JsonVersion {
uint32_t major;
uint32_t minor;
uint32_t patch;
};
struct ExtensionListing {
std::string name;
uint32_t extension_version;
};
// ManifestFile class -
// Base class responsible for finding and parsing manifest files.
class ManifestFile {
public:
// Non-copyable
ManifestFile(const ManifestFile &) = delete;
ManifestFile &operator=(const ManifestFile &) = delete;
ManifestFileType Type() const { return _type; }
const std::string &Filename() const { return _filename; }
const std::string &LibraryPath() const { return _library_path; }
void GetInstanceExtensionProperties(std::vector<XrExtensionProperties> &props);
const std::string &GetFunctionName(const std::string &func_name) const;
protected:
ManifestFile(ManifestFileType type, const std::string &filename, const std::string &library_path);
void ParseCommon(Json::Value const &root_node);
static bool IsValidJson(const Json::Value &root, JsonVersion &version);
private:
std::string _filename;
ManifestFileType _type;
std::string _library_path;
std::vector<ExtensionListing> _instance_extensions;
std::unordered_map<std::string, std::string> _functions_renamed;
};
// RuntimeManifestFile class -
// Responsible for finding and parsing Runtime-specific manifest files.
class RuntimeManifestFile : public ManifestFile {
public:
// Factory method
static XrResult FindManifestFiles(std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files);
private:
RuntimeManifestFile(const std::string &filename, const std::string &library_path);
static void CreateIfValid(const std::string &filename, std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files);
static void CreateIfValid(const Json::Value &root_node, const std::string &filename,
std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files);
};
// ApiLayerManifestFile class -
// Responsible for finding and parsing API Layer-specific manifest files.
class ApiLayerManifestFile : public ManifestFile {
public:
// Factory method
static XrResult FindManifestFiles(ManifestFileType type, std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
const std::string &LayerName() const { return _layer_name; }
void PopulateApiLayerProperties(XrApiLayerProperties &props) const;
private:
ApiLayerManifestFile(ManifestFileType type, const std::string &filename, const std::string &layer_name,
const std::string &description, const JsonVersion &api_version, const uint32_t &implementation_version,
const std::string &library_path);
static void CreateIfValid(ManifestFileType type, const std::string &filename,
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
JsonVersion _api_version;
std::string _layer_name;
std::string _description;
uint32_t _implementation_version;
};

View File

@@ -0,0 +1,493 @@
// 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 Author: Mark Young <marky@lunarg.com>
//
#include "runtime_interface.hpp"
#include "manifest_file.hpp"
#include "loader_interfaces.h"
#include "loader_logger.hpp"
#include "loader_platform.hpp"
#include "xr_generated_dispatch_table.h"
#include <openxr/openxr.h>
#include <cstring>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#ifdef XR_USE_PLATFORM_ANDROID
#include "android_utilities.h"
#include <json/value.h>
#endif // XR_USE_PLATFORM_ANDROID
#ifdef XR_KHR_LOADER_INIT_SUPPORT
namespace {
/*!
* Stores a copy of the data passed to the xrInitializeLoaderKHR function in a singleton.
*/
class LoaderInitData {
public:
/*!
* Singleton accessor.
*/
static LoaderInitData& instance() {
static LoaderInitData obj;
return obj;
}
#ifdef XR_USE_PLATFORM_ANDROID
/*!
* Type alias for the platform-specific structure type.
*/
using StructType = XrLoaderInitInfoAndroidKHR;
#endif
/*!
* Get our copy of the data, casted to pass to the runtime's matching method.
*/
const XrLoaderInitInfoBaseHeaderKHR* getParam() const { return reinterpret_cast<const XrLoaderInitInfoBaseHeaderKHR*>(&_data); }
/*!
* Get the data via its real structure type.
*/
const StructType& getData() const { return _data; }
/*!
* Has this been correctly initialized?
*/
bool initialized() const noexcept { return _initialized; }
/*!
* Initialize loader data - called by InitializeLoader() and thus ultimately by the loader's xrInitializeLoaderKHR
* implementation. Each platform that needs this extension will provide an implementation of this.
*/
XrResult initialize(const XrLoaderInitInfoBaseHeaderKHR* info);
private:
//! Private constructor, forces use of singleton accessor.
LoaderInitData() = default;
//! Platform-specific init data
StructType _data = {};
//! Flag for indicating whether _data is valid.
bool _initialized = false;
};
#ifdef XR_USE_PLATFORM_ANDROID
// Check and copy the Android-specific init data.
XrResult LoaderInitData::initialize(const XrLoaderInitInfoBaseHeaderKHR* info) {
if (info->type != XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) {
return XR_ERROR_VALIDATION_FAILURE;
}
auto cast_info = reinterpret_cast<XrLoaderInitInfoAndroidKHR const*>(info);
if (cast_info->applicationVM == nullptr) {
return XR_ERROR_VALIDATION_FAILURE;
}
if (cast_info->applicationContext == nullptr) {
return XR_ERROR_VALIDATION_FAILURE;
}
_data = *cast_info;
jni::init((jni::JavaVM*)_data.applicationVM);
_data.next = nullptr;
_initialized = true;
return XR_SUCCESS;
}
#endif // XR_USE_PLATFORM_ANDROID
} // namespace
XrResult InitializeLoader(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo) {
return LoaderInitData::instance().initialize(loaderInitInfo);
}
#endif // XR_KHR_LOADER_INIT_SUPPORT
#ifdef XR_USE_PLATFORM_ANDROID
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {
using wrap::android::content::Context;
auto& initData = LoaderInitData::instance();
if (!initData.initialized()) {
return XR_ERROR_INITIALIZATION_FAILED;
}
auto context = Context(reinterpret_cast<jobject>(initData.getData().applicationContext));
if (context.isNull()) {
return XR_ERROR_INITIALIZATION_FAILED;
}
Json::Value virtualManifest;
if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) {
return XR_ERROR_INITIALIZATION_FAILED;
}
out_manifest = virtualManifest;
return XR_SUCCESS;
}
#endif // XR_USE_PLATFORM_ANDROID
XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command,
std::unique_ptr<RuntimeManifestFile>& manifest_file) {
LoaderPlatformLibraryHandle runtime_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());
if (nullptr == runtime_library) {
std::string library_message = LoaderPlatformLibraryOpenError(manifest_file->LibraryPath());
std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
warning_message += manifest_file->Filename();
warning_message += ", failed to load with message \"";
warning_message += library_message;
warning_message += "\"";
LoaderLogger::LogErrorMessage(openxr_command, warning_message);
return XR_ERROR_FILE_ACCESS_ERROR;
}
#ifdef XR_KHR_LOADER_INIT_SUPPORT
if (!LoaderInitData::instance().initialized()) {
LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntime skipping manifest file " +
manifest_file->Filename() +
" because xrInitializeLoaderKHR was not yet called.");
LoaderPlatformLibraryClose(runtime_library);
return XR_ERROR_VALIDATION_FAILURE;
}
bool forwardedInitLoader = false;
{
// If we have xrInitializeLoaderKHR exposed as an export, forward call to it.
const auto function_name = manifest_file->GetFunctionName("xrInitializeLoaderKHR");
auto initLoader =
reinterpret_cast<PFN_xrInitializeLoaderKHR>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));
if (initLoader != nullptr) {
// we found the entry point one way or another.
LoaderLogger::LogInfoMessage(openxr_command,
"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime before "
"calling xrNegotiateLoaderRuntimeInterface.");
XrResult res = initLoader(LoaderInitData::instance().getParam());
if (!XR_SUCCEEDED(res)) {
LoaderLogger::LogErrorMessage(openxr_command,
"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");
LoaderPlatformLibraryClose(runtime_library);
return res;
}
forwardedInitLoader = true;
}
}
#endif
// Get and settle on an runtime interface version (using any provided name if required).
std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderRuntimeInterface");
auto negotiate =
reinterpret_cast<PFN_xrNegotiateLoaderRuntimeInterface>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));
// Loader info for negotiation
XrNegotiateLoaderInfo loader_info = {};
loader_info.structType = XR_LOADER_INTERFACE_STRUCT_LOADER_INFO;
loader_info.structVersion = XR_LOADER_INFO_STRUCT_VERSION;
loader_info.structSize = sizeof(XrNegotiateLoaderInfo);
loader_info.minInterfaceVersion = 1;
loader_info.maxInterfaceVersion = XR_CURRENT_LOADER_RUNTIME_VERSION;
loader_info.minApiVersion = XR_MAKE_VERSION(1, 0, 0);
loader_info.maxApiVersion = XR_MAKE_VERSION(1, 0x3ff, 0xfff); // Maximum allowed version for this major version.
// Set up the runtime return structure
XrNegotiateRuntimeRequest runtime_info = {};
runtime_info.structType = XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST;
runtime_info.structVersion = XR_RUNTIME_INFO_STRUCT_VERSION;
runtime_info.structSize = sizeof(XrNegotiateRuntimeRequest);
// Skip calling the negotiate function and fail if the function pointer
// could not get loaded
XrResult res = XR_ERROR_RUNTIME_FAILURE;
if (nullptr != negotiate) {
res = negotiate(&loader_info, &runtime_info);
}
// If we supposedly succeeded, but got a nullptr for GetInstanceProcAddr
// then something still went wrong, so return with an error.
if (XR_SUCCEEDED(res)) {
uint32_t runtime_major = XR_VERSION_MAJOR(runtime_info.runtimeApiVersion);
uint32_t runtime_minor = XR_VERSION_MINOR(runtime_info.runtimeApiVersion);
uint32_t loader_major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION);
if (nullptr == runtime_info.getInstanceProcAddr) {
std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
error_message += manifest_file->Filename();
error_message += ", negotiation succeeded but returned NULL getInstanceProcAddr";
LoaderLogger::LogErrorMessage(openxr_command, error_message);
res = XR_ERROR_FILE_CONTENTS_INVALID;
} else if (0 >= runtime_info.runtimeInterfaceVersion ||
XR_CURRENT_LOADER_RUNTIME_VERSION < runtime_info.runtimeInterfaceVersion) {
std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
error_message += manifest_file->Filename();
error_message += ", negotiation succeeded but returned invalid interface version";
LoaderLogger::LogErrorMessage(openxr_command, error_message);
res = XR_ERROR_FILE_CONTENTS_INVALID;
} else if (runtime_major != loader_major || (runtime_major == 0 && runtime_minor == 0)) {
std::string error_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
error_message += manifest_file->Filename();
error_message += ", OpenXR version returned not compatible with this loader";
LoaderLogger::LogErrorMessage(openxr_command, error_message);
res = XR_ERROR_FILE_CONTENTS_INVALID;
}
}
#ifdef XR_KHR_LOADER_INIT_SUPPORT
if (XR_SUCCEEDED(res) && !forwardedInitLoader) {
// Forward initialize loader call, where possible and if we did not do so before.
PFN_xrVoidFunction initializeVoid = nullptr;
PFN_xrInitializeLoaderKHR initialize = nullptr;
// Now we may try asking xrGetInstanceProcAddr
if (XR_SUCCEEDED(runtime_info.getInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", &initializeVoid))) {
if (initializeVoid == nullptr) {
LoaderLogger::LogErrorMessage(openxr_command,
"RuntimeInterface::LoadRuntime got success from xrGetInstanceProcAddr "
"for xrInitializeLoaderKHR, but output a null pointer.");
res = XR_ERROR_RUNTIME_FAILURE;
} else {
initialize = reinterpret_cast<PFN_xrInitializeLoaderKHR>(initializeVoid);
}
}
if (initialize != nullptr) {
// we found the entry point one way or another.
LoaderLogger::LogInfoMessage(openxr_command,
"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime after "
"calling xrNegotiateLoaderRuntimeInterface.");
res = initialize(LoaderInitData::instance().getParam());
if (!XR_SUCCEEDED(res)) {
LoaderLogger::LogErrorMessage(openxr_command,
"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");
}
}
}
#endif
if (XR_FAILED(res)) {
std::string warning_message = "RuntimeInterface::LoadRuntime skipping manifest file ";
warning_message += manifest_file->Filename();
warning_message += ", negotiation failed with error ";
warning_message += std::to_string(res);
LoaderLogger::LogErrorMessage(openxr_command, warning_message);
LoaderPlatformLibraryClose(runtime_library);
return res;
}
std::string info_message = "RuntimeInterface::LoadRuntime succeeded loading runtime defined in manifest file ";
info_message += manifest_file->Filename();
info_message += " using interface version ";
info_message += std::to_string(runtime_info.runtimeInterfaceVersion);
info_message += " and OpenXR API version ";
info_message += std::to_string(XR_VERSION_MAJOR(runtime_info.runtimeApiVersion));
info_message += ".";
info_message += std::to_string(XR_VERSION_MINOR(runtime_info.runtimeApiVersion));
LoaderLogger::LogInfoMessage(openxr_command, info_message);
// Use this runtime
GetInstance().reset(new RuntimeInterface(runtime_library, runtime_info.getInstanceProcAddr));
// Grab the list of extensions this runtime supports for easy filtering after the
// xrCreateInstance call
std::vector<std::string> supported_extensions;
std::vector<XrExtensionProperties> extension_properties;
GetInstance()->GetInstanceExtensionProperties(extension_properties);
supported_extensions.reserve(extension_properties.size());
for (XrExtensionProperties ext_prop : extension_properties) {
supported_extensions.emplace_back(ext_prop.extensionName);
}
GetInstance()->SetSupportedExtensions(supported_extensions);
return XR_SUCCESS;
}
XrResult RuntimeInterface::LoadRuntime(const std::string& openxr_command) {
// If something's already loaded, we're done here.
if (GetInstance() != nullptr) {
return XR_SUCCESS;
}
#ifdef XR_KHR_LOADER_INIT_SUPPORT
if (!LoaderInitData::instance().initialized()) {
LoaderLogger::LogErrorMessage(
openxr_command, "RuntimeInterface::LoadRuntime cannot run because xrInitializeLoaderKHR was not successfully called.");
return XR_ERROR_INITIALIZATION_FAILED;
}
#endif // XR_KHR_LOADER_INIT_SUPPORT
std::vector<std::unique_ptr<RuntimeManifestFile>> runtime_manifest_files = {};
// Find the available runtimes which we may need to report information for.
XrResult last_error = RuntimeManifestFile::FindManifestFiles(runtime_manifest_files);
if (XR_FAILED(last_error)) {
LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - unknown error");
} else {
last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
for (std::unique_ptr<RuntimeManifestFile>& manifest_file : runtime_manifest_files) {
last_error = RuntimeInterface::TryLoadingSingleRuntime(openxr_command, manifest_file);
if (XR_SUCCEEDED(last_error)) {
break;
}
}
}
// Unsuccessful in loading any runtime, throw the runtime unavailable message.
if (XR_FAILED(last_error)) {
LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - failed to load a runtime");
last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
}
return last_error;
}
void RuntimeInterface::UnloadRuntime(const std::string& openxr_command) {
if (GetInstance()) {
LoaderLogger::LogInfoMessage(openxr_command, "RuntimeInterface::UnloadRuntime - Unloading RuntimeInterface");
GetInstance().reset();
}
}
XrResult RuntimeInterface::GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) {
return GetInstance()->_get_instance_proc_addr(instance, name, function);
}
const XrGeneratedDispatchTable* RuntimeInterface::GetDispatchTable(XrInstance instance) {
XrGeneratedDispatchTable* table = nullptr;
std::lock_guard<std::mutex> mlock(GetInstance()->_dispatch_table_mutex);
auto it = GetInstance()->_dispatch_table_map.find(instance);
if (it != GetInstance()->_dispatch_table_map.end()) {
table = it->second.get();
}
return table;
}
const XrGeneratedDispatchTable* RuntimeInterface::GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger) {
XrInstance runtime_instance = XR_NULL_HANDLE;
{
std::lock_guard<std::mutex> mlock(GetInstance()->_messenger_to_instance_mutex);
auto it = GetInstance()->_messenger_to_instance_map.find(messenger);
if (it != GetInstance()->_messenger_to_instance_map.end()) {
runtime_instance = it->second;
}
}
return GetDispatchTable(runtime_instance);
}
RuntimeInterface::RuntimeInterface(LoaderPlatformLibraryHandle runtime_library, PFN_xrGetInstanceProcAddr get_instance_proc_addr)
: _runtime_library(runtime_library), _get_instance_proc_addr(get_instance_proc_addr) {}
RuntimeInterface::~RuntimeInterface() {
std::string info_message = "RuntimeInterface being destroyed.";
LoaderLogger::LogInfoMessage("", info_message);
{
std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
_dispatch_table_map.clear();
}
LoaderPlatformLibraryClose(_runtime_library);
}
void RuntimeInterface::GetInstanceExtensionProperties(std::vector<XrExtensionProperties>& extension_properties) {
std::vector<XrExtensionProperties> runtime_extension_properties;
PFN_xrEnumerateInstanceExtensionProperties rt_xrEnumerateInstanceExtensionProperties;
_get_instance_proc_addr(XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties",
reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrEnumerateInstanceExtensionProperties));
uint32_t count = 0;
uint32_t count_output = 0;
// Get the count from the runtime
rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, nullptr);
if (count_output > 0) {
runtime_extension_properties.resize(count_output);
count = count_output;
for (XrExtensionProperties& ext_prop : runtime_extension_properties) {
ext_prop.type = XR_TYPE_EXTENSION_PROPERTIES;
ext_prop.next = nullptr;
}
rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, runtime_extension_properties.data());
}
size_t ext_count = runtime_extension_properties.size();
size_t props_count = extension_properties.size();
for (size_t ext = 0; ext < ext_count; ++ext) {
bool found = false;
for (size_t prop = 0; prop < props_count; ++prop) {
// If we find it, then make sure the spec version matches that of the runtime instead of the
// layer.
if (strcmp(extension_properties[prop].extensionName, runtime_extension_properties[ext].extensionName) == 0) {
// Make sure the spec version used is the runtime's
extension_properties[prop].extensionVersion = runtime_extension_properties[ext].extensionVersion;
found = true;
break;
}
}
if (!found) {
extension_properties.push_back(runtime_extension_properties[ext]);
}
}
}
XrResult RuntimeInterface::CreateInstance(const XrInstanceCreateInfo* info, XrInstance* instance) {
XrResult res = XR_SUCCESS;
bool create_succeeded = false;
PFN_xrCreateInstance rt_xrCreateInstance;
_get_instance_proc_addr(XR_NULL_HANDLE, "xrCreateInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrCreateInstance));
res = rt_xrCreateInstance(info, instance);
if (XR_SUCCEEDED(res)) {
create_succeeded = true;
std::unique_ptr<XrGeneratedDispatchTable> dispatch_table(new XrGeneratedDispatchTable());
GeneratedXrPopulateDispatchTable(dispatch_table.get(), *instance, _get_instance_proc_addr);
std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
_dispatch_table_map[*instance] = std::move(dispatch_table);
}
// If the failure occurred during the populate, clean up the instance we had picked up from the runtime
if (XR_FAILED(res) && create_succeeded) {
PFN_xrDestroyInstance rt_xrDestroyInstance;
_get_instance_proc_addr(*instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));
rt_xrDestroyInstance(*instance);
*instance = XR_NULL_HANDLE;
}
return res;
}
XrResult RuntimeInterface::DestroyInstance(XrInstance instance) {
if (XR_NULL_HANDLE != instance) {
// Destroy the dispatch table for this instance first
{
std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
auto map_iter = _dispatch_table_map.find(instance);
if (map_iter != _dispatch_table_map.end()) {
_dispatch_table_map.erase(map_iter);
}
}
// Now delete the instance
PFN_xrDestroyInstance rt_xrDestroyInstance;
_get_instance_proc_addr(instance, "xrDestroyInstance", reinterpret_cast<PFN_xrVoidFunction*>(&rt_xrDestroyInstance));
rt_xrDestroyInstance(instance);
}
return XR_SUCCESS;
}
bool RuntimeInterface::TrackDebugMessenger(XrInstance instance, XrDebugUtilsMessengerEXT messenger) {
std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);
_messenger_to_instance_map[messenger] = instance;
return true;
}
void RuntimeInterface::ForgetDebugMessenger(XrDebugUtilsMessengerEXT messenger) {
if (XR_NULL_HANDLE != messenger) {
std::lock_guard<std::mutex> mlock(_messenger_to_instance_mutex);
_messenger_to_instance_map.erase(messenger);
}
}
void RuntimeInterface::SetSupportedExtensions(std::vector<std::string>& supported_extensions) {
_supported_extensions = supported_extensions;
}
bool RuntimeInterface::SupportsExtension(const std::string& extension_name) {
bool found_prop = false;
for (const std::string& supported_extension : _supported_extensions) {
if (supported_extension == extension_name) {
found_prop = true;
break;
}
}
return found_prop;
}

View File

@@ -0,0 +1,84 @@
// 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 Author: Mark Young <marky@lunarg.com>
//
#pragma once
#include "loader_platform.hpp"
#include <openxr/openxr.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <mutex>
#include <memory>
#ifdef XR_USE_PLATFORM_ANDROID
#define XR_KHR_LOADER_INIT_SUPPORT
#endif
namespace Json {
class Value;
}
#ifdef XR_KHR_LOADER_INIT_SUPPORT
//! Initialize loader, where required.
XrResult InitializeLoader(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo);
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest);
#endif
class RuntimeManifestFile;
struct XrGeneratedDispatchTable;
class RuntimeInterface {
public:
virtual ~RuntimeInterface();
// Helper functions for loading and unloading the runtime (but only when necessary)
static XrResult LoadRuntime(const std::string& openxr_command);
static void UnloadRuntime(const std::string& openxr_command);
static RuntimeInterface& GetRuntime() { return *(GetInstance().get()); }
static XrResult GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function);
// Get the direct dispatch table to this runtime, without API layers or loader terminators.
static const XrGeneratedDispatchTable* GetDispatchTable(XrInstance instance);
static const XrGeneratedDispatchTable* GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger);
void GetInstanceExtensionProperties(std::vector<XrExtensionProperties>& extension_properties);
bool SupportsExtension(const std::string& extension_name);
XrResult CreateInstance(const XrInstanceCreateInfo* info, XrInstance* instance);
XrResult DestroyInstance(XrInstance instance);
bool TrackDebugMessenger(XrInstance instance, XrDebugUtilsMessengerEXT messenger);
void ForgetDebugMessenger(XrDebugUtilsMessengerEXT messenger);
// No default construction
RuntimeInterface() = delete;
// Non-copyable
RuntimeInterface(const RuntimeInterface&) = delete;
RuntimeInterface& operator=(const RuntimeInterface&) = delete;
private:
RuntimeInterface(LoaderPlatformLibraryHandle runtime_library, PFN_xrGetInstanceProcAddr get_instance_proc_addr);
void SetSupportedExtensions(std::vector<std::string>& supported_extensions);
static XrResult TryLoadingSingleRuntime(const std::string& openxr_command, std::unique_ptr<RuntimeManifestFile>& manifest_file);
static std::unique_ptr<RuntimeInterface>& GetInstance() {
static std::unique_ptr<RuntimeInterface> instance;
return instance;
}
LoaderPlatformLibraryHandle _runtime_library;
PFN_xrGetInstanceProcAddr _get_instance_proc_addr;
std::unordered_map<XrInstance, std::unique_ptr<XrGeneratedDispatchTable>> _dispatch_table_map;
std::mutex _dispatch_table_mutex;
std::unordered_map<XrDebugUtilsMessengerEXT, XrInstance> _messenger_to_instance_map;
std::mutex _messenger_to_instance_mutex;
std::vector<std::string> _supported_extensions;
};

View File

@@ -0,0 +1,700 @@
// 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
// *********** THIS FILE IS GENERATED - DO NOT EDIT ***********
// See loader_source_generator.py for modifications
// ************************************************************
// 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
//
// 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: Mark Young <marky@lunarg.com>
//
#include "xr_generated_loader.hpp"
#include "api_layer_interface.hpp"
#include "exception_handling.hpp"
#include "hex_and_handles.h"
#include "loader_instance.hpp"
#include "loader_logger.hpp"
#include "loader_platform.hpp"
#include "runtime_interface.hpp"
#include "xr_generated_dispatch_table.h"
#include "xr_dependencies.h"
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <cstring>
#include <memory>
#include <new>
#include <string>
#include <unordered_map>
// Automatically generated instance trampolines and terminators
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetInstanceProperties(
XrInstance instance,
XrInstanceProperties* instanceProperties) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetInstanceProperties");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetInstanceProperties(instance, instanceProperties);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrPollEvent(
XrInstance instance,
XrEventDataBuffer* eventData) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrPollEvent");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->PollEvent(instance, eventData);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrResultToString(
XrInstance instance,
XrResult value,
char buffer[XR_MAX_RESULT_STRING_SIZE]) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrResultToString");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->ResultToString(instance, value, buffer);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrStructureTypeToString(
XrInstance instance,
XrStructureType value,
char buffer[XR_MAX_STRUCTURE_NAME_SIZE]) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrStructureTypeToString");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->StructureTypeToString(instance, value, buffer);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetSystem(
XrInstance instance,
const XrSystemGetInfo* getInfo,
XrSystemId* systemId) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetSystem");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetSystem(instance, getInfo, systemId);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetSystemProperties(
XrInstance instance,
XrSystemId systemId,
XrSystemProperties* properties) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetSystemProperties");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetSystemProperties(instance, systemId, properties);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentBlendModes(
XrInstance instance,
XrSystemId systemId,
XrViewConfigurationType viewConfigurationType,
uint32_t environmentBlendModeCapacityInput,
uint32_t* environmentBlendModeCountOutput,
XrEnvironmentBlendMode* environmentBlendModes) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrEnumerateEnvironmentBlendModes");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->EnumerateEnvironmentBlendModes(instance, systemId, viewConfigurationType, environmentBlendModeCapacityInput, environmentBlendModeCountOutput, environmentBlendModes);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateSession(
XrInstance instance,
const XrSessionCreateInfo* createInfo,
XrSession* session) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrCreateSession");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->CreateSession(instance, createInfo, session);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroySession(
XrSession session) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroySession");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->DestroySession(session);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateReferenceSpaces(
XrSession session,
uint32_t spaceCapacityInput,
uint32_t* spaceCountOutput,
XrReferenceSpaceType* spaces) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrEnumerateReferenceSpaces");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->EnumerateReferenceSpaces(session, spaceCapacityInput, spaceCountOutput, spaces);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateReferenceSpace(
XrSession session,
const XrReferenceSpaceCreateInfo* createInfo,
XrSpace* space) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrCreateReferenceSpace");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->CreateReferenceSpace(session, createInfo, space);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetReferenceSpaceBoundsRect(
XrSession session,
XrReferenceSpaceType referenceSpaceType,
XrExtent2Df* bounds) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetReferenceSpaceBoundsRect");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetReferenceSpaceBoundsRect(session, referenceSpaceType, bounds);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateActionSpace(
XrSession session,
const XrActionSpaceCreateInfo* createInfo,
XrSpace* space) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrCreateActionSpace");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->CreateActionSpace(session, createInfo, space);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpace(
XrSpace space,
XrSpace baseSpace,
XrTime time,
XrSpaceLocation* location) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrLocateSpace");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->LocateSpace(space, baseSpace, time, location);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpace(
XrSpace space) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroySpace");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->DestroySpace(space);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViewConfigurations(
XrInstance instance,
XrSystemId systemId,
uint32_t viewConfigurationTypeCapacityInput,
uint32_t* viewConfigurationTypeCountOutput,
XrViewConfigurationType* viewConfigurationTypes) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrEnumerateViewConfigurations");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->EnumerateViewConfigurations(instance, systemId, viewConfigurationTypeCapacityInput, viewConfigurationTypeCountOutput, viewConfigurationTypes);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetViewConfigurationProperties(
XrInstance instance,
XrSystemId systemId,
XrViewConfigurationType viewConfigurationType,
XrViewConfigurationProperties* configurationProperties) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetViewConfigurationProperties");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetViewConfigurationProperties(instance, systemId, viewConfigurationType, configurationProperties);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViewConfigurationViews(
XrInstance instance,
XrSystemId systemId,
XrViewConfigurationType viewConfigurationType,
uint32_t viewCapacityInput,
uint32_t* viewCountOutput,
XrViewConfigurationView* views) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrEnumerateViewConfigurationViews");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->EnumerateViewConfigurationViews(instance, systemId, viewConfigurationType, viewCapacityInput, viewCountOutput, views);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSwapchainFormats(
XrSession session,
uint32_t formatCapacityInput,
uint32_t* formatCountOutput,
int64_t* formats) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrEnumerateSwapchainFormats");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->EnumerateSwapchainFormats(session, formatCapacityInput, formatCountOutput, formats);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchain(
XrSession session,
const XrSwapchainCreateInfo* createInfo,
XrSwapchain* swapchain) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrCreateSwapchain");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->CreateSwapchain(session, createInfo, swapchain);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroySwapchain(
XrSwapchain swapchain) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroySwapchain");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->DestroySwapchain(swapchain);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSwapchainImages(
XrSwapchain swapchain,
uint32_t imageCapacityInput,
uint32_t* imageCountOutput,
XrSwapchainImageBaseHeader* images) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrEnumerateSwapchainImages");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->EnumerateSwapchainImages(swapchain, imageCapacityInput, imageCountOutput, images);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrAcquireSwapchainImage(
XrSwapchain swapchain,
const XrSwapchainImageAcquireInfo* acquireInfo,
uint32_t* index) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrAcquireSwapchainImage");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->AcquireSwapchainImage(swapchain, acquireInfo, index);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrWaitSwapchainImage(
XrSwapchain swapchain,
const XrSwapchainImageWaitInfo* waitInfo) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrWaitSwapchainImage");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->WaitSwapchainImage(swapchain, waitInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrReleaseSwapchainImage(
XrSwapchain swapchain,
const XrSwapchainImageReleaseInfo* releaseInfo) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrReleaseSwapchainImage");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->ReleaseSwapchainImage(swapchain, releaseInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrBeginSession(
XrSession session,
const XrSessionBeginInfo* beginInfo) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrBeginSession");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->BeginSession(session, beginInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEndSession(
XrSession session) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrEndSession");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->EndSession(session);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrRequestExitSession(
XrSession session) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrRequestExitSession");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->RequestExitSession(session);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrWaitFrame(
XrSession session,
const XrFrameWaitInfo* frameWaitInfo,
XrFrameState* frameState) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrWaitFrame");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->WaitFrame(session, frameWaitInfo, frameState);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrBeginFrame(
XrSession session,
const XrFrameBeginInfo* frameBeginInfo) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrBeginFrame");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->BeginFrame(session, frameBeginInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEndFrame(
XrSession session,
const XrFrameEndInfo* frameEndInfo) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrEndFrame");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->EndFrame(session, frameEndInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrLocateViews(
XrSession session,
const XrViewLocateInfo* viewLocateInfo,
XrViewState* viewState,
uint32_t viewCapacityInput,
uint32_t* viewCountOutput,
XrView* views) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrLocateViews");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->LocateViews(session, viewLocateInfo, viewState, viewCapacityInput, viewCountOutput, views);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrStringToPath(
XrInstance instance,
const char* pathString,
XrPath* path) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrStringToPath");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->StringToPath(instance, pathString, path);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrPathToString(
XrInstance instance,
XrPath path,
uint32_t bufferCapacityInput,
uint32_t* bufferCountOutput,
char* buffer) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrPathToString");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->PathToString(instance, path, bufferCapacityInput, bufferCountOutput, buffer);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateActionSet(
XrInstance instance,
const XrActionSetCreateInfo* createInfo,
XrActionSet* actionSet) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrCreateActionSet");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->CreateActionSet(instance, createInfo, actionSet);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroyActionSet(
XrActionSet actionSet) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroyActionSet");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->DestroyActionSet(actionSet);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateAction(
XrActionSet actionSet,
const XrActionCreateInfo* createInfo,
XrAction* action) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrCreateAction");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->CreateAction(actionSet, createInfo, action);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroyAction(
XrAction action) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroyAction");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->DestroyAction(action);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrSuggestInteractionProfileBindings(
XrInstance instance,
const XrInteractionProfileSuggestedBinding* suggestedBindings) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSuggestInteractionProfileBindings");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->SuggestInteractionProfileBindings(instance, suggestedBindings);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrAttachSessionActionSets(
XrSession session,
const XrSessionActionSetsAttachInfo* attachInfo) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrAttachSessionActionSets");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->AttachSessionActionSets(session, attachInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetCurrentInteractionProfile(
XrSession session,
XrPath topLevelUserPath,
XrInteractionProfileState* interactionProfile) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetCurrentInteractionProfile");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetCurrentInteractionProfile(session, topLevelUserPath, interactionProfile);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStateBoolean(
XrSession session,
const XrActionStateGetInfo* getInfo,
XrActionStateBoolean* state) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetActionStateBoolean");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetActionStateBoolean(session, getInfo, state);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStateFloat(
XrSession session,
const XrActionStateGetInfo* getInfo,
XrActionStateFloat* state) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetActionStateFloat");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetActionStateFloat(session, getInfo, state);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStateVector2f(
XrSession session,
const XrActionStateGetInfo* getInfo,
XrActionStateVector2f* state) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetActionStateVector2f");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetActionStateVector2f(session, getInfo, state);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStatePose(
XrSession session,
const XrActionStateGetInfo* getInfo,
XrActionStatePose* state) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetActionStatePose");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetActionStatePose(session, getInfo, state);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrSyncActions(
XrSession session,
const XrActionsSyncInfo* syncInfo) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSyncActions");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->SyncActions(session, syncInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateBoundSourcesForAction(
XrSession session,
const XrBoundSourcesForActionEnumerateInfo* enumerateInfo,
uint32_t sourceCapacityInput,
uint32_t* sourceCountOutput,
XrPath* sources) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrEnumerateBoundSourcesForAction");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->EnumerateBoundSourcesForAction(session, enumerateInfo, sourceCapacityInput, sourceCountOutput, sources);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetInputSourceLocalizedName(
XrSession session,
const XrInputSourceLocalizedNameGetInfo* getInfo,
uint32_t bufferCapacityInput,
uint32_t* bufferCountOutput,
char* buffer) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetInputSourceLocalizedName");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->GetInputSourceLocalizedName(session, getInfo, bufferCapacityInput, bufferCountOutput, buffer);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrApplyHapticFeedback(
XrSession session,
const XrHapticActionInfo* hapticActionInfo,
const XrHapticBaseHeader* hapticFeedback) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrApplyHapticFeedback");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->ApplyHapticFeedback(session, hapticActionInfo, hapticFeedback);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrStopHapticFeedback(
XrSession session,
const XrHapticActionInfo* hapticActionInfo) XRLOADER_ABI_TRY {
LoaderInstance* loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrStopHapticFeedback");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->StopHapticFeedback(session, hapticActionInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK

View File

@@ -0,0 +1,252 @@
// 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
// *********** THIS FILE IS GENERATED - DO NOT EDIT ***********
// See loader_source_generator.py for modifications
// ************************************************************
// 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
//
// 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: Mark Young <marky@lunarg.com>
//
#pragma once
#include <unordered_map>
#include <thread>
#include <mutex>
#include "xr_dependencies.h"
#include "openxr/openxr.h"
#include "openxr/openxr_platform.h"
#include "loader_interfaces.h"
#include "loader_instance.hpp"
#include "loader_platform.hpp"
#ifdef __cplusplus
extern "C" {
#endif
// Loader manually generated function prototypes
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetInstanceProperties(
XrInstance instance,
XrInstanceProperties* instanceProperties);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrPollEvent(
XrInstance instance,
XrEventDataBuffer* eventData);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrResultToString(
XrInstance instance,
XrResult value,
char buffer[XR_MAX_RESULT_STRING_SIZE]);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrStructureTypeToString(
XrInstance instance,
XrStructureType value,
char buffer[XR_MAX_STRUCTURE_NAME_SIZE]);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetSystem(
XrInstance instance,
const XrSystemGetInfo* getInfo,
XrSystemId* systemId);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetSystemProperties(
XrInstance instance,
XrSystemId systemId,
XrSystemProperties* properties);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentBlendModes(
XrInstance instance,
XrSystemId systemId,
XrViewConfigurationType viewConfigurationType,
uint32_t environmentBlendModeCapacityInput,
uint32_t* environmentBlendModeCountOutput,
XrEnvironmentBlendMode* environmentBlendModes);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateSession(
XrInstance instance,
const XrSessionCreateInfo* createInfo,
XrSession* session);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroySession(
XrSession session);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateReferenceSpaces(
XrSession session,
uint32_t spaceCapacityInput,
uint32_t* spaceCountOutput,
XrReferenceSpaceType* spaces);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateReferenceSpace(
XrSession session,
const XrReferenceSpaceCreateInfo* createInfo,
XrSpace* space);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetReferenceSpaceBoundsRect(
XrSession session,
XrReferenceSpaceType referenceSpaceType,
XrExtent2Df* bounds);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateActionSpace(
XrSession session,
const XrActionSpaceCreateInfo* createInfo,
XrSpace* space);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpace(
XrSpace space,
XrSpace baseSpace,
XrTime time,
XrSpaceLocation* location);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpace(
XrSpace space);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViewConfigurations(
XrInstance instance,
XrSystemId systemId,
uint32_t viewConfigurationTypeCapacityInput,
uint32_t* viewConfigurationTypeCountOutput,
XrViewConfigurationType* viewConfigurationTypes);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetViewConfigurationProperties(
XrInstance instance,
XrSystemId systemId,
XrViewConfigurationType viewConfigurationType,
XrViewConfigurationProperties* configurationProperties);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViewConfigurationViews(
XrInstance instance,
XrSystemId systemId,
XrViewConfigurationType viewConfigurationType,
uint32_t viewCapacityInput,
uint32_t* viewCountOutput,
XrViewConfigurationView* views);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSwapchainFormats(
XrSession session,
uint32_t formatCapacityInput,
uint32_t* formatCountOutput,
int64_t* formats);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchain(
XrSession session,
const XrSwapchainCreateInfo* createInfo,
XrSwapchain* swapchain);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroySwapchain(
XrSwapchain swapchain);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSwapchainImages(
XrSwapchain swapchain,
uint32_t imageCapacityInput,
uint32_t* imageCountOutput,
XrSwapchainImageBaseHeader* images);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrAcquireSwapchainImage(
XrSwapchain swapchain,
const XrSwapchainImageAcquireInfo* acquireInfo,
uint32_t* index);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrWaitSwapchainImage(
XrSwapchain swapchain,
const XrSwapchainImageWaitInfo* waitInfo);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrReleaseSwapchainImage(
XrSwapchain swapchain,
const XrSwapchainImageReleaseInfo* releaseInfo);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrBeginSession(
XrSession session,
const XrSessionBeginInfo* beginInfo);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEndSession(
XrSession session);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrRequestExitSession(
XrSession session);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrWaitFrame(
XrSession session,
const XrFrameWaitInfo* frameWaitInfo,
XrFrameState* frameState);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrBeginFrame(
XrSession session,
const XrFrameBeginInfo* frameBeginInfo);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEndFrame(
XrSession session,
const XrFrameEndInfo* frameEndInfo);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrLocateViews(
XrSession session,
const XrViewLocateInfo* viewLocateInfo,
XrViewState* viewState,
uint32_t viewCapacityInput,
uint32_t* viewCountOutput,
XrView* views);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrStringToPath(
XrInstance instance,
const char* pathString,
XrPath* path);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrPathToString(
XrInstance instance,
XrPath path,
uint32_t bufferCapacityInput,
uint32_t* bufferCountOutput,
char* buffer);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateActionSet(
XrInstance instance,
const XrActionSetCreateInfo* createInfo,
XrActionSet* actionSet);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroyActionSet(
XrActionSet actionSet);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateAction(
XrActionSet actionSet,
const XrActionCreateInfo* createInfo,
XrAction* action);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroyAction(
XrAction action);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrSuggestInteractionProfileBindings(
XrInstance instance,
const XrInteractionProfileSuggestedBinding* suggestedBindings);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrAttachSessionActionSets(
XrSession session,
const XrSessionActionSetsAttachInfo* attachInfo);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetCurrentInteractionProfile(
XrSession session,
XrPath topLevelUserPath,
XrInteractionProfileState* interactionProfile);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStateBoolean(
XrSession session,
const XrActionStateGetInfo* getInfo,
XrActionStateBoolean* state);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStateFloat(
XrSession session,
const XrActionStateGetInfo* getInfo,
XrActionStateFloat* state);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStateVector2f(
XrSession session,
const XrActionStateGetInfo* getInfo,
XrActionStateVector2f* state);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetActionStatePose(
XrSession session,
const XrActionStateGetInfo* getInfo,
XrActionStatePose* state);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrSyncActions(
XrSession session,
const XrActionsSyncInfo* syncInfo);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateBoundSourcesForAction(
XrSession session,
const XrBoundSourcesForActionEnumerateInfo* enumerateInfo,
uint32_t sourceCapacityInput,
uint32_t* sourceCountOutput,
XrPath* sources);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetInputSourceLocalizedName(
XrSession session,
const XrInputSourceLocalizedNameGetInfo* getInfo,
uint32_t bufferCapacityInput,
uint32_t* bufferCountOutput,
char* buffer);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrApplyHapticFeedback(
XrSession session,
const XrHapticActionInfo* hapticActionInfo,
const XrHapticBaseHeader* hapticFeedback);
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrStopHapticFeedback(
XrSession session,
const XrHapticActionInfo* hapticActionInfo);
#ifdef __cplusplus
} // extern "C"
#endif