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/command_line.h"
9 #include "base/location.h"
10 #include "base/profiler/scoped_tracker.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "base/trace_event/memory_dump_manager.h"
16 #include "base/trace_event/trace_event.h"
17 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
18 #include "content/browser/gpu/gpu_data_manager_impl.h"
19 #include "content/browser/gpu/gpu_process_host.h"
20 #include "content/browser/gpu/gpu_surface_tracker.h"
21 #include "content/browser/gpu/shader_disk_cache.h"
22 #include "content/common/child_process_host_impl.h"
23 #include "content/common/gpu/gpu_messages.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/content_browser_client.h"
26 #include "content/public/browser/gpu_data_manager.h"
27 #include "content/public/common/content_client.h"
28 #include "gpu/command_buffer/service/gpu_switches.h"
29 #include "ipc/ipc_channel_handle.h"
30 #include "ipc/message_filter.h"
34 BrowserGpuChannelHostFactory
* BrowserGpuChannelHostFactory::instance_
= NULL
;
36 struct BrowserGpuChannelHostFactory::CreateRequest
{
37 CreateRequest(int32 route_id
)
41 result(CREATE_COMMAND_BUFFER_FAILED
) {}
43 base::WaitableEvent event
;
46 CreateCommandBufferResult result
;
49 class BrowserGpuChannelHostFactory::EstablishRequest
50 : public base::RefCountedThreadSafe
<EstablishRequest
> {
52 static scoped_refptr
<EstablishRequest
> Create(CauseForGpuLaunch cause
,
54 uint64_t gpu_client_tracing_id
,
59 int gpu_host_id() { return gpu_host_id_
; }
60 IPC::ChannelHandle
& channel_handle() { return channel_handle_
; }
61 gpu::GPUInfo
gpu_info() { return gpu_info_
; }
64 friend class base::RefCountedThreadSafe
<EstablishRequest
>;
65 explicit EstablishRequest(CauseForGpuLaunch cause
,
67 uint64_t gpu_client_tracing_id
,
69 ~EstablishRequest() {}
71 void OnEstablishedOnIO(const IPC::ChannelHandle
& channel_handle
,
72 const gpu::GPUInfo
& gpu_info
);
76 base::WaitableEvent event_
;
77 CauseForGpuLaunch cause_for_gpu_launch_
;
78 const int gpu_client_id_
;
79 const uint64_t gpu_client_tracing_id_
;
81 bool reused_gpu_process_
;
82 IPC::ChannelHandle channel_handle_
;
83 gpu::GPUInfo gpu_info_
;
85 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner_
;
88 scoped_refptr
<BrowserGpuChannelHostFactory::EstablishRequest
>
89 BrowserGpuChannelHostFactory::EstablishRequest::Create(
90 CauseForGpuLaunch cause
,
92 uint64_t gpu_client_tracing_id
,
94 scoped_refptr
<EstablishRequest
> establish_request
= new EstablishRequest(
95 cause
, gpu_client_id
, gpu_client_tracing_id
, gpu_host_id
);
96 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
=
97 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
);
98 // PostTask outside the constructor to ensure at least one reference exists.
99 task_runner
->PostTask(
101 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO
,
103 return establish_request
;
106 BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
107 CauseForGpuLaunch cause
,
109 uint64_t gpu_client_tracing_id
,
111 : event_(false, false),
112 cause_for_gpu_launch_(cause
),
113 gpu_client_id_(gpu_client_id
),
114 gpu_client_tracing_id_(gpu_client_tracing_id
),
115 gpu_host_id_(gpu_host_id
),
116 reused_gpu_process_(false),
118 main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
121 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
122 // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
123 tracked_objects::ScopedTracker
tracking_profile(
124 FROM_HERE_WITH_EXPLICIT_FUNCTION(
126 "BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO"));
127 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
129 host
= GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED
,
130 cause_for_gpu_launch_
);
132 LOG(ERROR
) << "Failed to launch GPU process.";
136 gpu_host_id_
= host
->host_id();
137 reused_gpu_process_
= false;
139 if (reused_gpu_process_
) {
140 // We come here if we retried to establish the channel because of a
141 // failure in ChannelEstablishedOnIO, but we ended up with the same
142 // process ID, meaning the failure was not because of a channel error,
143 // but another reason. So fail now.
144 LOG(ERROR
) << "Failed to create channel.";
148 reused_gpu_process_
= true;
151 host
->EstablishGpuChannel(
152 gpu_client_id_
, gpu_client_tracing_id_
, true, true, true,
154 &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO
,
158 void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
159 const IPC::ChannelHandle
& channel_handle
,
160 const gpu::GPUInfo
& gpu_info
) {
161 if (channel_handle
.name
.empty() && reused_gpu_process_
) {
162 // We failed after re-using the GPU process, but it may have died in the
163 // mean time. Retry to have a chance to create a fresh GPU process.
164 DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "
165 "restart GPU process.";
168 channel_handle_
= channel_handle
;
169 gpu_info_
= gpu_info
;
174 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
176 main_task_runner_
->PostTask(
178 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain
,
182 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
184 BrowserGpuChannelHostFactory
* factory
=
185 BrowserGpuChannelHostFactory::instance();
186 factory
->GpuChannelEstablished();
191 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
192 DCHECK(main_task_runner_
->BelongsToCurrentThread());
194 // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is fixed.
195 tracked_objects::ScopedTracker
tracking_profile(
196 FROM_HERE_WITH_EXPLICIT_FUNCTION(
197 "125248 BrowserGpuChannelHostFactory::EstablishRequest::Wait"));
199 // We're blocking the UI thread, which is generally undesirable.
200 // In this case we need to wait for this before we can show any UI
201 // /anyway/, so it won't cause additional jank.
202 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
203 TRACE_EVENT0("browser",
204 "BrowserGpuChannelHostFactory::EstablishGpuChannelSync");
205 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
211 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
212 DCHECK(main_task_runner_
->BelongsToCurrentThread());
216 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
217 return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL
);
220 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel
) {
222 instance_
= new BrowserGpuChannelHostFactory();
223 if (establish_gpu_channel
) {
224 instance_
->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP
,
229 void BrowserGpuChannelHostFactory::Terminate() {
235 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
236 : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
237 gpu_client_tracing_id_(ChildProcessHost::kBrowserTracingProcessId
),
238 shutdown_event_(new base::WaitableEvent(true, false)),
239 gpu_memory_buffer_manager_(
240 new BrowserGpuMemoryBufferManager(gpu_client_id_
,
241 gpu_client_tracing_id_
)),
243 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
244 switches::kDisableGpuShaderDiskCache
)) {
245 DCHECK(GetContentClient());
246 base::FilePath cache_dir
=
247 GetContentClient()->browser()->GetShaderDiskCacheDirectory();
248 if (!cache_dir
.empty()) {
249 GetIOThreadTaskRunner()->PostTask(
252 &BrowserGpuChannelHostFactory::InitializeShaderDiskCacheOnIO
,
253 gpu_client_id_
, cache_dir
));
258 BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
259 DCHECK(IsMainThread());
260 if (pending_request_
.get())
261 pending_request_
->Cancel();
262 for (size_t n
= 0; n
< established_callbacks_
.size(); n
++)
263 established_callbacks_
[n
].Run();
264 shutdown_event_
->Signal();
266 gpu_channel_
->DestroyChannel();
271 bool BrowserGpuChannelHostFactory::IsMainThread() {
272 return BrowserThread::CurrentlyOn(BrowserThread::UI
);
275 scoped_refptr
<base::SingleThreadTaskRunner
>
276 BrowserGpuChannelHostFactory::GetIOThreadTaskRunner() {
277 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
);
280 scoped_ptr
<base::SharedMemory
>
281 BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size
) {
282 scoped_ptr
<base::SharedMemory
> shm(new base::SharedMemory());
283 if (!shm
->CreateAnonymous(size
))
284 return scoped_ptr
<base::SharedMemory
>();
288 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
289 CreateRequest
* request
,
291 const GPUCreateCommandBufferConfig
& init_params
) {
292 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
294 request
->event
.Signal();
298 gfx::GLSurfaceHandle surface
=
299 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id
);
301 host
->CreateViewCommandBuffer(
307 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO
,
312 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
313 CreateRequest
* request
, CreateCommandBufferResult result
) {
314 request
->result
= result
;
315 request
->event
.Signal();
318 CreateCommandBufferResult
BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
320 const GPUCreateCommandBufferConfig
& init_params
,
322 CreateRequest
request(route_id
);
323 GetIOThreadTaskRunner()->PostTask(
325 base::Bind(&BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO
,
326 base::Unretained(this), &request
, surface_id
, init_params
));
327 // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is fixed.
328 tracked_objects::ScopedTracker
tracking_profile(
329 FROM_HERE_WITH_EXPLICIT_FUNCTION(
330 "125248 BrowserGpuChannelHostFactory::CreateViewCommandBuffer"));
332 // We're blocking the UI thread, which is generally undesirable.
333 // In this case we need to wait for this before we can show any UI /anyway/,
334 // so it won't cause additional jank.
335 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
336 TRACE_EVENT0("browser",
337 "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
338 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
339 request
.event
.Wait();
340 return request
.result
;
343 // Blocking the UI thread to open a GPU channel is not supported on Android.
344 // (Opening the initial channel to a child process involves handling a reply
345 // task on the UI thread first, so we cannot block here.)
346 #if !defined(OS_ANDROID)
347 GpuChannelHost
* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
348 CauseForGpuLaunch cause_for_gpu_launch
) {
349 EstablishGpuChannel(cause_for_gpu_launch
, base::Closure());
351 if (pending_request_
.get())
352 pending_request_
->Wait();
354 return gpu_channel_
.get();
358 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
359 CauseForGpuLaunch cause_for_gpu_launch
,
360 const base::Closure
& callback
) {
361 if (gpu_channel_
.get() && gpu_channel_
->IsLost()) {
362 DCHECK(!pending_request_
.get());
363 // Recreate the channel if it has been lost.
364 gpu_channel_
->DestroyChannel();
368 if (!gpu_channel_
.get() && !pending_request_
.get()) {
369 // We should only get here if the context was lost.
370 pending_request_
= EstablishRequest::Create(
371 cause_for_gpu_launch
, gpu_client_id_
,
372 gpu_client_tracing_id_
,
376 if (!callback
.is_null()) {
377 if (gpu_channel_
.get())
380 established_callbacks_
.push_back(callback
);
384 GpuChannelHost
* BrowserGpuChannelHostFactory::GetGpuChannel() {
385 if (gpu_channel_
.get() && !gpu_channel_
->IsLost())
386 return gpu_channel_
.get();
391 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
392 DCHECK(IsMainThread());
393 DCHECK(pending_request_
.get());
394 if (pending_request_
->channel_handle().name
.empty()) {
395 DCHECK(!gpu_channel_
.get());
397 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/466866
399 tracked_objects::ScopedTracker
tracking_profile1(
400 FROM_HERE_WITH_EXPLICIT_FUNCTION(
401 "466866 BrowserGpuChannelHostFactory::GpuChannelEstablished1"));
402 GetContentClient()->SetGpuInfo(pending_request_
->gpu_info());
403 gpu_channel_
= GpuChannelHost::Create(
404 this, gpu_client_id_
, pending_request_
->gpu_info(),
405 pending_request_
->channel_handle(), shutdown_event_
.get(),
406 gpu_memory_buffer_manager_
.get());
408 gpu_host_id_
= pending_request_
->gpu_host_id();
409 pending_request_
= NULL
;
411 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/466866 is
413 tracked_objects::ScopedTracker
tracking_profile2(
414 FROM_HERE_WITH_EXPLICIT_FUNCTION(
415 "466866 BrowserGpuChannelHostFactory::GpuChannelEstablished2"));
417 for (size_t n
= 0; n
< established_callbacks_
.size(); n
++)
418 established_callbacks_
[n
].Run();
420 established_callbacks_
.clear();
424 void BrowserGpuChannelHostFactory::AddFilterOnIO(
426 scoped_refptr
<IPC::MessageFilter
> filter
) {
427 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
429 GpuProcessHost
* host
= GpuProcessHost::FromID(host_id
);
431 host
->AddFilter(filter
.get());
435 void BrowserGpuChannelHostFactory::InitializeShaderDiskCacheOnIO(
437 const base::FilePath
& cache_dir
) {
438 ShaderCacheFactory::GetInstance()->SetCacheInfo(gpu_client_id
, cache_dir
);
441 } // namespace content