feat: webxr feature flag (#34241)

* Add webxr feature flag

Add webxr feature flag to embedder_traits

Add webxr flag to constellation

Add webxr flag to compositor

Add webxr flag to canvas

Turn registry into optional

Add webxr flag to servo lib

Signed-off-by: Wu Yu Wei <yuweiwu@pm.me>
Co-authored-by: august kline <me@augustkline.com>

* Cargo fmt

Signed-off-by: Wu Yu Wei <yuweiwu@pm.me>

* Add missing license

Signed-off-by: Wu Yu Wei <yuweiwu@pm.me>

* Cargo clippy

Signed-off-by: Wu Yu Wei <yuweiwu@pm.me>

---------

Signed-off-by: Wu Yu Wei <yuweiwu@pm.me>
Co-authored-by: august kline <me@augustkline.com>
This commit is contained in:
Ngo Iok Ui (Wu Yu Wei) 2024-11-14 02:16:58 +09:00 committed by GitHub
parent 91f96cc9dd
commit 47a243614f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 456 additions and 374 deletions

View file

@ -13,6 +13,7 @@ path = "lib.rs"
[features]
webgl_backtrace = ["canvas_traits/webgl_backtrace"]
webxr = ["dep:webxr", "dep:webxr-api"]
[dependencies]
app_units = { workspace = true }
@ -46,5 +47,5 @@ unicode-script = { workspace = true }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_traits = { workspace = true }
webxr = { workspace = true, features = ["ipc"] }
webxr-api = { workspace = true, features = ["ipc"] }
webxr = { workspace = true, features = ["ipc"], optional = true }
webxr-api = { workspace = true, features = ["ipc"], optional = true }

View file

@ -13,3 +13,5 @@ pub mod canvas_paint_thread;
mod webgl_limits;
mod webgl_mode;
pub mod webgl_thread;
#[cfg(feature = "webxr")]
mod webxr;

View file

@ -17,14 +17,17 @@ use webrender_traits::{
RenderingContext, WebrenderExternalImageApi, WebrenderExternalImageRegistry,
WebrenderImageSource,
};
#[cfg(feature = "webxr")]
use webxr::SurfmanGL as WebXRSurfman;
#[cfg(feature = "webxr")]
use webxr_api::LayerGrandManager as WebXRLayerGrandManager;
use crate::webgl_thread::{WebGLThread, WebGLThreadInit, WebXRBridgeInit};
use crate::webgl_thread::{WebGLThread, WebGLThreadInit};
pub struct WebGLComm {
pub webgl_threads: WebGLThreads,
pub image_handler: Box<dyn WebrenderExternalImageApi>,
#[cfg(feature = "webxr")]
pub webxr_layer_grand_manager: WebXRLayerGrandManager<WebXRSurfman>,
}
@ -40,7 +43,9 @@ impl WebGLComm {
debug!("WebGLThreads::new()");
let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap();
let webrender_swap_chains = SwapChains::new();
let webxr_init = WebXRBridgeInit::new(sender.clone());
#[cfg(feature = "webxr")]
let webxr_init = crate::webxr::WebXRBridgeInit::new(sender.clone());
#[cfg(feature = "webxr")]
let webxr_layer_grand_manager = webxr_init.layer_grand_manager();
// This implementation creates a single `WebGLThread` for all the pipelines.
@ -54,6 +59,7 @@ impl WebGLComm {
connection: surfman.connection(),
adapter: surfman.adapter(),
api_type,
#[cfg(feature = "webxr")]
webxr_init,
};
@ -64,6 +70,7 @@ impl WebGLComm {
WebGLComm {
webgl_threads: WebGLThreads(sender),
image_handler: Box::new(external),
#[cfg(feature = "webxr")]
webxr_layer_grand_manager,
}
}

View file

@ -3,7 +3,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::{slice, thread};
@ -11,14 +10,16 @@ use std::{slice, thread};
use bitflags::bitflags;
use byteorder::{ByteOrder, NativeEndian, WriteBytesExt};
use canvas_traits::webgl;
#[cfg(feature = "webxr")]
use canvas_traits::webgl::WebXRCommand;
use canvas_traits::webgl::{
webgl_channel, ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, AlphaTreatment,
ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, AlphaTreatment,
GLContextAttributes, GLLimits, GlType, InternalFormatIntVec, ProgramLinkInfo, TexDataType,
TexFormat, WebGLBufferId, WebGLChan, WebGLCommand, WebGLCommandBacktrace, WebGLContextId,
WebGLCreateContextResult, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLMsg,
WebGLMsgSender, WebGLProgramId, WebGLQueryId, WebGLReceiver, WebGLRenderbufferId,
WebGLSLVersion, WebGLSamplerId, WebGLSender, WebGLShaderId, WebGLSyncId, WebGLTextureId,
WebGLVersion, WebGLVertexArrayId, WebXRCommand, WebXRLayerManagerId, YAxisTreatment,
WebGLVersion, WebGLVertexArrayId, YAxisTreatment,
};
use euclid::default::Size2D;
use fnv::FnvHashMap;
@ -39,22 +40,15 @@ use webrender_api::{
ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey,
};
use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType};
use webxr::SurfmanGL as WebXRSurfman;
use webxr_api::{
ContextId as WebXRContextId, Error as WebXRError, GLContexts as WebXRContexts,
GLTypes as WebXRTypes, LayerGrandManager as WebXRLayerGrandManager,
LayerGrandManagerAPI as WebXRLayerGrandManagerAPI, LayerId as WebXRLayerId,
LayerInit as WebXRLayerInit, LayerManager as WebXRLayerManager,
LayerManagerAPI as WebXRLayerManagerAPI, LayerManagerFactory as WebXRLayerManagerFactory,
SubImages as WebXRSubImages,
};
use crate::webgl_limits::GLLimitsDetect;
#[cfg(feature = "webxr")]
use crate::webxr::{WebXRBridge, WebXRBridgeContexts, WebXRBridgeInit};
struct GLContextData {
ctx: Context,
gl: Rc<Gl>,
glow: glow::Context,
pub(crate) struct GLContextData {
pub(crate) ctx: Context,
pub(crate) gl: Rc<Gl>,
pub(crate) glow: glow::Context,
state: GLState,
attributes: GLContextAttributes,
}
@ -212,6 +206,7 @@ pub(crate) struct WebGLThread {
webrender_swap_chains: SwapChains<WebGLContextId, Device>,
/// Whether this context is a GL or GLES context.
api_type: GlType,
#[cfg(feature = "webxr")]
/// The bridge to WebXR
pub webxr_bridge: WebXRBridge,
}
@ -227,6 +222,7 @@ pub(crate) struct WebGLThreadInit {
pub connection: Connection,
pub adapter: Adapter,
pub api_type: GlType,
#[cfg(feature = "webxr")]
pub webxr_init: WebXRBridgeInit,
}
@ -246,6 +242,7 @@ impl WebGLThread {
connection,
adapter,
api_type,
#[cfg(feature = "webxr")]
webxr_init,
}: WebGLThreadInit,
) -> Self {
@ -263,6 +260,7 @@ impl WebGLThread {
receiver: receiver.into_inner(),
webrender_swap_chains,
api_type,
#[cfg(feature = "webxr")]
webxr_bridge: WebXRBridge::new(webxr_init),
}
}
@ -363,8 +361,9 @@ impl WebGLThread {
WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => {
self.handle_webgl_command(ctx_id, command, backtrace);
},
WebGLMsg::WebXRCommand(command) => {
self.handle_webxr_command(command);
WebGLMsg::WebXRCommand(_command) => {
#[cfg(feature = "webxr")]
self.handle_webxr_command(_command);
},
WebGLMsg::SwapBuffers(swap_ids, sender, sent_time) => {
self.handle_swap_buffers(swap_ids, sender, sent_time);
@ -380,6 +379,7 @@ impl WebGLThread {
false
}
#[cfg(feature = "webxr")]
/// Handles a WebXR message
fn handle_webxr_command(&mut self, command: WebXRCommand) {
trace!("processing {:?}", command);
@ -723,17 +723,20 @@ impl WebGLThread {
&mut self.bound_context_id,
);
// Destroy WebXR layers associated with this context
let webxr_context_id = WebXRContextId::from(context_id);
let mut webxr_contexts = WebXRBridgeContexts {
contexts: &mut self.contexts,
bound_context_id: &mut self.bound_context_id,
};
self.webxr_bridge.destroy_all_layers(
&mut self.device,
&mut webxr_contexts,
webxr_context_id,
);
#[cfg(feature = "webxr")]
{
// Destroy WebXR layers associated with this context
let webxr_context_id = webxr_api::ContextId::from(context_id);
let mut webxr_contexts = WebXRBridgeContexts {
contexts: &mut self.contexts,
bound_context_id: &mut self.bound_context_id,
};
self.webxr_bridge.destroy_all_layers(
&mut self.device,
&mut webxr_contexts,
webxr_context_id,
);
}
// Release GL context.
let mut data = match self.contexts.remove(&context_id) {
@ -849,7 +852,7 @@ impl WebGLThread {
}
/// Gets a reference to a Context for a given WebGLContextId and makes it current if required.
fn make_current_if_needed<'a>(
pub(crate) fn make_current_if_needed<'a>(
device: &Device,
context_id: WebGLContextId,
contexts: &'a FnvHashMap<WebGLContextId, GLContextData>,
@ -868,7 +871,7 @@ impl WebGLThread {
}
/// Gets a mutable reference to a GLContextWrapper for a WebGLContextId and makes it current if required.
fn make_current_if_needed_mut<'a>(
pub(crate) fn make_current_if_needed_mut<'a>(
device: &Device,
context_id: WebGLContextId,
contexts: &'a mut FnvHashMap<WebGLContextId, GLContextData>,
@ -3016,313 +3019,3 @@ impl FramebufferRebindingInfo {
);
}
}
/// Bridge between WebGL and WebXR
pub(crate) struct WebXRBridge {
factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>,
managers: HashMap<WebXRLayerManagerId, Box<dyn WebXRLayerManagerAPI<WebXRSurfman>>>,
next_manager_id: u32,
}
impl WebXRBridge {
pub(crate) fn new(init: WebXRBridgeInit) -> WebXRBridge {
let WebXRBridgeInit {
factory_receiver, ..
} = init;
let managers = HashMap::new();
let next_manager_id = 1;
WebXRBridge {
factory_receiver,
managers,
next_manager_id,
}
}
}
impl WebXRBridge {
#[allow(unsafe_code)]
fn create_layer_manager(
&mut self,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
) -> Result<WebXRLayerManagerId, WebXRError> {
let factory = self
.factory_receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)?;
let manager = factory.build(device, contexts)?;
let manager_id = unsafe { WebXRLayerManagerId::new(self.next_manager_id) };
self.next_manager_id += 1;
self.managers.insert(manager_id, manager);
Ok(manager_id)
}
fn destroy_layer_manager(&mut self, manager_id: WebXRLayerManagerId) {
self.managers.remove(&manager_id);
}
fn create_layer(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
context_id: WebXRContextId,
layer_init: WebXRLayerInit,
) -> Result<WebXRLayerId, WebXRError> {
let manager = self
.managers
.get_mut(&manager_id)
.ok_or(WebXRError::NoMatchingDevice)?;
manager.create_layer(device, contexts, context_id, layer_init)
}
fn destroy_layer(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
context_id: WebXRContextId,
layer_id: WebXRLayerId,
) {
if let Some(manager) = self.managers.get_mut(&manager_id) {
manager.destroy_layer(device, contexts, context_id, layer_id);
}
}
fn destroy_all_layers(
&mut self,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
context_id: WebXRContextId,
) {
for manager in self.managers.values_mut() {
#[allow(clippy::unnecessary_to_owned)] // Needs mutable borrow later in destroy
for (other_id, layer_id) in manager.layers().to_vec() {
if other_id == context_id {
manager.destroy_layer(device, contexts, context_id, layer_id);
}
}
}
}
fn begin_frame(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<Vec<WebXRSubImages>, WebXRError> {
let manager = self
.managers
.get_mut(&manager_id)
.ok_or(WebXRError::NoMatchingDevice)?;
manager.begin_frame(device, contexts, layers)
}
fn end_frame(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<(), WebXRError> {
let manager = self
.managers
.get_mut(&manager_id)
.ok_or(WebXRError::NoMatchingDevice)?;
manager.end_frame(device, contexts, layers)
}
}
pub(crate) struct WebXRBridgeInit {
sender: WebGLSender<WebGLMsg>,
factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>,
factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>,
}
impl WebXRBridgeInit {
pub(crate) fn new(sender: WebGLSender<WebGLMsg>) -> WebXRBridgeInit {
let (factory_sender, factory_receiver) = crossbeam_channel::unbounded();
WebXRBridgeInit {
sender,
factory_sender,
factory_receiver,
}
}
pub(crate) fn layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> {
WebXRLayerGrandManager::new(WebXRBridgeGrandManager {
sender: self.sender.clone(),
factory_sender: self.factory_sender.clone(),
})
}
}
struct WebXRBridgeGrandManager {
sender: WebGLSender<WebGLMsg>,
// WebXR layer manager factories use generic trait objects under the
// hood, which aren't deserializable (even using typetag)
// so we can't send them over the regular webgl channel.
// Fortunately, the webgl thread runs in the same process as
// the webxr threads, so we can use a crossbeam channel to send
// factories.
factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>,
}
impl WebXRLayerGrandManagerAPI<WebXRSurfman> for WebXRBridgeGrandManager {
fn create_layer_manager(
&self,
factory: WebXRLayerManagerFactory<WebXRSurfman>,
) -> Result<WebXRLayerManager, WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self.factory_sender.send(factory);
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayerManager(
sender,
)));
let sender = self.sender.clone();
let manager_id = receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)??;
let layers = Vec::new();
Ok(WebXRLayerManager::new(WebXRBridgeManager {
manager_id,
sender,
layers,
}))
}
fn clone_layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> {
WebXRLayerGrandManager::new(WebXRBridgeGrandManager {
sender: self.sender.clone(),
factory_sender: self.factory_sender.clone(),
})
}
}
struct WebXRBridgeManager {
sender: WebGLSender<WebGLMsg>,
manager_id: WebXRLayerManagerId,
layers: Vec<(WebXRContextId, WebXRLayerId)>,
}
impl<GL: WebXRTypes> WebXRLayerManagerAPI<GL> for WebXRBridgeManager {
fn create_layer(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
context_id: WebXRContextId,
init: WebXRLayerInit,
) -> Result<WebXRLayerId, WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayer(
self.manager_id,
context_id,
init,
sender,
)));
let layer_id = receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)??;
self.layers.push((context_id, layer_id));
Ok(layer_id)
}
fn destroy_layer(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
context_id: WebXRContextId,
layer_id: WebXRLayerId,
) {
self.layers.retain(|&ids| ids != (context_id, layer_id));
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayer(
self.manager_id,
context_id,
layer_id,
)));
}
fn layers(&self) -> &[(WebXRContextId, WebXRLayerId)] {
&self.layers[..]
}
fn begin_frame(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<Vec<WebXRSubImages>, WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::BeginFrame(
self.manager_id,
layers.to_vec(),
sender,
)));
receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)?
}
fn end_frame(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<(), WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::EndFrame(
self.manager_id,
layers.to_vec(),
sender,
)));
receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)?
}
}
impl Drop for WebXRBridgeManager {
fn drop(&mut self) {
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayerManager(
self.manager_id,
)));
}
}
struct WebXRBridgeContexts<'a> {
contexts: &'a mut FnvHashMap<WebGLContextId, GLContextData>,
bound_context_id: &'a mut Option<WebGLContextId>,
}
impl<'a> WebXRContexts<WebXRSurfman> for WebXRBridgeContexts<'a> {
fn context(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&mut Context> {
let data = WebGLThread::make_current_if_needed_mut(
device,
WebGLContextId::from(context_id),
self.contexts,
self.bound_context_id,
)?;
Some(&mut data.ctx)
}
fn bindings(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&glow::Context> {
let data = WebGLThread::make_current_if_needed(
device,
WebGLContextId::from(context_id),
self.contexts,
self.bound_context_id,
)?;
Some(&data.glow)
}
}

332
components/canvas/webxr.rs Normal file
View file

@ -0,0 +1,332 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use canvas_traits::webgl::{
webgl_channel, WebGLContextId, WebGLMsg, WebGLSender, WebXRCommand, WebXRLayerManagerId,
};
use fnv::FnvHashMap;
use surfman::{Context, Device};
use webxr::SurfmanGL as WebXRSurfman;
use webxr_api::{
ContextId as WebXRContextId, Error as WebXRError, GLContexts as WebXRContexts,
GLTypes as WebXRTypes, LayerGrandManager as WebXRLayerGrandManager,
LayerGrandManagerAPI as WebXRLayerGrandManagerAPI, LayerId as WebXRLayerId,
LayerInit as WebXRLayerInit, LayerManager as WebXRLayerManager,
LayerManagerAPI as WebXRLayerManagerAPI, LayerManagerFactory as WebXRLayerManagerFactory,
SubImages as WebXRSubImages,
};
use crate::webgl_thread::{GLContextData, WebGLThread};
/// Bridge between WebGL and WebXR
pub(crate) struct WebXRBridge {
factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>,
managers: HashMap<WebXRLayerManagerId, Box<dyn WebXRLayerManagerAPI<WebXRSurfman>>>,
next_manager_id: u32,
}
impl WebXRBridge {
pub(crate) fn new(init: WebXRBridgeInit) -> WebXRBridge {
let WebXRBridgeInit {
factory_receiver, ..
} = init;
let managers = HashMap::new();
let next_manager_id = 1;
WebXRBridge {
factory_receiver,
managers,
next_manager_id,
}
}
}
impl WebXRBridge {
#[allow(unsafe_code)]
pub(crate) fn create_layer_manager(
&mut self,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
) -> Result<WebXRLayerManagerId, WebXRError> {
let factory = self
.factory_receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)?;
let manager = factory.build(device, contexts)?;
let manager_id = unsafe { WebXRLayerManagerId::new(self.next_manager_id) };
self.next_manager_id += 1;
self.managers.insert(manager_id, manager);
Ok(manager_id)
}
pub(crate) fn destroy_layer_manager(&mut self, manager_id: WebXRLayerManagerId) {
self.managers.remove(&manager_id);
}
pub(crate) fn create_layer(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
context_id: WebXRContextId,
layer_init: WebXRLayerInit,
) -> Result<WebXRLayerId, WebXRError> {
let manager = self
.managers
.get_mut(&manager_id)
.ok_or(WebXRError::NoMatchingDevice)?;
manager.create_layer(device, contexts, context_id, layer_init)
}
pub(crate) fn destroy_layer(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
context_id: WebXRContextId,
layer_id: WebXRLayerId,
) {
if let Some(manager) = self.managers.get_mut(&manager_id) {
manager.destroy_layer(device, contexts, context_id, layer_id);
}
}
pub(crate) fn destroy_all_layers(
&mut self,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
context_id: WebXRContextId,
) {
for manager in self.managers.values_mut() {
#[allow(clippy::unnecessary_to_owned)] // Needs mutable borrow later in destroy
for (other_id, layer_id) in manager.layers().to_vec() {
if other_id == context_id {
manager.destroy_layer(device, contexts, context_id, layer_id);
}
}
}
}
pub(crate) fn begin_frame(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<Vec<WebXRSubImages>, WebXRError> {
let manager = self
.managers
.get_mut(&manager_id)
.ok_or(WebXRError::NoMatchingDevice)?;
manager.begin_frame(device, contexts, layers)
}
pub(crate) fn end_frame(
&mut self,
manager_id: WebXRLayerManagerId,
device: &mut Device,
contexts: &mut dyn WebXRContexts<WebXRSurfman>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<(), WebXRError> {
let manager = self
.managers
.get_mut(&manager_id)
.ok_or(WebXRError::NoMatchingDevice)?;
manager.end_frame(device, contexts, layers)
}
}
pub(crate) struct WebXRBridgeInit {
sender: WebGLSender<WebGLMsg>,
factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>,
factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>,
}
impl WebXRBridgeInit {
pub(crate) fn new(sender: WebGLSender<WebGLMsg>) -> WebXRBridgeInit {
let (factory_sender, factory_receiver) = crossbeam_channel::unbounded();
WebXRBridgeInit {
sender,
factory_sender,
factory_receiver,
}
}
pub(crate) fn layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> {
WebXRLayerGrandManager::new(WebXRBridgeGrandManager {
sender: self.sender.clone(),
factory_sender: self.factory_sender.clone(),
})
}
}
struct WebXRBridgeGrandManager {
sender: WebGLSender<WebGLMsg>,
// WebXR layer manager factories use generic trait objects under the
// hood, which aren't deserializable (even using typetag)
// so we can't send them over the regular webgl channel.
// Fortunately, the webgl thread runs in the same process as
// the webxr threads, so we can use a crossbeam channel to send
// factories.
factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>,
}
impl WebXRLayerGrandManagerAPI<WebXRSurfman> for WebXRBridgeGrandManager {
fn create_layer_manager(
&self,
factory: WebXRLayerManagerFactory<WebXRSurfman>,
) -> Result<WebXRLayerManager, WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self.factory_sender.send(factory);
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayerManager(
sender,
)));
let sender = self.sender.clone();
let manager_id = receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)??;
let layers = Vec::new();
Ok(WebXRLayerManager::new(WebXRBridgeManager {
manager_id,
sender,
layers,
}))
}
fn clone_layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> {
WebXRLayerGrandManager::new(WebXRBridgeGrandManager {
sender: self.sender.clone(),
factory_sender: self.factory_sender.clone(),
})
}
}
struct WebXRBridgeManager {
sender: WebGLSender<WebGLMsg>,
manager_id: WebXRLayerManagerId,
layers: Vec<(WebXRContextId, WebXRLayerId)>,
}
impl<GL: WebXRTypes> WebXRLayerManagerAPI<GL> for WebXRBridgeManager {
fn create_layer(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
context_id: WebXRContextId,
init: WebXRLayerInit,
) -> Result<WebXRLayerId, WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayer(
self.manager_id,
context_id,
init,
sender,
)));
let layer_id = receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)??;
self.layers.push((context_id, layer_id));
Ok(layer_id)
}
fn destroy_layer(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
context_id: WebXRContextId,
layer_id: WebXRLayerId,
) {
self.layers.retain(|&ids| ids != (context_id, layer_id));
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayer(
self.manager_id,
context_id,
layer_id,
)));
}
fn layers(&self) -> &[(WebXRContextId, WebXRLayerId)] {
&self.layers[..]
}
fn begin_frame(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<Vec<WebXRSubImages>, WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::BeginFrame(
self.manager_id,
layers.to_vec(),
sender,
)));
receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)?
}
fn end_frame(
&mut self,
_: &mut GL::Device,
_: &mut dyn WebXRContexts<GL>,
layers: &[(WebXRContextId, WebXRLayerId)],
) -> Result<(), WebXRError> {
let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?;
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::EndFrame(
self.manager_id,
layers.to_vec(),
sender,
)));
receiver
.recv()
.map_err(|_| WebXRError::CommunicationError)?
}
}
impl Drop for WebXRBridgeManager {
fn drop(&mut self) {
let _ = self
.sender
.send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayerManager(
self.manager_id,
)));
}
}
pub(crate) struct WebXRBridgeContexts<'a> {
pub(crate) contexts: &'a mut FnvHashMap<WebGLContextId, GLContextData>,
pub(crate) bound_context_id: &'a mut Option<WebGLContextId>,
}
impl<'a> WebXRContexts<WebXRSurfman> for WebXRBridgeContexts<'a> {
fn context(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&mut Context> {
let data = WebGLThread::make_current_if_needed_mut(
device,
WebGLContextId::from(context_id),
self.contexts,
self.bound_context_id,
)?;
Some(&mut data.ctx)
}
fn bindings(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&glow::Context> {
let data = WebGLThread::make_current_if_needed(
device,
WebGLContextId::from(context_id),
self.contexts,
self.bound_context_id,
)?;
Some(&data.glow)
}
}

View file

@ -15,6 +15,7 @@ path = "lib.rs"
default = []
multiview = []
tracing = ["dep:tracing"]
webxr = ["dep:webxr"]
[dependencies]
base = { workspace = true }
@ -46,7 +47,7 @@ tracing = { workspace = true, optional = true }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_traits = { workspace = true }
webxr = { workspace = true }
webxr = { workspace = true, optional = true }
[dev-dependencies]
surfman = { workspace = true }

View file

@ -169,6 +169,7 @@ pub struct IOCompositor<Window: WindowMethods + ?Sized> {
/// The GL bindings for webrender
webrender_gl: Rc<dyn gleam::gl::Gl>,
#[cfg(feature = "webxr")]
/// Some XR devices want to run on the main thread.
pub webxr_main_thread: webxr::MainThreadRegistry,
@ -409,6 +410,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
webrender_api: state.webrender_api,
rendering_context: state.rendering_context,
webrender_gl: state.webrender_gl,
#[cfg(feature = "webxr")]
webxr_main_thread: state.webxr_main_thread,
pending_paint_metrics: HashMap::new(),
cursor: Cursor::None,
@ -1803,7 +1805,11 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
pipeline_ids.push(*pipeline_id);
}
}
let animation_state = if pipeline_ids.is_empty() && !self.webxr_main_thread.running() {
#[cfg(feature = "webxr")]
let webxr_running = self.webxr_main_thread.running();
#[cfg(not(feature = "webxr"))]
let webxr_running = false;
let animation_state = if pipeline_ids.is_empty() && webxr_running {
windowing::AnimationState::Idle
} else {
windowing::AnimationState::Animating
@ -2395,6 +2401,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
CompositionRequest::CompositeNow(_) => self.composite(),
}
#[cfg(feature = "webxr")]
// Run the WebXR main thread
self.webxr_main_thread.run_one_frame();

View file

@ -42,5 +42,6 @@ pub struct InitialCompositorState {
pub webrender_api: RenderApi,
pub rendering_context: RenderingContext,
pub webrender_gl: Rc<dyn gleam::gl::Gl>,
#[cfg(feature = "webxr")]
pub webxr_main_thread: webxr::MainThreadRegistry,
}

View file

@ -8,7 +8,7 @@ use std::fmt::{Debug, Error, Formatter};
use std::time::Duration;
use base::id::{PipelineId, TopLevelBrowsingContextId};
use embedder_traits::{EmbedderProxy, EventLoopWaker};
use embedder_traits::EventLoopWaker;
use euclid::Scale;
use keyboard_types::KeyboardEvent;
use libc::c_void;
@ -219,8 +219,14 @@ pub trait EmbedderMethods {
/// Returns a thread-safe object to wake up the window's event loop.
fn create_event_loop_waker(&mut self) -> Box<dyn EventLoopWaker>;
#[cfg(feature = "webxr")]
/// Register services with a WebXR Registry.
fn register_webxr(&mut self, _: &mut webxr::MainThreadRegistry, _: EmbedderProxy) {}
fn register_webxr(
&mut self,
_: &mut webxr::MainThreadRegistry,
_: embedder_traits::EmbedderProxy,
) {
}
/// Returns the user agent string to report in network requests.
fn get_user_agent_string(&self) -> Option<String> {

View file

@ -455,7 +455,7 @@ pub struct Constellation<STF, SWF> {
webgl_threads: Option<WebGLThreads>,
/// The XR device registry
webxr_registry: webxr_api::Registry,
webxr_registry: Option<webxr_api::Registry>,
/// A channel through which messages can be sent to the canvas paint thread.
canvas_sender: Sender<ConstellationCanvasMsg>,
@ -533,7 +533,7 @@ pub struct InitialConstellationState {
pub webgl_threads: Option<WebGLThreads>,
/// The XR device registry
pub webxr_registry: webxr_api::Registry,
pub webxr_registry: Option<webxr_api::Registry>,
pub glplayer_threads: Option<GLPlayerThreads>,

View file

@ -191,7 +191,7 @@ pub struct InitialPipelineState {
pub webgl_chan: Option<WebGLPipeline>,
/// The XR device registry
pub webxr_registry: webxr_api::Registry,
pub webxr_registry: Option<webxr_api::Registry>,
/// Application window's GL Context for Media player
pub player_context: WindowGLContext,
@ -496,7 +496,7 @@ pub struct UnprivilegedPipelineContent {
cross_process_compositor_api: CrossProcessCompositorApi,
webrender_document: DocumentId,
webgl_chan: Option<WebGLPipeline>,
webxr_registry: webxr_api::Registry,
webxr_registry: Option<webxr_api::Registry>,
player_context: WindowGLContext,
user_agent: Cow<'static, str>,
}

View file

@ -287,7 +287,7 @@ pub struct Window {
#[ignore_malloc_size_of = "defined in webxr"]
#[no_trace]
webxr_registry: webxr_api::Registry,
webxr_registry: Option<webxr_api::Registry>,
/// All of the elements that have an outstanding image request that was
/// initiated by layout during a reflow. They are stored in the script thread
@ -495,7 +495,7 @@ impl Window {
.map(|chan| WebGLCommandSender::new(chan.clone()))
}
pub fn webxr_registry(&self) -> webxr_api::Registry {
pub fn webxr_registry(&self) -> Option<webxr_api::Registry> {
self.webxr_registry.clone()
}
@ -2579,7 +2579,7 @@ impl Window {
creator_url: ServoUrl,
navigation_start: CrossProcessInstant,
webgl_chan: Option<WebGLChan>,
webxr_registry: webxr_api::Registry,
webxr_registry: Option<webxr_api::Registry>,
microtask_queue: Rc<MicrotaskQueue>,
webrender_document: DocumentId,
compositor_api: CrossProcessCompositorApi,

View file

@ -147,9 +147,9 @@ impl XRSystemMethods for XRSystem {
};
}),
);
window
.webxr_registry()
.supports_session(mode.into(), sender);
if let Some(mut r) = window.webxr_registry() {
r.supports_session(mode.into(), sender);
}
promise
}
@ -265,9 +265,9 @@ impl XRSystemMethods for XRSystem {
);
}),
);
window
.webxr_registry()
.request_session(mode.into(), init, sender, frame_sender);
if let Some(mut r) = window.webxr_registry() {
r.request_session(mode.into(), init, sender, frame_sender);
}
promise
}

View file

@ -175,9 +175,9 @@ impl XRTestMethods for XRTest {
);
}),
);
window
.webxr_registry()
.simulate_device_connection(init, sender);
if let Some(mut r) = window.webxr_registry() {
r.simulate_device_connection(init, sender);
}
p
}

