xref: /aosp_15_r20/external/skia/infra/bots/recipes/perf_skottiewasm_lottieweb.py (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1# Copyright 2019 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5# Recipe which runs Skottie-WASM and Lottie-Web perf.
6
7import calendar
8import json
9import re
10
11PYTHON_VERSION_COMPATIBILITY = "PY3"
12
13# trim
14DEPS = [
15  'flavor',
16  'checkout',
17  'env',
18  'infra',
19  'recipe_engine/context',
20  'recipe_engine/file',
21  'recipe_engine/json',
22  'recipe_engine/path',
23  'recipe_engine/properties',
24  'recipe_engine/step',
25  'recipe_engine/time',
26  'run',
27  'vars',
28]
29
30LOTTIE_WEB_EXCLUDE = [
31  # See https://bugs.chromium.org/p/skia/issues/detail?id=9187#c4
32  'lottiefiles.com - Progress Success.json',
33  # Fails with "val2 is not defined".
34  'lottiefiles.com - VR.json',
35  'vr_animation.json',
36  # Times out.
37  'lottiefiles.com - Nudge.json',
38  'lottiefiles.com - Retweet.json',
39  # Trace file has majority main_frame_aborted terminations in it and < 25
40  # occurrences of submitted_frame + missed_frame.
41  # Static scenes (nothing animating)
42  'mask1.json',
43  'mask2.json',
44  'stacking.json',
45]
46
47SKOTTIE_WASM_EXCLUDE = [
48  # Trace file has majority main_frame_aborted terminations in it and < 25
49  # occurrences of submitted_frame + missed_frame.
50  # Below descriptions are added from fmalita@'s comments in
51  # https://skia-review.googlesource.com/c/skia/+/229419
52
53  # Static scenes (nothing animating)
54  'mask1.json',
55  'mask2.json',
56  'stacking.json',
57  # Static in Skottie only due to unsupported feature (expressions).
58  'dna.json',
59  'elephant_trunk_swing.json',
60  # Looks all static in both skottie/lottie, not sure why lottie doesn't abort
61  # as many frames.
62  'hexadots.json',
63  # Very short transition, mostly static.
64  'screenhole.json',
65  # Broken in Skottie due to unidentified missing feature.
66  'interleague_golf_logo.json',
67  'loading.json',
68  'lottiefiles.com - Loading 2.json',
69  'streetby_loading.json',
70  'streetby_test_loading.json',
71  # Times out
72  'beetle.json',
73  # Too slow? Doesn't provide enough frames for analysis b/325452373
74  'Name.json',
75]
76
77# These files work in SVG but not in Canvas.
78LOTTIE_WEB_CANVAS_EXCLUDE = LOTTIE_WEB_EXCLUDE + [
79  'Hello World.json',
80  'interactive_menu.json',
81  'Name.json',
82]
83
84
85def RunSteps(api):
86  api.vars.setup()
87  api.flavor.setup(None)
88  checkout_root = api.path.start_dir
89  buildername = api.properties['buildername']
90  node_path = api.path.start_dir.joinpath('node', 'node', 'bin', 'node')
91  lottie_files = api.file.listdir(
92      'list lottie files', api.flavor.host_dirs.lotties_dir,
93      test_data=['lottie1.json', 'lottie2.json', 'lottie3.json', 'LICENSE'])
94
95  if 'SkottieWASM' in buildername:
96    source_type = 'skottie'
97    renderer = 'skottie-wasm'
98
99    perf_app_dir = checkout_root.joinpath('skia', 'tools', 'skottie-wasm-perf')
100    canvaskit_js_path = api.vars.build_dir.joinpath('canvaskit.js')
101    canvaskit_wasm_path = api.vars.build_dir.joinpath('canvaskit.wasm')
102    skottie_wasm_js_path = perf_app_dir.joinpath('skottie-wasm-perf.js')
103    perf_app_cmd = [
104        node_path, skottie_wasm_js_path,
105        '--canvaskit_js', canvaskit_js_path,
106        '--canvaskit_wasm', canvaskit_wasm_path,
107    ]
108    lottie_files = [x for x in lottie_files
109                    if api.path.basename(x) not in SKOTTIE_WASM_EXCLUDE]
110  elif 'LottieWeb' in buildername:
111    source_type = 'lottie-web'
112    renderer = 'lottie-web'
113    if 'Canvas' in buildername:
114      backend = 'canvas'
115      lottie_files = [
116          x for x in lottie_files
117          if api.path.basename(x) not in LOTTIE_WEB_CANVAS_EXCLUDE]
118    else:
119      backend = 'svg'
120      lottie_files = [x for x in lottie_files
121                      if api.path.basename(x) not in LOTTIE_WEB_EXCLUDE]
122
123    perf_app_dir = checkout_root.joinpath('skia', 'tools', 'lottie-web-perf')
124    lottie_web_js_path = perf_app_dir.joinpath('lottie-web-perf.js')
125    perf_app_cmd = [
126        node_path, lottie_web_js_path,
127        '--backend', backend,
128    ]
129  else:
130    raise Exception('Could not recognize the buildername %s' % buildername)
131
132  if api.vars.builder_cfg.get('cpu_or_gpu') == 'GPU':
133    perf_app_cmd.append('--use_gpu')
134
135  # Install prerequisites.
136  env_prefixes = {'PATH': [api.path.start_dir.joinpath('node', 'node', 'bin')]}
137  with api.context(cwd=perf_app_dir, env_prefixes=env_prefixes):
138    api.step('npm install', cmd=['npm', 'install'])
139
140  perf_results = {}
141  output_dir = api.path.mkdtemp('g3_try')
142  # Run the perf_app_cmd on each lottie file and parse the trace files.
143  for _, lottie_file in enumerate(lottie_files):
144    lottie_filename = api.path.basename(lottie_file)
145    if not lottie_filename.endswith('.json'):
146      continue
147    output_file = output_dir.joinpath(lottie_filename)
148    with api.context(cwd=perf_app_dir, env={'DISPLAY': ':0'}):
149      # This is occasionally flaky due to skbug.com/9207, adding retries.
150      attempts = 3
151      # Add output and input arguments to the cmd.
152      api.run.with_retry(api.step, 'Run perf cmd line app', attempts,
153                         cmd=perf_app_cmd + [
154                             '--input', lottie_file,
155                             '--output', output_file,
156                         ], infra_step=True)
157
158    perf_results[lottie_filename] = {
159        'gl': parse_trace(output_file, lottie_filename, api, renderer),
160    }
161
162  # Construct contents of the output JSON.
163  perf_json = {
164      'gitHash': api.properties['revision'],
165      'swarming_bot_id': api.vars.swarming_bot_id,
166      'swarming_task_id': api.vars.swarming_task_id,
167      'key': {
168        'bench_type': 'tracing',
169        'source_type': source_type,
170      },
171      'renderer': renderer,
172      'results': perf_results,
173  }
174  if api.vars.is_trybot:
175    perf_json['issue'] = api.vars.issue
176    perf_json['patchset'] = api.vars.patchset
177    perf_json['patch_storage'] = api.vars.patch_storage
178  # Add tokens from the builder name to the key.
179  reg = re.compile('Perf-(?P<os>[A-Za-z0-9_]+)-'
180                   '(?P<compiler>[A-Za-z0-9_]+)-'
181                   '(?P<model>[A-Za-z0-9_]+)-'
182                   '(?P<cpu_or_gpu>[A-Z]+)-'
183                   '(?P<cpu_or_gpu_value>[A-Za-z0-9_]+)-'
184                   '(?P<arch>[A-Za-z0-9_]+)-'
185                   '(?P<configuration>[A-Za-z0-9_]+)-'
186                   'All(-(?P<extra_config>[A-Za-z0-9_]+)|)')
187  m = reg.match(api.properties['buildername'])
188  keys = ['os', 'compiler', 'model', 'cpu_or_gpu', 'cpu_or_gpu_value', 'arch',
189          'configuration', 'extra_config']
190  for k in keys:
191    perf_json['key'][k] = m.group(k)
192
193  # Create the output JSON file in perf_data_dir for the Upload task to upload.
194  api.file.ensure_directory(
195      'makedirs perf_dir',
196      api.flavor.host_dirs.perf_data_dir)
197  now = api.time.utcnow()
198  ts = int(calendar.timegm(now.utctimetuple()))
199  json_path = api.flavor.host_dirs.perf_data_dir.joinpath(
200      'perf_%s_%d.json' % (api.properties['revision'], ts))
201  json_contents = json.dumps(
202      perf_json, indent=4, sort_keys=True, separators=(',', ': '))
203  api.file.write_text('write output JSON', json_path, json_contents)
204
205
206def parse_trace(trace_json, lottie_filename, api, renderer):
207  """parse_trace parses the specified trace JSON.
208
209  Parses the trace JSON and calculates the time of a single frame.
210  A dictionary is returned that has the following structure:
211  {
212    'frame_max_us': 100,
213    'frame_min_us': 90,
214    'frame_avg_us': 95,
215  }
216  """
217  script = api.infra.resource('parse_lottieweb_trace.py')
218  step_result = api.run(
219      api.step,
220      'parse %s trace' % lottie_filename,
221      cmd=['python3', script, trace_json, api.json.output(), renderer])
222
223  # Sanitize float outputs to 2 precision points.
224  output = dict(step_result.json.output)
225  output['frame_max_us'] = float("%.2f" % output['frame_max_us'])
226  output['frame_min_us'] = float("%.2f" % output['frame_min_us'])
227  output['frame_avg_us'] = float("%.2f" % output['frame_avg_us'])
228  return output
229
230
231def GenTests(api):
232  trace_output = """
233[{"ph":"X","name":"void skottie::Animation::seek(SkScalar)","ts":452,"dur":2.57,"tid":1,"pid":0},{"ph":"X","name":"void SkCanvas::drawPaint(const SkPaint &)","ts":473,"dur":2.67e+03,"tid":1,"pid":0},{"ph":"X","name":"void skottie::Animation::seek(SkScalar)","ts":3.15e+03,"dur":2.25,"tid":1,"pid":0},{"ph":"X","name":"void skottie::Animation::render(SkCanvas *, const SkRect *, RenderFlags) const","ts":3.15e+03,"dur":216,"tid":1,"pid":0},{"ph":"X","name":"void SkCanvas::drawPath(const SkPath &, const SkPaint &)","ts":3.35e+03,"dur":15.1,"tid":1,"pid":0},{"ph":"X","name":"void skottie::Animation::seek(SkScalar)","ts":3.37e+03,"dur":1.17,"tid":1,"pid":0},{"ph":"X","name":"void skottie::Animation::render(SkCanvas *, const SkRect *, RenderFlags) const","ts":3.37e+03,"dur":140,"tid":1,"pid":0}]
234"""
235  parse_trace_json = {
236      'frame_avg_us': 179.71,
237      'frame_min_us': 141.17,
238      'frame_max_us': 218.25
239  }
240
241
242  skottie_cpu_buildername = ('Perf-Debian10-EMCC-GCE-CPU-AVX2-wasm-Release-All-'
243                             'SkottieWASM')
244  yield (
245      api.test('skottie_wasm_perf') +
246      api.properties(buildername=skottie_cpu_buildername,
247                     repository='https://skia.googlesource.com/skia.git',
248                     revision='abc123',
249                     path_config='kitchen',
250                     trace_test_data=trace_output,
251                     swarm_out_dir='[SWARM_OUT_DIR]') +
252      api.step_data('parse lottie1.json trace',
253                    api.json.output(parse_trace_json)) +
254      api.step_data('parse lottie2.json trace',
255                    api.json.output(parse_trace_json)) +
256      api.step_data('parse lottie3.json trace',
257                    api.json.output(parse_trace_json))
258  )
259  yield (
260      api.test('skottie_wasm_perf_trybot') +
261      api.properties(buildername=skottie_cpu_buildername,
262                     repository='https://skia.googlesource.com/skia.git',
263                     revision='abc123',
264                     path_config='kitchen',
265                     trace_test_data=trace_output,
266                     swarm_out_dir='[SWARM_OUT_DIR]',
267                     patch_ref='89/456789/12',
268                     patch_repo='https://skia.googlesource.com/skia.git',
269                     patch_storage='gerrit',
270                     patch_set=7,
271                     patch_issue=1234,
272                     gerrit_project='skia',
273                     gerrit_url='https://skia-review.googlesource.com/') +
274      api.step_data('parse lottie1.json trace',
275                    api.json.output(parse_trace_json)) +
276      api.step_data('parse lottie2.json trace',
277                    api.json.output(parse_trace_json)) +
278      api.step_data('parse lottie3.json trace',
279                    api.json.output(parse_trace_json))
280  )
281
282  skottie_gpu_buildername = ('Perf-Debian10-EMCC-NUC7i5BNK-GPU-IntelIris640-'
283                             'wasm-Release-All-SkottieWASM')
284  yield (
285      api.test('skottie_wasm_perf_gpu') +
286      api.properties(buildername=skottie_gpu_buildername,
287                     repository='https://skia.googlesource.com/skia.git',
288                     revision='abc123',
289                     path_config='kitchen',
290                     trace_test_data=trace_output,
291                     swarm_out_dir='[SWARM_OUT_DIR]') +
292      api.step_data('parse lottie1.json trace',
293                    api.json.output(parse_trace_json)) +
294      api.step_data('parse lottie2.json trace',
295                    api.json.output(parse_trace_json)) +
296      api.step_data('parse lottie3.json trace',
297                    api.json.output(parse_trace_json))
298  )
299
300  lottieweb_cpu_buildername = ('Perf-Debian10-none-GCE-CPU-AVX2-x86_64-Release-'
301                               'All-LottieWeb')
302  yield (
303      api.test('lottie_web_perf') +
304      api.properties(buildername=lottieweb_cpu_buildername,
305                     repository='https://skia.googlesource.com/skia.git',
306                     revision='abc123',
307                     path_config='kitchen',
308                     trace_test_data=trace_output,
309                     swarm_out_dir='[SWARM_OUT_DIR]') +
310      api.step_data('parse lottie1.json trace',
311                    api.json.output(parse_trace_json)) +
312      api.step_data('parse lottie2.json trace',
313                    api.json.output(parse_trace_json)) +
314      api.step_data('parse lottie3.json trace',
315                    api.json.output(parse_trace_json))
316  )
317  yield (
318      api.test('lottie_web_perf_trybot') +
319      api.properties(buildername=lottieweb_cpu_buildername,
320                     repository='https://skia.googlesource.com/skia.git',
321                     revision='abc123',
322                     path_config='kitchen',
323                     trace_test_data=trace_output,
324                     swarm_out_dir='[SWARM_OUT_DIR]',
325                     patch_ref='89/456789/12',
326                     patch_repo='https://skia.googlesource.com/skia.git',
327                     patch_storage='gerrit',
328                     patch_set=7,
329                     patch_issue=1234,
330                     gerrit_project='skia',
331                     gerrit_url='https://skia-review.googlesource.com/') +
332      api.step_data('parse lottie1.json trace',
333                    api.json.output(parse_trace_json)) +
334      api.step_data('parse lottie2.json trace',
335                    api.json.output(parse_trace_json)) +
336      api.step_data('parse lottie3.json trace',
337                    api.json.output(parse_trace_json))
338  )
339
340  lottieweb_canvas_cpu_buildername = (
341      'Perf-Debian10-none-GCE-CPU-AVX2-x86_64-Release-All-LottieWeb_Canvas')
342  yield (
343      api.test('lottie_web_canvas_perf') +
344      api.properties(buildername=lottieweb_canvas_cpu_buildername,
345                     repository='https://skia.googlesource.com/skia.git',
346                     revision='abc123',
347                     path_config='kitchen',
348                     trace_test_data=trace_output,
349                     swarm_out_dir='[SWARM_OUT_DIR]') +
350      api.step_data('parse lottie1.json trace',
351                    api.json.output(parse_trace_json)) +
352      api.step_data('parse lottie2.json trace',
353                    api.json.output(parse_trace_json)) +
354      api.step_data('parse lottie3.json trace',
355                    api.json.output(parse_trace_json))
356  )
357  yield (
358      api.test('lottie_web_canvas_perf_trybot') +
359      api.properties(buildername=lottieweb_canvas_cpu_buildername,
360                     repository='https://skia.googlesource.com/skia.git',
361                     revision='abc123',
362                     path_config='kitchen',
363                     trace_test_data=trace_output,
364                     swarm_out_dir='[SWARM_OUT_DIR]',
365                     patch_ref='89/456789/12',
366                     patch_repo='https://skia.googlesource.com/skia.git',
367                     patch_storage='gerrit',
368                     patch_set=7,
369                     patch_issue=1234,
370                     gerrit_project='skia',
371                     gerrit_url='https://skia-review.googlesource.com/') +
372      api.step_data('parse lottie1.json trace',
373                    api.json.output(parse_trace_json)) +
374      api.step_data('parse lottie2.json trace',
375                    api.json.output(parse_trace_json)) +
376      api.step_data('parse lottie3.json trace',
377                    api.json.output(parse_trace_json))
378  )
379
380  unrecognized_buildername = ('Perf-Debian10-none-GCE-CPU-AVX2-x86_64-Release-'
381                              'All-Unrecognized')
382  yield (
383      api.test('unrecognized_builder') +
384      api.properties(buildername=unrecognized_buildername,
385                     repository='https://skia.googlesource.com/skia.git',
386                     revision='abc123',
387                     path_config='kitchen',
388                     swarm_out_dir='[SWARM_OUT_DIR]') +
389      api.expect_exception('Exception')
390  )
391