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.
5 #include "content/browser/gpu/browser_gpu_channel_host_factory.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"
26 BrowserGpuChannelHostFactory
* BrowserGpuChannelHostFactory::instance_
= NULL
;
28 struct BrowserGpuChannelHostFactory::CreateRequest
{
30 : event(true, false), gpu_host_id(0), route_id(MSG_ROUTING_NONE
),
31 result(CREATE_COMMAND_BUFFER_FAILED
) {}
33 base::WaitableEvent event
;
36 CreateCommandBufferResult result
;
39 class BrowserGpuChannelHostFactory::EstablishRequest
40 : public base::RefCountedThreadSafe
<EstablishRequest
> {
42 static scoped_refptr
<EstablishRequest
> Create(CauseForGpuLaunch cause
,
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_
; }
53 friend class base::RefCountedThreadSafe
<EstablishRequest
>;
54 explicit EstablishRequest(CauseForGpuLaunch cause
,
57 ~EstablishRequest() {}
59 void OnEstablishedOnIO(const IPC::ChannelHandle
& channel_handle
,
60 const gpu::GPUInfo
& gpu_info
);
64 base::WaitableEvent event_
;
65 CauseForGpuLaunch cause_for_gpu_launch_
;
66 const int gpu_client_id_
;
68 bool reused_gpu_process_
;
69 IPC::ChannelHandle channel_handle_
;
70 gpu::GPUInfo gpu_info_
;
72 scoped_refptr
<base::MessageLoopProxy
> main_loop_
;
75 scoped_refptr
<BrowserGpuChannelHostFactory::EstablishRequest
>
76 BrowserGpuChannelHostFactory::EstablishRequest::Create(CauseForGpuLaunch cause
,
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.
86 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO
,
88 return establish_request
;
91 BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
92 CauseForGpuLaunch cause
,
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),
101 main_loop_(base::MessageLoopProxy::current()) {
104 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
105 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
107 host
= GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED
,
108 cause_for_gpu_launch_
);
110 LOG(ERROR
) << "Failed to launch GPU process.";
114 gpu_host_id_
= host
->host_id();
115 reused_gpu_process_
= false;
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.";
126 reused_gpu_process_
= true;
129 host
->EstablishGpuChannel(
134 &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO
,
138 void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
139 const IPC::ChannelHandle
& channel_handle
,
140 const gpu::GPUInfo
& gpu_info
) {
141 if (channel_handle
.name
.empty() && reused_gpu_process_
) {
142 // We failed after re-using the GPU process, but it may have died in the
143 // mean time. Retry to have a chance to create a fresh GPU process.
144 DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "
145 "restart GPU process.";
148 channel_handle_
= channel_handle
;
149 gpu_info_
= gpu_info
;
154 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
156 main_loop_
->PostTask(
158 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain
,
162 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
164 BrowserGpuChannelHostFactory
* factory
=
165 BrowserGpuChannelHostFactory::instance();
166 factory
->GpuChannelEstablished();
171 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
172 DCHECK(main_loop_
->BelongsToCurrentThread());
174 // We're blocking the UI thread, which is generally undesirable.
175 // In this case we need to wait for this before we can show any UI
176 // /anyway/, so it won't cause additional jank.
177 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
178 TRACE_EVENT0("browser",
179 "BrowserGpuChannelHostFactory::EstablishGpuChannelSync");
180 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
186 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
187 DCHECK(main_loop_
->BelongsToCurrentThread());
191 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
192 return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL
);
195 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel
) {
197 instance_
= new BrowserGpuChannelHostFactory();
198 if (establish_gpu_channel
) {
199 instance_
->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP
,
204 void BrowserGpuChannelHostFactory::Terminate() {
210 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
211 : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
212 shutdown_event_(new base::WaitableEvent(true, false)),
214 next_create_gpu_memory_buffer_request_id_(0) {
217 BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
218 DCHECK(IsMainThread());
219 if (pending_request_
.get())
220 pending_request_
->Cancel();
221 for (size_t n
= 0; n
< established_callbacks_
.size(); n
++)
222 established_callbacks_
[n
].Run();
223 shutdown_event_
->Signal();
226 bool BrowserGpuChannelHostFactory::IsMainThread() {
227 return BrowserThread::CurrentlyOn(BrowserThread::UI
);
230 base::MessageLoop
* BrowserGpuChannelHostFactory::GetMainLoop() {
231 return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI
);
234 scoped_refptr
<base::MessageLoopProxy
>
235 BrowserGpuChannelHostFactory::GetIOLoopProxy() {
236 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
);
239 scoped_ptr
<base::SharedMemory
>
240 BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size
) {
241 scoped_ptr
<base::SharedMemory
> shm(new base::SharedMemory());
242 if (!shm
->CreateAnonymous(size
))
243 return scoped_ptr
<base::SharedMemory
>();
247 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
248 CreateRequest
* request
,
250 const GPUCreateCommandBufferConfig
& init_params
) {
251 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
253 request
->event
.Signal();
257 gfx::GLSurfaceHandle surface
=
258 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id
);
260 host
->CreateViewCommandBuffer(
266 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO
,
271 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
272 CreateRequest
* request
, CreateCommandBufferResult result
) {
273 request
->result
= result
;
274 request
->event
.Signal();
277 CreateCommandBufferResult
BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
279 const GPUCreateCommandBufferConfig
& init_params
,
281 CreateRequest request
;
282 request
.route_id
= route_id
;
283 GetIOLoopProxy()->PostTask(FROM_HERE
, base::Bind(
284 &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO
,
285 base::Unretained(this),
289 // We're blocking the UI thread, which is generally undesirable.
290 // In this case we need to wait for this before we can show any UI /anyway/,
291 // so it won't cause additional jank.
292 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
293 TRACE_EVENT0("browser",
294 "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
295 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
296 request
.event
.Wait();
297 return request
.result
;
300 GpuChannelHost
* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
301 CauseForGpuLaunch cause_for_gpu_launch
) {
302 EstablishGpuChannel(cause_for_gpu_launch
, base::Closure());
304 if (pending_request_
.get())
305 pending_request_
->Wait();
307 return gpu_channel_
.get();
310 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
311 CauseForGpuLaunch cause_for_gpu_launch
,
312 const base::Closure
& callback
) {
313 if (gpu_channel_
.get() && gpu_channel_
->IsLost()) {
314 DCHECK(!pending_request_
.get());
315 // Recreate the channel if it has been lost.
319 if (!gpu_channel_
.get() && !pending_request_
.get()) {
320 // We should only get here if the context was lost.
321 pending_request_
= EstablishRequest::Create(
322 cause_for_gpu_launch
, gpu_client_id_
, gpu_host_id_
);
325 if (!callback
.is_null()) {
326 if (gpu_channel_
.get())
329 established_callbacks_
.push_back(callback
);
333 GpuChannelHost
* BrowserGpuChannelHostFactory::GetGpuChannel() {
334 if (gpu_channel_
.get() && !gpu_channel_
->IsLost())
335 return gpu_channel_
.get();
340 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
341 DCHECK(IsMainThread());
342 DCHECK(pending_request_
.get());
343 if (pending_request_
->channel_handle().name
.empty()) {
344 DCHECK(!gpu_channel_
.get());
346 GetContentClient()->SetGpuInfo(pending_request_
->gpu_info());
347 gpu_channel_
= GpuChannelHost::Create(this,
348 pending_request_
->gpu_info(),
349 pending_request_
->channel_handle(),
350 shutdown_event_
.get());
352 gpu_host_id_
= pending_request_
->gpu_host_id();
353 pending_request_
= NULL
;
355 for (size_t n
= 0; n
< established_callbacks_
.size(); n
++)
356 established_callbacks_
[n
].Run();
358 established_callbacks_
.clear();
361 scoped_ptr
<gfx::GpuMemoryBuffer
>
362 BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(size_t width
,
364 unsigned internalformat
,
366 if (!GpuMemoryBufferImpl::IsFormatValid(internalformat
) ||
367 !GpuMemoryBufferImpl::IsUsageValid(usage
))
368 return scoped_ptr
<gfx::GpuMemoryBuffer
>();
370 return GpuMemoryBufferImpl::Create(gfx::Size(width
, height
),
372 usage
).PassAs
<gfx::GpuMemoryBuffer
>();
376 void BrowserGpuChannelHostFactory::AddFilterOnIO(
378 scoped_refptr
<IPC::MessageFilter
> filter
) {
379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
381 GpuProcessHost
* host
= GpuProcessHost::FromID(host_id
);
383 host
->AddFilter(filter
.get());
386 void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
387 const uint32
* message_ids
,
389 const base::Callback
<void(const IPC::Message
&)>& handler
,
390 base::TaskRunner
* target_task_runner
) {
393 << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
394 << " until the GpuProcessHost has been set up.";
396 scoped_refptr
<IPC::ForwardingMessageFilter
> filter
=
397 new IPC::ForwardingMessageFilter(message_ids
,
400 filter
->AddRoute(MSG_ROUTING_CONTROL
, handler
);
402 GetIOLoopProxy()->PostTask(
404 base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO
,
409 void BrowserGpuChannelHostFactory::CreateGpuMemoryBuffer(
410 const gfx::GpuMemoryBufferHandle
& handle
,
411 const gfx::Size
& size
,
412 unsigned internalformat
,
414 const CreateGpuMemoryBufferCallback
& callback
) {
415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
416 uint32 request_id
= next_create_gpu_memory_buffer_request_id_
++;
417 create_gpu_memory_buffer_requests_
[request_id
] = callback
;
418 GetIOLoopProxy()->PostTask(
420 base::Bind(&BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO
,
421 base::Unretained(this),
429 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBuffer(
430 const gfx::GpuMemoryBufferHandle
& handle
,
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
433 GetIOLoopProxy()->PostTask(
435 base::Bind(&BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO
,
436 base::Unretained(this),
441 void BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO(
442 const gfx::GpuMemoryBufferHandle
& handle
,
443 const gfx::Size
& size
,
444 unsigned internalformat
,
447 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
449 GpuMemoryBufferCreatedOnIO(request_id
, gfx::GpuMemoryBufferHandle());
453 host
->CreateGpuMemoryBuffer(
458 base::Bind(&BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO
,
459 base::Unretained(this),
463 void BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO(
465 const gfx::GpuMemoryBufferHandle
& handle
) {
466 BrowserThread::PostTask(
469 base::Bind(&BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated
,
470 base::Unretained(this),
475 void BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated(
477 const gfx::GpuMemoryBufferHandle
& handle
) {
478 CreateGpuMemoryBufferCallbackMap::iterator iter
=
479 create_gpu_memory_buffer_requests_
.find(request_id
);
480 DCHECK(iter
!= create_gpu_memory_buffer_requests_
.end());
481 iter
->second
.Run(handle
);
482 create_gpu_memory_buffer_requests_
.erase(iter
);
485 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO(
486 const gfx::GpuMemoryBufferHandle
& handle
,
488 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
492 host
->DestroyGpuMemoryBuffer(handle
, sync_point
);
495 } // namespace content