Auto merge of #23797 - asajeffrey:webxr-framebuffer, r=Manishearth

Added framebuffer and related attributes to XRWebGLLayer

<!-- Please describe your changes on the following line: -->

Implement the `framebuffer` attribute of `XRWebGLLayer`.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes have tests

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23797)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-07-19 18:40:44 -04:00 committed by GitHub
commit 1128f40f4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 249 additions and 68 deletions

4
Cargo.lock generated
View file

@ -5485,7 +5485,7 @@ dependencies = [
[[package]] [[package]]
name = "webxr" name = "webxr"
version = "0.0.1" version = "0.0.1"
source = "git+https://github.com/servo/webxr#abc779798259d287539347a5d2048ae745f1f2ac" source = "git+https://github.com/servo/webxr#5c0eb61f8f38429fde45673d0884f947d14e9c79"
dependencies = [ dependencies = [
"euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)",
@ -5497,7 +5497,7 @@ dependencies = [
[[package]] [[package]]
name = "webxr-api" name = "webxr-api"
version = "0.0.1" version = "0.0.1"
source = "git+https://github.com/servo/webxr#abc779798259d287539347a5d2048ae745f1f2ac" source = "git+https://github.com/servo/webxr#5c0eb61f8f38429fde45673d0884f947d14e9c79"
dependencies = [ dependencies = [
"euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -84,6 +84,8 @@ pub enum Error {
InvalidModification, InvalidModification,
/// NotReadableError DOMException /// NotReadableError DOMException
NotReadable, NotReadable,
/// OperationError DOMException
Operation,
/// TypeError JavaScript Error /// TypeError JavaScript Error
Type(String), Type(String),
@ -136,6 +138,7 @@ pub unsafe fn throw_dom_exception(cx: *mut JSContext, global: &GlobalScope, resu
Error::TypeMismatch => DOMErrorName::TypeMismatchError, Error::TypeMismatch => DOMErrorName::TypeMismatchError,
Error::InvalidModification => DOMErrorName::InvalidModificationError, Error::InvalidModification => DOMErrorName::InvalidModificationError,
Error::NotReadable => DOMErrorName::NotReadableError, Error::NotReadable => DOMErrorName::NotReadableError,
Error::Operation => DOMErrorName::OperationError,
Error::Type(message) => { Error::Type(message) => {
assert!(!JS_IsExceptionPending(cx)); assert!(!JS_IsExceptionPending(cx));
throw_type_error(cx, &message); throw_type_error(cx, &message);

View file

@ -57,8 +57,8 @@ use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
use encoding_rs::{Decoder, Encoding}; use encoding_rs::{Decoder, Encoding};
use euclid::Length as EuclidLength; use euclid::Length as EuclidLength;
use euclid::{ use euclid::{
Point2D, Rect, RigidTransform3D, Rotation3D, Transform2D, Transform3D, TypedRigidTransform3D, Point2D, Rect, RigidTransform3D, Rotation3D, Transform2D, Transform3D, TypedRect,
TypedScale, TypedSize2D, Vector2D, TypedRigidTransform3D, TypedScale, TypedSize2D, Vector2D,
}; };
use html5ever::buffer_queue::BufferQueue; use html5ever::buffer_queue::BufferQueue;
use html5ever::{LocalName, Namespace, Prefix, QualName}; use html5ever::{LocalName, Namespace, Prefix, QualName};
@ -647,6 +647,13 @@ unsafe impl<U> JSTraceable for TypedSize2D<u32, U> {
} }
} }
unsafe impl<U> JSTraceable for TypedRect<i32, U> {
#[inline]
unsafe fn trace(&self, _trc: *mut JSTracer) {
// Do nothing
}
}
unsafe impl JSTraceable for StyleLocked<FontFaceRule> { unsafe impl JSTraceable for StyleLocked<FontFaceRule> {
unsafe fn trace(&self, _trc: *mut JSTracer) { unsafe fn trace(&self, _trc: *mut JSTracer) {
// Do nothing. // Do nothing.

View file

@ -13,7 +13,7 @@ use crate::dom::globalscope::GlobalScope;
use dom_struct::dom_struct; use dom_struct::dom_struct;
#[repr(u16)] #[repr(u16)]
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)] #[derive(Clone, Copy, Debug, Eq, JSTraceable, MallocSizeOf, Ord, PartialEq, PartialOrd)]
pub enum DOMErrorName { pub enum DOMErrorName {
IndexSizeError = DOMExceptionConstants::INDEX_SIZE_ERR, IndexSizeError = DOMExceptionConstants::INDEX_SIZE_ERR,
HierarchyRequestError = DOMExceptionConstants::HIERARCHY_REQUEST_ERR, HierarchyRequestError = DOMExceptionConstants::HIERARCHY_REQUEST_ERR,
@ -36,7 +36,8 @@ pub enum DOMErrorName {
TimeoutError = DOMExceptionConstants::TIMEOUT_ERR, TimeoutError = DOMExceptionConstants::TIMEOUT_ERR,
InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR, InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR,
DataCloneError = DOMExceptionConstants::DATA_CLONE_ERR, DataCloneError = DOMExceptionConstants::DATA_CLONE_ERR,
NotReadableError = DOMExceptionConstants::NOT_READABLE_ERR, NotReadableError,
OperationError,
} }
impl DOMErrorName { impl DOMErrorName {
@ -64,6 +65,7 @@ impl DOMErrorName {
"InvalidNodeTypeError" => Some(DOMErrorName::InvalidNodeTypeError), "InvalidNodeTypeError" => Some(DOMErrorName::InvalidNodeTypeError),
"DataCloneError" => Some(DOMErrorName::DataCloneError), "DataCloneError" => Some(DOMErrorName::DataCloneError),
"NotReadableError" => Some(DOMErrorName::NotReadableError), "NotReadableError" => Some(DOMErrorName::NotReadableError),
"OperationError" => Some(DOMErrorName::OperationError),
_ => None, _ => None,
} }
} }
@ -107,6 +109,9 @@ impl DOMException {
}, },
DOMErrorName::DataCloneError => "The object can not be cloned.", DOMErrorName::DataCloneError => "The object can not be cloned.",
DOMErrorName::NotReadableError => "The I/O read operation failed.", DOMErrorName::NotReadableError => "The I/O read operation failed.",
DOMErrorName::OperationError => {
"The operation failed for an operation-specific reason."
},
}; };
( (
@ -147,11 +152,11 @@ impl DOMException {
} }
impl DOMExceptionMethods for DOMException { impl DOMExceptionMethods for DOMException {
// https://heycam.github.io/webidl/#dfn-DOMException // https://heycam.github.io/webidl/#dom-domexception-code
fn Code(&self) -> u16 { fn Code(&self) -> u16 {
match DOMErrorName::from(&self.name) { match DOMErrorName::from(&self.name) {
Some(code) => code as u16, Some(code) if code <= DOMErrorName::DataCloneError => code as u16,
None => 0 as u16, _ => 0,
} }
} }

View file

@ -11,6 +11,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::{TypedPoint2D, TypedRect, TypedSize2D};
use euclid::{TypedRigidTransform3D, TypedRotation3D, TypedTransform3D, TypedVector3D}; use euclid::{TypedRigidTransform3D, TypedRotation3D, TypedTransform3D, TypedVector3D};
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use webxr_api::{MockDeviceMsg, View, Views}; use webxr_api::{MockDeviceMsg, View, Views};
@ -71,13 +72,24 @@ pub fn get_views(views: &[FakeXRViewInit]) -> Fallible<Views> {
let offset_l = get_origin(&left.viewOffset)?.inverse(); let offset_l = get_origin(&left.viewOffset)?.inverse();
let offset_r = get_origin(&right.viewOffset)?.inverse(); let offset_r = get_origin(&right.viewOffset)?.inverse();
let size_l = TypedSize2D::new(views[0].resolution.width, views[0].resolution.height);
let size_r = TypedSize2D::new(views[1].resolution.width, views[1].resolution.height);
let origin_l = TypedPoint2D::new(0, 0);
let origin_r = TypedPoint2D::new(size_l.width, 0);
let viewport_l = TypedRect::new(origin_l, size_l);
let viewport_r = TypedRect::new(origin_r, size_r);
let left = View { let left = View {
projection: proj_l, projection: proj_l,
transform: offset_l, transform: offset_l,
viewport: viewport_l,
}; };
let right = View { let right = View {
projection: proj_r, projection: proj_r,
transform: offset_r, transform: offset_r,
viewport: viewport_r,
}; };
Ok(Views::Stereo(left, right)) Ok(Views::Stereo(left, right))
} }

View file

@ -349,10 +349,6 @@ impl WebGLRenderingContext {
} }
} }
pub fn size(&self) -> Size2D<u32> {
self.size.get()
}
// Helper function for validating framebuffer completeness in // Helper function for validating framebuffer completeness in
// calls touching the framebuffer. From the GLES 2.0.25 spec, // calls touching the framebuffer. From the GLES 2.0.25 spec,
// page 119: // page 119:

View file

@ -38,7 +38,6 @@ interface DOMException {
const unsigned short TIMEOUT_ERR = 23; const unsigned short TIMEOUT_ERR = 23;
const unsigned short INVALID_NODE_TYPE_ERR = 24; const unsigned short INVALID_NODE_TYPE_ERR = 24;
const unsigned short DATA_CLONE_ERR = 25; const unsigned short DATA_CLONE_ERR = 25;
const unsigned short NOT_READABLE_ERR = 26;
// Error code as u16 // Error code as u16
readonly attribute unsigned short code; readonly attribute unsigned short code;

View file

@ -30,9 +30,9 @@ interface XRWebGLLayer : XRLayer {
readonly attribute boolean stencil; readonly attribute boolean stencil;
readonly attribute boolean alpha; readonly attribute boolean alpha;
// readonly attribute WebGLFramebuffer framebuffer; readonly attribute WebGLFramebuffer framebuffer;
// readonly attribute unsigned long framebufferWidth; readonly attribute unsigned long framebufferWidth;
// readonly attribute unsigned long framebufferHeight; readonly attribute unsigned long framebufferHeight;
// // Methods // // Methods
XRViewport? getViewport(XRView view); XRViewport? getViewport(XRView view);

View file

@ -103,7 +103,7 @@ impl XRSession {
ret ret
} }
pub fn with_session<F: FnOnce(&Session)>(&self, with: F) { pub fn with_session<R, F: FnOnce(&Session) -> R>(&self, with: F) -> R {
let session = self.session.borrow(); let session = self.session.borrow();
with(&session) with(&session)
} }

View file

@ -8,36 +8,26 @@ use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::TypedRect;
use webxr_api::Viewport;
#[dom_struct] #[dom_struct]
pub struct XRViewport { pub struct XRViewport {
reflector_: Reflector, reflector_: Reflector,
x: u32, viewport: TypedRect<i32, Viewport>,
y: u32,
width: u32,
height: u32,
} }
impl XRViewport { impl XRViewport {
fn new_inherited(x: u32, y: u32, width: u32, height: u32) -> XRViewport { fn new_inherited(viewport: TypedRect<i32, Viewport>) -> XRViewport {
XRViewport { XRViewport {
reflector_: Reflector::new(), reflector_: Reflector::new(),
x, viewport,
y,
width,
height,
} }
} }
pub fn new( pub fn new(global: &GlobalScope, viewport: TypedRect<i32, Viewport>) -> DomRoot<XRViewport> {
global: &GlobalScope,
x: u32,
y: u32,
width: u32,
height: u32,
) -> DomRoot<XRViewport> {
reflect_dom_object( reflect_dom_object(
Box::new(XRViewport::new_inherited(x, y, width, height)), Box::new(XRViewport::new_inherited(viewport)),
global, global,
XRViewportBinding::Wrap, XRViewportBinding::Wrap,
) )
@ -47,21 +37,21 @@ impl XRViewport {
impl XRViewportMethods for XRViewport { impl XRViewportMethods for XRViewport {
/// https://immersive-web.github.io/webxr/#dom-xrviewport-x /// https://immersive-web.github.io/webxr/#dom-xrviewport-x
fn X(&self) -> i32 { fn X(&self) -> i32 {
self.x as i32 self.viewport.origin.x
} }
/// https://immersive-web.github.io/webxr/#dom-xrviewport-y /// https://immersive-web.github.io/webxr/#dom-xrviewport-y
fn Y(&self) -> i32 { fn Y(&self) -> i32 {
self.y as i32 self.viewport.origin.y
} }
/// https://immersive-web.github.io/webxr/#dom-xrviewport-width /// https://immersive-web.github.io/webxr/#dom-xrviewport-width
fn Width(&self) -> i32 { fn Width(&self) -> i32 {
self.width as i32 self.viewport.size.width
} }
/// https://immersive-web.github.io/webxr/#dom-xrviewport-height /// https://immersive-web.github.io/webxr/#dom-xrviewport-height
fn Height(&self) -> i32 { fn Height(&self) -> i32 {
self.height as i32 self.viewport.size.height
} }
} }

View file

@ -6,10 +6,15 @@ use crate::dom::bindings::codegen::Bindings::XRViewBinding::{XREye, XRViewMethod
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding; use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerInit; use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerInit;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods; use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::error::Fallible; use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::webgl_validations::types::TexImageTarget;
use crate::dom::webglframebuffer::WebGLFramebuffer;
use crate::dom::webglrenderingcontext::WebGLRenderingContext; use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::window::Window; use crate::dom::window::Window;
use crate::dom::xrlayer::XRLayer; use crate::dom::xrlayer::XRLayer;
@ -17,6 +22,9 @@ use crate::dom::xrsession::XRSession;
use crate::dom::xrview::XRView; use crate::dom::xrview::XRView;
use crate::dom::xrviewport::XRViewport; use crate::dom::xrviewport::XRViewport;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::rust::CustomAutoRooter;
use std::convert::TryInto;
use webxr_api::Views;
#[dom_struct] #[dom_struct]
pub struct XRWebGLLayer { pub struct XRWebGLLayer {
@ -27,6 +35,7 @@ pub struct XRWebGLLayer {
alpha: bool, alpha: bool,
context: Dom<WebGLRenderingContext>, context: Dom<WebGLRenderingContext>,
session: Dom<XRSession>, session: Dom<XRSession>,
framebuffer: Dom<WebGLFramebuffer>,
} }
impl XRWebGLLayer { impl XRWebGLLayer {
@ -34,6 +43,7 @@ impl XRWebGLLayer {
session: &XRSession, session: &XRSession,
context: &WebGLRenderingContext, context: &WebGLRenderingContext,
init: &XRWebGLLayerInit, init: &XRWebGLLayerInit,
framebuffer: &WebGLFramebuffer,
) -> XRWebGLLayer { ) -> XRWebGLLayer {
XRWebGLLayer { XRWebGLLayer {
xrlayer: XRLayer::new_inherited(), xrlayer: XRLayer::new_inherited(),
@ -43,6 +53,7 @@ impl XRWebGLLayer {
alpha: init.alpha, alpha: init.alpha,
context: Dom::from_ref(context), context: Dom::from_ref(context),
session: Dom::from_ref(session), session: Dom::from_ref(session),
framebuffer: Dom::from_ref(framebuffer),
} }
} }
@ -51,21 +62,82 @@ impl XRWebGLLayer {
session: &XRSession, session: &XRSession,
context: &WebGLRenderingContext, context: &WebGLRenderingContext,
init: &XRWebGLLayerInit, init: &XRWebGLLayerInit,
framebuffer: &WebGLFramebuffer,
) -> DomRoot<XRWebGLLayer> { ) -> DomRoot<XRWebGLLayer> {
reflect_dom_object( reflect_dom_object(
Box::new(XRWebGLLayer::new_inherited(session, context, init)), Box::new(XRWebGLLayer::new_inherited(
session,
context,
init,
framebuffer,
)),
global, global,
XRWebGLLayerBinding::Wrap, XRWebGLLayerBinding::Wrap,
) )
} }
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-xrwebgllayer
pub fn Constructor( pub fn Constructor(
global: &Window, global: &Window,
session: &XRSession, session: &XRSession,
context: &WebGLRenderingContext, context: &WebGLRenderingContext,
init: &XRWebGLLayerInit, init: &XRWebGLLayerInit,
) -> Fallible<DomRoot<Self>> { ) -> Fallible<DomRoot<Self>> {
Ok(XRWebGLLayer::new(&global.global(), session, context, init)) let cx = global.get_cx();
let old_fbo = context.bound_framebuffer();
let old_texture = context
.textures()
.active_texture_for_image_target(TexImageTarget::Texture2D);
// Step 8.2. "Initialize layers framebuffer to a new opaque framebuffer created with context."
let framebuffer = context.CreateFramebuffer().ok_or(Error::Operation)?;
// Step 8.3. "Allocate and initialize resources compatible with sessions XR device,
// including GPU accessible memory buffers, as required to support the compositing of layer."
// Create a new texture with size given by the session's recommended resolution
let texture = context.CreateTexture().ok_or(Error::Operation)?;
let resolution = session.with_session(|s| s.recommended_framebuffer_resolution());
let mut pixels = CustomAutoRooter::new(None);
context.BindTexture(constants::TEXTURE_2D, Some(&texture));
let sc = context.TexImage2D(
constants::TEXTURE_2D,
0,
constants::RGBA,
resolution.width,
resolution.height,
0,
constants::RGBA,
constants::UNSIGNED_BYTE,
pixels.root(cx),
);
// Bind the new texture to the framebuffer
context.BindFramebuffer(constants::FRAMEBUFFER, Some(&framebuffer));
context.FramebufferTexture2D(
constants::FRAMEBUFFER,
constants::COLOR_ATTACHMENT0,
constants::TEXTURE_2D,
Some(&texture),
0,
);
// Restore the WebGL state while complaining about global mutable state
context.BindTexture(constants::TEXTURE_2D, old_texture.as_ref().map(|t| &**t));
context.BindFramebuffer(constants::FRAMEBUFFER, old_fbo.as_ref().map(|f| &**f));
// Step 8.4: "If layers resources were unable to be created for any reason,
// throw an OperationError and abort these steps."
sc.or(Err(Error::Operation))?;
// Step 9. "Return layer."
Ok(XRWebGLLayer::new(
&global.global(),
session,
context,
init,
&framebuffer,
))
} }
} }
@ -95,28 +167,48 @@ impl XRWebGLLayerMethods for XRWebGLLayer {
DomRoot::from_ref(&self.context) DomRoot::from_ref(&self.context)
} }
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebuffer
fn Framebuffer(&self) -> DomRoot<WebGLFramebuffer> {
DomRoot::from_ref(&self.framebuffer)
}
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferwidth
fn FramebufferWidth(&self) -> u32 {
self.framebuffer
.size()
.unwrap_or((0, 0))
.0
.try_into()
.unwrap_or(0)
}
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferheight
fn FramebufferHeight(&self) -> u32 {
self.framebuffer
.size()
.unwrap_or((0, 0))
.1
.try_into()
.unwrap_or(0)
}
/// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport
fn GetViewport(&self, view: &XRView) -> Option<DomRoot<XRViewport>> { fn GetViewport(&self, view: &XRView) -> Option<DomRoot<XRViewport>> {
if self.session != view.session() { if self.session != view.session() {
return None; return None;
} }
let size = self.context.size(); let views = self.session.with_session(|s| s.views().clone());
let x = if view.Eye() == XREye::Left { let viewport = match (view.Eye(), views) {
0 (XREye::None, Views::Mono(view)) => view.viewport,
} else { (XREye::Left, Views::Stereo(view, _)) => view.viewport,
size.width / 2 (XREye::Right, Views::Stereo(_, view)) => view.viewport,
// The spec doesn't really say what to do in this case
// https://github.com/immersive-web/webxr/issues/769
_ => return None,
}; };
// XXXManishearth this assumes the WebVR default of canvases being cut in half
// which need not be generally true for all devices, and will not work in Some(XRViewport::new(&self.global(), viewport))
// inline VR mode
Some(XRViewport::new(
&self.global(),
x,
0,
size.width / 2,
size.height,
))
} }
} }

View file

@ -410535,6 +410535,12 @@
{} {}
] ]
], ],
"webxr/xrWebGLLayer_framebuffer.https.html": [
[
"webxr/xrWebGLLayer_framebuffer.https.html",
{}
]
],
"webxr/xrWebGLLayer_viewports.https.html": [ "webxr/xrWebGLLayer_viewports.https.html": [
[ [
"webxr/xrWebGLLayer_viewports.https.html", "webxr/xrWebGLLayer_viewports.https.html",
@ -688404,8 +688410,12 @@
"956edba98365d90eb180ad3c9697dae098bd25db", "956edba98365d90eb180ad3c9697dae098bd25db",
"testharness" "testharness"
], ],
"webxr/xrWebGLLayer_framebuffer.https.html": [
"7c4ff17c2547995afbf561850faaf92b6aa5e56f",
"testharness"
],
"webxr/xrWebGLLayer_viewports.https.html": [ "webxr/xrWebGLLayer_viewports.https.html": [
"9553a445e43153b716d55c7f2be606dd548b808e", "8cfccff3c01dedb41db4f26563824ce0cdb65ad3",
"testharness" "testharness"
], ],
"workers/META.yml": [ "workers/META.yml": [

View file

@ -86,9 +86,6 @@
[XRSessionEvent interface object name] [XRSessionEvent interface object name]
expected: FAIL expected: FAIL
[XRWebGLLayer interface: attribute framebufferHeight]
expected: FAIL
[XRInputSourceEvent interface: attribute inputSource] [XRInputSourceEvent interface: attribute inputSource]
expected: FAIL expected: FAIL
@ -128,9 +125,6 @@
[XRRay interface object name] [XRRay interface object name]
expected: FAIL expected: FAIL
[XRWebGLLayer interface: attribute framebufferWidth]
expected: FAIL
[XRInputSourceArray interface: existence and properties of interface prototype object's @@unscopables property] [XRInputSourceArray interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL expected: FAIL
@ -236,9 +230,6 @@
[XRInputSourceArray interface: existence and properties of interface prototype object's "constructor" property] [XRInputSourceArray interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL expected: FAIL
[XRWebGLLayer interface: attribute framebuffer]
expected: FAIL
[XRInputSourceEvent interface object length] [XRInputSourceEvent interface object length]
expected: FAIL expected: FAIL

View file

@ -0,0 +1,4 @@
[xrWebGLLayer_framebuffer.https.html]
[XRWebGLLayer reports a valid framebuffer for inline sessions]
expected: FAIL

View file

@ -0,0 +1,58 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/webxr_test_constants.js"></script>
<script src="resources/webxr_util.js"></script>
<canvas></canvas>
<script>
let immersiveTestName = "XRWebGLLayer reports a valid framebuffer for immersive sessions";
let inlineTestName = "XRWebGLLayer reports a valid framebuffer for inline sessions";
let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE;
let testFunction = function(session, fakeDeviceController, t) {
return session.requestReferenceSpace('viewer')
.then((space) => new Promise((resolve) => {
function onFrame(time, xrFrame) {
let layer = xrFrame.session.renderState.baseLayer;
let gl = layer.context;
// The layer's framebuffer is a WebGL framebuffer
assert_not_equals(layer.framebuffer, null);
assert_true(layer.framebuffer instanceof WebGLFramebuffer);
// The XR framebuffer is not bound to the GL context by default.
assert_not_equals(layer.framebuffer, gl.getParameter(gl.FRAMEBUFFER_BINDING));
// The XR framebuffer can be bound to the GL context.
gl.bindFramebuffer(gl.FRAMEBUFFER, layer.framebuffer);
assert_equals(layer.framebuffer, gl.getParameter(gl.FRAMEBUFFER_BINDING));
// The XR framebuffer has a 2D texture
let attachment = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
assert_equals(attachment, gl.TEXTURE);
// Check that each viewport fits inside the framebuffer dimensions
let viewer_pose = xrFrame.getViewerPose(space);
for (view of viewer_pose.views) {
let viewport = layer.getViewport(view);
assert_less_than_equal(viewport.x + viewport.width, layer.framebufferWidth);
assert_less_than_equal(viewport.y + viewport.height, layer.framebufferHeight);
}
// Finished test.
resolve();
}
session.requestAnimationFrame(onFrame);
}));
};
xr_session_promise_test(immersiveTestName, testFunction,
fakeDeviceInitParams, 'immersive-vr');
xr_session_promise_test(inlineTestName, testFunction,
fakeDeviceInitParams, 'inline');
</script>

View file

@ -22,6 +22,8 @@ let testFunction = function(session, fakeDeviceController, t) {
let layer = xrFrame.session.renderState.baseLayer; let layer = xrFrame.session.renderState.baseLayer;
for (view of viewer_pose.views) { for (view of viewer_pose.views) {
let viewport = layer.getViewport(view); let viewport = layer.getViewport(view);
let index = (view.eye === "right"? 1: 0);
let params = fakeDeviceInitParams.views[index];
// Ensure the returned object is an XRViewport object // Ensure the returned object is an XRViewport object
assert_not_equals(viewport, null); assert_not_equals(viewport, null);
@ -30,8 +32,20 @@ let testFunction = function(session, fakeDeviceController, t) {
// Ensure the viewport dimensions are valid // Ensure the viewport dimensions are valid
assert_greater_than_equal(viewport.x, 0); assert_greater_than_equal(viewport.x, 0);
assert_greater_than_equal(viewport.y, 0); assert_greater_than_equal(viewport.y, 0);
assert_greater_than_equal(viewport.width, 1); assert_equals(viewport.width, params.resolution.width);
assert_greater_than_equal(viewport.height, 1); assert_equals(viewport.height, params.resolution.height);
// Ensure none of the viewports overlap
for (other of viewer_pose.views) {
if (view !== other) {
let otherport = layer.getViewport(other);
let no_overlap = (viewport.x + viewport.width <= otherport.x) ||
(otherport.x + otherport.width <= viewport.x) ||
(viewport.y + viewport.height <= otherport.y) ||
(otherport.y + otherport.height <= viewport.y);
assert_true(no_overlap, "Overlap between viewport " + view.eye + " and " + other.eye);
}
}
} }
// Finished test. // Finished test.