1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/viz/service/hit_test/hit_test_aggregator.h"
6
7 #include "components/viz/common/hit_test/aggregated_hit_test_region.h"
8 #include "components/viz/service/surfaces/latest_local_surface_id_lookup_delegate.h"
9 #include "components/viz/service/surfaces/surface.h"
10
11 namespace viz {
12
13 namespace {
14 // TODO(gklassen): Review and select appropriate sizes based on
15 // telemetry / UMA.
16 constexpr uint32_t kMaxRegionsPerSurface = 1024;
17
18 // Whenever a hit test region is marked as kHitTestAsk there must be a reason
19 // for async hit test and vice versa.
FlagsAndAsyncReasonsMatch(uint32_t flags,uint32_t async_hit_test_reasons)20 bool FlagsAndAsyncReasonsMatch(uint32_t flags,
21 uint32_t async_hit_test_reasons) {
22 if (flags & kHitTestAsk)
23 return async_hit_test_reasons != kNotAsyncHitTest;
24 return async_hit_test_reasons == kNotAsyncHitTest;
25 }
26
27 } // namespace
28
29 HitTestManager::HitTestAsyncQueriedDebugRegion::
30 HitTestAsyncQueriedDebugRegion() = default;
HitTestAsyncQueriedDebugRegion(base::flat_set<FrameSinkId> regions)31 HitTestManager::HitTestAsyncQueriedDebugRegion::HitTestAsyncQueriedDebugRegion(
32 base::flat_set<FrameSinkId> regions)
33 : regions(std::move(regions)) {}
34 HitTestManager::HitTestAsyncQueriedDebugRegion::
35 ~HitTestAsyncQueriedDebugRegion() = default;
36
37 HitTestManager::HitTestAsyncQueriedDebugRegion::HitTestAsyncQueriedDebugRegion(
38 HitTestAsyncQueriedDebugRegion&&) = default;
39 HitTestManager::HitTestAsyncQueriedDebugRegion&
40 HitTestManager::HitTestAsyncQueriedDebugRegion::operator=(
41 HitTestAsyncQueriedDebugRegion&&) = default;
42
HitTestManager(SurfaceManager * surface_manager)43 HitTestManager::HitTestManager(SurfaceManager* surface_manager)
44 : surface_manager_(surface_manager) {}
45
46 HitTestManager::~HitTestManager() = default;
47
OnSurfaceDamaged(const SurfaceId & surface_id,const BeginFrameAck & ack)48 bool HitTestManager::OnSurfaceDamaged(const SurfaceId& surface_id,
49 const BeginFrameAck& ack) {
50 return false;
51 }
52
OnSurfaceDestroyed(const SurfaceId & surface_id)53 void HitTestManager::OnSurfaceDestroyed(const SurfaceId& surface_id) {
54 hit_test_region_lists_.erase(surface_id);
55 }
56
OnSurfaceActivated(const SurfaceId & surface_id)57 void HitTestManager::OnSurfaceActivated(const SurfaceId& surface_id) {
58 // When a Surface is activated we can confidently remove all
59 // associated HitTestRegionList objects with an older frame_index.
60 auto search = hit_test_region_lists_.find(surface_id);
61 if (search == hit_test_region_lists_.end())
62 return;
63
64 Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
65 DCHECK(surface);
66 uint64_t frame_index = surface->GetActiveFrameIndex();
67
68 auto& frame_index_map = search->second;
69 for (auto it = frame_index_map.begin(); it != frame_index_map.end();) {
70 if (it->first != frame_index)
71 it = frame_index_map.erase(it);
72 else
73 ++it;
74 }
75 }
76
SubmitHitTestRegionList(const SurfaceId & surface_id,const uint64_t frame_index,base::Optional<HitTestRegionList> hit_test_region_list)77 void HitTestManager::SubmitHitTestRegionList(
78 const SurfaceId& surface_id,
79 const uint64_t frame_index,
80 base::Optional<HitTestRegionList> hit_test_region_list) {
81 if (!hit_test_region_list) {
82 auto& frame_index_map = hit_test_region_lists_[surface_id];
83 if (!frame_index_map.empty()) {
84 // We will reuse the last submitted hit-test data.
85 uint64_t last_frame_index = frame_index_map.rbegin()->first;
86
87 HitTestRegionList last_hit_test_region_list =
88 std::move(frame_index_map[last_frame_index]);
89
90 frame_index_map[frame_index] = std::move(last_hit_test_region_list);
91 frame_index_map.erase(last_frame_index);
92 }
93 return;
94 }
95 if (!ValidateHitTestRegionList(surface_id, &*hit_test_region_list))
96 return;
97 ++submit_hit_test_region_list_index_;
98
99 // TODO(gklassen): Runtime validation that hit_test_region_list is valid.
100 // TODO(gklassen): Inform FrameSink that the hit_test_region_list is invalid.
101 // TODO(gklassen): FrameSink needs to inform the host of a difficult renderer.
102 hit_test_region_lists_[surface_id][frame_index] =
103 std::move(*hit_test_region_list);
104 }
105
GetActiveHitTestRegionList(LatestLocalSurfaceIdLookupDelegate * delegate,const FrameSinkId & frame_sink_id,uint64_t * store_active_frame_index) const106 const HitTestRegionList* HitTestManager::GetActiveHitTestRegionList(
107 LatestLocalSurfaceIdLookupDelegate* delegate,
108 const FrameSinkId& frame_sink_id,
109 uint64_t* store_active_frame_index) const {
110 if (!delegate)
111 return nullptr;
112
113 LocalSurfaceId local_surface_id =
114 delegate->GetSurfaceAtAggregation(frame_sink_id);
115 if (!local_surface_id.is_valid())
116 return nullptr;
117
118 SurfaceId surface_id(frame_sink_id, local_surface_id);
119 auto search = hit_test_region_lists_.find(surface_id);
120 if (search == hit_test_region_lists_.end())
121 return nullptr;
122
123 Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
124 DCHECK(surface);
125 uint64_t frame_index = surface->GetActiveFrameIndex();
126 if (store_active_frame_index)
127 *store_active_frame_index = frame_index;
128
129 auto& frame_index_map = search->second;
130 auto search2 = frame_index_map.find(frame_index);
131 if (search2 == frame_index_map.end())
132 return nullptr;
133
134 return &search2->second;
135 }
136
GetTraceId(const SurfaceId & id) const137 int64_t HitTestManager::GetTraceId(const SurfaceId& id) const {
138 Surface* surface = surface_manager_->GetSurfaceForId(id);
139 return surface->GetActiveFrame().metadata.begin_frame_ack.trace_id;
140 }
141
142 const base::flat_set<FrameSinkId>*
GetHitTestAsyncQueriedDebugRegions(const FrameSinkId & root_frame_sink_id) const143 HitTestManager::GetHitTestAsyncQueriedDebugRegions(
144 const FrameSinkId& root_frame_sink_id) const {
145 auto it = hit_test_async_queried_debug_regions_.find(root_frame_sink_id);
146 if (it == hit_test_async_queried_debug_regions_.end() ||
147 it->second.timer.Elapsed().InMilliseconds() > 2000) {
148 return nullptr;
149 }
150 return &it->second.regions;
151 }
152
SetHitTestAsyncQueriedDebugRegions(const FrameSinkId & root_frame_sink_id,const std::vector<FrameSinkId> & hit_test_async_queried_debug_queue)153 void HitTestManager::SetHitTestAsyncQueriedDebugRegions(
154 const FrameSinkId& root_frame_sink_id,
155 const std::vector<FrameSinkId>& hit_test_async_queried_debug_queue) {
156 hit_test_async_queried_debug_regions_[root_frame_sink_id] =
157 HitTestAsyncQueriedDebugRegion(base::flat_set<FrameSinkId>(
158 hit_test_async_queried_debug_queue.begin(),
159 hit_test_async_queried_debug_queue.end()));
160 }
161
ValidateHitTestRegionList(const SurfaceId & surface_id,HitTestRegionList * hit_test_region_list)162 bool HitTestManager::ValidateHitTestRegionList(
163 const SurfaceId& surface_id,
164 HitTestRegionList* hit_test_region_list) {
165 if (hit_test_region_list->regions.size() > kMaxRegionsPerSurface)
166 return false;
167 if (!FlagsAndAsyncReasonsMatch(
168 hit_test_region_list->flags,
169 hit_test_region_list->async_hit_test_reasons)) {
170 return false;
171 }
172 for (auto& region : hit_test_region_list->regions) {
173 // TODO(gklassen): Ensure that |region->frame_sink_id| is a child of
174 // |frame_sink_id|.
175 if (region.frame_sink_id.client_id() == 0) {
176 region.frame_sink_id = FrameSinkId(surface_id.frame_sink_id().client_id(),
177 region.frame_sink_id.sink_id());
178 }
179 if (!FlagsAndAsyncReasonsMatch(region.flags, region.async_hit_test_reasons))
180 return false;
181 }
182 return true;
183 }
184
185 } // namespace viz
186