diff options
author | Alan Jeffrey <ajeffrey@mozilla.com> | 2020-04-15 18:04:32 -0500 |
---|---|---|
committer | Alan Jeffrey <ajeffrey@mozilla.com> | 2020-06-28 16:37:45 -0500 |
commit | 349619ed2d741312e34924aabc3e6abcc3c468ed (patch) | |
tree | 1cf963eeca8cdf9ba914a2da7a5bedbae7b9d77f /components/script/dom | |
parent | af110ac21fcf1e108c919f5e9d724b2441996ed6 (diff) | |
download | servo-349619ed2d741312e34924aabc3e6abcc3c468ed.tar.gz servo-349619ed2d741312e34924aabc3e6abcc3c468ed.zip |
Support for webxr layer management
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/bindings/trace.rs | 6 | ||||
-rw-r--r-- | components/script/dom/document.rs | 4 | ||||
-rw-r--r-- | components/script/dom/webglframebuffer.rs | 110 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 23 | ||||
-rw-r--r-- | components/script/dom/webidls/XRLayer.webidl | 3 | ||||
-rw-r--r-- | components/script/dom/webidls/XRWebGLLayer.webidl | 3 | ||||
-rw-r--r-- | components/script/dom/webidls/XRWebGLSubImage.webidl | 6 | ||||
-rw-r--r-- | components/script/dom/xrframe.rs | 10 | ||||
-rw-r--r-- | components/script/dom/xrlayer.rs | 40 | ||||
-rw-r--r-- | components/script/dom/xrrenderstate.rs | 58 | ||||
-rw-r--r-- | components/script/dom/xrsession.rs | 142 | ||||
-rw-r--r-- | components/script/dom/xrwebglbinding.rs | 14 | ||||
-rw-r--r-- | components/script/dom/xrwebgllayer.rs | 156 | ||||
-rw-r--r-- | components/script/dom/xrwebglsubimage.rs | 13 |
14 files changed, 388 insertions, 200 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index d6216cdad48..5a540f3e912 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -60,7 +60,6 @@ use canvas_traits::webgl::{ use canvas_traits::webgl::{GLLimits, WebGLQueryId, WebGLSamplerId}; use canvas_traits::webgl::{WebGLBufferId, WebGLChan, WebGLContextId, WebGLError}; use canvas_traits::webgl::{WebGLFramebufferId, WebGLMsgSender, WebGLPipeline, WebGLProgramId}; -use canvas_traits::webgl::{WebGLOpaqueFramebufferId, WebGLTransparentFramebufferId}; use canvas_traits::webgl::{WebGLReceiver, WebGLRenderbufferId, WebGLSLVersion, WebGLSender}; use canvas_traits::webgl::{WebGLShaderId, WebGLSyncId, WebGLTextureId, WebGLVersion}; use content_security_policy::CspList; @@ -172,7 +171,6 @@ use webgpu::{ WebGPUTexture, WebGPUTextureView, }; use webrender_api::{DocumentId, ExternalImageId, ImageKey}; -use webxr_api::SwapChainId as WebXRSwapChainId; use webxr_api::{Finger, Hand, Ray, View}; unsafe_no_jsmanaged_fields!(Tm); @@ -550,8 +548,6 @@ unsafe_no_jsmanaged_fields!(ExternalImageId); unsafe_no_jsmanaged_fields!(WebGLBufferId); unsafe_no_jsmanaged_fields!(WebGLChan); unsafe_no_jsmanaged_fields!(WebGLFramebufferId); -unsafe_no_jsmanaged_fields!(WebGLOpaqueFramebufferId); -unsafe_no_jsmanaged_fields!(WebGLTransparentFramebufferId); unsafe_no_jsmanaged_fields!(WebGLMsgSender); unsafe_no_jsmanaged_fields!(WebGLPipeline); unsafe_no_jsmanaged_fields!(WebGLProgramId); @@ -587,12 +583,12 @@ unsafe_no_jsmanaged_fields!(Option<ComputePass>); unsafe_no_jsmanaged_fields!(GPUBufferState); unsafe_no_jsmanaged_fields!(GPUCommandEncoderState); unsafe_no_jsmanaged_fields!(Range<u64>); -unsafe_no_jsmanaged_fields!(WebXRSwapChainId); unsafe_no_jsmanaged_fields!(MediaList); unsafe_no_jsmanaged_fields!( webxr_api::Registry, webxr_api::Session, webxr_api::Frame, + webxr_api::LayerId, webxr_api::InputSource, webxr_api::InputId, webxr_api::Joint, diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index ce3204d3797..09b802b20e5 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -106,7 +106,7 @@ use crate::stylesheet_set::StylesheetSetRef; use crate::task::TaskBox; use crate::task_source::{TaskSource, TaskSourceName}; use crate::timers::OneshotTimerCallback; -use canvas_traits::webgl::{self, SwapChainId, WebGLContextId, WebGLMsg}; +use canvas_traits::webgl::{self, WebGLContextId, WebGLMsg}; use content_security_policy::{self as csp, CspList}; use cookie::Cookie; use devtools_traits::ScriptToDevtoolsControlMsg; @@ -2707,7 +2707,7 @@ impl Document { .borrow_mut() .drain() .filter(|(_, context)| context.onscreen()) - .map(|(id, _)| SwapChainId::Context(id)) + .map(|(id, _)| id) .collect(); if dirty_context_ids.is_empty() { diff --git a/components/script/dom/webglframebuffer.rs b/components/script/dom/webglframebuffer.rs index 9f38365c4c7..a0d8025a131 100644 --- a/components/script/dom/webglframebuffer.rs +++ b/components/script/dom/webglframebuffer.rs @@ -14,14 +14,13 @@ use crate::dom::webglrenderbuffer::WebGLRenderbuffer; use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use crate::dom::webgltexture::WebGLTexture; use crate::dom::xrsession::XRSession; +use canvas_traits::webgl::WebGLFramebufferId; use canvas_traits::webgl::{webgl_channel, WebGLError, WebGLResult, WebGLVersion}; use canvas_traits::webgl::{WebGLCommand, WebGLFramebufferBindingRequest}; -use canvas_traits::webgl::{WebGLFramebufferId, WebGLOpaqueFramebufferId}; use canvas_traits::webgl::{WebGLRenderbufferId, WebGLTextureId}; use dom_struct::dom_struct; use euclid::Size2D; use std::cell::Cell; -use webxr_api::SwapChainId as WebXRSwapChainId; use webxr_api::Viewport; pub enum CompleteForRendering { @@ -134,7 +133,7 @@ impl WebGLFramebuffer { let (sender, receiver) = webgl_channel().unwrap(); context.send_command(WebGLCommand::CreateFramebuffer(sender)); let id = receiver.recv().unwrap()?; - let framebuffer = WebGLFramebuffer::new(context, WebGLFramebufferId::Transparent(id)); + let framebuffer = WebGLFramebuffer::new(context, id); Some(framebuffer) } @@ -144,25 +143,16 @@ impl WebGLFramebuffer { session: &XRSession, context: &XRWebGLRenderingContext, size: Size2D<i32, Viewport>, - ) -> Option<(WebXRSwapChainId, DomRoot<Self>)> { - let (sender, receiver) = webgl_channel().unwrap(); + ) -> Option<DomRoot<Self>> { let context = match context { XRWebGLRenderingContext::WebGLRenderingContext(ref ctx) => DomRoot::from_ref(&**ctx), XRWebGLRenderingContext::WebGL2RenderingContext(ref ctx) => ctx.base_context(), }; - let _ = context.webgl_sender().send_create_webxr_swap_chain( - size.to_untyped(), - sender, - session.session_id(), - ); - let swap_chain_id = receiver.recv().unwrap()?; - let framebuffer_id = - WebGLFramebufferId::Opaque(WebGLOpaqueFramebufferId::WebXR(swap_chain_id)); - let framebuffer = WebGLFramebuffer::new(&*context, framebuffer_id); + let framebuffer = Self::maybe_new(&*context)?; framebuffer.size.set(Some((size.width, size.height))); framebuffer.status.set(constants::FRAMEBUFFER_COMPLETE); framebuffer.xr_session.set(Some(session)); - Some((swap_chain_id, framebuffer)) + Some(framebuffer) } pub fn new(context: &WebGLRenderingContext, id: WebGLFramebufferId) -> DomRoot<Self> { @@ -655,7 +645,57 @@ impl WebGLFramebuffer { // Opaque framebuffers cannot have their attachments changed // https://immersive-web.github.io/webxr/#opaque-framebuffer self.validate_transparent()?; + if let Some(texture) = texture { + // "If texture is not zero, then texture must either + // name an existing texture object with an target of + // textarget, or texture must name an existing cube + // map texture and textarget must be one of: + // TEXTURE_CUBE_MAP_POSITIVE_X, + // TEXTURE_CUBE_MAP_POSITIVE_Y, + // TEXTURE_CUBE_MAP_POSITIVE_Z, + // TEXTURE_CUBE_MAP_NEGATIVE_X, + // TEXTURE_CUBE_MAP_NEGATIVE_Y, or + // TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise, + // INVALID_OPERATION is generated." + let is_cube = match textarget { + constants::TEXTURE_2D => false, + + constants::TEXTURE_CUBE_MAP_POSITIVE_X => true, + constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true, + constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true, + constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true, + constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true, + constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true, + + _ => return Err(WebGLError::InvalidEnum), + }; + + match texture.target() { + Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}, + Some(_) if !is_cube => {}, + _ => return Err(WebGLError::InvalidOperation), + } + + let context = self.upcast::<WebGLObject>().context(); + let max_tex_size = if is_cube { + context.limits().max_cube_map_tex_size + } else { + context.limits().max_tex_size + }; + if level < 0 || level as u32 > log2(max_tex_size) { + return Err(WebGLError::InvalidValue); + } + } + self.texture2d_even_if_opaque(attachment, textarget, texture, level) + } + pub fn texture2d_even_if_opaque( + &self, + attachment: u32, + textarget: u32, + texture: Option<&WebGLTexture>, + level: i32, + ) -> WebGLResult<()> { let binding = self .attachment_binding(attachment) .ok_or(WebGLError::InvalidEnum)?; @@ -664,46 +704,6 @@ impl WebGLFramebuffer { // Note, from the GLES 2.0.25 spec, page 113: // "If texture is zero, then textarget and level are ignored." Some(texture) => { - // "If texture is not zero, then texture must either - // name an existing texture object with an target of - // textarget, or texture must name an existing cube - // map texture and textarget must be one of: - // TEXTURE_CUBE_MAP_POSITIVE_X, - // TEXTURE_CUBE_MAP_POSITIVE_Y, - // TEXTURE_CUBE_MAP_POSITIVE_Z, - // TEXTURE_CUBE_MAP_NEGATIVE_X, - // TEXTURE_CUBE_MAP_NEGATIVE_Y, or - // TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise, - // INVALID_OPERATION is generated." - let is_cube = match textarget { - constants::TEXTURE_2D => false, - - constants::TEXTURE_CUBE_MAP_POSITIVE_X => true, - constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true, - constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true, - constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true, - constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true, - constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true, - - _ => return Err(WebGLError::InvalidEnum), - }; - - match texture.target() { - Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}, - Some(_) if !is_cube => {}, - _ => return Err(WebGLError::InvalidOperation), - } - - let context = self.upcast::<WebGLObject>().context(); - let max_tex_size = if is_cube { - context.limits().max_cube_map_tex_size - } else { - context.limits().max_tex_size - }; - if level < 0 || level as u32 > log2(max_tex_size) { - return Err(WebGLError::InvalidValue); - } - *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture { texture: Dom::from_ref(texture), level: level, diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index a3f78e64c91..b4efd86033b 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -60,8 +60,8 @@ use canvas_traits::webgl::{ webgl_channel, AlphaTreatment, DOMToTextureCommand, GLContextAttributes, GLLimits, GlType, Parameter, SizedDataType, TexDataType, TexFormat, TexParameter, WebGLChan, WebGLCommand, WebGLCommandBacktrace, WebGLContextId, WebGLError, WebGLFramebufferBindingRequest, WebGLMsg, - WebGLMsgSender, WebGLOpaqueFramebufferId, WebGLProgramId, WebGLResult, WebGLSLVersion, - WebGLSendResult, WebGLSender, WebGLVersion, YAxisTreatment, + WebGLMsgSender, WebGLProgramId, WebGLResult, WebGLSLVersion, WebGLSendResult, WebGLSender, + WebGLVersion, YAxisTreatment, }; use dom_struct::dom_struct; use embedder_traits::EventLoopWaker; @@ -84,8 +84,6 @@ use std::cell::Cell; use std::cmp; use std::ptr::{self, NonNull}; use std::rc::Rc; -use webxr_api::SessionId; -use webxr_api::SwapChainId as WebXRSwapChainId; // From the GLES 2.0.25 spec, page 85: // @@ -406,10 +404,6 @@ impl WebGLRenderingContext { .send(command, capture_webgl_backtrace(self)); } - pub fn swap_buffers(&self, id: Option<WebGLOpaqueFramebufferId>) { - let _ = self.webgl_sender.send_swap_buffers(id); - } - pub fn webgl_error(&self, err: WebGLError) { // TODO(emilio): Add useful debug messages to this warn!( @@ -5003,19 +4997,6 @@ impl WebGLMessageSender { self.wake_after_send(|| self.sender.send(msg, backtrace)) } - pub fn send_swap_buffers(&self, id: Option<WebGLOpaqueFramebufferId>) -> WebGLSendResult { - self.wake_after_send(|| self.sender.send_swap_buffers(id)) - } - - pub fn send_create_webxr_swap_chain( - &self, - size: Size2D<i32>, - sender: WebGLSender<Option<WebXRSwapChainId>>, - id: SessionId, - ) -> WebGLSendResult { - self.wake_after_send(|| self.sender.send_create_webxr_swap_chain(size, sender, id)) - } - pub fn send_resize( &self, size: Size2D<u32>, diff --git a/components/script/dom/webidls/XRLayer.webidl b/components/script/dom/webidls/XRLayer.webidl index eb967abf091..d78e220b5f5 100644 --- a/components/script/dom/webidls/XRLayer.webidl +++ b/components/script/dom/webidls/XRLayer.webidl @@ -5,9 +5,6 @@ // https://immersive-web.github.io/layers/#xrlayertype [SecureContext, Exposed=Window, Pref="dom.webxr.layers.enabled"] interface XRLayer { - readonly attribute unsigned long pixelWidth; - readonly attribute unsigned long pixelHeight; - // attribute boolean blendTextureSourceAlpha; // attribute boolean chromaticAberrationCorrection; diff --git a/components/script/dom/webidls/XRWebGLLayer.webidl b/components/script/dom/webidls/XRWebGLLayer.webidl index 4d98e506765..995f6b25bf2 100644 --- a/components/script/dom/webidls/XRWebGLLayer.webidl +++ b/components/script/dom/webidls/XRWebGLLayer.webidl @@ -12,7 +12,8 @@ dictionary XRWebGLLayerInit { boolean depth = true; boolean stencil = false; boolean alpha = true; - // double framebufferScaleFactor = 1.0; + boolean ignoreDepthValues = false; + double framebufferScaleFactor = 1.0; }; [SecureContext, Exposed=Window, Pref="dom.webxr.enabled"] diff --git a/components/script/dom/webidls/XRWebGLSubImage.webidl b/components/script/dom/webidls/XRWebGLSubImage.webidl index f25552d533e..2682206cc0a 100644 --- a/components/script/dom/webidls/XRWebGLSubImage.webidl +++ b/components/script/dom/webidls/XRWebGLSubImage.webidl @@ -5,7 +5,9 @@ // https://immersive-web.github.io/layers/#xrwebglsubimagetype [SecureContext, Exposed=Window, Pref="dom.webxr.layers.enabled"] interface XRWebGLSubImage : XRSubImage { - readonly attribute WebGLTexture colorTexture; - readonly attribute WebGLTexture? depthStencilTexture; + [SameObject] readonly attribute WebGLTexture colorTexture; + [SameObject] readonly attribute WebGLTexture? depthStencilTexture; readonly attribute unsigned long? imageIndex; + readonly attribute unsigned long textureWidth; + readonly attribute unsigned long textureHeight; }; diff --git a/components/script/dom/xrframe.rs b/components/script/dom/xrframe.rs index 2381d4864d9..914d96333b5 100644 --- a/components/script/dom/xrframe.rs +++ b/components/script/dom/xrframe.rs @@ -20,6 +20,8 @@ use crate::dom::xrviewerpose::XRViewerPose; use dom_struct::dom_struct; use std::cell::Cell; use webxr_api::Frame; +use webxr_api::LayerId; +use webxr_api::SubImages; #[dom_struct] pub struct XRFrame { @@ -59,6 +61,14 @@ impl XRFrame { pub fn get_pose(&self, space: &XRSpace) -> Option<ApiPose> { space.get_pose(&self.data) } + + pub fn get_sub_images(&self, layer_id: LayerId) -> Option<&SubImages> { + self.data + .sub_images + .iter() + .filter(|sub_images| sub_images.layer_id == layer_id) + .next() + } } impl XRFrameMethods for XRFrame { diff --git a/components/script/dom/xrlayer.rs b/components/script/dom/xrlayer.rs index e70cdb5085a..b55df545e37 100644 --- a/components/script/dom/xrlayer.rs +++ b/components/script/dom/xrlayer.rs @@ -6,30 +6,22 @@ use crate::dom::bindings::codegen::Bindings::XRLayerBinding::XRLayerBinding::XRL use crate::dom::bindings::reflector::Reflector; use crate::dom::bindings::root::Dom; use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::xrframe::XRFrame; use crate::dom::xrsession::XRSession; +use canvas_traits::webgl::WebGLContextId; use dom_struct::dom_struct; -use euclid::Size2D; -use webxr_api::Viewport; +use webxr_api::LayerId; #[dom_struct] pub struct XRLayer { reflector: Reflector, session: Dom<XRSession>, context: Dom<WebGLRenderingContext>, - size: Size2D<u32, Viewport>, + #[ignore_malloc_size_of = "Layers don't heap-allocate"] + layer_id: LayerId, } impl XRLayerMethods for XRLayer { - /// https://immersive-web.github.io/layers/#dom-xrlayer-pixelwidth - fn PixelWidth(&self) -> u32 { - self.size.width - } - - /// https://immersive-web.github.io/layers/#dom-xrlayer-pixelheight - fn PixelHeight(&self) -> u32 { - self.size.height - } - /// https://immersive-web.github.io/layers/#dom-xrlayer-destroy fn Destroy(&self) { // TODO: Implement this @@ -41,13 +33,31 @@ impl XRLayer { pub fn new_inherited( session: &XRSession, context: &WebGLRenderingContext, - size: Size2D<u32, Viewport>, + layer_id: LayerId, ) -> XRLayer { XRLayer { reflector: Reflector::new(), session: Dom::from_ref(session), context: Dom::from_ref(context), - size: size, + layer_id, } } + + pub(crate) fn layer_id(&self) -> LayerId { + self.layer_id + } + + pub(crate) fn context_id(&self) -> WebGLContextId { + self.context.context_id() + } + + pub fn begin_frame(&self, _frame: &XRFrame) -> Option<()> { + // TODO: Implement this + None + } + + pub fn end_frame(&self, _frame: &XRFrame) -> Option<()> { + // TODO: Implement this + None + } } diff --git a/components/script/dom/xrrenderstate.rs b/components/script/dom/xrrenderstate.rs index d174af531d0..10ab1ba661f 100644 --- a/components/script/dom/xrrenderstate.rs +++ b/components/script/dom/xrrenderstate.rs @@ -11,9 +11,11 @@ use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::globalscope::GlobalScope; use crate::dom::xrlayer::XRLayer; use crate::dom::xrwebgllayer::XRWebGLLayer; +use canvas_traits::webgl::WebGLContextId; use dom_struct::dom_struct; use std::cell::Cell; -use webxr_api::SwapChainId; +use webxr_api::LayerId; +use webxr_api::SubImages; #[dom_struct] pub struct XRRenderState { @@ -21,7 +23,7 @@ pub struct XRRenderState { depth_near: Cell<f64>, depth_far: Cell<f64>, inline_vertical_fov: Cell<Option<f64>>, - layer: MutNullableDom<XRWebGLLayer>, + base_layer: MutNullableDom<XRWebGLLayer>, layers: DomRefCell<Vec<XRWebGLLayerOrXRLayer>>, } @@ -45,17 +47,26 @@ impl XRWebGLLayerOrXRLayer { } } - pub fn swap_chain_id(&self) -> Option<SwapChainId> { + pub(crate) fn layer_id(&self) -> Option<LayerId> { match self { - XRWebGLLayerOrXRLayer::XRWebGLLayer(layer) => Some(layer.swap_chain_id()), - XRWebGLLayerOrXRLayer::XRLayer(_) => None, + XRWebGLLayerOrXRLayer::XRWebGLLayer(ref layer) => layer.layer_id(), + XRWebGLLayerOrXRLayer::XRLayer(ref layer) => Some(layer.layer_id()), + } + } +} + +impl RootedXRWebGLLayerOrXRLayer { + pub(crate) fn layer_id(&self) -> Option<LayerId> { + match self { + RootedXRWebGLLayerOrXRLayer::XRWebGLLayer(ref layer) => layer.layer_id(), + RootedXRWebGLLayerOrXRLayer::XRLayer(ref layer) => Some(layer.layer_id()), } } - pub fn swap_buffers(&self) { + pub(crate) fn context_id(&self) -> WebGLContextId { match self { - XRWebGLLayerOrXRLayer::XRWebGLLayer(layer) => layer.swap_buffers(), - XRWebGLLayerOrXRLayer::XRLayer(_) => (), + RootedXRWebGLLayerOrXRLayer::XRWebGLLayer(ref layer) => layer.context_id(), + RootedXRWebGLLayerOrXRLayer::XRLayer(ref layer) => layer.context_id(), } } } @@ -74,7 +85,7 @@ impl XRRenderState { depth_near: Cell::new(depth_near), depth_far: Cell::new(depth_far), inline_vertical_fov: Cell::new(inline_vertical_fov), - layer: MutNullableDom::new(layer), + base_layer: MutNullableDom::new(layer), layers: DomRefCell::new(layers.iter().cloned().collect()), } } @@ -106,7 +117,7 @@ impl XRRenderState { self.depth_near.get(), self.depth_far.get(), self.inline_vertical_fov.get(), - self.layer.get().as_ref().map(|x| &**x), + self.base_layer.get().as_ref().map(|x| &**x), &layers, ) } @@ -121,8 +132,8 @@ impl XRRenderState { debug_assert!(self.inline_vertical_fov.get().is_some()); self.inline_vertical_fov.set(Some(fov)) } - pub fn set_layer(&self, layer: Option<&XRWebGLLayer>) { - self.layer.set(layer) + pub fn set_base_layer(&self, layer: Option<&XRWebGLLayer>) { + self.base_layer.set(layer) } pub fn set_layers(&self, layers: &[RootedXRWebGLLayerOrXRLayer]) { *self.layers.borrow_mut() = layers.iter().map(XRWebGLLayerOrXRLayer::from_ref).collect(); @@ -134,8 +145,25 @@ impl XRRenderState { let layers = self.layers.borrow(); f(&*layers) } - pub fn has_layer(&self) -> bool { - self.layer.get().is_some() || !self.layers.borrow().is_empty() + pub fn has_sub_images(&self, sub_images: &[SubImages]) -> bool { + if let Some(base_layer) = self.base_layer.get() { + match sub_images.len() { + // For inline sessions, there may be a base layer, but it won't have a framebuffer + 0 => base_layer.layer_id() == None, + // For immersive sessions, the base layer will have a framebuffer, + // so we make sure the layer id's match up + 1 => base_layer.layer_id() == Some(sub_images[0].layer_id), + _ => false, + } + } else { + // The layers API is only for immersive sessions + let layers = self.layers.borrow(); + sub_images.len() == layers.len() && + sub_images + .iter() + .zip(layers.iter()) + .all(|(sub_image, layer)| Some(sub_image.layer_id) == layer.layer_id()) + } } } @@ -157,6 +185,6 @@ impl XRRenderStateMethods for XRRenderState { /// https://immersive-web.github.io/webxr/#dom-xrrenderstate-baselayer fn GetBaseLayer(&self) -> Option<DomRoot<XRWebGLLayer>> { - self.layer.get() + self.base_layer.get() } } diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index ee7a865d280..c9f7b108a94 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -35,6 +35,7 @@ use crate::dom::xrinputsourcearray::XRInputSourceArray; use crate::dom::xrinputsourceevent::XRInputSourceEvent; use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrrenderstate::XRRenderState; +use crate::dom::xrrenderstate::XRWebGLLayerOrXRLayer; use crate::dom::xrsessionevent::XRSessionEvent; use crate::dom::xrspace::XRSpace; use crate::realms::InRealm; @@ -50,6 +51,7 @@ use std::collections::HashMap; use std::f64::consts::{FRAC_PI_2, PI}; use std::mem; use std::rc::Rc; +use webxr_api::ContextId as WebXRContextId; use webxr_api::{ self, util, ApiSpace, Display, EntityTypes, EnvironmentBlendMode, Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, Ray, SelectEvent, SelectKind, Session, SessionId, @@ -354,7 +356,7 @@ impl XRSession { /// https://immersive-web.github.io/webxr/#xr-animation-frame fn raf_callback(&self, mut frame: Frame) { - debug!("WebXR RAF callback"); + debug!("WebXR RAF callback {:?}", frame); #[cfg(feature = "xr-profile")] let raf_start = time::precise_time_ns(); #[cfg(feature = "xr-profile")] @@ -363,7 +365,9 @@ impl XRSession { (raf_start - frame.sent_time) as f64 / 1_000_000. ); - // Step 1 + // Step 1-2 happen in the xebxr device thread + + // Step 3 if let Some(pending) = self.pending_render_state.take() { // https://immersive-web.github.io/webxr/#apply-the-pending-render-state // (Steps 1-4 are implicit) @@ -371,33 +375,38 @@ impl XRSession { self.active_render_state.set(&pending); // Step 6-7: XXXManishearth handle inlineVerticalFieldOfView - if self.is_immersive() { - let swap_chain_id = pending - .GetBaseLayer() - .map(|layer| layer.swap_chain_id()) - .or_else(|| { - self.active_render_state.get().with_layers(|layers| { - layers.get(0).and_then(|layer| layer.swap_chain_id()) - }) - }); - self.session.borrow_mut().set_swap_chain(swap_chain_id); - } else { + if !self.is_immersive() { self.update_inline_projection_matrix() } } + // TODO: how does this fit the webxr spec? for event in frame.events.drain(..) { - self.handle_frame_update(event); + self.handle_frame_event(event); } - // Step 2 - if !self.active_render_state.get().has_layer() { + // Step 4 + // TODO: what should this check be? + // This is checking that the new render state has the same + // layers as the frame. + // Related to https://github.com/immersive-web/webxr/issues/1051 + if !self + .active_render_state + .get() + .has_sub_images(&frame.sub_images[..]) + { + // If the frame has different layers than the render state, + // we just return early, drawing a blank frame. + // This can result in flickering when the render state is changed. + // TODO: it would be better to not render anything until the next frame. + warn!("Rendering blank XR frame"); + self.session.borrow_mut().render_animation_frame(); return; } - // Step 3: XXXManishearth handle inline session + // Step 5: XXXManishearth handle inline session - // Step 4-5 + // Step 6-7 { let mut current = self.current_raf_callback_list.borrow_mut(); assert!(current.is_empty()); @@ -407,11 +416,17 @@ impl XRSession { let time = reduce_timing_resolution((frame.time_ns - start).to_ms()); let frame = XRFrame::new(&self.global(), self, frame); - // Step 6,7 + // Step 8-9 frame.set_active(true); frame.set_animation_frame(true); - // Step 8 + // Step 10 + self.apply_frame_updates(&*frame); + + // TODO: how does this fit with the webxr and xr layers specs? + self.layers_begin_frame(&*frame); + + // Step 11-12 self.outside_raf.set(false); let len = self.current_raf_callback_list.borrow().len(); for i in 0..len { @@ -426,21 +441,14 @@ impl XRSession { self.outside_raf.set(true); *self.current_raf_callback_list.borrow_mut() = vec![]; + // TODO: how does this fit with the webxr and xr layers specs? + self.layers_end_frame(&*frame); + + // Step 13 frame.set_active(false); - if self.is_immersive() { - if let Some(base_layer) = self.active_render_state.get().GetBaseLayer() { - base_layer.swap_buffers(); - } else { - self.active_render_state.get().with_layers(|layers| { - for layer in layers { - layer.swap_buffers(); - } - }); - } - self.session.borrow_mut().render_animation_frame(); - } else { - self.session.borrow_mut().start_render_loop(); - } + + // TODO: how does this fit the webxr spec? + self.session.borrow_mut().render_animation_frame(); #[cfg(feature = "xr-profile")] println!( @@ -498,7 +506,50 @@ impl XRSession { } } - fn handle_frame_update(&self, event: FrameUpdateEvent) { + // TODO: how does this align with the layers spec? + fn layers_begin_frame(&self, frame: &XRFrame) { + if let Some(layer) = self.active_render_state.get().GetBaseLayer() { + layer.begin_frame(frame); + } + self.active_render_state.get().with_layers(|layers| { + for layer in layers { + match layer { + XRWebGLLayerOrXRLayer::XRWebGLLayer(layer) => { + layer.begin_frame(frame); + }, + XRWebGLLayerOrXRLayer::XRLayer(layer) => { + layer.begin_frame(frame); + }, + } + } + }); + } + + // TODO: how does this align with the layers spec? + fn layers_end_frame(&self, frame: &XRFrame) { + if let Some(layer) = self.active_render_state.get().GetBaseLayer() { + layer.end_frame(frame); + } + self.active_render_state.get().with_layers(|layers| { + for layer in layers { + match layer { + XRWebGLLayerOrXRLayer::XRWebGLLayer(layer) => { + layer.end_frame(frame); + }, + XRWebGLLayerOrXRLayer::XRLayer(layer) => { + layer.end_frame(frame); + }, + } + } + }); + } + + /// https://immersive-web.github.io/webxr/#xrframe-apply-frame-updates + fn apply_frame_updates(&self, _frame: &XRFrame) { + // TODO: add a comment about why this is empty right now! + } + + fn handle_frame_event(&self, event: FrameUpdateEvent) { match event { FrameUpdateEvent::HitTestSourceAdded(id) => { if let Some(promise) = self.pending_hit_test_promises.borrow_mut().remove(&id) { @@ -624,8 +675,16 @@ impl XRSessionMethods for XRSession { pending.set_inline_vertical_fov(fov); } if let Some(ref layer) = init.baseLayer { - pending.set_layer(Some(&layer)); + pending.set_base_layer(Some(&layer)); pending.set_layers(&[]); + let layers = std::iter::once(layer) + .filter_map(|layer| { + let context_id = WebXRContextId::from(layer.context_id()); + let layer_id = layer.layer_id()?; + Some((context_id, layer_id)) + }) + .collect(); + self.session.borrow_mut().set_layers(layers); } if init.depthFar.is_some() || init.depthNear.is_some() { @@ -637,8 +696,17 @@ impl XRSessionMethods for XRSession { // TODO: add spec link for this step once XR layers has settled down // https://immersive-web.github.io/layers/ if let Some(ref layers) = init.layers { - pending.set_layer(None); + pending.set_base_layer(None); pending.set_layers(layers); + let layers = layers + .iter() + .filter_map(|layer| { + let context_id = WebXRContextId::from(layer.context_id()); + let layer_id = layer.layer_id()?; + Some((context_id, layer_id)) + }) + .collect(); + self.session.borrow_mut().set_layers(layers); } Ok(()) diff --git a/components/script/dom/xrwebglbinding.rs b/components/script/dom/xrwebglbinding.rs index 27435aa3ddc..4f5efaa3cf7 100644 --- a/components/script/dom/xrwebglbinding.rs +++ b/components/script/dom/xrwebglbinding.rs @@ -16,6 +16,7 @@ use crate::dom::xrlayer::XRLayer; use crate::dom::xrsession::XRSession; use crate::dom::xrview::XRView; use crate::dom::xrwebglsubimage::XRWebGLSubImage; +use canvas_traits::webgl::WebGLContextId; use dom_struct::dom_struct; #[dom_struct] @@ -53,6 +54,19 @@ impl WebGLRenderingContextOrWebGL2RenderingContext { } } +impl RootedWebGLRenderingContextOrWebGL2RenderingContext { + pub(crate) fn context_id(&self) -> WebGLContextId { + match self { + RootedWebGLRenderingContextOrWebGL2RenderingContext::WebGLRenderingContext( + ref context, + ) => context.context_id(), + RootedWebGLRenderingContextOrWebGL2RenderingContext::WebGL2RenderingContext( + ref context, + ) => context.base_context().context_id(), + } + } +} + impl XRWebGLBindingMethods for XRWebGLBinding { /// https://immersive-web.github.io/layers/#dom-xrwebglbinding-getsubimage fn GetSubImage(&self, _layer: &XRLayer, _frame: &XRFrame) -> Option<DomRoot<XRWebGLSubImage>> { diff --git a/components/script/dom/xrwebgllayer.rs b/components/script/dom/xrwebgllayer.rs index f93c4c22bc2..c4dd5999636 100644 --- a/components/script/dom/xrwebgllayer.rs +++ b/components/script/dom/xrwebgllayer.rs @@ -2,6 +2,8 @@ * 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 crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants; use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods; use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextBinding::WebGL2RenderingContextMethods; use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerInit; @@ -13,17 +15,24 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::globalscope::GlobalScope; use crate::dom::webglframebuffer::WebGLFramebuffer; +use crate::dom::webglobject::WebGLObject; +use crate::dom::webgltexture::WebGLTexture; use crate::dom::webglrenderingcontext::WebGLRenderingContext; use crate::dom::webgl2renderingcontext::WebGL2RenderingContext; use crate::dom::window::Window; +use crate::dom::xrframe::XRFrame; use crate::dom::xrsession::XRSession; use crate::dom::xrview::XRView; use crate::dom::xrviewport::XRViewport; -use canvas_traits::webgl::WebGLFramebufferId; +use canvas_traits::webgl::WebGLContextId; +use canvas_traits::webgl::WebGLCommand; +use canvas_traits::webgl::WebGLTextureId; use dom_struct::dom_struct; use euclid::{Rect, Size2D}; use std::convert::TryInto; -use webxr_api::SwapChainId as WebXRSwapChainId; +use webxr_api::ContextId as WebXRContextId; +use webxr_api::LayerId; +use webxr_api::LayerInit; use webxr_api::Viewport; #[derive(JSTraceable, MallocSizeOf)] @@ -33,6 +42,28 @@ pub enum RenderingContext { WebGL2(Dom<WebGL2RenderingContext>), } +impl RenderingContext { + fn context_id(&self) -> WebGLContextId { + match self { + RenderingContext::WebGL1(ref ctx) => ctx.context_id(), + RenderingContext::WebGL2(ref ctx) => ctx.base_context().context_id(), + } + } +} + +impl<'a> From<&'a XRWebGLLayerInit> for LayerInit { + fn from(init: &'a XRWebGLLayerInit) -> LayerInit { + LayerInit::WebGLLayer { + alpha: init.alpha, + antialias: init.antialias, + depth: init.depth, + stencil: init.stencil, + framebuffer_scale_factor: *init.framebufferScaleFactor as f32, + ignore_depth_values: init.ignoreDepthValues, + } + } +} + #[dom_struct] pub struct XRWebGLLayer { reflector_: Reflector, @@ -40,21 +71,22 @@ pub struct XRWebGLLayer { depth: bool, stencil: bool, alpha: bool, - #[ignore_malloc_size_of = "ids don't malloc"] - swap_chain_id: Option<WebXRSwapChainId>, context: RenderingContext, session: Dom<XRSession>, /// If none, this is an inline session (the composition disabled flag is true) framebuffer: Option<Dom<WebGLFramebuffer>>, + /// If none, this is an inline session (the composition disabled flag is true) + #[ignore_malloc_size_of = "Layer ids don't heap-allocate"] + layer_id: Option<LayerId>, } impl XRWebGLLayer { pub fn new_inherited( - swap_chain_id: Option<WebXRSwapChainId>, session: &XRSession, context: XRWebGLRenderingContext, init: &XRWebGLLayerInit, framebuffer: Option<&WebGLFramebuffer>, + layer_id: Option<LayerId>, ) -> XRWebGLLayer { XRWebGLLayer { reflector_: Reflector::new(), @@ -62,7 +94,7 @@ impl XRWebGLLayer { depth: init.depth, stencil: init.stencil, alpha: init.alpha, - swap_chain_id, + layer_id, context: match context { XRWebGLRenderingContext::WebGLRenderingContext(ctx) => { RenderingContext::WebGL1(Dom::from_ref(&*ctx)) @@ -78,19 +110,19 @@ impl XRWebGLLayer { pub fn new( global: &GlobalScope, - swap_chain_id: Option<WebXRSwapChainId>, session: &XRSession, context: XRWebGLRenderingContext, init: &XRWebGLLayerInit, framebuffer: Option<&WebGLFramebuffer>, + layer_id: Option<LayerId>, ) -> DomRoot<XRWebGLLayer> { reflect_dom_object( Box::new(XRWebGLLayer::new_inherited( - swap_chain_id, session, context, init, framebuffer, + layer_id, )), global, ) @@ -104,7 +136,6 @@ impl XRWebGLLayer { context: XRWebGLRenderingContext, init: &XRWebGLLayerInit, ) -> Fallible<DomRoot<Self>> { - let framebuffer; // Step 2 if session.is_ended() { return Err(Error::InvalidState); @@ -112,27 +143,29 @@ impl XRWebGLLayer { // XXXManishearth step 3: throw error if context is lost // XXXManishearth step 4: check XR compat flag for immersive sessions - // Step 9.2. "Initialize layer’s framebuffer to a new opaque framebuffer created with context." - let (swap_chain_id, framebuffer) = if session.is_immersive() { - let size = session.with_session(|session| { - session - .recommended_framebuffer_resolution() - .expect("immersive session must have viewports") - }); - let (swap_chain_id, fb) = WebGLFramebuffer::maybe_new_webxr(session, &context, size) + let (framebuffer, layer_id) = if session.is_immersive() { + // Step 9.2. "Initialize layer’s framebuffer to a new opaque framebuffer created with context." + let size = session + .with_session(|session| session.recommended_framebuffer_resolution()) .ok_or(Error::Operation)?; - framebuffer = fb; - (Some(swap_chain_id), Some(&*framebuffer)) + let framebuffer = WebGLFramebuffer::maybe_new_webxr(session, &context, size) + .ok_or(Error::Operation)?; + + // Step 9.3. "Allocate and initialize resources compatible with session’s XR device, + // including GPU accessible memory buffers, as required to support the compositing of layer." + let context_id = WebXRContextId::from(context.context_id()); + let layer_init = LayerInit::from(init); + let layer_id = session + .with_session(|session| session.create_layer(context_id, layer_init)) + .map_err(|_| Error::Operation)?; + + // Step 9.4: "If layer’s resources were unable to be created for any reason, + // throw an OperationError and abort these steps." + (Some(framebuffer), Some(layer_id)) } else { (None, None) }; - // Step 9.3. "Allocate and initialize resources compatible with session’s XR device, - // including GPU accessible memory buffers, as required to support the compositing of layer." - - // Step 9.4: "If layer’s resources were unable to be created for any reason, - // throw an OperationError and abort these steps." - // Ensure that we finish setting up this layer before continuing. match context { XRWebGLRenderingContext::WebGLRenderingContext(ref ctx) => ctx.Finish(), @@ -142,35 +175,24 @@ impl XRWebGLLayer { // Step 10. "Return layer." Ok(XRWebGLLayer::new( &global.global(), - swap_chain_id, session, context, init, - framebuffer, + framebuffer.as_deref(), + layer_id, )) } - pub fn swap_chain_id(&self) -> WebXRSwapChainId { - self.swap_chain_id - .expect("swap_chain_id must not be called for inline sessions") + pub fn layer_id(&self) -> Option<LayerId> { + self.layer_id } - pub fn session(&self) -> &XRSession { - &self.session + pub fn context_id(&self) -> WebGLContextId { + self.context.context_id() } - pub fn swap_buffers(&self) { - if let WebGLFramebufferId::Opaque(id) = self - .framebuffer - .as_ref() - .expect("swap_buffers must not be called for inline sessions") - .id() - { - match self.context { - RenderingContext::WebGL1(ref ctx) => ctx.swap_buffers(Some(id)), - RenderingContext::WebGL2(ref ctx) => ctx.base_context().swap_buffers(Some(id)), - } - } + pub fn session(&self) -> &XRSession { + &self.session } pub fn size(&self) -> Size2D<u32, Viewport> { @@ -188,6 +210,52 @@ impl XRWebGLLayer { Size2D::from_untyped(size) } } + + fn texture_target(&self) -> u32 { + if cfg!(target_os = "macos") { + sparkle::gl::TEXTURE_RECTANGLE + } else { + sparkle::gl::TEXTURE_2D + } + } + + pub fn begin_frame(&self, frame: &XRFrame) -> Option<()> { + debug!("XRWebGLLayer begin frame"); + let framebuffer = self.framebuffer.as_ref()?; + let context = framebuffer.upcast::<WebGLObject>().context(); + let sub_images = frame.get_sub_images(self.layer_id?)?; + // TODO: Cache this texture + let color_texture_id = + WebGLTextureId::maybe_new(sub_images.sub_image.as_ref()?.color_texture)?; + let color_texture = WebGLTexture::new(context, color_texture_id); + let target = self.texture_target(); + // TODO: rebind the current bindings + context.send_command(WebGLCommand::BindTexture(target, Some(color_texture_id))); + framebuffer.bind(constants::FRAMEBUFFER); + framebuffer + .texture2d_even_if_opaque( + constants::COLOR_ATTACHMENT0, + self.texture_target(), + Some(&color_texture), + 0, + ) + .ok()?; + // TODO: depth/stencil + Some(()) + } + + pub fn end_frame(&self, _frame: &XRFrame) -> Option<()> { + debug!("XRWebGLLayer end frame"); + // TODO: invalidate the old texture + let framebuffer = self.framebuffer.as_ref()?; + // TODO: rebind the current bindings + framebuffer.bind(constants::FRAMEBUFFER); + framebuffer + .texture2d_even_if_opaque(constants::COLOR_ATTACHMENT0, self.texture_target(), None, 0) + .ok()?; + framebuffer.upcast::<WebGLObject>().context().Flush(); + Some(()) + } } impl XRWebGLLayerMethods for XRWebGLLayer { diff --git a/components/script/dom/xrwebglsubimage.rs b/components/script/dom/xrwebglsubimage.rs index 0331d36c4c2..0813bd85d9e 100644 --- a/components/script/dom/xrwebglsubimage.rs +++ b/components/script/dom/xrwebglsubimage.rs @@ -8,6 +8,8 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::webgltexture::WebGLTexture; use crate::dom::xrsubimage::XRSubImage; use dom_struct::dom_struct; +use euclid::Size2D; +use webxr_api::Viewport; #[dom_struct] pub struct XRWebGLSubImage { @@ -15,6 +17,7 @@ pub struct XRWebGLSubImage { color_texture: Dom<WebGLTexture>, depth_stencil_texture: Option<Dom<WebGLTexture>>, image_index: Option<u32>, + size: Size2D<u32, Viewport>, } impl XRWebGLSubImageMethods for XRWebGLSubImage { @@ -32,4 +35,14 @@ impl XRWebGLSubImageMethods for XRWebGLSubImage { fn GetImageIndex(&self) -> Option<u32> { self.image_index } + + /// https://immersive-web.github.io/layers/#dom-xrwebglsubimage-texturewidth + fn TextureWidth(&self) -> u32 { + self.size.width + } + + /// https://immersive-web.github.io/layers/#dom-xrwebglsubimage-textureheight + fn TextureHeight(&self) -> u32 { + self.size.height + } } |