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