summaryrefslogtreecommitdiff
path: root/chromium/components/viz/service/hit_test/hit_test_manager.cc
blob: 205186d39e6cf20f94213050a97829914ad88670 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/viz/service/hit_test/hit_test_manager.h"

#include <utility>
#include <vector>

#include "components/viz/common/hit_test/aggregated_hit_test_region.h"
#include "components/viz/service/hit_test/hit_test_aggregator.h"
#include "components/viz/service/surfaces/latest_local_surface_id_lookup_delegate.h"
#include "components/viz/service/surfaces/surface.h"

namespace viz {

namespace {
// TODO(gklassen): Review and select appropriate sizes based on
// telemetry / UMA.
constexpr uint32_t kMaxRegionsPerSurface = 1024;

// Whenever a hit test region is marked as kHitTestAsk there must be a reason
// for async hit test and vice versa.
bool FlagsAndAsyncReasonsMatch(uint32_t flags,
                               uint32_t async_hit_test_reasons) {
  if (flags & kHitTestAsk)
    return async_hit_test_reasons != kNotAsyncHitTest;
  return async_hit_test_reasons == kNotAsyncHitTest;
}

}  // namespace

HitTestManager::HitTestManager(SurfaceManager* surface_manager)
    : surface_manager_(surface_manager) {}

HitTestManager::~HitTestManager() = default;

bool HitTestManager::OnSurfaceDamaged(const SurfaceId& surface_id,
                                      const BeginFrameAck& ack) {
  return false;
}

void HitTestManager::OnSurfaceDestroyed(const SurfaceId& surface_id) {
  hit_test_region_lists_.erase(surface_id);
}

void HitTestManager::OnSurfaceActivated(const SurfaceId& surface_id) {
  // When a Surface is activated we can confidently remove all
  // associated HitTestRegionList objects with an older frame_index.
  auto search = hit_test_region_lists_.find(surface_id);
  if (search == hit_test_region_lists_.end())
    return;

  Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
  DCHECK(surface);
  uint64_t frame_index = surface->GetActiveFrameIndex();

  auto& frame_index_map = search->second;
  for (auto it = frame_index_map.begin(); it != frame_index_map.end();) {
    if (it->first != frame_index)
      it = frame_index_map.erase(it);
    else
      ++it;
  }
}

void HitTestManager::SubmitHitTestRegionList(
    const SurfaceId& surface_id,
    const uint64_t frame_index,
    absl::optional<HitTestRegionList> hit_test_region_list) {
  if (!hit_test_region_list) {
    auto& frame_index_map = hit_test_region_lists_[surface_id];
    if (!frame_index_map.empty()) {
      // We will reuse the last submitted hit-test data.
      uint64_t last_frame_index = frame_index_map.rbegin()->first;

      HitTestRegionList last_hit_test_region_list =
          std::move(frame_index_map[last_frame_index]);

      frame_index_map[frame_index] = std::move(last_hit_test_region_list);
      frame_index_map.erase(last_frame_index);
    }
    return;
  }
  if (!ValidateHitTestRegionList(surface_id, &*hit_test_region_list))
    return;
  ++submit_hit_test_region_list_index_;

  // TODO(gklassen): Runtime validation that hit_test_region_list is valid.
  // TODO(gklassen): Inform FrameSink that the hit_test_region_list is invalid.
  // TODO(gklassen): FrameSink needs to inform the host of a difficult renderer.
  hit_test_region_lists_[surface_id][frame_index] =
      std::move(*hit_test_region_list);
}

const HitTestRegionList* HitTestManager::GetActiveHitTestRegionList(
    LatestLocalSurfaceIdLookupDelegate* delegate,
    const FrameSinkId& frame_sink_id,
    uint64_t* store_active_frame_index) const {
  if (!delegate)
    return nullptr;

  LocalSurfaceId local_surface_id =
      delegate->GetSurfaceAtAggregation(frame_sink_id);
  if (!local_surface_id.is_valid())
    return nullptr;

  SurfaceId surface_id(frame_sink_id, local_surface_id);
  auto search = hit_test_region_lists_.find(surface_id);
  if (search == hit_test_region_lists_.end())
    return nullptr;

  Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
  DCHECK(surface);
  uint64_t frame_index = surface->GetActiveFrameIndex();
  if (store_active_frame_index)
    *store_active_frame_index = frame_index;

  auto& frame_index_map = search->second;
  auto search2 = frame_index_map.find(frame_index);
  if (search2 == frame_index_map.end())
    return nullptr;

  return &search2->second;
}

int64_t HitTestManager::GetTraceId(const SurfaceId& id) const {
  Surface* surface = surface_manager_->GetSurfaceForId(id);
  return surface->GetActiveFrameMetadata().begin_frame_ack.trace_id;
}

bool HitTestManager::ValidateHitTestRegionList(
    const SurfaceId& surface_id,
    HitTestRegionList* hit_test_region_list) {
  if (hit_test_region_list->regions.size() > kMaxRegionsPerSurface)
    return false;
  if (!FlagsAndAsyncReasonsMatch(
          hit_test_region_list->flags,
          hit_test_region_list->async_hit_test_reasons)) {
    return false;
  }
  for (auto& region : hit_test_region_list->regions) {
    // TODO(gklassen): Ensure that |region->frame_sink_id| is a child of
    // |frame_sink_id|.
    if (region.frame_sink_id.client_id() == 0) {
      region.frame_sink_id = FrameSinkId(surface_id.frame_sink_id().client_id(),
                                         region.frame_sink_id.sink_id());
    }
    if (!FlagsAndAsyncReasonsMatch(region.flags, region.async_hit_test_reasons))
      return false;
  }
  return true;
}

}  // namespace viz