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/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"
23 BrowserGpuChannelHostFactory
* BrowserGpuChannelHostFactory::instance_
= NULL
;
25 BrowserGpuChannelHostFactory::CreateRequest::CreateRequest()
28 route_id(MSG_ROUTING_NONE
) {
31 BrowserGpuChannelHostFactory::CreateRequest::~CreateRequest() {
34 BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
35 CauseForGpuLaunch cause
,
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),
44 main_loop_(base::MessageLoopProxy::current()) {
45 scoped_refptr
<base::MessageLoopProxy
> loop
=
46 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
);
49 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO
,
53 BrowserGpuChannelHostFactory::EstablishRequest::~EstablishRequest() {
56 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
57 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
59 host
= GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED
,
60 cause_for_gpu_launch_
);
62 LOG(ERROR
) << "Failed to launch GPU process.";
66 gpu_host_id_
= host
->host_id();
67 reused_gpu_process_
= false;
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.";
78 reused_gpu_process_
= true;
81 host
->EstablishGpuChannel(
85 &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO
,
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.";
99 channel_handle_
= channel_handle
;
100 gpu_info_
= gpu_info
;
105 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
107 main_loop_
->PostTask(
109 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain
,
113 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
115 BrowserGpuChannelHostFactory
* factory
=
116 BrowserGpuChannelHostFactory::instance();
117 factory
->GpuChannelEstablished();
122 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
123 DCHECK(main_loop_
->BelongsToCurrentThread());
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
;
137 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
138 DCHECK(main_loop_
->BelongsToCurrentThread());
142 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
143 return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL
);
146 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel
) {
148 instance_
= new BrowserGpuChannelHostFactory(establish_gpu_channel
);
151 void BrowserGpuChannelHostFactory::Terminate() {
157 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory(
158 bool establish_gpu_channel
)
159 : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
160 shutdown_event_(new base::WaitableEvent(true, false)),
162 if (establish_gpu_channel
) {
163 pending_request_
= new EstablishRequest(
164 CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP
, gpu_client_id_
, gpu_host_id_
);
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();
177 bool BrowserGpuChannelHostFactory::IsMainThread() {
178 return BrowserThread::CurrentlyOn(BrowserThread::UI
);
181 base::MessageLoop
* BrowserGpuChannelHostFactory::GetMainLoop() {
182 return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI
);
185 scoped_refptr
<base::MessageLoopProxy
>
186 BrowserGpuChannelHostFactory::GetIOLoopProxy() {
187 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
);
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
>();
198 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
199 CreateRequest
* request
,
201 const GPUCreateCommandBufferConfig
& init_params
) {
202 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
204 request
->event
.Signal();
208 gfx::GLSurfaceHandle surface
=
209 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id
);
211 host
->CreateViewCommandBuffer(
216 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO
,
221 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
222 CreateRequest
* request
, int32 route_id
) {
223 request
->route_id
= route_id
;
224 request
->event
.Signal();
227 int32
BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
229 const GPUCreateCommandBufferConfig
& init_params
) {
230 CreateRequest request
;
231 GetIOLoopProxy()->PostTask(FROM_HERE
, base::Bind(
232 &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO
,
233 base::Unretained(this),
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
;
248 void BrowserGpuChannelHostFactory::CreateImageOnIO(
249 gfx::PluginWindowHandle window
,
251 const CreateImageCallback
& callback
) {
252 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
254 ImageCreatedOnIO(callback
, gfx::Size());
262 base::Bind(&BrowserGpuChannelHostFactory::ImageCreatedOnIO
, callback
));
266 void BrowserGpuChannelHostFactory::ImageCreatedOnIO(
267 const CreateImageCallback
& callback
, const gfx::Size size
) {
268 BrowserThread::PostTask(
271 base::Bind(&BrowserGpuChannelHostFactory::OnImageCreated
,
276 void BrowserGpuChannelHostFactory::OnImageCreated(
277 const CreateImageCallback
& callback
, const gfx::Size size
) {
281 void BrowserGpuChannelHostFactory::CreateImage(
282 gfx::PluginWindowHandle window
,
284 const CreateImageCallback
& callback
) {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
286 GetIOLoopProxy()->PostTask(FROM_HERE
, base::Bind(
287 &BrowserGpuChannelHostFactory::CreateImageOnIO
,
288 base::Unretained(this),
294 void BrowserGpuChannelHostFactory::DeleteImageOnIO(
295 int32 image_id
, int32 sync_point
) {
296 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
301 host
->DeleteImage(gpu_client_id_
, image_id
, sync_point
);
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),
314 GpuChannelHost
* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
315 CauseForGpuLaunch cause_for_gpu_launch
) {
316 EstablishGpuChannel(cause_for_gpu_launch
, base::Closure());
318 if (pending_request_
)
319 pending_request_
->Wait();
321 return gpu_channel_
.get();
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.
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_
);
339 if (!callback
.is_null()) {
343 established_callbacks_
.push_back(callback
);
347 GpuChannelHost
* BrowserGpuChannelHostFactory::GetGpuChannel() {
348 if (gpu_channel_
&& !gpu_channel_
->IsLost())
354 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
355 DCHECK(IsMainThread());
356 DCHECK(pending_request_
);
357 if (pending_request_
->channel_handle().name
.empty())
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
;
368 for (size_t n
= 0; n
< established_callbacks_
.size(); n
++)
369 established_callbacks_
[n
].Run();
371 established_callbacks_
.clear();
374 scoped_ptr
<gfx::GpuMemoryBuffer
>
375 BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(
378 unsigned internalformat
) {
379 if (!GpuMemoryBufferImpl::IsFormatValid(internalformat
))
380 return scoped_ptr
<gfx::GpuMemoryBuffer
>();
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
>();
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
>();
393 return buffer
.PassAs
<gfx::GpuMemoryBuffer
>();
397 void BrowserGpuChannelHostFactory::AddFilterOnIO(
399 scoped_refptr
<IPC::ChannelProxy::MessageFilter
> filter
) {
400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
402 GpuProcessHost
* host
= GpuProcessHost::FromID(host_id
);
404 host
->AddFilter(filter
.get());
407 void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
408 const uint32
* message_ids
,
410 const base::Callback
<void(const IPC::Message
&)>& handler
,
411 base::TaskRunner
* target_task_runner
) {
414 << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
415 << " until the GpuProcessHost has been set up.";
417 scoped_refptr
<IPC::ForwardingMessageFilter
> filter
=
418 new IPC::ForwardingMessageFilter(message_ids
,
421 filter
->AddRoute(MSG_ROUTING_CONTROL
, handler
);
423 GetIOLoopProxy()->PostTask(
425 base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO
,
430 } // namespace content