Skip to content

Commit

Permalink
VR multiview Framebuffers
Browse files Browse the repository at this point in the history
  • Loading branch information
MortimerGoro committed Nov 8, 2017
1 parent af1cd38 commit 9e2f5f1
Show file tree
Hide file tree
Showing 21 changed files with 518 additions and 61 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions components/canvas_traits/Cargo.toml
Expand Up @@ -21,3 +21,4 @@ offscreen_gl_context = { version = "0.12", features = ["serde"] }
serde = "1.0"
servo_config = {path = "../config"}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
webvr_traits = {path = "../webvr_traits"}
1 change: 1 addition & 0 deletions components/canvas_traits/lib.rs
Expand Up @@ -18,6 +18,7 @@ extern crate offscreen_gl_context;
#[macro_use] extern crate serde;
extern crate servo_config;
extern crate webrender_api;
extern crate webvr_traits;

pub mod canvas;
pub mod webgl;
Expand Down
13 changes: 12 additions & 1 deletion components/canvas_traits/webgl.rs
Expand Up @@ -7,6 +7,7 @@ use nonzero::NonZero;
use offscreen_gl_context::{GLContextAttributes, GLLimits};
use std::fmt;
use webrender_api::{DocumentId, ImageKey, PipelineId};
use webvr_traits::{WebVRFramebuffer, WebVRFramebufferAttributes};

/// Sender type used in WebGLCommands.
pub use ::webgl_channel::WebGLSender;
Expand Down Expand Up @@ -111,6 +112,12 @@ impl WebGLMsgSender {
self.sender.send(WebGLMsg::WebGLCommand(self.ctx_id, command))
}

/// Send a generic WebGLMsg command
#[inline]
pub fn send_msg(&self, msg: WebGLMsg) -> WebGLSendResult {
self.sender.send(msg)
}

