blob: c04a4fb31aa38552e8106563411bd977d4b456da [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 "content/browser/android/synchronous_compositor_browser_filter.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/stl_util.h"
#include "content/browser/android/synchronous_compositor_host.h"
#include "content/browser/bad_message.h"
#include "content/common/android/sync_compositor_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "ui/android/window_android.h"
namespace content {
SynchronousCompositorBrowserFilter::SynchronousCompositorBrowserFilter(
int process_id)
: BrowserMessageFilter(SyncCompositorMsgStart),
render_process_host_(RenderProcessHost::FromID(process_id)) {
DCHECK(render_process_host_);
}
SynchronousCompositorBrowserFilter::~SynchronousCompositorBrowserFilter() {
DCHECK(compositor_host_pending_renderer_state_.empty());
DCHECK(future_map_.empty());
}
void SynchronousCompositorBrowserFilter::SyncStateAfterVSync(
ui::WindowAndroid* window_android,
SynchronousCompositorHost* compositor_host) {
DCHECK(!window_android_in_vsync_ ||
window_android_in_vsync_ == window_android)
<< !!window_android_in_vsync_;
DCHECK(compositor_host);
DCHECK(!base::ContainsValue(compositor_host_pending_renderer_state_,
compositor_host));
compositor_host_pending_renderer_state_.push_back(compositor_host);
if (window_android_in_vsync_)
return;
window_android_in_vsync_ = window_android;
window_android_in_vsync_->AddVSyncCompleteCallback(
base::Bind(&SynchronousCompositorBrowserFilter::VSyncComplete, this));
}
bool SynchronousCompositorBrowserFilter::OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(SynchronousCompositorBrowserFilter, message)
IPC_MESSAGE_HANDLER_GENERIC(SyncCompositorHostMsg_ReturnFrame,
ReceiveFrame(message))
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
bool SynchronousCompositorBrowserFilter::ReceiveFrame(
const IPC::Message& message) {
SyncCompositorHostMsg_ReturnFrame::Param param;
if (!SyncCompositorHostMsg_ReturnFrame::Read(&message, &param))
return false;
int routing_id = message.routing_id();
scoped_refptr<SynchronousCompositor::FrameFuture> future;
{
base::AutoLock lock(future_map_lock_);
auto itr = future_map_.find(routing_id);
if (itr == future_map_.end() || itr->second.empty()) {
bad_message::ReceivedBadMessage(render_process_host_,
bad_message::SCO_INVALID_ARGUMENT);
return true;
}
future = std::move(itr->second.front());
DCHECK(future);
itr->second.pop_front();
if (itr->second.empty())
future_map_.erase(itr);
}
auto frame_ptr = base::MakeUnique<SynchronousCompositor::Frame>();
frame_ptr->compositor_frame_sink_id = std::get<0>(param);
base::Optional<cc::CompositorFrame>& compositor_frame = std::get<1>(param);
if (compositor_frame) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(
&SynchronousCompositorBrowserFilter::ProcessFrameMetadataOnUIThread,
this, routing_id,
base::Passed(compositor_frame->metadata.Clone())));
frame_ptr->frame.reset(new cc::CompositorFrame);
*frame_ptr->frame = std::move(*compositor_frame);
}
future->SetFrame(std::move(frame_ptr));
return true;
}
void SynchronousCompositorBrowserFilter::ProcessFrameMetadataOnUIThread(
int routing_id,
cc::CompositorFrameMetadata metadata) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto itr = hosts_.find(routing_id);
if (itr == hosts_.end())
return;
itr->second->UpdateFrameMetaData(std::move(metadata));
}
void SynchronousCompositorBrowserFilter::RegisterHost(
SynchronousCompositorHost* host) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(host);
DCHECK(!base::ContainsKey(hosts_, host->routing_id()));
hosts_[host->routing_id()] = host;
}
void SynchronousCompositorBrowserFilter::UnregisterHost(
SynchronousCompositorHost* host) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(host);
int routing_id = host->routing_id();
DCHECK(base::ContainsKey(hosts_, routing_id));
DCHECK_EQ(host, hosts_[routing_id]);
hosts_.erase(routing_id);
}
void SynchronousCompositorBrowserFilter::SetFrameFuture(
int routing_id,
scoped_refptr<SynchronousCompositor::FrameFuture> frame_future) {
DCHECK(frame_future);
base::AutoLock lock(future_map_lock_);
if (!filter_ready_) {
frame_future->SetFrame(nullptr);
return;
}
auto itr = future_map_.find(routing_id);
if (itr == future_map_.end()) {
auto emplace_result = future_map_.emplace(routing_id, FrameFutureQueue());
DCHECK(emplace_result.second);
itr = emplace_result.first;
}
// Allowing arbitrary number of pending futures can lead to increase in frame
// latency. Due to this, Android platform already ensures that here that there
// can be at most 2 pending frames. Here, we rely on Android to do the
// necessary blocking, which allows more parallelism without increasing
// latency. But DCHECK Android blocking is working.
DCHECK_LT(itr->second.size(), 2u);
itr->second.emplace_back(std::move(frame_future));
}
void SynchronousCompositorBrowserFilter::OnFilterAdded(IPC::Channel* channel) {
base::AutoLock lock(future_map_lock_);
filter_ready_ = true;
}
void SynchronousCompositorBrowserFilter::OnFilterRemoved() {
SignalAllFutures();
}
void SynchronousCompositorBrowserFilter::OnChannelClosing() {
SignalAllFutures();
}
void SynchronousCompositorBrowserFilter::SignalAllFutures() {
base::AutoLock lock(future_map_lock_);
for (auto& pair : future_map_) {
for (auto& future_ptr : pair.second) {
future_ptr->SetFrame(nullptr);
}
}
future_map_.clear();
filter_ready_ = false;
}
void SynchronousCompositorBrowserFilter::VSyncComplete() {
DCHECK(window_android_in_vsync_);
window_android_in_vsync_ = nullptr;
std::vector<int> routing_ids;
routing_ids.reserve(compositor_host_pending_renderer_state_.size());
for (const auto* host : compositor_host_pending_renderer_state_)
routing_ids.push_back(host->routing_id());
std::vector<SyncCompositorCommonRendererParams> params;
params.reserve(compositor_host_pending_renderer_state_.size());
{
base::ThreadRestrictions::ScopedAllowWait wait;
if (!render_process_host_->Send(
new SyncCompositorMsg_SynchronizeRendererState(routing_ids,
&params))) {
compositor_host_pending_renderer_state_.clear();
return;
}
}
if (compositor_host_pending_renderer_state_.size() != params.size()) {
bad_message::ReceivedBadMessage(render_process_host_,
bad_message::SCO_INVALID_ARGUMENT);
compositor_host_pending_renderer_state_.clear();
return;
}
for (size_t i = 0; i < compositor_host_pending_renderer_state_.size(); ++i) {
compositor_host_pending_renderer_state_[i]->ProcessCommonParams(params[i]);
}
compositor_host_pending_renderer_state_.clear();
}
} // namespace content