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(
133 &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO,
137 void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
138 const IPC::ChannelHandle& channel_handle,
139 const gpu::GPUInfo& gpu_info) {
140 if (channel_handle.name.empty() && reused_gpu_process_) {
141 // We failed after re-using the GPU process, but it may have died in the
142 // mean time. Retry to have a chance to create a fresh GPU process.
143 DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "
144 "restart GPU process.";
147 channel_handle_ = channel_handle;
148 gpu_info_ = gpu_info;
153 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
155 main_loop_->PostTask(
157 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain,
161 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
163 BrowserGpuChannelHostFactory* factory =
164 BrowserGpuChannelHostFactory::instance();
165 factory->GpuChannelEstablished();
170 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
171 DCHECK(main_loop_->BelongsToCurrentThread());
173 // We're blocking the UI thread, which is generally undesirable.
174 // In this case we need to wait for this before we can show any UI
175 // /anyway/, so it won't cause additional jank.
176 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
177 TRACE_EVENT0("browser",
178 "BrowserGpuChannelHostFactory::EstablishGpuChannelSync");
179 base::ThreadRestrictions::ScopedAllowWait allow_wait;
185 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
186 DCHECK(main_loop_->BelongsToCurrentThread());
190 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
191 return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL);
194 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {
196 instance_ = new BrowserGpuChannelHostFactory();
197 if (establish_gpu_channel) {
198 instance_->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP,
203 void BrowserGpuChannelHostFactory::Terminate() {
209 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
210 : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
211 shutdown_event_(new base::WaitableEvent(true, false)),
213 next_create_gpu_memory_buffer_request_id_(0) {
216 BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
217 DCHECK(IsMainThread());
218 if (pending_request_)
219 pending_request_->Cancel();
220 for (size_t n = 0; n < established_callbacks_.size(); n++)
221 established_callbacks_[n].Run();
222 shutdown_event_->Signal();
225 bool BrowserGpuChannelHostFactory::IsMainThread() {
226 return BrowserThread::CurrentlyOn(BrowserThread::UI);
229 base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() {
230 return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI);
233 scoped_refptr<base::MessageLoopProxy>
234 BrowserGpuChannelHostFactory::GetIOLoopProxy() {
235 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
238 scoped_ptr<base::SharedMemory>
239 BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) {
240 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
241 if (!shm->CreateAnonymous(size))
242 return scoped_ptr<base::SharedMemory>();
246 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
247 CreateRequest* request,
249 const GPUCreateCommandBufferConfig& init_params) {
250 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
252 request->event.Signal();
256 gfx::GLSurfaceHandle surface =
257 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
259 host->CreateViewCommandBuffer(
265 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
270 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
271 CreateRequest* request, CreateCommandBufferResult result) {
272 request->result = result;
273 request->event.Signal();
276 CreateCommandBufferResult BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
278 const GPUCreateCommandBufferConfig& init_params,
280 CreateRequest request;
281 request.route_id = route_id;
282 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
283 &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
284 base::Unretained(this),
288 // We're blocking the UI thread, which is generally undesirable.
289 // In this case we need to wait for this before we can show any UI /anyway/,
290 // so it won't cause additional jank.
291 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
292 TRACE_EVENT0("browser",
293 "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
294 base::ThreadRestrictions::ScopedAllowWait allow_wait;
295 request.event.Wait();
296 return request.result;
299 void BrowserGpuChannelHostFactory::CreateImageOnIO(
300 gfx::PluginWindowHandle window,
302 const CreateImageCallback& callback) {
303 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
305 ImageCreatedOnIO(callback, gfx::Size());
313 base::Bind(&BrowserGpuChannelHostFactory::ImageCreatedOnIO, callback));
317 void BrowserGpuChannelHostFactory::ImageCreatedOnIO(
318 const CreateImageCallback& callback, const gfx::Size size) {
319 BrowserThread::PostTask(
322 base::Bind(&BrowserGpuChannelHostFactory::OnImageCreated,
327 void BrowserGpuChannelHostFactory::OnImageCreated(
328 const CreateImageCallback& callback, const gfx::Size size) {
332 void BrowserGpuChannelHostFactory::CreateImage(
333 gfx::PluginWindowHandle window,
335 const CreateImageCallback& callback) {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
338 &BrowserGpuChannelHostFactory::CreateImageOnIO,
339 base::Unretained(this),
345 void BrowserGpuChannelHostFactory::DeleteImageOnIO(
346 int32 image_id, int32 sync_point) {
347 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
352 host->DeleteImage(gpu_client_id_, image_id, sync_point);
355 void BrowserGpuChannelHostFactory::DeleteImage(
356 int32 image_id, int32 sync_point) {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
359 &BrowserGpuChannelHostFactory::DeleteImageOnIO,
360 base::Unretained(this),
365 GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
366 CauseForGpuLaunch cause_for_gpu_launch) {
367 EstablishGpuChannel(cause_for_gpu_launch, base::Closure());
369 if (pending_request_)
370 pending_request_->Wait();
372 return gpu_channel_.get();
375 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
376 CauseForGpuLaunch cause_for_gpu_launch,
377 const base::Closure& callback) {
378 if (gpu_channel_.get() && gpu_channel_->IsLost()) {
379 DCHECK(!pending_request_);
380 // Recreate the channel if it has been lost.
384 if (!gpu_channel_ && !pending_request_) {
385 // We should only get here if the context was lost.
386 pending_request_ = EstablishRequest::Create(
387 cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);
390 if (!callback.is_null()) {
394 established_callbacks_.push_back(callback);
398 GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() {
399 if (gpu_channel_ && !gpu_channel_->IsLost())
405 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
406 DCHECK(IsMainThread());
407 DCHECK(pending_request_);
408 if (pending_request_->channel_handle().name.empty()) {
409 DCHECK(!gpu_channel_);
411 GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
412 gpu_channel_ = GpuChannelHost::Create(this,
413 pending_request_->gpu_info(),
414 pending_request_->channel_handle(),
415 shutdown_event_.get());
417 gpu_host_id_ = pending_request_->gpu_host_id();
418 pending_request_ = NULL;
420 for (size_t n = 0; n < established_callbacks_.size(); n++)
421 established_callbacks_[n].Run();
423 established_callbacks_.clear();
426 scoped_ptr<gfx::GpuMemoryBuffer>
427 BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(size_t width,
429 unsigned internalformat,
431 if (!GpuMemoryBufferImpl::IsFormatValid(internalformat) ||
432 !GpuMemoryBufferImpl::IsUsageValid(usage))
433 return scoped_ptr<gfx::GpuMemoryBuffer>();
435 return GpuMemoryBufferImpl::Create(gfx::Size(width, height),
437 usage).PassAs<gfx::GpuMemoryBuffer>();
441 void BrowserGpuChannelHostFactory::AddFilterOnIO(
443 scoped_refptr<IPC::MessageFilter> filter) {
444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
446 GpuProcessHost* host = GpuProcessHost::FromID(host_id);
448 host->AddFilter(filter.get());
451 void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
452 const uint32* message_ids,
454 const base::Callback<void(const IPC::Message&)>& handler,
455 base::TaskRunner* target_task_runner) {
458 << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
459 << " until the GpuProcessHost has been set up.";
461 scoped_refptr<IPC::ForwardingMessageFilter> filter =
462 new IPC::ForwardingMessageFilter(message_ids,
465 filter->AddRoute(MSG_ROUTING_CONTROL, handler);
467 GetIOLoopProxy()->PostTask(
469 base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO,
474 void BrowserGpuChannelHostFactory::CreateGpuMemoryBuffer(
475 const gfx::GpuMemoryBufferHandle& handle,
476 const gfx::Size& size,
477 unsigned internalformat,
479 const CreateGpuMemoryBufferCallback& callback) {
480 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
481 uint32 request_id = next_create_gpu_memory_buffer_request_id_++;
482 create_gpu_memory_buffer_requests_[request_id] = callback;
483 GetIOLoopProxy()->PostTask(
485 base::Bind(&BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO,
486 base::Unretained(this),
494 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBuffer(
495 const gfx::GpuMemoryBufferHandle& handle,
497 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
498 GetIOLoopProxy()->PostTask(
500 base::Bind(&BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO,
501 base::Unretained(this),
506 void BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO(
507 const gfx::GpuMemoryBufferHandle& handle,
508 const gfx::Size& size,
509 unsigned internalformat,
512 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
514 GpuMemoryBufferCreatedOnIO(request_id, gfx::GpuMemoryBufferHandle());
518 host->CreateGpuMemoryBuffer(
523 base::Bind(&BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO,
524 base::Unretained(this),
528 void BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO(
530 const gfx::GpuMemoryBufferHandle& handle) {
531 BrowserThread::PostTask(
534 base::Bind(&BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated,
535 base::Unretained(this),
540 void BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated(
542 const gfx::GpuMemoryBufferHandle& handle) {
543 CreateGpuMemoryBufferCallbackMap::iterator iter =
544 create_gpu_memory_buffer_requests_.find(request_id);
545 DCHECK(iter != create_gpu_memory_buffer_requests_.end());
546 iter->second.Run(handle);
547 create_gpu_memory_buffer_requests_.erase(iter);
550 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO(
551 const gfx::GpuMemoryBufferHandle& handle,
553 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
557 host->DestroyGpuMemoryBuffer(handle, sync_point);
560 } // namespace content