Android上OpenXR SDK查找并加载runtime流程

OpenXR SDK地址为:GitHub - KhronosGroup/OpenXR-SDK-Source: Sources for OpenXR loader, basic API layers, and example code.

Monado runtime地址为:Monado / Monado · GitLab 

system broker地址为:Ryan Pavlik / openxr-android-broker · GitLab

本文主要讲解openxr应用如何选择monado runtime的主要流程。

其完整流程图如下

由于图太大,有的地方不清晰,故将某些部分截图重新展示

CreateInstance流程如下

 

查找runtime流程如下 

API layer load流程图如下

 

具体流程如下

目录

1.实例化OpenXrProgram

2. CreateOpenXrProgram

3. 实例化后会CreateInstance

4. LogLayersAndExtensions

5.  xrEnumerateInstanceExtensionProperties

6.  LoaderXrEnumerateInstanceExtensionProperties

7. RuntimeInterface::LoadRuntime-查找并加载runtime

8. RuntimeManifestFile::FindManifestFiles--查找runtime

9. GetPlatformRuntimeVirtualManifest--查找runtime-标志1

10. getActiveRuntimeVirtualManifest--查找runtime-标志1

11. PlatformGetGlobalRuntimeFileName--查找runtime-标志3

12. CreateIfValid-标志2

13. CreateIfValid-标志4

 14. TryLoadingSingleRuntime--加载runtime-标志3


以SDK中的hello_xr应用为例,其入口在main.cpp中

1.实例化OpenXrProgram

main.cpp
void android_main(struct android_app* app) {
...
// Initialize the OpenXR program.
//实例化OpenXrProgram
std::shared_ptr<IOpenXrProgram> program = CreateOpenXrProgram(options, platformPlugin, graphicsPlugin);

// Initialize the loader for this platform
PFN_xrInitializeLoaderKHR initializeLoader = nullptr;
//将LoaderXrInitializeLoaderKHR方法指针赋值给initializeLoader
if (XR_SUCCEEDED(
        xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)(&initializeLoader)))) {
    XrLoaderInitInfoAndroidKHR loaderInitInfoAndroid = {XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR};
    loaderInitInfoAndroid.applicationVM = app->activity->vm;
    loaderInitInfoAndroid.applicationContext = app->activity->clazz;
//执行LoaderXrInitializeLoaderKHR方法
    initializeLoader((const XrLoaderInitInfoBaseHeaderKHR*)&loaderInitInfoAndroid);
}
//CreateInstance
program->CreateInstance();
program->InitializeSystem();
...
}

2. CreateOpenXrProgram

openxr_program.cpp

