Added SwapInterval to the GPU command buffer
[chromium-blink-merge.git] / content / browser / gpu / browser_gpu_channel_host_factory.cc
blob851e9e5e3c7ca583562f06f1ffdadc40d9caa9bc
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"
7 #include <set>
9 #include "base/bind.h"
10 #include "base/debug/trace_event.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "base/tracked_objects.h"
14 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
15 #include "content/browser/gpu/gpu_data_manager_impl.h"
16 #include "content/browser/gpu/gpu_process_host.h"
17 #include "content/browser/gpu/gpu_surface_tracker.h"
18 #include "content/common/child_process_host_impl.h"
19 #include "content/common/gpu/gpu_memory_buffer_factory.h"
20 #include "content/common/gpu/gpu_messages.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/gpu_data_manager.h"
23 #include "content/public/common/content_client.h"
24 #include "ipc/ipc_channel_handle.h"
25 #include "ipc/ipc_forwarding_message_filter.h"
26 #include "ipc/message_filter.h"
28 #if defined(OS_MACOSX)
29 #include "content/common/gpu/gpu_memory_buffer_factory_io_surface.h"
30 #endif
32 #if defined(OS_ANDROID)
33 #include "content/common/gpu/gpu_memory_buffer_factory_surface_texture.h"
34 #endif
36 #if defined(USE_OZONE)
37 #include "content/common/gpu/gpu_memory_buffer_factory_ozone_native_buffer.h"
38 #endif
40 namespace content {
41 namespace {
43 base::LazyInstance<std::set<gfx::GpuMemoryBuffer::Usage>>
44 g_enabled_gpu_memory_buffer_usages;
47 BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL;
49 struct BrowserGpuChannelHostFactory::CreateRequest {
50 CreateRequest(int32 route_id)
51 : event(true, false),
52 gpu_host_id(0),
53 route_id(route_id),
54 result(CREATE_COMMAND_BUFFER_FAILED) {}
55 ~CreateRequest() {}
56 base::WaitableEvent event;
57 int gpu_host_id;
58 int32 route_id;
59 CreateCommandBufferResult result;
62 class BrowserGpuChannelHostFactory::EstablishRequest
63 : public base::RefCountedThreadSafe<EstablishRequest> {
64 public:
65 static scoped_refptr<EstablishRequest> Create(CauseForGpuLaunch cause,
66 int gpu_client_id,
67 int gpu_host_id);
68 void Wait();
69 void Cancel();
71 int gpu_host_id() { return gpu_host_id_; }
72 IPC::ChannelHandle& channel_handle() { return channel_handle_; }
73 gpu::GPUInfo gpu_info() { return gpu_info_; }
75 private:
76 friend class base::RefCountedThreadSafe<EstablishRequest>;
77 explicit EstablishRequest(CauseForGpuLaunch cause,
78 int gpu_client_id,
79 int gpu_host_id);
80 ~EstablishRequest() {}
81 void EstablishOnIO();
82 void OnEstablishedOnIO(const IPC::ChannelHandle& channel_handle,
83 const gpu::GPUInfo& gpu_info);
84 void FinishOnIO();
85 void FinishOnMain();
87 base::WaitableEvent event_;
88 CauseForGpuLaunch cause_for_gpu_launch_;
89 const int gpu_client_id_;
90 int gpu_host_id_;
91 bool reused_gpu_process_;
92 IPC::ChannelHandle channel_handle_;
93 gpu::GPUInfo gpu_info_;
94 bool finished_;
95 scoped_refptr<base::MessageLoopProxy> main_loop_;
98 scoped_refptr<BrowserGpuChannelHostFactory::EstablishRequest>
99 BrowserGpuChannelHostFactory::EstablishRequest::Create(CauseForGpuLaunch cause,
100 int gpu_client_id,
101 int gpu_host_id) {
102 scoped_refptr<EstablishRequest> establish_request =
103 new EstablishRequest(cause, gpu_client_id, gpu_host_id);
104 scoped_refptr<base::MessageLoopProxy> loop =
105 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
106 // PostTask outside the constructor to ensure at least one reference exists.
107 loop->PostTask(
108 FROM_HERE,
109 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO,
110 establish_request));
111 return establish_request;
114 BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
115 CauseForGpuLaunch cause,
116 int gpu_client_id,
117 int gpu_host_id)
118 : event_(false, false),
119 cause_for_gpu_launch_(cause),
120 gpu_client_id_(gpu_client_id),
121 gpu_host_id_(gpu_host_id),
122 reused_gpu_process_(false),
123 finished_(false),
124 main_loop_(base::MessageLoopProxy::current()) {
127 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
128 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
129 if (!host) {
130 host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
131 cause_for_gpu_launch_);
132 if (!host) {
133 LOG(ERROR) << "Failed to launch GPU process.";
134 FinishOnIO();
135 return;
137 gpu_host_id_ = host->host_id();
138 reused_gpu_process_ = false;
139 } else {
140 if (reused_gpu_process_) {
141 // We come here if we retried to establish the channel because of a
142 // failure in ChannelEstablishedOnIO, but we ended up with the same
143 // process ID, meaning the failure was not because of a channel error,
144 // but another reason. So fail now.
145 LOG(ERROR) << "Failed to create channel.";
146 FinishOnIO();
147 return;
149 reused_gpu_process_ = true;
152 host->EstablishGpuChannel(
153 gpu_client_id_,
154 true,
155 true,
156 base::Bind(
157 &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO,
158 this));
161 void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
162 const IPC::ChannelHandle& channel_handle,
163 const gpu::GPUInfo& gpu_info) {
164 if (channel_handle.name.empty() && reused_gpu_process_) {
165 // We failed after re-using the GPU process, but it may have died in the
166 // mean time. Retry to have a chance to create a fresh GPU process.
167 DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "
168 "restart GPU process.";
169 EstablishOnIO();
170 } else {
171 channel_handle_ = channel_handle;
172 gpu_info_ = gpu_info;
173 FinishOnIO();
177 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
178 event_.Signal();
179 main_loop_->PostTask(
180 FROM_HERE,
181 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain,
182 this));
185 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
186 if (!finished_) {
187 BrowserGpuChannelHostFactory* factory =
188 BrowserGpuChannelHostFactory::instance();
189 factory->GpuChannelEstablished();
190 finished_ = true;
194 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
195 DCHECK(main_loop_->BelongsToCurrentThread());
197 // Since the current task synchronously waits for establishing a GPU
198 // channel, it shouldn't be tallied because its execution time has nothing
199 // to do with its efficiency. Using task stopwatch to exclude the waiting
200 // time from the current task run time.
201 tracked_objects::TaskStopwatch stopwatch;
202 stopwatch.Start();
204 // We're blocking the UI thread, which is generally undesirable.
205 // In this case we need to wait for this before we can show any UI
206 // /anyway/, so it won't cause additional jank.
207 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
208 TRACE_EVENT0("browser",
209 "BrowserGpuChannelHostFactory::EstablishGpuChannelSync");
210 base::ThreadRestrictions::ScopedAllowWait allow_wait;
211 event_.Wait();
213 stopwatch.Stop();
215 FinishOnMain();
218 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
219 DCHECK(main_loop_->BelongsToCurrentThread());
220 finished_ = true;
223 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
224 return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL);
227 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {
228 DCHECK(!instance_);
229 instance_ = new BrowserGpuChannelHostFactory();
230 if (establish_gpu_channel) {
231 instance_->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP,
232 base::Closure());
236 void BrowserGpuChannelHostFactory::Terminate() {
237 DCHECK(instance_);
238 delete instance_;
239 instance_ = NULL;
242 // static
243 void BrowserGpuChannelHostFactory::EnableGpuMemoryBufferFactoryUsage(
244 gfx::GpuMemoryBuffer::Usage usage) {
245 g_enabled_gpu_memory_buffer_usages.Get().insert(usage);
248 // static
249 bool BrowserGpuChannelHostFactory::IsGpuMemoryBufferFactoryUsageEnabled(
250 gfx::GpuMemoryBuffer::Usage usage) {
251 return g_enabled_gpu_memory_buffer_usages.Get().count(usage) != 0;
254 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
255 : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
256 shutdown_event_(new base::WaitableEvent(true, false)),
257 gpu_memory_buffer_manager_(
258 new BrowserGpuMemoryBufferManager(this, gpu_client_id_)),
259 gpu_host_id_(0),
260 next_create_gpu_memory_buffer_request_id_(0) {
263 BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
264 DCHECK(IsMainThread());
265 if (pending_request_.get())
266 pending_request_->Cancel();
267 for (size_t n = 0; n < established_callbacks_.size(); n++)
268 established_callbacks_[n].Run();
269 shutdown_event_->Signal();
272 bool BrowserGpuChannelHostFactory::IsMainThread() {
273 return BrowserThread::CurrentlyOn(BrowserThread::UI);
276 base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() {
277 return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI);
280 scoped_refptr<base::MessageLoopProxy>
281 BrowserGpuChannelHostFactory::GetIOLoopProxy() {
282 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
285 scoped_ptr<base::SharedMemory>
286 BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) {
287 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
288 if (!shm->CreateAnonymous(size))
289 return scoped_ptr<base::SharedMemory>();
290 return shm.Pass();
293 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
294 CreateRequest* request,
295 int32 surface_id,
296 const GPUCreateCommandBufferConfig& init_params) {
297 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
298 if (!host) {
299 request->event.Signal();
300 return;
303 gfx::GLSurfaceHandle surface =
304 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
306 host->CreateViewCommandBuffer(
307 surface,
308 surface_id,
309 gpu_client_id_,
310 init_params,
311 request->route_id,
312 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
313 request));
316 // static
317 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
318 CreateRequest* request, CreateCommandBufferResult result) {
319 request->result = result;
320 request->event.Signal();
323 CreateCommandBufferResult BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
324 int32 surface_id,
325 const GPUCreateCommandBufferConfig& init_params,
326 int32 route_id) {
327 CreateRequest request(route_id);
328 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
329 &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
330 base::Unretained(this),
331 &request,
332 surface_id,
333 init_params));
334 // We're blocking the UI thread, which is generally undesirable.
335 // In this case we need to wait for this before we can show any UI /anyway/,
336 // so it won't cause additional jank.
337 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
338 TRACE_EVENT0("browser",
339 "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
340 base::ThreadRestrictions::ScopedAllowWait allow_wait;
341 request.event.Wait();
342 return request.result;
345 // Blocking the UI thread to open a GPU channel is not supported on Android.
346 // (Opening the initial channel to a child process involves handling a reply
347 // task on the UI thread first, so we cannot block here.)
348 #if !defined(OS_ANDROID)
349 GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
350 CauseForGpuLaunch cause_for_gpu_launch) {
351 EstablishGpuChannel(cause_for_gpu_launch, base::Closure());
353 if (pending_request_.get())
354 pending_request_->Wait();
356 return gpu_channel_.get();
358 #endif
360 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
361 CauseForGpuLaunch cause_for_gpu_launch,
362 const base::Closure& callback) {
363 if (gpu_channel_.get() && gpu_channel_->IsLost()) {
364 DCHECK(!pending_request_.get());
365 // Recreate the channel if it has been lost.
366 gpu_channel_ = NULL;
369 if (!gpu_channel_.get() && !pending_request_.get()) {
370 // We should only get here if the context was lost.
371 pending_request_ = EstablishRequest::Create(
372 cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);
375 if (!callback.is_null()) {
376 if (gpu_channel_.get())
377 callback.Run();
378 else
379 established_callbacks_.push_back(callback);
383 GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() {
384 if (gpu_channel_.get() && !gpu_channel_->IsLost())
385 return gpu_channel_.get();
387 return NULL;
390 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
391 DCHECK(IsMainThread());
392 DCHECK(pending_request_.get());
393 if (pending_request_->channel_handle().name.empty()) {
394 DCHECK(!gpu_channel_.get());
395 } else {
396 GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
397 gpu_channel_ =
398 GpuChannelHost::Create(this,
399 pending_request_->gpu_info(),
400 pending_request_->channel_handle(),
401 shutdown_event_.get(),
402 BrowserGpuMemoryBufferManager::current());
404 gpu_host_id_ = pending_request_->gpu_host_id();
405 pending_request_ = NULL;
407 for (size_t n = 0; n < established_callbacks_.size(); n++)
408 established_callbacks_[n].Run();
410 established_callbacks_.clear();
413 // static
414 void BrowserGpuChannelHostFactory::AddFilterOnIO(
415 int host_id,
416 scoped_refptr<IPC::MessageFilter> filter) {
417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
419 GpuProcessHost* host = GpuProcessHost::FromID(host_id);
420 if (host)
421 host->AddFilter(filter.get());
424 void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
425 const uint32* message_ids,
426 size_t num_messages,
427 const base::Callback<void(const IPC::Message&)>& handler,
428 base::TaskRunner* target_task_runner) {
429 DCHECK(gpu_host_id_)
430 << "Do not call"
431 << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
432 << " until the GpuProcessHost has been set up.";
434 scoped_refptr<IPC::ForwardingMessageFilter> filter =
435 new IPC::ForwardingMessageFilter(message_ids,
436 num_messages,
437 target_task_runner);
438 filter->AddRoute(MSG_ROUTING_CONTROL, handler);
440 GetIOLoopProxy()->PostTask(
441 FROM_HERE,
442 base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO,
443 gpu_host_id_,
444 filter));
447 bool BrowserGpuChannelHostFactory::IsGpuMemoryBufferConfigurationSupported(
448 gfx::GpuMemoryBuffer::Format format,
449 gfx::GpuMemoryBuffer::Usage usage) {
450 // Return early if usage is not enabled.
451 if (!IsGpuMemoryBufferFactoryUsageEnabled(usage))
452 return false;
454 // Preferred type is always used by factory.
455 std::vector<gfx::GpuMemoryBufferType> supported_types;
456 GpuMemoryBufferFactory::GetSupportedTypes(&supported_types);
457 DCHECK(!supported_types.empty());
458 switch (supported_types[0]) {
459 case gfx::SHARED_MEMORY_BUFFER:
460 // Shared memory buffers must be created in-process.
461 return false;
462 #if defined(OS_MACOSX)
463 case gfx::IO_SURFACE_BUFFER:
464 return GpuMemoryBufferFactoryIOSurface::
465 IsGpuMemoryBufferConfigurationSupported(format, usage);
466 #endif
467 #if defined(OS_ANDROID)
468 case gfx::SURFACE_TEXTURE_BUFFER:
469 return GpuMemoryBufferFactorySurfaceTexture::
470 IsGpuMemoryBufferConfigurationSupported(format, usage);
471 #endif
472 #if defined(USE_OZONE)
473 case gfx::OZONE_NATIVE_BUFFER:
474 return GpuMemoryBufferFactoryOzoneNativeBuffer::
475 IsGpuMemoryBufferConfigurationSupported(format, usage);
476 #endif
477 default:
478 NOTREACHED();
479 return false;
483 void BrowserGpuChannelHostFactory::CreateGpuMemoryBuffer(
484 gfx::GpuMemoryBufferId id,
485 const gfx::Size& size,
486 gfx::GpuMemoryBuffer::Format format,
487 gfx::GpuMemoryBuffer::Usage usage,
488 int client_id,
489 int32 surface_id,
490 const CreateGpuMemoryBufferCallback& callback) {
491 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
493 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
494 if (!host) {
495 callback.Run(gfx::GpuMemoryBufferHandle());
496 return;
499 uint32 request_id = next_create_gpu_memory_buffer_request_id_++;
500 create_gpu_memory_buffer_requests_[request_id] = callback;
502 host->CreateGpuMemoryBuffer(
503 id, size, format, usage, client_id, surface_id,
504 base::Bind(&BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated,
505 base::Unretained(this), request_id));
508 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBuffer(
509 gfx::GpuMemoryBufferId id,
510 int client_id,
511 int32 sync_point) {
512 BrowserThread::PostTask(
513 BrowserThread::IO,
514 FROM_HERE,
515 base::Bind(&BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO,
516 base::Unretained(this),
518 client_id,
519 sync_point));
522 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO(
523 gfx::GpuMemoryBufferId id,
524 int client_id,
525 int32 sync_point) {
526 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
528 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
529 if (!host)
530 return;
532 host->DestroyGpuMemoryBuffer(id, client_id, sync_point);
535 void BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated(
536 uint32 request_id,
537 const gfx::GpuMemoryBufferHandle& handle) {
538 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
540 CreateGpuMemoryBufferCallbackMap::iterator iter =
541 create_gpu_memory_buffer_requests_.find(request_id);
542 DCHECK(iter != create_gpu_memory_buffer_requests_.end());
543 iter->second.Run(handle);
544 create_gpu_memory_buffer_requests_.erase(iter);
547 } // namespace content