Support for webxr layer management

This commit is contained in:
Alan Jeffrey 2020-04-15 18:04:32 -05:00
parent af110ac21f
commit 349619ed2d
34 changed files with 949 additions and 642 deletions

View file

@ -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,

View file

@ -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() {

View file

@ -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,

View file

@ -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>,

View file

@ -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;

View file

@ -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"]

View file

@ -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;
};

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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()
}
}

View file

@ -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(())

View file

@ -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>> {

View file

@ -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 layers 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 layers 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 sessions 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 layers 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 sessions XR device,
// including GPU accessible memory buffers, as required to support the compositing of layer."
// Step 9.4: "If layers 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,37 +175,26 @@ 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 context_id(&self) -> WebGLContextId {
self.context.context_id()
}
pub fn session(&self) -> &XRSession {
&self.session
}
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 size(&self) -> Size2D<u32, Viewport> {
if let Some(framebuffer) = self.framebuffer.as_ref() {
let size = framebuffer.size().unwrap_or((0, 0));
@ -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 {

View file

@ -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
}
}