Home | History | Annotate | Download | only in gpu
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #if defined(OS_WIN)
      6 #include <windows.h>
      7 #endif
      8 
      9 #include "content/common/gpu/gpu_channel.h"
     10 
     11 #include <queue>
     12 #include <vector>
     13 
     14 #include "base/bind.h"
     15 #include "base/command_line.h"
     16 #include "base/debug/trace_event.h"
     17 #include "base/message_loop/message_loop_proxy.h"
     18 #include "base/stl_util.h"
     19 #include "base/strings/string_util.h"
     20 #include "base/timer/timer.h"
     21 #include "content/common/gpu/devtools_gpu_agent.h"
     22 #include "content/common/gpu/gpu_channel_manager.h"
     23 #include "content/common/gpu/gpu_messages.h"
     24 #include "content/common/gpu/sync_point_manager.h"
     25 #include "content/public/common/content_switches.h"
     26 #include "gpu/command_buffer/common/mailbox.h"
     27 #include "gpu/command_buffer/service/gpu_scheduler.h"
     28 #include "gpu/command_buffer/service/mailbox_manager.h"
     29 #include "ipc/ipc_channel.h"
     30 #include "ipc/message_filter.h"
     31 #include "ui/gl/gl_context.h"
     32 #include "ui/gl/gl_surface.h"
     33 
     34 #if defined(OS_POSIX)
     35 #include "ipc/ipc_channel_posix.h"
     36 #endif
     37 
     38 namespace content {
     39 namespace {
     40 
     41 // Number of milliseconds between successive vsync. Many GL commands block
     42 // on vsync, so thresholds for preemption should be multiples of this.
     43 const int64 kVsyncIntervalMs = 17;
     44 
     45 // Amount of time that we will wait for an IPC to be processed before
     46 // preempting. After a preemption, we must wait this long before triggering
     47 // another preemption.
     48 const int64 kPreemptWaitTimeMs = 2 * kVsyncIntervalMs;
     49 
     50 // Once we trigger a preemption, the maximum duration that we will wait
     51 // before clearing the preemption.
     52 const int64 kMaxPreemptTimeMs = kVsyncIntervalMs;
     53 
     54 // Stop the preemption once the time for the longest pending IPC drops
     55 // below this threshold.
     56 const int64 kStopPreemptThresholdMs = kVsyncIntervalMs;
     57 
     58 }  // anonymous namespace
     59 
     60 // This filter does three things:
     61 // - it counts and timestamps each message forwarded to the channel
     62 //   so that we can preempt other channels if a message takes too long to
     63 //   process. To guarantee fairness, we must wait a minimum amount of time
     64 //   before preempting and we limit the amount of time that we can preempt in
     65 //   one shot (see constants above).
     66 // - it handles the GpuCommandBufferMsg_InsertSyncPoint message on the IO
     67 //   thread, generating the sync point ID and responding immediately, and then
     68 //   posting a task to insert the GpuCommandBufferMsg_RetireSyncPoint message
     69 //   into the channel's queue.
     70 // - it generates mailbox names for clients of the GPU process on the IO thread.
     71 class GpuChannelMessageFilter : public IPC::MessageFilter {
     72  public:
     73   GpuChannelMessageFilter(base::WeakPtr<GpuChannel> gpu_channel,
     74                           scoped_refptr<SyncPointManager> sync_point_manager,
     75                           scoped_refptr<base::MessageLoopProxy> message_loop,
     76                           bool future_sync_points)
     77       : preemption_state_(IDLE),
     78         gpu_channel_(gpu_channel),
     79         sender_(NULL),
     80         sync_point_manager_(sync_point_manager),
     81         message_loop_(message_loop),
     82         messages_forwarded_to_channel_(0),
     83         a_stub_is_descheduled_(false),
     84         future_sync_points_(future_sync_points) {}
     85 
     86   virtual void OnFilterAdded(IPC::Sender* sender) OVERRIDE {
     87     DCHECK(!sender_);
     88     sender_ = sender;
     89   }
     90 
     91   virtual void OnFilterRemoved() OVERRIDE {
     92     DCHECK(sender_);
     93     sender_ = NULL;
     94   }
     95 
     96   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
     97     DCHECK(sender_);
     98 
     99     bool handled = false;
    100     if ((message.type() == GpuCommandBufferMsg_RetireSyncPoint::ID) &&
    101         !future_sync_points_) {
    102       DLOG(ERROR) << "Untrusted client should not send "
    103                      "GpuCommandBufferMsg_RetireSyncPoint message";
    104       return true;
    105     }
    106 
    107     if (message.type() == GpuCommandBufferMsg_InsertSyncPoint::ID) {
    108       Tuple1<bool> retire;
    109       IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
    110       if (!GpuCommandBufferMsg_InsertSyncPoint::ReadSendParam(&message,
    111                                                               &retire)) {
    112         reply->set_reply_error();
    113         Send(reply);
    114         return true;
    115       }
    116       if (!future_sync_points_ && !retire.a) {
    117         LOG(ERROR) << "Untrusted contexts can't create future sync points";
    118         reply->set_reply_error();
    119         Send(reply);
    120         return true;
    121       }
    122       uint32 sync_point = sync_point_manager_->GenerateSyncPoint();
    123       GpuCommandBufferMsg_InsertSyncPoint::WriteReplyParams(reply, sync_point);
    124       Send(reply);
    125       message_loop_->PostTask(
    126           FROM_HERE,
    127           base::Bind(&GpuChannelMessageFilter::InsertSyncPointOnMainThread,
    128                      gpu_channel_,
    129                      sync_point_manager_,
    130                      message.routing_id(),
    131                      retire.a,
    132                      sync_point));
    133       handled = true;
    134     }
    135 
    136     // All other messages get processed by the GpuChannel.
    137     messages_forwarded_to_channel_++;
    138     if (preempting_flag_.get())
    139       pending_messages_.push(PendingMessage(messages_forwarded_to_channel_));
    140     UpdatePreemptionState();
    141 
    142     return handled;
    143   }
    144 
    145   void MessageProcessed(uint64 messages_processed) {
    146     while (!pending_messages_.empty() &&
    147            pending_messages_.front().message_number <= messages_processed)
    148       pending_messages_.pop();
    149     UpdatePreemptionState();
    150   }
    151 
    152   void SetPreemptingFlagAndSchedulingState(
    153       gpu::PreemptionFlag* preempting_flag,
    154       bool a_stub_is_descheduled) {
    155     preempting_flag_ = preempting_flag;
    156     a_stub_is_descheduled_ = a_stub_is_descheduled;
    157   }
    158 
    159   void UpdateStubSchedulingState(bool a_stub_is_descheduled) {
    160     a_stub_is_descheduled_ = a_stub_is_descheduled;
    161     UpdatePreemptionState();
    162   }
    163 
    164   bool Send(IPC::Message* message) {
    165     return sender_->Send(message);
    166   }
    167 
    168  protected:
    169   virtual ~GpuChannelMessageFilter() {}
    170 
    171  private:
    172   enum PreemptionState {
    173     // Either there's no other channel to preempt, there are no messages
    174     // pending processing, or we just finished preempting and have to wait
    175     // before preempting again.
    176     IDLE,
    177     // We are waiting kPreemptWaitTimeMs before checking if we should preempt.
    178     WAITING,
    179     // We can preempt whenever any IPC processing takes more than
    180     // kPreemptWaitTimeMs.
    181     CHECKING,
    182     // We are currently preempting (i.e. no stub is descheduled).
    183     PREEMPTING,
    184     // We would like to preempt, but some stub is descheduled.
    185     WOULD_PREEMPT_DESCHEDULED,
    186   };
    187 
    188   PreemptionState preemption_state_;
    189 
    190   // Maximum amount of time that we can spend in PREEMPTING.
    191   // It is reset when we transition to IDLE.
    192   base::TimeDelta max_preemption_time_;
    193 
    194   struct PendingMessage {
    195     uint64 message_number;
    196     base::TimeTicks time_received;
    197 
    198     explicit PendingMessage(uint64 message_number)
    199         : message_number(message_number),
    200           time_received(base::TimeTicks::Now()) {
    201     }
    202   };
    203 
    204   void UpdatePreemptionState() {
    205     switch (preemption_state_) {
    206       case IDLE:
    207         if (preempting_flag_.get() && !pending_messages_.empty())
    208           TransitionToWaiting();
    209         break;
    210       case WAITING:
    211         // A timer will transition us to CHECKING.
    212         DCHECK(timer_.IsRunning());
    213         break;
    214       case CHECKING:
    215         if (!pending_messages_.empty()) {
    216           base::TimeDelta time_elapsed =
    217               base::TimeTicks::Now() - pending_messages_.front().time_received;
    218           if (time_elapsed.InMilliseconds() < kPreemptWaitTimeMs) {
    219             // Schedule another check for when the IPC may go long.
    220             timer_.Start(
    221                 FROM_HERE,
    222                 base::TimeDelta::FromMilliseconds(kPreemptWaitTimeMs) -
    223                     time_elapsed,
    224                 this, &GpuChannelMessageFilter::UpdatePreemptionState);
    225           } else {
    226             if (a_stub_is_descheduled_)
    227               TransitionToWouldPreemptDescheduled();
    228             else
    229               TransitionToPreempting();
    230           }
    231         }
    232         break;
    233       case PREEMPTING:
    234         // A TransitionToIdle() timer should always be running in this state.
    235         DCHECK(timer_.IsRunning());
    236         if (a_stub_is_descheduled_)
    237           TransitionToWouldPreemptDescheduled();
    238         else
    239           TransitionToIdleIfCaughtUp();
    240         break;
    241       case WOULD_PREEMPT_DESCHEDULED:
    242         // A TransitionToIdle() timer should never be running in this state.
    243         DCHECK(!timer_.IsRunning());
    244         if (!a_stub_is_descheduled_)
    245           TransitionToPreempting();
    246         else
    247           TransitionToIdleIfCaughtUp();
    248         break;
    249       default:
    250         NOTREACHED();
    251     }
    252   }
    253 
    254   void TransitionToIdleIfCaughtUp() {
    255     DCHECK(preemption_state_ == PREEMPTING ||
    256            preemption_state_ == WOULD_PREEMPT_DESCHEDULED);
    257     if (pending_messages_.empty()) {
    258       TransitionToIdle();
    259     } else {
    260       base::TimeDelta time_elapsed =
    261           base::TimeTicks::Now() - pending_messages_.front().time_received;
    262       if (time_elapsed.InMilliseconds() < kStopPreemptThresholdMs)
    263         TransitionToIdle();
    264     }
    265   }
    266 
    267   void TransitionToIdle() {
    268     DCHECK(preemption_state_ == PREEMPTING ||
    269            preemption_state_ == WOULD_PREEMPT_DESCHEDULED);
    270     // Stop any outstanding timer set to force us from PREEMPTING to IDLE.
    271     timer_.Stop();
    272 
    273     preemption_state_ = IDLE;
    274     preempting_flag_->Reset();
    275     TRACE_COUNTER_ID1("gpu", "GpuChannel::Preempting", this, 0);
    276 
    277     UpdatePreemptionState();
    278   }
    279 
    280   void TransitionToWaiting() {
    281     DCHECK_EQ(preemption_state_, IDLE);
    282     DCHECK(!timer_.IsRunning());
    283 
    284     preemption_state_ = WAITING;
    285     timer_.Start(
    286         FROM_HERE,
    287         base::TimeDelta::FromMilliseconds(kPreemptWaitTimeMs),
    288         this, &GpuChannelMessageFilter::TransitionToChecking);
    289   }
    290 
    291   void TransitionToChecking() {
    292     DCHECK_EQ(preemption_state_, WAITING);
    293     DCHECK(!timer_.IsRunning());
    294 
    295     preemption_state_ = CHECKING;
    296     max_preemption_time_ = base::TimeDelta::FromMilliseconds(kMaxPreemptTimeMs);
    297     UpdatePreemptionState();
    298   }
    299 
    300   void TransitionToPreempting() {
    301     DCHECK(preemption_state_ == CHECKING ||
    302            preemption_state_ == WOULD_PREEMPT_DESCHEDULED);
    303     DCHECK(!a_stub_is_descheduled_);
    304 
    305     // Stop any pending state update checks that we may have queued
    306     // while CHECKING.
    307     if (preemption_state_ == CHECKING)
    308       timer_.Stop();
    309 
    310     preemption_state_ = PREEMPTING;
    311     preempting_flag_->Set();
    312     TRACE_COUNTER_ID1("gpu", "GpuChannel::Preempting", this, 1);
    313 
    314     timer_.Start(
    315        FROM_HERE,
    316        max_preemption_time_,
    317        this, &GpuChannelMessageFilter::TransitionToIdle);
    318 
    319     UpdatePreemptionState();
    320   }
    321 
    322   void TransitionToWouldPreemptDescheduled() {
    323     DCHECK(preemption_state_ == CHECKING ||
    324            preemption_state_ == PREEMPTING);
    325     DCHECK(a_stub_is_descheduled_);
    326 
    327     if (preemption_state_ == CHECKING) {
    328       // Stop any pending state update checks that we may have queued
    329       // while CHECKING.
    330       timer_.Stop();
    331     } else {
    332       // Stop any TransitionToIdle() timers that we may have queued
    333       // while PREEMPTING.
    334       timer_.Stop();
    335       max_preemption_time_ = timer_.desired_run_time() - base::TimeTicks::Now();
    336       if (max_preemption_time_ < base::TimeDelta()) {
    337         TransitionToIdle();
    338         return;
    339       }
    340     }
    341 
    342     preemption_state_ = WOULD_PREEMPT_DESCHEDULED;
    343     preempting_flag_->Reset();
    344     TRACE_COUNTER_ID1("gpu", "GpuChannel::Preempting", this, 0);
    345 
    346     UpdatePreemptionState();
    347   }
    348 
    349   static void InsertSyncPointOnMainThread(
    350       base::WeakPtr<GpuChannel> gpu_channel,
    351       scoped_refptr<SyncPointManager> manager,
    352       int32 routing_id,
    353       bool retire,
    354       uint32 sync_point) {
    355     // This function must ensure that the sync point will be retired. Normally
    356     // we'll find the stub based on the routing ID, and associate the sync point
    357     // with it, but if that fails for any reason (channel or stub already
    358     // deleted, invalid routing id), we need to retire the sync point
    359     // immediately.
    360     if (gpu_channel) {
    361       GpuCommandBufferStub* stub = gpu_channel->LookupCommandBuffer(routing_id);
    362       if (stub) {
    363         stub->AddSyncPoint(sync_point);
    364         if (retire) {
    365           GpuCommandBufferMsg_RetireSyncPoint message(routing_id, sync_point);
    366           gpu_channel->OnMessageReceived(message);
    367         }
    368         return;
    369       } else {
    370         gpu_channel->MessageProcessed();
    371       }
    372     }
    373     manager->RetireSyncPoint(sync_point);
    374   }
    375 
    376   // NOTE: this weak pointer is never dereferenced on the IO thread, it's only
    377   // passed through - therefore the WeakPtr assumptions are respected.
    378   base::WeakPtr<GpuChannel> gpu_channel_;
    379   IPC::Sender* sender_;
    380   scoped_refptr<SyncPointManager> sync_point_manager_;
    381   scoped_refptr<base::MessageLoopProxy> message_loop_;
    382   scoped_refptr<gpu::PreemptionFlag> preempting_flag_;
    383 
    384   std::queue<PendingMessage> pending_messages_;
    385 
    386   // Count of the number of IPCs forwarded to the GpuChannel.
    387   uint64 messages_forwarded_to_channel_;
    388 
    389   base::OneShotTimer<GpuChannelMessageFilter> timer_;
    390 
    391   bool a_stub_is_descheduled_;
    392 
    393   // True if this channel can create future sync points.
    394   bool future_sync_points_;
    395 };
    396 
    397 GpuChannel::GpuChannel(GpuChannelManager* gpu_channel_manager,
    398                        GpuWatchdog* watchdog,
    399                        gfx::GLShareGroup* share_group,
    400                        gpu::gles2::MailboxManager* mailbox,
    401                        int client_id,
    402                        bool software,
    403                        bool allow_future_sync_points)
    404     : gpu_channel_manager_(gpu_channel_manager),
    405       messages_processed_(0),
    406       client_id_(client_id),
    407       share_group_(share_group ? share_group : new gfx::GLShareGroup),
    408       mailbox_manager_(mailbox ? mailbox : new gpu::gles2::MailboxManager),
    409       watchdog_(watchdog),
    410       software_(software),
    411       handle_messages_scheduled_(false),
    412       currently_processing_message_(NULL),
    413       num_stubs_descheduled_(0),
    414       allow_future_sync_points_(allow_future_sync_points),
    415       weak_factory_(this) {
    416   DCHECK(gpu_channel_manager);
    417   DCHECK(client_id);
    418 
    419   channel_id_ = IPC::Channel::GenerateVerifiedChannelID("gpu");
    420   const base::CommandLine* command_line =
    421       base::CommandLine::ForCurrentProcess();
    422   log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages);
    423 }
    424 
    425 GpuChannel::~GpuChannel() {
    426   STLDeleteElements(&deferred_messages_);
    427   if (preempting_flag_.get())
    428     preempting_flag_->Reset();
    429 }
    430 
    431 void GpuChannel::Init(base::MessageLoopProxy* io_message_loop,
    432                       base::WaitableEvent* shutdown_event) {
    433   DCHECK(!channel_.get());
    434 
    435   // Map renderer ID to a (single) channel to that process.
    436   channel_ = IPC::SyncChannel::Create(channel_id_,
    437                                       IPC::Channel::MODE_SERVER,
    438                                       this,
    439                                       io_message_loop,
    440                                       false,
    441                                       shutdown_event);
    442 
    443   filter_ =
    444       new GpuChannelMessageFilter(weak_factory_.GetWeakPtr(),
    445                                   gpu_channel_manager_->sync_point_manager(),
    446                                   base::MessageLoopProxy::current(),
    447                                   allow_future_sync_points_);
    448   io_message_loop_ = io_message_loop;
    449   channel_->AddFilter(filter_.get());
    450 
    451   devtools_gpu_agent_.reset(new DevToolsGpuAgent(this));
    452 }
    453 
    454 std::string GpuChannel::GetChannelName() {
    455   return channel_id_;
    456 }
    457 
    458 #if defined(OS_POSIX)
    459 int GpuChannel::TakeRendererFileDescriptor() {
    460   if (!channel_) {
    461     NOTREACHED();
    462     return -1;
    463   }
    464   return channel_->TakeClientFileDescriptor();
    465 }
    466 #endif  // defined(OS_POSIX)
    467 
    468 bool GpuChannel::OnMessageReceived(const IPC::Message& message) {
    469   if (log_messages_) {
    470     DVLOG(1) << "received message @" << &message << " on channel @" << this
    471              << " with type " << message.type();
    472   }
    473 
    474   if (message.type() == GpuCommandBufferMsg_WaitForTokenInRange::ID ||
    475       message.type() == GpuCommandBufferMsg_WaitForGetOffsetInRange::ID) {
    476     // Move Wait commands to the head of the queue, so the renderer
    477     // doesn't have to wait any longer than necessary.
    478     deferred_messages_.push_front(new IPC::Message(message));
    479   } else {
    480     deferred_messages_.push_back(new IPC::Message(message));
    481   }
    482 
    483   OnScheduled();
    484 
    485   return true;
    486 }
    487 
    488 void GpuChannel::OnChannelError() {
    489   gpu_channel_manager_->RemoveChannel(client_id_);
    490 }
    491 
    492 bool GpuChannel::Send(IPC::Message* message) {
    493   // The GPU process must never send a synchronous IPC message to the renderer
    494   // process. This could result in deadlock.
    495   DCHECK(!message->is_sync());
    496   if (log_messages_) {
    497     DVLOG(1) << "sending message @" << message << " on channel @" << this
    498              << " with type " << message->type();
    499   }
    500 
    501   if (!channel_) {
    502     delete message;
    503     return false;
    504   }
    505 
    506   return channel_->Send(message);
    507 }
    508 
    509 void GpuChannel::RequeueMessage() {
    510   DCHECK(currently_processing_message_);
    511   deferred_messages_.push_front(
    512       new IPC::Message(*currently_processing_message_));
    513   messages_processed_--;
    514   currently_processing_message_ = NULL;
    515 }
    516 
    517 void GpuChannel::OnScheduled() {
    518   if (handle_messages_scheduled_)
    519     return;
    520   // Post a task to handle any deferred messages. The deferred message queue is
    521   // not emptied here, which ensures that OnMessageReceived will continue to
    522   // defer newly received messages until the ones in the queue have all been
    523   // handled by HandleMessage. HandleMessage is invoked as a
    524   // task to prevent reentrancy.
    525   base::MessageLoop::current()->PostTask(
    526       FROM_HERE,
    527       base::Bind(&GpuChannel::HandleMessage, weak_factory_.GetWeakPtr()));
    528   handle_messages_scheduled_ = true;
    529 }
    530 
    531 void GpuChannel::StubSchedulingChanged(bool scheduled) {
    532   bool a_stub_was_descheduled = num_stubs_descheduled_ > 0;
    533   if (scheduled) {
    534     num_stubs_descheduled_--;
    535     OnScheduled();
    536   } else {
    537     num_stubs_descheduled_++;
    538   }
    539   DCHECK_LE(num_stubs_descheduled_, stubs_.size());
    540   bool a_stub_is_descheduled = num_stubs_descheduled_ > 0;
    541 
    542   if (a_stub_is_descheduled != a_stub_was_descheduled) {
    543     if (preempting_flag_.get()) {
    544       io_message_loop_->PostTask(
    545           FROM_HERE,
    546           base::Bind(&GpuChannelMessageFilter::UpdateStubSchedulingState,
    547                      filter_,
    548                      a_stub_is_descheduled));
    549     }
    550   }
    551 }
    552 
    553 CreateCommandBufferResult GpuChannel::CreateViewCommandBuffer(
    554     const gfx::GLSurfaceHandle& window,
    555     int32 surface_id,
    556     const GPUCreateCommandBufferConfig& init_params,
    557     int32 route_id) {
    558   TRACE_EVENT1("gpu",
    559                "GpuChannel::CreateViewCommandBuffer",
    560                "surface_id",
    561                surface_id);
    562 
    563   GpuCommandBufferStub* share_group = stubs_.Lookup(init_params.share_group_id);
    564 
    565   // Virtualize compositor contexts on OS X to prevent performance regressions
    566   // when enabling FCM.
    567   // http://crbug.com/180463
    568   bool use_virtualized_gl_context = false;
    569 #if defined(OS_MACOSX)
    570   use_virtualized_gl_context = true;
    571 #endif
    572 
    573   scoped_ptr<GpuCommandBufferStub> stub(
    574       new GpuCommandBufferStub(this,
    575                                share_group,
    576                                window,
    577                                mailbox_manager_.get(),
    578                                gfx::Size(),
    579                                disallowed_features_,
    580                                init_params.attribs,
    581                                init_params.gpu_preference,
    582                                use_virtualized_gl_context,
    583                                route_id,
    584                                surface_id,
    585                                watchdog_,
    586                                software_,
    587                                init_params.active_url));
    588   if (preempted_flag_.get())
    589     stub->SetPreemptByFlag(preempted_flag_);
    590   if (!router_.AddRoute(route_id, stub.get())) {
    591     DLOG(ERROR) << "GpuChannel::CreateViewCommandBuffer(): "
    592                    "failed to add route";
    593     return CREATE_COMMAND_BUFFER_FAILED_AND_CHANNEL_LOST;
    594   }
    595   stubs_.AddWithID(stub.release(), route_id);
    596   return CREATE_COMMAND_BUFFER_SUCCEEDED;
    597 }
    598 
    599 GpuCommandBufferStub* GpuChannel::LookupCommandBuffer(int32 route_id) {
    600   return stubs_.Lookup(route_id);
    601 }
    602 
    603 void GpuChannel::LoseAllContexts() {
    604   gpu_channel_manager_->LoseAllContexts();
    605 }
    606 
    607 void GpuChannel::MarkAllContextsLost() {
    608   for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_);
    609        !it.IsAtEnd(); it.Advance()) {
    610     it.GetCurrentValue()->MarkContextLost();
    611   }
    612 }
    613 
    614 bool GpuChannel::AddRoute(int32 route_id, IPC::Listener* listener) {
    615   return router_.AddRoute(route_id, listener);
    616 }
    617 
    618 void GpuChannel::RemoveRoute(int32 route_id) {
    619   router_.RemoveRoute(route_id);
    620 }
    621 
    622 gpu::PreemptionFlag* GpuChannel::GetPreemptionFlag() {
    623   if (!preempting_flag_.get()) {
    624     preempting_flag_ = new gpu::PreemptionFlag;
    625     io_message_loop_->PostTask(
    626         FROM_HERE, base::Bind(
    627             &GpuChannelMessageFilter::SetPreemptingFlagAndSchedulingState,
    628             filter_, preempting_flag_, num_stubs_descheduled_ > 0));
    629   }
    630   return preempting_flag_.get();
    631 }
    632 
    633 void GpuChannel::SetPreemptByFlag(
    634     scoped_refptr<gpu::PreemptionFlag> preempted_flag) {
    635   preempted_flag_ = preempted_flag;
    636 
    637   for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_);
    638        !it.IsAtEnd(); it.Advance()) {
    639     it.GetCurrentValue()->SetPreemptByFlag(preempted_flag_);
    640   }
    641 }
    642 
    643 void GpuChannel::OnDestroy() {
    644   TRACE_EVENT0("gpu", "GpuChannel::OnDestroy");
    645   gpu_channel_manager_->RemoveChannel(client_id_);
    646 }
    647 
    648 bool GpuChannel::OnControlMessageReceived(const IPC::Message& msg) {
    649   bool handled = true;
    650   IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg)
    651     IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateOffscreenCommandBuffer,
    652                         OnCreateOffscreenCommandBuffer)
    653     IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer,
    654                         OnDestroyCommandBuffer)
    655     IPC_MESSAGE_HANDLER(GpuChannelMsg_DevToolsStartEventsRecording,
    656                         OnDevToolsStartEventsRecording)
    657     IPC_MESSAGE_HANDLER(GpuChannelMsg_DevToolsStopEventsRecording,
    658                         OnDevToolsStopEventsRecording)
    659     IPC_MESSAGE_UNHANDLED(handled = false)
    660   IPC_END_MESSAGE_MAP()
    661   DCHECK(handled) << msg.type();
    662   return handled;
    663 }
    664 
    665 size_t GpuChannel::MatchSwapBufferMessagesPattern(
    666     IPC::Message* current_message) {
    667   DCHECK(current_message);
    668   if (deferred_messages_.empty() || !current_message)
    669     return 0;
    670   // Only care about AsyncFlush message.
    671   if (current_message->type() != GpuCommandBufferMsg_AsyncFlush::ID)
    672     return 0;
    673 
    674   size_t index = 0;
    675   int32 routing_id = current_message->routing_id();
    676 
    677   // Fetch the first message and move index to point to the second message.
    678   IPC::Message* first_message = deferred_messages_[index++];
    679 
    680   // If the current message is AsyncFlush, the expected message sequence for
    681   // SwapBuffer should be AsyncFlush->Echo. We only try to match Echo message.
    682   if (current_message->type() == GpuCommandBufferMsg_AsyncFlush::ID &&
    683       first_message->type() == GpuCommandBufferMsg_Echo::ID &&
    684       first_message->routing_id() == routing_id) {
    685     return 1;
    686   }
    687 
    688   // No matched message is found.
    689   return 0;
    690 }
    691 
    692 void GpuChannel::HandleMessage() {
    693   handle_messages_scheduled_ = false;
    694   if (deferred_messages_.empty())
    695     return;
    696 
    697   size_t matched_messages_num = 0;
    698   bool should_handle_swapbuffer_msgs_immediate = false;
    699   IPC::Message* m = NULL;
    700   GpuCommandBufferStub* stub = NULL;
    701 
    702   do {
    703     m = deferred_messages_.front();
    704     stub = stubs_.Lookup(m->routing_id());
    705     if (stub) {
    706       if (!stub->IsScheduled())
    707         return;
    708       if (stub->IsPreempted()) {
    709         OnScheduled();
    710         return;
    711       }
    712     }
    713 
    714     scoped_ptr<IPC::Message> message(m);
    715     deferred_messages_.pop_front();
    716     bool message_processed = true;
    717 
    718     currently_processing_message_ = message.get();
    719     bool result;
    720     if (message->routing_id() == MSG_ROUTING_CONTROL)
    721       result = OnControlMessageReceived(*message);
    722     else
    723       result = router_.RouteMessage(*message);
    724     currently_processing_message_ = NULL;
    725 
    726     if (!result) {
    727       // Respond to sync messages even if router failed to route.
    728       if (message->is_sync()) {
    729         IPC::Message* reply = IPC::SyncMessage::GenerateReply(&*message);
    730         reply->set_reply_error();
    731         Send(reply);
    732       }
    733     } else {
    734       // If the command buffer becomes unscheduled as a result of handling the
    735       // message but still has more commands to process, synthesize an IPC
    736       // message to flush that command buffer.
    737       if (stub) {
    738         if (stub->HasUnprocessedCommands()) {
    739           deferred_messages_.push_front(new GpuCommandBufferMsg_Rescheduled(
    740               stub->route_id()));
    741           message_processed = false;
    742         }
    743       }
    744     }
    745     if (message_processed)
    746       MessageProcessed();
    747 
    748     if (deferred_messages_.empty())
    749       break;
    750 
    751     // We process the pending messages immediately if these messages matches
    752     // the pattern of SwapBuffers, for example, GLRenderer always issues
    753     // SwapBuffers calls with a specific IPC message patterns, for example,
    754     // it should be AsyncFlush->Echo sequence.
    755     //
    756     // Instead of posting a task to message loop, it could avoid the possibility
    757     // of being blocked by other channels, and make SwapBuffers executed as soon
    758     // as possible.
    759     if (!should_handle_swapbuffer_msgs_immediate) {
    760       // Start from the current processing message to match SwapBuffer pattern.
    761       matched_messages_num = MatchSwapBufferMessagesPattern(message.get());
    762       should_handle_swapbuffer_msgs_immediate =
    763           matched_messages_num > 0 && stub;
    764     } else {
    765       DCHECK_GT(matched_messages_num, 0u);
    766       --matched_messages_num;
    767       if (!stub || matched_messages_num == 0)
    768         should_handle_swapbuffer_msgs_immediate = false;
    769     }
    770   } while (should_handle_swapbuffer_msgs_immediate);
    771 
    772   if (!deferred_messages_.empty()) {
    773     OnScheduled();
    774   }
    775 }
    776 
    777 void GpuChannel::OnCreateOffscreenCommandBuffer(
    778     const gfx::Size& size,
    779     const GPUCreateCommandBufferConfig& init_params,
    780     int32 route_id,
    781     bool* succeeded) {
    782   TRACE_EVENT0("gpu", "GpuChannel::OnCreateOffscreenCommandBuffer");
    783   GpuCommandBufferStub* share_group = stubs_.Lookup(init_params.share_group_id);
    784 
    785   scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub(
    786       this,
    787       share_group,
    788       gfx::GLSurfaceHandle(),
    789       mailbox_manager_.get(),
    790       size,
    791       disallowed_features_,
    792       init_params.attribs,
    793       init_params.gpu_preference,
    794       false,
    795       route_id,
    796       0,
    797       watchdog_,
    798       software_,
    799       init_params.active_url));
    800   if (preempted_flag_.get())
    801     stub->SetPreemptByFlag(preempted_flag_);
    802   if (!router_.AddRoute(route_id, stub.get())) {
    803     DLOG(ERROR) << "GpuChannel::OnCreateOffscreenCommandBuffer(): "
    804                    "failed to add route";
    805     *succeeded = false;
    806     return;
    807   }
    808   stubs_.AddWithID(stub.release(), route_id);
    809   TRACE_EVENT1("gpu", "GpuChannel::OnCreateOffscreenCommandBuffer",
    810                "route_id", route_id);
    811   *succeeded = true;
    812 }
    813 
    814 void GpuChannel::OnDestroyCommandBuffer(int32 route_id) {
    815   TRACE_EVENT1("gpu", "GpuChannel::OnDestroyCommandBuffer",
    816                "route_id", route_id);
    817 
    818   GpuCommandBufferStub* stub = stubs_.Lookup(route_id);
    819   if (!stub)
    820     return;
    821   bool need_reschedule = (stub && !stub->IsScheduled());
    822   router_.RemoveRoute(route_id);
    823   stubs_.Remove(route_id);
    824   // In case the renderer is currently blocked waiting for a sync reply from the
    825   // stub, we need to make sure to reschedule the GpuChannel here.
    826   if (need_reschedule) {
    827     // This stub won't get a chance to reschedule, so update the count now.
    828     StubSchedulingChanged(true);
    829   }
    830 }
    831 
    832 void GpuChannel::OnDevToolsStartEventsRecording(int32 route_id,
    833                                                 bool* succeeded) {
    834   *succeeded = devtools_gpu_agent_->StartEventsRecording(route_id);
    835 }
    836 
    837 void GpuChannel::OnDevToolsStopEventsRecording() {
    838   devtools_gpu_agent_->StopEventsRecording();
    839 }
    840 
    841 void GpuChannel::MessageProcessed() {
    842   messages_processed_++;
    843   if (preempting_flag_.get()) {
    844     io_message_loop_->PostTask(
    845         FROM_HERE,
    846         base::Bind(&GpuChannelMessageFilter::MessageProcessed,
    847                    filter_,
    848                    messages_processed_));
    849   }
    850 }
    851 
    852 void GpuChannel::CacheShader(const std::string& key,
    853                              const std::string& shader) {
    854   gpu_channel_manager_->Send(
    855       new GpuHostMsg_CacheShader(client_id_, key, shader));
    856 }
    857 
    858 void GpuChannel::AddFilter(IPC::MessageFilter* filter) {
    859   channel_->AddFilter(filter);
    860 }
    861 
    862 void GpuChannel::RemoveFilter(IPC::MessageFilter* filter) {
    863   channel_->RemoveFilter(filter);
    864 }
    865 
    866 uint64 GpuChannel::GetMemoryUsage() {
    867   uint64 size = 0;
    868   for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_);
    869        !it.IsAtEnd(); it.Advance()) {
    870     size += it.GetCurrentValue()->GetMemoryUsage();
    871   }
    872   return size;
    873 }
    874 
    875 }  // namespace content
    876