Skip to content

Commit d5150b7

Browse files
dengkailrpavlik
dengkail
authored andcommitted
OpenXR loader: add API layer discovery support.
1 parent 6f3ccf3 commit d5150b7

6 files changed

+341
-7
lines changed

src/loader/android_utilities.cpp

+186-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,134 @@ 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 imllementationVersion = 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"] = imllementationVersion;
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 bool getApiLayerCursor(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 false;
459+
}
460+
461+
cursor.moveToFirst();
462+
for (int i = 0; i < cursor.getCount(); ++i) {
463+
populateApiLayerManifest(layerType, cursor, virtualManifests);
464+
cursor.moveToNext();
465+
}
466+
cursor.close();
467+
468+
return true;
469+
}
470+
471+
int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context,
472+
std::vector<Json::Value> &virtualManifests) {
473+
static bool hasQueryBroker = false;
474+
static std::vector<Json::Value> implicitLayerManifest;
475+
static std::vector<Json::Value> explicitLayerManifest;
476+
477+
__android_log_print(ANDROID_LOG_INFO, TAG, "Try to get %s API layer from broker!", layerType.c_str());
478+
if (!hasQueryBroker) {
479+
jni::Array<std::string> projection =
480+
makeArray({api_layer::Columns::FILE_FORMAT_VERSION, api_layer::Columns::NAME, api_layer::Columns::NATIVE_LIB_DIR,
481+
api_layer::Columns::SO_FILENAME, api_layer::Columns::API_VERSION, api_layer::Columns::IMPLEMENTATION_VERSION,
482+
api_layer::Columns::DESCRIPTION, api_layer::Columns::ENABLE_ENVIRONMENT,
483+
api_layer::Columns::DISABLE_ENVIRONMENT, api_layer::Columns::FUNCTIONS,
484+
api_layer::Columns::INSTANCE_EXTENSION_NAMES, api_layer::Columns::INSTANCE_EXTENSION_VERSIONS});
485+
486+
getApiLayerCursor(IMP_LAYER, context, projection, implicitLayerManifest);
487+
getApiLayerCursor(EXP_LAYER, context, projection, explicitLayerManifest);
488+
489+
hasQueryBroker = true;
490+
}
491+
492+
virtualManifests = (layerType == IMP_LAYER) ? implicitLayerManifest : explicitLayerManifest;
493+
return 0;
494+
}
495+
317496
} // namespace openxr_android
318497

319498
#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

+121
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,112 @@ void ApiLayerManifestFile::AddManifestFilesAndroid(ManifestFileType type,
730730
}
731731
#endif // XR_USE_PLATFORM_ANDROID
732732

