ui: stop creating timespans, timescales and pxspans on every slice

This CL fixes a major performance issue on ~largish (even at the scale
of ~70MB) traces where the UI becomes completely unusable when zoomed
in.

Change-Id: I16e56c28b2581674a94fa1e0b5c606f69ab2d036
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index 39ab0e9..d1f505b 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -20,6 +20,7 @@
   colorToStr,
   UNEXPECTED_PINK_COLOR,
 } from '../common/colorizer';
+import {HighPrecisionTimeSpan} from '../common/high_precision_time';
 import {LONG, NUM} from '../common/query_result';
 import {Selection, SelectionKind} from '../common/state';
 import {
@@ -34,6 +35,7 @@
 import {Slice} from './slice';
 import {DEFAULT_SLICE_LAYOUT, SliceLayout} from './slice_layout';
 import {constraintsToQuerySuffix} from './sql_utils';
+import {PxSpan, TimeScale} from './time_scale';
 import {NewTrackArgs, SliceRect, Track} from './track';
 import {BUCKETS_PER_PIXEL, CacheKey, TrackCache} from './track_cache';
 
@@ -875,8 +877,10 @@
     return this.computedTrackHeight;
   }
 
-  getSliceRect(_tStart: TPTime, _tEnd: TPTime, _depth: number): SliceRect
-      |undefined {
+  getSliceRect(
+      _visibleTimeScale: TimeScale, _visibleWindowTime: HighPrecisionTimeSpan,
+      _windowSpan: PxSpan, _tStart: TPTime, _tEnd: TPTime,
+      _depth: number): SliceRect|undefined {
     // TODO(hjd): Implement this as part of updating flow events.
     return undefined;
   }
diff --git a/ui/src/frontend/flow_events_renderer.ts b/ui/src/frontend/flow_events_renderer.ts
index c511758..ddf23d8 100644
--- a/ui/src/frontend/flow_events_renderer.ts
+++ b/ui/src/frontend/flow_events_renderer.ts
@@ -146,12 +146,19 @@
 
   private getSliceRect(args: FlowEventsRendererArgs, point: FlowPoint):
       SliceRect|undefined {
+    const {visibleTimeScale, visibleWindowTime, windowSpan} =
+        globals.frontendLocalState;
     const trackPanel = args.trackIdToTrackPanel.get(point.trackId) ?.panel;
     if (!trackPanel) {
       return undefined;
     }
     return trackPanel.getSliceRect(
-        point.sliceStartTs, point.sliceEndTs, point.depth);
+        visibleTimeScale,
+        visibleWindowTime,
+        windowSpan,
+        point.sliceStartTs,
+        point.sliceEndTs,
+        point.depth);
   }
 
   render(ctx: CanvasRenderingContext2D, args: FlowEventsRendererArgs) {
diff --git a/ui/src/frontend/track.ts b/ui/src/frontend/track.ts
index a2aa8dd..f890d36 100644
--- a/ui/src/frontend/track.ts
+++ b/ui/src/frontend/track.ts
@@ -16,12 +16,14 @@
 
 import {assertExists} from '../base/logging';
 import {EngineProxy} from '../common/engine';
+import {HighPrecisionTimeSpan} from '../common/high_precision_time';
 import {TrackState} from '../common/state';
 import {TPTime} from '../common/time';
 import {TrackData} from '../common/track_data';
 
 import {checkerboard} from './checkerboard';
 import {globals} from './globals';
+import {PxSpan, TimeScale} from './time_scale';
 import {TrackButtonAttrs} from './track_panel';
 
 // Args passed to the track constructors when creating a new track.
@@ -208,8 +210,10 @@
   // only for track types that support slices e.g. chrome_slice, async_slices
   // tStart - slice start time in seconds, tEnd - slice end time in seconds,
   // depth - slice depth
-  getSliceRect(_tStart: TPTime, _tEnd: TPTime, _depth: number): SliceRect
-      |undefined {
+  getSliceRect(
+      _visibleTimeScale: TimeScale, _visibleWindowTime: HighPrecisionTimeSpan,
+      _windowSpan: PxSpan, _tStart: TPTime, _tEnd: TPTime,
+      _depth: number): SliceRect|undefined {
     return undefined;
   }
 }
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 4c7b2a5..aab1e54 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -16,6 +16,7 @@
 import m from 'mithril';
 
 import {Actions} from '../common/actions';
+import {HighPrecisionTimeSpan} from '../common/high_precision_time';
 import {TrackState} from '../common/state';
 import {TPTime} from '../common/time';
 
@@ -26,6 +27,7 @@
 import {BLANK_CHECKBOX, CHECKBOX, PIN} from './icons';
 import {Panel, PanelSize} from './panel';
 import {verticalScrollToTrack} from './scroll_helper';
+import {PxSpan, TimeScale} from './time_scale';
 import {SliceRect, Track} from './track';
 import {trackRegistry} from './track_registry';
 import {
@@ -458,11 +460,14 @@
     }
   }
 
