wolvic 浏览器 openxr 支持现状

865 阅读6分钟

背景

Wolvic 始于 Firefox Reality 浏览器进化树的一个新分支。Firefox Reality 是Mozilla公司启动的一个有趣而重要的开发项目,当时 XR 设备仍相当年轻。从那时起,更成熟的独立设备组件逐步进入了广泛的消费市场,Firefox Reality 项目需要新的投资、更新和扶持。

2022年,Igalia 接过该项目管理权,并将其命名为 “Wolvic”,在过渡期间,发布为 Beta 测试版本。

提交概览

涉及到 openxr 相关的提交如下:

1d8e3db6 OpenXR: Emulate Oculus Touch interaction profile if hand tracking is enabled (#385) 
461edbad [OpenXR][Pico] Enable hand tracking in the manifest 
bc69476d [OpenXR] Vertically flip OpenXR layers in Meta 
1996822d [OpenXR][Pico] Remove scaling factor (#387) 
fdbe0b2a [OpenXR][Pico] Fix brightness issues 
bc6573e8 [OpenXR] Properly initialize the DeviceType 
094de340 OpenXR: Detect runtime's support for hand tracking 
ba767abb OpenXR: Retrieve hand mesh using XR_FB_hand_tracking mesh extension 
359c9df3 Add Pico XR build variant  #2
c79773a9 [OpenXR] Call xrBeginSession after resuming app only for HVR 
7b42cacb Add com.oculus.intent.category to Meta's manifests 
14d96c73 Add the include path for OVR platform on Meta OpenXR builds 
b198a4c3 [OVR] Move OVR user entitlement out of DeviceDelegateOculusVR 
701bea45 OpenXR: Add vertical flip to equirect layers on Oculus 
8b7d8703 OpenXR: Fix brightness of Wolvic logo during splash screen 
ec7997ef OpenXR: Enable the XR_EXT_hand_tracking and XR_FB_hand_tracking_aim extensions 
5fe39948 OpenXR: Add code to retrieve hand joints and aim state 
a955fc51 Make HVR backend prefer the standalone OpenXR headers from third party repo 
77071d75 Add some extra HVR & OpenXR debugging 
a9eec019 [HVR] Do not exit the OpenXR session when exiting the security zone 
5716b81d Filter out incompatible controllers based on DoF (#294) 
c36889f3 Make BUTTON_APP exit fullscreen mode (#278) 
4f6884bd Fix Oculus Touch2 OpenXR input mapping 
41638c50 Get controller's haptic count from input profiles 
b31351f3 Add support for haptic feedback in OpenXR backend 
f7ada466 Set 'StageParameters' display capability when a stage space is available 
bfe26f11 HVR: Add a workaround for 6DoF controllers' poses (#115) 
327e45e4 6DoF profile for HVR controllers 
bc6f43f6 OpenXR Improvements: Always use localSpace. Fix frame prediction. Add 6DOF controller buttons (#3981) 
7ed449cc Fix OpenXR Cubemap layers brightness issues using SRGBA color format (#3976) 
40b4c40e Fix SGBA related brightness issues in Oculus OpenXR builds (#3975) 
ff6d40f1 OpenXR Input Refactor (#3965) 
ca22eb78 Fix OpenXR Quad layer placement. Fix invalid quaternion assertion failure.. (#3961) 
3c70b993 OpenXR implementation (#3428)  #1

可以看到其中比较重要的是 #1 和 #2。 #1 奠定了 wolvic 浏览器对于 OpenXR的支持基础,后续不过是在修复bug,重构代码,以及新增设备支持。 #2 则添加了 pico 的 OpenXR 支持。

代码目录概览

├── app 
    ├── src 
        │
        ├── common 
        │   ├── chromium // 使用 chromium 内核
        │   ├── gecko  // 使用 gecko 内核
        │   └── shared // chromium 或者 gecko 公共的代码,包括页面组件,输入,声音等等。
        ......
        ├── main 
        ......
        │   ├── cpp  // 包含应用入口以及最上层的封装,包含主要的执行逻辑。
        ......
        ├── openxr 
        │   └── cpp  // 实现了具体的启动 OpenXR ,绘制等等的逻辑。
        ├── picoxr 
        │   ├── assets // 手柄资源
        │   ├── java // PlatformActivity,最上层 VRBrowserActivity 的基类,定义了 Pico 的一些特殊行为。
        ......
......
├── third_party 
│   ├── OpenXR-SDK // OpenXR 的 sdk 目录。
│   └── picoxr // Pico 的 OpenXR 的 sdk 目录。
......

整个工程的大致结构如下:

流程图.jpg

整体代码分层设计比较清晰,添加对应的平台支持也比较方便,而且无需关注上层的 UI 组件等等,只需要实现了对应的平台层接口即可。

OpenXR 相关代码流程分析

整体启动流程和 hello_xr 的官方示例比较类似。启动点位于 native-lib.cpp 的 android_main 方法。整个流程大致如下: UML 图.jpg

大致流程如上图。而 OpenXR 的启动关键位置在 DeviceDelegateOpenXR::Create 和 当 APP_CMD_INIT_WINDOW 方法回调时的 EnterVR 方法,以及 BrowserWorld::Instance().Draw() 方法来完成。下面来分别看看上述三个方法分别承担的任务。

DeviceDelegateOpenXR::Create

流程图 (1).jpg

执行了 OpenXR 环境的创建,并最终设定设备类型。

EnterVR

流程图 (2).jpg

从图上可以看出,EnterVR 执行了基本的启动流程,初始化了 session, 创建了 SwapChain,初始化了手柄及事件,处理 OpenXR 返回的事件回调等。

BrowserWorld::Instance().Draw()

Draw 的过程分为两种,一种是首页的界面,也就是进入之后的图标动画,加上整个界面的浏览器窗口,天空盒背景等等。一种是点击某个网页,进入 VR 的模式。两种都是走 Draw 方法,但是会有一些不同。

共同流程

流程图 (3).jpg

从图上可以看出,整个绘制流程分为三步,先 StartFrame, 再执行 Draw,最后 EndFrame,整个绘制就是这三步循环执行。Draw 的过程被代理给 drawHandler,EndFrame 过程被代理给 frameEndHandler,这两个都是在 StartFrame 的时候通过 TickXXX 方法设置的。在 StartFrame 的时候会检查是否初始化过GL,如果没有会调用 context 的 InitializeGL() 方法,其中初始化了 egl 相关的内容。 ModelLoader 则负责初始化模型加载器的初始化。再调用一下 device 的 ProcessEvent 检查一下当前状态,是否应该退出等等。接下来就分为三种模式, TickSplashAnimation 表示的是进入应用时的图标加载动画,TickWorld 则是进入应用之后加载过动画展现出来的首页,包括整个大的背景,浏览器窗口,菜单栏等等。TickImmersive 则是当点击一个 VR 网页的进入 VR 按钮时进入的一个 VR 空间。此处略过动画加载,先来分别看看 TickWorld 和 TickImmersive 的流程。

TickWorld

流程图 (4).jpg 从图上可以看出,TickWorld 中主要是对一些矩阵信息做了更新,为下一步的 Draw 做准备。他设置了 Draw 的方法为 DrawWorld。再来看一下 DrawWorld 中做了什么。 流程图 (5).jpg

整个绘制流程也很清晰,首先绑定眼睛的 swapChain,再逐个从 skybox 到 背景,到手柄,到最后的UI组件,依次开始绘制。Wolvic 中使用了 Layers 这个概念来代表每个绘制对象,可能会有柱形的应用,可能会有立方体的,可能会有矩形的,这每一个都有各自的 gl 的 program,都有各自的 OpenXR 中的 space。更加详细的此处暂时略过,后续有需求可以加以完善。

到此为止,首页的绘制过程就结束了。

TickImmersive

流程图 (6).jpg VR 空间的绘制流程大致如上,wolvic 和 gecko 内核的交流主要就是通过 externalVR 来实现的。在 tick 过程中,首先等待 gecko 内核侧返回帧信息,这个过程是加锁阻塞等待的,有可能会失败,失败之后就会抛弃此帧,图上未画出。正常情况下,都会获取成功,拿到帧信息之后,再获取加载状态,是在加载中,还是渲染中。之后调用设备的 StartFrame,此方法上面已经说过,此处略过,然后传递位置信息到 gecko 中。当还在加载中时,会去 TickWebXRIntersitial,此方法绘制的是加载过程中的界面,此处也先不提。反之,在渲染时,先设置 VR 空间大小,通过 blitter 来存储 gecko 传过来的 texture 信息。最后设置了 drawHandler和 frameEndhandler。这两个在上述共同流程中可以看到,会被 Draw 过程调用。具体的绘制在 DrawImmersive 方法中,先让设备绑定 swapChain,然后把 blitter 中的 texture 通过 gl 调用,绘制上去。

Wolvic 中没有对 VR 空间的每一帧,每一个 texture 的生成过程,此部分都在 gecko 内核中。wolvic 所做的就是拿到这些 texture,用 OpenXR 的绘制流程去绘制这些 texture,很好的解藕了内核和上层 VR 实现之间的关系。