Upstream version 6.35.121.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/threading/thread_restrictions.h"
10 #include "content/browser/gpu/gpu_data_manager_impl.h"
11 #include "content/browser/gpu/gpu_process_host.h"
12 #include "content/browser/gpu/gpu_surface_tracker.h"
13 #include "content/common/child_process_host_impl.h"
14 #include "content/common/gpu/client/gpu_memory_buffer_impl_shm.h"
15 #include "content/common/gpu/gpu_messages.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/gpu_data_manager.h"
18 #include "content/public/common/content_client.h"
19 #include "ipc/ipc_forwarding_message_filter.h"
20
21 namespace content {
22
23 BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL;
24
25 BrowserGpuChannelHostFactory::CreateRequest::CreateRequest()
26     : event(true, false),
27       gpu_host_id(0),
28       route_id(MSG_ROUTING_NONE) {
29 }
30
31 BrowserGpuChannelHostFactory::CreateRequest::~CreateRequest() {
32 }
33
34 BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
35     CauseForGpuLaunch cause,
36     int gpu_client_id,
37     int gpu_host_id)
38     : event_(false, false),
39       cause_for_gpu_launch_(cause),
40       gpu_client_id_(gpu_client_id),
41       gpu_host_id_(gpu_host_id),
42       reused_gpu_process_(false),
43       finished_(false),
44       main_loop_(base::MessageLoopProxy::current()) {
45   scoped_refptr<base::MessageLoopProxy> loop =
46       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
47   loop->PostTask(
48       FROM_HERE,
49       base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO,
50                  this));
51 }
52
53 BrowserGpuChannelHostFactory::EstablishRequest::~EstablishRequest() {
54 }
55
56 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
57   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
58   if (!host) {
59     host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
60                                cause_for_gpu_launch_);
61     if (!host) {
62       LOG(ERROR) << "Failed to launch GPU process.";
63       FinishOnIO();
64       return;
65     }
66     gpu_host_id_ = host->host_id();
67     reused_gpu_process_ = false;
68   } else {
69     if (reused_gpu_process_) {
70       // We come here if we retried to establish the channel because of a
71       // failure in ChannelEstablishedOnIO, but we ended up with the same
72       // process ID, meaning the failure was not because of a channel error,
73       // but another reason. So fail now.
74       LOG(ERROR) << "Failed to create channel.";
75       FinishOnIO();
76       return;
77     }
78     reused_gpu_process_ = true;
79   }
80
81   host->EstablishGpuChannel(
82       gpu_client_id_,
83       true,
84       base::Bind(
85           &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO,
86           this));
87 }
88
89 void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
90     const IPC::ChannelHandle& channel_handle,
91     const gpu::GPUInfo& gpu_info) {
92   if (channel_handle.name.empty() && reused_gpu_process_) {
93     // We failed after re-using the GPU process, but it may have died in the
94     // mean time. Retry to have a chance to create a fresh GPU process.
95     DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "
96                 "restart GPU process.";
97     EstablishOnIO();
98   } else {
99     channel_handle_ = channel_handle;
100     gpu_info_ = gpu_info;
101     FinishOnIO();
102   }
103 }
104
105 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
106   event_.Signal();
107   main_loop_->PostTask(
108       FROM_HERE,
109       base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain,
110                  this));
111 }
112
113 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
114   if (!finished_) {
115     BrowserGpuChannelHostFactory* factory =
116         BrowserGpuChannelHostFactory::instance();
117     factory->GpuChannelEstablished();
118     finished_ = true;
119   }
120 }
121
122 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
123   DCHECK(main_loop_->BelongsToCurrentThread());
124   {
125     // We're blocking the UI thread, which is generally undesirable.
126     // In this case we need to wait for this before we can show any UI
127     // /anyway/, so it won't cause additional jank.
128     // TODO(piman): Make this asynchronous (http://crbug.com/125248).
129     TRACE_EVENT0("browser",
130                  "BrowserGpuChannelHostFactory::EstablishGpuChannelSync");
131     base::ThreadRestrictions::ScopedAllowWait allow_wait;
132     event_.Wait();
133   }
134   FinishOnMain();
135 }
136
137 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
138   DCHECK(main_loop_->BelongsToCurrentThread());
139   finished_ = true;
140 }
141
142 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
143   return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL);
144 }
145
146 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {
147   DCHECK(!instance_);
148   instance_ = new BrowserGpuChannelHostFactory(establish_gpu_channel);
149 }
150
151 void BrowserGpuChannelHostFactory::Terminate() {
152   DCHECK(instance_);
153   delete instance_;
154   instance_ = NULL;
155 }
156
157 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory(
158     bool establish_gpu_channel)
159     : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
160       shutdown_event_(new base::WaitableEvent(true, false)),
161       gpu_host_id_(0) {
162   if (establish_gpu_channel) {
163     pending_request_ = new EstablishRequest(
164         CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP, gpu_client_id_, gpu_host_id_);
165   }
166 }
167
168 BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
169   DCHECK(IsMainThread());
170   if (pending_request_)
171     pending_request_->Cancel();
172   for (size_t n = 0; n < established_callbacks_.size(); n++)
173     established_callbacks_[n].Run();
174   shutdown_event_->Signal();
175 }
176
177 bool BrowserGpuChannelHostFactory::IsMainThread() {
178   return BrowserThread::CurrentlyOn(BrowserThread::UI);
179 }
180
181 base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() {
182   return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI);
183 }
184
185 scoped_refptr<base::MessageLoopProxy>
186 BrowserGpuChannelHostFactory::GetIOLoopProxy() {
187   return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
188 }
189
190 scoped_ptr<base::SharedMemory>
191 BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) {
192   scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
193   if (!shm->CreateAnonymous(size))
194     return scoped_ptr<base::SharedMemory>();
195   return shm.Pass();
196 }
197
198 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
199     CreateRequest* request,
200     int32 surface_id,
201     const GPUCreateCommandBufferConfig& init_params) {
202   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
203   if (!host) {
204     request->event.Signal();
205     return;
206   }
207
208   gfx::GLSurfaceHandle surface =
209       GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
210
211   host->CreateViewCommandBuffer(
212       surface,
213       surface_id,
214       gpu_client_id_,
215       init_params,
216       base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
217                  request));
218 }
219
220 // static
221 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
222     CreateRequest* request, int32 route_id) {
223   request->route_id = route_id;
224   request->event.Signal();
225 }
226
227 int32 BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
228       int32 surface_id,
229       const GPUCreateCommandBufferConfig& init_params) {
230   CreateRequest request;
231   GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
232         &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
233         base::Unretained(this),
234         &request,
235         surface_id,
236         init_params));
237   // We're blocking the UI thread, which is generally undesirable.
238   // In this case we need to wait for this before we can show any UI /anyway/,
239   // so it won't cause additional jank.
240   // TODO(piman): Make this asynchronous (http://crbug.com/125248).
241   TRACE_EVENT0("browser",
242                "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
243   base::ThreadRestrictions::ScopedAllowWait allow_wait;
244   request.event.Wait();
245   return request.route_id;
246 }
247
248 void BrowserGpuChannelHostFactory::CreateImageOnIO(
249     gfx::PluginWindowHandle window,
250     int32 image_id,
251     const CreateImageCallback& callback) {
252   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
253   if (!host) {
254     ImageCreatedOnIO(callback, gfx::Size());
255     return;
256   }
257
258   host->CreateImage(
259       window,
260       gpu_client_id_,
261       image_id,
262       base::Bind(&BrowserGpuChannelHostFactory::ImageCreatedOnIO, callback));
263 }
264
265 // static
266 void BrowserGpuChannelHostFactory::ImageCreatedOnIO(
267     const CreateImageCallback& callback, const gfx::Size size) {
268   BrowserThread::PostTask(
269       BrowserThread::UI,
270       FROM_HERE,
271       base::Bind(&BrowserGpuChannelHostFactory::OnImageCreated,
272                  callback, size));
273 }
274
275 // static
276 void BrowserGpuChannelHostFactory::OnImageCreated(
277     const CreateImageCallback& callback, const gfx::Size size) {
278   callback.Run(size);
279 }
280
281 void BrowserGpuChannelHostFactory::CreateImage(
282     gfx::PluginWindowHandle window,
283     int32 image_id,
284     const CreateImageCallback& callback) {
285   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
286   GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
287         &BrowserGpuChannelHostFactory::CreateImageOnIO,
288         base::Unretained(this),
289         window,
290         image_id,
291         callback));
292 }
293
294 void BrowserGpuChannelHostFactory::DeleteImageOnIO(
295     int32 image_id, int32 sync_point) {
296   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
297   if (!host) {
298     return;
299   }
300
301   host->DeleteImage(gpu_client_id_, image_id, sync_point);
302 }
303
304 void BrowserGpuChannelHostFactory::DeleteImage(
305     int32 image_id, int32 sync_point) {
306   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
307   GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
308         &BrowserGpuChannelHostFactory::DeleteImageOnIO,
309         base::Unretained(this),
310         image_id,
311         sync_point));
312 }
313
314 GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
315     CauseForGpuLaunch cause_for_gpu_launch) {
316   EstablishGpuChannel(cause_for_gpu_launch, base::Closure());
317
318   if (pending_request_)
319     pending_request_->Wait();
320
321   return gpu_channel_.get();
322 }
323
324 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
325     CauseForGpuLaunch cause_for_gpu_launch,
326     const base::Closure& callback) {
327   if (gpu_channel_.get() && gpu_channel_->IsLost()) {
328     DCHECK(!pending_request_);
329     // Recreate the channel if it has been lost.
330     gpu_channel_ = NULL;
331   }
332
333   if (!gpu_channel_ && !pending_request_) {
334     // We should only get here if the context was lost.
335     pending_request_ = new EstablishRequest(
336         cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);
337   }
338
339   if (!callback.is_null()) {
340     if (gpu_channel_)
341       callback.Run();
342     else
343       established_callbacks_.push_back(callback);
344   }
345 }
346
347 GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() {
348   if (gpu_channel_ && !gpu_channel_->IsLost())
349     return gpu_channel_;
350
351   return NULL;
352 }
353
354 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
355   DCHECK(IsMainThread());
356   DCHECK(pending_request_);
357   if (pending_request_->channel_handle().name.empty())
358     return;
359
360   GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
361   gpu_channel_ = GpuChannelHost::Create(this,
362                                         pending_request_->gpu_info(),
363                                         pending_request_->channel_handle(),
364                                         shutdown_event_.get());
365   gpu_host_id_ = pending_request_->gpu_host_id();
366   pending_request_ = NULL;
367
368   for (size_t n = 0; n < established_callbacks_.size(); n++)
369     established_callbacks_[n].Run();
370
371   established_callbacks_.clear();
372 }
373
374 scoped_ptr<gfx::GpuMemoryBuffer>
375     BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(
376         size_t width,
377         size_t height,
378         unsigned internalformat) {
379   if (!GpuMemoryBufferImpl::IsFormatValid(internalformat))
380     return scoped_ptr<gfx::GpuMemoryBuffer>();
381
382   size_t size = width * height *
383       GpuMemoryBufferImpl::BytesPerPixel(internalformat);
384   scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
385   if (!shm->CreateAnonymous(size))
386     return scoped_ptr<gfx::GpuMemoryBuffer>();
387
388   scoped_ptr<GpuMemoryBufferImplShm> buffer(
389       new GpuMemoryBufferImplShm(gfx::Size(width, height), internalformat));
390   if (!buffer->InitializeFromSharedMemory(shm.Pass()))
391     return scoped_ptr<gfx::GpuMemoryBuffer>();
392
393   return buffer.PassAs<gfx::GpuMemoryBuffer>();
394 }
395
396 // static
397 void BrowserGpuChannelHostFactory::AddFilterOnIO(
398     int host_id,
399     scoped_refptr<IPC::ChannelProxy::MessageFilter> filter) {
400   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
401
402   GpuProcessHost* host = GpuProcessHost::FromID(host_id);
403   if (host)
404     host->AddFilter(filter.get());
405 }
406
407 void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
408       const uint32* message_ids,
409       size_t num_messages,
410       const base::Callback<void(const IPC::Message&)>& handler,
411       base::TaskRunner* target_task_runner) {
412   DCHECK(gpu_host_id_)
413       << "Do not call"
414       << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
415       << " until the GpuProcessHost has been set up.";
416
417   scoped_refptr<IPC::ForwardingMessageFilter> filter =
418       new IPC::ForwardingMessageFilter(message_ids,
419                                        num_messages,
420                                        target_task_runner);
421   filter->AddRoute(MSG_ROUTING_CONTROL, handler);
422
423   GetIOLoopProxy()->PostTask(
424       FROM_HERE,
425       base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO,
426                  gpu_host_id_,
427                  filter));
428 }
429
430 }  // namespace content