blob: 5d616204a6a3b09603afb9eb84cc96a4fb304da0 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// 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