/// Send a WebVRCommand message
#[inline]
pub fn send_vr(&self, command: WebVRCommand) -> WebGLSendResult {
Expand Down Expand Up @@ -384,9 +391,13 @@ pub type WebVRDeviceId = u32;
#[derive(Clone, Deserialize, Serialize)]
pub enum WebVRCommand {
/// Start presenting to a VR device.
Create(WebVRDeviceId),
Create(WebVRDeviceId, WebVRFramebufferAttributes, WebGLSender<Vec<WebVRFramebuffer>>),
/// Synchronize the pose information to be used in the frame.
SyncPoses(WebVRDeviceId, f64, f64, WebGLSender<Result<Vec<u8>, ()>>),
/// Binds a framebuffer exposed by the VR device.
BindFramebuffer(WebVRDeviceId, u32),
/// Unbinds a framebuffer exposed by the VR device.
UnbindFramebuffer(WebVRDeviceId, u32),
/// Submit the frame to a VR device using the specified texture coordinates.
SubmitFrame(WebVRDeviceId, [f32; 4], [f32; 4]),
/// Stop presenting to a VR device
Expand Down
7 changes: 5 additions & 2 deletions components/script/dom/bindings/trace.rs
Expand Up @@ -33,7 +33,7 @@ use app_units::Au;
use canvas_traits::canvas::{CanvasGradientStop, LinearGradientStyle, RadialGradientStyle};
use canvas_traits::canvas::{CompositionOrBlending, LineCapStyle, LineJoinStyle, RepetitionStyle};
use canvas_traits::webgl::{WebGLBufferId, WebGLFramebufferId, WebGLProgramId, WebGLRenderbufferId};
use canvas_traits::webgl::{WebGLChan, WebGLContextShareMode, WebGLError, WebGLPipeline, WebGLMsgSender, WebGLVersion};
use canvas_traits::webgl::{WebGLChan, WebGLContextShareMode, WebGLError, WebGLPipeline, WebGLMsg, WebGLMsgSender, WebGLVersion};
use canvas_traits::webgl::{WebGLReceiver, WebGLSender, WebGLShaderId, WebGLTextureId, WebGLVertexArrayId};
use cssparser::RGBA;
use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
Expand Down Expand Up @@ -112,7 +112,7 @@ use style::values::specified::Length;
use time::Duration;
use uuid::Uuid;
use webrender_api::{DocumentId, ImageKey};
use webvr_traits::WebVRGamepadHand;
use webvr_traits::{WebVRFramebuffer, WebVRFramebufferAttributes, WebVRGamepadHand};

/// A trait to allow tracing (only) DOM objects.
pub unsafe trait JSTraceable {
Expand Down Expand Up @@ -404,6 +404,7 @@ unsafe_no_jsmanaged_fields!(WebGLBufferId);
unsafe_no_jsmanaged_fields!(WebGLChan);
unsafe_no_jsmanaged_fields!(WebGLContextShareMode);
unsafe_no_jsmanaged_fields!(WebGLFramebufferId);
unsafe_no_jsmanaged_fields!(WebGLMsg);
unsafe_no_jsmanaged_fields!(WebGLMsgSender);
unsafe_no_jsmanaged_fields!(WebGLPipeline);
unsafe_no_jsmanaged_fields!(WebGLProgramId);
Expand All @@ -413,6 +414,8 @@ unsafe_no_jsmanaged_fields!(WebGLTextureId);
unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
unsafe_no_jsmanaged_fields!(WebGLVersion);
unsafe_no_jsmanaged_fields!(MediaList);
unsafe_no_jsmanaged_fields!(WebVRFramebuffer);
unsafe_no_jsmanaged_fields!(WebVRFramebufferAttributes);
unsafe_no_jsmanaged_fields!(WebVRGamepadHand);
unsafe_no_jsmanaged_fields!(ScriptToConstellationChan);
unsafe_no_jsmanaged_fields!(InteractiveMetrics);
Expand Down
2 changes: 2 additions & 0 deletions components/script/dom/mod.rs
Expand Up @@ -464,6 +464,8 @@ pub mod vreyeparameters;
pub mod vrfieldofview;
pub mod vrframedata;
pub mod vrpose;
pub mod vrview;
pub mod vrviewlist;
pub mod vrstageparameters;
pub mod webgl_extensions;
pub use self::webgl_extensions::ext::*;
Expand Down
56 changes: 48 additions & 8 deletions components/script/dom/vrdisplay.rs
Expand Up @@ -10,14 +10,15 @@ use dom::bindings::codegen::Bindings::VRDisplayBinding;
use dom::bindings::codegen::Bindings::VRDisplayBinding::VRDisplayMethods;
use dom::bindings::codegen::Bindings::VRDisplayBinding::VREye;
use dom::bindings::codegen::Bindings::VRLayerBinding::VRLayer;
use dom::bindings::codegen::Bindings::VRViewBinding::VRAttributes;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use dom::bindings::codegen::Bindings::WindowBinding::FrameRequestCallback;
use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::num::Finite;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::root::{DomRoot, MutDom, MutNullableDom};
use dom::bindings::root::{DomRoot, MutDom, MutNullableDom, RootedReference};
use dom::bindings::str::DOMString;
use dom::event::Event;
use dom::eventtarget::EventTarget;
Expand All @@ -29,6 +30,8 @@ use dom::vreyeparameters::VREyeParameters;
use dom::vrframedata::VRFrameData;
use dom::vrpose::VRPose;
use dom::vrstageparameters::VRStageParameters;
use dom::vrview::VRView;
use dom::vrviewlist::VRViewList;
use dom::webglrenderingcontext::WebGLRenderingContext;
use dom_struct::dom_struct;
use ipc_channel::ipc::{self, IpcSender};
Expand All @@ -40,7 +43,8 @@ use std::ops::Deref;
use std::rc::Rc;
use std::sync::mpsc;
use std::thread;
use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData, WebVRLayer, WebVRMsg};
use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData};
use webvr_traits::{WebVRFramebufferAttributes, WebVRLayer, WebVRMsg};

#[dom_struct]
pub struct VRDisplay {
Expand Down Expand Up @@ -71,6 +75,9 @@ pub struct VRDisplay {
running_display_raf: Cell<bool>,
paused: Cell<bool>,
stopped_on_pause: Cell<bool>,
#[ignore_malloc_size_of = "Defined in rust-webvr"]
attributes: Cell<WebVRFramebufferAttributes>,
vr_views: MutNullableDom<VRViewList>,
}

unsafe_no_jsmanaged_fields!(WebVRDisplayData);
Expand Down Expand Up @@ -116,7 +123,9 @@ impl VRDisplay {
paused: Cell::new(false),
// This flag is set when the Display was presenting when it received a VR Pause event.
// When the VR Resume event is received and the flag is set, VR presentation automatically restarts.
stopped_on_pause: Cell::new(false)
stopped_on_pause: Cell::new(false),
attributes: Cell::new(Default::default()),
vr_views: MutNullableDom::default(),
}
}

Expand Down Expand Up @@ -304,9 +313,10 @@ impl VRDisplayMethods for VRDisplay {
let layer_ctx;

match layer {
Ok((bounds, ctx)) => {
Ok((bounds, ctx, attributes)) => {
layer_bounds = bounds;
layer_ctx = ctx;
self.attributes.set(attributes);
},
Err(msg) => {
let msg = msg.to_string();
Expand Down Expand Up @@ -396,13 +406,28 @@ impl VRDisplayMethods for VRDisplay {
}

let layer = self.layer.borrow();
let attributes = self.attributes.get();

vec![VRLayer {
leftBounds: Some(bounds_to_vec(&layer.left_bounds)),
rightBounds: Some(bounds_to_vec(&layer.right_bounds)),
source: self.layer_ctx.get().map(|ctx| ctx.Canvas()),
attributes: VRAttributes {
depth: attributes.depth,
multiview: attributes.multiview,
antialias: attributes.multisampling,
},
}]
}

// https://w3c.github.io/webvr/spec/latest/#vrview-interface
fn GetViews(&self) -> Option<DomRoot<VRViewList>> {
if !self.presenting.get() {
return None;
}

self.vr_views.get()
}
}

impl VRDisplay {
Expand Down Expand Up @@ -495,6 +520,18 @@ impl VRDisplay {
let far_init = self.depth_far.get();
let pipeline_id = self.global().pipeline_id();

// Initialize compositor
let (fbos_sender, fbos_receiver) = webgl_channel().unwrap();
api_sender.send_vr(WebVRCommand::Create(display_id, self.attributes.get(), fbos_sender)).unwrap();
let mut fbos = fbos_receiver.recv().unwrap();
rooted_vec!(let list <- fbos.drain(0..)
.map(|fbo| VRView::new(&self.global(),
api_sender.clone(),
display_id,
fbo)));
let viewlist = VRViewList::new(self.global().as_window(), list.r());
self.vr_views.set(Some(&viewlist));

// The render loop at native headset frame rate is implemented using a dedicated thread.
// Every loop iteration syncs pose data with the HMD, submits the pixels to the display and waits for Vsync.
// Both the requestAnimationFrame call of a VRDisplay in the JavaScript thread and the VRSyncPoses call
Expand All @@ -506,8 +543,6 @@ impl VRDisplay {
let mut near = near_init;
let mut far = far_init;

// Initialize compositor
api_sender.send_vr(WebVRCommand::Create(display_id)).unwrap();
loop {
// Run RAF callbacks on JavaScript thread
let this = address.clone();
Expand Down Expand Up @@ -628,13 +663,18 @@ fn parse_bounds(src: &Option<Vec<Finite<f32>>>, dst: &mut [f32; 4]) -> Result<()
}
}

fn validate_layer(layer: &VRLayer) -> Result<(WebVRLayer, DomRoot<WebGLRenderingContext>), &'static str> {
fn validate_layer(layer: &VRLayer) -> Result<(WebVRLayer, DomRoot<WebGLRenderingContext>, WebVRFramebufferAttributes), &'static str> {
let ctx = layer.source.as_ref().map(|ref s| s.get_base_webgl_context()).unwrap_or(None);
if let Some(ctx) = ctx {
let mut data = WebVRLayer::default();
parse_bounds(&layer.leftBounds, &mut data.left_bounds)?;
parse_bounds(&layer.rightBounds, &mut data.right_bounds)?;
Ok((data, ctx))
let attributes = WebVRFramebufferAttributes {
depth: layer.attributes.depth,
multiview: layer.attributes.multiview,
multisampling: layer.attributes.antialias,
};
Ok((data, ctx, attributes))
} else {
Err("VRLayer source must be a WebGL Context")
}
Expand Down
90 changes: 90 additions & 0 deletions components/script/dom/vrview.rs
@@ -0,0 +1,90 @@
/* 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 http://mozilla.org/MPL/2.0/. */

use canvas_traits::webgl::{WebGLFramebufferId, WebGLMsg, WebGLMsgSender, WebVRCommand, WebVRDeviceId};
use dom::bindings::codegen::Bindings::VRViewBinding;
use dom::bindings::codegen::Bindings::VRViewBinding::{VRAttributes, VRViewMethods, VRViewport};
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::root::{MutNullableDom, DomRoot};
use dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
use dom::webglframebuffer::{OpaqueFBOMessages, WebGLFramebuffer};
use webvr_traits::WebVRFramebuffer;

#[dom_struct]
pub struct VRView {
reflector_: Reflector,
renderer: WebGLMsgSender,
device_id: WebVRDeviceId,
#[ignore_malloc_size_of = "Defined in rust-webvr"]
fbo: WebVRFramebuffer,
webgl_fbo: MutNullableDom<WebGLFramebuffer>,
}

impl VRView {
fn new_inherited(renderer: WebGLMsgSender,
device_id: WebVRDeviceId,
fbo: WebVRFramebuffer) -> VRView {
VRView {
reflector_: Reflector::new(),
renderer,
device_id,
fbo,
webgl_fbo: Default::default(),
}
}

pub fn new(global: &GlobalScope,
renderer: WebGLMsgSender,
device_id: WebVRDeviceId,
fbo: WebVRFramebuffer) -> DomRoot<VRView> {
reflect_dom_object(Box::new(VRView::new_inherited(renderer, device_id, fbo)),
global,
VRViewBinding::Wrap)
}
}

impl VRViewMethods for VRView {
// https://w3c.github.io/webvr/#interface-interface-vrfieldofview
#[allow(unsafe_code)]
fn Framebuffer(&self) -> DomRoot<WebGLFramebuffer> {
self.webgl_fbo.or_init(|| {
let fbo_id = unsafe {
// Generate a dummy FBO id that avoids collisions with the real WebGL FBOs.
WebGLFramebufferId::new(self.device_id * 1000 + self.fbo.eye_index)
};
let bind_msg = WebGLMsg::WebVRCommand(self.renderer.context_id(),
WebVRCommand::BindFramebuffer(self.device_id,
self.fbo.eye_index));
let unbind_msg = Some(WebGLMsg::WebVRCommand(self.renderer.context_id(),
WebVRCommand::UnbindFramebuffer(self.device_id,
self.fbo.eye_index)));
let opaque_messages = OpaqueFBOMessages {
bind_msg, unbind_msg
};

WebGLFramebuffer::new_opaque(self.global().as_window(),
self.renderer.clone(),
fbo_id,
opaque_messages)
})
}

fn GetViewport(&self) -> VRViewport {
VRViewport {
x: Some(self.fbo.viewport.x),
y: Some(self.fbo.viewport.y),
width: Some(self.fbo.viewport.width),
height: Some(self.fbo.viewport.height),
}
}

fn GetAttributes(&self) -> VRAttributes {
VRAttributes {
depth: self.fbo.attributes.depth,
multiview: self.fbo.attributes.multiview,
antialias: self.fbo.attributes.multisampling,
}
}
}
48 changes: 48 additions & 0 deletions components/script/dom/vrviewlist.rs
@@ -0,0 +1,48 @@
/* 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 http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::VRViewListBinding;
use dom::bindings::codegen::Bindings::VRViewListBinding::VRViewListMethods;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::root::{Dom, DomRoot};
use dom::vrview::VRView;
use dom::window::Window;
use dom_struct::dom_struct;

#[dom_struct]
pub struct VRViewList {
reflector_: Reflector,
views: Vec<Dom<VRView>>,
}

impl VRViewList {
fn new_inherited(views: &[&VRView]) -> VRViewList {
VRViewList {
reflector_: Reflector::new(),
views: views.iter().map(|VRView| Dom::from_ref(*VRView)).collect(),
}
}

pub fn new(window: &Window, views: &[&VRView]) -> DomRoot<VRViewList> {
reflect_dom_object(Box::new(VRViewList::new_inherited(views)),
window, VRViewListBinding::Wrap)
}
}

impl VRViewListMethods for VRViewList {
/// https://w3c.github.io/VRView-events/#widl-VRViewList-length
fn Length(&self) -> u32 {
self.views.len() as u32
}

/// https://w3c.github.io/VRView-events/#widl-VRViewList-item-getter-VRView-unsigned-long-index
fn Item(&self, index: u32) -> Option<DomRoot<VRView>> {
self.views.get(index as usize).map(|js| DomRoot::from_ref(&**js))
}

/// https://w3c.github.io/VRView-events/#widl-VRViewList-item-getter-VRView-unsigned-long-index
fn IndexedGetter(&self, index: u32) -> Option<DomRoot<VRView>> {
self.Item(index)
}
}

0 comments on commit 9e2f5f1

Please sign in to comment.