std::shared_ptr<IOpenXrProgram> CreateOpenXrProgram(const std::shared_ptr<Options>& options, const std::shared_ptr<IPlatformPlugin>& platformPlugin, const std::shared_ptr<IGraphicsPlugin>& graphicsPlugin) {
//make_shared函数的主要功能是在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr;由于是通过shared_ptr管理内存,因此一种安全分配和使用动态内存的方法。
    return std::make_shared<OpenXrProgram>(options, platformPlugin, graphicsPlugin);
}
struct OpenXrProgram : IOpenXrProgram {
    OpenXrProgram(const std::shared_ptr<Options>& options, const std::shared_ptr<IPlatformPlugin>& platformPlugin,
                  const std::shared_ptr<IGraphicsPlugin>& graphicsPlugin)
        : m_options(options),//赋值
          m_platformPlugin(platformPlugin),
          m_graphicsPlugin(graphicsPlugin),
          m_acceptableBlendModes{XR_ENVIRONMENT_BLEND_MODE_OPAQUE, XR_ENVIRONMENT_BLEND_MODE_ADDITIVE,
                                 XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND} {}

3. 实例化后会CreateInstance

openxr_program.cpp
void CreateInstance() override {
    LogLayersAndExtensions();
    CreateInstanceInternal();
    LogInstanceInfo();
}

4. LogLayersAndExtensions

openxr_program.cpp
static void LogLayersAndExtensions() {
    // Write out extension properties for a given layer.
    const auto logExtensions = [](const char* layerName, int indent = 0) {
        uint32_t instanceExtensionCount;
        //CHECK_XRCMD是用于判断函数是否执行FAILED
        //xrEnumerateInstanceExtensionProperties返回可用实例扩展的属性
        CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(layerName, 0, &instanceExtensionCount, nullptr));
......
}

5.  xrEnumerateInstanceExtensionProperties

loader_core.cpp
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL  xrEnumerateInstanceExtensionProperties(const char *layerName, uint32_t propertyCapacityInput, uint32_t *propertyCountOutput, XrExtensionProperties *properties) {
    return LoaderXrEnumerateInstanceExtensionProperties(layerName,  propertyCapacityInput, propertyCountOutput, properties);
}

6.  LoaderXrEnumerateInstanceExtensionProperties

loader_core.cpp
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderXrEnumerateInstanceExtensionProperties(const char *layerName, uint32_t propertyCapacityInput, uint32_t *propertyCountOutput, XrExtensionProperties *properties) XRLOADER_ABI_TRY {
 ...   
 // Get the layer extension properties
    result = ApiLayerInterface::GetInstanceExtensionProperties("xrEnumerateInstanceExtensionProperties", layerName,
                                                               extension_properties);
    if (XR_SUCCEEDED(result) && !just_layer_properties) {
        // If not specific to a layer, get the runtime extension properties
        //如果不是特定于某个层,则获取运行时扩展属性
        result = RuntimeInterface::LoadRuntime("xrEnumerateInstanceExtensionProperties");
        if (XR_SUCCEEDED(result)) {
    RuntimeInterface::GetRuntime().GetInstanceExtensionProperties(extension_properties);
       ...
}

7. RuntimeInterface::LoadRuntime-查找并加载runtime

runtime_interface.cpp
XrResult RuntimeInterface::LoadRuntime(const std::string& openxr_command) {
    // If something's already loaded, we're done here.
    //如果已经有Instance实例了,则return, 这代表一个APP只有一个Instance实例
    if (GetInstance() != nullptr) {
        return XR_SUCCESS;
    }
#ifdef XR_KHR_LOADER_INIT_SUPPORT

    if (!LoaderInitData::instance().initialized()) {
   
   //Instance没有成功初始化
        LoaderLogger::LogErrorMessage(
            openxr_command, "RuntimeInterface::LoadRuntime cannot run because xrInitializeLoaderKHR was not successfully called.");
        return XR_ERROR_INITIALIZATION_FAILED;
    }
#endif  // XR_KHR_LOADER_INIT_SUPPORT

    std::vector<std::unique_ptr<RuntimeManifestFile>> runtime_manifest_files = {};

    // Find the available runtimes which we may need to report information for.
    //查找可用的runtime,会对runtime_manifest_files赋值
    XrResult last_error = RuntimeManifestFile::FindManifestFiles(runtime_manifest_files);
    if (XR_FAILED(last_error)) {
        LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - unknown error");
    } else {
        last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
        for (std::unique_ptr<RuntimeManifestFile>& manifest_file : runtime_manifest_files) {
            //加载runtime-----标志5
            last_error = RuntimeInterface::TryLoadingSingleRuntime(openxr_command, manifest_file);
            if (XR_SUCCEEDED(last_error)) {
                break;
            }
        }
    }

    // Unsuccessful in loading any runtime, throw the runtime unavailable message.
    //如果没有加载到任何的runtime,会返回错误,且应用无法正常运转,表现大多为黑屏,或者卡在启动动画上
    if (XR_FAILED(last_error)) {
        LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - failed to load a runtime");
        last_error = XR_ERROR_RUNTIME_UNAVAILABLE;
    }
        
    return last_error;
}

8. RuntimeManifestFile::FindManifestFiles--查找runtime

manifest_file.cpp
// Find all manifest files in the appropriate search paths/registries for the given type.
XrResult RuntimeManifestFile::FindManifestFiles(std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
#if defined(XR_KHR_LOADER_INIT_SUPPORT)
        Json::Value virtualManifest;
        //获取runtime
        result = GetPlatformRuntimeVirtualManifest(virtualManifest);//标志1
        if (XR_SUCCESS == result) {
            //将获取到的virtualManifest中的信息赋值给manifest_files
            RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files);//标志2
            return result;
        }
#endif  // defined(XR_KHR_LOADER_INIT_SUPPORT)
//如果上面通过GetPlatformRuntimeVirtualManifest没有找到runtime,则会通过PlatformGetGlobalRuntimeFileName继续查找
        if (!PlatformGetGlobalRuntimeFileName(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), filename)) {
   
   //标志3
            LoaderLogger::LogErrorMessage(
                "", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment");
            return XR_ERROR_RUNTIME_UNAVAILABLE;
        }
        result = XR_SUCCESS;
        LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::FindManifestFiles - using global runtime file " + filename);
#endif
    }
    //将filename文件中的信息赋值给manifest_files 
    RuntimeManifestFile::CreateIfValid(filename, manifest_files);//标志4
...
}

9. GetPlatformRuntimeVirtualManifest--查找runtime-标志1

runtime_interface.cpp
#ifdef XR_USE_PLATFORM_ANDROID
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {
    using wrap::android::content::Context;
    auto& initData = LoaderInitData::instance();
    if (!initData.initialized()) {
        return XR_ERROR_INITIALIZATION_FAILED;
    }
    auto context = Context(reinterpret_cast<jobject>(initData.getData().applicationContext));
    if (context.isNull()) {
        return XR_ERROR_INITIALIZATION_FAILED;
    }
    Json::Value virtualManifest;
    //即将通过Cursor要去查系统中的runtime了
    if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) {
        return XR_ERROR_INITIALIZATION_FAILED;
    }
    //virtualManifest赋值给out_manifest 
    out_manifest = virtualManifest;
    return XR_SUCCESS;
}
#endif  // XR_USE_PLATFORM_ANDROID

