Bug 1229481 - Part 2: Add draw rect reftest for WebVR; r?kip draft
authorDaosheng Mu <daoshengmu@gmail.com>
Fri, 05 May 2017 19:00:25 +0800
changeset 588181 dd7692cc4605fcaa1fdda75e284315ea83d2a5b4
parent 586414 ca4c0c31d37dcf4cfe5d4f2c5886d07598ba4995
child 631498 b72d5b90cb830c621d23732180cf21a5a76f5e0b
push id61952
push userbmo:dmu@mozilla.com
push dateFri, 02 Jun 2017 10:24:39 +0000
reviewerskip
bugs1229481
milestone55.0a1
Bug 1229481 - Part 2: Add draw rect reftest for WebVR; r?kip MozReview-Commit-ID: CnSYFFZS8lP
dom/vr/moz.build
dom/vr/test/reftest/VRSimulationDriver.js
dom/vr/test/reftest/draw_rect.html
dom/vr/test/reftest/draw_rect.png
dom/vr/test/reftest/reftest.list
dom/vr/test/reftest/webgl-util.js
dom/vr/test/reftest/wrapper.html
layout/reftests/reftest.list
--- 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