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

-- This file creates two public views:
--     - chrome_scroll_input_offsets and
--     - chrome_presented_scroll_offsets
--
-- These views store the pixel deltas and offsets for (respectively) all chrome
-- scroll inputs (coalesced and not coalesced), and for chrome presented frames
-- (not coalesced), along with the associated timestamp, and id.
--
-- Raw deltas are recorded as changes in pixel positions along the y-axis of a
-- screen, and are scaled to the viewport size. The corresponding trace event
-- for this is TranslateAndScaleWebInputEvent. These are the deltas for all
-- chrome scroll inputs.
--
-- For presented frames, the delta is calculated from the visual offset,
-- recorded once the input has been processed, in the
-- InputHandlerProxy::HandleGestureScrollUpdate_Result event. These values are
-- also scaled to the screen size.
--
-- Offsets are calculated by summing all of the deltas, ordered by timestamp.
-- For a given input/frame, the offset is the sum of its corresponding delta and
-- all previous deltas.
--
--
-- All values required for calculating deltas and offsets are recorded at
-- various stages of input processing, and are unified by a single
-- scroll_update_id value, recorded as scroll_deltas.trace_id in each event.

INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_v3;

-- Non-coalesced scroll update events and their timestamps.
CREATE PERFETTO VIEW _non_coalesced_scrolls AS
SELECT
  scroll_update_id,
  ts
FROM chrome_gesture_scroll_updates
WHERE is_coalesced = False;

-- All (coalesced and non-coalesced) vertical scrolling deltas and their
-- associated scroll ids. Delta values are recorded after being scaled to the
-- device's screen size in the TranslateAndScaleWebInputEvent trace event. In
-- this trace event, the deltas recorded represent the true (read "original")
-- values that the Browser receives from Android, and the only processing is
-- scaling and translation.
CREATE PERFETTO TABLE _scroll_deltas AS
SELECT
  EXTRACT_ARG(arg_set_id, 'scroll_deltas.trace_id') AS scroll_update_id,
  EXTRACT_ARG(arg_set_id, 'scroll_deltas.original_delta_y') AS delta_y,
  EXTRACT_ARG(arg_set_id, 'scroll_deltas.original_delta_y') IS NOT NULL AS is_coalesced
FROM slice
WHERE name = "TranslateAndScaleWebInputEvent";

-- Associate the raw (original) deltas (_scroll_deltas) with the
-- corresponding non-coalesced scroll updates
-- (_non_coalesced_scroll_updates) to get the timestamp of the event
-- those deltas. This allows for ordering delta recordings to track them over
-- time.
CREATE PERFETTO VIEW _non_coalesced_deltas AS
SELECT
  scroll_update_id,
  ts,
  delta_y
FROM _non_coalesced_scrolls
INNER JOIN _scroll_deltas
  USING (scroll_update_id);

-- Selecting information scroll update events that have been coalesced,
-- including timestamp and the specific event (scroll update id) it was
-- coalesced into. Recordings of deltas will need to be associated with the
-- timestamp of the scroll update they were coalesced into.
CREATE PERFETTO TABLE _scroll_update_coalesce_info AS
SELECT
  ts,
  EXTRACT_ARG(arg_set_id, 'scroll_deltas.coalesced_to_trace_id') AS coalesced_to_scroll_update_id,
  EXTRACT_ARG(arg_set_id, 'scroll_deltas.trace_id') AS scroll_update_id
FROM slice
WHERE name = "WebCoalescedInputEvent::CoalesceWith" AND
  coalesced_to_scroll_update_id IS NOT NULL;