-  getSliceRect(tStart: TPTime, tDur: TPTime, depth: number): SliceRect
-      |undefined {
+  getSliceRect(
+      visibleTimeScale: TimeScale, visibleWindowTime: HighPrecisionTimeSpan,
+      windowSpan: PxSpan, tStart: TPTime, tDur: TPTime,
+      depth: number): SliceRect|undefined {
     if (this.track === undefined) {
       return undefined;
     }
-    return this.track.getSliceRect(tStart, tDur, depth);
+    return this.track.getSliceRect(
+        visibleTimeScale, visibleWindowTime, windowSpan, tStart, tDur, depth);
   }
 }
diff --git a/ui/src/tracks/chrome_slices/index.ts b/ui/src/tracks/chrome_slices/index.ts
index 4e190f9..99358a5 100644
--- a/ui/src/tracks/chrome_slices/index.ts
+++ b/ui/src/tracks/chrome_slices/index.ts
@@ -16,7 +16,10 @@
 import {Actions} from '../../common/actions';
 import {cropText, drawIncompleteSlice} from '../../common/canvas_utils';
 import {colorForThreadIdleSlice, hslForSlice} from '../../common/colorizer';
-import {HighPrecisionTime} from '../../common/high_precision_time';
+import {
+  HighPrecisionTime,
+  HighPrecisionTimeSpan,
+} from '../../common/high_precision_time';
 import {PluginContext} from '../../common/plugin_api';
 import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
 import {TPDuration, TPTime} from '../../common/time';
@@ -25,6 +28,7 @@
 import {checkerboardExcept} from '../../frontend/checkerboard';
 import {globals} from '../../frontend/globals';
 import {cachedHsluvToHex} from '../../frontend/hsluv_cache';
+import {PxSpan, TimeScale} from '../../frontend/time_scale';
 import {NewTrackArgs, SliceRect, Track} from '../../frontend/track';
 
 export const SLICE_TRACK_KIND = 'ChromeSliceTrack';
@@ -178,7 +182,8 @@
   renderCanvas(ctx: CanvasRenderingContext2D): void {
     // TODO: fonts and colors should come from the CSS and not hardcoded here.
 
-    const {visibleTimeScale, visibleWindowTime} = globals.frontendLocalState;
+    const {visibleTimeScale, visibleWindowTime, windowSpan} =
+        globals.frontendLocalState;
     const data = this.data();
 
     if (data === undefined) return;  // Can't possibly draw anything.
@@ -203,6 +208,7 @@
     // drawings, otherwise it would result under another rect.
     let drawRectOnSelected = () => {};
 
+
     for (let i = 0; i < data.starts.length; i++) {
       const tStart = data.starts[i];
       let tEnd = data.ends[i];
@@ -219,7 +225,8 @@
         tEnd = visibleWindowTime.end.toTPTime('ceil');
       }
 
-      const rect = this.getSliceRect(tStart, tEnd, depth);
+      const rect = this.getSliceRect(
+          visibleTimeScale, visibleWindowTime, windowSpan, tStart, tEnd, depth);
       if (!rect || !rect.visible) {
         continue;
       }
@@ -408,17 +415,13 @@
     return SLICE_HEIGHT * (this.config.maxDepth + 1) + 2 * TRACK_PADDING;
   }
 
-  getSliceRect(tStart: TPTime, tEnd: TPTime, depth: number): SliceRect
-      |undefined {
-    const {
-      visibleTimeScale: timeScale,
-      visibleWindowTime,
-      windowSpan,
-    } = globals.frontendLocalState;
-
+  getSliceRect(
+      visibleTimeScale: TimeScale, visibleWindowTime: HighPrecisionTimeSpan,
+      windowSpan: PxSpan, tStart: TPTime, tEnd: TPTime,
+      depth: number): SliceRect|undefined {
     const pxEnd = windowSpan.end;
-    const left = Math.max(timeScale.tpTimeToPx(tStart), 0);
-    const right = Math.min(timeScale.tpTimeToPx(tEnd), pxEnd);
+    const left = Math.max(visibleTimeScale.tpTimeToPx(tStart), 0);
+    const right = Math.min(visibleTimeScale.tpTimeToPx(tEnd), pxEnd);
 
     const visible =
         !(visibleWindowTime.start.gt(tEnd) || visibleWindowTime.end.lt(tStart));