Commit 3ff944ee authored by bsheedy's avatar bsheedy Committed by Commit Bot

Reland "Add AR instrumentation test support"

This is a reland of a3015a89

Original change's description:
> Add AR instrumentation test support
>
> Adds the necessary changes to build and run
> AR (augmented reality) tests in Monochrome.
>
> Does not actually enable the test APK to be
> built or run anywhere - that will be added
> at a later time once additional work to
> properly run the tests on bots is done, e.g.
> installing the ArCore APK.
>
> Bug: 851020
> Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_layout_tests_slimming_paint_v2;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:linux_vr;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
> Change-Id: I4148fcc6626ef79852f78f6019cc7c6f297f3e37
> Reviewed-on: https://chromium-review.googlesource.com/1101958
> Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
> Reviewed-by: Yaron Friedman <yfriedman@chromium.org>
> Reviewed-by: Brandon Jones <bajones@chromium.org>
> Reviewed-by: agrieve <agrieve@chromium.org>
> Reviewed-by: Michael Thiessen <mthiesse@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#573599}

TBR=yfriedman@chromium.org,mthiesse@chromium.org,bajones@chromium.org,agrieve@chromium.org

Bug: 851020
Change-Id: If8410366848ff25d276cb2473f773c153a32f94f
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_layout_tests_slimming_paint_v2;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:linux_vr;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
Reviewed-on: https://chromium-review.googlesource.com/1131715Reviewed-by: default avatarBrian Sheedy <bsheedy@chromium.org>
Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#573899}
parent 59b4cd57
......@@ -2807,6 +2807,8 @@ if (enable_java_templates) {
# Generate incremental apk related operations at runtime.
public_deps += _incremental_apk_operations
}
} else {
not_needed([ "_incremental_apk_operations" ])
}
}
......@@ -2852,13 +2854,18 @@ if (enable_java_templates) {
template("instrumentation_test_apk") {
assert(defined(invoker.apk_name))
testonly = true
_incremental_allowed =
!defined(invoker.never_incremental) || !invoker.never_incremental
_apk_target_name = "${target_name}__apk"
_test_runner_target_name = "${target_name}__test_runner_script"
_dist_ijar_path =
"$root_build_dir/test.lib.java/" + invoker.apk_name + ".jar"
_incremental_test_runner_target_name =
"${_test_runner_target_name}_incremental"
_incremental_test_name = "${invoker.target_name}_incremental"
if (_incremental_allowed) {
_incremental_test_runner_target_name =
"${_test_runner_target_name}_incremental"
_incremental_test_name = "${invoker.target_name}_incremental"
}
if (incremental_apk_by_default) {
_incremental_test_runner_target_name = _test_runner_target_name
_incremental_test_name = invoker.target_name
......@@ -2883,22 +2890,24 @@ if (enable_java_templates) {
test_jar = _dist_ijar_path
}
}
test_runner_script(_incremental_test_runner_target_name) {
forward_variables_from(invoker,
[
"additional_apks",
"apk_under_test",
"data",
"data_deps",
"deps",
"ignore_all_data_deps",
"public_deps",
])
test_name = _incremental_test_name
test_type = "instrumentation"
apk_target = ":$_apk_target_name"
test_jar = _dist_ijar_path
incremental_install = true
if (_incremental_allowed) {
test_runner_script(_incremental_test_runner_target_name) {
forward_variables_from(invoker,
[
"additional_apks",
"apk_under_test",
"data",
"data_deps",
"deps",
"ignore_all_data_deps",
"public_deps",
])
test_name = _incremental_test_name
test_type = "instrumentation"
apk_target = ":$_apk_target_name"
test_jar = _dist_ijar_path
incremental_install = true
}
}
android_apk(_apk_target_name) {
......@@ -2981,14 +2990,16 @@ if (enable_java_templates) {
}
}
group("${target_name}_incremental") {
public_deps = [
":$_incremental_test_runner_target_name",
":${_apk_target_name}_dist_ijar",
":${_apk_target_name}_incremental",
]
if (defined(invoker.apk_under_test)) {
public_deps += [ "${invoker.apk_under_test}_incremental" ]
if (_incremental_allowed) {
group("${target_name}_incremental") {
public_deps = [
":$_incremental_test_runner_target_name",
":${_apk_target_name}_dist_ijar",
":${_apk_target_name}_incremental",
]
if (defined(invoker.apk_under_test)) {
public_deps += [ "${invoker.apk_under_test}_incremental" ]
}
}
}
}
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.chromium.chrome.tests">
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23" />
<uses-sdk android:minSdkVersion="{{min_sdk_version}}" android:targetSdkVersion="{{target_sdk_version}}" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
......@@ -15,7 +15,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="ChromePublicTest">
android:label="{% block application_name %}ChromePublicTest{% endblock %}">
<uses-library android:name="android.test.runner" />
......
{% extends "chrome/android/javatests/AndroidManifest.xml" %}
## Copyright 2018 The Chromium Authors. All rights reserved.
## Use of this source code is governed by a BSD-style license that can be
## found in the LICENSE file.
## Note: This is a jinja2 template, processed at build time into the final manifest.
##
{% block application_name %}MonochromePublicTest{% endblock %}
\ No newline at end of file
......@@ -178,6 +178,7 @@ public class CastTestRule extends ChromeActivityTestRule<ChromeActivity> {
return waitForStates(states, waitTimeMs);
}
@Override
public EmbeddedTestServer getTestServer() {
return mTestServer;
}
......
......@@ -61,6 +61,7 @@ public class NotificationTestRule extends ChromeActivityTestRule<ChromeTabbedAct
}
/** Returns the test server. */
@Override
public EmbeddedTestServer getTestServer() {
return mTestServer;
}
......
......@@ -209,6 +209,18 @@ public class TestFramework {
}
}
/**
* Helper function to make sure that the JavaScript test harness did not detect any failures.
* Similar to endTest, but does not fail if the test is still detected as running. This is
* useful because not all tests make use of the test harness' test/assert features (particularly
* simple enter/exit tests), but may still want to ensure that no unexpected JavaScript errors
* were encountered.
* @param webContents The Webcontents for the tab to check for failures in.
*/
public static void assertNoJavaScriptErrors(WebContents webContents) {
Assert.assertNotEquals(checkTestStatus(webContents), STATUS_FAILED);
}
/**
* Polls the provided JavaScript boolean until the timeout is reached or
* the boolean is true.
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.vr_shell;
import static org.chromium.chrome.browser.vr_shell.TestFramework.PAGE_LOAD_TIMEOUT_S;
import static org.chromium.chrome.browser.vr_shell.XrTestFramework.POLL_TIMEOUT_LONG_MS;
import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.chromium.base.test.params.ParameterAnnotations.ClassParameter;
import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
import org.chromium.base.test.params.ParameterSet;
import org.chromium.base.test.params.ParameterizedRunner;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.vr_shell.rules.VrActivityRestriction;
import org.chromium.chrome.browser.vr_shell.util.VrTestRuleUtils;
import org.chromium.chrome.browser.vr_shell.util.XrTransitionUtils;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
import org.chromium.net.test.EmbeddedTestServer;
import java.util.List;
import java.util.concurrent.Callable;
/**
* End-to-end tests for testing WebXR for AR's requestSession behavior.
*/
@RunWith(ParameterizedRunner.class)
@UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
@CommandLineFlags.
Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "enable-features=WebXR,WebXRHitTest"})
@MinAndroidSdkLevel(Build.VERSION_CODES.N) // WebXR for AR is only supported on N+
public class WebXrArSessionTest {
@ClassParameter
private static List<ParameterSet> sClassParams =
VrTestRuleUtils.generateDefaultVrTestRuleParameters();
@Rule
public RuleChain mRuleChain;
private ChromeActivityTestRule mTestRule;
private XrTestFramework mXrTestFramework;
private EmbeddedTestServer mServer;
private boolean mShouldCreateServer;
public WebXrArSessionTest(Callable<ChromeActivityTestRule> callable) throws Exception {
mTestRule = callable.call();
mRuleChain = VrTestRuleUtils.wrapRuleInVrActivityRestrictionRule(mTestRule);
}
@Before
public void setUp() throws Exception {
mXrTestFramework = new XrTestFramework(mTestRule);
// WebappActivityTestRule automatically creates a test server, and creating multiple causes
// it to crash hitting a DCHECK. So, only handle the server ourselves if whatever test rule
// we're using doesn't create one itself.
mServer = mTestRule.getTestServer();
if (mServer == null) {
mShouldCreateServer = true;
mServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
}
}
@After
public void tearDown() throws Exception {
if (mServer != null && mShouldCreateServer) {
mServer.stopAndDestroyServer();
}
}
/**
* Tests that a session request for AR succeeds.
*/
@Test
@MediumTest
@VrActivityRestriction({VrActivityRestriction.SupportedActivity.ALL})
public void testArRequestSessionSucceeds() throws InterruptedException {
mXrTestFramework.loadUrlAndAwaitInitialization(
mServer.getURL(XrTestFramework.getEmbeddedServerPathForHtmlTestFile(
"test_ar_request_session_succeeds")),
PAGE_LOAD_TIMEOUT_S);
XrTransitionUtils.enterArSessionOrFail(mXrTestFramework.getFirstTabWebContents());
XrTestFramework.assertNoJavaScriptErrors(mXrTestFramework.getFirstTabWebContents());
}
/**
* Tests that repeatedly starting and stopping AR sessions does not cause any unexpected
* behavior. Regression test for https://crbug.com/837894.
*/
@Test
@MediumTest
@VrActivityRestriction({VrActivityRestriction.SupportedActivity.ALL})
public void testRepeatedArSessionsSucceed() throws InterruptedException {
mXrTestFramework.loadUrlAndAwaitInitialization(
mServer.getURL(XrTestFramework.getEmbeddedServerPathForHtmlTestFile(
"test_ar_request_session_succeeds")),
PAGE_LOAD_TIMEOUT_S);
for (int i = 0; i < 2; i++) {
XrTransitionUtils.enterArSessionOrFail(mXrTestFramework.getFirstTabWebContents());
XrTransitionUtils.endArSession(mXrTestFramework.getFirstTabWebContents());
}
XrTestFramework.assertNoJavaScriptErrors(mXrTestFramework.getFirstTabWebContents());
}
/**
* Tests that repeated calls to requestSession on the same page only prompts the user for
* camera permissions once.
*/
@Test
@MediumTest
@VrActivityRestriction({VrActivityRestriction.SupportedActivity.ALL})
public void testRepeatedArSessionsOnlyPromptPermissionsOnce() throws InterruptedException {
mXrTestFramework.loadUrlAndAwaitInitialization(
mServer.getURL(XrTestFramework.getEmbeddedServerPathForHtmlTestFile(
"test_ar_request_session_succeeds")),
PAGE_LOAD_TIMEOUT_S);
Assert.assertTrue(XrTransitionUtils.arSessionRequestWouldTriggerPermissionPrompt(
mXrTestFramework.getFirstTabWebContents()));
XrTransitionUtils.enterArSessionOrFail(mXrTestFramework.getFirstTabWebContents());
XrTransitionUtils.endArSession(mXrTestFramework.getFirstTabWebContents());
// Manually run through the same steps as enterArSessionOrFail so that we don't trigger
// its automatic permission acceptance.
Assert.assertFalse(XrTransitionUtils.arSessionRequestWouldTriggerPermissionPrompt(
mXrTestFramework.getFirstTabWebContents()));
XrTransitionUtils.enterPresentation(mXrTestFramework.getFirstTabWebContents());
Assert.assertTrue(XrTestFramework.pollJavaScriptBoolean(
"sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS,
mXrTestFramework.getFirstTabWebContents()));
}
}
\ No newline at end of file
......@@ -121,7 +121,6 @@ public class TransitionUtils {
* JavaScript step to finish.
*
* Only meant to be used alongside the test framework from VrTestFramework.
* @param cvc The ContentViewCore for the tab the canvas is in.
* @param webContents The WebContents for the tab the JavaScript step is in.
*/
public static void enterPresentationAndWait(WebContents webContents) {
......
......@@ -5,20 +5,23 @@
package org.chromium.chrome.browser.vr_shell.util;
import static org.chromium.chrome.browser.vr_shell.XrTestFramework.POLL_TIMEOUT_LONG_MS;
import static org.chromium.chrome.browser.vr_shell.XrTestFramework.POLL_TIMEOUT_SHORT_MS;
import android.content.DialogInterface;
import org.junit.Assert;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.browser.permissions.PermissionDialogController;
import org.chromium.chrome.browser.vr_shell.TestVrShellDelegate;
import org.chromium.chrome.browser.vr_shell.XrTestFramework;
import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.WebContents;
/**
* Class containing utility functions for transitioning between different
* states in VR, such as fullscreen, WebVR presentation, and the VR browser.
*
* All the transitions in this class are performed directly through Chrome,
* as opposed to NFC tag simulation which involves receiving an intent from
* an outside application (VR Services).
* states in WebXR, e.g. entering exclusive or AR sessions.
*/
public class XrTransitionUtils extends TransitionUtils {
/**
......@@ -26,9 +29,72 @@ public class XrTransitionUtils extends TransitionUtils {
* the two APIs.
*/
public static void enterPresentationOrFail(WebContents webContents) {
XrTestFramework.runJavaScriptOrFail(
"sessionTypeToRequest = sessionTypes.EXCLUSIVE", POLL_TIMEOUT_LONG_MS, webContents);
enterPresentation(webContents);
Assert.assertTrue(XrTestFramework.pollJavaScriptBoolean(
"exclusiveSession != null", POLL_TIMEOUT_LONG_MS, webContents));
"sessionInfos[sessionTypes.EXCLUSIVE].currentSession != null", POLL_TIMEOUT_LONG_MS,
webContents));
Assert.assertTrue(TestVrShellDelegate.getVrShellForTesting().getWebVrModeEnabled());
}
/**
* Requests that WebXR start an AR session, failing the test if it does not succeed.
* @param webContents The WebContents to run start the AR session in.
*/
public static void enterArSessionOrFail(WebContents webContents) {
XrTestFramework.runJavaScriptOrFail(
"sessionTypeToRequest = sessionTypes.AR", POLL_TIMEOUT_LONG_MS, webContents);
// Requesting an AR session for the first time on a page will always prompt for camera
// permissions, but not on subsequent requests, so check to see if we'll need to accept it
// after requesting the session.
boolean expectPermissionPrompt = arSessionRequestWouldTriggerPermissionPrompt(webContents);
// TODO(bsheedy): Rename enterPresentation since it's used for both presentation and AR?
enterPresentation(webContents);
if (expectPermissionPrompt) {
// Wait for the permission prompt to appear.
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return PermissionDialogController.getInstance().getCurrentDialogForTesting()
!= null;
}
});
// Accept the permission prompt.
ThreadUtils.runOnUiThreadBlocking(() -> {
PermissionDialogController.getInstance()
.getCurrentDialogForTesting()
.getButton(DialogInterface.BUTTON_POSITIVE)
.performClick();
});
}
Assert.assertTrue(XrTestFramework.pollJavaScriptBoolean(
"sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS,
webContents));
}
/**
* Ends the current AR session.
* @param webContents The WebContents to end the AR session in.
*/
public static void endArSession(WebContents webContents) {
XrTestFramework.runJavaScriptOrFail("sessionInfos[sessionTypes.AR].currentSession.end()",
POLL_TIMEOUT_SHORT_MS, webContents);
}
/**
* Checks whether an AR session request would trigger a Camera permission prompt.
* @param webContents The WebContents to check permissions in.
* @return True if an AR session request will cause a permission prompt, false otherwise.
*/
public static boolean arSessionRequestWouldTriggerPermissionPrompt(WebContents webContents) {
XrTestFramework.runJavaScriptOrFail("checkIfArSessionWouldTriggerPermissionPrompt()",
POLL_TIMEOUT_SHORT_MS, webContents);
Assert.assertTrue(XrTestFramework.pollJavaScriptBoolean(
"arSessionRequestWouldTriggerPermissionPrompt !== null", POLL_TIMEOUT_SHORT_MS,
webContents));
return Boolean.valueOf(
XrTestFramework.runJavaScriptOrFail("arSessionRequestWouldTriggerPermissionPrompt",
POLL_TIMEOUT_SHORT_MS, webContents));
}
}
......@@ -80,6 +80,7 @@ public class WebappActivityTestRule extends ChromeActivityTestRule<WebappActivit
super(WebappActivity0.class);
}
@Override
public EmbeddedTestServer getTestServer() {
return mTestServerRule.getServer();
}
......
......@@ -24,19 +24,23 @@ void XrBrowserTestBase::EnterPresentation(content::WebContents* web_contents) {
void XrBrowserTestBase::EnterPresentationOrFail(
content::WebContents* web_contents) {
EnterPresentation(web_contents);
EXPECT_TRUE(PollJavaScriptBoolean("exclusiveSession!= null", kPollTimeoutLong,
web_contents));
EXPECT_TRUE(PollJavaScriptBoolean(
"sessionInfos[sessionTypes.EXCLUSIVE].currentSession != null",
kPollTimeoutLong, web_contents));
}
void XrBrowserTestBase::ExitPresentation(content::WebContents* web_contents) {
EXPECT_TRUE(content::ExecuteScript(web_contents, "exclusiveSession.end()"));
EXPECT_TRUE(content::ExecuteScript(
web_contents,
"sessionInfos[sessionTypes.EXCLUSIVE].currentSession.end()"));
}
void XrBrowserTestBase::ExitPresentationOrFail(
content::WebContents* web_contents) {
ExitPresentation(web_contents);
EXPECT_TRUE(PollJavaScriptBoolean("exclusiveSession == null",
kPollTimeoutLong, web_contents));
EXPECT_TRUE(PollJavaScriptBoolean(
"sessionInfos[sessionTypes.EXCLUSIVE].currentSession == null",
kPollTimeoutLong, web_contents));
}
} // namespace vr
......@@ -56,6 +56,7 @@ import org.chromium.content.browser.test.util.JavaScriptUtils;
import org.chromium.content.browser.test.util.RenderProcessLimit;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.ui.base.PageTransition;
import java.lang.reflect.AnnotatedElement;
......@@ -621,6 +622,13 @@ public class ChromeActivityTestRule<T extends ChromeActivity> extends ActivityTe
return mCurrentTestName;
}
/**
* Gets the ChromeActivityTestRule's EmbeddedTestServer instance if it has one.
*/
public EmbeddedTestServer getTestServer() {
return null;
}
/**
* @return {@link WebContents} of the active tab of the activity.
*/
......
<!doctype html>
<!--
Tests that AR requestSessions succeed.
-->
<html>
<head>
<link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
</head>
<body>
<canvas id="webgl-canvas"></canvas>
<script src="../../../../../../third_party/WebKit/LayoutTests/resources/testharness.js"></script>
<script src="../resources/webxr_e2e.js"></script>
<script>var shouldAutoCreateNonExclusiveSession = false;</script>
<script src="../resources/webxr_boilerplate.js"></script>
</body>
</html>
......@@ -21,7 +21,7 @@ is active, but resumes afterwards.
// Verify that we call a rAF once, then make sure any subsequent calls
// are not done while there is an exclusive session.
onMagicWindowXRFrameCallback = function() {
if (exclusiveSession !== null) {
if (sessionInfos[sessionTypes.EXCLUSIVE].currentSession !== null) {
t.step( () => {
assert_unreached(
"Non-exclusive rAF called during exclusive session");
......
......@@ -62,9 +62,10 @@ that Daydream controller input is registered when using Daydream View.
function stepSetupListeners(numIterations) {
iterations = numIterations;
exclusiveSession.addEventListener('selectstart', onSelectStart, false);
exclusiveSession.addEventListener('selectend', onSelectEnd, false);
exclusiveSession.addEventListener('select', onSelect, false);
let currentSession = sessionInfos[sessionTypes.EXCLUSIVE].currentSession;
currentSession.addEventListener('selectstart', onSelectStart, false);
currentSession.addEventListener('selectend', onSelectEnd, false);
currentSession.addEventListener('select', onSelect, false);
}
</script>
</body>
......
......@@ -18,6 +18,9 @@ test can query for whether each submitted frame used the correct pose.
var t = async_test("Pose data is correct");
var frame_id = 0;
var frame_data_array = {};
// We exit presentation before checking stuff that needs the frame of
// reference, so we need to cache its value.
var cached_frame_of_ref = null;
function FloatCompare(a, b) {
return Math.abs(a-b) < 0.001;
......@@ -40,13 +43,13 @@ test can query for whether each submitted frame used the correct pose.
function checkFrameView(frame_id, eye, expected) {
let frame_data = frame_data_array[frame_id];
let pose = frame_data.getDevicePose(exclusiveFrameOfRef);
let pose = frame_data.getDevicePose(cached_frame_of_ref);
return MatrixCompare(pose.getViewMatrix(frame_data_array[frame_id].views[eye]), expected);
}
function checkFramePose(frame_id, expected) {
let frame_data = frame_data_array[frame_id];
let pose = frame_data.getDevicePose(exclusiveFrameOfRef);
let pose = frame_data.getDevicePose(cached_frame_of_ref);
if (!pose) {
// We can intermittently get null poses. For now treat them as passing,
// even though this should be fixed.
......@@ -63,6 +66,7 @@ test can query for whether each submitted frame used the correct pose.
// Encode an index into the clear color.
frame_id++;
frame_data_array[frame_id] = frame;
cached_frame_of_ref = sessionInfos[sessionTypes.EXCLUSIVE].currentFrameOfRef;
var encoded_frame_id = {};
encoded_frame_id.r = frame_id % 256;
......
......@@ -27,7 +27,7 @@ Tests that WebXR doesn't update frame data when the tab is not focused
return;
}
onMagicWindowXRFrameCallback = null;
pose = frame.getDevicePose(magicWindowFrameOfRef);
pose = frame.getDevicePose(sessionInfos[sessionTypes.MAGIC_WINDOW].currentFrameOfRef);
t.step( () => {
assert_true(pose != null,
"getDevicePose returned a non-null object");
......
......@@ -22,17 +22,81 @@ var onMagicWindowXRFrameCallback = null;
var onExclusiveXRFrameCallback = null;
var shouldSubmitFrame = true;
var hasPresentedFrame = false;
var arSessionRequestWouldTriggerPermissionPrompt = null;
var magicWindowSession = null;
var magicWindowFrameOfRef = null;
var exclusiveSession = null;
var exclusiveFrameOfRef = null;
var sessionTypes = Object.freeze({
EXCLUSIVE: 1,
AR: 2,
MAGIC_WINDOW: 3
});
var sessionTypeToRequest = sessionTypes.EXCLUSIVE;
class SessionInfo {
constructor() {
this.session = null;
this.frameOfRef = null;
}
get currentSession() {
return this.session;
}
get currentFrameOfRef() {
return this.frameOfRef;
}
set currentSession(session) {
this.session = session;
}
set currentFrameOfRef(frameOfRef) {
this.frameOfRef = frameOfRef;
}
clearSession() {
this.session = null;
this.frameOfRef = null;
}
}
var sessionInfos = {}
sessionInfos[sessionTypes.EXCLUSIVE] = new SessionInfo();
sessionInfos[sessionTypes.AR] = new SessionInfo();
sessionInfos[sessionTypes.MAGIC_WINDOW] = new SessionInfo();
function getSessionType(session) {
if (session.exclusive) {
return sessionTypes.EXCLUSIVE;
} else if (sessionInfos[sessionTypes.AR].currentSession == session) {
// TODO(bsheedy): Replace this check if there's ever something like
// session.ar for checking if the session is AR-capable.
return sessionTypes.AR;
} else {
return sessionTypes.MAGIC_WINDOW;
}
}
function onRequestSession() {
xrDevice.requestSession({exclusive: true}).then( (session) => {
exclusiveSession = session;
onSessionStarted(session);
});
switch (sessionTypeToRequest) {
case sessionTypes.EXCLUSIVE:
xrDevice.requestSession({exclusive: true}).then( (session) => {
sessionInfos[sessionTypes.EXCLUSIVE].currentSession = session;
onSessionStarted(session);
});
break;
case sessionTypes.AR:
let sessionOptions = {
requestAR: true,
outputContext: webglCanvas.getContext('xrpresent'),
};
xrDevice.requestSession(sessionOptions).then((session) => {
sessionInfos[sessionTypes.AR].currentSession = session;
onSessionStarted(session);
});
break;
default:
throw 'Given unsupported WebXR session type enum ' + sessionTypeToRequest;
}
}
function onSessionStarted(session) {
......@@ -45,7 +109,7 @@ function onSessionStarted(session) {
let offscreenCanvas = document.createElement('canvas');
gl = offscreenCanvas.getContext('webgl', glAttribs);
if (!gl) {
console.error('Failed to get WebGL context');
throw 'Failed to get WebGL context';
}
gl.clearColor(0.0, 1.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
......@@ -55,28 +119,21 @@ function onSessionStarted(session) {
session.baseLayer = new XRWebGLLayer(session, gl);
session.requestFrameOfReference('eye-level').then( (frameOfRef) => {
if (session.exclusive) {
exclusiveFrameOfRef = frameOfRef;
} else {
magicWindowFrameOfRef = frameOfRef;
}
sessionInfos[getSessionType(session)].currentFrameOfRef = frameOfRef;
session.requestAnimationFrame(onXRFrame);
});
}
function onSessionEnded(event) {
if (event.session.exclusive)
exclusiveSession = null
else
magicWindowSession = null;
sessionInfos[getSessionType(event.session)].clearSession();
}
function onXRFrame(t, frame) {
let session = frame.session;
session.requestAnimationFrame(onXRFrame);
let frameOfRef = session.exclusive ?
exclusiveFrameOfRef :
magicWindowFrameOfRef;
let frameOfRef = null;
frameOfRef = sessionInfos[getSessionType(session)].currentFrameOfRef;
let pose = frame.getDevicePose(frameOfRef);
// Exiting the rAF callback without dirtying the GL context is interpreted
......@@ -87,22 +144,40 @@ function onXRFrame(t, frame) {
gl.bindFramebuffer(gl.FRAMEBUFFER, session.baseLayer.framebuffer);
// If in an exclusive session, set canvas to blue. Otherwise, red.
if (session.exclusive) {
gl.clearColor(0.0, 0.0, 1.0, 1.0);
if (onExclusiveXRFrameCallback) {
onExclusiveXRFrameCallback(session, frame, gl);
}
} else {
if (onMagicWindowXRFrameCallback) {
onMagicWindowXRFrameCallback(session, frame);
}
gl.clearColor(1.0, 0.0, 0.0, 1.0);
// If in an exclusive session, set canvas to blue.
// If in an AR session, just draw the camera.
// Otherwise, red.
switch (getSessionType(session)) {
case sessionTypes.EXCLUSIVE:
gl.clearColor(0.0, 0.0, 1.0, 1.0);
if (onExclusiveXRFrameCallback) {
onExclusiveXRFrameCallback(session, frame, gl);
}
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
break;
case sessionTypes.AR:
// Do nothing for now
break;
default:
if (onMagicWindowXRFrameCallback) {
onMagicWindowXRFrameCallback(session, frame);
}
gl.clearColor(1.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
hasPresentedFrame = true;
}
function checkIfArSessionWouldTriggerPermissionPrompt() {
arSessionRequestWouldTriggerPermissionPrompt = null;
navigator.permissions.query({name: 'camera'}).then( (permission) => {
arSessionRequestWouldTriggerPermissionPrompt = permission.state == 'prompt';
}, () => {
throw 'Permission query rejected';
});
}
// Try to get an XRDevice and set up a non-exclusive session with it
if (navigator.xr) {
navigator.xr.requestDevice().then( (device) => {
......@@ -113,11 +188,22 @@ if (navigator.xr) {
// Set up the device to have a non-exclusive session (magic window) drawing
// into the full screen canvas on the page
let ctx = webglCanvas.getContext('xrpresent');
device.requestSession({outputContext: ctx}).then( (session) => {
onSessionStarted(session);
}).then( () => {
// WebXR for VR tests want a non-exclusive session to be automatically
// created on page load to reduce the amount of boilerplate code necessary.
// However, doing so during AR tests currently fails due to AR sessions
// always requiring a user gesture. So, allow a page to set a variable
// before loading this JavaScript file if they wish to skip the automatic
// non-exclusive session creation.
if (typeof shouldAutoCreateNonExclusiveSession === 'undefined'
|| shouldAutoCreateNonExclusiveSession === true) {
device.requestSession({outputContext: ctx}).then( (session) => {
onSessionStarted(session);
}).then( () => {
initializationSteps['magicWindowStarted'] = true;
});
} else {
initializationSteps['magicWindowStarted'] = true;
});
}
}).then( () => {
initializationSteps['getXRDevice'] = true;
});
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment