• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
GpuChannelMessageFilter(base::WeakPtr<GpuChannel> gpu_channel,scoped_refptr<SyncPointManager> sync_point_manager,scoped_refptr<base::MessageLoopProxy> message_loop,bool future_sync_points)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 
OnFilterAdded(IPC::Sender * sender)86   virtual void OnFilterAdded(IPC::Sender* sender) OVERRIDE {
87     DCHECK(!sender_);
88     sender_ = sender;
89   }
90 
OnFilterRemoved()91   virtual void OnFilterRemoved() OVERRIDE {
92     DCHECK(sender_);
93     sender_ = NULL;
94   }
95 
OnMessageReceived(const IPC::Message & message)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 
MessageProcessed(uint64 messages_processed)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 
SetPreemptingFlagAndSchedulingState(gpu::PreemptionFlag * preempting_flag,bool a_stub_is_descheduled)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 
UpdateStubSchedulingState(bool a_stub_is_descheduled)159   void UpdateStubSchedulingState(bool a_stub_is_descheduled) {
160     a_stub_is_descheduled_ = a_stub_is_descheduled;
161     UpdatePreemptionState();
162   }
163 
Send(IPC::Message * message)164   bool Send(IPC::Message* message) {
165     return sender_->Send(message);
166   }
167 
168  protected:
~GpuChannelMessageFilter()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 
PendingMessagecontent::GpuChannelMessageFilter::PendingMessage198     explicit PendingMessage(uint64 message_number)
199         : message_number(message_number),
200           time_received(base::TimeTicks::Now()) {
201     }
202   };
203 
UpdatePreemptionState()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 
TransitionToIdleIfCaughtUp()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 
TransitionToIdle()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 
TransitionToWaiting()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 
TransitionToChecking()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 
TransitionToPreempting()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 
TransitionToWouldPreemptDescheduled()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 
InsertSyncPointOnMainThread(base::WeakPtr<GpuChannel> gpu_channel,scoped_refptr<SyncPointManager> manager,int32 routing_id,bool retire,uint32 sync_point)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 
GpuChannel(GpuChannelManager * gpu_channel_manager,GpuWatchdog * watchdog,gfx::GLShareGroup * share_group,gpu::gles2::MailboxManager * mailbox,int client_id,bool software,bool allow_future_sync_points)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 
~GpuChannel()425 GpuChannel::~GpuChannel() {
426   STLDeleteElements(&deferred_messages_);
427   if (preempting_flag_.get())
428     preempting_flag_->Reset();
429 }
430 
Init(base::MessageLoopProxy * io_message_loop,base::WaitableEvent * shutdown_event)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 
GetChannelName()454 std::string GpuChannel::GetChannelName() {
455   return channel_id_;
456 }
457 
458 #if defined(OS_POSIX)
TakeRendererFileDescriptor()459 int GpuChannel::TakeRendererFileDescriptor() {
460   if (!channel_) {
461     NOTREACHED();
462     return -1;
463   }
464   return channel_->TakeClientFileDescriptor();
465 }
466 #endif  // defined(OS_POSIX)
467 
OnMessageReceived(const IPC::Message & message)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 
OnChannelError()488 void GpuChannel::OnChannelError() {
489   gpu_channel_manager_->RemoveChannel(client_id_);
490 }
491 
Send(IPC::Message * message)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 
RequeueMessage()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 
OnScheduled()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 
StubSchedulingChanged(bool scheduled)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 
CreateViewCommandBuffer(const gfx::GLSurfaceHandle & window,int32 surface_id,const GPUCreateCommandBufferConfig & init_params,int32 route_id)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 
LookupCommandBuffer(int32 route_id)599 GpuCommandBufferStub* GpuChannel::LookupCommandBuffer(int32 route_id) {
600   return stubs_.Lookup(route_id);
601 }
602 
LoseAllContexts()603 void GpuChannel::LoseAllContexts() {
604   gpu_channel_manager_->LoseAllContexts();
605 }
606 
MarkAllContextsLost()607 void GpuChannel::MarkAllContextsLost() {
608   for (StubMap::Iterator<GpuCommandBufferStub> it(&stubs_);
609        !it.IsAtEnd(); it.Advance()) {
610     it.GetCurrentValue()->MarkContextLost();
611   }
612 }
613 
AddRoute(int32 route_id,IPC::Listener * listener)614 bool GpuChannel::AddRoute(int32 route_id, IPC::Listener* listener) {
615   return router_.AddRoute(route_id, listener);
616 }
617 
RemoveRoute(int32 route_id)618 void GpuChannel::RemoveRoute(int32 route_id) {
619   router_.RemoveRoute(route_id);
620 }
621 
GetPreemptionFlag()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 
SetPreemptByFlag(scoped_refptr<gpu::PreemptionFlag> preempted_flag)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 
OnDestroy()643 void GpuChannel::OnDestroy() {
644   TRACE_EVENT0("gpu", "GpuChannel::OnDestroy");
645   gpu_channel_manager_->RemoveChannel(client_id_);
646 }
647 
OnControlMessageReceived(const IPC::Message & msg)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 
MatchSwapBufferMessagesPattern(IPC::Message * current_message)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 
HandleMessage()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 
OnCreateOffscreenCommandBuffer(const gfx::Size & size,const GPUCreateCommandBufferConfig & init_params,int32 route_id,bool * succeeded)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 
OnDestroyCommandBuffer(int32 route_id)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 
OnDevToolsStartEventsRecording(int32 route_id,bool * succeeded)832 void GpuChannel::OnDevToolsStartEventsRecording(int32 route_id,
833                                                 bool* succeeded) {
834   *succeeded = devtools_gpu_agent_->StartEventsRecording(route_id);
835 }
836 
OnDevToolsStopEventsRecording()837 void GpuChannel::OnDevToolsStopEventsRecording() {
838   devtools_gpu_agent_->StopEventsRecording();
839 }
840 
MessageProcessed()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 
CacheShader(const std::string & key,const std::string & shader)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 
AddFilter(IPC::MessageFilter * filter)858 void GpuChannel::AddFilter(IPC::MessageFilter* filter) {
859   channel_->AddFilter(filter);
860 }
861 
RemoveFilter(IPC::MessageFilter * filter)862 void GpuChannel::RemoveFilter(IPC::MessageFilter* filter) {
863   channel_->RemoveFilter(filter);
864 }
865 
GetMemoryUsage()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