Skip to content

Commit

Permalink
OpenXR loader: add API layer discovery support.
Browse files Browse the repository at this point in the history
  • Loading branch information
quic-dengkail committed Mar 22, 2024
1 parent 99256e9 commit 8cbba72
Show file tree
Hide file tree
Showing 7 changed files with 535 additions and 42 deletions.
11 changes: 11 additions & 0 deletions src/common/platform_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ static inline bool PlatformUtilsSetEnv(const char* name, const char* value) {
}

#elif defined(XR_OS_ANDROID)
#include <sys/system_properties.h>

static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {
// Stub func
Expand All @@ -370,6 +371,16 @@ static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* va
return false;
}

static inline bool PlatformUtilsGetSysProp(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
334 changes: 298 additions & 36 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__
4 changes: 3 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,8 @@ class LoaderInitData {
XrResult InitializeLoaderInitData(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo);

#ifdef XR_USE_PLATFORM_ANDROID
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest);
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFileSource& runtime_source);
XrResult GetPlatformApiLayerVirtualManifests(std::string type, std::vector<Json::Value>& out_manifest, bool system_broker);
std::string GetAndroidNativeLibraryDir();
void* Android_Get_Asset_Manager();
#endif // XR_USE_PLATFORM_ANDROID
Expand Down
176 changes: 174 additions & 2 deletions src/loader/manifest_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,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 @@ -763,6 +764,140 @@ 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 Json::Value &root_node, const std::string &filename,
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
std::ostringstream error_ss("ApiLayerManifestFile::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;
}

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;
#if !defined(XR_OS_ANDROID)
// 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;
}
#else
// Implicit layers require system property to disable it on Android
if (layer_root_node["disable_sys_prop"].isNull() || !layer_root_node["disable_sys_prop"].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
if (!layer_root_node["enable_sys_prop"].isNull() && layer_root_node["enable_sys_prop"].isString()) {
std::string enable_sys_prop = layer_root_node["enable_sys_prop"].asString();
// If it's not set to true, disable this layer
if (!PlatformUtilsGetSysProp(enable_sys_prop.c_str(), true)) {
enabled = false;
}
}

std::string disable_sys_prop = layer_root_node["disable_sys_prop"].asString();
if (PlatformUtilsGetSysProp(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";
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;
}

char *end_ptr;
uint32_t implementation_version = strtol(layer_root_node["implementation_version"].asString().c_str(), &end_ptr, 10);
if (*end_ptr != '\0') {
error_ss << "layer " << filename << " has invalid implementation version.";
LoaderLogger::LogWarningMessage("", error_ss.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.
// Handle 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) {
Expand Down Expand Up @@ -876,6 +1011,7 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));

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

Expand Down Expand Up @@ -935,6 +1071,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 virtualManifest;
bool systemBroker = true;
ManifestFileSource runtimeSource = ManifestFileSource::FROM_JSON_MANIFEST;
XrResult result = GetPlatformRuntimeVirtualManifest(virtualManifest, runtimeSource);
if (XR_SUCCESS == result) {
if (runtimeSource == ManifestFileSource::FROM_INSTALLABLE_BROKER) {
systemBroker = 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 @@ -967,7 +1121,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 @@ -981,6 +1137,22 @@ 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> virtualManifests;
std::string layerType = (type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER) ? "implicit" : "explicit";
result = GetPlatformApiLayerVirtualManifests(layerType, virtualManifests, systemBroker);
if (XR_SUCCESS == result) {
for (const auto &virtualManifest : virtualManifests) {
ApiLayerManifestFile::CreateIfValid(type, virtualManifest, "virtual manifest", manifest_files);
}
} else {
LoaderLogger::LogErrorMessage(
"",
"ApiLayerManifestFile::FindManifestFiles - failed to get virtual manifest files from system/installable broker.");
assert(0);
}
}

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

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

enum ManifestFileSource {
FROM_JSON_MANIFEST = 0,
FROM_INSTALLABLE_BROKER,
FROM_SYSTEM_BROKER,
};

struct JsonVersion {
uint32_t major;
uint32_t minor;
Expand Down Expand Up @@ -101,6 +107,8 @@ class ApiLayerManifestFile : public ManifestFile {

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 Json::Value &root_node, const std::string &filename,
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
static void CreateIfValid(ManifestFileType type, const std::string &filename,
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
/// @return false if we could not find the library.
Expand Down
29 changes: 27 additions & 2 deletions src/loader/runtime_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#endif // XR_USE_PLATFORM_ANDROID

#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFileSource& runtime_source) {
using wrap::android::content::Context;
auto& initData = LoaderInitData::instance();
if (!initData.initialized()) {
Expand All @@ -45,13 +45,38 @@ XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {
if (context.isNull()) {
return XR_ERROR_INITIALIZATION_FAILED;
}
bool systemBroker = false;
Json::Value virtualManifest;
if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) {
if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest, systemBroker)) {
runtime_source = ManifestFileSource::FROM_JSON_MANIFEST;
return XR_ERROR_INITIALIZATION_FAILED;
}
if (systemBroker) {
runtime_source = ManifestFileSource::FROM_SYSTEM_BROKER;
} else {
runtime_source = ManifestFileSource::FROM_INSTALLABLE_BROKER;
}
out_manifest = virtualManifest;
return XR_SUCCESS;
}

XrResult GetPlatformApiLayerVirtualManifests(std::string type, std::vector<Json::Value>& out_manifest, bool system_broker) {
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;
}
std::vector<Json::Value> virtualManifests;
if (0 != openxr_android::getApiLayerVirtualManifests(type, context, virtualManifests, system_broker)) {
return XR_ERROR_INITIALIZATION_FAILED;
}
out_manifest = virtualManifests;
return XR_SUCCESS;
}
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)

XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command,
Expand Down

0 comments on commit 8cbba72

Please sign in to comment.