Skip to content

Commit fb198f9

Browse files
author
dengkail
committed
OpenXR loader: add API layer discovery support.
1 parent f66902f commit fb198f9

6 files changed

+347
-9
lines changed

src/loader/android_utilities.cpp

+184-7
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@ constexpr auto SYSTEM_AUTHORITY = "org.khronos.openxr.system_runtime_broker";
3838
constexpr auto BASE_PATH = "openxr";
3939
constexpr auto ABI_PATH = "abi";
4040
constexpr auto RUNTIMES_PATH = "runtimes";
41+
constexpr auto API_LAYERS_PATH = "api_layer";
42+
constexpr auto IMP_LAYER = "implicit";
43+
constexpr auto EXP_LAYER = "explicit";
4144

4245
constexpr const char *getBrokerAuthority(bool systemBroker) { return systemBroker ? SYSTEM_AUTHORITY : AUTHORITY; }
46+
constexpr const char *getLayerTypePath(bool isImplicitLayer) { return isImplicitLayer ? IMP_LAYER : EXP_LAYER; }
4347

4448
struct BaseColumns {
4549
/**
@@ -164,6 +168,51 @@ struct Columns : BaseColumns {
164168
};
165169
} // namespace functions
166170

171+
namespace api_layer {
172+
/**
173+
* Final path component to this URI.
174+
*/
175+
176+
/**
177+
* Create a content URI for querying all rows of the implicit/explicit API layer data for a given
178+
* runtime package and major version of OpenXR.
179+
*
180+
* @param systemBroker If the system runtime broker (instead of the installable one) should be queried.
181+
* @param majorVer The major version of OpenXR.
182+
* @param layerType The layer type of the API layer.
183+
* @param abi The Android ABI name in use.
184+
* @return A content URI for the entire table: the function remapping for that runtime.
185+
*/
186+
static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const &layerType, const char *abi) {
187+
auto builder = Uri_Builder::construct();
188+
builder.scheme("content")
189+
.authority(getBrokerAuthority(systemBroker))
190+
.appendPath(BASE_PATH)
191+
.appendPath(std::to_string(majorVersion))
192+
.appendPath(ABI_PATH)
193+
.appendPath(abi)
194+
.appendPath(API_LAYERS_PATH)
195+
.appendPath(getLayerTypePath(layerType == IMP_LAYER));
196+
return builder.build();
197+
}
198+
struct Columns : BaseColumns {
199+
// implicit or explicit
200+
static constexpr auto FILE_FORMAT_VERSION = "file_format_version";
201+
static constexpr auto NAME = "name";
202+
static constexpr auto NATIVE_LIB_DIR = "native_lib_dir";
203+
static constexpr auto SO_FILENAME = "so_filename";
204+
static constexpr auto API_VERSION = "api_version";
205+
static constexpr auto IMPLEMENTATION_VERSION = "implementation_version";
206+
static constexpr auto DESCRIPTION = "description";
207+
static constexpr auto ENABLE_ENVIRONMENT = "enable_environment";
208+
static constexpr auto DISABLE_ENVIRONMENT = "disable_environment";
209+
static constexpr auto FUNCTIONS = "functions";
210+
// extensions names will be combined like "extension1&extension2"
211+
static constexpr auto INSTANCE_EXTENSION_NAMES = "instance_extension_names";
212+
static constexpr auto INSTANCE_EXTENSION_VERSIONS = "instance_extension_versions";
213+
};
214+
} // namespace api_layer
215+
167216
} // namespace
168217

