blob: 429b1154ee5d282db5c90489ff61c28241ac3e41 [file] [log] [blame]
// Copyright 2014 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.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include "base/stl_util.h"
#include "components/viz/common/quads/copy_output_request.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/common/surfaces/local_surface_id_allocator.h"
#include "components/viz/service/surfaces/surface_client.h"
#include "components/viz/service/surfaces/surface_manager.h"
#include "components/viz/service/viz_service_export.h"
namespace viz {
Surface::Surface(const SurfaceInfo& surface_info,
SurfaceManager* surface_manager,
base::WeakPtr<SurfaceClient> surface_client,
BeginFrameSource* begin_frame_source,
bool needs_sync_tokens)
: surface_info_(surface_info),
surface_manager_(surface_manager),
surface_client_(std::move(surface_client)),
deadline_(begin_frame_source),
needs_sync_tokens_(needs_sync_tokens) {
deadline_.AddObserver(this);
}
Surface::~Surface() {
ClearCopyRequests();
surface_manager_->SurfaceDiscarded(this);
UnrefFrameResourcesAndRunDrawCallback(std::move(pending_frame_data_));
UnrefFrameResourcesAndRunDrawCallback(std::move(active_frame_data_));
deadline_.Cancel();
deadline_.RemoveObserver(this);
}
void Surface::SetPreviousFrameSurface(Surface* surface) {
DCHECK(surface && (HasActiveFrame() || HasPendingFrame()));
previous_frame_surface_id_ = surface->surface_id();
cc::CompositorFrame& frame = active_frame_data_ ? active_frame_data_->frame
: pending_frame_data_->frame;
surface->TakeLatencyInfo(&frame.metadata.latency_info);
surface->TakeLatencyInfoFromPendingFrame(&frame.metadata.latency_info);
}
void Surface::RefResources(const std::vector<TransferableResource>& resources) {
if (surface_client_)
surface_client_->RefResources(resources);
}
void Surface::UnrefResources(const std::vector<ReturnedResource>& resources) {
if (surface_client_)
surface_client_->UnrefResources(resources);
}
void Surface::RejectCompositorFramesToFallbackSurfaces() {
std::vector<FrameSinkId> frame_sink_ids_for_dependencies;
for (const SurfaceId& surface_id :
GetPendingFrame().metadata.activation_dependencies) {
frame_sink_ids_for_dependencies.push_back(surface_id.frame_sink_id());
}
for (const SurfaceId& surface_id :
GetPendingFrame().metadata.referenced_surfaces) {
// A surface ID in |referenced_surfaces| that has a corresponding surface
// ID in |activation_dependencies| with the same frame sink ID is said to
// be a fallback surface that can be used in place of the primary surface
// if the deadline passes before the dependency becomes available.
auto it = std::find(frame_sink_ids_for_dependencies.begin(),
frame_sink_ids_for_dependencies.end(),
surface_id.frame_sink_id());
bool is_fallback_surface = it != frame_sink_ids_for_dependencies.end();
if (!is_fallback_surface)
continue;
Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
// A misbehaving client may report a non-existent surface ID as a
// |referenced_surface|. In that case, |surface| would be nullptr, and
// there is nothing to do here.
if (surface)
surface->Close();
}
}
void Surface::Close() {
closed_ = true;
}
bool Surface::QueueFrame(cc::CompositorFrame frame,
uint64_t frame_index,
base::OnceClosure callback,
const WillDrawCallback& will_draw_callback) {
late_activation_dependencies_.clear();
if (frame.size_in_pixels() != surface_info_.size_in_pixels() ||
frame.device_scale_factor() != surface_info_.device_scale_factor()) {
TRACE_EVENT_INSTANT0("cc", "Surface invariants violation",
TRACE_EVENT_SCOPE_THREAD);
return false;
}
if (closed_) {
std::vector<ReturnedResource> resources =
TransferableResource::ReturnResources(frame.resource_list);
surface_client_->ReturnResources(resources);
std::move(callback).Run();
return true;
}
if (active_frame_data_ || pending_frame_data_)
previous_frame_surface_id_ = surface_id();
TakeLatencyInfoFromPendingFrame(&frame.metadata.latency_info);
base::Optional<FrameData> previous_pending_frame_data =
std::move(pending_frame_data_);
pending_frame_data_.reset();
UpdateActivationDependencies(frame);
// Receive and track the resources referenced from the cc::CompositorFrame
// regardless of whether it's pending or active.
surface_client_->ReceiveFromChild(frame.resource_list);
if (activation_dependencies_.empty()) {
// If there are no blockers, then immediately activate the frame.
ActivateFrame(FrameData(std::move(frame), frame_index, std::move(callback),
will_draw_callback));
} else {
pending_frame_data_ = FrameData(std::move(frame), frame_index,
std::move(callback), will_draw_callback);
RejectCompositorFramesToFallbackSurfaces();
// Ask the surface manager to inform |this| when its dependencies are
// resolved.
surface_manager_->RequestSurfaceResolution(this);
}
// Returns resources for the previous pending frame.
UnrefFrameResourcesAndRunDrawCallback(std::move(previous_pending_frame_data));
return true;
}
void Surface::RequestCopyOfOutput(
std::unique_ptr<CopyOutputRequest> copy_request) {
if (!active_frame_data_)
return; // |copy_request| auto-sends empty result on out-of-scope.
std::vector<std::unique_ptr<CopyOutputRequest>>& copy_requests =
active_frame_data_->frame.render_pass_list.back()->copy_requests;
if (copy_request->has_source()) {
const base::UnguessableToken& source = copy_request->source();
// Remove existing CopyOutputRequests made on the Surface by the same
// source.
base::EraseIf(copy_requests,
[&source](const std::unique_ptr<CopyOutputRequest>& x) {
return x->has_source() && x->source() == source;
});
}
copy_requests.push_back(std::move(copy_request));
}
void Surface::NotifySurfaceIdAvailable(const SurfaceId& surface_id) {
auto it = activation_dependencies_.find(surface_id);
// This surface may no longer have blockers if the deadline has passed.
if (it == activation_dependencies_.end())
return;
activation_dependencies_.erase(it);
if (!activation_dependencies_.empty())
return;
// All blockers have been cleared. The surface can be activated now.
ActivatePendingFrame();
}
void Surface::ActivatePendingFrameForDeadline() {
if (!pending_frame_data_ ||
!pending_frame_data_->frame.metadata.can_activate_before_dependencies) {
return;
}
// If a frame is being activated because of a deadline, then clear its set
// of blockers.
late_activation_dependencies_ = std::move(activation_dependencies_);
activation_dependencies_.clear();
ActivatePendingFrame();
}
Surface::FrameData::FrameData(cc::CompositorFrame&& frame,
uint64_t frame_index,
base::OnceClosure draw_callback,
const WillDrawCallback& will_draw_callback)
: frame(std::move(frame)),
frame_index(frame_index),
draw_callback(std::move(draw_callback)),
will_draw_callback(will_draw_callback) {}
Surface::FrameData::FrameData(FrameData&& other) = default;
Surface::FrameData& Surface::FrameData::operator=(FrameData&& other) = default;
Surface::FrameData::~FrameData() = default;
void Surface::ActivatePendingFrame() {
DCHECK(pending_frame_data_);
FrameData frame_data = std::move(*pending_frame_data_);
pending_frame_data_.reset();
ActivateFrame(std::move(frame_data));
}
// A frame is activated if all its Surface ID dependences are active or a
// deadline has hit and the frame was forcibly activated.
void Surface::ActivateFrame(FrameData frame_data) {
deadline_.Cancel();
// Save root pass copy requests.
std::vector<std::unique_ptr<CopyOutputRequest>> old_copy_requests;
if (active_frame_data_) {
std::swap(old_copy_requests,
active_frame_data_->frame.render_pass_list.back()->copy_requests);
}
ClearCopyRequests();
TakeLatencyInfo(&frame_data.frame.metadata.latency_info);
base::Optional<FrameData> previous_frame_data = std::move(active_frame_data_);
active_frame_data_ = std::move(frame_data);
for (auto& copy_request : old_copy_requests)
RequestCopyOfOutput(std::move(copy_request));
UnrefFrameResourcesAndRunDrawCallback(std::move(previous_frame_data));
if (!seen_first_frame_activation_) {
seen_first_frame_activation_ = true;
surface_manager_->FirstSurfaceActivation(surface_info_);
}
if (surface_client_)
surface_client_->OnSurfaceActivated(this);
surface_manager_->SurfaceActivated(this);
}
void Surface::UpdateActivationDependencies(
const cc::CompositorFrame& current_frame) {
base::flat_set<SurfaceId> new_activation_dependencies;
for (const SurfaceId& surface_id :
current_frame.metadata.activation_dependencies) {
Surface* dependency = surface_manager_->GetSurfaceForId(surface_id);
// If a activation dependency does not have a corresponding active frame in
// the display compositor, then it blocks this frame.
if (!dependency || !dependency->HasActiveFrame())
new_activation_dependencies.insert(surface_id);
}
// If this Surface has a previous pending frame, then we must determine the
// changes in dependencies so that we can update the SurfaceDependencyTracker
// map.
base::flat_set<SurfaceId> added_dependencies;
base::flat_set<SurfaceId> removed_dependencies;
ComputeChangeInDependencies(activation_dependencies_,
new_activation_dependencies, &added_dependencies,
&removed_dependencies);
// If there is a change in the dependency set, then inform SurfaceManager.
if (!added_dependencies.empty() || !removed_dependencies.empty()) {
surface_manager_->SurfaceDependenciesChanged(this, added_dependencies,
removed_dependencies);
}
activation_dependencies_ = std::move(new_activation_dependencies);
}
void Surface::ComputeChangeInDependencies(
const base::flat_set<SurfaceId>& existing_dependencies,
const base::flat_set<SurfaceId>& new_dependencies,
base::flat_set<SurfaceId>* added_dependencies,
base::flat_set<SurfaceId>* removed_dependencies) {
for (const SurfaceId& surface_id : existing_dependencies) {
if (!new_dependencies.count(surface_id))
removed_dependencies->insert(surface_id);
}
for (const SurfaceId& surface_id : new_dependencies) {
if (!existing_dependencies.count(surface_id))
added_dependencies->insert(surface_id);
}
}
void Surface::TakeCopyOutputRequests(Surface::CopyRequestsMap* copy_requests) {
DCHECK(copy_requests->empty());
if (!active_frame_data_)
return;
for (const auto& render_pass : active_frame_data_->frame.render_pass_list) {
for (auto& request : render_pass->copy_requests) {
copy_requests->insert(
std::make_pair(render_pass->id, std::move(request)));
}
render_pass->copy_requests.clear();
}
}
const cc::CompositorFrame& Surface::GetActiveFrame() const {
DCHECK(active_frame_data_);
return active_frame_data_->frame;
}
const cc::CompositorFrame& Surface::GetPendingFrame() {
DCHECK(pending_frame_data_);
return pending_frame_data_->frame;
}
void Surface::TakeLatencyInfo(std::vector<ui::LatencyInfo>* latency_info) {
if (!active_frame_data_)
return;
TakeLatencyInfoFromFrame(&active_frame_data_->frame, latency_info);
}
void Surface::RunDrawCallback() {
if (active_frame_data_ && !active_frame_data_->draw_callback.is_null())
std::move(active_frame_data_->draw_callback).Run();
}
void Surface::RunWillDrawCallback(const gfx::Rect& damage_rect) {
if (!active_frame_data_ || active_frame_data_->will_draw_callback.is_null())
return;
active_frame_data_->will_draw_callback.Run(surface_id().local_surface_id(),
damage_rect);
}
void Surface::AddDestructionDependency(SurfaceSequence sequence) {
destruction_dependencies_.push_back(sequence);
}
void Surface::SatisfyDestructionDependencies(
base::flat_set<SurfaceSequence>* sequences,
base::flat_set<FrameSinkId>* valid_frame_sink_ids) {
base::EraseIf(destruction_dependencies_,
[sequences, valid_frame_sink_ids](SurfaceSequence seq) {
return (!!sequences->erase(seq) ||
!valid_frame_sink_ids->count(seq.frame_sink_id));
});
}
void Surface::OnDeadline() {
ActivatePendingFrameForDeadline();
}
void Surface::UnrefFrameResourcesAndRunDrawCallback(
base::Optional<FrameData> frame_data) {
if (!frame_data || !surface_client_)
return;
std::vector<ReturnedResource> resources =
TransferableResource::ReturnResources(frame_data->frame.resource_list);
// No point in returning same sync token to sender.
for (auto& resource : resources)
resource.sync_token.Clear();
surface_client_->UnrefResources(resources);
if (!frame_data->draw_callback.is_null())
std::move(frame_data->draw_callback).Run();
}
void Surface::ClearCopyRequests() {
if (active_frame_data_) {
for (const auto& render_pass : active_frame_data_->frame.render_pass_list) {
// When the container is cleared, all copy requests within it will
// auto-send an empty result as they are being destroyed.
render_pass->copy_requests.clear();
}
}
}
void Surface::TakeLatencyInfoFromPendingFrame(
std::vector<ui::LatencyInfo>* latency_info) {
if (!pending_frame_data_)
return;
TakeLatencyInfoFromFrame(&pending_frame_data_->frame, latency_info);
}
// static
void Surface::TakeLatencyInfoFromFrame(
cc::CompositorFrame* frame,
std::vector<ui::LatencyInfo>* latency_info) {
if (latency_info->empty()) {
frame->metadata.latency_info.swap(*latency_info);
return;
}
std::copy(frame->metadata.latency_info.begin(),
frame->metadata.latency_info.end(),
std::back_inserter(*latency_info));
frame->metadata.latency_info.clear();
}
} // namespace viz