Bug 1229481 - Part 2: Add draw rect reftest for WebVR; r?kip
MozReview-Commit-ID: CnSYFFZS8lP
--- a/dom/vr/moz.build
+++ b/dom/vr/moz.build
@@ -1,16 +1,16 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
with Files("**"):
- BUG_COMPONENT = ("Core", "DOM")
+ BUG_COMPONENT = ("Core", "WebVR")
EXPORTS.mozilla.dom += [
'VRDisplay.h',
'VRDisplayEvent.h',
'VREventObserver.h',
'VRServiceTest.h'
]
@@ -23,9 +23,10 @@ UNIFIED_SOURCES = [
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/dom/base'
]
-MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
\ No newline at end of file
+MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
+REFTEST_MANIFESTS += ['test/reftest/reftest.list']
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/reftest/VRSimulationDriver.js
@@ -0,0 +1,56 @@
+
+var VRServiceTest;
+var vrMockDisplay;
+
+var VRSimulationDriver = (function() {
+"use strict";
+
+var AttachWebVRDisplay = function() {
+ var promise = VRServiceTest.attachVRDisplay("VRDisplayTest");
+ promise.then(function (display) {
+ vrMockDisplay = display;
+ });
+
+ return promise;
+};
+
+var SetVRDisplayPose = function(position,
+ linearVelocity, linearAcceleration,
+ orientation, angularVelocity,
+ angularAcceleration) {
+ vrMockDisplay.setPose(position, linearVelocity, linearAcceleration,
+ orientation, angularVelocity, angularAcceleration);
+};
+
+var SetEyeResolution = function(width, height) {
+ vrMockDisplay.setEyeResolution(width, height);
+}
+
+var SetEyeParameter = function(eye, offsetX, offsetY, offsetZ,
+ upDegree, rightDegree, downDegree, leftDegree) {
+ vrMockDisplay.setEyeParameter(eye, offsetX, offsetY, offsetZ, upDegree, rightDegree,
+ downDegree, leftDegree);
+}
+
+var SetMountState = function(isMounted) {
+ vrMockDisplay.setMountState(isMounted);
+}
+
+var UpdateVRDisplay = function() {
+ vrMockDisplay.update();
+}
+
+var API = {
+ AttachWebVRDisplay: AttachWebVRDisplay,
+ SetVRDisplayPose: SetVRDisplayPose,
+ SetEyeResolution: SetEyeResolution,
+ SetEyeParameter: SetEyeParameter,
+ SetMountState: SetMountState,
+ UpdateVRDisplay: UpdateVRDisplay,
+
+ none: false
+};
+
+return API;
+
+}());
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/reftest/draw_rect.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<meta charset='UTF-8'>
+<!-- Draw rect in WebGL and submit it to the VR device as a base64 image.
+If this fails, something is seriously wrong. -->
+<html class="reftest-wait">
+<head>
+ <script type='text/javascript' src='webgl-util.js'></script>
+ <script type='text/javascript' src="VRSimulationDriver.js"></script>
+ <script id="vs" type="x-shader/x-vertex">
+ attribute vec2 aVertCoord;
+
+ void main(void) {
+ gl_Position = vec4(aVertCoord, 0.0, 1.0);
+ }
+ </script>
+ <script id="fs" type="x-shader/x-fragment">
+ precision mediump float;
+
+ void main(void) {
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ }
+ </script>
+ <script type='text/javascript'>
+ 'use strict';
+
+ var submitResult = null;
+ var vrDisplay = null;
+ var webglCanvas = null;
+ var gl = null;
+ var prog = null;
+ var img = null;
+
+ function setStatus(text) {
+ var elem = document.getElementById('status');
+ elem.innerHTML = text;
+ }
+
+ function initVRMock() {
+ VRServiceTest = navigator.requestVRServiceTest();
+ if (!VRServiceTest) {
+ setStatus('VRServiceTest get failed.');
+ return;
+ }
+
+ VRSimulationDriver.AttachWebVRDisplay().then(() => {
+ // Looking for VR displays
+ if (navigator.getVRDisplays) {
+ submitResult = new VRSubmitFrameResult();
+ navigator.getVRDisplays().then(function (displays) {
+ if (displays.length > 0) {
+ vrDisplay = displays[0];
+ vrDisplay.requestPresent([{ source: webglCanvas }]);
+ vrDisplay.requestAnimationFrame(onAnimationFrame);
+ }
+ });
+ }
+ });
+ }
+
+ function onAnimationFrame() {
+ if (!vrDisplay.isPresenting) {
+ return;
+ }
+
+ vrDisplay.requestAnimationFrame(onAnimationFrame);
+ gl.clearColor(0.0, 1.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ // Presenting render a stereo view.
+ gl.viewport(0, 0, webglCanvas.width * 0.5, webglCanvas.height);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+ gl.viewport(webglCanvas.width * 0.5, 0, webglCanvas.width * 0.5, webglCanvas.height);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+ // Indicate VRDisplay we're done rendering.
+ vrDisplay.submitFrame();
+ if (vrDisplay.getSubmitFrameResult(submitResult)) {
+ if (!img) {
+ img = document.createElement("img");
+ img.onload = function(){
+ webglCanvas.style.display = 'none';
+ vrDisplay.exitPresent();
+ setTimeout(testComplete, 0);
+ };
+ img.src = submitResult.base64Image;
+ document.body.appendChild(img);
+ } else {
+ img.src = submitResult.base64Image;
+ }
+ }
+ }
+
+ function runTest() {
+ webglCanvas = document.getElementById('canvas');
+ gl = WebGLUtil.getWebGL('canvas');
+ if (!gl) {
+ setStatus('WebGL context creation failed.');
+ return;
+ }
+ gl.disable(gl.DEPTH_TEST);
+ prog = WebGLUtil.createProgramByIds(gl, 'vs', 'fs');
+ if (!prog) {
+ setStatus('Program linking failed.');
+ return;
+ }
+ prog.aVertCoord = gl.getAttribLocation(prog, "aVertCoord");
+
+ var vertCoordArr = new Float32Array([
+ -0.5, -0.5,
+ 0.5, -0.5,
+ -0.5, 0.5,
+ 0.5, 0.5,
+ ]);
+ var vertCoordBuff = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertCoordBuff);
+ gl.bufferData(gl.ARRAY_BUFFER, vertCoordArr, gl.STATIC_DRAW);
+ gl.useProgram(prog);
+ gl.enableVertexAttribArray(prog.aVertCoord);
+ gl.vertexAttribPointer(prog.aVertCoord, 2, gl.FLOAT, false, 0, 0);
+
+ initVRMock();
+ }
+
+ function testComplete() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+</head>
+
+<body onload='runTest();'>
+ <canvas id='canvas' width='256' height='256'></canvas>
+ <div id='status'></div>
+</body>
+
+</html>
\ No newline at end of file
new file mode 100644
index 0000000000000000000000000000000000000000..0f4d24a0d19ea26224adb3d90dab146f5a4c7a95
GIT binary patch
literal 1747
zc%17D@N?(olHy`uVBq!ia0y~yU}OMc4mKbaQ?w$Dfq`w7r;B4q#hkYnH}WzV@EqRo
z=YL5x8#{MPnextR&$N$>o-%d{orl>~3V$@H@X1d&@{oCwMU8_{JHN-H55q&!oxfuH
zU8~x9+2!mn*k4?G`TMnnt%JS8cE9`a$6qr3V%$~w>z|b${{{XFYcGGlURc#o*N``N
z-~PVK%wL#GUVr&pI!A7S{DSD^@7F*6!tj@2)TmLTMvWRZie^R)e2GaH$wWJisj`nh
THge7eR#pt2u6{1-oD!M<+goFD
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/reftest/reftest.list
@@ -0,0 +1,7 @@
+# WebVR Reftests
+default-preferences pref(dom.vr.puppet.enabled,true) pref(dom.vr.test.enabled,true) pref(dom.vr.require-gesture,false) pref(dom.vr.puppet.submitframe,1)
+
+# VR SubmitFrame is only implemented for D3D11 now.
+# We need to continue to investigate why these reftests can be run well in local,
+# but will be suspended until terminating on reftest debug build.
+skip-if(!winWidget||!layersGPUAccelerated||isDebugBuild) == draw_rect.html wrapper.html?draw_rect.png
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/reftest/webgl-util.js
@@ -0,0 +1,170 @@
+WebGLUtil = (function() {
+ // ---------------------------------------------------------------------------
+ // Error handling (for obvious failures, such as invalid element ids)
+
+ function defaultErrorFunc(str) {
+ console.log('Error: ' + str);
+ }
+
+ var gErrorFunc = defaultErrorFunc;
+ function setErrorFunc(func) {
+ gErrorFunc = func;
+ }
+
+ function error(str) {
+ gErrorFunc(str);
+ }
+
+ // ---------------------------------------------------------------------------
+ // Warning handling (for failures that may be intentional)
+
+ function defaultWarningFunc(str) {
+ console.log('Warning: ' + str);
+ }
+
+ var gWarningFunc = defaultWarningFunc;
+ function setWarningFunc(func) {
+ gWarningFunc = func;
+ }
+
+ function warning(str) {
+ gWarningFunc(str);
+ }
+
+ // ---------------------------------------------------------------------------
+ // WebGL helpers
+
+ function getWebGL(canvasId, requireConformant, attributes) {
+ // `requireConformant` will default to falsey if it is not supplied.
+
+ var canvas = document.getElementById(canvasId);
+
+ var gl = null;
+ try {
+ gl = canvas.getContext('webgl', attributes);
+ } catch(e) {}
+
+ if (!gl && !requireConformant) {
+ try {
+ gl = canvas.getContext('experimental-webgl', attributes);
+ } catch(e) {}
+ }
+
+ if (!gl) {
+ error('WebGL context could not be retrieved from \'' + canvasId + '\'.');
+ return null;
+ }
+
+ return gl;
+ }
+
+ function withWebGL2(canvasId, callback, onFinished) {
+ var run = function() {
+ var canvas = document.getElementById(canvasId);
+
+ var gl = null;
+ try {
+ gl = canvas.getContext('webgl2');
+ } catch(e) {}
+
+ if (!gl) {
+ todo(false, 'WebGL2 is not supported');
+ onFinished();
+ return;
+ }
+
+ function errorFunc(str) {
+ ok(false, 'Error: ' + str);
+ }
+ setErrorFunc(errorFunc);
+ setWarningFunc(errorFunc);
+
+ callback(gl);
+ onFinished();
+ };
+
+ try {
+ var prefArrArr = [
+ ['webgl.force-enabled', true],
+ ['webgl.enable-webgl2', true],
+ ];
+ var prefEnv = {'set': prefArrArr};
+ SpecialPowers.pushPrefEnv(prefEnv, run);
+ } catch (e) {
+ warning('No SpecialPowers, but trying WebGL2 anyway...');
+ run();
+ }
+ }
+
+ function getContentFromElem(elem) {
+ var str = "";
+ var k = elem.firstChild;
+ while (k) {
+ if (k.nodeType == 3)
+ str += k.textContent;
+
+ k = k.nextSibling;
+ }
+
+ return str;
+ }
+
+ // Returns a valid shader, or null on errors.
+ function createShaderById(gl, id) {
+ var elem = document.getElementById(id);
+ if (!elem) {
+ error('Failed to create shader from non-existent id \'' + id + '\'.');
+ return null;
+ }
+
+ var src = getContentFromElem(elem);
+
+ var shader;
+ if (elem.type == "x-shader/x-fragment") {
+ shader = gl.createShader(gl.FRAGMENT_SHADER);
+ } else if (elem.type == "x-shader/x-vertex") {
+ shader = gl.createShader(gl.VERTEX_SHADER);
+ } else {
+ error('Bad MIME type for shader \'' + id + '\': ' + elem.type + '.');
+ return null;
+ }
+
+ gl.shaderSource(shader, src);
+ gl.compileShader(shader);
+
+ return shader;
+ }
+
+ function createProgramByIds(gl, vsId, fsId) {
+ var vs = createShaderById(gl, vsId);
+ var fs = createShaderById(gl, fsId);
+ if (!vs || !fs)
+ return null;
+
+ var prog = gl.createProgram();
+ gl.attachShader(prog, vs);
+ gl.attachShader(prog, fs);
+ gl.linkProgram(prog);
+
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
+ var str = "Shader program linking failed:";
+ str += "\nShader program info log:\n" + gl.getProgramInfoLog(prog);
+ str += "\n\nVert shader log:\n" + gl.getShaderInfoLog(vs);
+ str += "\n\nFrag shader log:\n" + gl.getShaderInfoLog(fs);
+ warning(str);
+ return null;
+ }
+
+ return prog;
+ }
+
+ return {
+ setErrorFunc: setErrorFunc,
+ setWarningFunc: setWarningFunc,
+
+ getWebGL: getWebGL,
+ withWebGL2: withWebGL2,
+ createShaderById: createShaderById,
+ createProgramByIds: createProgramByIds,
+ };
+})();
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/reftest/wrapper.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<title>Image reftest wrapper</title>
+<style type="text/css">
+ #image1 { background-color: rgb(10, 100, 250); }
+</style>
+<script>
+ // The image is loaded async after the page loads
+ // wait for it to finish loading
+ function onImageLoad() {
+ document.documentElement.removeAttribute("class");
+ };
+</script>
+</head>
+<body>
+<img id="image1">
+<script>
+ // Use as "wrapper.html?image.png"
+ var imgURL = document.location.search.substr(1);
+ document.images[0].onload = onImageLoad;
+ document.images[0].onerror = onImageLoad;
+ document.images[0].src = imgURL;
+</script>
+</body>
+</html>
+
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -368,16 +368,19 @@ include transform-3d/reftest.list
# unicode/ (verify that we don't do expend effort doing unicode-aware case checks)
include unicode/reftest.list
# usercss
include usercss/reftest.list
include view-source/reftest.list
+# vr
+include ../../dom/vr/test/reftest/reftest.list
+
# web-animations
include web-animations/reftest.list
# webcomponents/
include webcomponents/reftest.list
# widget/
include ../../widget/reftests/reftest.list