diff --git a/BUILD.gn b/BUILD.gn index 6cc7dae6300115..9f1e2c126cb64e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -757,15 +757,13 @@ group("gn_all") { "//chrome/browser/vr:vr_common_perftests", "//chrome/browser/vr:vr_common_unittests", "//chrome/browser/vr:vr_pixeltests", + "//tools/perf/contrib/vr_benchmarks:vr_perf_tests", ] if (is_desktop_linux && use_ozone) { deps += [ "//chrome/browser/vr/testapp:vr_testapp" ] } if (is_android) { - deps += [ - "//chrome/browser/android/vr:vr_android_unittests", - "//tools/perf/contrib/vr_benchmarks:vr_perf_tests", - ] + deps += [ "//chrome/browser/android/vr:vr_android_unittests" ] } } diff --git a/tools/perf/contrib/vr_benchmarks/BUILD.gn b/tools/perf/contrib/vr_benchmarks/BUILD.gn index 7996169bc9144c..f5cc7c7a96488e 100644 --- a/tools/perf/contrib/vr_benchmarks/BUILD.gn +++ b/tools/perf/contrib/vr_benchmarks/BUILD.gn @@ -4,14 +4,13 @@ import("//chrome/browser/vr/features.gni") -assert(is_android) - group("vr_perf_tests") { testonly = true data = [ "./data/", + "./desktop_runtimes/", "./__init__.py", - "./shared_android_vr_page_state.py", + "./shared_vr_page_state.py", "./vr_benchmarks.py", "./vr_browsing_mode_pages.py", "./vr_sample_page.py", @@ -19,26 +18,33 @@ group("vr_perf_tests") { "./webvr_sample_pages.py", "./webvr_wpr_pages.py", "./webxr_sample_pages.py", - "//chrome/android/shared_preference_files/test/", - "//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", - "//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "//chrome/test/data/xr/webvr_info/samples/", "//third_party/webxr_test_pages/webxr-samples", ] data_deps = [ - "//chrome/android:vr_nfc_simulator_apk", "//testing:run_perf_test", ] - # We'll only ever use the assets if it's a Chrome-branded build. We don't have - # a way of checking whether the files are actually present to copy, but the - # script will deal with that. - if (use_vr_assets_component) { - data_deps += [ ":generate_vr_assets_profile" ] - } deps = [ "//tools/perf:perf", ] + + if (is_android) { + data += [ + "//chrome/android/shared_preference_files/test/", + "//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", + "//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", + ] + + data_deps += [ "//chrome/android:vr_nfc_simulator_apk" ] + + # We'll only ever use the assets if it's a Chrome-branded build. We don't have + # a way of checking whether the files are actually present to copy, but the + # script will deal with that. + if (use_vr_assets_component) { + data_deps += [ ":generate_vr_assets_profile" ] + } + } } # Copies files to the gen/ directory and creates a manifest so that the VR diff --git a/tools/perf/contrib/vr_benchmarks/desktop_runtimes/__init__.py b/tools/perf/contrib/vr_benchmarks/desktop_runtimes/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/tools/perf/contrib/vr_benchmarks/desktop_runtimes/base_runtime.py b/tools/perf/contrib/vr_benchmarks/desktop_runtimes/base_runtime.py new file mode 100644 index 00000000000000..a4db125973b7d0 --- /dev/null +++ b/tools/perf/contrib/vr_benchmarks/desktop_runtimes/base_runtime.py @@ -0,0 +1,39 @@ +# Copyright 2019 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. + +class DesktopRuntimeBase(object): + """Interface for all desktop VR runtimes.""" + + def __init__(self, finder_options): + self._finder_options = finder_options + + def Setup(self): + """Called once before any stories are run.""" + self._finder_options.browser_options.AppendExtraBrowserArgs( + '--enable-features=%s' % self.GetFeatureName()) + self._SetupInternal() + + def _SetupInternal(self): + raise NotImplementedError( + 'No runtime setup defined for %s' % self.__class__.__name__) + + def WillRunStory(self): + """Called before each story is run.""" + self._WillRunStoryInternal() + + def _WillRunStoryInternal(self): + raise NotImplementedError( + 'No runtime pre-story defined for %s' % self.__class__.__name__) + + def TearDown(self): + """Called once after all stories are run.""" + self._TearDownInternal() + + def _TearDownInternal(self): + raise NotImplementedError( + 'No runtime tear down defined for %s' % self.__class__.__name__) + + def GetFeatureName(self): + raise NotImplementedError( + 'No feature defined for %s' % self.__class__.__name__) diff --git a/tools/perf/contrib/vr_benchmarks/desktop_runtimes/oculus_runtimes.py b/tools/perf/contrib/vr_benchmarks/desktop_runtimes/oculus_runtimes.py new file mode 100644 index 00000000000000..d4ae033b633df1 --- /dev/null +++ b/tools/perf/contrib/vr_benchmarks/desktop_runtimes/oculus_runtimes.py @@ -0,0 +1,64 @@ +# Copyright 2019 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. + +import logging +import os +import subprocess +from contrib.vr_benchmarks.desktop_runtimes import base_runtime + + +# pylint: disable=abstract-method +class _BaseOculusRuntime(base_runtime.DesktopRuntimeBase): + """Base class for all Oculus runtimes.""" + + def __init__(self, *args, **kwargs): + super(_BaseOculusRuntime, self).__init__(*args, **kwargs) + + def GetFeatureName(self): + return 'OculusVR' + + +class OculusRuntimeReal(_BaseOculusRuntime): + """Class for using the real Oculus runtime for desktop tests.""" + + OCULUS_BASE_ENVIRONMENT_VARIABLE = 'OculusBase' + + def __init__(self, *args, **kwargs): + super(OculusRuntimeReal, self).__init__(*args, **kwargs) + self._runtime_handle = None + + def _SetupInternal(self): + # We need to launch the Oculus client before running any tests to ensure + # that the runtime is ready when we try to enter VR. + self._runtime_handle = subprocess.Popen([self._GetOculusClientPath()]) + + def _WillRunStoryInternal(self): + if not self._runtime_handle: + raise RuntimeError( + 'Somehow called real Oculus pre-story without calling setup') + if self._runtime_handle.poll() != None: + logging.warning( + 'Oculus client closed prematurely with code %d, restarting', + self._runtime_handle.returncode) + self._runtime_handle = subprocess.Popen([self._GetOculusClientPath()]) + + def _TearDownInternal(self): + if not self._runtime_handle: + raise RuntimeError( + 'Somehow called real Oculus tear down without calling setup') + if self._runtime_handle.poll() is None: + self._runtime_handle.terminate() + + def _GetOculusClientPath(self): + # The install location of the Oculus runtime is set in the OculusBase + # environment variable at install time. + if self.OCULUS_BASE_ENVIRONMENT_VARIABLE not in os.environ: + raise RuntimeError('Failed to find the Oculus install location. Are you ' + 'sure it\'s installed?') + return os.path.join(os.environ[self.OCULUS_BASE_ENVIRONMENT_VARIABLE], + 'Support', 'oculus-client', 'OculusClient.exe') + + +class OculusRuntimeMock(base_runtime.DesktopRuntimeBase): + """Class for using a mock Oculus runtime for desktop tests.""" diff --git a/tools/perf/contrib/vr_benchmarks/desktop_runtimes/openvr_runtimes.py b/tools/perf/contrib/vr_benchmarks/desktop_runtimes/openvr_runtimes.py new file mode 100644 index 00000000000000..340cbea5b2f7da --- /dev/null +++ b/tools/perf/contrib/vr_benchmarks/desktop_runtimes/openvr_runtimes.py @@ -0,0 +1,21 @@ +# Copyright 2019 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. + +from contrib.vr_benchmarks.desktop_runtimes import base_runtime + + +# pylint: disable=abstract-method +class _OpenVRRuntimeBase(base_runtime.DesktopRuntimeBase): + """Base class for all OpenVR runtimes.""" + + def GetFeatureName(self): + return 'OpenVR' + + +class OpenVRRuntimeReal(_OpenVRRuntimeBase): + """Class for using the real OpenVR runtime for desktop tests.""" + + +class OpenVRRuntimeMock(_OpenVRRuntimeBase): + """Class for using the mock OpenVR runtime for desktop tests.""" diff --git a/tools/perf/contrib/vr_benchmarks/desktop_runtimes/wmr_runtimes.py b/tools/perf/contrib/vr_benchmarks/desktop_runtimes/wmr_runtimes.py new file mode 100644 index 00000000000000..f36dfd168d4b9b --- /dev/null +++ b/tools/perf/contrib/vr_benchmarks/desktop_runtimes/wmr_runtimes.py @@ -0,0 +1,23 @@ +# Copyright 2019 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. + +from contrib.vr_benchmarks.desktop_runtimes import base_runtime + + +# pylint: disable=abstract-method +class _WMRRuntimeBase(base_runtime.DesktopRuntimeBase): + """Base class for all WMR runtimes.""" + + def GetFeatureName(self): + return 'WindowsMixedReality' + + +class WMRRuntimeReal(_WMRRuntimeBase): + """Class for using the real Windows Mixed Reality runtime for desktop tests. + """ + + +class WMRRuntimeMock(_WMRRuntimeBase): + """Class for using the mock Windows Mixed Reality runtime for desktop tests. + """ diff --git a/tools/perf/contrib/vr_benchmarks/shared_android_vr_page_state.py b/tools/perf/contrib/vr_benchmarks/shared_android_vr_page_state.py deleted file mode 100644 index fce84e742c5ba1..00000000000000 --- a/tools/perf/contrib/vr_benchmarks/shared_android_vr_page_state.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright 2017 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. - -import os -from core import path_util -from devil.android.sdk import intent # pylint: disable=import-error -path_util.AddAndroidPylibToPath() -from pylib.utils import shared_preference_utils -from telemetry.core import android_platform -from telemetry.core import platform -from telemetry.core import util -from telemetry.internal.platform import android_device -from telemetry.page import shared_page_state - - -CARDBOARD_PATH = os.path.join('chrome', 'android', 'shared_preference_files', - 'test', 'vr_cardboard_skipdon_setupcomplete.json') -FAKE_TRACKER_COMPONENT = ('com.google.vr.vrcore/' - '.tracking.HeadTrackingService') -SUPPORTED_POSE_TRACKER_MODES = [ - 'frozen', # Static pose looking straight forward. - 'sweep', # Moves head back and forth horizontally. - 'rotate', # Moves head continuously in a circle. - 'circle_strafe', # Moves head continuously in a circle (also changes - # position if 6DoF supported?). - 'motion_sickness', # Moves head in a sort of figure-eight pattern. -] -SUPPORTED_POSE_TRACKER_TYPES = [ - 'sensor', # Standard sensor-fusion-based pose tracker. - 'tango', # Tango-based pose tracker. - 'platform', # ? - 'fake', # Fake pose tracker that can provide pre-defined pose sets. -] - - -class SharedAndroidVrPageState(shared_page_state.SharedPageState): - """SharedPageState for VR Telemetry tests. - - Performs the same functionality as SharedPageState, but with three main - differences: - 1. It is currently restricted to Android - 2. It performs VR-specific setup such as installing and configuring - additional APKs that are necessary for testing - 3. It cycles the screen off then on before each story, similar to how - AndroidScreenRestorationSharedState ensures that the screen is on. See - _CycleScreen() for an explanation on the reasoning behind this. - """ - def __init__(self, test, finder_options, story_set, possible_browser=None): - # TODO(bsheedy): See about making this a cross-platform SharedVrPageState - - # Seems like we should be able to use SharedPageState's default platform - # property instead of specifying AndroidPlatform, and then just perform - # different setup based off the platform type - device = android_device.GetDevice(finder_options) - assert device, 'Android device is required for this story' - self._platform = platform.GetPlatformForDevice(device, finder_options) - assert self._platform, 'Unable to create Android platform' - assert isinstance(self._platform, android_platform.AndroidPlatform) - - super(SharedAndroidVrPageState, self).__init__( - test, finder_options, story_set, possible_browser) - self._story_set = story_set - # Optimization so we're not doing redundant service starts before every - # story. - self._did_set_tracker = False - self._PerformAndroidVrSetup() - - def _PerformAndroidVrSetup(self): - if not self._finder_options.disable_vrcore_install: - self._InstallVrCore() - self._ConfigureVrCore(os.path.join(path_util.GetChromiumSrcDir(), - self._finder_options.shared_prefs_file)) - self._InstallNfcApk() - if not self._finder_options.disable_keyboard_install: - self._InstallKeyboardApk() - - def _InstallVrCore(self): - """Installs the VrCore APK.""" - # TODO(bsheedy): Add support for temporarily replacing it if it's still - # installed as a system app on the test device - self._platform.InstallApplication( - os.path.join(path_util.GetChromiumSrcDir(), 'third_party', - 'gvr-android-sdk', 'test-apks', 'vr_services', - 'vr_services_current.apk')) - - def _ConfigureVrCore(self, filepath): - """Configures VrCore using the provided settings file.""" - settings = shared_preference_utils.ExtractSettingsFromJson(filepath) - for setting in settings: - shared_pref = self._platform.GetSharedPrefs( - setting['package'], setting['filename'], - use_encrypted_path=setting.get('supports_encrypted_path', False)) - shared_preference_utils.ApplySharedPreferenceSetting( - shared_pref, setting) - - def _InstallNfcApk(self): - """Installs the APK that allows VR tests to simulate a headset NFC scan.""" - chromium_root = path_util.GetChromiumSrcDir() - # Find the most recently build APK - candidate_apks = [] - for build_path in util.GetBuildDirectories(chromium_root): - apk_path = os.path.join(build_path, 'apks', 'VrNfcSimulator.apk') - if os.path.exists(apk_path): - last_changed = os.path.getmtime(apk_path) - candidate_apks.append((last_changed, apk_path)) - - if not candidate_apks: - raise RuntimeError( - 'Could not find VrNfcSimulator.apk in a build output directory') - newest_apk_path = sorted(candidate_apks)[-1][1] - self._platform.InstallApplication( - os.path.join(chromium_root, newest_apk_path)) - - def _InstallKeyboardApk(self): - """Installs the VR Keyboard APK.""" - self._platform.InstallApplication( - os.path.join(path_util.GetChromiumSrcDir(), 'third_party', - 'gvr-android-sdk', 'test-apks', 'vr_keyboard', - 'vr_keyboard_current.apk')) - - def _SetFakePoseTrackerIfNotSet(self): - if self._story_set.use_fake_pose_tracker and not self._did_set_tracker: - self.SetPoseTrackerType('fake') - self.SetPoseTrackerMode('sweep') - self._did_set_tracker = True - - def SetPoseTrackerType(self, tracker_type): - """Sets the VrCore pose tracker to the given type. - - Only works if VrCore has been configured to use the VrCore-side tracker - by setting EnableVrCoreHeadTracking to true. This setting persists between - VR sessions and Chrome restarts. - - Args: - tracker_type: A string corresponding to the tracker type to set. - - Raises: - RuntimeError if the given |tracker_type| is not in the supported list. - """ - if tracker_type not in SUPPORTED_POSE_TRACKER_TYPES: - raise RuntimeError('Given tracker %s is not supported.' % tracker_type) - self.platform.StartAndroidService(start_intent=intent.Intent( - action='com.google.vr.vrcore.SET_TRACKER_TYPE', - component=FAKE_TRACKER_COMPONENT, - extras={'com.google.vr.vrcore.TRACKER_TYPE': tracker_type})) - - def SetPoseTrackerMode(self, tracker_mode): - """Sets the fake VrCore pose tracker to provide poses in the given mode. - - Only works after SetPoseTrackerType has been set to 'fake'. This setting - persists between VR sessions and Chrome restarts. - - Args: - tracker_mode: A string corresponding to the tracker mode to set. - - Raises: - RuntimeError if the given |tracker_mode| is not in the supported list. - """ - if tracker_mode not in SUPPORTED_POSE_TRACKER_MODES: - raise RuntimeError('Given mode %s is not supported.' % tracker_mode) - self.platform.StartAndroidService(start_intent=intent.Intent( - action='com.google.vr.vrcore.SET_FAKE_TRACKER_MODE', - component=FAKE_TRACKER_COMPONENT, - extras={'com.google.vr.vrcore.FAKE_TRACKER_MODE': tracker_mode})) - - def WillRunStory(self, page): - super(SharedAndroidVrPageState, self).WillRunStory(page) - if not self._finder_options.disable_screen_reset: - self._CycleScreen() - self._SetFakePoseTrackerIfNotSet() - - def TearDownState(self): - super(SharedAndroidVrPageState, self).TearDownState() - # Reset the tracker type to use the actual sensor if it's been changed. When - # run on the bots, this shouldn't matter since the service will be killed - # during the automatic restart, but this could persist when run locally. - if self._did_set_tracker: - self.SetPoseTrackerType('sensor') - # Re-apply Cardboard as the viewer to leave the device in a consistent - # state after a benchmark run - # TODO(bsheedy): Remove this after crbug.com/772969 is fixed - self._ConfigureVrCore(os.path.join(path_util.GetChromiumSrcDir(), - CARDBOARD_PATH)) - - def _CycleScreen(self): - """Cycles the screen off then on. - - This is because VR test devices are set to have normal screen brightness and - automatically turn off after several minutes instead of the usual approach - of having the screen always on at minimum brightness. This is due to the - motion-to-photon latency test being sensitive to screen brightness, and min - brightness does not work well for it. - - Simply using TurnScreenOn does not actually reset the timer for turning off - the screen, so instead cycle the screen to refresh it periodically. - """ - self.platform.android_action_runner.TurnScreenOff() - self.platform.android_action_runner.TurnScreenOn() - - @property - def platform(self): - return self._platform - - @property - def recording_wpr(self): - return self._finder_options.recording_wpr diff --git a/tools/perf/contrib/vr_benchmarks/shared_vr_page_state.py b/tools/perf/contrib/vr_benchmarks/shared_vr_page_state.py new file mode 100644 index 00000000000000..720a9221161421 --- /dev/null +++ b/tools/perf/contrib/vr_benchmarks/shared_vr_page_state.py @@ -0,0 +1,210 @@ +# Copyright 2017 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. + +import os +from core import path_util +path_util.AddAndroidPylibToPath() +from pylib.utils import shared_preference_utils +from telemetry.core import android_platform +from telemetry.core import util +from telemetry.page import shared_page_state +from contrib.vr_benchmarks.desktop_runtimes import oculus_runtimes +from contrib.vr_benchmarks.desktop_runtimes import openvr_runtimes +from contrib.vr_benchmarks.desktop_runtimes import wmr_runtimes + + +CARDBOARD_PATH = os.path.join('chrome', 'android', 'shared_preference_files', + 'test', 'vr_cardboard_skipdon_setupcomplete.json') + + +class SharedVrPageStateFactory(shared_page_state.SharedPageState): + """"Factory" for picking the correct SharedVrPageState subclass. + + This is a hacky way to automatically change the shared page state that's used + depending on which platform the benchmark is being run on. The + shared_page_state_class that gets passed to the Page constructor must be an + instance of SharedState, so we can't just pass a function pointer that returns + an instance of the correct subclass when called. + + Additionally, we only really know what platform we're being run on after + SharedPageState's constructor is called, as we can't rely on the given + possible_browser to determine it. + + So, we have to call SharedPageState's constructor, find out which platform + we're being run on, switch our class out for the correct one, and + re-construct ourselves. + """ + def __init__(self, test, finder_options, story_set, possible_browser=None): + super(SharedVrPageStateFactory, self).__init__( + test, finder_options, story_set, possible_browser) + + if isinstance(self.platform, android_platform.AndroidPlatform): + self.__class__ = AndroidSharedVrPageState + elif self.platform.GetOSName().lower() == 'win': + self.__class__ = WindowsSharedVrPageState + else: + raise NotImplementedError( + 'No VR SharedPageState implemented for platform %s' % + self.platform.GetOSName()) + # Use self._possible_browser to avoid duplicate computation if + # possible_browser is None. + self.__init__(test, finder_options, story_set, self._possible_browser) + + +class _SharedVrPageState(shared_page_state.SharedPageState): + """Abstract, platform-independent SharedPageState for VR tests. + + Must be subclassed for each platform, since VR setup and tear down differs + between each. + """ + def __init__(self, test, finder_options, story_set, possible_browser=None): + super(_SharedVrPageState, self).__init__( + test, finder_options, story_set, possible_browser) + self._story_set = story_set + + @property + def recording_wpr(self): + return self._finder_options.recording_wpr + + +class AndroidSharedVrPageState(_SharedVrPageState): + """Android-specific VR SharedPageState. + + Platform-specific functionality: + 1. Performs Android VR-specific setup such as installing and configuring + additional APKs that are necessary for testing. + 2. Cycles the screen off then on before each story, similar to how + AndroidScreenRestorationSharedState ensures that the screen is on. See + _CycleScreen() for an explanation on the reasoning behind this. + """ + def __init__(self, test, finder_options, story_set, possible_browser=None): + super(AndroidSharedVrPageState, self).__init__( + test, finder_options, story_set, possible_browser) + if not self._finder_options.disable_vrcore_install: + self._InstallVrCore() + self._ConfigureVrCore(os.path.join(path_util.GetChromiumSrcDir(), + self._finder_options.shared_prefs_file)) + self._InstallNfcApk() + if not self._finder_options.disable_keyboard_install: + self._InstallKeyboardApk() + + def _InstallVrCore(self): + """Installs the VrCore APK.""" + self.platform.InstallApplication( + os.path.join(path_util.GetChromiumSrcDir(), 'third_party', + 'gvr-android-sdk', 'test-apks', 'vr_services', + 'vr_services_current.apk')) + + def _ConfigureVrCore(self, filepath): + """Configures VrCore using the provided settings file.""" + settings = shared_preference_utils.ExtractSettingsFromJson(filepath) + for setting in settings: + shared_pref = self.platform.GetSharedPrefs( + setting['package'], setting['filename'], + use_encrypted_path=setting.get('supports_encrypted_path', False)) + shared_preference_utils.ApplySharedPreferenceSetting( + shared_pref, setting) + + def _InstallNfcApk(self): + """Installs the APK that allows VR tests to simulate a headset NFC scan.""" + chromium_root = path_util.GetChromiumSrcDir() + # Find the most recently build APK + candidate_apks = [] + for build_path in util.GetBuildDirectories(chromium_root): + apk_path = os.path.join(build_path, 'apks', 'VrNfcSimulator.apk') + if os.path.exists(apk_path): + last_changed = os.path.getmtime(apk_path) + candidate_apks.append((last_changed, apk_path)) + + if not candidate_apks: + raise RuntimeError( + 'Could not find VrNfcSimulator.apk in a build output directory') + newest_apk_path = sorted(candidate_apks)[-1][1] + self.platform.InstallApplication( + os.path.join(chromium_root, newest_apk_path)) + + def _InstallKeyboardApk(self): + """Installs the VR Keyboard APK.""" + self.platform.InstallApplication( + os.path.join(path_util.GetChromiumSrcDir(), 'third_party', + 'gvr-android-sdk', 'test-apks', 'vr_keyboard', + 'vr_keyboard_current.apk')) + + def WillRunStory(self, page): + super(AndroidSharedVrPageState, self).WillRunStory(page) + if not self._finder_options.disable_screen_reset: + self._CycleScreen() + + def TearDownState(self): + super(AndroidSharedVrPageState, self).TearDownState() + # Re-apply Cardboard as the viewer to leave the device in a consistent + # state after a benchmark run + # TODO(bsheedy): Remove this after crbug.com/772969 is fixed + self._ConfigureVrCore(os.path.join(path_util.GetChromiumSrcDir(), + CARDBOARD_PATH)) + + def _CycleScreen(self): + """Cycles the screen off then on. + + This is because VR test devices are set to have normal screen brightness and + automatically turn off after several minutes instead of the usual approach + of having the screen always on at minimum brightness. This is due to the + motion-to-photon latency test being sensitive to screen brightness, and min + brightness does not work well for it. + + Simply using TurnScreenOn does not actually reset the timer for turning off + the screen, so instead cycle the screen to refresh it periodically. + """ + self.platform.android_action_runner.TurnScreenOff() + self.platform.android_action_runner.TurnScreenOn() + + +class WindowsSharedVrPageState(_SharedVrPageState): + """Windows-specific VR SharedPageState. + + Platform-specific functionality involves starting and stopping different + VR runtimes before and after all stories are run. + """ + + # Constants to make the below map more readable + MOCK_RUNTIME = False + REAL_RUNTIME = True + # Map of runtime names to runtime classes for both real and mock + # implementations. Real runtimes require specialized hardware and software + # to be installed, i.e. exactly how a real user would use VR. Mock runtimes + # avoid this, but can't necessarily be implemented. + DESKTOP_RUNTIMES = { + 'oculus': { + MOCK_RUNTIME: oculus_runtimes.OculusRuntimeMock, + REAL_RUNTIME: oculus_runtimes.OculusRuntimeReal, + }, + 'openvr': { + MOCK_RUNTIME: openvr_runtimes.OpenVRRuntimeMock, + REAL_RUNTIME: openvr_runtimes.OpenVRRuntimeReal + }, + 'wmr': { + MOCK_RUNTIME: wmr_runtimes.WMRRuntimeMock, + REAL_RUNTIME: wmr_runtimes.WMRRuntimeReal, + }, + } + + def __init__(self, test, finder_options, story_set, possible_browser): + super(WindowsSharedVrPageState, self).__init__( + test, finder_options, story_set, possible_browser) + + # Get the specific runtime implementation depending on runtime choice and + # whether we're using the real or mock one. + self._desktop_runtime = self.DESKTOP_RUNTIMES[ + self._finder_options.desktop_runtime][ + self._finder_options.use_real_runtime](self._finder_options) + # Enable the correct feature for the specified runtime. + self._desktop_runtime.Setup() + + def WillRunStory(self, page): + super(WindowsSharedVrPageState, self).WillRunStory(page) + self._desktop_runtime.WillRunStory() + + def TearDownState(self): + super(WindowsSharedVrPageState, self).TearDownState() + self._desktop_runtime.TearDown() diff --git a/tools/perf/contrib/vr_benchmarks/vr_benchmarks.py b/tools/perf/contrib/vr_benchmarks/vr_benchmarks.py index b212fdf0fcce72..ae56555159965f 100644 --- a/tools/perf/contrib/vr_benchmarks/vr_benchmarks.py +++ b/tools/perf/contrib/vr_benchmarks/vr_benchmarks.py @@ -11,6 +11,7 @@ from telemetry.timeline import chrome_trace_category_filter from telemetry.timeline import chrome_trace_config from telemetry.web_perf import timeline_based_measurement +from contrib.vr_benchmarks import shared_vr_page_state as vr_state from contrib.vr_benchmarks import vr_browsing_mode_pages from contrib.vr_benchmarks import webvr_sample_pages from contrib.vr_benchmarks import webvr_wpr_pages @@ -19,6 +20,16 @@ class _BaseVRBenchmark(perf_benchmark.PerfBenchmark): + # Trace categories that should be enabled for all VR benchmarks. + COMMON_TRACE_CATEGORIES = [ + '-*', # Remove all default categories. + 'blink.console', # Necessary for memory measurements. + 'disabled-by-default-memory-infra', # Necessary for memory measurements. + 'gpu', # Necessary for various VR metrics. + 'toplevel', # Debug category. + 'viz', # Debug category. + ] + @classmethod def AddBenchmarkCommandLineArgs(cls, parser): parser.add_option( @@ -59,24 +70,37 @@ def AddBenchmarkCommandLineArgs(cls, parser): 'for it. This largely boils down to adding waits/sleeps in order ' 'to ensure that enough streaming data is recorded for the ' 'benchmark to run without issues.') + parser.add_option( + '--desktop-runtime', + default='openvr', + choices=vr_state.WindowsSharedVrPageState.DESKTOP_RUNTIMES.keys(), + help='Which VR runtime to use on Windows. Defaults to %default') + parser.add_option( + '--use-real-runtime', + action='store_true', + default=False, + help='Use the real runtime instead of a mock implementation. This ' + 'requires the runtime to be installed on the system.') class _BaseWebVRWebXRBenchmark(_BaseVRBenchmark): - SUPPORTED_PLATFORMS = [story.expectations.ALL_ANDROID] + SUPPORTED_PLATFORMS = [ + story.expectations.ALL_ANDROID, + story.expectations.WIN_10 + ] def CreateCoreTimelineBasedMeasurementOptions(self): - memory_categories = ['blink.console', 'disabled-by-default-memory-infra'] - gpu_categories = ['gpu'] - debug_categories = ['toplevel', 'viz'] - category_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter( - ','.join(['-*'] + memory_categories + gpu_categories - + debug_categories)) + category_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter() + for category in self.COMMON_TRACE_CATEGORIES: + category_filter.AddFilter(category) + options = timeline_based_measurement.Options(category_filter) options.config.enable_android_graphics_memtrack = True options.config.enable_platform_display_trace = True - options.SetTimelineBasedMetrics(['memoryMetric', 'webvrMetric']) + options.SetTimelineBasedMetrics( + ['memoryMetric', 'webvrMetric', 'webxrMetric']) options.config.chrome_trace_config.SetMemoryDumpConfig( chrome_trace_config.MemoryDumpConfig()) return options @@ -172,12 +196,10 @@ class _BaseBrowsingBenchmark(_BaseVRBenchmark): SUPPORTED_PLATFORMS = [story.expectations.ALL_ANDROID] def CreateTimelineBasedMeasurementOptions(self): - memory_categories = ['blink.console', 'disabled-by-default-memory-infra'] - gpu_categories = ['gpu'] - debug_categories = ['toplevel', 'viz'] - category_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter( - ','.join(['-*'] + memory_categories + gpu_categories - + debug_categories)) + category_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter() + for category in self.COMMON_TRACE_CATEGORIES: + category_filter.AddFilter(category) + options = timeline_based_measurement.Options(category_filter) options.config.enable_android_graphics_memtrack = True options.config.enable_platform_display_trace = True diff --git a/tools/perf/contrib/vr_benchmarks/vr_browsing_mode_pages.py b/tools/perf/contrib/vr_benchmarks/vr_browsing_mode_pages.py index c555e7f28d5030..6aa1616b0cd66d 100644 --- a/tools/perf/contrib/vr_benchmarks/vr_browsing_mode_pages.py +++ b/tools/perf/contrib/vr_benchmarks/vr_browsing_mode_pages.py @@ -8,7 +8,7 @@ from telemetry import story from telemetry.page import shared_page_state from devil.android.sdk import intent # pylint: disable=import-error -from contrib.vr_benchmarks import shared_android_vr_page_state as vr_state +from contrib.vr_benchmarks import shared_vr_page_state as vr_state from contrib.vr_benchmarks.vr_sample_page import VrSamplePage from contrib.vr_benchmarks.vr_story_set import VrStorySet from page_sets import top_10_mobile @@ -72,7 +72,7 @@ def __init__(self, page_set, url, name, extra_browser_args=None): page_set=page_set, name=name, extra_browser_args=extra_browser_args, - shared_page_state_class=vr_state.SharedAndroidVrPageState) + shared_page_state_class=vr_state.AndroidSharedVrPageState) self._shared_page_state = None def RunPageInteractions(self, action_runner): diff --git a/tools/perf/contrib/vr_benchmarks/vr_sample_page.py b/tools/perf/contrib/vr_benchmarks/vr_sample_page.py index 73a5f08eab6fff..54390c0d3b473f 100644 --- a/tools/perf/contrib/vr_benchmarks/vr_sample_page.py +++ b/tools/perf/contrib/vr_benchmarks/vr_sample_page.py @@ -5,8 +5,7 @@ import os import re from telemetry import page -from contrib.vr_benchmarks import (shared_android_vr_page_state as - vr_state) +from contrib.vr_benchmarks import shared_vr_page_state as vr_state WEBVR_SAMPLE_DIR = os.path.join( os.path.dirname(__file__), '..', '..', '..', '..', 'chrome', 'test', @@ -40,7 +39,7 @@ def __init__(self, sample_directory, sample_page, page_set, page_set=page_set, name=name, extra_browser_args=extra_browser_args, - shared_page_state_class=vr_state.SharedAndroidVrPageState) + shared_page_state_class=vr_state.SharedVrPageStateFactory) self._shared_page_state = None def Run(self, shared_state): diff --git a/tools/perf/contrib/vr_benchmarks/webvr_wpr_pages.py b/tools/perf/contrib/vr_benchmarks/webvr_wpr_pages.py index 965ff81167b83f..78d0965b116b0d 100644 --- a/tools/perf/contrib/vr_benchmarks/webvr_wpr_pages.py +++ b/tools/perf/contrib/vr_benchmarks/webvr_wpr_pages.py @@ -4,7 +4,7 @@ from telemetry import story from telemetry import page -from contrib.vr_benchmarks import (shared_android_vr_page_state as vr_state) +from contrib.vr_benchmarks import (shared_vr_page_state as vr_state) from contrib.vr_benchmarks.vr_story_set import VrStorySet class WebVrWprPage(page.Page): @@ -27,7 +27,7 @@ def __init__(self, page_set, url, name, interaction_function, page_set=page_set, name=name, extra_browser_args=extra_browser_args, - shared_page_state_class=vr_state.SharedAndroidVrPageState) + shared_page_state_class=vr_state.SharedVrPageStateFactory) self._shared_page_state = None self._interaction_function = interaction_function