733+
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const Json::Value &root_node, const std::string &filename,
734+
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
735+
std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
736+
JsonVersion file_version = {};
737+
if (!ManifestFile::IsValidJson(root_node, file_version)) {
738+
error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
739+
LoaderLogger::LogErrorMessage("", error_ss.str());
740+
return;
741+
}
742+
743+
Json::Value layer_root_node = root_node["api_layer"];
744+
745+
// The API Layer manifest file needs the "api_layer" root as well as other sub-nodes.
746+
// If any of those aren't there, fail.
747+
if (layer_root_node.isNull() || layer_root_node["name"].isNull() || !layer_root_node["name"].isString() ||
748+
layer_root_node["api_version"].isNull() || !layer_root_node["api_version"].isString() ||
749+
layer_root_node["library_path"].isNull() || !layer_root_node["library_path"].isString() ||
750+
layer_root_node["implementation_version"].isNull() || !layer_root_node["implementation_version"].isString()) {
751+
error_ss << filename << " is missing required fields. Verify all proper fields exist.";
752+
LoaderLogger::LogErrorMessage("", error_ss.str());
753+
return;
754+
}
755+
if (MANIFEST_TYPE_IMPLICIT_API_LAYER == type) {
756+
bool enabled = true;
757+
// Implicit layers require the disable environment variable.
758+
if (layer_root_node["disable_environment"].isNull() || !layer_root_node["disable_environment"].isString()) {
759+
error_ss << "Implicit layer " << filename << " is missing \"disable_environment\"";
760+
LoaderLogger::LogErrorMessage("", error_ss.str());
761+
return;
762+
}
763+
// Check if there's an enable environment variable provided
764+
if (!layer_root_node["enable_environment"].isNull() && layer_root_node["enable_environment"].isString()) {
765+
std::string env_var = layer_root_node["enable_environment"].asString();
766+
// If it's not set in the environment, disable the layer
767+
if (!PlatformUtilsGetEnvSet(env_var.c_str())) {
768+
enabled = false;
769+
}
770+
}
771+
// Check for the disable environment variable, which must be provided in the JSON
772+
std::string env_var = layer_root_node["disable_environment"].asString();
773+
// If the env var is set, disable the layer. Disable env var overrides enable above
774+
if (PlatformUtilsGetEnvSet(env_var.c_str())) {
775+
enabled = false;
776+
}
777+
778+
// Not enabled, so pretend like it isn't even there.
779+
if (!enabled) {
780+
error_ss << "Implicit layer " << filename << " is disabled";
781+
LoaderLogger::LogInfoMessage("", error_ss.str());
782+
return;
783+
}
784+
}
785+
std::string layer_name = layer_root_node["name"].asString();
786+
std::string api_version_string = layer_root_node["api_version"].asString();
787+
JsonVersion api_version = {};
788+
const int num_fields = sscanf(api_version_string.c_str(), "%u.%u", &api_version.major, &api_version.minor);
789+
api_version.patch = 0;
790+
791+
if ((num_fields != 2) || (api_version.major == 0 && api_version.minor == 0) ||
792+
api_version.major > XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) {
793+
error_ss << "layer " << filename << " has invalid API Version. Skipping layer.";
794+
LoaderLogger::LogWarningMessage("", error_ss.str());
795+
return;
796+
}
797+
798+
uint32_t implementation_version = atoi(layer_root_node["implementation_version"].asString().c_str());
799+
std::string library_path = layer_root_node["library_path"].asString();
800+
801+
// If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
802+
// global library path.
803+
if (library_path.find('\\') != std::string::npos || library_path.find('/') != std::string::npos) {
804+
// If the library_path is an absolute path, just use that if it exists
805+
if (FileSysUtilsIsAbsolutePath(library_path)) {
806+
if (!FileSysUtilsPathExists(library_path)) {
807+
error_ss << filename << " library " << library_path << " does not appear to exist";
808+
LoaderLogger::LogErrorMessage("", error_ss.str());
809+
return;
810+
}
811+
} else {
812+
// Otherwise, treat the library path as a relative path based on the JSON file.
813+
std::string combined_path;
814+
std::string file_parent;
815+
if (!FileSysUtilsGetParentPath(filename, file_parent) ||
816+
!FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
817+
error_ss << filename << " library " << combined_path << " does not appear to exist";
818+
LoaderLogger::LogErrorMessage("", error_ss.str());
819+
return;
820+
}
821+
library_path = combined_path;
822+
}
823+
}
824+
825+
std::string description;
826+
if (!layer_root_node["description"].isNull() && layer_root_node["description"].isString()) {
827+
description = layer_root_node["description"].asString();
828+
}
829+
830+
// Add this layer manifest file
831+
manifest_files.emplace_back(
832+
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
833+
834+
// Add any extensions to it after the fact.
835+
// Handle any renamed functions
836+
manifest_files.back()->ParseCommon(layer_root_node);
837+
}
838+
733839
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream,
734840
LibraryLocator locate_library,
735841
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
@@ -843,6 +949,7 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin
843949
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
844950

845951
// Add any extensions to it after the fact.
952+
// Handle any renamed functions
846953
manifest_files.back()->ParseCommon(layer_root_node);
847954
}
848955

@@ -950,5 +1057,19 @@ XrResult ApiLayerManifestFile::FindManifestFiles(ManifestFileType type,
9501057
ApiLayerManifestFile::AddManifestFilesAndroid(type, manifest_files);
9511058
#endif // XR_USE_PLATFORM_ANDROID
9521059

1060+
#if defined(XR_KHR_LOADER_INIT_SUPPORT)
1061+
std::vector<Json::Value> virtualManifests;
1062+
std::string layerType = (type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER) ? "implicit" : "explicit";
1063+
XrResult result = GetPlatformApiLayerVirtualManifests(layerType, virtualManifests);
1064+
if (XR_SUCCESS == result) {
1065+
for (int i = 0; i < virtualManifests.size(); ++i) {
1066+
ApiLayerManifestFile::CreateIfValid(type, virtualManifests[i], "virtual manifest", manifest_files);
1067+
}
1068+
} else {
1069+
LoaderLogger::LogErrorMessage("", "ApiLayerManifestFile::FindManifestFiles - faile to get virtual manifest files.");
1070+
assert(0);
1071+
}
1072+
#endif // XR_KHR_LOADER_INIT_SUPPORT
1073+
9531074
return XR_SUCCESS;
9541075
}

0 commit comments

Comments
 (0)