View file

@ -669,7 +669,7 @@ pub struct ScriptThread {
/// The WebXR device registry
#[no_trace]
webxr_registry: webxr_api::Registry,
webxr_registry: Option<webxr_api::Registry>,
/// The worklet thread pool
worklet_thread_pool: DomRefCell<Option<Rc<WorkletThreadPool>>>,

View file

@ -31,6 +31,13 @@ webgl_backtrace = [
"canvas/webgl_backtrace",
"canvas_traits/webgl_backtrace",
]
webxr = [
"dep:webxr",
"dep:webxr-api",
"compositing/webxr",
"embedder_traits/webxr",
"canvas/webxr",
]
[dependencies]
background_hang_monitor = { path = "../background_hang_monitor" }
@ -82,8 +89,8 @@ webgpu = { path = "../webgpu" }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_traits = { workspace = true }
webxr = { workspace = true }
webxr-api = { workspace = true }
webxr = { workspace = true, optional = true }
webxr-api = { workspace = true, optional = true }
[target.'cfg(all(not(target_os = "windows"), not(target_os = "ios"), not(target_os = "android"), not(target_arch = "arm"), not(target_arch = "aarch64")))'.dependencies]
gaol = "0.2.1"

View file

@ -396,6 +396,7 @@ where
let WebGLComm {
webgl_threads,
#[cfg(feature = "webxr")]
webxr_layer_grand_manager,
image_handler,
} = WebGLComm::new(
@ -410,9 +411,11 @@ where
external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
// Create the WebXR main thread
#[cfg(feature = "webxr")]
let mut webxr_main_thread =
webxr::MainThreadRegistry::new(event_loop_waker, webxr_layer_grand_manager)
.expect("Failed to create WebXR device registry");
#[cfg(feature = "webxr")]
if pref!(dom.webxr.enabled) {
embedder.register_webxr(&mut webxr_main_thread, embedder_proxy.clone());
}
@ -454,6 +457,7 @@ where
devtools_sender,
webrender_document,
webrender_api_sender,
#[cfg(feature = "webxr")]
webxr_main_thread.registry(),
player_context,
Some(webgl_threads),
@ -491,6 +495,7 @@ where
webrender_api,
rendering_context,
webrender_gl,
#[cfg(feature = "webxr")]
webxr_main_thread,
},
composite_target,
@ -1033,7 +1038,7 @@ fn create_constellation(
devtools_sender: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
webrender_document: DocumentId,
webrender_api_sender: RenderApiSender,
webxr_registry: webxr_api::Registry,
#[cfg(feature = "webxr")] webxr_registry: webxr_api::Registry,
player_context: WindowGLContext,
webgl_threads: Option<WebGLThreads>,
glplayer_threads: Option<GLPlayerThreads>,
@ -1082,7 +1087,10 @@ fn create_constellation(
mem_profiler_chan,
webrender_document,
webrender_api_sender,
webxr_registry,
#[cfg(feature = "webxr")]
webxr_registry: Some(webxr_registry),
#[cfg(not(feature = "webxr"))]
webxr_registry: None,
webgl_threads,
glplayer_threads,
player_context,

View file

@ -11,6 +11,9 @@ rust-version.workspace = true
name = "embedder_traits"
path = "lib.rs"
[features]
webxr = ["dep:webxr-api"]
[dependencies]
base = { workspace = true }
cfg-if = { workspace = true }
@ -23,4 +26,4 @@ num-traits = { workspace = true }
serde = { workspace = true }
servo_url = { path = "../../url" }
webrender_api = { workspace = true }
webxr-api = { workspace = true, features = ["ipc"] }
webxr-api = { workspace = true, features = ["ipc"], optional = true }

View file

@ -15,7 +15,6 @@ use num_derive::FromPrimitive;
use serde::{Deserialize, Serialize};
use servo_url::ServoUrl;
use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
pub use webxr_api::MainThreadWaker as EventLoopWaker;
/// A cursor for the window. This is different from a CSS cursor (see
/// `CursorKind`) in that it has no `Auto` value.
@ -59,6 +58,21 @@ pub enum Cursor {
ZoomOut,
}
#[cfg(feature = "webxr")]
pub use webxr_api::MainThreadWaker as EventLoopWaker;
#[cfg(not(feature = "webxr"))]
pub trait EventLoopWaker: 'static + Send {
fn clone_box(&self) -> Box<dyn EventLoopWaker>;
fn wake(&self);
}
#[cfg(not(feature = "webxr"))]
impl Clone for Box<dyn EventLoopWaker> {
fn clone(&self) -> Self {
EventLoopWaker::clone_box(self.as_ref())
}
}
/// Sends messages to the embedder.
pub struct EmbedderProxy {
pub sender: Sender<(Option<TopLevelBrowsingContextId>, EmbedderMsg)>,

View file

@ -658,7 +658,7 @@ pub struct InitialScriptState {
/// A channel to the WebGL thread used in this pipeline.
pub webgl_chan: Option<WebGLPipeline>,
/// The XR device registry
pub webxr_registry: webxr_api::Registry,
pub webxr_registry: Option<webxr_api::Registry>,
/// The Webrender document ID associated with this thread.
pub webrender_document: DocumentId,
/// Access to the compositor across a process boundary.

View file

@ -39,7 +39,7 @@ ProductName = "Servo"
[features]
debugmozjs = ["libservo/debugmozjs"]
default = ["layout_2013", "max_log_level", "webdriver"]
default = ["layout_2013", "max_log_level", "webdriver", "libservo/webxr"]
jitspew = ["libservo/jitspew"]
js_backtrace = ["libservo/js_backtrace"]
layout_2013 = ["libservo/layout_2013"]