Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenXR loader: add API layer discovery support. #413

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changes/sdk/mr.3475.gl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
- pr.413.gh.OpenXR-SDK-Source
---
Improvement: Loader: Code cleanup and robustness improvements.
10 changes: 10 additions & 0 deletions src/common/platform_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,16 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) {
return {};
}

static inline bool PlatformUtilsGetBoolSysProp(const char* name, bool default_value) {
bool result = default_value;
char value[PROP_VALUE_MAX] = {};
if (__system_property_get(name, value) != 0) {
result = (value[0] == 't');
}

return result;
}

// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
// Prefix for the runtime JSON file name
Expand Down
489 changes: 376 additions & 113 deletions src/loader/android_utilities.cpp

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion src/loader/android_utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,20 @@ using wrap::android::content::Context;
*
* @return 0 on success, something else on failure.
*/
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest);
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest,
bool &systemBroker);

/*!
* Find the implicit/explicit API layers on the system, and return a constructed JSON object representing it.
*
* @param type An String to indicate layer type of API layers, implicit or explicit.
* @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 getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context,
std::vector<Json::Value> &virtualManifests, bool systemBroker);
} // namespace openxr_android

#endif // __ANDROID__
7 changes: 4 additions & 3 deletions src/loader/api_layer_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,9 +422,10 @@ XrResult ApiLayerInterface::LoadApiLayers(const std::string& openxr_command, uin
}

// 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));
std::unique_ptr<ApiLayerInterface> iface{new ApiLayerInterface(manifest_file->LayerName(), layer_library,
supported_extensions, api_layer_info.getInstanceProcAddr,
api_layer_info.createApiLayerInstance)};
api_layer_interfaces.emplace_back(std::move(iface));

// If we load one, clear all errors.
any_loaded = true;
Expand Down
9 changes: 8 additions & 1 deletion src/loader/loader_init_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <json/value.h>
#include <android/asset_manager_jni.h>
#include "android_utilities.h"
#include "manifest_file.hpp"
#endif // XR_USE_PLATFORM_ANDROID

#ifdef XR_KHR_LOADER_INIT_SUPPORT
Expand Down Expand Up @@ -84,7 +85,13 @@ class LoaderInitData {
XrResult InitializeLoaderInitData(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo);

#ifdef XR_USE_PLATFORM_ANDROID
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest);

//! Modifies @p out_manifest and @p out_runtime_source only if returning successfully
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFileSource& out_runtime_source);

//! Modifies @p out_manifest only if returning successfully
XrResult GetPlatformApiLayerVirtualManifests(bool is_implicit, bool system_broker, std::vector<Json::Value>& out_manifest);

std::string GetAndroidNativeLibraryDir();
void* Android_Get_Asset_Manager();
#endif // XR_USE_PLATFORM_ANDROID
Expand Down
168 changes: 141 additions & 27 deletions src/loader/manifest_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -638,10 +638,10 @@ void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std:
}

// Add this runtime manifest file
manifest_files.emplace_back(new RuntimeManifestFile(filename, lib_path));
std::unique_ptr<RuntimeManifestFile> manifest{new RuntimeManifestFile(filename, lib_path)};
manifest_files.emplace_back(std::move(manifest));

// Add any extensions to it after the fact.
// Handle any renamed functions
// Add any extensions to it after the fact, while handling any renamed functions
manifest_files.back()->ParseCommon(runtime_root_node);
}

Expand Down Expand Up @@ -682,7 +682,8 @@ XrResult RuntimeManifestFile::FindManifestFiles(const std::string &openxr_comman

#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
Json::Value virtualManifest;
result = GetPlatformRuntimeVirtualManifest(virtualManifest);
ManifestFileSource runtimeSource = ManifestFileSource::FROM_JSON_MANIFEST;
result = GetPlatformRuntimeVirtualManifest(virtualManifest, runtimeSource);
if (XR_SUCCESS == result) {
RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files);
return result;
Expand Down Expand Up @@ -778,22 +779,10 @@ void ApiLayerManifestFile::AddManifestFilesAndroid(const std::string &openxr_com
}
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)

void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream,
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, const Json::Value &root_node,
LibraryLocator locate_library,
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
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.";
Expand All @@ -813,29 +802,72 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}

// Figure out enabled state of implicit layers
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()) {
auto &disable_env_node = layer_root_node["disable_environment"];
if (disable_env_node.isNull() || !disable_env_node.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();
// Check if there's an enable environment variable provided: If so, it must be set in the environment.
auto &enable_env_node = layer_root_node["enable_environment"];
if (!enable_env_node.isNull() && enable_env_node.isString()) {
std::string env_var = enable_env_node.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();
std::string env_var = disable_env_node.asString();
// If the env var is set, disable the layer. Disable env var overrides enable above
if (PlatformUtilsGetEnvSet(env_var.c_str())) {
enabled = false;
}

#if defined(XR_OS_ANDROID)

// Implicit layers require the disable system property on Android.
auto &disable_prop_node = layer_root_node["disable_sys_prop"];
if (disable_prop_node.isNull() || !disable_prop_node.isString()) {
error_ss << "Implicit layer " << filename << " is missing \"disable_sys_prop\"";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}

// Check if there's an system property to enable this API layer
auto &enable_prop_node = layer_root_node["enable_sys_prop"];
if (!enable_prop_node.isNull() && enable_prop_node.isString()) {
std::string enable_sys_prop = enable_prop_node.asString();
if (enable_sys_prop.empty()) {
error_ss << "Implicit layer " << filename << " has a present but empty \"enable_sys_prop\"";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
// TODO other validation on the enable_sys_prop?
// If it's not set to true, disable this layer
if (!PlatformUtilsGetBoolSysProp(enable_sys_prop.c_str(), true)) {
enabled = false;
}
}

std::string disable_sys_prop = disable_prop_node.asString();
if (disable_sys_prop.empty()) {
error_ss << "Implicit layer " << filename << " has a present but empty \"disable_sys_prop\"";
LoaderLogger::LogErrorMessage("", error_ss.str());
return;
}
// TODO other validation on the disable_sys_prop?
if (PlatformUtilsGetBoolSysProp(disable_sys_prop.c_str(), false)) {
enabled = false;
}
#endif

// Not enabled, so pretend like it isn't even there.
if (!enabled) {
error_ss << "Implicit layer " << filename << " is disabled";
Expand All @@ -856,7 +888,16 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin
return;
}

uint32_t implementation_version = atoi(layer_root_node["implementation_version"].asString().c_str());
uint32_t implementation_version = 0;
{
char *end_ptr;
implementation_version = strtol(layer_root_node["implementation_version"].asString().c_str(), &end_ptr, 10);
if (*end_ptr != '\0') {
std::ostringstream oss(error_ss.str());
oss << "layer " << filename << " has invalid implementation version.";
LoaderLogger::LogWarningMessage("", oss.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
Expand Down Expand Up @@ -887,13 +928,34 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin
}

// Add this layer manifest file
manifest_files.emplace_back(
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
std::unique_ptr<ApiLayerManifestFile> manifest{
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path)};
manifest_files.emplace_back(std::move(manifest));

// Add any extensions to it after the fact.
// Add any extensions to it after the fact, while handling any renamed functions
manifest_files.back()->ParseCommon(layer_root_node);
}

void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream,
LibraryLocator locate_library,
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
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;
}

CreateIfValid(type, filename, root_node, locate_library, manifest_files);
}

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);
Expand Down Expand Up @@ -950,6 +1012,24 @@ void ApiLayerManifestFile::PopulateApiLayerProperties(XrApiLayerProperties &prop
// Find all layer manifest files in the appropriate search paths/registries for the given type.
XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_command, ManifestFileType type,
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
bool search_json_layer = true;
bool search_broker_layer = true;

#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
Json::Value virtual_manifest;
bool system_broker = true;
ManifestFileSource runtime_source = ManifestFileSource::FROM_JSON_MANIFEST;
XrResult result = GetPlatformRuntimeVirtualManifest(virtual_manifest, runtime_source);
if (XR_SUCCESS == result) {
if (runtime_source == ManifestFileSource::FROM_INSTALLABLE_BROKER) {
system_broker = false;
search_json_layer = false;
}
} else {
search_broker_layer = false;
}
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)

std::string relative_path;
std::string override_env_var;
std::string registry_location;
Expand Down Expand Up @@ -982,7 +1062,9 @@ XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_comma

bool override_active = false;
std::vector<std::string> filenames;
ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames);
if (search_json_layer) {
ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames);
}

#ifdef XR_OS_WINDOWS
// Read the registry if the override wasn't active.
Expand All @@ -996,6 +1078,38 @@ XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_comma
}

#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
if (search_broker_layer) {
std::vector<Json::Value> virtual_manifests;
result = GetPlatformApiLayerVirtualManifests(type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER, system_broker,
virtual_manifests);
if (XR_SUCCESS == result) {
std::string fnBase;
{
std::ostringstream oss{"_virtualManifest_"};
if (system_broker) {
oss << "systemBroker_";
} else {
oss << "installableBroker_";
}
if (type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER) {
oss << "implicit.json";
} else {
oss << "explicit.json";
}
fnBase = oss.str();
}
const size_t n = virtual_manifests.size();
for (size_t i = 0; i < n; ++i) {
ApiLayerManifestFile::CreateIfValid(type, std::to_string(i) + fnBase, virtual_manifests[i],
&ApiLayerManifestFile::LocateLibraryInAssets, manifest_files);
}
} else {
LoaderLogger::LogInfoMessage(openxr_command,
"ApiLayerManifestFile::FindManifestFiles - failed to get virtual manifest files from "
"system/installable broker, likely not supported by current broker version.");
}
}

ApiLayerManifestFile::AddManifestFilesAndroid(openxr_command, type, manifest_files);
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)

Expand Down
12 changes: 12 additions & 0 deletions src/loader/manifest_file.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ enum ManifestFileType {
MANIFEST_TYPE_EXPLICIT_API_LAYER,
};

//! Where did the data for this manifest file (may be virtual) come from?
enum ManifestFileSource {
//! An actual json file on a file system
FROM_JSON_MANIFEST = 0,
//! The installable runtime broker on Android
FROM_INSTALLABLE_BROKER,
//! The system runtime broker on Android
FROM_SYSTEM_BROKER,
};

struct JsonVersion {
uint32_t major;
uint32_t minor;
Expand Down Expand Up @@ -99,6 +109,8 @@ class ApiLayerManifestFile : public ManifestFile {
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, const Json::Value &root_node,
LibraryLocator locate_library, std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
static void CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream,
LibraryLocator locate_library, std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
static void CreateIfValid(ManifestFileType type, const std::string &filename,
Expand Down
Loading