Skip to content

Commit

Permalink
OpenXR loader: add API layer discovery support.
Browse files Browse the repository at this point in the history
Loader currently did not support API layer disconvery yet. This patch
support this feature.

Loader will try to get all implicit/explicit API layers from broker supported by
chosen active runtime and populate virtual API layer manifests from returned cursor.
Here is authority and path used to find correct cursor:
Loader ---> broker

authority:org.khronos.openxr.runtime_broker
path:/openxr/major_ver/abi/[abi]/api_layer/[implicit:explicit]
  • Loading branch information
dengkail committed Jul 5, 2023
1 parent 2c252a3 commit 3821979
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 7 deletions.
197 changes: 190 additions & 7 deletions src/loader/android_utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down Expand Up @@ -164,6 +168,52 @@ 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<std::string> makeArray(std::initializer_list<const char *> &&list) {
Expand Down Expand Up @@ -245,11 +295,12 @@ 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<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());
/// 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<std::string> 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) {
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -314,6 +365,138 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte
virtualManifest = builder.build();
return 0;
}

static bool populateApiLayerManifest(std::string layerType, Cursor& cursor, std::vector<Json::Value>& 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<std::string> nameVec;
std::vector<std::string> 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<std::string> functionNameVec;
std::vector<std::string> 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<std::string> &projection,
std::vector<Json::Value> &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<Json::Value> &virtualManifests) {
static bool hasQueryBroker = false;
static std::vector<Json::Value> implicitLayerManifest;
static std::vector<Json::Value> explicitLayerManifest;

__android_log_print(ANDROID_LOG_INFO, TAG, "Try to get %s API layer from broker!", layerType.c_str());
if(!hasQueryBroker){
jni::Array<std::string> 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__
11 changes: 11 additions & 0 deletions src/loader/android_utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ 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<Json::Value> &virtualManifests);
} // namespace openxr_android

#endif // __ANDROID__
121 changes: 121 additions & 0 deletions src/loader/manifest_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<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;
// 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<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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<Json::Value> 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;
}
Loading

0 comments on commit 3821979

Please sign in to comment.