--
-- Copyright 2020 The Android Open Source Project
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     https://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

SELECT RUN_METRIC('chrome/scroll_jank.sql');

-- Below we want to collect TouchMoves and figure out if they blocked any
-- GestureScrollUpdates. This table gets the TouchMove slice and joins it with
-- the data from the first flow event for that TouchMove.
DROP TABLE IF EXISTS touch_move_and_begin_flow;
CREATE PERFETTO TABLE touch_move_and_begin_flow AS
SELECT
  flow.begin_flow_id,
  flow.begin_flow_ts,
  flow.begin_flow_track_id,
  move.*
FROM (
    SELECT
      EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
      *
    FROM slice move
    WHERE name = "InputLatency::TouchMove"
  ) move JOIN (
    SELECT
      MIN(id) AS begin_flow_id,
      track_id AS begin_flow_track_id,
      ts AS begin_flow_ts,
      EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id")
      AS begin_flow_trace_id
    FROM slice
    WHERE
      name = "LatencyInfo.Flow"
      AND EXTRACT_ARG(arg_set_id, "chrome_latency_info.step") IS NULL
    GROUP BY begin_flow_trace_id
  ) flow ON flow.begin_flow_trace_id = move.trace_id;

-- Now we take the TouchMove and beginning flow event and figure out if there
-- is an end flow event on the same browser track_id. This will allow us to see
-- if it was blocking because if they share the same parent stack then they
-- weren't blocking.
DROP TABLE IF EXISTS touch_move_begin_and_end_flow;
CREATE PERFETTO TABLE touch_move_begin_and_end_flow AS
SELECT
  flow.end_flow_id,
  flow.end_flow_ts,
  flow.end_flow_track_id,
  move.*
FROM touch_move_and_begin_flow move LEFT JOIN (
    SELECT
      MAX(id) AS end_flow_id,
      ts AS end_flow_ts,
      track_id AS end_flow_track_id,
      EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id")
      AS end_flow_trace_id
    FROM slice
    WHERE
      name = "LatencyInfo.Flow"
      AND EXTRACT_ARG(arg_set_id, "chrome_latency_info.step") IS NULL
    GROUP BY end_flow_trace_id
  ) flow ON
    flow.end_flow_trace_id = move.trace_id
    AND move.begin_flow_track_id = flow.end_flow_track_id
    AND flow.end_flow_id != move.begin_flow_id
WHERE flow.end_flow_id IS NOT NULL;

-- Now that we have the begin and the end we need to find the parent stack of
-- both. If the end didn't happen on the browser (end is NULL), then we can
-- ignore it because it couldn't have generated a GestureScrollUpdate.
DROP TABLE IF EXISTS touch_move_with_ancestor;
CREATE PERFETTO TABLE touch_move_with_ancestor AS
SELECT
  begin.id AS begin_ancestor_id,
  end.id AS end_ancestor_id,
  end.ts AS end_ancestor_ts,
  end.dur AS end_ancestor_dur,
  end.track_id AS end_ancestor_track_id,
  move.*
FROM
  touch_move_begin_and_end_flow move JOIN
  ancestor_slice(begin_flow_id) begin ON begin.depth = 0 LEFT JOIN
  ancestor_slice(end_flow_id) end ON end.depth = 0;

-- Now take the parent stack for the end and find if a GestureScrollUpdate was
-- launched that share the same parent as the end flow event for the TouchMove.
-- This is the GestureScrollUpdate that the TouchMove blocked (or didn't block)
-- depending on if the begin flow event is in the same stack.
DROP TABLE IF EXISTS blocking_touch_move_with_scroll_update;
CREATE PERFETTO TABLE blocking_touch_move_with_scroll_update AS
SELECT
  move.begin_ancestor_id != move.end_ancestor_id AS blocking_touch_move,
  scroll.scroll_begin_flow_id,
  scroll.scroll_begin_flow_trace_id,
  scroll.scroll_id,
  move.*
FROM touch_move_with_ancestor move LEFT JOIN (
  SELECT in_flow.*, in_scroll.scroll_id FROM (
    SELECT
      MIN(slice.id) AS scroll_begin_flow_id,
      slice.ts AS scroll_begin_flow_ts,
      slice.track_id AS scroll_begin_flow_track_id,
      EXTRACT_ARG(slice.arg_set_id, "chrome_latency_info.trace_id")
      AS scroll_begin_flow_trace_id,
      ancestor.id AS scroll_begin_flow_ancestor_id
    FROM
      slice LEFT JOIN
      ancestor_slice(slice.id) AS ancestor ON ancestor.depth = 0
    WHERE
      slice.name = "LatencyInfo.Flow"
      AND EXTRACT_ARG(slice.arg_set_id, "chrome_latency_info.step") IS NULL
    GROUP BY scroll_begin_flow_trace_id
  ) in_flow JOIN (
    SELECT
      id AS scroll_id,
      EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id")
      AS scroll_trace_id
    FROM slice in_scroll
    WHERE
      name = "InputLatency::GestureScrollUpdate"
      AND dur != -1
      AND NOT EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced")
  ) in_scroll ON
    in_scroll.scroll_trace_id = in_flow.scroll_begin_flow_trace_id
) scroll ON
  scroll.scroll_begin_flow_track_id = move.end_ancestor_track_id
  AND scroll.scroll_begin_flow_ancestor_id = move.end_ancestor_id
  AND scroll.scroll_begin_flow_ts > move.end_ancestor_ts
  AND scroll.scroll_begin_flow_ts < move.end_ancestor_ts + move.end_ancestor_dur
  AND scroll.scroll_begin_flow_id > move.end_ancestor_id
WHERE scroll.scroll_id IS NOT NULL;

-- Now filter out any TouchMoves that weren't during a complete scroll. Most of
-- the other ones will be null anyway since they won't have
-- GestureScrollUpdates.
DROP VIEW IF EXISTS scroll_jank_cause_blocking_touch_move;
CREATE PERFETTO VIEW scroll_jank_cause_blocking_touch_move AS
SELECT
  id,
  ts,
  dur,
  track_id,
  blocking_touch_move,
  scroll_id
FROM joined_scroll_begin_and_end begin_and_end JOIN (
    SELECT
      *
    FROM blocking_touch_move_with_scroll_update
  ) touch ON
    touch.ts <= begin_and_end.end_ts
    AND touch.ts > begin_and_end.begin_ts + begin_and_end.begin_dur
    AND touch.trace_id > begin_and_end.begin_trace_id
    AND touch.trace_id < begin_and_end.end_trace_id;