169218
static inline jni::Array<std::string> makeArray(std::initializer_list<const char *> &&list) {
@@ -245,11 +294,13 @@ static int populateFunctions(wrap::android::content::Context const &context, boo
245294
return 0;
246295
}
247296

248-
/// Get cursor for active runtime, parameterized by whether or not we use the system broker
249-
static bool getActiveRuntimeCursor(wrap::android::content::Context const &context, jni::Array<std::string> const &projection,
250-
bool systemBroker, Cursor &cursor) {
251-
auto uri = active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI);
252-
ALOGI("getActiveRuntimeCursor: Querying URI: %s", uri.toString().c_str());
297+
/// Get cursor for active runtime or API layer, parameterized by target type and whether or not we use the system broker
298+
static bool getCursor(wrap::android::content::Context const &context, jni::Array<std::string> const &projection,
299+
std::string const &targetType, bool systemBroker, Cursor &cursor) {
300+
auto uri = (targetType == RUNTIMES_PATH)
301+
? active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI)
302+
: api_layer::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), targetType, ABI);
303+
ALOGI("getCursor: Querying URI: %s", uri.toString().c_str());
253304
try {
254305
cursor = context.getContentResolver().query(uri, projection);
255306
} catch (const std::exception &e) {
@@ -279,10 +330,10 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte
279330
// First, try getting the installable broker's provider
280331
bool systemBroker = false;
281332
Cursor cursor;
282-
if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) {
333+
if (!getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor)) {
283334
// OK, try the system broker as a fallback.
284335
systemBroker = true;
285-
getActiveRuntimeCursor(context, projection, systemBroker, cursor);
336+
getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor);
286337
}
287338

