browser_gpu_channel_host_factory.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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}
213
214BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
215  DCHECK(IsMainThread());
216  if (pending_request_)
217    pending_request_->Cancel();
218  for (size_t n = 0; n < established_callbacks_.size(); n++)
219    established_callbacks_[n].Run();
220  shutdown_event_->Signal();
221}
222
223bool BrowserGpuChannelHostFactory::IsMainThread() {
224  return BrowserThread::CurrentlyOn(BrowserThread::UI);
225}
226
227base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() {
228  return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI);
229}
230
231scoped_refptr<base::MessageLoopProxy>
232BrowserGpuChannelHostFactory::GetIOLoopProxy() {
233  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
234}
235
236scoped_ptr<base::SharedMemory>
237BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) {
238  scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
239  if (!shm->CreateAnonymous(size))
240    return scoped_ptr<base::SharedMemory>();
241  return shm.Pass();
242}
243
244void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
245    CreateRequest* request,
246    int32 surface_id,
247    const GPUCreateCommandBufferConfig& init_params) {
248  GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
249  if (!host) {
250    request->event.Signal();
251    return;
252  }
253
254  gfx::GLSurfaceHandle surface =
255      GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
256
257  host->CreateViewCommandBuffer(
258      surface,
259      surface_id,
260      gpu_client_id_,
261      init_params,
262      request->route_id,
263      base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
264                 request));
265}
266
267// static
268void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
269    CreateRequest* request, bool succeeded) {
270  request->succeeded = succeeded;
271  request->event.Signal();
272}
273
274bool BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
275      int32 surface_id,
276      const GPUCreateCommandBufferConfig& init_params,
277      int32 route_id) {
278  CreateRequest request;
279  request.route_id = route_id;
280  GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
281        &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
282        base::Unretained(this),
283        &request,
284        surface_id,
285        init_params));
286  // We're blocking the UI thread, which is generally undesirable.
287  // In this case we need to wait for this before we can show any UI /anyway/,
288  // so it won't cause additional jank.
289  // TODO(piman): Make this asynchronous (http://crbug.com/125248).
290  TRACE_EVENT0("browser",
291               "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
292  base::ThreadRestrictions::ScopedAllowWait allow_wait;
293  request.event.Wait();
294  return request.succeeded;
295}
296
297void BrowserGpuChannelHostFactory::CreateImageOnIO(
298    gfx::PluginWindowHandle window,
299    int32 image_id,
300    const CreateImageCallback& callback) {
301  GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
302  if (!host) {
303    ImageCreatedOnIO(callback, gfx::Size());
304    return;
305  }
306
307  host->CreateImage(
308      window,
309      gpu_client_id_,
310      image_id,
311      base::Bind(&BrowserGpuChannelHostFactory::ImageCreatedOnIO, callback));
312}
313
314// static
315void BrowserGpuChannelHostFactory::ImageCreatedOnIO(
316    const CreateImageCallback& callback, const gfx::Size size) {
317  BrowserThread::PostTask(
318      BrowserThread::UI,
319      FROM_HERE,
320      base::Bind(&BrowserGpuChannelHostFactory::OnImageCreated,
321                 callback, size));
322}
323
324// static
325void BrowserGpuChannelHostFactory::OnImageCreated(
326    const CreateImageCallback& callback, const gfx::Size size) {
327  callback.Run(size);
328}
329
330void BrowserGpuChannelHostFactory::CreateImage(
331    gfx::PluginWindowHandle window,
332    int32 image_id,
333    const CreateImageCallback& callback) {
334  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
335  GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
336        &BrowserGpuChannelHostFactory::CreateImageOnIO,
337        base::Unretained(this),
338        window,
339        image_id,
340        callback));
341}
342
343void BrowserGpuChannelHostFactory::DeleteImageOnIO(
344    int32 image_id, int32 sync_point) {
345  GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
346  if (!host) {
347    return;
348  }
349
350  host->DeleteImage(gpu_client_id_, image_id, sync_point);
351}
352
353void BrowserGpuChannelHostFactory::DeleteImage(
354    int32 image_id, int32 sync_point) {
355  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
356  GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
357        &BrowserGpuChannelHostFactory::DeleteImageOnIO,
358        base::Unretained(this),
359        image_id,
360        sync_point));
361}
362
363GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
364    CauseForGpuLaunch cause_for_gpu_launch) {
365  EstablishGpuChannel(cause_for_gpu_launch, base::Closure());
366
367  if (pending_request_)
368    pending_request_->Wait();
369
370  return gpu_channel_.get();
371}
372
373void BrowserGpuChannelHostFactory::EstablishGpuChannel(
374    CauseForGpuLaunch cause_for_gpu_launch,
375    const base::Closure& callback) {
376  if (gpu_channel_.get() && gpu_channel_->IsLost()) {
377    DCHECK(!pending_request_);
378    // Recreate the channel if it has been lost.
379    gpu_channel_ = NULL;
380  }
381
382  if (!gpu_channel_ && !pending_request_) {
383    // We should only get here if the context was lost.
384    pending_request_ = EstablishRequest::Create(
385        cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);
386  }
387
388  if (!callback.is_null()) {
389    if (gpu_channel_)
390      callback.Run();
391    else
392      established_callbacks_.push_back(callback);
393  }
394}
395
396GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() {
397  if (gpu_channel_ && !gpu_channel_->IsLost())
398    return gpu_channel_;
399
400  return NULL;
401}
402
403void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
404  DCHECK(IsMainThread());
405  DCHECK(pending_request_);
406  if (pending_request_->channel_handle().name.empty()) {
407    DCHECK(!gpu_channel_);
408  } else {
409    GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
410    gpu_channel_ = GpuChannelHost::Create(this,
411                                          pending_request_->gpu_info(),
412                                          pending_request_->channel_handle(),
413                                          shutdown_event_.get());
414  }
415  gpu_host_id_ = pending_request_->gpu_host_id();
416  pending_request_ = NULL;
417
418  for (size_t n = 0; n < established_callbacks_.size(); n++)
419    established_callbacks_[n].Run();
420
421  established_callbacks_.clear();
422}
423
424scoped_ptr<gfx::GpuMemoryBuffer>
425BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(size_t width,
426                                                      size_t height,
427                                                      unsigned internalformat,
428                                                      unsigned usage) {
429  if (!GpuMemoryBufferImpl::IsFormatValid(internalformat) ||
430      !GpuMemoryBufferImpl::IsUsageValid(usage))
431    return scoped_ptr<gfx::GpuMemoryBuffer>();
432
433  return GpuMemoryBufferImpl::Create(gfx::Size(width, height),
434                                     internalformat,
435                                     usage).PassAs<gfx::GpuMemoryBuffer>();
436}
437
438// static
439void BrowserGpuChannelHostFactory::AddFilterOnIO(
440    int host_id,
441    scoped_refptr<IPC::MessageFilter> filter) {
442  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
443
444  GpuProcessHost* host = GpuProcessHost::FromID(host_id);
445  if (host)
446    host->AddFilter(filter.get());
447}
448
449void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
450      const uint32* message_ids,
451      size_t num_messages,
452      const base::Callback<void(const IPC::Message&)>& handler,
453      base::TaskRunner* target_task_runner) {
454  DCHECK(gpu_host_id_)
455      << "Do not call"
456      << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
457      << " until the GpuProcessHost has been set up.";
458
459  scoped_refptr<IPC::ForwardingMessageFilter> filter =
460      new IPC::ForwardingMessageFilter(message_ids,
461                                       num_messages,
462                                       target_task_runner);
463  filter->AddRoute(MSG_ROUTING_CONTROL, handler);
464
465  GetIOLoopProxy()->PostTask(
466      FROM_HERE,
467      base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO,
468                 gpu_host_id_,
469                 filter));
470}
471
472}  // namespace content
473