// 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. #if defined(OS_WIN) #include <windows.h> #endif #include "content/common/gpu/gpu_channel.h" #include <queue> #include <vector> #include "base/bind.h" #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/message_loop/message_loop_proxy.h" #include "base/strings/string_util.h" #include "base/timer/timer.h" #include "content/common/gpu/devtools_gpu_agent.h" #include "content/common/gpu/gpu_channel_manager.h" #include "content/common/gpu/gpu_messages.h" #include "content/common/gpu/media/gpu_video_encode_accelerator.h" #include "content/common/gpu/sync_point_manager.h" #include "content/public/common/content_switches.h" #include "crypto/random.h" #include "gpu/command_buffer/common/mailbox.h" #include "gpu/command_buffer/service/gpu_scheduler.h" #include "gpu/command_buffer/service/image_manager.h" #include "gpu/command_buffer/service/mailbox_manager.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_proxy.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_image.h" #include "ui/gl/gl_surface.h" #if defined(OS_POSIX) #include "ipc/ipc_channel_posix.h" #endif #if defined(OS_ANDROID) #include "content/common/gpu/stream_texture_manager_android.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 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 kPreemptWaitTimeMs = 2 * kVsyncIntervalMs; // Once we trigger a preemption, the maximum duration that we will wait // before clearing the preemption. const int64 kMaxPreemptTimeMs = kVsyncIntervalMs; // Stop the preemption once the time for the longest pending IPC drops // below this threshold. const int64 kStopPreemptThresholdMs = kVsyncIntervalMs; } // anonymous namespace // This filter does three things: // - it counts and timestamps each message forwarded to the channel // so that we can preempt other channels if a message takes too long to // process. To guarantee fairness, we must wait a minimum amount of time // before preempting and we limit the amount of time that we can preempt in // one shot (see constants above). // - it handles the GpuCommandBufferMsg_InsertSyncPoint message on the IO // thread, generating the sync point ID and responding immediately, and then // posting a task to insert the GpuCommandBufferMsg_RetireSyncPoint message // into the channel's queue. // - it generates mailbox names for clients of the GPU process on the IO thread. class GpuChannelMessageFilter : public IPC::ChannelProxy::MessageFilter { public: // Takes ownership of gpu_channel (see below). GpuChannelMessageFilter(base::WeakPtr<GpuChannel>* gpu_channel, scoped_refptr<SyncPointManager> sync_point_manager, scoped_refptr<base::MessageLoopProxy> message_loop) : preemption_state_(IDLE), gpu_channel_(gpu_channel), channel_(NULL), sync_point_manager_(sync_point_manager), message_loop_(message_loop), messages_forwarded_to_channel_(0), a_stub_is_descheduled_(false) { } virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE { DCHECK(!channel_); channel_ = channel; } virtual void OnFilterRemoved() OVERRIDE { DCHECK(channel_); channel_ = NULL; } virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { DCHECK(channel_); bool handled = true; IPC_BEGIN_MESSAGE_MAP(GpuChannelMessageFilter, message) IPC_MESSAGE_HANDLER(GpuChannelMsg_GenerateMailboxNames, OnGenerateMailboxNames) IPC_MESSAGE_HANDLER(GpuChannelMsg_GenerateMailboxNamesAsync, OnGenerateMailboxNamesAsync) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() if (message.type() == GpuCommandBufferMsg_RetireSyncPoint::ID) { // This message should not be sent explicitly by the renderer. NOTREACHED(); handled = true; } // All other messages get processed by the GpuChannel. if (!handled) { messages_forwarded_to_channel_++; if (preempting_flag_.get()) pending_messages_.push(PendingMessage(messages_forwarded_to_channel_)); UpdatePreemptionState(); } if (message.type() == GpuCommandBufferMsg_InsertSyncPoint::ID) { uint32 sync_point = sync_point_manager_->GenerateSyncPoint(); IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); GpuCommandBufferMsg_InsertSyncPoint::WriteReplyParams(reply, sync_point); Send(reply); message_loop_->PostTask(FROM_HERE, base::Bind( &GpuChannelMessageFilter::InsertSyncPointOnMainThread, gpu_channel_, sync_point_manager_, message.routing_id(), sync_point)); handled = true; } return handled; } void MessageProcessed(uint64 messages_processed) { while (!pending_messages_.empty() && pending_messages_.front().message_number <= messages_processed) pending_messages_.pop(); UpdatePreemptionState(); } void SetPreemptingFlagAndSchedulingState( gpu::PreemptionFlag* preempting_flag, bool a_stub_is_descheduled) { preempting_flag_ = preempting_flag; a_stub_is_descheduled_ = a_stub_is_descheduled; } void UpdateStubSchedulingState(bool a_stub_is_descheduled) { a_stub_is_descheduled_ = a_stub_is_descheduled; UpdatePreemptionState(); } bool Send(IPC::Message* message) { return channel_->Send(message); } protected: virtual ~GpuChannelMessageFilter() { message_loop_->PostTask(FROM_HERE, base::Bind( &GpuChannelMessageFilter::DeleteWeakPtrOnMainThread, gpu_channel_)); } private: // Message handlers. void OnGenerateMailboxNames(unsigned num, std::vector<gpu::Mailbox>* result) { TRACE_EVENT1("gpu", "OnGenerateMailboxNames", "num", num); result->resize(num); for (unsigned i = 0; i < num; ++i) crypto::RandBytes((*result)[i].name, sizeof((*result)[i].name)); } void OnGenerateMailboxNamesAsync(unsigned num) { std::vector<gpu::Mailbox> names; OnGenerateMailboxNames(num, &names); Send(new GpuChannelMsg_GenerateMailboxNamesReply(names)); } enum PreemptionState { // Either there's no other channel to preempt, there are no messages // pending processing, or we just finished preempting and have to wait // before preempting again. IDLE, // We are waiting kPreemptWaitTimeMs before checking if we should preempt. WAITING, // We can preempt whenever any IPC processing takes more than // kPreemptWaitTimeMs. CHECKING, // We are currently preempting (i.e. no stub is descheduled). PREEMPTING, // We would like to preempt, but some stub is descheduled. WOULD_PREEMPT_DESCHEDULED, }; PreemptionState preemption_state_; // Maximum amount of time that we can spend in PREEMPTING. // It is reset when we transition to IDLE. base::TimeDelta max_preemption_time_; struct PendingMessage { uint64 message_number; base::TimeTicks time_received; explicit PendingMessage(uint64 message_number) : message_number(message_number), time_received(base::TimeTicks::Now()) { } }; void UpdatePreemptionState() { switch (preemption_state_) { case IDLE: if (preempting_flag_.get() && !pending_messages_.empty()) TransitionToWaiting(); break; case WAITING: // A timer will transition us to CHECKING. DCHECK(timer_.IsRunning()); break; case CHECKING: if (!pending_messages_.empty()) { base::TimeDelta time_elapsed = base::TimeTicks::Now() - pending_messages_.front().time_received; 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, &GpuChannelMessageFilter::UpdatePreemptionState); } else { if (a_stub_is_descheduled_) TransitionToWouldPreemptDescheduled(); else TransitionToPreempting(); } } break; case PREEMPTING: // A TransitionToIdle() timer should always be running in this state. DCHECK(timer_.IsRunning()); if (a_stub_is_descheduled_) TransitionToWouldPreemptDescheduled(); else TransitionToIdleIfCaughtUp(); break; case WOULD_PREEMPT_DESCHEDULED: // A TransitionToIdle() timer should never be running in this state. DCHECK(!timer_.IsRunning()); if (!a_stub_is_descheduled_) TransitionToPreempting(); else TransitionToIdleIfCaughtUp(); break; default: NOTREACHED(); } } void TransitionToIdleIfCaughtUp() { DCHECK(preemption_state_ == PREEMPTING || preemption_state_ == WOULD_PREEMPT_DESCHEDULED); if (pending_messages_.empty()) { TransitionToIdle(); } else { base::TimeDelta time_elapsed = base::TimeTicks::Now() - pending_messages_.front().time_received; if (time_elapsed.InMilliseconds() < kStopPreemptThresholdMs) TransitionToIdle(); } } void TransitionToIdle() { DCHECK(preemption_state_ == PREEMPTING || preemption_state_ == WOULD_PREEMPT_DESCHEDULED); // Stop any outstanding timer set to force us from PREEMPTING to IDLE. timer_.Stop(); preemption_state_ = IDLE; preempting_flag_->Reset(); TRACE_COUNTER_ID1("gpu", "GpuChannel::Preempting", this, 0); UpdatePreemptionState(); } void TransitionToWaiting() { DCHECK_EQ(preemption_state_, IDLE); DCHECK(!timer_.IsRunning()); preemption_state_ = WAITING; timer_.Start( FROM_HERE, base::TimeDelta::FromMilliseconds(kPreemptWaitTimeMs), this, &GpuChannelMessageFilter::TransitionToChecking); } void TransitionToChecking() { DCHECK_EQ(preemption_state_, WAITING); DCHECK(!timer_.IsRunning()); preemption_state_ = CHECKING; max_preemption_time_ = base::TimeDelta::FromMilliseconds(kMaxPreemptTimeMs); UpdatePreemptionState(); } void TransitionToPreempting() { DCHECK(preemption_state_ == CHECKING || preemption_state_ == WOULD_PREEMPT_DESCHEDULED); DCHECK(!a_stub_is_descheduled_); // Stop any pending state update checks that we may have queued // while CHECKING. if (preemption_state_ == CHECKING) timer_.Stop(); preemption_state_ = PREEMPTING; preempting_flag_->Set(); TRACE_COUNTER_ID1("gpu", "GpuChannel::Preempting", this, 1); timer_.Start( FROM_HERE, max_preemption_time_, this, &GpuChannelMessageFilter::TransitionToIdle); UpdatePreemptionState(); } void TransitionToWouldPreemptDescheduled() { DCHECK(preemption_state_ == CHECKING || preemption_state_ == PREEMPTING); DCHECK(a_stub_is_descheduled_); if (preemption_state_ == CHECKING) { // Stop any pending state update checks that we may have queued // while CHECKING. timer_.Stop(); } else { // Stop any TransitionToIdle() timers that we may have queued // while PREEMPTING. timer_.Stop(); max_preemption_time_ = timer_.desired_run_time() - base::TimeTicks::Now(); if (max_preemption_time_ < base::TimeDelta()) { TransitionToIdle(); return; } } preemption_state_ = WOULD_PREEMPT_DESCHEDULED; preempting_flag_->Reset(); TRACE_COUNTER_ID1("gpu", "GpuChannel::Preempting", this, 0); UpdatePreemptionState(); } static void InsertSyncPointOnMainThread( base::WeakPtr<GpuChannel>* gpu_channel, scoped_refptr<SyncPointManager> manager, int32 routing_id, uint32 sync_point) { // This function must ensure that the sync point will be retired. Normally // we'll find the stub based on the routing ID, and associate the sync point // with it, but if that fails for any reason (channel or stub already // deleted, invalid routing id), we need to retire the sync point // immediately. if (gpu_channel->get()) { GpuCommandBufferStub* stub = gpu_channel->get()->LookupCommandBuffer( routing_id); if (stub) { stub->AddSyncPoint(sync_point); GpuCommandBufferMsg_RetireSyncPoint message(routing_id, sync_point); gpu_channel->get()->OnMessageReceived(message); return; } else { gpu_channel->get()->MessageProcessed(); } } manager->RetireSyncPoint(sync_point); } static void DeleteWeakPtrOnMainThread( base::WeakPtr<GpuChannel>* gpu_channel) { delete gpu_channel; } // NOTE: this is a pointer to a weak pointer. It is never dereferenced on the // IO thread, it's only passed through - therefore the WeakPtr assumptions are // respected. base::WeakPtr<GpuChannel>* gpu_channel_; IPC::Channel* channel_; scoped_refptr<SyncPointManager> sync_point_manager_; scoped_refptr<base::MessageLoopProxy> message_loop_; scoped_refptr<gpu::PreemptionFlag> preempting_flag_; std::queue<PendingMessage> pending_messages_; // Count of the number of IPCs forwarded to the GpuChannel. uint64 messages_forwarded_to_channel_; base::OneShotTimer<GpuChannelMessageFilter> timer_; bool a_stub_is_descheduled_; }; GpuChannel::GpuChannel(GpuChannelManager* gpu_channel_manager, GpuWatchdog* watchdog, gfx::GLShareGroup* share_group, gpu::gles2::MailboxManager* mailbox, int client_id, bool software) : gpu_channel_manager_(gpu_channel_manager), messages_processed_(0), client_id_(client_id), share_group_(share_group ? share_group : new gfx::GLShareGroup), mailbox_manager_(mailbox ? mailbox : new gpu::gles2::MailboxManager), image_manager_(new gpu::gles2::ImageManager), watchdog_(watchdog), software_(software), handle_messages_scheduled_(false), processed_get_state_fast_(false), currently_processing_message_(NULL), weak_factory_(this), num_stubs_descheduled_(0) { DCHECK(gpu_channel_manager); DCHECK(client_id); channel_id_ = IPC::Channel::GenerateVerifiedChannelID("gpu"); const CommandLine* command_line = CommandLine::ForCurrentProcess(); log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages); disallowed_features_.multisampling = command_line->HasSwitch(switches::kDisableGLMultisampling); #if defined(OS_ANDROID) stream_texture_manager_.reset(new StreamTextureManagerAndroid(this)); #endif } bool GpuChannel::Init(base::MessageLoopProxy* io_message_loop, base::WaitableEvent* shutdown_event) { DCHECK(!channel_.get()); // Map renderer ID to a (single) channel to that process. channel_.reset(new IPC::SyncChannel( channel_id_, IPC::Channel::MODE_SERVER, this, io_message_loop, false, shutdown_event)); base::WeakPtr<GpuChannel>* weak_ptr(new base::WeakPtr<GpuChannel>( weak_factory_.GetWeakPtr())); filter_ = new GpuChannelMessageFilter( weak_ptr, gpu_channel_manager_->sync_point_manager(), base::MessageLoopProxy::current()); io_message_loop_ = io_message_loop; channel_->AddFilter(filter_.get()); devtools_gpu_agent_.reset(new DevToolsGpuAgent(this)); return true; } std::string GpuChannel::GetChannelName() { return channel_id_; } #if defined(OS_POSIX) int GpuChannel::TakeRendererFileDescriptor() { if (!channel_) { NOTREACHED(); return -1; } return channel_->TakeClientFileDescriptor(); } #endif // defined(OS_POSIX) bool GpuChannel::OnMessageReceived(const IPC::Message& message) { if (log_messages_) { DVLOG(1) << "received message @" << &message << " on channel @" << this << " with type " << message.type(); } if (message.type() == GpuCommandBufferMsg_GetStateFast::ID) { if (processed_get_state_fast_) { // Require a non-GetStateFast message in between two GetStateFast // messages, to ensure progress is made. std::deque<IPC::Message*>::iterator point = deferred_messages_.begin(); while (point != deferred_messages_.end() && (*point)->type() == GpuCommandBufferMsg_GetStateFast::ID) { ++point; } if (point != deferred_messages_.end()) { ++point; } deferred_messages_.insert(point, new IPC::Message(message)); } else { // Move GetStateFast commands to the head of the queue, so the renderer // doesn't have to wait any longer than necessary. deferred_messages_.push_front(new IPC::Message(message)); } } else { deferred_messages_.push_back(new IPC::Message(message)); } OnScheduled(); return true; } 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()); if (log_messages_) { DVLOG(1) << "sending message @" << message << " on channel @" << this << " with type " << message->type(); } if (!channel_) { delete message; return false; } return channel_->Send(message); } void GpuChannel::RequeueMessage() { DCHECK(currently_processing_message_); deferred_messages_.push_front( new IPC::Message(*currently_processing_message_)); messages_processed_--; currently_processing_message_ = NULL; } void GpuChannel::OnScheduled() { if (handle_messages_scheduled_) return; // Post a task to handle any deferred messages. The deferred message queue is // not emptied here, which ensures that OnMessageReceived will continue to // defer newly received messages until the ones in the queue have all been // handled by HandleMessage. HandleMessage is invoked as a // task to prevent reentrancy. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&GpuChannel::HandleMessage, weak_factory_.GetWeakPtr())); handle_messages_scheduled_ = true; } void GpuChannel::StubSchedulingChanged(bool scheduled) { bool a_stub_was_descheduled = num_stubs_descheduled_ > 0; if (scheduled) { num_stubs_descheduled_--; OnScheduled(); } else { num_stubs_descheduled_++; } DCHECK_LE(num_stubs_descheduled_, stubs_.size()); bool a_stub_is_descheduled = num_stubs_descheduled_ > 0; if (a_stub_is_descheduled != a_stub_was_descheduled) { if (preempting_flag_.get()) { io_message_loop_->PostTask( FROM_HERE, base::Bind(&GpuChannelMessageFilter::UpdateStubSchedulingState, filter_, a_stub_is_descheduled)); } } } void GpuChannel::CreateViewCommandBuffer( const gfx::GLSurfaceHandle& window, int32 surface_id, const GPUCreateCommandBufferConfig& init_params, int32* route_id) { TRACE_EVENT1("gpu", "GpuChannel::CreateViewCommandBuffer", "surface_id", surface_id); *route_id = MSG_ROUTING_NONE; GpuCommandBufferStub* share_group = stubs_.Lookup(init_params.share_group_id); // Virtualize compositor contexts on OS X to prevent performance regressions // when enabling FCM. // http://crbug.com/180463 bool use_virtualized_gl_context = false; #if defined(OS_MACOSX) use_virtualized_gl_context = true; #endif *route_id = GenerateRouteID(); scoped_ptr<GpuCommandBufferStub> stub( new GpuCommandBufferStub(this, share_group, window, mailbox_manager_.get(), image_manager_.get(), gfx::Size(), disallowed_features_, init_params.attribs, init_params.gpu_preference, use_virtualized_gl_context, *route_id, surface_id, watchdog_, software_, init_params.active_url)); if (preempted_flag_.get()) stub->SetPreemptByFlag(preempted_flag_); router_.AddRoute(*route_id, stub.get()); stubs_.AddWithID(stub.release(), *route_id); } GpuCommandBufferStub* GpuChannel::LookupCommandBuffer(int32 route_id) { return stubs_.Lookup(route_id); } void GpuChannel::CreateImage( gfx::PluginWindowHandle window, int32 image_id, gfx::Size* size) { TRACE_EVENT1("gpu", "GpuChannel::CreateImage", "image_id", image_id); *size = gfx::Size(); if (image_manager_->LookupImage(image_id)) { LOG(ERROR) << "CreateImage failed, image_id already in use."; return; } scoped_refptr<gfx::GLImage> image = gfx::GLImage::CreateGLImage(window); if (!image.get()) return; image_manager_->AddImage(image.get(), image_id); *size = image->GetSize(); } void GpuChannel::DeleteImage(int32 image_id) { TRACE_EVENT1("gpu", "GpuChannel::DeleteImage", "image_id", image_id); image_manager_->RemoveImage(image_id); } void GpuChannel::LoseAllContexts() { gpu_channel_manager_->LoseAllContexts(); } void GpuChannel::MarkAllContextsLost() { for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_); !it.IsAtEnd(); it.Advance()) { it.GetCurrentValue()->MarkContextLost(); } } void GpuChannel::DestroySoon() { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&GpuChannel::OnDestroy, this)); } int GpuChannel::GenerateRouteID() { static int last_id = 0; return ++last_id; } void GpuChannel::AddRoute(int32 route_id, IPC::Listener* listener) { router_.AddRoute(route_id, listener); } void GpuChannel::RemoveRoute(int32 route_id) { router_.RemoveRoute(route_id); } gpu::PreemptionFlag* GpuChannel::GetPreemptionFlag() { if (!preempting_flag_.get()) { preempting_flag_ = new gpu::PreemptionFlag; io_message_loop_->PostTask( FROM_HERE, base::Bind( &GpuChannelMessageFilter::SetPreemptingFlagAndSchedulingState, filter_, preempting_flag_, num_stubs_descheduled_ > 0)); } return preempting_flag_.get(); } void GpuChannel::SetPreemptByFlag( scoped_refptr<gpu::PreemptionFlag> preempted_flag) { preempted_flag_ = preempted_flag; for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_); !it.IsAtEnd(); it.Advance()) { it.GetCurrentValue()->SetPreemptByFlag(preempted_flag_); } } GpuChannel::~GpuChannel() { if (preempting_flag_.get()) preempting_flag_->Reset(); } void GpuChannel::OnDestroy() { TRACE_EVENT0("gpu", "GpuChannel::OnDestroy"); gpu_channel_manager_->RemoveChannel(client_id_); } bool GpuChannel::OnControlMessageReceived(const IPC::Message& msg) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg) IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateOffscreenCommandBuffer, OnCreateOffscreenCommandBuffer) IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer, OnDestroyCommandBuffer) IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateVideoEncoder, OnCreateVideoEncoder) IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyVideoEncoder, OnDestroyVideoEncoder) IPC_MESSAGE_HANDLER(GpuChannelMsg_DevToolsStartEventsRecording, OnDevToolsStartEventsRecording) IPC_MESSAGE_HANDLER(GpuChannelMsg_DevToolsStopEventsRecording, OnDevToolsStopEventsRecording) #if defined(OS_ANDROID) IPC_MESSAGE_HANDLER(GpuChannelMsg_RegisterStreamTextureProxy, OnRegisterStreamTextureProxy) IPC_MESSAGE_HANDLER(GpuChannelMsg_EstablishStreamTexture, OnEstablishStreamTexture) IPC_MESSAGE_HANDLER(GpuChannelMsg_SetStreamTextureSize, OnSetStreamTextureSize) #endif IPC_MESSAGE_HANDLER( GpuChannelMsg_CollectRenderingStatsForSurface, OnCollectRenderingStatsForSurface) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() DCHECK(handled) << msg.type(); return handled; } void GpuChannel::HandleMessage() { handle_messages_scheduled_ = false; if (deferred_messages_.empty()) return; bool should_fast_track_ack = false; IPC::Message* m = deferred_messages_.front(); GpuCommandBufferStub* stub = stubs_.Lookup(m->routing_id()); do { if (stub) { if (!stub->IsScheduled()) return; if (stub->IsPreempted()) { OnScheduled(); return; } } scoped_ptr<IPC::Message> message(m); deferred_messages_.pop_front(); bool message_processed = true; processed_get_state_fast_ = (message->type() == GpuCommandBufferMsg_GetStateFast::ID); currently_processing_message_ = message.get(); bool result; if (message->routing_id() == MSG_ROUTING_CONTROL) result = OnControlMessageReceived(*message); else result = router_.RouteMessage(*message); currently_processing_message_ = NULL; if (!result) { // Respond to sync messages even if router failed to route. if (message->is_sync()) { IPC::Message* reply = IPC::SyncMessage::GenerateReply(&*message); reply->set_reply_error(); Send(reply); } } else { // If the command buffer becomes unscheduled as a result of handling the // message but still has more commands to process, synthesize an IPC // message to flush that command buffer. if (stub) { if (stub->HasUnprocessedCommands()) { deferred_messages_.push_front(new GpuCommandBufferMsg_Rescheduled( stub->route_id())); message_processed = false; } } } if (message_processed) MessageProcessed(); // We want the EchoACK following the SwapBuffers to be sent as close as // possible, avoiding scheduling other channels in the meantime. should_fast_track_ack = false; if (!deferred_messages_.empty()) { m = deferred_messages_.front(); stub = stubs_.Lookup(m->routing_id()); should_fast_track_ack = (m->type() == GpuCommandBufferMsg_Echo::ID) && stub && stub->IsScheduled(); } } while (should_fast_track_ack); if (!deferred_messages_.empty()) { OnScheduled(); } } void GpuChannel::OnCreateOffscreenCommandBuffer( const gfx::Size& size, const GPUCreateCommandBufferConfig& init_params, int32* route_id) { TRACE_EVENT0("gpu", "GpuChannel::OnCreateOffscreenCommandBuffer"); GpuCommandBufferStub* share_group = stubs_.Lookup(init_params.share_group_id); *route_id = GenerateRouteID(); scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub( this, share_group, gfx::GLSurfaceHandle(), mailbox_manager_.get(), image_manager_.get(), size, disallowed_features_, init_params.attribs, init_params.gpu_preference, false, *route_id, 0, watchdog_, software_, init_params.active_url)); if (preempted_flag_.get()) stub->SetPreemptByFlag(preempted_flag_); router_.AddRoute(*route_id, stub.get()); stubs_.AddWithID(stub.release(), *route_id); TRACE_EVENT1("gpu", "GpuChannel::OnCreateOffscreenCommandBuffer", "route_id", route_id); } void GpuChannel::OnDestroyCommandBuffer(int32 route_id) { TRACE_EVENT1("gpu", "GpuChannel::OnDestroyCommandBuffer", "route_id", route_id); GpuCommandBufferStub* stub = stubs_.Lookup(route_id); if (!stub) return; bool need_reschedule = (stub && !stub->IsScheduled()); router_.RemoveRoute(route_id); stubs_.Remove(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 GpuChannel here. if (need_reschedule) { // This stub won't get a chance to reschedule, so update the count now. StubSchedulingChanged(true); } } void GpuChannel::OnCreateVideoEncoder(int32* route_id) { TRACE_EVENT0("gpu", "GpuChannel::OnCreateVideoEncoder"); *route_id = GenerateRouteID(); GpuVideoEncodeAccelerator* encoder = new GpuVideoEncodeAccelerator(this, *route_id); router_.AddRoute(*route_id, encoder); video_encoders_.AddWithID(encoder, *route_id); } void GpuChannel::OnDestroyVideoEncoder(int32 route_id) { TRACE_EVENT1( "gpu", "GpuChannel::OnDestroyVideoEncoder", "route_id", route_id); GpuVideoEncodeAccelerator* encoder = video_encoders_.Lookup(route_id); if (!encoder) return; router_.RemoveRoute(route_id); video_encoders_.Remove(route_id); } void GpuChannel::OnDevToolsStartEventsRecording(int32* route_id) { devtools_gpu_agent_->StartEventsRecording(route_id); } void GpuChannel::OnDevToolsStopEventsRecording() { devtools_gpu_agent_->StopEventsRecording(); } #if defined(OS_ANDROID) void GpuChannel::OnRegisterStreamTextureProxy( int32 stream_id, int32* route_id) { // Note that route_id is only used for notifications sent out from here. // StreamTextureManager owns all texture objects and for incoming messages // it finds the correct object based on stream_id. *route_id = GenerateRouteID(); stream_texture_manager_->RegisterStreamTextureProxy(stream_id, *route_id); } void GpuChannel::OnEstablishStreamTexture( int32 stream_id, int32 primary_id, int32 secondary_id) { stream_texture_manager_->EstablishStreamTexture( stream_id, primary_id, secondary_id); } void GpuChannel::OnSetStreamTextureSize( int32 stream_id, const gfx::Size& size) { stream_texture_manager_->SetStreamTextureSize(stream_id, size); } #endif void GpuChannel::OnCollectRenderingStatsForSurface( int32 surface_id, GpuRenderingStats* stats) { for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_); !it.IsAtEnd(); it.Advance()) { int texture_upload_count = it.GetCurrentValue()->decoder()->GetTextureUploadCount(); base::TimeDelta total_texture_upload_time = it.GetCurrentValue()->decoder()->GetTotalTextureUploadTime(); base::TimeDelta total_processing_commands_time = it.GetCurrentValue()->decoder()->GetTotalProcessingCommandsTime(); stats->global_texture_upload_count += texture_upload_count; stats->global_total_texture_upload_time += total_texture_upload_time; stats->global_total_processing_commands_time += total_processing_commands_time; if (it.GetCurrentValue()->surface_id() == surface_id) { stats->texture_upload_count += texture_upload_count; stats->total_texture_upload_time += total_texture_upload_time; stats->total_processing_commands_time += total_processing_commands_time; } } GPUVideoMemoryUsageStats usage_stats; gpu_channel_manager_->gpu_memory_manager()->GetVideoMemoryUsageStats( &usage_stats); stats->global_video_memory_bytes_allocated = usage_stats.bytes_allocated; } void GpuChannel::MessageProcessed() { messages_processed_++; if (preempting_flag_.get()) { io_message_loop_->PostTask( FROM_HERE, base::Bind(&GpuChannelMessageFilter::MessageProcessed, filter_, messages_processed_)); } } void GpuChannel::CacheShader(const std::string& key, const std::string& shader) { gpu_channel_manager_->Send( new GpuHostMsg_CacheShader(client_id_, key, shader)); } void GpuChannel::AddFilter(IPC::ChannelProxy::MessageFilter* filter) { channel_->AddFilter(filter); } void GpuChannel::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { channel_->RemoveFilter(filter); } } // namespace content