288339
if (cursor.isNull()) {
@@ -314,6 +365,132 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte
314365
virtualManifest = builder.build();
315366
return 0;
316367
}
368+
369+
static bool populateApiLayerManifest(std::string layerType, Cursor &cursor, std::vector<Json::Value> &layerRootNode) {
370+
auto fileFormatVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::FILE_FORMAT_VERSION));
371+
auto name = cursor.getString(cursor.getColumnIndex(api_layer::Columns::NAME));
372+
auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR));
373+
auto fileName = cursor.getString(cursor.getColumnIndex(api_layer::Columns::SO_FILENAME));
374+
auto apiVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::API_VERSION));
375+
auto implementationVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::IMPLEMENTATION_VERSION));
376+
auto description = cursor.getString(cursor.getColumnIndex(api_layer::Columns::DESCRIPTION));
377+
auto disableEnv = cursor.getString(cursor.getColumnIndex(api_layer::Columns::DISABLE_ENVIRONMENT));
378+
auto enableEnv = cursor.getString(cursor.getColumnIndex(api_layer::Columns::ENABLE_ENVIRONMENT));
379+
auto extensionNames = cursor.getString(cursor.getColumnIndex(api_layer::Columns::INSTANCE_EXTENSION_NAMES));
380+
auto extensionVersions = cursor.getString(cursor.getColumnIndex(api_layer::Columns::INSTANCE_EXTENSION_VERSIONS));
381+
auto functions = cursor.getString(cursor.getColumnIndex(api_layer::Columns::FUNCTIONS));
382+
383+
__android_log_print(ANDROID_LOG_INFO, TAG, "Got api layer: type: %s, name: %s, native lib dir: %s, fileName: %s, functions: %s",
384+
layerType.c_str(), name.c_str(), libDir.c_str(), fileName.c_str(), functions.c_str());
385+
386+
Json::Value rootNode(Json::objectValue);
387+
rootNode["file_format_version"] = fileFormatVersion;
388+
rootNode["api_layer"] = Json::objectValue;
389+
rootNode["api_layer"]["name"] = name;
390+
rootNode["api_layer"]["library_path"] = libDir + "/" + fileName;
391+
rootNode["api_layer"]["api_version"] = apiVersion;
392+
rootNode["api_layer"]["implementation_version"] = implementationVersion;
393+
rootNode["api_layer"]["description"] = description;
394+
rootNode["api_layer"]["disable_environment"] = disableEnv;
395+
rootNode["api_layer"]["enable_environment"] = enableEnv;
396+
397+
rootNode["api_layer"]["instance_extensions"] = Json::Value(Json::arrayValue);
398+
std::vector<std::string> nameVec;
399+
std::vector<std::string> versionVec;
400+
// extract extension names
401+
std::istringstream issNames(extensionNames);
402+
std::string item;
403+
while (std::getline(issNames, item, '&')) {
404+
nameVec.push_back(item);
405+
}
406+
// extract extension versions
407+
std::istringstream issVersions(extensionVersions);
408+
while (std::getline(issVersions, item, '&')) {
409+
versionVec.push_back(item);
410+
}
411+
412+
Json::Value extension(Json::objectValue);
413+
if (nameVec.size() == versionVec.size()) {
414+
for (int i = 0; i < nameVec.size(); ++i) {
415+
extension["name"] = nameVec[i];
416+
extension["extension_version"] = versionVec[i];
417+
rootNode["api_layer"]["instance_extensions"].append(extension);
418+
__android_log_print(ANDROID_LOG_INFO, TAG, "extension name: %s, extension version: %s", nameVec[i].c_str(),
419+
versionVec[i].c_str());
420+
}
421+
} else {
422+
__android_log_print(ANDROID_LOG_INFO, TAG, "api layer extension name not match extension version!");
423+
}
424+
425+
std::vector<std::string> functionNameVec;
426+
std::vector<std::string> symbolVec;
427+
std::istringstream issFunctions(functions);
428+
while (std::getline(issFunctions, item, '&')) {
429+
std::size_t pos = item.find(':');
430+
if (pos != item.npos) {
431+
functionNameVec.push_back(item.substr(0, pos));
432+
symbolVec.push_back(item.substr(pos + 1, item.size()));
433+
}
434+
}
435+
436+
rootNode["api_layer"]["functions"] = Json::Value(Json::objectValue);
437+
if (functions != "None") {
438+
for (int i = 0; i < functionNameVec.size(); ++i) {
439+
rootNode["api_layer"]["functions"][functionNameVec[i]] = symbolVec[i];
440+
__android_log_print(ANDROID_LOG_INFO, TAG, "function name: %s, symbol: %s", functionNameVec[i].c_str(),
441+
symbolVec[i].c_str());
442+
}
443+
} else {
444+
__android_log_print(ANDROID_LOG_INFO, TAG, "functions field not existed!");
445+
}
446+
447+
layerRootNode.push_back(rootNode);
448+
449+
return true;
450+
}
451+
452+
static void enumerateApiLayerManifests(std::string layerType, wrap::android::content::Context const &context,
453+
jni::Array<std::string> &projection, std::vector<Json::Value> &virtualManifests) {
454+
Cursor cursor;
455+
456+
getCursor(context, projection, layerType, false, cursor);
457+
if (cursor.isNull()) {
458+
return;
459+
}
460+
cursor.moveToFirst();
461+
for (int i = 0; i < cursor.getCount(); ++i) {
462+
populateApiLayerManifest(layerType, cursor, virtualManifests);
463+
cursor.moveToNext();
464+
}
465+
466+
cursor.close();
467+
}
468+
469+
int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context,
470+
std::vector<Json::Value> &virtualManifests) {
471+
static bool hasQueryBroker = false;
472+
static std::vector<Json::Value> implicitLayerManifest;
473+
static std::vector<Json::Value> explicitLayerManifest;
474+
475+
__android_log_print(ANDROID_LOG_INFO, TAG, "Try to get %s API layer from broker!", layerType.c_str());
476+
if (!hasQueryBroker) {
477+
jni::Array<std::string> projection =
478+
makeArray({api_layer::Columns::FILE_FORMAT_VERSION, api_layer::Columns::NAME, api_layer::Columns::NATIVE_LIB_DIR,
479+
api_layer::Columns::SO_FILENAME, api_layer::Columns::API_VERSION, api_layer::Columns::IMPLEMENTATION_VERSION,
480+
api_layer::Columns::DESCRIPTION, api_layer::Columns::ENABLE_ENVIRONMENT,
481+
api_layer::Columns::DISABLE_ENVIRONMENT, api_layer::Columns::FUNCTIONS,
482+
api_layer::Columns::INSTANCE_EXTENSION_NAMES, api_layer::Columns::INSTANCE_EXTENSION_VERSIONS});
483+
484+
enumerateApiLayerManifests(IMP_LAYER, context, projection, implicitLayerManifest);
485+
enumerateApiLayerManifests(EXP_LAYER, context, projection, explicitLayerManifest);
486+
487+
hasQueryBroker = true;
488+
}
489+
490+
virtualManifests = (layerType == IMP_LAYER) ? implicitLayerManifest : explicitLayerManifest;
491+
return 0;
492+
}
493+
317494
} // namespace openxr_android
318495

