对OpenXR 的一些理解

参考官方文档openxr-10-reference-guide

  • 一个标准的openxr应用包含方法调用、对象创建、Session状态变化、渲染循环等


    image.png
  • 以monon的openxr runtime为例,主要涉及以下几个大的模块

1、连接方面,有蓝牙设备,和usb设备,涉及蓝牙通信和HID通信
2、语言方面设计openxr 、vulkan、opengl等
3、api方面有交换链、session会话、event队列
4、ipc通信方面有soctet通信、匿名共享内存
5、渲染方面主要是vulkan 语法和 shader

  • monon的数据来源主要是获取手机自带的加速度和陀螺仪 模拟成3dof数据
while (ASensorEventQueue_getEvents(d->event_queue, &event, 1) > 0) {

        switch (event.type) {
        case ASENSOR_TYPE_ACCELEROMETER: {
            accel.x = event.acceleration.y;
            accel.y = -event.acceleration.x;
            accel.z = event.acceleration.z;

            ANDROID_TRACE(d, "accel %ld %.2f %.2f %.2f", event.timestamp, accel.x, accel.y, accel.z);
            break;
        }
        case ASENSOR_TYPE_GYROSCOPE: {
            gyro.x = -event.data[1];
            gyro.y = event.data[0];
            gyro.z = event.data[2];

            ANDROID_TRACE(d, "gyro %ld %.2f %.2f %.2f", event.timestamp, gyro.x, gyro.y, gyro.z);

            // TODO: Make filter handle accelerometer
            struct xrt_vec3 null_accel;

            // Lock last and the fusion.
            os_mutex_lock(&d->lock);

            m_imu_3dof_update(&d->fusion, event.timestamp, &null_accel, &gyro);

            // Now done.
            os_mutex_unlock(&d->lock);
        }
        default: ANDROID_TRACE(d, "Unhandled event type %d", event.type);
        }
    }
  • monon的openxr Runingtime Session创建过程,
    1、有个宏控XR_USE_GRAPHICS_API_VULKAN 表示用vulkan作为图形库的语言。
//src/xrt/state_trackers/oxr/oxr_api_session.c
XrResult
oxr_xrCreateSession(XrInstance instance, const XrSessionCreateInfo *createInfo, XrSession *out_session)
{
    OXR_TRACE_MARKER();

      ret = oxr_session_create(&log, &inst->system, createInfo, &sess);
}
//src/xrt/state_trackers/oxr/oxr_session.c

XrResult
oxr_session_create(struct oxr_logger *log,
                   struct oxr_system *sys,
                   const XrSessionCreateInfo *createInfo,
                   struct oxr_session **out_session)
{
XrResult ret = oxr_session_create_impl(log, sys, createInfo, &xsi, &sess);

}

/* Just the allocation and populate part, so we can use early-returns to
 * simplify code flow and avoid weird if/else */
static XrResult
oxr_session_create_impl(struct oxr_logger *log,
                        struct oxr_system *sys,
                        const XrSessionCreateInfo *createInfo,
                        const struct xrt_session_info *xsi,
                        struct oxr_session **out_session){
#ifdef XR_USE_GRAPHICS_API_VULKAN
    XrGraphicsBindingVulkanKHR const *vulkan =
        OXR_GET_INPUT_FROM_CHAIN(createInfo, XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR, XrGraphicsBindingVulkanKHR);
    if (vulkan != NULL) {
        OXR_VERIFY_ARG_NOT_ZERO(log, vulkan->instance);
        OXR_VERIFY_ARG_NOT_ZERO(log, vulkan->physicalDevice);
        if (vulkan->device == VK_NULL_HANDLE) {
            return oxr_error(log, XR_ERROR_GRAPHICS_DEVICE_INVALID, "VkDevice must not be VK_NULL_HANDLE");
        }

        if (!sys->gotten_requirements) {
            return oxr_error(log, XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING,
                             "Has not called "
                             "xrGetVulkanGraphicsRequirementsKHR");
        }

        if (sys->suggested_vulkan_physical_device == VK_NULL_HANDLE) {
            char *fn = sys->inst->extensions.KHR_vulkan_enable ? "xrGetVulkanGraphicsDeviceKHR"
                                                               : "xrGetVulkanGraphicsDevice2KHR";
            return oxr_error(log, XR_ERROR_VALIDATION_FAILURE, "Has not called %s", fn);
        }

        if (sys->suggested_vulkan_physical_device != vulkan->physicalDevice) {
            char *fn = sys->inst->extensions.KHR_vulkan_enable ? "xrGetVulkanGraphicsDeviceKHR"
                                                               : "xrGetVulkanGraphicsDevice2KHR";
            return oxr_error(
                log, XR_ERROR_VALIDATION_FAILURE,
                "XrGraphicsBindingVulkanKHR::physicalDevice %p must match device %p specified by %s",
                (void *)vulkan->physicalDevice, (void *)sys->suggested_vulkan_physical_device, fn);
        }

        OXR_SESSION_ALLOCATE(log, sys, *out_session);
        OXR_ALLOCATE_NATIVE_COMPOSITOR(log, xsi, *out_session);
        return oxr_session_populate_vk(log, sys, vulkan, *out_session);//这里表示用的是vk,还可以选择用opengl
    }
#endif
}

