Upstream version 9.37.195.0
[platform/framework/web/crosswalk.git] / src / content / browser / gpu / browser_gpu_channel_host_factory.cc
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 #include "content/browser/gpu/browser_gpu_channel_host_factory.h"
6
7 #include "base/bind.h"
8 #include "base/debug/trace_event.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "content/browser/gpu/gpu_data_manager_impl.h"
12 #include "content/browser/gpu/gpu_process_host.h"
13 #include "content/browser/gpu/gpu_surface_tracker.h"
14 #include "content/common/child_process_host_impl.h"
15 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
16 #include "content/common/gpu/gpu_messages.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/gpu_data_manager.h"
19 #include "content/public/common/content_client.h"
20 #include "ipc/ipc_channel_handle.h"
21 #include "ipc/ipc_forwarding_message_filter.h"
22 #include "ipc/message_filter.h"
23
24 namespace content {
25
26 BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL;
27
28 struct BrowserGpuChannelHostFactory::CreateRequest {
29   CreateRequest()
30       : event(true, false), gpu_host_id(0), route_id(MSG_ROUTING_NONE),
31         result(CREATE_COMMAND_BUFFER_FAILED) {}
32   ~CreateRequest() {}
33   base::WaitableEvent event;
34   int gpu_host_id;
35   int32 route_id;
36   CreateCommandBufferResult result;
37 };
38
39 class BrowserGpuChannelHostFactory::EstablishRequest
40     : public base::RefCountedThreadSafe<EstablishRequest> {
41  public:
42   static scoped_refptr<EstablishRequest> Create(CauseForGpuLaunch cause,
43                                                 int gpu_client_id,
44                                                 int gpu_host_id);
45   void Wait();
46   void Cancel();
47
48   int gpu_host_id() { return gpu_host_id_; }
49   IPC::ChannelHandle& channel_handle() { return channel_handle_; }
50   gpu::GPUInfo gpu_info() { return gpu_info_; }
51
52  private:
53   friend class base::RefCountedThreadSafe<EstablishRequest>;
54   explicit EstablishRequest(CauseForGpuLaunch cause,
55                             int gpu_client_id,
56                             int gpu_host_id);
57   ~EstablishRequest() {}
58   void EstablishOnIO();
59   void OnEstablishedOnIO(const IPC::ChannelHandle& channel_handle,
60                          const gpu::GPUInfo& gpu_info);
61   void FinishOnIO();
62   void FinishOnMain();
63
64   base::WaitableEvent event_;
65   CauseForGpuLaunch cause_for_gpu_launch_;
66   const int gpu_client_id_;
67   int gpu_host_id_;
68   bool reused_gpu_process_;
69   IPC::ChannelHandle channel_handle_;
70   gpu::GPUInfo gpu_info_;
71   bool finished_;
72   scoped_refptr<base::MessageLoopProxy> main_loop_;
73 };
74
75 scoped_refptr<BrowserGpuChannelHostFactory::EstablishRequest>
76 BrowserGpuChannelHostFactory::EstablishRequest::Create(CauseForGpuLaunch cause,
77                                                        int gpu_client_id,
78                                                        int gpu_host_id) {
79   scoped_refptr<EstablishRequest> establish_request =
80       new EstablishRequest(cause, gpu_client_id, gpu_host_id);
81   scoped_refptr<base::MessageLoopProxy> loop =
82       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
83   // PostTask outside the constructor to ensure at least one reference exists.
84   loop->PostTask(
85       FROM_HERE,
86       base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO,
87                  establish_request));
88   return establish_request;
89 }
90
91 BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
92     CauseForGpuLaunch cause,
93     int gpu_client_id,
94     int gpu_host_id)
95     : event_(false, false),
96       cause_for_gpu_launch_(cause),
97       gpu_client_id_(gpu_client_id),
98       gpu_host_id_(gpu_host_id),
99       reused_gpu_process_(false),
100       finished_(false),
101       main_loop_(base::MessageLoopProxy::current()) {
102 }
103
104 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
105   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
106   if (!host) {
107     host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
108                                cause_for_gpu_launch_);
109     if (!host) {
110       LOG(ERROR) << "Failed to launch GPU process.";
111       FinishOnIO();
112       return;
113     }
114     gpu_host_id_ = host->host_id();
115     reused_gpu_process_ = false;
116   } else {
117     if (reused_gpu_process_) {
118       // We come here if we retried to establish the channel because of a
119       // failure in ChannelEstablishedOnIO, but we ended up with the same
120       // process ID, meaning the failure was not because of a channel error,
121       // but another reason. So fail now.
122       LOG(ERROR) << "Failed to create channel.";
123       FinishOnIO();
124       return;
125     }
126     reused_gpu_process_ = true;
127   }
128
129   host->EstablishGpuChannel(
130       gpu_client_id_,
131       true,
132       base::Bind(
133           &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO,
134           this));
135 }
136
137 void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
138     const IPC::ChannelHandle& channel_handle,
139     const gpu::GPUInfo& gpu_info) {
140   if (channel_handle.name.empty() && reused_gpu_process_) {
141     // We failed after re-using the GPU process, but it may have died in the
142     // mean time. Retry to have a chance to create a fresh GPU process.
143     DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "
144                 "restart GPU process.";
145     EstablishOnIO();
146   } else {
147     channel_handle_ = channel_handle;
148     gpu_info_ = gpu_info;
149     FinishOnIO();
150   }
151 }
152
153 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
154   event_.Signal();
155   main_loop_->PostTask(
156       FROM_HERE,
157       base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain,
158                  this));
159 }
160
161 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
162   if (!finished_) {
163     BrowserGpuChannelHostFactory* factory =
164         BrowserGpuChannelHostFactory::instance();
165     factory->GpuChannelEstablished();
166     finished_ = true;
167   }
168 }
169
170 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
171   DCHECK(main_loop_->BelongsToCurrentThread());
172   {
173     // We're blocking the UI thread, which is generally undesirable.
174     // In this case we need to wait for this before we can show any UI
175     // /anyway/, so it won't cause additional jank.
176     // TODO(piman): Make this asynchronous (http://crbug.com/125248).
177     TRACE_EVENT0("browser",
178                  "BrowserGpuChannelHostFactory::EstablishGpuChannelSync");
179     base::ThreadRestrictions::ScopedAllowWait allow_wait;
180     event_.Wait();
181   }
182   FinishOnMain();
183 }
184
185 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
186   DCHECK(main_loop_->BelongsToCurrentThread());
187   finished_ = true;
188 }
189
190 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
191   return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL);
192 }
193
194 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {
195   DCHECK(!instance_);
196   instance_ = new BrowserGpuChannelHostFactory();
197   if (establish_gpu_channel) {
198     instance_->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP,
199                                    base::Closure());
200   }
201 }
202
203 void BrowserGpuChannelHostFactory::Terminate() {
204   DCHECK(instance_);
205   delete instance_;
206   instance_ = NULL;
207 }
208
209 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
210     : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
211       shutdown_event_(new base::WaitableEvent(true, false)),
212       gpu_host_id_(0),
213       next_create_gpu_memory_buffer_request_id_(0) {
214 }
215
216 BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
217   DCHECK(IsMainThread());
218   if (pending_request_)
219     pending_request_->Cancel();
220   for (size_t n = 0; n < established_callbacks_.size(); n++)
221     established_callbacks_[n].Run();
222   shutdown_event_->Signal();
223 }
224
225 bool BrowserGpuChannelHostFactory::IsMainThread() {
226   return BrowserThread::CurrentlyOn(BrowserThread::UI);
227 }
228
229 base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() {
230   return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI);
231 }
232
233 scoped_refptr<base::MessageLoopProxy>
234 BrowserGpuChannelHostFactory::GetIOLoopProxy() {
235   return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
236 }
237
238 scoped_ptr<base::SharedMemory>
239 BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) {
240   scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
241   if (!shm->CreateAnonymous(size))
242     return scoped_ptr<base::SharedMemory>();
243   return shm.Pass();
244 }
245
246 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
247     CreateRequest* request,
248     int32 surface_id,
249     const GPUCreateCommandBufferConfig& init_params) {
250   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
251   if (!host) {
252     request->event.Signal();
253     return;
254   }
255
256   gfx::GLSurfaceHandle surface =
257       GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
258
259   host->CreateViewCommandBuffer(
260       surface,
261       surface_id,
262       gpu_client_id_,
263       init_params,
264       request->route_id,
265       base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
266                  request));
267 }
268
269 // static
270 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
271     CreateRequest* request, CreateCommandBufferResult result) {
272   request->result = result;
273   request->event.Signal();
274 }
275
276 CreateCommandBufferResult BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
277       int32 surface_id,
278       const GPUCreateCommandBufferConfig& init_params,
279       int32 route_id) {
280   CreateRequest request;
281   request.route_id = route_id;
282   GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
283         &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
284         base::Unretained(this),
285         &request,
286         surface_id,
287         init_params));
288   // We're blocking the UI thread, which is generally undesirable.
289   // In this case we need to wait for this before we can show any UI /anyway/,
290   // so it won't cause additional jank.
291   // TODO(piman): Make this asynchronous (http://crbug.com/125248).
292   TRACE_EVENT0("browser",
293                "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
294   base::ThreadRestrictions::ScopedAllowWait allow_wait;
295   request.event.Wait();
296   return request.result;
297 }
298
299 void BrowserGpuChannelHostFactory::CreateImageOnIO(
300     gfx::PluginWindowHandle window,
301     int32 image_id,
302     const CreateImageCallback& callback) {
303   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
304   if (!host) {
305     ImageCreatedOnIO(callback, gfx::Size());
306     return;
307   }
308
309   host->CreateImage(
310       window,
311       gpu_client_id_,
312       image_id,
313       base::Bind(&BrowserGpuChannelHostFactory::ImageCreatedOnIO, callback));
314 }
315
316 // static
317 void BrowserGpuChannelHostFactory::ImageCreatedOnIO(
318     const CreateImageCallback& callback, const gfx::Size size) {
319   BrowserThread::PostTask(
320       BrowserThread::UI,
321       FROM_HERE,
322       base::Bind(&BrowserGpuChannelHostFactory::OnImageCreated,
323                  callback, size));
324 }
325
326 // static
327 void BrowserGpuChannelHostFactory::OnImageCreated(
328     const CreateImageCallback& callback, const gfx::Size size) {
329   callback.Run(size);
330 }
331
332 void BrowserGpuChannelHostFactory::CreateImage(
333     gfx::PluginWindowHandle window,
334     int32 image_id,
335     const CreateImageCallback& callback) {
336   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337   GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
338         &BrowserGpuChannelHostFactory::CreateImageOnIO,
339         base::Unretained(this),
340         window,
341         image_id,
342         callback));
343 }
344
345 void BrowserGpuChannelHostFactory::DeleteImageOnIO(
346     int32 image_id, int32 sync_point) {
347   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
348   if (!host) {
349     return;
350   }
351
352   host->DeleteImage(gpu_client_id_, image_id, sync_point);
353 }
354
355 void BrowserGpuChannelHostFactory::DeleteImage(
356     int32 image_id, int32 sync_point) {
357   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358   GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
359         &BrowserGpuChannelHostFactory::DeleteImageOnIO,
360         base::Unretained(this),
361         image_id,
362         sync_point));
363 }
364
365 GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
366     CauseForGpuLaunch cause_for_gpu_launch) {
367   EstablishGpuChannel(cause_for_gpu_launch, base::Closure());
368
369   if (pending_request_)
370     pending_request_->Wait();
371
372   return gpu_channel_.get();
373 }
374
375 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
376     CauseForGpuLaunch cause_for_gpu_launch,
377     const base::Closure& callback) {
378   if (gpu_channel_.get() && gpu_channel_->IsLost()) {
379     DCHECK(!pending_request_);
380     // Recreate the channel if it has been lost.
381     gpu_channel_ = NULL;
382   }
383
384   if (!gpu_channel_ && !pending_request_) {
385     // We should only get here if the context was lost.
386     pending_request_ = EstablishRequest::Create(
387         cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);
388   }
389
390   if (!callback.is_null()) {
391     if (gpu_channel_)
392       callback.Run();
393     else
394       established_callbacks_.push_back(callback);
395   }
396 }
397
398 GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() {
399   if (gpu_channel_ && !gpu_channel_->IsLost())
400     return gpu_channel_;
401
402   return NULL;
403 }
404
405 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
406   DCHECK(IsMainThread());
407   DCHECK(pending_request_);
408   if (pending_request_->channel_handle().name.empty()) {
409     DCHECK(!gpu_channel_);
410   } else {
411     GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
412     gpu_channel_ = GpuChannelHost::Create(this,
413                                           pending_request_->gpu_info(),
414                                           pending_request_->channel_handle(),
415                                           shutdown_event_.get());
416   }
417   gpu_host_id_ = pending_request_->gpu_host_id();
418   pending_request_ = NULL;
419
420   for (size_t n = 0; n < established_callbacks_.size(); n++)
421     established_callbacks_[n].Run();
422
423   established_callbacks_.clear();
424 }
425
426 scoped_ptr<gfx::GpuMemoryBuffer>
427 BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(size_t width,
428                                                       size_t height,
429                                                       unsigned internalformat,
430                                                       unsigned usage) {
431   if (!GpuMemoryBufferImpl::IsFormatValid(internalformat) ||
432       !GpuMemoryBufferImpl::IsUsageValid(usage))
433     return scoped_ptr<gfx::GpuMemoryBuffer>();
434
435   return GpuMemoryBufferImpl::Create(gfx::Size(width, height),
436                                      internalformat,
437                                      usage).PassAs<gfx::GpuMemoryBuffer>();
438 }
439
440 // static
441 void BrowserGpuChannelHostFactory::AddFilterOnIO(
442     int host_id,
443     scoped_refptr<IPC::MessageFilter> filter) {
444   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
445
446   GpuProcessHost* host = GpuProcessHost::FromID(host_id);
447   if (host)
448     host->AddFilter(filter.get());
449 }
450
451 void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
452       const uint32* message_ids,
453       size_t num_messages,
454       const base::Callback<void(const IPC::Message&)>& handler,
455       base::TaskRunner* target_task_runner) {
456   DCHECK(gpu_host_id_)
457       << "Do not call"
458       << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
459       << " until the GpuProcessHost has been set up.";
460
461   scoped_refptr<IPC::ForwardingMessageFilter> filter =
462       new IPC::ForwardingMessageFilter(message_ids,
463                                        num_messages,
464                                        target_task_runner);
465   filter->AddRoute(MSG_ROUTING_CONTROL, handler);
466
467   GetIOLoopProxy()->PostTask(
468       FROM_HERE,
469       base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO,
470                  gpu_host_id_,
471                  filter));
472 }
473
474 void BrowserGpuChannelHostFactory::CreateGpuMemoryBuffer(
475     const gfx::GpuMemoryBufferHandle& handle,
476     const gfx::Size& size,
477     unsigned internalformat,
478     unsigned usage,
479     const CreateGpuMemoryBufferCallback& callback) {
480   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
481   uint32 request_id = next_create_gpu_memory_buffer_request_id_++;
482   create_gpu_memory_buffer_requests_[request_id] = callback;
483   GetIOLoopProxy()->PostTask(
484       FROM_HERE,
485       base::Bind(&BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO,
486                  base::Unretained(this),
487                  handle,
488                  size,
489                  internalformat,
490                  usage,
491                  request_id));
492 }
493
494 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBuffer(
495     const gfx::GpuMemoryBufferHandle& handle,
496     int32 sync_point) {
497   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
498   GetIOLoopProxy()->PostTask(
499       FROM_HERE,
500       base::Bind(&BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO,
501                  base::Unretained(this),
502                  handle,
503                  sync_point));
504 }
505
506 void BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO(
507     const gfx::GpuMemoryBufferHandle& handle,
508     const gfx::Size& size,
509     unsigned internalformat,
510     unsigned usage,
511     uint32 request_id) {
512   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
513   if (!host) {
514     GpuMemoryBufferCreatedOnIO(request_id, gfx::GpuMemoryBufferHandle());
515     return;
516   }
517
518   host->CreateGpuMemoryBuffer(
519       handle,
520       size,
521       internalformat,
522       usage,
523       base::Bind(&BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO,
524                  base::Unretained(this),
525                  request_id));
526 }
527
528 void BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO(
529     uint32 request_id,
530     const gfx::GpuMemoryBufferHandle& handle) {
531   BrowserThread::PostTask(
532       BrowserThread::UI,
533       FROM_HERE,
534       base::Bind(&BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated,
535                  base::Unretained(this),
536                  request_id,
537                  handle));
538 }
539
540 void BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated(
541     uint32 request_id,
542     const gfx::GpuMemoryBufferHandle& handle) {
543   CreateGpuMemoryBufferCallbackMap::iterator iter =
544       create_gpu_memory_buffer_requests_.find(request_id);
545   DCHECK(iter != create_gpu_memory_buffer_requests_.end());
546   iter->second.Run(handle);
547   create_gpu_memory_buffer_requests_.erase(iter);
548 }
549
550 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO(
551     const gfx::GpuMemoryBufferHandle& handle,
552     int32 sync_point) {
553   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
554   if (!host)
555     return;
556
557   host->DestroyGpuMemoryBuffer(handle, sync_point);
558 }
559
560 }  // namespace content