blob: 6cfeae8ec2b04abb49551738b222ebd5d5e1d58d [file] [log] [blame]
// 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 "cc/surfaces/surface_dependency_tracker.h"
#include "cc/surfaces/surface.h"
#include "cc/surfaces/surface_info.h"
#include "cc/surfaces/surface_manager.h"
namespace cc {
namespace {
constexpr uint32_t kMaxBeginFrameCount = 4;
}
SurfaceDependencyTracker::SurfaceDependencyTracker(
SurfaceManager* surface_manager,
BeginFrameSource* begin_frame_source)
: surface_manager_(surface_manager),
begin_frame_source_(begin_frame_source) {
surface_manager_->AddObserver(this);
begin_frame_source_->AddObserver(this);
}
SurfaceDependencyTracker::~SurfaceDependencyTracker() {
surface_manager_->RemoveObserver(this);
begin_frame_source_->RemoveObserver(this);
for (Surface* pending_surface : pending_surfaces_)
pending_surface->RemoveObserver(this);
pending_surfaces_.clear();
}
void SurfaceDependencyTracker::RequestSurfaceResolution(Surface* surface) {
DCHECK(surface->HasPendingFrame());
const CompositorFrame& pending_frame = surface->GetPendingFrame();
bool needs_begin_frame =
pending_frame.metadata.can_activate_before_dependencies;
// Referenced surface IDs that aren't currently known to the surface manager
// or do not have an active CompsotiorFrame block this frame.
for (const SurfaceId& surface_id :
pending_frame.metadata.referenced_surfaces) {
Surface* surface_dependency = surface_manager_->GetSurfaceForId(surface_id);
if (!surface_dependency || !surface_dependency->HasActiveFrame())
blocked_surfaces_[surface_id].insert(surface);
}
if (!pending_surfaces_.count(surface)) {
surface->AddObserver(this);
pending_surfaces_.insert(surface);
}
if (needs_begin_frame && !frames_since_deadline_set_)
frames_since_deadline_set_ = 0;
}
void SurfaceDependencyTracker::OnBeginFrame(const BeginFrameArgs& args) {
// If no deadline is set then we have nothing to do.
if (!frames_since_deadline_set_)
return;
// TODO(fsamuel, kylechar): We have a single global deadline here. We should
// scope deadlines to surface subtrees. We cannot do that until
// SurfaceReferences have been fully implemented
// (see https://crbug.com/689719).
last_begin_frame_args_ = args;
// Nothing to do if we haven't hit a deadline yet.
if (++(*frames_since_deadline_set_) != kMaxBeginFrameCount)
return;
// Activate all surfaces that respect the deadline.
PendingSurfaceSet pending_surfaces(pending_surfaces_);
for (Surface* pending_surface : pending_surfaces)
pending_surface->ActivatePendingFrameForDeadline();
frames_since_deadline_set_.reset();
}
const BeginFrameArgs& SurfaceDependencyTracker::LastUsedBeginFrameArgs() const {
return last_begin_frame_args_;
}
void SurfaceDependencyTracker::OnBeginFrameSourcePausedChanged(bool paused) {}
void SurfaceDependencyTracker::OnReferencedSurfacesChanged(
Surface* surface,
const std::vector<SurfaceId>* active_referenced_surfaces,
const std::vector<SurfaceId>* pending_referenced_surfaces) {}
void SurfaceDependencyTracker::OnSurfaceDiscarded(Surface* surface) {
// If the surface being destroyed doesn't have a pending frame then we have
// nothing to do here.
if (!surface->HasPendingFrame())
return;
const CompositorFrame& pending_frame = surface->GetPendingFrame();
DCHECK(!pending_frame.metadata.referenced_surfaces.empty());
for (const SurfaceId& surface_id :
pending_frame.metadata.referenced_surfaces) {
auto it = blocked_surfaces_.find(surface_id);
if (it == blocked_surfaces_.end())
continue;
auto& pending_surface_set = it->second;
auto pending_surface_it = pending_surface_set.find(surface);
if (pending_surface_it != pending_surface_set.end()) {
pending_surface_set.erase(surface);
if (pending_surface_set.empty())
blocked_surfaces_.erase(surface_id);
}
}
if (blocked_surfaces_.empty())
frames_since_deadline_set_.reset();
pending_surfaces_.erase(surface);
surface->RemoveObserver(this);
// 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());
}
void SurfaceDependencyTracker::OnSurfaceActivated(Surface* surface) {
surface->RemoveObserver(this);
pending_surfaces_.erase(surface);
NotifySurfaceIdAvailable(surface->surface_id());
}
void SurfaceDependencyTracker::OnSurfaceDependenciesChanged(
Surface* surface,
const SurfaceDependencies& added_dependencies,
const SurfaceDependencies& removed_dependencies) {
// Update the |blocked_surfaces_| map with the changes in dependencies.
for (const SurfaceId& surface_id : added_dependencies)
blocked_surfaces_[surface_id].insert(surface);
for (const SurfaceId& surface_id : removed_dependencies) {
auto it = blocked_surfaces_.find(surface_id);
it->second.erase(surface);
if (it->second.empty())
blocked_surfaces_.erase(it);
}
// If there are no more dependencies to resolve then we don't need to have a
// deadline.
if (blocked_surfaces_.empty())
frames_since_deadline_set_.reset();
}
// SurfaceObserver implementation:
void SurfaceDependencyTracker::OnSurfaceCreated(
const SurfaceInfo& surface_info) {
// This is called when a Surface has an activated frame for the first time.
// SurfaceDependencyTracker only observes Surfaces that contain pending
// frames. SurfaceDependencyTracker becomes aware of CompositorFrames that
// activate immediately go through here.
NotifySurfaceIdAvailable(surface_info.id());
}
void SurfaceDependencyTracker::OnSurfaceDamaged(const SurfaceId& surface_id,
bool* changed) {}
void SurfaceDependencyTracker::NotifySurfaceIdAvailable(
const SurfaceId& surface_id) {
auto it = blocked_surfaces_.find(surface_id);
if (it == blocked_surfaces_.end())
return;
// Unblock surfaces that depend on this |surface_id|.
PendingSurfaceSet blocked_pending_surface_set(it->second);
blocked_surfaces_.erase(it);
// If there are no more blockers in the system, then we no longer need to
// have a deadline.
if (blocked_surfaces_.empty())
frames_since_deadline_set_.reset();
// Tell each surface about the availability of its blocker.
for (Surface* blocked_pending_surface : blocked_pending_surface_set)
blocked_pending_surface->NotifySurfaceIdAvailable(surface_id);
}
} // namespace cc