From af6aeca2683195c0c1758149018a96094f970dd6 Mon Sep 17 00:00:00 2001 From: dengkail Date: Wed, 5 Jul 2023 14:52:12 +0800 Subject: [PATCH] OpenXR loader: add API layer discovery support. --- src/loader/android_utilities.cpp | 193 +++++++++++++++++++++++++++++-- src/loader/android_utilities.h | 12 ++ src/loader/manifest_file.cpp | 121 +++++++++++++++++++ src/loader/manifest_file.hpp | 2 + src/loader/runtime_interface.cpp | 19 +++ src/loader/runtime_interface.hpp | 1 + 6 files changed, 341 insertions(+), 7 deletions(-) diff --git a/src/loader/android_utilities.cpp b/src/loader/android_utilities.cpp index 11c112dc6..9f94b5180 100644 --- a/src/loader/android_utilities.cpp +++ b/src/loader/android_utilities.cpp @@ -38,8 +38,12 @@ 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 auto API_LAYERS_PATH = "api_layer"; +constexpr auto IMP_LAYER = "implicit"; +constexpr auto EXP_LAYER = "explicit"; constexpr const char *getBrokerAuthority(bool systemBroker) { return systemBroker ? SYSTEM_AUTHORITY : AUTHORITY; } +constexpr const char *getLayerTypePath(bool isImplicitLayer) { return isImplicitLayer ? IMP_LAYER : EXP_LAYER; } struct BaseColumns { /** @@ -164,6 +168,51 @@ struct Columns : BaseColumns { }; } // namespace functions +namespace api_layer { +/** + * Final path component to this URI. + */ + +/** + * Create a content URI for querying all rows of the implicit/explicit API layer 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 layerType The layer type of the API layer. + * @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 &layerType, 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(API_LAYERS_PATH) + .appendPath(getLayerTypePath(layerType == IMP_LAYER)); + return builder.build(); +} +struct Columns : BaseColumns { + // implicit or explicit + static constexpr auto FILE_FORMAT_VERSION = "file_format_version"; + static constexpr auto NAME = "name"; + static constexpr auto NATIVE_LIB_DIR = "native_lib_dir"; + static constexpr auto SO_FILENAME = "so_filename"; + static constexpr auto API_VERSION = "api_version"; + static constexpr auto IMPLEMENTATION_VERSION = "implementation_version"; + static constexpr auto DESCRIPTION = "description"; + static constexpr auto ENABLE_ENVIRONMENT = "enable_environment"; + static constexpr auto DISABLE_ENVIRONMENT = "disable_environment"; + static constexpr auto FUNCTIONS = "functions"; + // extensions names will be combined like "extension1&extension2" + static constexpr auto INSTANCE_EXTENSION_NAMES = "instance_extension_names"; + static constexpr auto INSTANCE_EXTENSION_VERSIONS = "instance_extension_versions"; +}; +} // namespace api_layer + } // namespace static inline jni::Array makeArray(std::initializer_list &&list) { @@ -245,11 +294,13 @@ static int populateFunctions(wrap::android::content::Context const &context, boo 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 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()); +/// Get cursor for active runtime or API layer, parameterized by target type and whether or not we use the system broker +static bool getCursor(wrap::android::content::Context const &context, jni::Array const &projection, + std::string const &targetType, bool systemBroker, Cursor &cursor) { + auto uri = (targetType == RUNTIMES_PATH) + ? active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI) + : api_layer::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), targetType, ABI); + ALOGI("getCursor: Querying URI: %s", uri.toString().c_str()); try { cursor = context.getContentResolver().query(uri, projection); } catch (const std::exception &e) { @@ -279,10 +330,10 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte // First, try getting the installable broker's provider bool systemBroker = false; Cursor cursor; - if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) { + if (!getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor)) { // OK, try the system broker as a fallback. systemBroker = true; - getActiveRuntimeCursor(context, projection, systemBroker, cursor); + getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor); } if (cursor.isNull()) { @@ -314,6 +365,134 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte virtualManifest = builder.build(); return 0; } + +static bool populateApiLayerManifest(std::string layerType, Cursor &cursor, std::vector &layerRootNode) { + auto fileFormatVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::FILE_FORMAT_VERSION)); + auto name = cursor.getString(cursor.getColumnIndex(api_layer::Columns::NAME)); + auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR)); + auto fileName = cursor.getString(cursor.getColumnIndex(api_layer::Columns::SO_FILENAME)); + auto apiVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::API_VERSION)); + auto imllementationVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::IMPLEMENTATION_VERSION)); + auto description = cursor.getString(cursor.getColumnIndex(api_layer::Columns::DESCRIPTION)); + auto disableEnv = cursor.getString(cursor.getColumnIndex(api_layer::Columns::DISABLE_ENVIRONMENT)); + auto enableEnv = cursor.getString(cursor.getColumnIndex(api_layer::Columns::ENABLE_ENVIRONMENT)); + auto extensionNames = cursor.getString(cursor.getColumnIndex(api_layer::Columns::INSTANCE_EXTENSION_NAMES)); + auto extensionVersions = cursor.getString(cursor.getColumnIndex(api_layer::Columns::INSTANCE_EXTENSION_VERSIONS)); + auto functions = cursor.getString(cursor.getColumnIndex(api_layer::Columns::FUNCTIONS)); + + __android_log_print(ANDROID_LOG_INFO, TAG, "Got api layer: type: %s, name: %s, native lib dir: %s, fileName: %s, functions: %s", + layerType.c_str(), name.c_str(), libDir.c_str(), fileName.c_str(), functions.c_str()); + + Json::Value rootNode(Json::objectValue); + rootNode["file_format_version"] = fileFormatVersion; + rootNode["api_layer"] = Json::objectValue; + rootNode["api_layer"]["name"] = name; + rootNode["api_layer"]["library_path"] = libDir + "/" + fileName; + rootNode["api_layer"]["api_version"] = apiVersion; + rootNode["api_layer"]["implementation_version"] = imllementationVersion; + rootNode["api_layer"]["description"] = description; + rootNode["api_layer"]["disable_environment"] = disableEnv; + rootNode["api_layer"]["enable_environment"] = enableEnv; + + rootNode["api_layer"]["instance_extensions"] = Json::Value(Json::arrayValue); + std::vector nameVec; + std::vector versionVec; + // extract extension names + std::istringstream issNames(extensionNames); + std::string item; + while (std::getline(issNames, item, '&')) { + nameVec.push_back(item); + } + // extract extension versions + std::istringstream issVersions(extensionVersions); + while (std::getline(issVersions, item, '&')) { + versionVec.push_back(item); + } + + Json::Value extension(Json::objectValue); + if (nameVec.size() == versionVec.size()) { + for (int i = 0; i < nameVec.size(); ++i) { + extension["name"] = nameVec[i]; + extension["extension_version"] = versionVec[i]; + rootNode["api_layer"]["instance_extensions"].append(extension); + __android_log_print(ANDROID_LOG_INFO, TAG, "extension name: %s, extension version: %s", nameVec[i].c_str(), + versionVec[i].c_str()); + } + } else { + __android_log_print(ANDROID_LOG_INFO, TAG, "api layer extension name not match extension version!"); + } + + std::vector functionNameVec; + std::vector symbolVec; + std::istringstream issFunctions(functions); + while (std::getline(issFunctions, item, '&')) { + std::size_t pos = item.find(':'); + if (pos != item.npos) { + functionNameVec.push_back(item.substr(0, pos)); + symbolVec.push_back(item.substr(pos + 1, item.size())); + } + } + + rootNode["api_layer"]["functions"] = Json::Value(Json::objectValue); + if (functions != "None") { + for (int i = 0; i < functionNameVec.size(); ++i) { + rootNode["api_layer"]["functions"][functionNameVec[i]] = symbolVec[i]; + __android_log_print(ANDROID_LOG_INFO, TAG, "function name: %s, symbol: %s", functionNameVec[i].c_str(), + symbolVec[i].c_str()); + } + } else { + __android_log_print(ANDROID_LOG_INFO, TAG, "functions field not existed!"); + } + + layerRootNode.push_back(rootNode); + + return true; +} + +static bool getApiLayerCursor(std::string layerType, wrap::android::content::Context const &context, + jni::Array &projection, std::vector &virtualManifests) { + Cursor cursor; + + getCursor(context, projection, layerType, false, cursor); + if (cursor.isNull()) { + return false; + } + + cursor.moveToFirst(); + for (int i = 0; i < cursor.getCount(); ++i) { + populateApiLayerManifest(layerType, cursor, virtualManifests); + cursor.moveToNext(); + } + cursor.close(); + + return true; +} + +int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context, + std::vector &virtualManifests) { + static bool hasQueryBroker = false; + static std::vector implicitLayerManifest; + static std::vector explicitLayerManifest; + + __android_log_print(ANDROID_LOG_INFO, TAG, "Try to get %s API layer from broker!", layerType.c_str()); + if (!hasQueryBroker) { + jni::Array projection = + makeArray({api_layer::Columns::FILE_FORMAT_VERSION, api_layer::Columns::NAME, api_layer::Columns::NATIVE_LIB_DIR, + api_layer::Columns::SO_FILENAME, api_layer::Columns::API_VERSION, api_layer::Columns::IMPLEMENTATION_VERSION, + api_layer::Columns::DESCRIPTION, api_layer::Columns::ENABLE_ENVIRONMENT, + api_layer::Columns::DISABLE_ENVIRONMENT, api_layer::Columns::FUNCTIONS, + api_layer::Columns::INSTANCE_EXTENSION_NAMES, api_layer::Columns::INSTANCE_EXTENSION_VERSIONS}); + + getApiLayerCursor(IMP_LAYER, context, projection, implicitLayerManifest); + getApiLayerCursor(EXP_LAYER, context, projection, explicitLayerManifest); + + hasQueryBroker = true; + } + + virtualManifests = (layerType == IMP_LAYER) ? implicitLayerManifest : explicitLayerManifest; + return 0; +} + } // namespace openxr_android #endif // __ANDROID__ diff --git a/src/loader/android_utilities.h b/src/loader/android_utilities.h index f66c9bf1d..3356fd95e 100644 --- a/src/loader/android_utilities.h +++ b/src/loader/android_utilities.h @@ -27,6 +27,18 @@ using wrap::android::content::Context; * @return 0 on success, something else on failure. */ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest); + +/*! + * 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 &virtualManifests); } // namespace openxr_android #endif // __ANDROID__ diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index bba6b8ca0..889bd4861 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -723,6 +723,112 @@ void ApiLayerManifestFile::AddManifestFilesAndroid(ManifestFileType type, } #endif // XR_USE_PLATFORM_ANDROID +void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const Json::Value &root_node, const std::string &filename, + std::vector> &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; + // 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. + // 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> &manifest_files) { @@ -836,6 +942,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); } @@ -943,5 +1050,19 @@ XrResult ApiLayerManifestFile::FindManifestFiles(ManifestFileType type, ApiLayerManifestFile::AddManifestFilesAndroid(type, manifest_files); #endif // XR_USE_PLATFORM_ANDROID +#if defined(XR_KHR_LOADER_INIT_SUPPORT) + std::vector virtualManifests; + std::string layerType = (type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER) ? "implicit" : "explicit"; + XrResult result = GetPlatformApiLayerVirtualManifests(layerType, virtualManifests); + if (XR_SUCCESS == result) { + for (int i = 0; i < virtualManifests.size(); ++i) { + ApiLayerManifestFile::CreateIfValid(type, virtualManifests[i], "virtual manifest", manifest_files); + } + } else { + LoaderLogger::LogErrorMessage("", "ApiLayerManifestFile::FindManifestFiles - faile to get virtual manifest files."); + assert(0); + } +#endif // XR_KHR_LOADER_INIT_SUPPORT + return XR_SUCCESS; } diff --git a/src/loader/manifest_file.hpp b/src/loader/manifest_file.hpp index de0aab65c..a6b857eaf 100644 --- a/src/loader/manifest_file.hpp +++ b/src/loader/manifest_file.hpp @@ -99,6 +99,8 @@ class ApiLayerManifestFile : public ManifestFile { static void CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream, LibraryLocator locate_library, std::vector> &manifest_files); + static void CreateIfValid(ManifestFileType type, const Json::Value &root_node, const std::string &filename, + std::vector> &manifest_files); static void CreateIfValid(ManifestFileType type, const std::string &filename, std::vector> &manifest_files); /// @return false if we could not find the library. diff --git a/src/loader/runtime_interface.cpp b/src/loader/runtime_interface.cpp index cb1a98af7..052c1336d 100644 --- a/src/loader/runtime_interface.cpp +++ b/src/loader/runtime_interface.cpp @@ -166,6 +166,25 @@ XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) { out_manifest = virtualManifest; return XR_SUCCESS; } + +XrResult GetPlatformApiLayerVirtualManifests(std::string type, std::vector& out_manifest) { + using wrap::android::content::Context; + auto& initData = LoaderInitData::instance(); + if (!initData.initialized()) { + return XR_ERROR_INITIALIZATION_FAILED; + } + auto context = Context(reinterpret_cast(initData.getData().applicationContext)); + if (context.isNull()) { + return XR_ERROR_INITIALIZATION_FAILED; + } + std::vector virtualManifests; + if (0 != openxr_android::getApiLayerVirtualManifests(type, context, virtualManifests)) { + return XR_ERROR_INITIALIZATION_FAILED; + } + out_manifest = virtualManifests; + return XR_SUCCESS; +} + #endif // XR_USE_PLATFORM_ANDROID XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command, diff --git a/src/loader/runtime_interface.hpp b/src/loader/runtime_interface.hpp index 8d55ec674..3bc159db2 100644 --- a/src/loader/runtime_interface.hpp +++ b/src/loader/runtime_interface.hpp @@ -31,6 +31,7 @@ class Value; //! Initialize loader, where required. XrResult InitializeLoader(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo); XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest); +XrResult GetPlatformApiLayerVirtualManifests(std::string type, std::vector& out_manifest); std::string GetAndroidNativeLibraryDir(); void* Android_Get_Asset_Manager(); #endif