blob: 3cdb7996689ac7949aba8a62063f618d43dd8813 [file] [log] [blame]
// Copyright 2019 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/display/display_damage_tracker.h"
#include "base/observer_list.h"
#include "base/trace_event/trace_event.h"
#include "components/viz/service/display/surface_aggregator.h"
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_manager.h"
namespace viz {
namespace {
bool ShouldAccumulateInteraction(
SurfaceObserver::HandleInteraction handle_interaction) {
switch (handle_interaction) {
case SurfaceObserver::HandleInteraction::kYes:
return true;
case SurfaceObserver::HandleInteraction::kNo:
return false;
case SurfaceObserver::HandleInteraction::kNoChange:
return false;
}
}
} // namespace
DisplayDamageTracker::DisplayDamageTracker(SurfaceManager* surface_manager,
SurfaceAggregator* aggregator)
: surface_manager_(surface_manager), aggregator_(aggregator) {
DCHECK(surface_manager_);
DCHECK(aggregator_);
surface_manager_->AddObserver(this);
}
DisplayDamageTracker::~DisplayDamageTracker() {
surface_manager_->RemoveObserver(this);
}
void DisplayDamageTracker::SetDisplayBeginFrameSourceId(
uint64_t begin_frame_source_id) {
begin_frame_source_id_ = begin_frame_source_id;
}
void DisplayDamageTracker::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
}
void DisplayDamageTracker::SetRootFrameMissing(bool missing) {
TRACE_EVENT1("viz", "DisplayDamageTracker::SetRootFrameMissing", "missing",
missing);
if (root_frame_missing_ == missing)
return;
root_frame_missing_ = missing;
NotifyRootFrameMissing(missing);
}
void DisplayDamageTracker::SetNewRootSurface(const SurfaceId& root_surface_id) {
TRACE_EVENT0("viz", "DisplayDamageTracker::SetNewRootSurface");
root_surface_id_ = root_surface_id;
UpdateRootFrameMissing();
SetRootSurfaceDamaged();
}
void DisplayDamageTracker::SetRootSurfaceDamaged() {
BeginFrameAck ack;
ack.has_damage = true;
// Since we're damaging to redraw the last activated frame, there shouldn't be
// any change in interaction state.
ProcessSurfaceDamage(root_surface_id_, ack, true,
HandleInteraction::kNoChange);
}
bool DisplayDamageTracker::IsRootSurfaceValid() const {
return root_surface_id_.is_valid();
}
void DisplayDamageTracker::DisplayResized() {
expecting_root_surface_damage_because_of_resize_ = true;
// Technically we don't have any damage yet, but we need to draw after resize,
// so we report display damaged here.
NotifyDisplayDamaged(root_surface_id_);
}
void DisplayDamageTracker::ProcessSurfaceDamage(
const SurfaceId& surface_id,
const BeginFrameAck& ack,
bool display_damaged,
HandleInteraction handle_interaction) {
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"),
"DisplayDamageTracker::SurfaceDamaged", "surface_id",
surface_id.ToString());
has_surface_damage_due_to_interaction_ |=
ShouldAccumulateInteraction(handle_interaction);
if (surface_id == root_surface_id_)
expecting_root_surface_damage_because_of_resize_ = false;
// Update surface state.
bool valid_ack = ack.frame_id.IsSequenceValid();
if (valid_ack) {
auto it = surface_states_.find(surface_id);
// Ignore stray acknowledgments for prior BeginFrames, to ensure we don't
// override a newer sequence number in the surface state. We may receive
// such stray acks e.g. when a CompositorFrame activates in a later
// BeginFrame than it was created.
if (it != surface_states_.end() &&
!it->second.last_ack.frame_id.IsNextInSequenceTo(ack.frame_id)) {
it->second.last_ack = ack;
} else {
valid_ack = false;
}
}
if (display_damaged) {
NotifyDisplayDamaged(surface_id);
} else if (valid_ack) {
NotifyPendingSurfacesChanged();
}
}
bool DisplayDamageTracker::SurfaceHasUnackedFrame(
const SurfaceId& surface_id) const {
Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
if (!surface)
return false;
return surface->HasUnackedActiveFrame();
}
bool DisplayDamageTracker::HasPendingSurfaces(
const BeginFrameArgs& begin_frame_args) {
for (auto& entry : surface_states_) {
const SurfaceId& surface_id = entry.first;
const SurfaceBeginFrameState& state = entry.second;
// Surface is ready if it hasn't received the current BeginFrame or receives
// BeginFrames from a different source and thus likely belongs to a
// different surface hierarchy.
if (!state.last_args.IsValid() ||
state.last_args.frame_id != begin_frame_args.frame_id) {
continue;
}
// Surface is ready if it has acknowledged the current BeginFrame.
if (state.last_ack.frame_id == begin_frame_args.frame_id) {
continue;
}
// Surface is ready if there is an unacked active CompositorFrame, because
// its producer is CompositorFrameAck throttled.
if (SurfaceHasUnackedFrame(surface_id))
continue;
TRACE_EVENT_INSTANT2("viz", "DisplayDamageTracker::HasPendingSurfaces",
TRACE_EVENT_SCOPE_THREAD, "has_pending_surfaces", true,
"pending_surface_id", surface_id.ToString());
return true;
}
TRACE_EVENT_INSTANT1("viz", "DisplayDamageTracker::HasPendingSurfaces",
TRACE_EVENT_SCOPE_THREAD, "has_pending_surfaces", false);
return false;
}
bool DisplayDamageTracker::HasDamageDueToInteraction() {
return has_surface_damage_due_to_interaction_;
}
void DisplayDamageTracker::DidFinishFrame() {
// We need to unset this bit otherwise we will continue to draw immediately
// even when we have no new damage from an active scroller.
has_surface_damage_due_to_interaction_ = false;
}
void DisplayDamageTracker::OnSurfaceMarkedForDestruction(
const SurfaceId& surface_id) {
auto it = surface_states_.find(surface_id);
if (it == surface_states_.end())
return;
surface_states_.erase(it);
NotifyPendingSurfacesChanged();
}
bool DisplayDamageTracker::CheckForDisplayDamage(const SurfaceId& surface_id) {
return aggregator_->CheckForDisplayDamage(surface_id);
}
bool DisplayDamageTracker::OnSurfaceDamaged(
const SurfaceId& surface_id,
const BeginFrameAck& ack,
HandleInteraction handle_interaction) {
bool display_damaged = false;
if (ack.has_damage) {
// Display is damaged if we purged some resources or if this surface
// contributes to this display.
display_damaged = aggregator_->ForceReleaseResourcesIfNeeded(surface_id) ||
CheckForDisplayDamage(surface_id);
if (surface_id == root_surface_id_)
display_damaged = true;
if (display_damaged)
surfaces_to_ack_on_next_draw_.push_back(surface_id);
}
if (surface_id == root_surface_id_)
UpdateRootFrameMissing();
ProcessSurfaceDamage(surface_id, ack, display_damaged, handle_interaction);
return display_damaged;
}
bool DisplayDamageTracker::CheckBeginFrameSourceId(uint64_t source_id) {
return !begin_frame_source_id_ || source_id == *begin_frame_source_id_ ||
source_id == BeginFrameArgs::kManualSourceId;
}
void DisplayDamageTracker::OnSurfaceDamageExpected(const SurfaceId& surface_id,
const BeginFrameArgs& args) {
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"),
"DisplayDamageTracker::SurfaceDamageExpected", "surface_id",
surface_id.ToString());
// Insert a new state for the surface if we don't know of it yet. We don't
// use OnSurfaceCreated() for this, because it may not be called if a
// CompositorFrameSinkSupport starts submitting frames to a different
// Display, but continues using the same Surface, or if a Surface does not
// activate its first CompositorFrame immediately.
surface_states_[surface_id].last_args = args;
// HasPendingSurfaces() won't consider any surfaces that received a begin
// frame from a different displays begin frame source but will still iterate
// through all of the entries in `surface_states_`. That iteration is
// expensive so avoid doing it when source_id doesn't match.
if (!CheckBeginFrameSourceId(args.frame_id.source_id)) {
return;
}
NotifyPendingSurfacesChanged();
}
void DisplayDamageTracker::UpdateRootFrameMissing() {
Surface* surface = surface_manager_->GetSurfaceForId(root_surface_id_);
SetRootFrameMissing(!surface || !surface->HasActiveFrame());
}
void DisplayDamageTracker::RunDrawCallbacks() {
for (const auto& surface_id : surfaces_to_ack_on_next_draw_) {
Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
if (surface)
surface->SendAckToClient();
}
surfaces_to_ack_on_next_draw_.clear();
// |surfaces_to_ack_on_next_draw_| does not cover surfaces that are being
// embedded for the first time, so also go through SurfaceAggregator's list.
for (const auto& surface_id : aggregator_->previous_contained_surfaces()) {
Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
if (surface) {
surface->SendAckToClient();
}
}
}
void DisplayDamageTracker::NotifyDisplayDamaged(SurfaceId surface_id) {
if (delegate_) {
delegate_->OnDisplayDamaged(surface_id);
}
}
void DisplayDamageTracker::NotifyRootFrameMissing(bool missing) {
if (delegate_) {
delegate_->OnRootFrameMissing(missing);
}
}
void DisplayDamageTracker::NotifyPendingSurfacesChanged() {
if (delegate_) {
delegate_->OnPendingSurfacesChanged();
}
}
} // namespace viz