10. getActiveRuntimeVirtualManifest--查找runtime-标志1

android_utilities.cpp

int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest) {
    jni::Array<std::string> projection = makeArray({active_runtime::Columns::PACKAGE_NAME,active_runtime::Columns::NATIVE_LIB_DIR, active_runtime::Columns::SO_FILENAME, active_runtime::Columns::HAS_FUNCTIONS});

    // First, try getting the installable broker's provider
    bool systemBroker = false;
    Cursor cursor;
    //log输出:08-02 08:43:48.651  4294  4331 I OpenXR-Loader: getActiveRuntimeCursor: Querying URI: content://org.khronos.openxr.runtime_broker/openxr/1/abi/arm64-v8a/runtimes/active/0
//非系统应用
    if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) {
        // OK, try the system broker as a fallback.
       //是系统应用,不是通过用户去安装的
        systemBroker = true;
        getActiveRuntimeCursor(context, projection, systemBroker, cursor);
        //log输出:08-02 08:43:48.654  4294  4331 I OpenXR-Loader: getActiveRuntimeCursor: Querying URI: content://org.khronos.openxr.system_runtime_broker/openxr/1/abi/arm64-v8a/runtimes/active/0
    }

    if (cursor.isNull()) {
        // Couldn't find either broker
        ALOGE("Could access neither the installable nor system runtime broker.");
        return -1;
    }

    cursor.moveToFirst();
//通过cursor去查column对应的内容
    auto filename = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::SO_FILENAME));
    auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR));
    auto packageName = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::PACKAGE_NAME));

    auto hasFunctions = cursor.getInt(cursor.getColumnIndex(active_runtime::Columns::HAS_FUNCTIONS)) == 1;
    __android_log_print(ANDROID_LOG_INFO, TAG, "Got runtime: package: %s, so filename: %s, native lib dir: %s, has functions: %s",
                        packageName.c_str(), filename.c_str(), libDir.c_str(), (hasFunctions ? "yes" : "no"));
