1# Lint as: python2, python3
2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5"""This is a client side WebGL many planets deep test."""
6
7import numpy
8import os
9import time
10
11from autotest_lib.client.bin import test
12from autotest_lib.client.common_lib import error
13from autotest_lib.client.bin import utils
14from autotest_lib.client.common_lib.cros import chrome
15from autotest_lib.client.cros.graphics import graphics_utils
16from autotest_lib.client.cros.power import power_rapl
17
18
19class graphics_WebGLManyPlanetsDeep(graphics_utils.GraphicsTest):
20    """WebGL many planets deep graphics test."""
21    version = 1
22    frame_data = {}
23    perf_keyval = {}
24    test_duration_secs = 30
25
26    def setup(self):
27        self.job.setup_dep(['webgl_mpd'])
28        self.job.setup_dep(['graphics'])
29
30    def initialize(self):
31        super(graphics_WebGLManyPlanetsDeep, self).initialize()
32
33    def cleanup(self):
34        super(graphics_WebGLManyPlanetsDeep, self).cleanup()
35
36    def run_many_planets_deep_test(self, browser, test_url):
37        """Runs the many planets deep test from the given url.
38
39        @param browser: The Browser object to run the test with.
40        @param test_url: The URL to the many planets deep test site.
41        """
42        if not utils.wait_for_idle_cpu(60.0, 0.1):
43            if not utils.wait_for_idle_cpu(20.0, 0.2):
44                raise error.TestFail('Failed: Could not get idle CPU.')
45
46        tab = browser.tabs.New()
47        tab.Navigate(test_url)
48        tab.Activate()
49        tab.WaitForDocumentReadyStateToBeComplete()
50
51        # Wait 3 seconds for the page to stabilize.
52        time.sleep(3)
53
54        # Reset our own FPS counter and start recording FPS and rendering time.
55        end_time = time.time() + self.test_duration_secs
56        tab.ExecuteJavaScript('g_crosFpsCounter.reset();')
57        while time.time() < end_time:
58            frame_data = tab.EvaluateJavaScript(
59                'g_crosFpsCounter.getFrameData();')
60            for datum in frame_data:
61                if not datum or datum['seq'] in self.frame_data:
62                    continue
63                self.frame_data[datum['seq']] = {
64                    'start_time': datum['startTime'],
65                    'frame_elapsed_time': datum['frameElapsedTime'],
66                    'js_elapsed_time': datum['jsElapsedTime']
67                }
68            time.sleep(1)
69
70        # Intel only: Record the power consumption for the next few seconds.
71        self.rapl_rate = power_rapl.get_rapl_measurement(
72            'rapl_many_planets_deep')
73        tab.Close()
74
75    def calculate_perf_values(self):
76        """Calculates all the perf values from the collected data."""
77        arr = numpy.array([[v['frame_elapsed_time'], v['js_elapsed_time']]
78                           for v in list(self.frame_data.values())])
79        std = arr.std(axis=0)
80        mean = arr.mean(axis=0)
81        avg_fps = 1000.0 / mean[0]
82        self.perf_keyval.update({
83            'average_fps': avg_fps,
84            'per_frame_dt_ms_std': std[0],
85            'per_frame_dt_ms_mean': mean[0],
86            'js_render_time_ms_std': std[1],
87            'js_render_time_ms_mean': mean[1]
88        })
89
90        # Remove entries that we don't care about.
91        rapl_rate = {key: self.rapl_rate[key]
92                     for key in list(self.rapl_rate.keys()) if key.endswith('pwr')}
93        # Report to chromeperf/ dashboard.
94        for key, values in list(rapl_rate.items()):
95            self.output_perf_value(
96                description=key,
97                value=values,
98                units='W',
99                higher_is_better=False,
100                graph='rapl_power_consumption'
101            )
102        self.output_perf_value(
103            description='average_fps',
104            value=avg_fps,
105            units='fps',
106            higher_is_better=True)
107
108        with open('frame_data', 'w') as f:
109            line_format = '%10s %20s %20s %20s\n'
110            f.write(line_format % ('seq', 'start_time', 'frame_render_time_ms',
111                                   'js_render_time_ms'))
112            for k in sorted(self.frame_data.keys()):
113                d = self.frame_data[k]
114                f.write(line_format % (k, d['start_time'],
115                                       d['frame_elapsed_time'],
116                                       d['js_elapsed_time']))
117
118    @graphics_utils.GraphicsTest.failure_report_decorator('graphics_WebGLManyPlanetsDeep')
119    def run_once(self, test_duration_secs=30, fullscreen=True):
120        """Finds a brower with telemetry, and run the test.
121
122        @param test_duration_secs: The test duration in seconds to run the test
123                for.
124        @param fullscreen: Whether to run the test in fullscreen.
125        """
126        self.test_duration_secs = test_duration_secs
127
128        ext_paths = []
129        if fullscreen:
130            ext_paths.append(
131                os.path.join(self.autodir, 'deps', 'graphics',
132                             'graphics_test_extension'))
133
134        with chrome.Chrome(logged_in=False,
135                           extension_paths=ext_paths,
136                           init_network_controller=True) as cr:
137            websrc_dir = os.path.join(self.autodir, 'deps', 'webgl_mpd', 'src')
138            if not cr.browser.platform.SetHTTPServerDirectories(websrc_dir):
139                raise error.TestFail('Failed: Unable to start HTTP server')
140            test_url = cr.browser.platform.http_server.UrlOf(
141                os.path.join(websrc_dir, 'ManyPlanetsDeep.html'))
142            self.run_many_planets_deep_test(cr.browser, test_url)
143
144        self.calculate_perf_values()
145        self.write_perf_keyval(self.perf_keyval)
146