Skip to content

Commit 3821979

Browse files
author
dengkail
committed
OpenXR loader: add API layer discovery support.
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]
1 parent 2c252a3 commit 3821979

File tree

6 files changed

+344
-7
lines changed

6 files changed

+344
-7
lines changed

src/loader/android_utilities.cpp

Lines changed: 190 additions & 7 deletions
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,52 @@ struct Columns : BaseColumns {
164168
};
165169
} // namespace functions
166170

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

169219
static inline jni::Array<std::string> makeArray(std::initializer_list<const char *> &&list) {
@@ -245,11 +295,12 @@ static int populateFunctions(wrap::android::content::Context const &context, boo
245295
return 0;
246296
}
247297

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());
298+
/// Get cursor for active runtime or API layer, parameterized by target type and whether or not we use the system broker
299+
static bool getCursor(wrap::android::content::Context const &context, jni::Array<std::string> const &projection,
300+
std::string const &targetType, bool systemBroker, Cursor &cursor) {
301+
auto uri = (targetType == RUNTIMES_PATH) ? 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,138 @@ 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+
387+
Json::Value rootNode(Json::objectValue);
388+
rootNode["file_format_version"] = fileFormatVersion;
389+
rootNode["api_layer"] = Json::objectValue;
390+
rootNode["api_layer"]["name"] = name;
391+
rootNode["api_layer"]["library_path"] = libDir + "/" + fileName;
392+
rootNode["api_layer"]["api_version"] = apiVersion;
393+
rootNode["api_layer"]["implementation_version"] = imllementationVersion;
394+
rootNode["api_layer"]["description"] = description;
395+
rootNode["api_layer"]["disable_environment"] = disableEnv;
396+
rootNode["api_layer"]["enable_environment"] = enableEnv;
397+
398+
rootNode["api_layer"]["instance_extensions"] = Json::Value(Json::arrayValue);
399+
std::vector<std::string> nameVec;
400+
std::vector<std::string> versionVec;
401+
//extract extension names
402+
std::istringstream issNames(extensionNames);
403+
std::string item;
404+
while (std::getline(issNames, item, '&')) {
405+
nameVec.push_back(item);
406+
}
407+
//extract extension versions
408+
std::istringstream issVersions(extensionVersions);
409+
while (std::getline(issVersions, item, '&')) {
410+
versionVec.push_back(item);
411+
}
412+
413+
Json::Value extension(Json::objectValue);
414+
if(nameVec.size() == versionVec.size()){
415+
for(int i = 0; i < nameVec.size();++i){
416+
extension["name"] = nameVec[i];
417+
extension["extension_version"] = versionVec[i];
418+
rootNode["api_layer"]["instance_extensions"].append(extension);
419+
__android_log_print(ANDROID_LOG_INFO, TAG, "extension name: %s, extension version: %s",
420+
nameVec[i].c_str(), versionVec[i].c_str());
421+
}
422+
}
423+
else{
424+
__android_log_print(ANDROID_LOG_INFO, TAG, "api layer extension name not match extension version!");
425+
}
426+
427+
std::vector<std::string> functionNameVec;
428+
std::vector<std::string> symbolVec;
429+
std::istringstream issFunctions(functions);
430+
while (std::getline(issFunctions, item, '&')) {
431+
std::size_t pos = item.find(':');
432+
if (pos != item.npos) {
433+
functionNameVec.push_back(item.substr(0, pos));
434+
symbolVec.push_back(item.substr(pos+1, item.size()));
435+
}
436+
}
437+
438+
rootNode["api_layer"]["functions"] = Json::Value(Json::objectValue);
439+
if (functions != "None") {
440+
for (int i = 0; i < functionNameVec.size(); ++i) {
441+
rootNode["api_layer"]["functions"][functionNameVec[i]] = symbolVec[i];
442+
__android_log_print(ANDROID_LOG_INFO, TAG, "function name: %s, symbol: %s",
443+
functionNameVec[i].c_str(), symbolVec[i].c_str());
444+
}
445+
} else {
446+
__android_log_print(ANDROID_LOG_INFO, TAG, "functions field not existed!");
447+
}
448+
449+
layerRootNode.push_back(rootNode);
450+
451+
return true;
452+
}
453+
454+
static bool getApiLayerCursor(std::string layerType, wrap::android::content::Context const &context,
455+
jni::Array<std::string> &projection,
456+
std::vector<Json::Value> &virtualManifests) {
457+
Cursor cursor;
458+
459+
getCursor(context, projection, layerType, false, cursor);
460+
if (cursor.isNull()) {
461+
return false;
462+
}
463+
464+
cursor.moveToFirst();
465+
for(int i = 0; i < cursor.getCount(); ++i){
466+
populateApiLayerManifest(layerType, cursor, virtualManifests);
467+
cursor.moveToNext();
468+
}
469+
cursor.close();
470+
471+
return true;
472+
}
473+
474+
int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context,
475+
std::vector<Json::Value> &virtualManifests) {
476+
static bool hasQueryBroker = false;
477+
static std::vector<Json::Value> implicitLayerManifest;
478+
static std::vector<Json::Value> explicitLayerManifest;
479+
480+
__android_log_print(ANDROID_LOG_INFO, TAG, "Try to get %s API layer from broker!", layerType.c_str());
481+
if(!hasQueryBroker){
482+
jni::Array<std::string> projection = makeArray({api_layer::Columns::FILE_FORMAT_VERSION, api_layer::Columns::NAME,
483+
api_layer::Columns::NATIVE_LIB_DIR, api_layer::Columns::SO_FILENAME,
484+
api_layer::Columns::API_VERSION, api_layer::Columns::IMPLEMENTATION_VERSION,
485+
api_layer::Columns::DESCRIPTION, api_layer::Columns::ENABLE_ENVIRONMENT,
486+
api_layer::Columns::DISABLE_ENVIRONMENT, api_layer::Columns::FUNCTIONS,
487+
api_layer::Columns::INSTANCE_EXTENSION_NAMES,
488+
api_layer::Columns::INSTANCE_EXTENSION_VERSIONS});
489+
490+
getApiLayerCursor(IMP_LAYER, context, projection, implicitLayerManifest);
491+
getApiLayerCursor(EXP_LAYER, context, projection, explicitLayerManifest);
492+
493+
hasQueryBroker= true;
494+
}
495+
496+
virtualManifests = (layerType == IMP_LAYER) ? implicitLayerManifest : explicitLayerManifest;
497+
return 0;
498+
}
499+
317500
} // namespace openxr_android
318501