//log输出: 08-02 08:39:29.543  4425  4462 I OpenXR-Loader: Got runtime: package: org.freedesktop.monado.openxr_runtime.out_of_process, so filename: libopenxr_monado.so, native lib dir:/data/app/~~ZNtBhWeJzxYgOPuiZZVjgQ==/org.freedesktop.monado.openxr_runtime.out_of_process-PVkP76Im0S4PZZp5d2Gb5g==/lib/arm64, has functions: no
//可以看到通过URI查出来的包名是org.freedesktop.monado.openxr_runtime.out_of_process, 对应的runtime so名字是libopenxr_monado.so
    auto lib_path = libDir + "/" + filename;//lib_path就是runtime so的全路径
    cursor.close();
//创建json文件,并将lib_path传入,root 名为runtime, lib_path为子节点
    JsonManifestBuilder builder{"runtime", lib_path};
    if (hasFunctions) {
        int result = populateFunctions(context, systemBroker, packageName, builder);
        if (result != 0) {
            return result;
        }
    }
//json文件创建
    virtualManifest = builder.build();
    return 0;
}
}  // namespace openxr_android

11. PlatformGetGlobalRuntimeFileName--查找runtime-标志3

platform_utils.hpp
此函数会在"/product", "/odm", "/oem", "/vendor", "/system"目录下的/etc/openxr/下查找active_runtime.json文件
// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
    // Prefix for the runtime JSON file name
    static const char* rt_dir_prefixes[] = {
   
   "/product", "/odm", "/oem", "/vendor", "/system"};
    static const std::string rt_filename = "/active_runtime.json";
    static const std::string subdir = "/etc/openxr/";
    for (const auto prefix : rt_dir_prefixes) {
        auto path = prefix + subdir + std::to_string(major_version) + rt_filename;
        struct stat buf;
        if (0 == stat(path.c_str(), &buf)) {
            file_name = path;//返回active_runtime.json所在的目录
            return true;
        }
    }
    return false;
}

12. CreateIfValid-标志2

manifest_file.cpp
void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std::string &filename,std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
    std::ostringstream error_ss("RuntimeManifestFile::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的root节点是runtime,子节点是library_path,如果有一个为空,则return
    const Json::Value &runtime_root_node = root_node["runtime"];
    // The Runtime manifest file needs the "runtime" root as well as a sub-node for "library_path".  If any of those aren't there,
    // fail.
    if (runtime_root_node.isNull() || runtime_root_node["library_path"].isNull() || !runtime_root_node["library_path"].isString()) {
        error_ss << filename << " is missing required fields.  Verify all proper fields exist.";
        LoaderLogger::LogErrorMessage("", error_ss.str());
        return;
    }
//将子节点library_path的转化为string类型
    std::string lib_path = runtime_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 (lib_path.find('\\') != std::string::npos || lib_path.find('/') != std::string::npos) {
        // If the library_path is an absolute path, just use that if it exists
//如果lib_path是绝对路径,如果存在则直接使用, 我们上面存的是绝对路径
        if (FileSysUtilsIsAbsolutePath(lib_path)) {
            if (!FileSysUtilsPathExists(lib_path)) {
                error_ss << filename << " library " << lib_path << " does not appear to exist";
                LoaderLogger::LogErrorMessage("", error_ss.str());
                return;
            }
        } else {
           ...
        }
    }
    //add manifest files
    // Add this runtime manifest file
    manifest_files.emplace_back(new RuntimeManifestFile(filename, lib_path));

    // Add any extensions to it after the fact.
    // Handle any renamed functions
    manifest_files.back()->ParseCommon(runtime_root_node);
}

13. CreateIfValid-标志4