2、XrGraphicsBindingVulkanKHR 是xr图形绑定vulkan语言支持扩展

//src/xrt/state_trackers/oxr/oxr_session_gfx_vk.c
XrResult
oxr_session_populate_vk(struct oxr_logger *log,
                        struct oxr_system *sys,
                        XrGraphicsBindingVulkanKHR const *next,
                        struct oxr_session *sess)
{
struct xrt_compositor_native *xcn = sess->xcn;
    struct xrt_compositor_vk *xcvk = xrt_gfx_vk_provider_create( //
        xcn,                                                     //
        next->instance,                                          //
        vkGetInstanceProcAddr,                                   //
        next->physicalDevice,                                    //
        next->device,                                            //
        sess->sys->vk.timeline_semaphore_enabled,                //
        next->queueFamilyIndex,                                  //
        next->queueIndex);                                       //

    if (xcvk == NULL) {
        return oxr_error(log, XR_ERROR_INITIALIZATION_FAILED, "Failed to create an vk client compositor");
    }

    sess->compositor = &xcvk->base;
    sess->create_swapchain = oxr_swapchain_vk_create;  //指定交换链创建

    return XR_SUCCESS;
}
//src/xrt/compositor/client/comp_vk_glue.c 
client_vk_compositor_create

//src/xrt/compositor/client/comp_vk_client.c
struct client_vk_compositor *
client_vk_compositor_create(struct xrt_compositor_native *xcn,
                            VkInstance instance,
                            PFN_vkGetInstanceProcAddr getProc,
                            VkPhysicalDevice physicalDevice,
                            VkDevice device,
                            bool timeline_semaphore_enabled,
                            uint32_t queueFamilyIndex,
                            uint32_t queueIndex)
{
//主要是初始化渲染相关的一些东西了
    c->base.base.create_swapchain = client_vk_swapchain_create;
    c->base.base.begin_session = client_vk_compositor_begin_session;
    c->base.base.end_session = client_vk_compositor_end_session;
    c->base.base.wait_frame = client_vk_compositor_wait_frame;
    c->base.base.begin_frame = client_vk_compositor_begin_frame;
    c->base.base.discard_frame = client_vk_compositor_discard_frame;
    c->base.base.layer_begin = client_vk_compositor_layer_begin;
    c->base.base.layer_stereo_projection = client_vk_compositor_layer_stereo_projection;
    c->base.base.layer_stereo_projection_depth = client_vk_compositor_layer_stereo_projection_depth;
    c->base.base.layer_quad = client_vk_compositor_layer_quad;
    c->base.base.layer_cube = client_vk_compositor_layer_cube;
    c->base.base.layer_cylinder = client_vk_compositor_layer_cylinder;
    c->base.base.layer_equirect1 = client_vk_compositor_layer_equirect1;
    c->base.base.layer_equirect2 = client_vk_compositor_layer_equirect2;
    c->base.base.layer_commit = client_vk_compositor_layer_commit;
    c->base.base.destroy = client_vk_compositor_destroy;
    c->base.base.poll_events = client_vk_compositor_poll_events;
}

3、client 里面的client_vk_compositor_create 中的c->base.base.begin_session = client_vk_compositor_begin_session; 可以看出 调用到 xc->begin_session ,这里的xc 就是传过来的
xrt_compositor_native 结构体中的base -->struct xrt_compositor base;

static xrt_result_t
client_vk_compositor_begin_session(struct xrt_compositor *xc, enum xrt_view_type type)
{
    COMP_TRACE_MARKER();

    struct client_vk_compositor *c = client_vk_compositor(xc);

    // Pipe down call into native compositor.
    return xrt_comp_begin_session(&c->xcn->base, type);//这里传过去的xrt_compositor_native 中的     
 xrt_compositor 
}

