| // 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 "gpu/ipc/service/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/threading/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 "gpu/command_buffer/common/mailbox.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/ipc/common/gpu_messages.h" |
| #include "gpu/ipc/service/gpu_channel_manager.h" |
| #include "gpu/ipc/service/gpu_channel_manager_delegate.h" |
| #include "gpu/ipc/service/gpu_memory_buffer_factory.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 gpu { |
| 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, |
| GpuStreamPriority stream_priority, |
| GpuChannel* channel, |
| const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, |
| const scoped_refptr<PreemptionFlag>& preempting_flag, |
| const scoped_refptr<PreemptionFlag>& preempted_flag, |
| SyncPointManager* sync_point_manager) { |
| return new GpuChannelMessageQueue(stream_id, stream_priority, channel, |
| io_task_runner, preempting_flag, |
| preempted_flag, sync_point_manager); |
| } |
| |
| scoped_refptr<SyncPointOrderData> |
| GpuChannelMessageQueue::GetSyncPointOrderData() { |
| return sync_point_order_data_; |
| } |
| |
| GpuChannelMessageQueue::GpuChannelMessageQueue( |
| int32_t stream_id, |
| GpuStreamPriority stream_priority, |
| GpuChannel* channel, |
| const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, |
| const scoped_refptr<PreemptionFlag>& preempting_flag, |
| const scoped_refptr<PreemptionFlag>& preempted_flag, |
| 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_(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_); |
| std::unique_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, |
| SyncPointManager* sync_point_manager, |
| GpuWatchdogThread* watchdog, |
| gl::GLShareGroup* share_group, |
| gles2::MailboxManager* mailbox, |
| PreemptionFlag* preempting_flag, |
| 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), |
| 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), |
| 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_STREAM_DEFAULT, GpuStreamPriority::HIGH); |
| AddRouteToStream(MSG_ROUTING_CONTROL, GPU_STREAM_DEFAULT); |
| } |
| |
| GpuChannel::~GpuChannel() { |
| // Clear stubs first because of dependencies. |
| stubs_.clear(); |
| |
| for (auto& kv : streams_) |
| kv.second->Disable(); |
| |
| if (preempting_flag_.get()) |
| preempting_flag_->Reset(); |
| } |
| |
| IPC::ChannelHandle GpuChannel::Init(base::WaitableEvent* shutdown_event) { |
| DCHECK(shutdown_event); |
| DCHECK(!channel_); |
| |
| IPC::ChannelHandle client_handle; |
| IPC::ChannelHandle server_handle; |
| IPC::Channel::GenerateMojoChannelHandlePair( |
| "gpu", &client_handle, &server_handle); |
| channel_id_ = client_handle.name; |
| |
| channel_ = |
| IPC::SyncChannel::Create(server_handle, IPC::Channel::MODE_SERVER, |
| this, io_task_runner_, false, shutdown_event); |
| |
| channel_->AddFilter(filter_.get()); |
| |
| return client_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::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_HANDLER(GpuChannelMsg_GetDriverBugWorkArounds, |
| OnGetDriverBugWorkArounds) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| return handled; |
| } |
| |
| scoped_refptr<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()) || |
| !message_queue->IsScheduled()) { |
| DCHECK((uint32_t)GpuCommandBufferMsg_AsyncFlush::ID == msg.type() || |
| (uint32_t)GpuCommandBufferMsg_WaitSyncToken::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, |
| 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_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( |
| const GPUCreateCommandBufferConfig& init_params, |
| int32_t route_id, |
| base::SharedMemoryHandle shared_state_handle, |
| bool* result, |
| gpu::Capabilities* capabilities) { |
| TRACE_EVENT2("gpu", "GpuChannel::OnCreateCommandBuffer", "route_id", route_id, |
| "offscreen", (init_params.surface_handle == kNullSurfaceHandle)); |
| std::unique_ptr<base::SharedMemory> shared_state_shm( |
| new base::SharedMemory(shared_state_handle, false)); |
| std::unique_ptr<GpuCommandBufferStub> stub = |
| CreateCommandBuffer(init_params, route_id, std::move(shared_state_shm)); |
| if (stub) { |
| *result = true; |
| *capabilities = stub->decoder()->GetCapabilities(); |
| stubs_.set(route_id, std::move(stub)); |
| } else { |
| *result = false; |
| *capabilities = gpu::Capabilities(); |
| } |
| } |
| |
| std::unique_ptr<GpuCommandBufferStub> GpuChannel::CreateCommandBuffer( |
| const GPUCreateCommandBufferConfig& init_params, |
| int32_t route_id, |
| std::unique_ptr<base::SharedMemory> shared_state_shm) { |
| if (init_params.surface_handle != kNullSurfaceHandle && |
| !allow_view_command_buffers_) { |
| DLOG(ERROR) << "GpuChannel::CreateCommandBuffer(): attempt to create a " |
| "view context on a non-priviledged channel"; |
| return nullptr; |
| } |
| |
| 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::CreateCommandBuffer(): invalid share group id"; |
| return nullptr; |
| } |
| |
| int32_t stream_id = init_params.stream_id; |
| if (share_group && stream_id != share_group->stream_id()) { |
| DLOG(ERROR) << "GpuChannel::CreateCommandBuffer(): stream id does not " |
| "match share group stream id"; |
| return nullptr; |
| } |
| |
| GpuStreamPriority stream_priority = init_params.stream_priority; |
| if (!allow_real_time_streams_ && |
| stream_priority == GpuStreamPriority::REAL_TIME) { |
| DLOG(ERROR) << "GpuChannel::CreateCommandBuffer(): real time stream " |
| "priority not allowed"; |
| return nullptr; |
| } |
| |
| if (share_group && !share_group->decoder()) { |
| // This should catch test errors where we did not Initialize the |
| // share_group's CommandBuffer. |
| DLOG(ERROR) << "GpuChannel::CreateCommandBuffer(): shared context was " |
| "not initialized"; |
| return nullptr; |
| } |
| |
| if (share_group && share_group->decoder()->WasContextLost()) { |
| DLOG(ERROR) << "GpuChannel::CreateCommandBuffer(): shared context was " |
| "already lost"; |
| return nullptr; |
| } |
| |
| scoped_refptr<GpuChannelMessageQueue> queue = LookupStream(stream_id); |
| if (!queue) |
| queue = CreateStream(stream_id, stream_priority); |
| |
| std::unique_ptr<GpuCommandBufferStub> stub(GpuCommandBufferStub::Create( |
| this, share_group, init_params, route_id, std::move(shared_state_shm))); |
| |
| if (!stub) { |
| DestroyStreamIfNecessary(queue); |
| return nullptr; |
| } |
| |
| if (!AddRoute(route_id, stream_id, stub.get())) { |
| DestroyStreamIfNecessary(queue); |
| DLOG(ERROR) << "GpuChannel::CreateCommandBuffer(): failed to add route"; |
| return nullptr; |
| } |
| |
| return stub; |
| } |
| |
| void GpuChannel::OnDestroyCommandBuffer(int32_t route_id) { |
| TRACE_EVENT1("gpu", "GpuChannel::OnDestroyCommandBuffer", |
| "route_id", route_id); |
| |
| std::unique_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::OnGetDriverBugWorkArounds( |
| std::vector<std::string>* gpu_driver_bug_workarounds) { |
| gpu_driver_bug_workarounds->clear(); |
| #define GPU_OP(type, name) \ |
| if (gpu_channel_manager_->gpu_driver_bug_workarounds().name) \ |
| gpu_driver_bug_workarounds->push_back(#name); |
| GPU_DRIVER_BUG_WORKAROUNDS(GPU_OP) |
| #undef GPU_OP |
| } |
| |
| 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<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, |
| SurfaceHandle surface_handle) { |
| 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_, surface_handle); |
| } |
| } |
| } |
| |
| } // namespace gpu |