manifest_file.cpp
void RuntimeManifestFile::CreateIfValid(std::string const &filename
                                    , std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
    std::ifstream json_stream(filename, std::ifstream::in);
    LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::CreateIfValid - attempting to load " + filename);
    std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid ");
    if (!json_stream.is_open()) {
        error_ss << "failed to open " << filename << ".  Does it exist?";
        LoaderLogger::LogErrorMessage("", error_ss.str());
        return;
    }
    Json::CharReaderBuilder builder;
    std::string errors;
    Json::Value root_node = Json::nullValue;
...
//会调用到12步
 CreateIfValid(root_node, filename, manifest_files);
}

 14. TryLoadingSingleRuntime--加载runtime-标志5

runtime_interface.cpp
XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command,
                                                   std::unique_ptr<RuntimeManifestFile>& manifest_file) {
    LoaderPlatformLibraryHandle runtime_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());//dlopen runtime so
...
#ifdef XR_KHR_LOADER_INIT_SUPPORT
    if (!LoaderInitData::instance().initialized()) {
        LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntime skipping manifest file " +manifest_file->Filename() +" because xrInitializeLoaderKHR was not yet called.");
...
        // If we have xrInitializeLoaderKHR exposed as an export, forward call to it.
        const auto function_name = manifest_file->GetFunctionName("xrInitializeLoaderKHR");
        auto initLoader =
            reinterpret_cast<PFN_xrInitializeLoaderKHR>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));//dlsym
        if (initLoader != nullptr) {
   
   //initLoader为空
...
        }
    }
...
// Get and settle on an runtime interface version (using any provided name if required).
std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderRuntimeInterface");
auto negotiate =
    reinterpret_cast<PFN_xrNegotiateLoaderRuntimeInterface>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));//dlsym
...
// Skip calling the negotiate function and fail if the function pointer
// could not get loaded
XrResult res = XR_ERROR_RUNTIME_FAILURE;
if (nullptr != negotiate) {
    //执行runtime中的xrNegotiateLoaderRuntimeInterface方法
    //会对runtime_info结构体中的子变量getInstanceProcAddr赋值,赋值成了runtime中的方法,方法名为oxr_xrGetInstanceProcAddr
    res = negotiate(&loader_info, &runtime_info);
}
...
#ifdef XR_KHR_LOADER_INIT_SUPPORT
    if (XR_SUCCEEDED(res) && !forwardedInitLoader) {
        // Forward initialize loader call, where possible and if we did not do so before.
        PFN_xrVoidFunction initializeVoid = nullptr;
        PFN_xrInitializeLoaderKHR initialize = nullptr;

        // Now we may try asking xrGetInstanceProcAddr
        //执行runtime中的oxr_xrGetInstanceProcAddr方法,会将xrInitializeLoaderKHR方法指针赋值为runtime中的oxr_xrInitializeLoaderKHR
        if (XR_SUCCEEDED(runtime_info.getInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", &initializeVoid))) {//
            if (initializeVoid == nullptr) {
                LoaderLogger::LogErrorMessage(openxr_command,"RuntimeInterface::LoadRuntime got success from xrGetInstanceProcAddr "
"for xrInitializeLoaderKHR, but output a null pointer.");
                res = XR_ERROR_RUNTIME_FAILURE;
            } else {
//将initializeVoid函数指针赋值给initialize 
                initialize = reinterpret_cast<PFN_xrInitializeLoaderKHR>(initializeVoid);
            }
        }
        if (initialize != nullptr) {
            // we found the entry point one way or another.
            LoaderLogger::LogInfoMessage(openxr_command,"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime after calling xrNegotiateLoaderRuntimeInterface.");
//执行runtime中的oxr_xrInitializeLoaderKHR方法
            res = initialize(LoaderInitData::instance().getParam());
            if (!XR_SUCCEEDED(res)) {
LoaderLogger::LogErrorMessage(openxr_command,"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed.");
            }
        }

    }
...
// Use this runtime
//使用这个runtime
    GetInstance().reset(new RuntimeInterface(runtime_library, runtime_info.getInstanceProcAddr));
...
}

猜你喜欢

转载自blog.csdn.net/weixin_41028555/article/details/132303199