仍然以OpenXR SDK源码和Monado runtime源码来分析
以CreateSwapchains()函数为例来串一下,看看OpenXR SDK和runtime是怎么链接起来的
OpenXR SDK的源码里有一个hello_xr的sample code,
这个hello_xr 的main函数:void android_main(struct android_app* app)在 :
src\tests\hello_xr\main.cpp
/**
* This is the main entry point of a native application that is using
* android_native_app_glue. It runs in its own thread, with its own
* event loop for receiving input events and doing other things.
*/
void android_main(struct android_app* app) {
try {
JNIEnv* Env;
app->activity->vm->AttachCurrentThread(&Env, nullptr);
...
...
// Initialize the loader for this platform
PFN_xrInitializeLoaderKHR initializeLoader = nullptr;
if (XR_SUCCEEDED(
xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)(&initializeLoader)))) {
XrLoaderInitInfoAndroidKHR loaderInitInfoAndroid;
memset(&loaderInitInfoAndroid, 0, sizeof(loaderInitInfoAndroid));
loaderInitInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
loaderInitInfoAndroid.next = NULL;
loaderInitInfoAndroid.applicationVM = app->activity->vm;
loaderInitInfoAndroid.applicationContext = app->activity->clazz;
initializeLoader((const XrLoaderInitInfoBaseHeaderKHR*)&loaderInitInfoAndroid);
}
program->CreateInstance();
program->InitializeSystem();
program->InitializeSession();
program->CreateSwapchains();
...
...
}
这里面会做很多事情,其中有一句:
program->CreateSwapchains();
CreateSwapchains()的定义在:
src\tests\hello_xr\openxr_program.cpp
void CreateSwapchains() override {
...
...
// Create a swapchain for each view.
for (uint32_t i = 0; i < viewCount; i++) {
const XrViewConfigurationView& vp = m_configViews[i];
Log::Write(Log::Level::Info,
Fmt("Creating swapchain for view %d with dimensions Width=%d Height=%d SampleCount=%d", i,
vp.recommendedImageRectWidth, vp.recommendedImageRectHeight, vp.recommendedSwapchainSampleCount));
// Create the swapchain.
XrSwapchainCreateInfo swapchainCreateInfo{XR_TYPE_SWAPCHAIN_CREATE_INFO};
swapchainCreateInfo.arraySize = 1;
swapchainCreateInfo.format = m_colorSwapchainFormat;
swapchainCreateInfo.width = vp.recommendedImageRectWidth;
swapchainCreateInfo.height = vp.recommendedImageRectHeight;
swapchainCreateInfo.mipCount = 1;
swapchainCreateInfo.faceCount = 1;
swapchainCreateInfo.sampleCount = m_graphicsPlugin->GetSupportedSwapchainSampleCount(vp);
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
Swapchain swapchain;
swapchain.width = swapchainCreateInfo.width;
swapchain.height = swapchainCreateInfo.height;
CHECK_XRCMD(xrCreateSwapchain(m_session, &swapchainCreateInfo, &swapchain.handle));
...
...
}
...
}
代码很多,其中有一句:
CHECK_XRCMD(xrCreateSwapchain(m_session, &swapchainCreateInfo, &swapchain.handle));
注意这个xrCreateSwapchain(...)函数的三个参数:
XrSession m_session{XR_NULL_HANDLE};
XrSwapchainCreateInfo swapchainCreateInfo{XR_TYPE_SWAPCHAIN_CREATE_INFO};
struct Swapchain {
XrSwapchain handle;
int32_t width;
int32_t height;
}; 实例的成员 XrSwapchain handle;
全局搜索xrCreateSwapchain(...)函数,
发现他在这两个文件中分别有包含
src\loader\openxr-loader.def
src\loader\openxr-loader.map
这两份文件中就包含了OpenXR SDK 和 Runtime 中需要进行衔接的Api:
通过对openxr-loader.def 和 openxr-loader.map两个文件的全局查找可以看到
src\loader\CMakeLists.txt 里分别有如下两段:
if(DYNAMIC_LOADER) add_definitions(-DXRAPI_DLL_EXPORT)
set(LIBRARY_TYPE SHARED)
if(WIN32) list(APPEND openxr_loader_RESOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/openxr-loader.def)
endif()
else() # build static lib
set(LIBRARY_TYPE STATIC)
endif()
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
target_compile_options( openxr_loader
PRIVATE -Wextra
-fno-strict-aliasing
-fno-builtin-memcmp
"$<$:-fno-rtti>"
-ffunction-sections
-fdata-sections )
# Make build depend on the version script/export map
target_sources(openxr_loader PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/openxr-loader.map)
# Add the linker flag.
set_target_properties(openxr_loader PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/openxr-loader.map")
# For GCC version 7.1 or greater, we need to disable the implicit fallthrough warning since
# there's no consistent way to satisfy all compilers until they all accept the C++17 standard
if(CMAKE_COMPILER_IS_GNUCC AND NOT (CMAKE_CXX_COMPILER_VERSION LESS 7.1))
target_compile_options(openxr_loader PRIVATE -Wimplicit-fallthrough=0)
endif()
endif()
从这两段代码可以很明显的看到在编译时,
openxr-loader.def 和 openxr-loader.map会被加载,
openxr-loader.def 是被Win32位的系统加载。
再接着看monado runtime这边,是如何实现 xrCreateSwapchain(..)函数的
首先 src\external\openxr_includes\openxr\openxr_platform_defines.h 定义了很多宏,重点看看下面这段及其注释:
/* Platform-specific calling convention macros.
*
* Platforms should define these so that OpenXR clients call OpenXR functions
* with the same calling conventions that the OpenXR implementation expects.
*
* XRAPI_ATTR - Placed before the return type in function declarations.
* Useful for C++11 and GCC/Clang-style function attribute syntax.
* XRAPI_CALL - Placed after the return type in function declarations.
* Useful for MSVC-style calling convention syntax.
* XRAPI_PTR - Placed between the '(' and '*' in function pointer types.
*
* Function declaration: XRAPI_ATTR void XRAPI_CALL xrFunction(void);
* Function pointer type: typedef void (XRAPI_PTR *PFN_xrFunction)(void);
*/
#if defined(_WIN32)
#define XRAPI_ATTR
// On Windows, functions use the stdcall convention
#define XRAPI_CALL __stdcall
#define XRAPI_PTR XRAPI_CALL
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
#error "API not supported for the 'armeabi' NDK ABI"
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
// On Android 32-bit ARM targets, functions use the "hardfloat"
// calling convention, i.e. float parameters are passed in registers. This
// is true even if the rest of the application passes floats on the stack,
// as it does by default when compiling for the armeabi-v7a NDK ABI.
#define XRAPI_ATTR __attribute__((pcs("aapcs-vfp")))
#define XRAPI_CALL
#define XRAPI_PTR XRAPI_ATTR
#else
// On other platforms, use the default calling convention
#define XRAPI_ATTR
#define XRAPI_CALL
#define XRAPI_PTR
#endif
src\external\openxr_includes\openxr\openxr.h
里对openxr-loader.map的每个函数都进行声明,同时还声明了很多结构体。
typedef XrResult (XRAPI_PTR *PFN_xrCreateSwapchain)(XrSession session, const XrSwapchainCreateInfo* createInfo, XrSwapchain* swapchain);
XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchain(
XrSession session,
const XrSwapchainCreateInfo* createInfo,
XrSwapchain* swapchain);
在 src\xrt\state_trackers\oxr\oxr_api_funcs.h 中,
同样对openxr-loader.map里的所有函数按照功能,分别进行了声明。
* oxr_api_negotiate.c
* oxr_api_instance.c
* oxr_api_system.c
* oxr_api_session.c
* oxr_api_space.c
* oxr_api_swapchain.c
//! OpenXR API function @ep{xrEnumerateSwapchainFormats}
XRAPI_ATTR XrResult XRAPI_CALL
oxr_xrEnumerateSwapchainFormats(XrSession session,
uint32_t formatCapacityInput,
uint32_t *formatCountOutput,
int64_t *formats);
//! OpenXR API function @ep{xrCreateSwapchain}
XRAPI_ATTR XrResult XRAPI_CALL
oxr_xrCreateSwapchain(XrSession session, const XrSwapchainCreateInfo *createInfo, XrSwapchain *swapchain);
//! OpenXR API function @ep{xrDestroySwapchain}
XRAPI_ATTR XrResult XRAPI_CALL
oxr_xrDestroySwapchain(XrSwapchain swapchain);
//! OpenXR API function @ep{xrEnumerateSwapchainImages}
XRAPI_ATTR XrResult XRAPI_CALL
oxr_xrEnumerateSwapchainImages(XrSwapchain swapchain,
uint32_t imageCapacityInput,
uint32_t *imageCountOutput,
XrSwapchainImageBaseHeader *images);
//! OpenXR API function @ep{xrAcquireSwapchainImage}
XRAPI_ATTR XrResult XRAPI_CALL
oxr_xrAcquireSwapchainImage(XrSwapchain swapchain, const XrSwapchainImageAcquireInfo *acquireInfo, uint32_t *index);
//! OpenXR API function @ep{xrWaitSwapchainImage}
XRAPI_ATTR XrResult XRAPI_CALL
oxr_xrWaitSwapchainImage(XrSwapchain swapchain, const XrSwapchainImageWaitInfo *waitInfo);
//! OpenXR API function @ep{xrReleaseSwapchainImage}
XRAPI_ATTR XrResult XRAPI_CALL
oxr_xrReleaseSwapchainImage(XrSwapchain swapchain, const XrSwapchainImageReleaseInfo *releaseInfo);
* oxr_api_debug.c
* oxr_api_action.c
并且在 src\xrt\state_trackers\oxr 目录下分别进行函数实现:
在 src\xrt\state_trackers\oxr\CMakeLists.txt 里可以看到,Runtime中对OpenXR SDK需要衔接的函数实现,会编译成库的形式提供给OpenXR SDK使用。
就目前来看,src\xrt\state_trackers\oxr\ 下以 oxr_api_ 为前缀的文件和CMakeLists.txt,才是对OpenXR SDK 中 openxr-loader.map 里函数的实现。
暂时不太清楚之前提到的 src\external\openxr_includes\openxr\ 这个目录里对 openxr-loader.map 的函数声明是做何用。
oxr_xrCreateSwapchain()函数在src\xrt\state_trackers\oxr\oxr_api_swapchain.c 中被具体定义实现:
XrResult
oxr_xrCreateSwapchain(XrSession session, const XrSwapchainCreateInfo *createInfo, XrSwapchain *out_swapchain)
{
OXR_TRACE_MARKER();
XrResult ret;
struct oxr_session *sess;
struct oxr_swapchain *sc;
struct oxr_logger log;
OXR_VERIFY_SESSION_AND_INIT_LOG(&log, session, sess, "xrCreateSwapchain");
if (sess->compositor == NULL) {
return oxr_error(&log, XR_ERROR_VALIDATION_FAILURE, "Is illegal in headless sessions");
}
...
// Short hand.
struct oxr_instance *inst = sess->sys->inst;
XrSwapchainUsageFlags flags = 0;
flags |= XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
flags |= XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
flags |= XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT;
flags |= XR_SWAPCHAIN_USAGE_TRANSFER_SRC_BIT;
flags |= XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT;
flags |= XR_SWAPCHAIN_USAGE_SAMPLED_BIT;
flags |= XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT;
if (inst->extensions.MND_swapchain_usage_input_attachment_bit ||
inst->extensions.KHR_swapchain_usage_input_attachment_bit) {
// aliased to XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_MND
flags |= XR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_KHR;
}
...
return oxr_session_success_result(sess);
}
这就是以 CreateSwapchains() 函数为例,OpenXR SDK 与 Monado Runtime的链接过程