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