-- Copyright 2024 The Chromium Authors
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.

-- Finds the start timestamp for a given slice's descendant with a given name.
-- If there are multiple descendants with a given name, the function will return
-- the first one, so it's most useful when working with a timeline broken down
-- into phases, where each subphase can happen only once.
CREATE PERFETTO FUNCTION _descendant_slice_begin(
  -- Id of the parent slice.
  parent_id LONG,
  -- Name of the child with the desired start TS.
  child_name STRING
)
-- Start timestamp of the child or NULL if it doesn't exist.
RETURNS LONG AS
SELECT s.ts
FROM descendant_slice($parent_id) s
WHERE s.name GLOB $child_name
LIMIT 1;

-- Finds the end timestamp for a given slice's descendant with a given name.
-- If there are multiple descendants with a given name, the function will return
-- the first one, so it's most useful when working with a timeline broken down
-- into phases, where each subphase can happen only once.
CREATE PERFETTO FUNCTION _descendant_slice_end(
  -- Id of the parent slice.
  parent_id LONG,
  -- Name of the child with the desired end TS.
  child_name STRING
)
-- End timestamp of the child or NULL if it doesn't exist.
RETURNS LONG AS
SELECT
  CASE WHEN s.dur
    IS NOT -1 THEN s.ts + s.dur
    ELSE NULL
  END
FROM descendant_slice($parent_id) s
WHERE s.name GLOB $child_name
LIMIT 1;

-- Checks if slice has a descendant with provided name.
CREATE PERFETTO FUNCTION _has_descendant_slice_with_name(
  -- Id of the slice to check descendants of.
  id LONG,
  -- Name of potential descendant slice.
  descendant_name STRING
)
-- Whether `descendant_name` is a name of an descendant slice.
RETURNS BOOL AS
SELECT EXISTS(
  SELECT 1
  FROM descendant_slice($id)
  WHERE name = $descendant_name
  LIMIT 1
);

-- Returns the presentation timestamp for a given EventLatency slice.
-- This is either the end of
-- SwapEndToPresentationCompositorFrame (if it exists),
-- the end of LatchToPresentation (if it exists),
-- the end of SwapStartToPresentation (if it exists),
-- or the end of LatchToSwapEnd (workaround in older Chrome versions).
CREATE PERFETTO FUNCTION _get_presentation_timestamp(
  -- The slice id which we need the presentation timestamp for.
  id LONG
)
RETURNS LONG AS
SELECT
  COALESCE(_descendant_slice_end(id, 'SwapEndToPresentationCompositorFrame'),
    _descendant_slice_end(id, '*ToPresentation'),
    _descendant_slice_end(id, 'LatchToSwapEnd'))
FROM slice WHERE $id = id;

-- All EventLatency slices.
CREATE PERFETTO TABLE chrome_event_latencies(
  -- Slice Id for the EventLatency scroll event.
  id LONG,
  -- Slice name.
  name STRING,
  -- The start timestamp of the scroll.
  ts TIMESTAMP,
  -- The duration of the scroll.
  dur DURATION,
  -- The id of the scroll update event.
  scroll_update_id LONG,
  -- Whether this input event was presented.
  is_presented BOOL,
  -- EventLatency event type.
  event_type STRING,
  -- Perfetto track this slice is found on.
  track_id LONG,
  -- Vsync interval (in milliseconds).
  vsync_interval_ms DOUBLE,
  -- Whether the corresponding frame is janky.
  is_janky_scrolled_frame BOOL,
  -- Timestamp of the BufferAvailableToBufferReady substage.
  buffer_available_timestamp LONG,
  -- Timestamp of the BufferReadyToLatch substage.
  buffer_ready_timestamp LONG,
  -- Timestamp of the LatchToSwapEnd substage.
  latch_timestamp LONG,
  -- Timestamp of the SwapEndToPresentationCompositorFrame substage.
  swap_end_timestamp LONG,
  -- Frame presentation timestamp aka the timestamp of the
  -- SwapEndToPresentationCompositorFrame substage.
  -- TODO(b/341047059): temporarily use LatchToSwapEnd as a workaround if
  -- SwapEndToPresentationCompositorFrame is missing due to b/247542163.
  presentation_timestamp LONG
) AS
SELECT
  slice.id,
  slice.name,
  slice.ts,
  slice.dur,
  EXTRACT_arg(arg_set_id, 'event_latency.event_latency_id') AS scroll_update_id,
  _has_descendant_slice_with_name(
    slice.id,
    'SubmitCompositorFrameToPresentationCompositorFrame')
    AS is_presented,
  EXTRACT_ARG(arg_set_id, 'event_latency.event_type') AS event_type,
  slice.track_id,
  EXTRACT_ARG(arg_set_id, 'event_latency.vsync_interval_ms')
    AS vsync_interval_ms,
  COALESCE(EXTRACT_ARG(arg_set_id, 'event_latency.is_janky_scrolled_frame'), 0)
    AS is_janky_scrolled_frame,
  _descendant_slice_begin(slice.id, 'BufferAvailableToBufferReady')
    AS buffer_available_timestamp,
  _descendant_slice_begin(slice.id, 'BufferReadyToLatch')
    AS buffer_ready_timestamp,
  _descendant_slice_begin(slice.id, 'LatchToSwapEnd') AS latch_timestamp,
  _descendant_slice_begin(slice.id, 'SwapEndToPresentationCompositorFrame')
    AS swap_end_timestamp,
  _get_presentation_timestamp(slice.id) AS presentation_timestamp
