blob: 5f3115613c4cc5d3e53d35fe18536f896a4a6cea [file] [log] [blame]
// Copyright 2015 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 "third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.h"
#include "base/bind.h"
#include "base/memory/shared_memory_mapping.h"
#include "components/viz/common/features.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/gfx/skia_util.h"
namespace blink {
SynchronousCompositorProxy::SynchronousCompositorProxy(
blink::SynchronousInputHandlerProxy* input_handler_proxy)
: input_handler_proxy_(input_handler_proxy),
viz_frame_submission_enabled_(
features::IsUsingVizFrameSubmissionForWebView()),
page_scale_factor_(0.f),
min_page_scale_factor_(0.f),
max_page_scale_factor_(0.f),
need_invalidate_count_(0u),
invalidate_needs_draw_(false),
did_activate_pending_tree_count_(0u) {
DCHECK(input_handler_proxy_);
}
SynchronousCompositorProxy::~SynchronousCompositorProxy() {
// The LayerTreeFrameSink is destroyed/removed by the compositor before
// shutting down everything.
DCHECK_EQ(layer_tree_frame_sink_, nullptr);
input_handler_proxy_->SetSynchronousInputHandler(nullptr);
}
void SynchronousCompositorProxy::Init() {
input_handler_proxy_->SetSynchronousInputHandler(this);
}
void SynchronousCompositorProxy::SetLayerTreeFrameSink(
SynchronousLayerTreeFrameSink* layer_tree_frame_sink) {
DCHECK_NE(layer_tree_frame_sink_, layer_tree_frame_sink);
DCHECK(layer_tree_frame_sink);
if (layer_tree_frame_sink_) {
layer_tree_frame_sink_->SetSyncClient(nullptr);
}
layer_tree_frame_sink_ = layer_tree_frame_sink;
use_in_process_zero_copy_software_draw_ =
layer_tree_frame_sink_->UseZeroCopySoftwareDraw();
layer_tree_frame_sink_->SetSyncClient(this);
LayerTreeFrameSinkCreated();
if (begin_frame_paused_)
layer_tree_frame_sink_->SetBeginFrameSourcePaused(true);
}
void SynchronousCompositorProxy::UpdateRootLayerState(
const gfx::ScrollOffset& total_scroll_offset,
const gfx::ScrollOffset& max_scroll_offset,
const gfx::SizeF& scrollable_size,
float page_scale_factor,
float min_page_scale_factor,
float max_page_scale_factor) {
if (total_scroll_offset_ != total_scroll_offset ||
max_scroll_offset_ != max_scroll_offset ||
scrollable_size_ != scrollable_size ||
page_scale_factor_ != page_scale_factor ||
min_page_scale_factor_ != min_page_scale_factor ||
max_page_scale_factor_ != max_page_scale_factor) {
total_scroll_offset_ = total_scroll_offset;
max_scroll_offset_ = max_scroll_offset;
scrollable_size_ = scrollable_size;
page_scale_factor_ = page_scale_factor;
min_page_scale_factor_ = min_page_scale_factor;
max_page_scale_factor_ = max_page_scale_factor;
SendAsyncRendererStateIfNeeded();
}
}
void SynchronousCompositorProxy::Invalidate(bool needs_draw) {
++need_invalidate_count_;
invalidate_needs_draw_ |= needs_draw;
SendAsyncRendererStateIfNeeded();
}
void SynchronousCompositorProxy::DidActivatePendingTree() {
++did_activate_pending_tree_count_;
SendAsyncRendererStateIfNeeded();
}
mojom::blink::SyncCompositorCommonRendererParamsPtr
SynchronousCompositorProxy::PopulateNewCommonParams() {
mojom::blink::SyncCompositorCommonRendererParamsPtr params =
mojom::blink::SyncCompositorCommonRendererParams::New();
params->version = ++version_;
params->total_scroll_offset = total_scroll_offset_;
params->max_scroll_offset = max_scroll_offset_;
params->scrollable_size = scrollable_size_;
params->page_scale_factor = page_scale_factor_;
params->min_page_scale_factor = min_page_scale_factor_;
params->max_page_scale_factor = max_page_scale_factor_;
params->need_invalidate_count = need_invalidate_count_;
params->invalidate_needs_draw = invalidate_needs_draw_;
params->did_activate_pending_tree_count = did_activate_pending_tree_count_;
return params;
}
void SynchronousCompositorProxy::DemandDrawHwAsync(
mojom::blink::SyncCompositorDemandDrawHwParamsPtr params) {
DemandDrawHw(
std::move(params),
base::BindOnce(&SynchronousCompositorProxy::SendDemandDrawHwAsyncReply,
base::Unretained(this)));
}
void SynchronousCompositorProxy::DemandDrawHw(
mojom::blink::SyncCompositorDemandDrawHwParamsPtr params,
DemandDrawHwCallback callback) {
invalidate_needs_draw_ = false;
hardware_draw_reply_ = std::move(callback);
if (layer_tree_frame_sink_) {
layer_tree_frame_sink_->DemandDrawHw(
params->viewport_size, params->viewport_rect_for_tile_priority,
params->transform_for_tile_priority, params->need_new_local_surface_id);
}
// Ensure that a response is always sent even if the reply hasn't
// generated a compostior frame.
if (hardware_draw_reply_) {
// Did not swap.
std::move(hardware_draw_reply_)
.Run(PopulateNewCommonParams(), 0u, 0u, base::nullopt, base::nullopt,
base::nullopt);
}
}
void SynchronousCompositorProxy::WillSkipDraw() {
if (layer_tree_frame_sink_) {
layer_tree_frame_sink_->WillSkipDraw();
}
}
struct SynchronousCompositorProxy::SharedMemoryWithSize {
base::WritableSharedMemoryMapping shared_memory;
const size_t buffer_size;
bool zeroed;
SharedMemoryWithSize(base::WritableSharedMemoryMapping shm_mapping,
size_t buffer_size)
: shared_memory(std::move(shm_mapping)),
buffer_size(buffer_size),
zeroed(true) {}
};
void SynchronousCompositorProxy::ZeroSharedMemory() {
// It is possible for this to get called twice, eg. if draw is called before
// the LayerTreeFrameSink is ready. Just ignore duplicated calls rather than
// inventing a complicated system to avoid it.
if (software_draw_shm_->zeroed)
return;
memset(software_draw_shm_->shared_memory.memory(), 0,
software_draw_shm_->buffer_size);
software_draw_shm_->zeroed = true;
}
void SynchronousCompositorProxy::DemandDrawSw(
mojom::blink::SyncCompositorDemandDrawSwParamsPtr params,
DemandDrawSwCallback callback) {
invalidate_needs_draw_ = false;
software_draw_reply_ = std::move(callback);
if (layer_tree_frame_sink_) {
if (use_in_process_zero_copy_software_draw_) {
layer_tree_frame_sink_->DemandDrawSwZeroCopy();
} else {
DoDemandDrawSw(std::move(params));
}
}
// Ensure that a response is always sent even if the reply hasn't
// generated a compostior frame.
if (software_draw_reply_) {
// Did not swap.
std::move(software_draw_reply_)
.Run(PopulateNewCommonParams(), 0u, base::nullopt);
}
}
void SynchronousCompositorProxy::DoDemandDrawSw(
mojom::blink::SyncCompositorDemandDrawSwParamsPtr params) {
DCHECK(layer_tree_frame_sink_);
DCHECK(software_draw_shm_->zeroed);
software_draw_shm_->zeroed = false;
SkImageInfo info =
SkImageInfo::MakeN32Premul(params->size.width(), params->size.height());
size_t stride = info.minRowBytes();
size_t buffer_size = info.computeByteSize(stride);
DCHECK_EQ(software_draw_shm_->buffer_size, buffer_size);
SkBitmap bitmap;
if (!bitmap.installPixels(info, software_draw_shm_->shared_memory.memory(),
stride)) {
return;
}
SkCanvas canvas(bitmap);
canvas.clipRect(gfx::RectToSkRect(params->clip));
canvas.concat(SkMatrix(params->transform.matrix()));
layer_tree_frame_sink_->DemandDrawSw(&canvas);
}
void SynchronousCompositorProxy::SubmitCompositorFrame(
uint32_t layer_tree_frame_sink_id,
const viz::LocalSurfaceId& local_surface_id,
base::Optional<viz::CompositorFrame> frame,
base::Optional<viz::HitTestRegionList> hit_test_region_list) {
// Verify that exactly one of these is true.
DCHECK(hardware_draw_reply_.is_null() ^ software_draw_reply_.is_null());
mojom::blink::SyncCompositorCommonRendererParamsPtr common_renderer_params =
PopulateNewCommonParams();
if (hardware_draw_reply_) {
// For viz the CF was submitted directly via CompositorFrameSink
DCHECK(frame || viz_frame_submission_enabled_);
DCHECK(local_surface_id.is_valid());
std::move(hardware_draw_reply_)
.Run(std::move(common_renderer_params), layer_tree_frame_sink_id,
NextMetadataVersion(), local_surface_id, std::move(frame),
std::move(hit_test_region_list));
} else if (software_draw_reply_) {
DCHECK(frame);
std::move(software_draw_reply_)
.Run(std::move(common_renderer_params), NextMetadataVersion(),
std::move(frame->metadata));
} else {
NOTREACHED();
}
}
void SynchronousCompositorProxy::SetNeedsBeginFrames(bool needs_begin_frames) {
if (needs_begin_frames_ == needs_begin_frames)
return;
needs_begin_frames_ = needs_begin_frames;
if (host_)
host_->SetNeedsBeginFrames(needs_begin_frames);
}
void SynchronousCompositorProxy::SinkDestroyed() {
layer_tree_frame_sink_ = nullptr;
}
void SynchronousCompositorProxy::SetBeginFrameSourcePaused(bool paused) {
begin_frame_paused_ = paused;
if (layer_tree_frame_sink_)
layer_tree_frame_sink_->SetBeginFrameSourcePaused(paused);
}
void SynchronousCompositorProxy::BeginFrame(
const viz::BeginFrameArgs& args,
const HashMap<uint32_t, viz::FrameTimingDetails>& timing_details) {
if (layer_tree_frame_sink_) {
base::flat_map<uint32_t, viz::FrameTimingDetails> timings;
for (const auto& pair : timing_details) {
timings[pair.key] = pair.value;
}
layer_tree_frame_sink_->DidPresentCompositorFrame(timings);
if (needs_begin_frames_)
layer_tree_frame_sink_->BeginFrame(args);
}
SendBeginFrameResponse(PopulateNewCommonParams());
}
void SynchronousCompositorProxy::SetScroll(
const gfx::ScrollOffset& new_total_scroll_offset) {
if (total_scroll_offset_ == new_total_scroll_offset)
return;
total_scroll_offset_ = new_total_scroll_offset;
input_handler_proxy_->SynchronouslySetRootScrollOffset(total_scroll_offset_);
}
void SynchronousCompositorProxy::SetMemoryPolicy(uint32_t bytes_limit) {
if (!layer_tree_frame_sink_)
return;
layer_tree_frame_sink_->SetMemoryPolicy(bytes_limit);
}
void SynchronousCompositorProxy::ReclaimResources(
uint32_t layer_tree_frame_sink_id,
const Vector<viz::ReturnedResource>& resources) {
if (!layer_tree_frame_sink_)
return;
layer_tree_frame_sink_->ReclaimResources(layer_tree_frame_sink_id, resources);
}
void SynchronousCompositorProxy::SetSharedMemory(
base::WritableSharedMemoryRegion shm_region,
SetSharedMemoryCallback callback) {
bool success = false;
mojom::blink::SyncCompositorCommonRendererParamsPtr common_renderer_params;
if (shm_region.IsValid()) {
base::WritableSharedMemoryMapping shm_mapping = shm_region.Map();
if (shm_mapping.IsValid()) {
software_draw_shm_ = std::make_unique<SharedMemoryWithSize>(
std::move(shm_mapping), shm_mapping.size());
common_renderer_params = PopulateNewCommonParams();
success = true;
}
}
if (!common_renderer_params) {
common_renderer_params =
mojom::blink::SyncCompositorCommonRendererParams::New();
}
std::move(callback).Run(success, std::move(common_renderer_params));
}
void SynchronousCompositorProxy::ZoomBy(float zoom_delta,
const gfx::Point& anchor,
ZoomByCallback callback) {
zoom_by_reply_ = std::move(callback);
input_handler_proxy_->SynchronouslyZoomBy(zoom_delta, anchor);
std::move(zoom_by_reply_).Run(PopulateNewCommonParams());
}
uint32_t SynchronousCompositorProxy::NextMetadataVersion() {
return ++metadata_version_;
}
void SynchronousCompositorProxy::SendDemandDrawHwAsyncReply(
mojom::blink::SyncCompositorCommonRendererParamsPtr,
uint32_t layer_tree_frame_sink_id,
uint32_t metadata_version,
const base::Optional<viz::LocalSurfaceId>& local_surface_id,
base::Optional<viz::CompositorFrame> frame,
base::Optional<viz::HitTestRegionList> hit_test_region_list) {
control_host_->ReturnFrame(layer_tree_frame_sink_id, metadata_version,
local_surface_id, std::move(frame),
std::move(hit_test_region_list));
}
void SynchronousCompositorProxy::SendBeginFrameResponse(
mojom::blink::SyncCompositorCommonRendererParamsPtr param) {
control_host_->BeginFrameResponse(std::move(param));
}
void SynchronousCompositorProxy::SendAsyncRendererStateIfNeeded() {
if (hardware_draw_reply_ || software_draw_reply_ || zoom_by_reply_ || !host_)
return;
host_->UpdateState(PopulateNewCommonParams());
}
void SynchronousCompositorProxy::LayerTreeFrameSinkCreated() {
DCHECK(layer_tree_frame_sink_);
if (host_)
host_->LayerTreeFrameSinkCreated();
}
void SynchronousCompositorProxy::BindChannel(
mojo::PendingRemote<mojom::blink::SynchronousCompositorControlHost>
control_host,
mojo::PendingAssociatedRemote<mojom::blink::SynchronousCompositorHost> host,
mojo::PendingAssociatedReceiver<mojom::blink::SynchronousCompositor>
compositor_request) {
// Reset bound mojo channels before rebinding new variants as the
// associated RenderWidgetHost may be reused.
control_host_.reset();
host_.reset();
receiver_.reset();
control_host_.Bind(std::move(control_host));
host_.Bind(std::move(host));
receiver_.Bind(std::move(compositor_request));
receiver_.set_disconnect_handler(base::BindOnce(
&SynchronousCompositorProxy::HostDisconnected, base::Unretained(this)));
if (layer_tree_frame_sink_)
LayerTreeFrameSinkCreated();
if (needs_begin_frames_)
host_->SetNeedsBeginFrames(true);
}
void SynchronousCompositorProxy::HostDisconnected() {
// It is possible due to bugs that the Host is disconnected without pausing
// begin frames. This causes hard-to-reproduce but catastrophic bug of
// blocking the renderer main thread forever on a commit. See
// crbug.com/1010478 for when this happened. This is to prevent a similar
// bug in the future.
SetBeginFrameSourcePaused(true);
}
} // namespace blink