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