| // Copyright 2016 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/surfaces/surface_dependency_tracker.h" |
| |
| #include "build/build_config.h" |
| #include "components/viz/common/surfaces/surface_info.h" |
| #include "components/viz/service/surfaces/surface.h" |
| #include "components/viz/service/surfaces/surface_manager.h" |
| |
| namespace viz { |
| |
| SurfaceDependencyTracker::SurfaceDependencyTracker( |
| SurfaceManager* surface_manager) |
| : surface_manager_(surface_manager) {} |
| |
| SurfaceDependencyTracker::~SurfaceDependencyTracker() = default; |
| |
| void SurfaceDependencyTracker::TrackEmbedding(Surface* surface) { |
| // If |surface| is blocking on the arrival of a parent and the parent frame |
| // has not yet arrived then track this |surface|'s SurfaceId by FrameSinkId so |
| // that if a parent refers to it or a more recent surface, then |
| // SurfaceDependencyTracker reports back that a dependency has been added. |
| if (surface->block_activation_on_parent() && !surface->HasDependentFrame()) { |
| surfaces_blocked_on_parent_by_frame_sink_id_[surface->surface_id() |
| .frame_sink_id()] |
| .insert(surface->surface_id()); |
| } |
| } |
| |
| void SurfaceDependencyTracker::RequestSurfaceResolution(Surface* surface) { |
| DCHECK(surface->HasPendingFrame()); |
| |
| if (IsSurfaceLate(surface)) { |
| ActivateLateSurfaceSubtree(surface); |
| return; |
| } |
| |
| // Activation dependencies that aren't currently known to the surface manager |
| // or do not have an active CompositorFrame block this frame. |
| for (const SurfaceId& surface_id : surface->activation_dependencies()) { |
| Surface* dependency = surface_manager_->GetSurfaceForId(surface_id); |
| if (!dependency || !dependency->HasActiveFrame()) { |
| blocked_surfaces_from_dependency_[surface_id.frame_sink_id()].insert( |
| surface->surface_id()); |
| } |
| } |
| |
| UpdateSurfaceDeadline(surface); |
| } |
| |
| bool SurfaceDependencyTracker::HasSurfaceBlockedOn( |
| const SurfaceId& surface_id) const { |
| auto it = blocked_surfaces_from_dependency_.find(surface_id.frame_sink_id()); |
| if (it == blocked_surfaces_from_dependency_.end()) |
| return false; |
| |
| for (const SurfaceId& blocked_surface_by_id : it->second) { |
| Surface* blocked_surface = |
| surface_manager_->GetSurfaceForId(blocked_surface_by_id); |
| if (blocked_surface && blocked_surface->IsBlockedOn(surface_id)) |
| return true; |
| } |
| return false; |
| } |
| |
| void SurfaceDependencyTracker::OnSurfaceActivated(Surface* surface) { |
| if (!surface->late_activation_dependencies().empty()) |
| surfaces_with_missing_dependencies_.insert(surface->surface_id()); |
| else |
| surfaces_with_missing_dependencies_.erase(surface->surface_id()); |
| NotifySurfaceIdAvailable(surface->surface_id()); |
| // We treat an activation (by deadline) as being the equivalent of a parent |
| // embedding the surface. |
| OnSurfaceDependencyAdded(surface->surface_id()); |
| } |
| |
| void SurfaceDependencyTracker::OnSurfaceDependencyAdded( |
| const SurfaceId& surface_id) { |
| auto it = surfaces_blocked_on_parent_by_frame_sink_id_.find( |
| surface_id.frame_sink_id()); |
| if (it == surfaces_blocked_on_parent_by_frame_sink_id_.end()) |
| return; |
| |
| std::vector<SurfaceId> dependencies_to_notify; |
| |
| base::flat_set<SurfaceId>& blocked_surfaces = it->second; |
| for (auto iter = blocked_surfaces.begin(); iter != blocked_surfaces.end();) { |
| bool should_notify = |
| iter->local_surface_id() <= surface_id.local_surface_id(); |
| #if defined(OS_ANDROID) |
| // On Android we work around a throttling bug by also firing if the |
| // immediately preceding surface has a dependency added. |
| // TODO(https://crbug.com/898460): Solve this generally. |
| bool is_same_parent = |
| iter->local_surface_id().parent_sequence_number() == |
| surface_id.local_surface_id().parent_sequence_number(); |
| bool is_next_child = |
| iter->local_surface_id().child_sequence_number() == |
| surface_id.local_surface_id().child_sequence_number() + 1; |
| should_notify |= is_same_parent && is_next_child; |
| #endif |
| if (should_notify) { |
| dependencies_to_notify.push_back(*iter); |
| iter = blocked_surfaces.erase(iter); |
| } else { |
| ++iter; |
| } |
| } |
| |
| if (blocked_surfaces.empty()) |
| surfaces_blocked_on_parent_by_frame_sink_id_.erase(it); |
| |
| for (const SurfaceId& dependency : dependencies_to_notify) { |
| Surface* surface = surface_manager_->GetSurfaceForId(dependency); |
| if (surface) |
| surface->OnSurfaceDependencyAdded(); |
| } |
| } |
| |
| void SurfaceDependencyTracker::OnSurfaceDependenciesChanged( |
| Surface* surface, |
| const base::flat_set<FrameSinkId>& added_dependencies, |
| const base::flat_set<FrameSinkId>& removed_dependencies) { |
| // Update the |blocked_surfaces_from_dependency_| map with the changes in |
| // dependencies. |
| for (const FrameSinkId& frame_sink_id : added_dependencies) { |
| blocked_surfaces_from_dependency_[frame_sink_id].insert( |
| surface->surface_id()); |
| } |
| |
| for (const FrameSinkId& frame_sink_id : removed_dependencies) { |
| auto it = blocked_surfaces_from_dependency_.find(frame_sink_id); |
| if (it != blocked_surfaces_from_dependency_.end()) { |
| it->second.erase(surface->surface_id()); |
| if (it->second.empty()) |
| blocked_surfaces_from_dependency_.erase(it); |
| } |
| } |
| } |
| |
| void SurfaceDependencyTracker::OnSurfaceDiscarded(Surface* surface) { |
| surfaces_with_missing_dependencies_.erase(surface->surface_id()); |
| |
| base::flat_set<FrameSinkId> removed_dependencies; |
| for (const SurfaceId& surface_id : surface->activation_dependencies()) |
| removed_dependencies.insert(surface_id.frame_sink_id()); |
| |
| OnSurfaceDependenciesChanged(surface, {}, removed_dependencies); |
| |
| // Pretend that the discarded surface's SurfaceId is now available to |
| // unblock dependencies because we now know the surface will never activate. |
| NotifySurfaceIdAvailable(surface->surface_id()); |
| OnSurfaceDependencyAdded(surface->surface_id()); |
| } |
| |
| void SurfaceDependencyTracker::OnFrameSinkInvalidated( |
| const FrameSinkId& frame_sink_id) { |
| // We now know the frame sink will never generated any more frames, |
| // thus unblock all dependencies to any future surfaces. |
| NotifySurfaceIdAvailable(SurfaceId::MaxSequenceId(frame_sink_id)); |
| OnSurfaceDependencyAdded(SurfaceId::MaxSequenceId(frame_sink_id)); |
| } |
| |
| void SurfaceDependencyTracker::ActivateLateSurfaceSubtree(Surface* surface) { |
| DCHECK(surface->HasPendingFrame()); |
| |
| base::flat_set<SurfaceId> late_dependencies( |
| surface->activation_dependencies()); |
| for (const SurfaceId& surface_id : late_dependencies) { |
| Surface* dependency = surface_manager_->GetSurfaceForId(surface_id); |
| if (dependency && dependency->HasPendingFrame()) |
| ActivateLateSurfaceSubtree(dependency); |
| } |
| |
| surface->ActivatePendingFrameForDeadline(base::nullopt); |
| } |
| |
| void SurfaceDependencyTracker::UpdateSurfaceDeadline(Surface* surface) { |
| DCHECK(surface->HasPendingFrame()); |
| |
| // Inherit the deadline from the first parent blocked on this surface. |
| auto it = blocked_surfaces_from_dependency_.find( |
| surface->surface_id().frame_sink_id()); |
| if (it != blocked_surfaces_from_dependency_.end()) { |
| const base::flat_set<SurfaceId>& dependent_parent_ids = it->second; |
| for (const SurfaceId& parent_id : dependent_parent_ids) { |
| Surface* parent = surface_manager_->GetSurfaceForId(parent_id); |
| if (parent && parent->has_deadline() && |
| parent->activation_dependencies().count(surface->surface_id())) { |
| surface->InheritActivationDeadlineFrom(parent); |
| break; |
| } |
| } |
| } |
| |
| DCHECK(!surface_manager_->activation_deadline_in_frames() || |
| surface->has_deadline()); |
| |
| // Recursively propagate the newly set deadline to children. |
| base::flat_set<SurfaceId> activation_dependencies( |
| surface->activation_dependencies()); |
| for (const SurfaceId& surface_id : activation_dependencies) { |
| Surface* dependency = surface_manager_->GetSurfaceForId(surface_id); |
| if (dependency && dependency->HasPendingFrame()) |
| UpdateSurfaceDeadline(dependency); |
| } |
| } |
| |
| bool SurfaceDependencyTracker::IsSurfaceLate(Surface* surface) { |
| for (const SurfaceId& surface_id : surfaces_with_missing_dependencies_) { |
| Surface* activated_surface = surface_manager_->GetSurfaceForId(surface_id); |
| DCHECK(activated_surface->HasActiveFrame()); |
| if (activated_surface->late_activation_dependencies().count( |
| surface->surface_id())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void SurfaceDependencyTracker::NotifySurfaceIdAvailable( |
| const SurfaceId& surface_id) { |
| auto it = blocked_surfaces_from_dependency_.find(surface_id.frame_sink_id()); |
| if (it == blocked_surfaces_from_dependency_.end()) |
| return; |
| |
| // Unblock surfaces that depend on this |surface_id|. |
| base::flat_set<SurfaceId> blocked_surfaces_by_id(it->second); |
| |
| // Tell each surface about the availability of its blocker. |
| for (const SurfaceId& blocked_surface_by_id : blocked_surfaces_by_id) { |
| Surface* blocked_surface = |
| surface_manager_->GetSurfaceForId(blocked_surface_by_id); |
| if (!blocked_surface) { |
| // A blocked surface may have been garbage collected during dependency |
| // resolution. |
| continue; |
| } |
| blocked_surface->NotifySurfaceIdAvailable(surface_id); |
| } |
| } |
| |
| } // namespace viz |