static inline xrt_result_t
xrt_comp_begin_session(struct xrt_compositor *xc, enum xrt_view_type view_type)
{
    return xc->begin_session(xc, view_type);
}

4、xrt_compositor_native 的xrt_compositor 的begin_session 函数指针在 src/xrt/compositor/main/comp_compositor.c 的xrt_gfx_provider_create_system 方法中被赋值

xrt_result_t
xrt_gfx_provider_create_system(struct xrt_device *xdev, struct xrt_system_compositor **out_xsysc)
{
    struct comp_compositor *c = U_TYPED_CALLOC(struct comp_compositor);

    c->base.base.base.begin_session = compositor_begin_session;  //这里赋值,其他的几个方法类似
    c->base.base.base.end_session = compositor_end_session;
    c->base.base.base.predict_frame = compositor_predict_frame;
    c->base.base.base.mark_frame = compositor_mark_frame;
    c->base.base.base.begin_frame = compositor_begin_frame;
    c->base.base.base.discard_frame = compositor_discard_frame;
    c->base.base.base.layer_commit = compositor_layer_commit;
    c->base.base.base.poll_events = compositor_poll_events;
    c->base.base.base.destroy = compositor_destroy;
    c->frame.waited.id = -1;
    c->frame.rendering.id = -1;
    c->xdev = xdev;

5、至此compositor_begin_session 创建完成 ,特别记录compositor_layer_commit 函数是应用提交一帧后怎么绘制到屏幕的流程,下面是用perfetto抓取的trace。


image.png
  • monon的openxr渲染提交命令缓冲过程
src/xrt/compositor/main/comp_compositor.c
static xrt_result_t
compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphics_sync_handle_t sync_handle)
{
comp_renderer_draw(c->r);
}

//src/xrt/compositor/main/comp_renderer.c
void
comp_renderer_draw(struct comp_renderer *r)
{

comp_target_mark_begin(ct, c->frame.rendering.id, os_monotonic_get_ns());
if (use_compute) {
        dispatch_compute(r, &crc);
    } else {
        dispatch_graphics(r, &rr);
    }

    renderer_present_swapchain_image(r, c->frame.rendering.desired_present_time_ns,
                                     c->frame.rendering.present_slop_ns);

}