FROM slice
WHERE name = 'EventLatency';

-- All EventLatency slices that are relevant to scrolling, including presented
-- pinches. Materialized to reduce how many times we query slice.
CREATE PERFETTO TABLE _gesture_scroll_events_no_scroll_id
AS
SELECT
  name,
  ts,
  dur,
  id,
  scroll_update_id,
  is_presented,
  _get_presentation_timestamp(chrome_event_latencies.id)
  AS presentation_timestamp,
  event_type,
  track_id
FROM chrome_event_latencies
WHERE (
  event_type GLOB '*GESTURE_SCROLL*'
  -- Pinches are only relevant if the frame was presented.
  OR (event_type GLOB '*GESTURE_PINCH_UPDATE'
    AND _has_descendant_slice_with_name(
      id,
      'SubmitCompositorFrameToPresentationCompositorFrame')
  )
);

-- Extracts scroll id for the EventLatency slice at `ts`.
CREATE PERFETTO FUNCTION chrome_get_most_recent_scroll_begin_id(
  -- Timestamp of the EventLatency slice to get the scroll id for.
  ts TIMESTAMP)
-- The event_latency_id of the EventLatency slice with the type
-- GESTURE_SCROLL_BEGIN that is the closest to `ts`.
RETURNS LONG AS
SELECT scroll_update_id
FROM _gesture_scroll_events_no_scroll_id
WHERE event_type = 'GESTURE_SCROLL_BEGIN'
AND ts<=$ts
ORDER BY ts DESC
LIMIT 1;

-- All scroll-related events (frames) including gesture scroll updates, begins
-- and ends with respective scroll ids and start/end timestamps, regardless of
-- being presented. This includes pinches that were presented. See b/315761896
-- for context on pinches.
CREATE PERFETTO TABLE chrome_gesture_scroll_events(
  -- Slice Id for the EventLatency scroll event.
  id LONG,
  -- Slice name.
  name STRING,
  -- The start timestamp of the scroll.
  ts TIMESTAMP,
  -- The duration of the scroll.
  dur DURATION,
  -- The id of the scroll update event.
  scroll_update_id LONG,
  -- The id of the scroll.
  scroll_id LONG,
  -- Whether this input event was presented.
  is_presented BOOL,
  -- Frame presentation timestamp aka the timestamp of the
  -- SwapEndToPresentationCompositorFrame substage.
  -- TODO(b/341047059): temporarily use LatchToSwapEnd as a workaround if
  -- SwapEndToPresentationCompositorFrame is missing due to b/247542163.
  presentation_timestamp LONG,
  -- EventLatency event type.
  event_type STRING,
  -- Perfetto track this slice is found on.
  track_id LONG
) AS
SELECT
  id,
  name,
  ts,
  dur,
  scroll_update_id,
  chrome_get_most_recent_scroll_begin_id(ts) AS scroll_id,
  is_presented,
  presentation_timestamp,
  event_type,
  track_id
FROM _gesture_scroll_events_no_scroll_id;