319496
#endif // __ANDROID__

src/loader/android_utilities.h

+12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ using wrap::android::content::Context;
2727
* @return 0 on success, something else on failure.
2828
*/
2929
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest);
30+
31+
/*!
32+
* Find the implicit/explicit API layers on the system, and return a constructed JSON object representing it.
33+
*
34+
* @param type An String to indicate layer type of API layers, implicit or explicit.
35+
* @param context An Android context, preferably an Activity Context.
36+
* @param[out] virtualManifest The Json::Value to fill with the virtual manifest.
37+
*
38+
* @return 0 on success, something else on failure.
39+
*/
40+
int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context,
41+
std::vector<Json::Value> &virtualManifests);
3042
} // namespace openxr_android
3143

3244
#endif // __ANDROID__

src/loader/manifest_file.cpp

+127
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,118 @@ void ApiLayerManifestFile::AddManifestFilesAndroid(ManifestFileType type,
755755
}
756756
#endif // XR_USE_PLATFORM_ANDROID
757757

758+
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const Json::Value &root_node, const std::string &filename,
759+
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
760+
std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
761+
JsonVersion file_version = {};
762+
if (!ManifestFile::IsValidJson(root_node, file_version)) {
763+
error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
764+
LoaderLogger::LogErrorMessage("", error_ss.str());
765+
return;
766+
}
767+
768+
Json::Value layer_root_node = root_node["api_layer"];
769+
770+
// The API Layer manifest file needs the "api_layer" root as well as other sub-nodes.
771+
// If any of those aren't there, fail.
772+
if (layer_root_node.isNull() || layer_root_node["name"].isNull() || !layer_root_node["name"].isString() ||
773+
layer_root_node["api_version"].isNull() || !layer_root_node["api_version"].isString() ||
774+
layer_root_node["library_path"].isNull() || !layer_root_node["library_path"].isString() ||
775+
layer_root_node["implementation_version"].isNull() || !layer_root_node["implementation_version"].isString()) {
776+
error_ss << filename << " is missing required fields. Verify all proper fields exist.";
777+
LoaderLogger::LogErrorMessage("", error_ss.str());
778+
return;
779+
}
780+
if (MANIFEST_TYPE_IMPLICIT_API_LAYER == type) {
781+
bool enabled = true;
782+
// Implicit layers require the disable environment variable.
783+
if (layer_root_node["disable_environment"].isNull() || !layer_root_node["disable_environment"].isString()) {
784+
error_ss << "Implicit layer " << filename << " is missing \"disable_environment\"";
785+
LoaderLogger::LogErrorMessage("", error_ss.str());
786+
return;
787+
}
788+
// Check if there's an enable environment variable provided
789+
if (!layer_root_node["enable_environment"].isNull() && layer_root_node["enable_environment"].isString()) {
790+
std::string env_var = layer_root_node["enable_environment"].asString();
791+
// If it's not set in the environment, disable the layer
792+
if (!PlatformUtilsGetEnvSet(env_var.c_str())) {
793+
enabled = false;
794+
}
795+
}
796+
// Check for the disable environment variable, which must be provided in the JSON
797+
std::string env_var = layer_root_node["disable_environment"].asString();
798+
// If the env var is set, disable the layer. Disable env var overrides enable above
799+
if (PlatformUtilsGetEnvSet(env_var.c_str())) {
800+
enabled = false;
801+
}
802+
803+
// Not enabled, so pretend like it isn't even there.
804+
if (!enabled) {
805+
error_ss << "Implicit layer " << filename << " is disabled";
806+
LoaderLogger::LogInfoMessage("", error_ss.str());
807+
return;
808+
}
809+
}
810+
std::string layer_name = layer_root_node["name"].asString();
811+
std::string api_version_string = layer_root_node["api_version"].asString();
812+
JsonVersion api_version = {};
813+
const int num_fields = sscanf(api_version_string.c_str(), "%u.%u", &api_version.major, &api_version.minor);
814+
api_version.patch = 0;
815+
816+
if ((num_fields != 2) || (api_version.major == 0 && api_version.minor == 0) ||
817+
api_version.major > XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) {
818+
error_ss << "layer " << filename << " has invalid API Version. Skipping layer.";
819+
LoaderLogger::LogWarningMessage("", error_ss.str());
820+
return;
821+
}
822+
823+
char *end_ptr;
824+
uint32_t implementation_version = strtol(layer_root_node["implementation_version"].asString().c_str(), &end_ptr, 10);
825+
if (*end_ptr != '\0') {
826+
error_ss << "layer " << filename << " has invalid implementation version.";
827+
LoaderLogger::LogWarningMessage("", error_ss.str());
828+
}
829+
830+
std::string library_path = layer_root_node["library_path"].asString();
831+
832+
// If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
833+
// global library path.
834+
if (library_path.find('\\') != std::string::npos || library_path.find('/') != std::string::npos) {
835+
// If the library_path is an absolute path, just use that if it exists
836+
if (FileSysUtilsIsAbsolutePath(library_path)) {
837+
if (!FileSysUtilsPathExists(library_path)) {
838+
error_ss << filename << " library " << library_path << " does not appear to exist";
839+
LoaderLogger::LogErrorMessage("", error_ss.str());
840+
return;
841+
}
842+
} else {
843+
// Otherwise, treat the library path as a relative path based on the JSON file.
844+
std::string combined_path;
845+
std::string file_parent;
846+
if (!FileSysUtilsGetParentPath(filename, file_parent) ||
847+
!FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
848+
error_ss << filename << " library " << combined_path << " does not appear to exist";
849+
LoaderLogger::LogErrorMessage("", error_ss.str());
850+
return;
851+
}
852+
library_path = combined_path;
853+
}
854+
}
855+
856+
std::string description;
857+
if (!layer_root_node["description"].isNull() && layer_root_node["description"].isString()) {
858+
description = layer_root_node["description"].asString();
859+
}
860+
861+
// Add this layer manifest file
862+
manifest_files.emplace_back(
863+
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
864+
865+
// Add any extensions to it after the fact.
866+
// Handle any renamed functions
867+
manifest_files.back()->ParseCommon(layer_root_node);
868+
}
869+
758870
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream,
759871
LibraryLocator locate_library,
760872
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
@@ -868,6 +980,7 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin
868980
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
869981

