Index: content/common/gpu/gpu_channel.cc |
diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c79d20151fffb4c6f34bc9755f0bf04a250d9774 |
--- /dev/null |
+++ b/content/common/gpu/gpu_channel.cc |
@@ -0,0 +1,1053 @@ |
+// Copyright (c) 2012 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/common/gpu/gpu_channel.h" |
+ |
+#include <utility> |
+ |
+#if defined(OS_WIN) |
+#include <windows.h> |
+#endif |
+ |
+#include <algorithm> |
+#include <deque> |
+#include <set> |
+#include <vector> |
+ |
+#include "base/atomicops.h" |
+#include "base/bind.h" |
+#include "base/command_line.h" |
+#include "base/location.h" |
+#include "base/numerics/safe_conversions.h" |
+#include "base/single_thread_task_runner.h" |
+#include "base/stl_util.h" |
+#include "base/strings/string_util.h" |
+#include "base/synchronization/lock.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "base/timer/timer.h" |
+#include "base/trace_event/memory_dump_manager.h" |
+#include "base/trace_event/process_memory_dump.h" |
+#include "base/trace_event/trace_event.h" |
+#include "build/build_config.h" |
+#include "content/common/gpu/gpu_channel_manager.h" |
+#include "content/common/gpu/gpu_channel_manager_delegate.h" |
+#include "content/common/gpu/gpu_memory_buffer_factory.h" |
+#include "gpu/command_buffer/common/mailbox.h" |
+#include "gpu/command_buffer/common/value_state.h" |
+#include "gpu/command_buffer/service/command_executor.h" |
+#include "gpu/command_buffer/service/image_factory.h" |
+#include "gpu/command_buffer/service/mailbox_manager.h" |
+#include "gpu/command_buffer/service/sync_point_manager.h" |
+#include "gpu/command_buffer/service/valuebuffer_manager.h" |
+#include "gpu/ipc/common/gpu_messages.h" |
+#include "ipc/ipc_channel.h" |
+#include "ipc/message_filter.h" |
+#include "ui/gl/gl_context.h" |
+#include "ui/gl/gl_image_shared_memory.h" |
+#include "ui/gl/gl_surface.h" |
+ |
+#if defined(OS_POSIX) |
+#include "ipc/ipc_channel_posix.h" |
+#endif |
+ |
+namespace content { |
+namespace { |
+ |
+// Number of milliseconds between successive vsync. Many GL commands block |
+// on vsync, so thresholds for preemption should be multiples of this. |
+const int64_t kVsyncIntervalMs = 17; |
+ |
+// Amount of time that we will wait for an IPC to be processed before |
+// preempting. After a preemption, we must wait this long before triggering |
+// another preemption. |
+const int64_t kPreemptWaitTimeMs = 2 * kVsyncIntervalMs; |
+ |
+// Once we trigger a preemption, the maximum duration that we will wait |
+// before clearing the preemption. |
+const int64_t kMaxPreemptTimeMs = kVsyncIntervalMs; |
+ |
+// Stop the preemption once the time for the longest pending IPC drops |
+// below this threshold. |
+const int64_t kStopPreemptThresholdMs = kVsyncIntervalMs; |
+ |
+} // anonymous namespace |
+ |
+scoped_refptr<GpuChannelMessageQueue> GpuChannelMessageQueue::Create( |
+ int32_t stream_id, |
+ gpu::GpuStreamPriority stream_priority, |
+ GpuChannel* channel, |
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, |
+ const scoped_refptr<gpu::PreemptionFlag>& preempting_flag, |
+ const scoped_refptr<gpu::PreemptionFlag>& preempted_flag, |
+ gpu::SyncPointManager* sync_point_manager) { |
+ return new GpuChannelMessageQueue(stream_id, stream_priority, channel, |
+ io_task_runner, preempting_flag, |
+ preempted_flag, sync_point_manager); |
+} |
+ |
+scoped_refptr<gpu::SyncPointOrderData> |
+GpuChannelMessageQueue::GetSyncPointOrderData() { |
+ return sync_point_order_data_; |
+} |
+ |
+GpuChannelMessageQueue::GpuChannelMessageQueue( |
+ int32_t stream_id, |
+ gpu::GpuStreamPriority stream_priority, |
+ GpuChannel* channel, |
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, |
+ const scoped_refptr<gpu::PreemptionFlag>& preempting_flag, |
+ const scoped_refptr<gpu::PreemptionFlag>& preempted_flag, |
+ gpu::SyncPointManager* sync_point_manager) |
+ : stream_id_(stream_id), |
+ stream_priority_(stream_priority), |
+ enabled_(true), |
+ scheduled_(true), |
+ channel_(channel), |
+ preemption_state_(IDLE), |
+ max_preemption_time_( |
+ base::TimeDelta::FromMilliseconds(kMaxPreemptTimeMs)), |
+ timer_(new base::OneShotTimer), |
+ sync_point_order_data_(gpu::SyncPointOrderData::Create()), |
+ io_task_runner_(io_task_runner), |
+ preempting_flag_(preempting_flag), |
+ preempted_flag_(preempted_flag), |
+ sync_point_manager_(sync_point_manager) { |
+ timer_->SetTaskRunner(io_task_runner); |
+ io_thread_checker_.DetachFromThread(); |
+} |
+ |
+GpuChannelMessageQueue::~GpuChannelMessageQueue() { |
+ DCHECK(!enabled_); |
+ DCHECK(channel_messages_.empty()); |
+} |
+ |
+void GpuChannelMessageQueue::Disable() { |
+ { |
+ base::AutoLock auto_lock(channel_lock_); |
+ DCHECK(enabled_); |
+ enabled_ = false; |
+ } |
+ |
+ // We guarantee that the queues will no longer be modified after enabled_ |
+ // is set to false, it is now safe to modify the queue without the lock. |
+ // All public facing modifying functions check enabled_ while all |
+ // private modifying functions DCHECK(enabled_) to enforce this. |
+ while (!channel_messages_.empty()) { |
+ const IPC::Message& msg = channel_messages_.front()->message; |
+ if (msg.is_sync()) { |
+ IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); |
+ reply->set_reply_error(); |
+ channel_->Send(reply); |
+ } |
+ channel_messages_.pop_front(); |
+ } |
+ |
+ sync_point_order_data_->Destroy(); |
+ sync_point_order_data_ = nullptr; |
+ |
+ io_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&GpuChannelMessageQueue::DisableIO, this)); |
+} |
+ |
+void GpuChannelMessageQueue::DisableIO() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ timer_ = nullptr; |
+} |
+ |
+bool GpuChannelMessageQueue::IsScheduled() const { |
+ base::AutoLock lock(channel_lock_); |
+ return scheduled_; |
+} |
+ |
+void GpuChannelMessageQueue::OnRescheduled(bool scheduled) { |
+ base::AutoLock lock(channel_lock_); |
+ DCHECK(enabled_); |
+ if (scheduled_ == scheduled) |
+ return; |
+ scheduled_ = scheduled; |
+ if (scheduled) |
+ channel_->PostHandleMessage(this); |
+ if (preempting_flag_) { |
+ io_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&GpuChannelMessageQueue::UpdatePreemptionState, this)); |
+ } |
+} |
+ |
+uint32_t GpuChannelMessageQueue::GetUnprocessedOrderNum() const { |
+ return sync_point_order_data_->unprocessed_order_num(); |
+} |
+ |
+uint32_t GpuChannelMessageQueue::GetProcessedOrderNum() const { |
+ return sync_point_order_data_->processed_order_num(); |
+} |
+ |
+bool GpuChannelMessageQueue::PushBackMessage(const IPC::Message& message) { |
+ base::AutoLock auto_lock(channel_lock_); |
+ if (enabled_) { |
+ if (message.type() == GpuCommandBufferMsg_WaitForTokenInRange::ID || |
+ message.type() == GpuCommandBufferMsg_WaitForGetOffsetInRange::ID) { |
+ channel_->PostHandleOutOfOrderMessage(message); |
+ return true; |
+ } |
+ |
+ uint32_t order_num = sync_point_order_data_->GenerateUnprocessedOrderNumber( |
+ sync_point_manager_); |
+ scoped_ptr<GpuChannelMessage> msg( |
+ new GpuChannelMessage(message, order_num, base::TimeTicks::Now())); |
+ |
+ if (channel_messages_.empty()) { |
+ DCHECK(scheduled_); |
+ channel_->PostHandleMessage(this); |
+ } |
+ |
+ channel_messages_.push_back(std::move(msg)); |
+ |
+ if (preempting_flag_) |
+ UpdatePreemptionStateHelper(); |
+ |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+const GpuChannelMessage* GpuChannelMessageQueue::BeginMessageProcessing() { |
+ base::AutoLock auto_lock(channel_lock_); |
+ DCHECK(enabled_); |
+ // If we have been preempted by another channel, just post a task to wake up. |
+ if (preempted_flag_ && preempted_flag_->IsSet()) { |
+ channel_->PostHandleMessage(this); |
+ return nullptr; |
+ } |
+ if (channel_messages_.empty()) |
+ return nullptr; |
+ sync_point_order_data_->BeginProcessingOrderNumber( |
+ channel_messages_.front()->order_number); |
+ return channel_messages_.front().get(); |
+} |
+ |
+void GpuChannelMessageQueue::PauseMessageProcessing() { |
+ base::AutoLock auto_lock(channel_lock_); |
+ DCHECK(!channel_messages_.empty()); |
+ |
+ // If we have been preempted by another channel, just post a task to wake up. |
+ if (scheduled_) |
+ channel_->PostHandleMessage(this); |
+ |
+ sync_point_order_data_->PauseProcessingOrderNumber( |
+ channel_messages_.front()->order_number); |
+} |
+ |
+void GpuChannelMessageQueue::FinishMessageProcessing() { |
+ base::AutoLock auto_lock(channel_lock_); |
+ DCHECK(!channel_messages_.empty()); |
+ DCHECK(scheduled_); |
+ |
+ sync_point_order_data_->FinishProcessingOrderNumber( |
+ channel_messages_.front()->order_number); |
+ channel_messages_.pop_front(); |
+ |
+ if (!channel_messages_.empty()) |
+ channel_->PostHandleMessage(this); |
+ |
+ if (preempting_flag_) { |
+ io_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&GpuChannelMessageQueue::UpdatePreemptionState, this)); |
+ } |
+} |
+ |
+void GpuChannelMessageQueue::UpdatePreemptionState() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ base::AutoLock lock(channel_lock_); |
+ UpdatePreemptionStateHelper(); |
+} |
+ |
+void GpuChannelMessageQueue::UpdatePreemptionStateHelper() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ switch (preemption_state_) { |
+ case IDLE: |
+ UpdateStateIdle(); |
+ break; |
+ case WAITING: |
+ UpdateStateWaiting(); |
+ break; |
+ case CHECKING: |
+ UpdateStateChecking(); |
+ break; |
+ case PREEMPTING: |
+ UpdateStatePreempting(); |
+ break; |
+ case WOULD_PREEMPT_DESCHEDULED: |
+ UpdateStateWouldPreemptDescheduled(); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ } |
+} |
+ |
+void GpuChannelMessageQueue::UpdateStateIdle() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ DCHECK(!timer_->IsRunning()); |
+ if (!channel_messages_.empty()) |
+ TransitionToWaiting(); |
+} |
+ |
+void GpuChannelMessageQueue::UpdateStateWaiting() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ // Transition to CHECKING if timer fired. |
+ if (!timer_->IsRunning()) |
+ TransitionToChecking(); |
+} |
+ |
+void GpuChannelMessageQueue::UpdateStateChecking() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ if (!channel_messages_.empty()) { |
+ base::TimeTicks time_recv = channel_messages_.front()->time_received; |
+ base::TimeDelta time_elapsed = base::TimeTicks::Now() - time_recv; |
+ if (time_elapsed.InMilliseconds() < kPreemptWaitTimeMs) { |
+ // Schedule another check for when the IPC may go long. |
+ timer_->Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kPreemptWaitTimeMs) - time_elapsed, |
+ this, &GpuChannelMessageQueue::UpdatePreemptionState); |
+ } else { |
+ timer_->Stop(); |
+ if (!scheduled_) |
+ TransitionToWouldPreemptDescheduled(); |
+ else |
+ TransitionToPreempting(); |
+ } |
+ } |
+} |
+ |
+void GpuChannelMessageQueue::UpdateStatePreempting() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ // We should stop preempting if the timer fired or for other conditions. |
+ if (!timer_->IsRunning() || ShouldTransitionToIdle()) { |
+ TransitionToIdle(); |
+ } else if (!scheduled_) { |
+ // Save the remaining preemption time before stopping the timer. |
+ max_preemption_time_ = timer_->desired_run_time() - base::TimeTicks::Now(); |
+ timer_->Stop(); |
+ TransitionToWouldPreemptDescheduled(); |
+ } |
+} |
+ |
+void GpuChannelMessageQueue::UpdateStateWouldPreemptDescheduled() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ DCHECK(!timer_->IsRunning()); |
+ if (ShouldTransitionToIdle()) { |
+ TransitionToIdle(); |
+ } else if (scheduled_) { |
+ TransitionToPreempting(); |
+ } |
+} |
+ |
+bool GpuChannelMessageQueue::ShouldTransitionToIdle() const { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ DCHECK(preemption_state_ == PREEMPTING || |
+ preemption_state_ == WOULD_PREEMPT_DESCHEDULED); |
+ if (channel_messages_.empty()) { |
+ return true; |
+ } else { |
+ base::TimeTicks next_tick = channel_messages_.front()->time_received; |
+ base::TimeDelta time_elapsed = base::TimeTicks::Now() - next_tick; |
+ if (time_elapsed.InMilliseconds() < kStopPreemptThresholdMs) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+void GpuChannelMessageQueue::TransitionToIdle() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ DCHECK(preemption_state_ == PREEMPTING || |
+ preemption_state_ == WOULD_PREEMPT_DESCHEDULED); |
+ |
+ preemption_state_ = IDLE; |
+ preempting_flag_->Reset(); |
+ |
+ max_preemption_time_ = base::TimeDelta::FromMilliseconds(kMaxPreemptTimeMs); |
+ timer_->Stop(); |
+ |
+ TRACE_COUNTER_ID1("gpu", "GpuChannel::Preempting", this, 0); |
+ |
+ UpdateStateIdle(); |
+} |
+ |
+void GpuChannelMessageQueue::TransitionToWaiting() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ DCHECK_EQ(preemption_state_, IDLE); |
+ DCHECK(!timer_->IsRunning()); |
+ |
+ preemption_state_ = WAITING; |
+ |
+ timer_->Start(FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kPreemptWaitTimeMs), this, |
+ &GpuChannelMessageQueue::UpdatePreemptionState); |
+} |
+ |
+void GpuChannelMessageQueue::TransitionToChecking() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ DCHECK_EQ(preemption_state_, WAITING); |
+ DCHECK(!timer_->IsRunning()); |
+ |
+ preemption_state_ = CHECKING; |
+ |
+ UpdateStateChecking(); |
+} |
+ |
+void GpuChannelMessageQueue::TransitionToPreempting() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ DCHECK(preemption_state_ == CHECKING || |
+ preemption_state_ == WOULD_PREEMPT_DESCHEDULED); |
+ DCHECK(scheduled_); |
+ |
+ preemption_state_ = PREEMPTING; |
+ preempting_flag_->Set(); |
+ TRACE_COUNTER_ID1("gpu", "GpuChannel::Preempting", this, 1); |
+ |
+ DCHECK_LE(max_preemption_time_, |
+ base::TimeDelta::FromMilliseconds(kMaxPreemptTimeMs)); |
+ timer_->Start(FROM_HERE, max_preemption_time_, this, |
+ &GpuChannelMessageQueue::UpdatePreemptionState); |
+} |
+ |
+void GpuChannelMessageQueue::TransitionToWouldPreemptDescheduled() { |
+ DCHECK(io_thread_checker_.CalledOnValidThread()); |
+ DCHECK(preempting_flag_); |
+ channel_lock_.AssertAcquired(); |
+ DCHECK(preemption_state_ == CHECKING || preemption_state_ == PREEMPTING); |
+ DCHECK(!scheduled_); |
+ |
+ preemption_state_ = WOULD_PREEMPT_DESCHEDULED; |
+ preempting_flag_->Reset(); |
+ TRACE_COUNTER_ID1("gpu", "GpuChannel::Preempting", this, 0); |
+} |
+ |
+GpuChannelMessageFilter::GpuChannelMessageFilter() |
+ : sender_(nullptr), peer_pid_(base::kNullProcessId) {} |
+ |
+GpuChannelMessageFilter::~GpuChannelMessageFilter() {} |
+ |
+void GpuChannelMessageFilter::OnFilterAdded(IPC::Sender* sender) { |
+ DCHECK(!sender_); |
+ sender_ = sender; |
+ for (scoped_refptr<IPC::MessageFilter>& filter : channel_filters_) { |
+ filter->OnFilterAdded(sender_); |
+ } |
+} |
+ |
+void GpuChannelMessageFilter::OnFilterRemoved() { |
+ DCHECK(sender_); |
+ for (scoped_refptr<IPC::MessageFilter>& filter : channel_filters_) { |
+ filter->OnFilterRemoved(); |
+ } |
+ sender_ = nullptr; |
+ peer_pid_ = base::kNullProcessId; |
+} |
+ |
+void GpuChannelMessageFilter::OnChannelConnected(int32_t peer_pid) { |
+ DCHECK(peer_pid_ == base::kNullProcessId); |
+ peer_pid_ = peer_pid; |
+ for (scoped_refptr<IPC::MessageFilter>& filter : channel_filters_) { |
+ filter->OnChannelConnected(peer_pid); |
+ } |
+} |
+ |
+void GpuChannelMessageFilter::OnChannelError() { |
+ for (scoped_refptr<IPC::MessageFilter>& filter : channel_filters_) { |
+ filter->OnChannelError(); |
+ } |
+} |
+ |
+void GpuChannelMessageFilter::OnChannelClosing() { |
+ for (scoped_refptr<IPC::MessageFilter>& filter : channel_filters_) { |
+ filter->OnChannelClosing(); |
+ } |
+} |
+ |
+void GpuChannelMessageFilter::AddChannelFilter( |
+ scoped_refptr<IPC::MessageFilter> filter) { |
+ channel_filters_.push_back(filter); |
+ if (sender_) |
+ filter->OnFilterAdded(sender_); |
+ if (peer_pid_ != base::kNullProcessId) |
+ filter->OnChannelConnected(peer_pid_); |
+} |
+ |
+void GpuChannelMessageFilter::RemoveChannelFilter( |
+ scoped_refptr<IPC::MessageFilter> filter) { |
+ if (sender_) |
+ filter->OnFilterRemoved(); |
+ channel_filters_.erase( |
+ std::find(channel_filters_.begin(), channel_filters_.end(), filter)); |
+} |
+ |
+// This gets called from the main thread and assumes that all messages which |
+// lead to creation of a new route are synchronous messages. |
+// TODO(sunnyps): Create routes (and streams) on the IO thread so that we can |
+// make the CreateCommandBuffer/VideoDecoder/VideoEncoder messages asynchronous. |
+void GpuChannelMessageFilter::AddRoute( |
+ int32_t route_id, |
+ const scoped_refptr<GpuChannelMessageQueue>& queue) { |
+ base::AutoLock lock(routes_lock_); |
+ routes_.insert(std::make_pair(route_id, queue)); |
+} |
+ |
+void GpuChannelMessageFilter::RemoveRoute(int32_t route_id) { |
+ base::AutoLock lock(routes_lock_); |
+ routes_.erase(route_id); |
+} |
+ |
+bool GpuChannelMessageFilter::OnMessageReceived(const IPC::Message& message) { |
+ DCHECK(sender_); |
+ |
+ if (message.should_unblock() || message.is_reply()) |
+ return MessageErrorHandler(message, "Unexpected message type"); |
+ |
+ if (message.type() == GpuChannelMsg_Nop::ID) { |
+ IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); |
+ Send(reply); |
+ return true; |
+ } |
+ |
+ for (scoped_refptr<IPC::MessageFilter>& filter : channel_filters_) { |
+ if (filter->OnMessageReceived(message)) |
+ return true; |
+ } |
+ |
+ scoped_refptr<GpuChannelMessageQueue> message_queue = |
+ LookupStreamByRoute(message.routing_id()); |
+ |
+ if (!message_queue) |
+ return MessageErrorHandler(message, "Could not find message queue"); |
+ |
+ if (!message_queue->PushBackMessage(message)) |
+ return MessageErrorHandler(message, "Channel destroyed"); |
+ |
+ return true; |
+} |
+ |
+bool GpuChannelMessageFilter::Send(IPC::Message* message) { |
+ return sender_->Send(message); |
+} |
+ |
+scoped_refptr<GpuChannelMessageQueue> |
+GpuChannelMessageFilter::LookupStreamByRoute(int32_t route_id) { |
+ base::AutoLock lock(routes_lock_); |
+ auto it = routes_.find(route_id); |
+ if (it != routes_.end()) |
+ return it->second; |
+ return nullptr; |
+} |
+ |
+bool GpuChannelMessageFilter::MessageErrorHandler(const IPC::Message& message, |
+ const char* error_msg) { |
+ DLOG(ERROR) << error_msg; |
+ if (message.is_sync()) { |
+ IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); |
+ reply->set_reply_error(); |
+ Send(reply); |
+ } |
+ return true; |
+} |
+ |
+GpuChannel::GpuChannel(GpuChannelManager* gpu_channel_manager, |
+ gpu::SyncPointManager* sync_point_manager, |
+ GpuWatchdog* watchdog, |
+ gfx::GLShareGroup* share_group, |
+ gpu::gles2::MailboxManager* mailbox, |
+ gpu::PreemptionFlag* preempting_flag, |
+ gpu::PreemptionFlag* preempted_flag, |
+ base::SingleThreadTaskRunner* task_runner, |
+ base::SingleThreadTaskRunner* io_task_runner, |
+ int32_t client_id, |
+ uint64_t client_tracing_id, |
+ bool allow_view_command_buffers, |
+ bool allow_real_time_streams) |
+ : gpu_channel_manager_(gpu_channel_manager), |
+ sync_point_manager_(sync_point_manager), |
+ unhandled_message_listener_(nullptr), |
+ channel_id_(IPC::Channel::GenerateVerifiedChannelID("gpu")), |
+ preempting_flag_(preempting_flag), |
+ preempted_flag_(preempted_flag), |
+ client_id_(client_id), |
+ client_tracing_id_(client_tracing_id), |
+ task_runner_(task_runner), |
+ io_task_runner_(io_task_runner), |
+ share_group_(share_group), |
+ mailbox_manager_(mailbox), |
+ subscription_ref_set_(new gpu::gles2::SubscriptionRefSet), |
+ pending_valuebuffer_state_(new gpu::ValueStateMap), |
+ watchdog_(watchdog), |
+ allow_view_command_buffers_(allow_view_command_buffers), |
+ allow_real_time_streams_(allow_real_time_streams), |
+ weak_factory_(this) { |
+ DCHECK(gpu_channel_manager); |
+ DCHECK(client_id); |
+ |
+ filter_ = new GpuChannelMessageFilter(); |
+ |
+ scoped_refptr<GpuChannelMessageQueue> control_queue = |
+ CreateStream(gpu::GPU_STREAM_DEFAULT, gpu::GpuStreamPriority::HIGH); |
+ AddRouteToStream(MSG_ROUTING_CONTROL, gpu::GPU_STREAM_DEFAULT); |
+ |
+ subscription_ref_set_->AddObserver(this); |
+} |
+ |
+GpuChannel::~GpuChannel() { |
+ // Clear stubs first because of dependencies. |
+ stubs_.clear(); |
+ |
+ for (auto& kv : streams_) |
+ kv.second->Disable(); |
+ |
+ subscription_ref_set_->RemoveObserver(this); |
+ if (preempting_flag_.get()) |
+ preempting_flag_->Reset(); |
+} |
+ |
+IPC::ChannelHandle GpuChannel::Init(base::WaitableEvent* shutdown_event) { |
+ DCHECK(shutdown_event); |
+ DCHECK(!channel_); |
+ |
+ IPC::ChannelHandle channel_handle(channel_id_); |
+ |
+ channel_ = |
+ IPC::SyncChannel::Create(channel_handle, IPC::Channel::MODE_SERVER, this, |
+ io_task_runner_, false, shutdown_event); |
+ |
+#if defined(OS_POSIX) |
+ // On POSIX, pass the renderer-side FD. Also mark it as auto-close so |
+ // that it gets closed after it has been sent. |
+ base::ScopedFD renderer_fd = channel_->TakeClientFileDescriptor(); |
+ DCHECK(renderer_fd.is_valid()); |
+ channel_handle.socket = base::FileDescriptor(std::move(renderer_fd)); |
+#endif |
+ |
+ channel_->AddFilter(filter_.get()); |
+ |
+ return channel_handle; |
+} |
+ |
+void GpuChannel::SetUnhandledMessageListener(IPC::Listener* listener) { |
+ unhandled_message_listener_ = listener; |
+} |
+ |
+base::WeakPtr<GpuChannel> GpuChannel::AsWeakPtr() { |
+ return weak_factory_.GetWeakPtr(); |
+} |
+ |
+base::ProcessId GpuChannel::GetClientPID() const { |
+ return channel_->GetPeerPID(); |
+} |
+ |
+uint32_t GpuChannel::GetProcessedOrderNum() const { |
+ uint32_t processed_order_num = 0; |
+ for (auto& kv : streams_) { |
+ processed_order_num = |
+ std::max(processed_order_num, kv.second->GetProcessedOrderNum()); |
+ } |
+ return processed_order_num; |
+} |
+ |
+uint32_t GpuChannel::GetUnprocessedOrderNum() const { |
+ uint32_t unprocessed_order_num = 0; |
+ for (auto& kv : streams_) { |
+ unprocessed_order_num = |
+ std::max(unprocessed_order_num, kv.second->GetUnprocessedOrderNum()); |
+ } |
+ return unprocessed_order_num; |
+} |
+ |
+bool GpuChannel::OnMessageReceived(const IPC::Message& msg) { |
+ // All messages should be pushed to channel_messages_ and handled separately. |
+ NOTREACHED(); |
+ return false; |
+} |
+ |
+void GpuChannel::OnChannelError() { |
+ gpu_channel_manager_->RemoveChannel(client_id_); |
+} |
+ |
+bool GpuChannel::Send(IPC::Message* message) { |
+ // The GPU process must never send a synchronous IPC message to the renderer |
+ // process. This could result in deadlock. |
+ DCHECK(!message->is_sync()); |
+ |
+ DVLOG(1) << "sending message @" << message << " on channel @" << this |
+ << " with type " << message->type(); |
+ |
+ if (!channel_) { |
+ delete message; |
+ return false; |
+ } |
+ |
+ return channel_->Send(message); |
+} |
+ |
+void GpuChannel::OnAddSubscription(unsigned int target) { |
+ gpu_channel_manager()->delegate()->AddSubscription(client_id_, target); |
+} |
+ |
+void GpuChannel::OnRemoveSubscription(unsigned int target) { |
+ gpu_channel_manager()->delegate()->RemoveSubscription(client_id_, target); |
+} |
+ |
+void GpuChannel::OnStreamRescheduled(int32_t stream_id, bool scheduled) { |
+ scoped_refptr<GpuChannelMessageQueue> queue = LookupStream(stream_id); |
+ DCHECK(queue); |
+ queue->OnRescheduled(scheduled); |
+} |
+ |
+GpuCommandBufferStub* GpuChannel::LookupCommandBuffer(int32_t route_id) { |
+ return stubs_.get(route_id); |
+} |
+ |
+void GpuChannel::LoseAllContexts() { |
+ gpu_channel_manager_->LoseAllContexts(); |
+} |
+ |
+void GpuChannel::MarkAllContextsLost() { |
+ for (auto& kv : stubs_) |
+ kv.second->MarkContextLost(); |
+} |
+ |
+bool GpuChannel::AddRoute(int32_t route_id, |
+ int32_t stream_id, |
+ IPC::Listener* listener) { |
+ if (router_.AddRoute(route_id, listener)) { |
+ AddRouteToStream(route_id, stream_id); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+void GpuChannel::RemoveRoute(int32_t route_id) { |
+ router_.RemoveRoute(route_id); |
+ RemoveRouteFromStream(route_id); |
+} |
+ |
+bool GpuChannel::OnControlMessageReceived(const IPC::Message& msg) { |
+ bool handled = true; |
+ IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg) |
+ IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateCommandBuffer, |
+ OnCreateCommandBuffer) |
+ IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer, |
+ OnDestroyCommandBuffer) |
+ IPC_MESSAGE_UNHANDLED(handled = false) |
+ IPC_END_MESSAGE_MAP() |
+ return handled; |
+} |
+ |
+scoped_refptr<gpu::SyncPointOrderData> GpuChannel::GetSyncPointOrderData( |
+ int32_t stream_id) { |
+ auto it = streams_.find(stream_id); |
+ DCHECK(it != streams_.end()); |
+ DCHECK(it->second); |
+ return it->second->GetSyncPointOrderData(); |
+} |
+ |
+void GpuChannel::PostHandleMessage( |
+ const scoped_refptr<GpuChannelMessageQueue>& queue) { |
+ task_runner_->PostTask(FROM_HERE, |
+ base::Bind(&GpuChannel::HandleMessage, |
+ weak_factory_.GetWeakPtr(), queue)); |
+} |
+ |
+void GpuChannel::PostHandleOutOfOrderMessage(const IPC::Message& msg) { |
+ task_runner_->PostTask(FROM_HERE, |
+ base::Bind(&GpuChannel::HandleOutOfOrderMessage, |
+ weak_factory_.GetWeakPtr(), msg)); |
+} |
+ |
+void GpuChannel::HandleMessage( |
+ const scoped_refptr<GpuChannelMessageQueue>& message_queue) { |
+ const GpuChannelMessage* channel_msg = |
+ message_queue->BeginMessageProcessing(); |
+ if (!channel_msg) |
+ return; |
+ |
+ const IPC::Message& msg = channel_msg->message; |
+ int32_t routing_id = msg.routing_id(); |
+ GpuCommandBufferStub* stub = stubs_.get(routing_id); |
+ |
+ DCHECK(!stub || stub->IsScheduled()); |
+ |
+ DVLOG(1) << "received message @" << &msg << " on channel @" << this |
+ << " with type " << msg.type(); |
+ |
+ HandleMessageHelper(msg); |
+ |
+ // If we get descheduled or yield while processing a message. |
+ if (stub && stub->HasUnprocessedCommands()) { |
+ DCHECK_EQ((uint32_t)GpuCommandBufferMsg_AsyncFlush::ID, msg.type()); |
+ message_queue->PauseMessageProcessing(); |
+ } else { |
+ message_queue->FinishMessageProcessing(); |
+ } |
+} |
+ |
+void GpuChannel::HandleMessageHelper(const IPC::Message& msg) { |
+ int32_t routing_id = msg.routing_id(); |
+ |
+ bool handled = false; |
+ if (routing_id == MSG_ROUTING_CONTROL) { |
+ handled = OnControlMessageReceived(msg); |
+ } else { |
+ handled = router_.RouteMessage(msg); |
+ } |
+ |
+ if (!handled && unhandled_message_listener_) |
+ handled = unhandled_message_listener_->OnMessageReceived(msg); |
+ |
+ // Respond to sync messages even if router failed to route. |
+ if (!handled && msg.is_sync()) { |
+ IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); |
+ reply->set_reply_error(); |
+ Send(reply); |
+ } |
+} |
+ |
+void GpuChannel::HandleOutOfOrderMessage(const IPC::Message& msg) { |
+ HandleMessageHelper(msg); |
+} |
+ |
+void GpuChannel::HandleMessageForTesting(const IPC::Message& msg) { |
+ HandleMessageHelper(msg); |
+} |
+ |
+scoped_refptr<GpuChannelMessageQueue> GpuChannel::CreateStream( |
+ int32_t stream_id, |
+ gpu::GpuStreamPriority stream_priority) { |
+ DCHECK(streams_.find(stream_id) == streams_.end()); |
+ scoped_refptr<GpuChannelMessageQueue> queue = GpuChannelMessageQueue::Create( |
+ stream_id, stream_priority, this, io_task_runner_, |
+ (stream_id == gpu::GPU_STREAM_DEFAULT) ? preempting_flag_ : nullptr, |
+ preempted_flag_, sync_point_manager_); |
+ streams_.insert(std::make_pair(stream_id, queue)); |
+ streams_to_num_routes_.insert(std::make_pair(stream_id, 0)); |
+ return queue; |
+} |
+ |
+scoped_refptr<GpuChannelMessageQueue> GpuChannel::LookupStream( |
+ int32_t stream_id) { |
+ auto stream_it = streams_.find(stream_id); |
+ if (stream_it != streams_.end()) |
+ return stream_it->second; |
+ return nullptr; |
+} |
+ |
+void GpuChannel::DestroyStreamIfNecessary( |
+ const scoped_refptr<GpuChannelMessageQueue>& queue) { |
+ int32_t stream_id = queue->stream_id(); |
+ if (streams_to_num_routes_[stream_id] == 0) { |
+ queue->Disable(); |
+ streams_to_num_routes_.erase(stream_id); |
+ streams_.erase(stream_id); |
+ } |
+} |
+ |
+void GpuChannel::AddRouteToStream(int32_t route_id, int32_t stream_id) { |
+ DCHECK(streams_.find(stream_id) != streams_.end()); |
+ DCHECK(routes_to_streams_.find(route_id) == routes_to_streams_.end()); |
+ streams_to_num_routes_[stream_id]++; |
+ routes_to_streams_.insert(std::make_pair(route_id, stream_id)); |
+ filter_->AddRoute(route_id, streams_[stream_id]); |
+} |
+ |
+void GpuChannel::RemoveRouteFromStream(int32_t route_id) { |
+ DCHECK(routes_to_streams_.find(route_id) != routes_to_streams_.end()); |
+ int32_t stream_id = routes_to_streams_[route_id]; |
+ DCHECK(streams_.find(stream_id) != streams_.end()); |
+ routes_to_streams_.erase(route_id); |
+ streams_to_num_routes_[stream_id]--; |
+ filter_->RemoveRoute(route_id); |
+ DestroyStreamIfNecessary(streams_[stream_id]); |
+} |
+ |
+#if defined(OS_ANDROID) |
+const GpuCommandBufferStub* GpuChannel::GetOneStub() const { |
+ for (const auto& kv : stubs_) { |
+ const GpuCommandBufferStub* stub = kv.second; |
+ if (stub->decoder() && !stub->decoder()->WasContextLost()) |
+ return stub; |
+ } |
+ return nullptr; |
+} |
+#endif |
+ |
+void GpuChannel::OnCreateCommandBuffer( |
+ gpu::SurfaceHandle surface_handle, |
+ const gfx::Size& size, |
+ const GPUCreateCommandBufferConfig& init_params, |
+ int32_t route_id, |
+ bool* succeeded) { |
+ TRACE_EVENT2("gpu", "GpuChannel::OnCreateCommandBuffer", "route_id", route_id, |
+ "offscreen", (surface_handle == gpu::kNullSurfaceHandle)); |
+ *succeeded = false; |
+ if (surface_handle != gpu::kNullSurfaceHandle && |
+ !allow_view_command_buffers_) { |
+ DLOG(ERROR) << "GpuChannel::CreateCommandBuffer(): attempt to create a " |
+ "view context on a non-priviledged channel"; |
+ return; |
+ } |
+ |
+ int32_t share_group_id = init_params.share_group_id; |
+ GpuCommandBufferStub* share_group = stubs_.get(share_group_id); |
+ |
+ if (!share_group && share_group_id != MSG_ROUTING_NONE) { |
+ DLOG(ERROR) |
+ << "GpuChannel::OnCreateCommandBuffer(): invalid share group id"; |
+ return; |
+ } |
+ |
+ int32_t stream_id = init_params.stream_id; |
+ if (share_group && stream_id != share_group->stream_id()) { |
+ DLOG(ERROR) << "GpuChannel::OnCreateCommandBuffer(): stream id does not " |
+ "match share group stream id"; |
+ return; |
+ } |
+ |
+ gpu::GpuStreamPriority stream_priority = init_params.stream_priority; |
+ if (!allow_real_time_streams_ && |
+ stream_priority == gpu::GpuStreamPriority::REAL_TIME) { |
+ DLOG(ERROR) << "GpuChannel::OnCreateCommandBuffer(): real time stream " |
+ "priority not allowed"; |
+ return; |
+ } |
+ |
+ scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub( |
+ this, sync_point_manager_, task_runner_.get(), share_group, |
+ surface_handle, mailbox_manager_.get(), preempted_flag_.get(), |
+ subscription_ref_set_.get(), pending_valuebuffer_state_.get(), size, |
+ disallowed_features_, init_params.attribs, init_params.gpu_preference, |
+ init_params.stream_id, route_id, watchdog_, init_params.active_url)); |
+ |
+ scoped_refptr<GpuChannelMessageQueue> queue = LookupStream(stream_id); |
+ if (!queue) |
+ queue = CreateStream(stream_id, stream_priority); |
+ |
+ if (!AddRoute(route_id, stream_id, stub.get())) { |
+ DestroyStreamIfNecessary(queue); |
+ DLOG(ERROR) << "GpuChannel::OnCreateCommandBuffer(): failed to add route"; |
+ return; |
+ } |
+ |
+ stubs_.set(route_id, std::move(stub)); |
+ *succeeded = true; |
+} |
+ |
+void GpuChannel::OnDestroyCommandBuffer(int32_t route_id) { |
+ TRACE_EVENT1("gpu", "GpuChannel::OnDestroyCommandBuffer", |
+ "route_id", route_id); |
+ |
+ scoped_ptr<GpuCommandBufferStub> stub = stubs_.take_and_erase(route_id); |
+ // In case the renderer is currently blocked waiting for a sync reply from the |
+ // stub, we need to make sure to reschedule the correct stream here. |
+ if (stub && !stub->IsScheduled()) { |
+ // This stub won't get a chance to reschedule the stream so do that now. |
+ OnStreamRescheduled(stub->stream_id(), true); |
+ } |
+ |
+ RemoveRoute(route_id); |
+} |
+ |
+void GpuChannel::CacheShader(const std::string& key, |
+ const std::string& shader) { |
+ gpu_channel_manager_->delegate()->StoreShaderToDisk(client_id_, key, shader); |
+} |
+ |
+void GpuChannel::AddFilter(IPC::MessageFilter* filter) { |
+ io_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&GpuChannelMessageFilter::AddChannelFilter, |
+ filter_, make_scoped_refptr(filter))); |
+} |
+ |
+void GpuChannel::RemoveFilter(IPC::MessageFilter* filter) { |
+ io_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&GpuChannelMessageFilter::RemoveChannelFilter, |
+ filter_, make_scoped_refptr(filter))); |
+} |
+ |
+uint64_t GpuChannel::GetMemoryUsage() { |
+ // Collect the unique memory trackers in use by the |stubs_|. |
+ std::set<gpu::gles2::MemoryTracker*> unique_memory_trackers; |
+ for (auto& kv : stubs_) |
+ unique_memory_trackers.insert(kv.second->GetMemoryTracker()); |
+ |
+ // Sum the memory usage for all unique memory trackers. |
+ uint64_t size = 0; |
+ for (auto* tracker : unique_memory_trackers) { |
+ size += gpu_channel_manager()->gpu_memory_manager()->GetTrackerMemoryUsage( |
+ tracker); |
+ } |
+ |
+ return size; |
+} |
+ |
+scoped_refptr<gl::GLImage> GpuChannel::CreateImageForGpuMemoryBuffer( |
+ const gfx::GpuMemoryBufferHandle& handle, |
+ const gfx::Size& size, |
+ gfx::BufferFormat format, |
+ uint32_t internalformat) { |
+ switch (handle.type) { |
+ case gfx::SHARED_MEMORY_BUFFER: { |
+ if (!base::IsValueInRangeForNumericType<size_t>(handle.stride)) |
+ return nullptr; |
+ scoped_refptr<gl::GLImageSharedMemory> image( |
+ new gl::GLImageSharedMemory(size, internalformat)); |
+ if (!image->Initialize(handle.handle, handle.id, format, handle.offset, |
+ handle.stride)) { |
+ return nullptr; |
+ } |
+ |
+ return image; |
+ } |
+ default: { |
+ GpuChannelManager* manager = gpu_channel_manager(); |
+ if (!manager->gpu_memory_buffer_factory()) |
+ return nullptr; |
+ |
+ return manager->gpu_memory_buffer_factory() |
+ ->AsImageFactory() |
+ ->CreateImageForGpuMemoryBuffer(handle, |
+ size, |
+ format, |
+ internalformat, |
+ client_id_); |
+ } |
+ } |
+} |
+ |
+void GpuChannel::HandleUpdateValueState( |
+ unsigned int target, const gpu::ValueState& state) { |
+ pending_valuebuffer_state_->UpdateState(target, state); |
+} |
+ |
+} // namespace content |