static void
dispatch_graphics(struct comp_renderer *r, struct comp_rendering *rr)
{
renderer_get_view_projection(r);
comp_layer_renderer_draw(r->lr);

renderer_build_rendering(r, rr, rtr, src_samplers, src_image_views, src_norm_rects);
renderer_submit_queue(r, rr->cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
}
//src/xrt/compositor/main/comp_layer_renderer.c
void
comp_layer_renderer_draw(struct comp_layer_renderer *self)
{
    COMP_TRACE_MARKER();

    struct vk_bundle *vk = self->vk;

    VkCommandBuffer cmd_buffer;
    if (vk_init_cmd_buffer(vk, &cmd_buffer) != VK_SUCCESS)
        return;
    os_mutex_lock(&vk->cmd_pool_mutex);
    if (self->layer_count == 0) {
        _render_stereo(self, vk, cmd_buffer, &background_color_idle);
    } else {
        _render_stereo(self, vk, cmd_buffer, &background_color_active);
    }
    os_mutex_unlock(&vk->cmd_pool_mutex);

    VkResult res = vk_submit_cmd_buffer(vk, cmd_buffer);
    vk_check_error("vk_submit_cmd_buffer", res, );
}

vk_submit_cmd_buffer 在 src/xrt/auxiliary/vk/vk_helpers.c 中
使用vkQueueSubmit函数向图像队列提交命令缓冲区

  • runtime 迷惑的结构体定义,我们看到这种comp_compositor ,c->base.base.base.begin_session 有三个base 分别代表啥呢
xrt_result_t
xrt_gfx_provider_create_system(struct xrt_device *xdev, struct xrt_system_compositor **out_xsysc)
{
    struct comp_compositor *c = U_TYPED_CALLOC(struct comp_compositor);

    c->base.base.base.begin_session = compositor_begin_session;
    c->base.base.base.end_session = compositor_end_session;

1、comp_compositor 结构体

struct comp_compositor
{
    struct comp_base base;

    //! Renderer helper.
    struct comp_renderer *r;

    //! The target we are displaying to.
    struct comp_target *target;

    //! The device we are displaying to.
    struct xrt_device *xdev;

    //! The settings.
    struct comp_settings settings;

    //! Vulkan shaders that the compositor uses.
    struct comp_shaders shaders;

    //! Timestamp of last-rendered (immersive) frame.
    int64_t last_frame_time_ns;

    //! State for generating the correct set of events.
    enum comp_state state;

    /*!
     * @brief Data exclusive to the begin_frame/end_frame for computing an
     * estimate of the app's needs.
     */
    struct
    {
        int64_t last_begin;
        int64_t last_end;
    } app_profiling;

    struct
    {
        //! Current Index for times_ns.
        int index;

        //! Timestamps of last-rendered (immersive) frames.
        int64_t times_ns[NUM_FRAME_TIMES];

        //! Frametimes between last-rendered (immersive) frames.
        float timings_ms[NUM_FRAME_TIMES];

        //! Average FPS of last NUM_FRAME_TIMES rendered frames.
        float fps;

        struct u_var_timing *debug_var;
    } compositor_frame_times;

    struct
    {
        struct comp_frame waited;
        struct comp_frame rendering;
    } frame;

    struct
    {
        //! Temporarily disable ATW
        bool atw_off;
    } debug;

    struct comp_resources nr;
};

2、struct comp_base base; 主要封装了xrt_compositor_native 和 vulkan

struct comp_base
{
    //! Base native compositor.
    struct xrt_compositor_native base;

    //! Vulkan bundle of useful things, used by swapchain and fence.
    struct vk_bundle vk;

    //! For default @ref xrt_compositor::wait_frame.
    struct os_precise_sleeper sleeper;

    //! Swapchain garbage collector, used by swapchain, child class needs to call.
    struct comp_swapchain_gc cscgc;

    //! We only need to track a single slot.
    struct comp_layer_slot slot;
};

3、struct xrt_compositor_native base; 封装了xrt_compositor

struct xrt_compositor_native
{
    //! @public Base
    struct xrt_compositor base;
};

4、struct xrt_compositor base; 里面主要是定义了一些函数指针

struct xrt_compositor
{
    /*!
     * Capabilities and recommended values information.
     */
    struct xrt_compositor_info info;

    /*!
     * Create a swapchain with a set of images.
     *
     * The pointer pointed to by @p out_xsc has to either be NULL or a valid
     * @ref xrt_swapchain pointer. If there is a valid @ref xrt_swapchain
     * pointed by the pointed pointer it will have it reference decremented.
     */
    xrt_result_t (*create_swapchain)(struct xrt_compositor *xc,
                                     const struct xrt_swapchain_create_info *info,
                                     struct xrt_swapchain **out_xsc);
  • 因为openxr的渲染部分是用vulkan实现的,最后可以再看下vulkan绘制出一个三角形流程可以从大的方面理解openxr的渲染流程
init_vulkan_instance();//创建Vulkan实例
 enumerate_vulkan_phy_devices();//获取物理设备列表
create_vulkan_devices();//创建逻辑设备
create_vulkan_CommandBuffer();//创建命令缓冲
init_queue();//获取设备中支持图形工作的队列
create_vulkan_swapChain();//初始化交换链
create_vulkan_DepthBuffer();//创建深度缓冲
create_render_pass();//创建渲染通道
create_frame_buffer();//创建帧缓冲
createDrawableObject();//创建绘制用的物体
initPipeline();//初始化渲染管线
createFence();//创建栅栏
initPresentInfo();//初始化呈现信息
initMatrix();//初始化基本变换矩阵、摄像机矩阵、投影矩阵
drawObject();//执行绘制
destroyFence();//销毁栅栏
destroyPipeline();//销毁管线
destroyDrawableObject();//销毁绘制用物体
destroy_frame_buffer();//销毁帧缓冲
destroy_render_pass();//销毁渲染通道相关
destroy_vulkan_DepthBuffer();//销毁深度缓冲相关
destroy_vulkan_swapChain();//销毁交换链相关
destroy_vulkan_CommandBuffer();//销毁命令缓冲
destroy_vulkan_devices();//销毁逻辑设备
destroy_vulkan_instance();//销毁Vulkan 实例
  • 绘制一帧画面要经过
    1、从交换链获取当前帧索引
    2、为渲染通道设置当前帧索引
    3、初始化命令缓冲,然后启动命令缓冲
    4、将当前帧送入一直变量缓冲
    5、更新绘制用描述集
    6、绘制
    7、结束渲染通道 结束命令缓冲 等待信号量
    8、提交命令缓冲
    9、等待渲染完毕
    10、为呈现信息指定当前交换链索引
    11、呈现
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容