319502
#endif // __ANDROID__

src/loader/android_utilities.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ 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, std::vector<Json::Value> &virtualManifests);
3041
} // namespace openxr_android
3142

3243
#endif // __ANDROID__

src/loader/manifest_file.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,112 @@ void ApiLayerManifestFile::AddManifestFilesAndroid(ManifestFileType type,
723723
}
724724
#endif // XR_USE_PLATFORM_ANDROID
725725

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

838944
// Add any extensions to it after the fact.
945+
// Handle any renamed functions
839946
manifest_files.back()->ParseCommon(layer_root_node);
840947
}
841948

@@ -943,5 +1050,19 @@ XrResult ApiLayerManifestFile::FindManifestFiles(ManifestFileType type,
9431050
ApiLayerManifestFile::AddManifestFilesAndroid(type, manifest_files);
9441051
#endif // XR_USE_PLATFORM_ANDROID
9451052

1053+
#if defined(XR_KHR_LOADER_INIT_SUPPORT)
1054+
std::vector<Json::Value> virtualManifests;
1055+
std::string layerType = (type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER) ? "implicit" : "explicit";
1056+
XrResult result = GetPlatformApiLayerVirtualManifests(layerType, virtualManifests);
1057+
if (XR_SUCCESS == result) {
1058+
for(int i = 0; i < virtualManifests.size();++i){
1059+
ApiLayerManifestFile::CreateIfValid(type, virtualManifests[i], "virtual manifest", manifest_files);
1060+
}
1061+
} else {
1062+
LoaderLogger::LogErrorMessage("", "ApiLayerManifestFile::FindManifestFiles - faile to get virtual manifest files.");
1063+
assert(0);
1064+
}
1065+
#endif // XR_KHR_LOADER_INIT_SUPPORT
1066+
9461067
return XR_SUCCESS;
9471068
}

0 commit comments

Comments
 (0)