-- Associate the raw (original) deltas (_scroll_deltas) with the
-- corresponding coalesced scroll updates (_scroll_update_coalesce_info)
-- to get the timestamp of the event those deltas were coalesced into. This
-- allows us to get the scaled coordinates for all of the input events
-- (original input coordinates can't be used due to scaling).
CREATE PERFETTO VIEW _coalesced_deltas AS
SELECT
  _scroll_update_coalesce_info.coalesced_to_scroll_update_id AS scroll_update_id,
  ts,
  _scroll_deltas.delta_y AS delta_y,
  TRUE AS is_coalesced
FROM _scroll_update_coalesce_info
LEFT JOIN _scroll_deltas
  USING (scroll_update_id);

-- All of the presented frame scroll update ids.
CREATE PERFETTO VIEW chrome_deltas_presented_frame_scroll_update_ids(
  -- A scroll update id that was included in the presented frame.
  -- There may be zero, one, or more.
  scroll_update_id INT,
  -- Slice id
  id INT
) AS
SELECT
  args.int_value AS scroll_update_id,
  slice.id
FROM args
LEFT JOIN slice
  USING (arg_set_id)
WHERE slice.name = 'PresentedFrameInformation'
AND args.flat_key GLOB 'scroll_deltas.trace_ids_in_gpu_frame*';;

-- When every GestureScrollUpdate event is processed, the offset set by the
-- compositor is recorded. This offset is scaled to the device screen size, and
-- can be used to calculate deltas.
CREATE PERFETTO VIEW _presented_frame_offsets AS
SELECT
  EXTRACT_ARG(arg_set_id, 'scroll_deltas.trace_id') AS scroll_update_id,
  EXTRACT_ARG(arg_set_id, 'scroll_deltas.visual_offset_y') AS visual_offset_y
FROM slice
WHERE name = 'InputHandlerProxy::HandleGestureScrollUpdate_Result';

-- The raw coordinates and pixel offsets for all input events which were part of
-- a scroll. This includes input events that were converted to scroll events
-- which were presented (_non_coalesced_scrolls) and scroll events which
-- were coalesced (_coalesced_deltas).
CREATE PERFETTO TABLE chrome_scroll_input_offsets(
  -- Trace id associated with the scroll.
  scroll_update_id INT,
  -- Timestamp the of the scroll input event.
  ts INT,
  -- The delta in raw coordinates between this scroll update event and the previous.
  delta_y INT,
  -- The pixel offset of this scroll update event compared to the previous one.
  offset_y INT
) AS
-- First collect all coalesced and non-coalesced deltas so that the offsets
-- can be calculated from them in order of timestamp.
WITH all_deltas AS (
  SELECT
    scroll_update_id,
    ts,
    delta_y
  FROM _non_coalesced_deltas
  WHERE delta_y IS NOT NULL
  UNION
  SELECT
    scroll_update_id,
    ts,
    delta_y
  FROM _coalesced_deltas
  WHERE delta_y IS NOT NULL
  ORDER BY scroll_update_id, ts)
SELECT
  scroll_update_id,
  ts,
  delta_y,
  SUM(IFNULL(delta_y, 0)) OVER (
    ORDER BY scroll_update_id, ts
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS offset_y
FROM all_deltas;

-- Calculate the total visual offset for all presented frames (non-coalesced
-- scroll updates) that have raw deltas recorded. These visual offsets
-- correspond with the inverse of the deltas for the presented frame.
CREATE PERFETTO VIEW _preprocessed_presented_frame_offsets AS
SELECT
  chrome_full_frame_view.scroll_update_id,
  chrome_full_frame_view.presentation_timestamp AS ts,
  chrome_deltas_presented_frame_scroll_update_ids.id,
  _presented_frame_offsets.visual_offset_y -
    LAG(_presented_frame_offsets.visual_offset_y)
    OVER (ORDER BY chrome_full_frame_view.presentation_timestamp)
      AS presented_frame_visual_offset_y
FROM chrome_full_frame_view
LEFT JOIN _scroll_deltas
  USING (scroll_update_id)
LEFT JOIN chrome_deltas_presented_frame_scroll_update_ids
  USING (scroll_update_id)
LEFT JOIN _presented_frame_offsets
  USING (scroll_update_id)
WHERE _scroll_deltas.delta_y IS NOT NULL;

-- The scrolling offsets for the actual (applied) scroll events. These are not
-- necessarily inclusive of all user scroll events, rather those scroll events
-- that are actually processed.
CREATE PERFETTO TABLE chrome_presented_scroll_offsets(
  -- Trace Id associated with the scroll.
  scroll_update_id INT,
  -- Presentation timestamp.
  ts INT,
  -- The delta in coordinates as processed by Chrome between this scroll update
  -- event and the previous.
  delta_y INT,
  -- The pixel offset of this scroll update (the presented frame) compared to
  -- the previous one.
  offset_y INT
) AS
WITH all_deltas AS (
  SELECT
    scroll_update_id,
    id,
    MAX(ts) AS ts,
    SUM(presented_frame_visual_offset_y) * -1 AS delta_y
  FROM _preprocessed_presented_frame_offsets
  GROUP BY id
  ORDER BY ts)
SELECT
  scroll_update_id,
  ts,
  delta_y,
  SUM(IFNULL(delta_y, 0)) OVER (
    ORDER BY scroll_update_id, ts
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS offset_y
FROM all_deltas;