870982
// Add any extensions to it after the fact.
983+
// Handle any renamed functions
871984
manifest_files.back()->ParseCommon(layer_root_node);
872985
}
873986

@@ -975,5 +1088,19 @@ XrResult ApiLayerManifestFile::FindManifestFiles(ManifestFileType type,
9751088
ApiLayerManifestFile::AddManifestFilesAndroid(type, manifest_files);
9761089
#endif // XR_USE_PLATFORM_ANDROID
9771090

1091+
#if defined(XR_KHR_LOADER_INIT_SUPPORT)
1092+
std::vector<Json::Value> virtualManifests;
1093+
std::string layerType = (type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER) ? "implicit" : "explicit";
1094+
XrResult result = GetPlatformApiLayerVirtualManifests(layerType, virtualManifests);
1095+
if (XR_SUCCESS == result) {
1096+
for (int i = 0; i < virtualManifests.size(); ++i) {
1097+
ApiLayerManifestFile::CreateIfValid(type, virtualManifests[i], "virtual manifest", manifest_files);
1098+
}
1099+
} else {
1100+
LoaderLogger::LogErrorMessage("", "ApiLayerManifestFile::FindManifestFiles - faile to get virtual manifest files.");
1101+
assert(0);
1102+
}
1103+
#endif // XR_KHR_LOADER_INIT_SUPPORT
1104+
9781105
return XR_SUCCESS;
9791106
}

0 commit comments

Comments
 (0)