| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/gpu/browser_gpu_channel_host_factory.h" |
| |
| #include <set> |
| |
| #include "base/bind.h" |
| #include "base/profiler/scoped_tracker.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/trace_event/trace_event.h" |
| #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" |
| #include "content/browser/gpu/gpu_data_manager_impl.h" |
| #include "content/browser/gpu/gpu_process_host.h" |
| #include "content/browser/gpu/gpu_surface_tracker.h" |
| #include "content/common/child_process_host_impl.h" |
| #include "content/common/gpu/gpu_memory_buffer_factory.h" |
| #include "content/common/gpu/gpu_messages.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/gpu_data_manager.h" |
| #include "content/public/common/content_client.h" |
| #include "gpu/GLES2/gl2extchromium.h" |
| #include "ipc/ipc_channel_handle.h" |
| #include "ipc/ipc_forwarding_message_filter.h" |
| #include "ipc/message_filter.h" |
| |
| #if defined(OS_MACOSX) |
| #include "content/common/gpu/gpu_memory_buffer_factory_io_surface.h" |
| #endif |
| |
| #if defined(OS_ANDROID) |
| #include "content/common/gpu/gpu_memory_buffer_factory_surface_texture.h" |
| #endif |
| |
| #if defined(USE_OZONE) |
| #include "content/common/gpu/gpu_memory_buffer_factory_ozone_native_buffer.h" |
| #endif |
| |
| namespace content { |
| namespace { |
| |
| base::LazyInstance<std::set<gfx::GpuMemoryBuffer::Usage>> |
| g_enabled_gpu_memory_buffer_usages; |
| } |
| |
| BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL; |
| |
| struct BrowserGpuChannelHostFactory::CreateRequest { |
| CreateRequest(int32 route_id) |
| : event(true, false), |
| gpu_host_id(0), |
| route_id(route_id), |
| result(CREATE_COMMAND_BUFFER_FAILED) {} |
| ~CreateRequest() {} |
| base::WaitableEvent event; |
| int gpu_host_id; |
| int32 route_id; |
| CreateCommandBufferResult result; |
| }; |
| |
| class BrowserGpuChannelHostFactory::EstablishRequest |
| : public base::RefCountedThreadSafe<EstablishRequest> { |
| public: |
| static scoped_refptr<EstablishRequest> Create(CauseForGpuLaunch cause, |
| int gpu_client_id, |
| int gpu_host_id); |
| void Wait(); |
| void Cancel(); |
| |
| int gpu_host_id() { return gpu_host_id_; } |
| IPC::ChannelHandle& channel_handle() { return channel_handle_; } |
| gpu::GPUInfo gpu_info() { return gpu_info_; } |
| |
| private: |
| friend class base::RefCountedThreadSafe<EstablishRequest>; |
| explicit EstablishRequest(CauseForGpuLaunch cause, |
| int gpu_client_id, |
| int gpu_host_id); |
| ~EstablishRequest() {} |
| void EstablishOnIO(); |
| void OnEstablishedOnIO(const IPC::ChannelHandle& channel_handle, |
| const gpu::GPUInfo& gpu_info); |
| void FinishOnIO(); |
| void FinishOnMain(); |
| |
| base::WaitableEvent event_; |
| CauseForGpuLaunch cause_for_gpu_launch_; |
| const int gpu_client_id_; |
| int gpu_host_id_; |
| bool reused_gpu_process_; |
| IPC::ChannelHandle channel_handle_; |
| gpu::GPUInfo gpu_info_; |
| bool finished_; |
| scoped_refptr<base::MessageLoopProxy> main_loop_; |
| }; |
| |
| scoped_refptr<BrowserGpuChannelHostFactory::EstablishRequest> |
| BrowserGpuChannelHostFactory::EstablishRequest::Create(CauseForGpuLaunch cause, |
| int gpu_client_id, |
| int gpu_host_id) { |
| scoped_refptr<EstablishRequest> establish_request = |
| new EstablishRequest(cause, gpu_client_id, gpu_host_id); |
| scoped_refptr<base::MessageLoopProxy> loop = |
| BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); |
| // PostTask outside the constructor to ensure at least one reference exists. |
| loop->PostTask( |
| FROM_HERE, |
| base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO, |
| establish_request)); |
| return establish_request; |
| } |
| |
| BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest( |
| CauseForGpuLaunch cause, |
| int gpu_client_id, |
| int gpu_host_id) |
| : event_(false, false), |
| cause_for_gpu_launch_(cause), |
| gpu_client_id_(gpu_client_id), |
| gpu_host_id_(gpu_host_id), |
| reused_gpu_process_(false), |
| finished_(false), |
| main_loop_(base::MessageLoopProxy::current()) { |
| } |
| |
| void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() { |
| // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed. |
| tracked_objects::ScopedTracker tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "477117 " |
| "BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO")); |
| GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); |
| if (!host) { |
| host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, |
| cause_for_gpu_launch_); |
| if (!host) { |
| LOG(ERROR) << "Failed to launch GPU process."; |
| FinishOnIO(); |
| return; |
| } |
| gpu_host_id_ = host->host_id(); |
| reused_gpu_process_ = false; |
| } else { |
| if (reused_gpu_process_) { |
| // We come here if we retried to establish the channel because of a |
| // failure in ChannelEstablishedOnIO, but we ended up with the same |
| // process ID, meaning the failure was not because of a channel error, |
| // but another reason. So fail now. |
| LOG(ERROR) << "Failed to create channel."; |
| FinishOnIO(); |
| return; |
| } |
| reused_gpu_process_ = true; |
| } |
| |
| host->EstablishGpuChannel( |
| gpu_client_id_, |
| true, |
| true, |
| base::Bind( |
| &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO, |
| this)); |
| } |
| |
| void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO( |
| const IPC::ChannelHandle& channel_handle, |
| const gpu::GPUInfo& gpu_info) { |
| if (channel_handle.name.empty() && reused_gpu_process_) { |
| // We failed after re-using the GPU process, but it may have died in the |
| // mean time. Retry to have a chance to create a fresh GPU process. |
| DVLOG(1) << "Failed to create channel on existing GPU process. Trying to " |
| "restart GPU process."; |
| EstablishOnIO(); |
| } else { |
| channel_handle_ = channel_handle; |
| gpu_info_ = gpu_info; |
| FinishOnIO(); |
| } |
| } |
| |
| void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() { |
| event_.Signal(); |
| main_loop_->PostTask( |
| FROM_HERE, |
| base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain, |
| this)); |
| } |
| |
| void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() { |
| if (!finished_) { |
| BrowserGpuChannelHostFactory* factory = |
| BrowserGpuChannelHostFactory::instance(); |
| factory->GpuChannelEstablished(); |
| finished_ = true; |
| } |
| } |
| |
| void BrowserGpuChannelHostFactory::EstablishRequest::Wait() { |
| DCHECK(main_loop_->BelongsToCurrentThread()); |
| { |
| // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is fixed. |
| tracked_objects::ScopedTracker tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "125248 BrowserGpuChannelHostFactory::EstablishRequest::Wait")); |
| |
| // We're blocking the UI thread, which is generally undesirable. |
| // In this case we need to wait for this before we can show any UI |
| // /anyway/, so it won't cause additional jank. |
| // TODO(piman): Make this asynchronous (http://crbug.com/125248). |
| TRACE_EVENT0("browser", |
| "BrowserGpuChannelHostFactory::EstablishGpuChannelSync"); |
| base::ThreadRestrictions::ScopedAllowWait allow_wait; |
| event_.Wait(); |
| } |
| FinishOnMain(); |
| } |
| |
| void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() { |
| DCHECK(main_loop_->BelongsToCurrentThread()); |
| finished_ = true; |
| } |
| |
| bool BrowserGpuChannelHostFactory::CanUseForTesting() { |
| return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL); |
| } |
| |
| void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) { |
| DCHECK(!instance_); |
| instance_ = new BrowserGpuChannelHostFactory(); |
| if (establish_gpu_channel) { |
| instance_->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP, |
| base::Closure()); |
| } |
| } |
| |
| void BrowserGpuChannelHostFactory::Terminate() { |
| DCHECK(instance_); |
| delete instance_; |
| instance_ = NULL; |
| } |
| |
| // static |
| void BrowserGpuChannelHostFactory::EnableGpuMemoryBufferFactoryUsage( |
| gfx::GpuMemoryBuffer::Usage usage) { |
| g_enabled_gpu_memory_buffer_usages.Get().insert(usage); |
| } |
| |
| // static |
| bool BrowserGpuChannelHostFactory::IsGpuMemoryBufferFactoryUsageEnabled( |
| gfx::GpuMemoryBuffer::Usage usage) { |
| return g_enabled_gpu_memory_buffer_usages.Get().count(usage) != 0; |
| } |
| |
| // static |
| uint32 BrowserGpuChannelHostFactory::GetImageTextureTarget() { |
| if (!IsGpuMemoryBufferFactoryUsageEnabled(gfx::GpuMemoryBuffer::MAP)) |
| return GL_TEXTURE_2D; |
| |
| std::vector<gfx::GpuMemoryBufferType> supported_types; |
| GpuMemoryBufferFactory::GetSupportedTypes(&supported_types); |
| DCHECK(!supported_types.empty()); |
| |
| // The GPU service will always use the preferred type. |
| gfx::GpuMemoryBufferType type = supported_types[0]; |
| |
| switch (type) { |
| case gfx::SURFACE_TEXTURE_BUFFER: |
| case gfx::OZONE_NATIVE_BUFFER: |
| // GPU memory buffers that are shared with the GL using EGLImages require |
| // TEXTURE_EXTERNAL_OES. |
| return GL_TEXTURE_EXTERNAL_OES; |
| case gfx::IO_SURFACE_BUFFER: |
| // IOSurface backed images require GL_TEXTURE_RECTANGLE_ARB. |
| return GL_TEXTURE_RECTANGLE_ARB; |
| default: |
| return GL_TEXTURE_2D; |
| } |
| } |
| |
| BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory() |
| : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()), |
| shutdown_event_(new base::WaitableEvent(true, false)), |
| gpu_memory_buffer_manager_( |
| new BrowserGpuMemoryBufferManager(this, gpu_client_id_)), |
| gpu_host_id_(0), |
| next_create_gpu_memory_buffer_request_id_(0) { |
| } |
| |
| BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() { |
| DCHECK(IsMainThread()); |
| if (pending_request_.get()) |
| pending_request_->Cancel(); |
| for (size_t n = 0; n < established_callbacks_.size(); n++) |
| established_callbacks_[n].Run(); |
| shutdown_event_->Signal(); |
| } |
| |
| bool BrowserGpuChannelHostFactory::IsMainThread() { |
| return BrowserThread::CurrentlyOn(BrowserThread::UI); |
| } |
| |
| base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() { |
| return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI); |
| } |
| |
| scoped_refptr<base::MessageLoopProxy> |
| BrowserGpuChannelHostFactory::GetIOLoopProxy() { |
| return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); |
| } |
| |
| scoped_ptr<base::SharedMemory> |
| BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) { |
| scoped_ptr<base::SharedMemory> shm(new base::SharedMemory()); |
| if (!shm->CreateAnonymous(size)) |
| return scoped_ptr<base::SharedMemory>(); |
| return shm.Pass(); |
| } |
| |
| void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO( |
| CreateRequest* request, |
| int32 surface_id, |
| const GPUCreateCommandBufferConfig& init_params) { |
| GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); |
| if (!host) { |
| request->event.Signal(); |
| return; |
| } |
| |
| gfx::GLSurfaceHandle surface = |
| GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id); |
| |
| host->CreateViewCommandBuffer( |
| surface, |
| surface_id, |
| gpu_client_id_, |
| init_params, |
| request->route_id, |
| base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO, |
| request)); |
| } |
| |
| // static |
| void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO( |
| CreateRequest* request, CreateCommandBufferResult result) { |
| request->result = result; |
| request->event.Signal(); |
| } |
| |
| CreateCommandBufferResult BrowserGpuChannelHostFactory::CreateViewCommandBuffer( |
| int32 surface_id, |
| const GPUCreateCommandBufferConfig& init_params, |
| int32 route_id) { |
| CreateRequest request(route_id); |
| GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind( |
| &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO, |
| base::Unretained(this), |
| &request, |
| surface_id, |
| init_params)); |
| // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is fixed. |
| tracked_objects::ScopedTracker tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "125248 BrowserGpuChannelHostFactory::CreateViewCommandBuffer")); |
| |
| // We're blocking the UI thread, which is generally undesirable. |
| // In this case we need to wait for this before we can show any UI /anyway/, |
| // so it won't cause additional jank. |
| // TODO(piman): Make this asynchronous (http://crbug.com/125248). |
| TRACE_EVENT0("browser", |
| "BrowserGpuChannelHostFactory::CreateViewCommandBuffer"); |
| base::ThreadRestrictions::ScopedAllowWait allow_wait; |
| request.event.Wait(); |
| return request.result; |
| } |
| |
| // Blocking the UI thread to open a GPU channel is not supported on Android. |
| // (Opening the initial channel to a child process involves handling a reply |
| // task on the UI thread first, so we cannot block here.) |
| #if !defined(OS_ANDROID) |
| GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync( |
| CauseForGpuLaunch cause_for_gpu_launch) { |
| EstablishGpuChannel(cause_for_gpu_launch, base::Closure()); |
| |
| if (pending_request_.get()) |
| pending_request_->Wait(); |
| |
| return gpu_channel_.get(); |
| } |
| #endif |
| |
| void BrowserGpuChannelHostFactory::EstablishGpuChannel( |
| CauseForGpuLaunch cause_for_gpu_launch, |
| const base::Closure& callback) { |
| if (gpu_channel_.get() && gpu_channel_->IsLost()) { |
| DCHECK(!pending_request_.get()); |
| // Recreate the channel if it has been lost. |
| gpu_channel_ = NULL; |
| } |
| |
| if (!gpu_channel_.get() && !pending_request_.get()) { |
| // We should only get here if the context was lost. |
| pending_request_ = EstablishRequest::Create( |
| cause_for_gpu_launch, gpu_client_id_, gpu_host_id_); |
| } |
| |
| if (!callback.is_null()) { |
| if (gpu_channel_.get()) |
| callback.Run(); |
| else |
| established_callbacks_.push_back(callback); |
| } |
| } |
| |
| GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() { |
| if (gpu_channel_.get() && !gpu_channel_->IsLost()) |
| return gpu_channel_.get(); |
| |
| return NULL; |
| } |
| |
| void BrowserGpuChannelHostFactory::GpuChannelEstablished() { |
| DCHECK(IsMainThread()); |
| DCHECK(pending_request_.get()); |
| if (pending_request_->channel_handle().name.empty()) { |
| DCHECK(!gpu_channel_.get()); |
| } else { |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/466866 |
| // is fixed. |
| tracked_objects::ScopedTracker tracking_profile1( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "466866 BrowserGpuChannelHostFactory::GpuChannelEstablished1")); |
| GetContentClient()->SetGpuInfo(pending_request_->gpu_info()); |
| gpu_channel_ = |
| GpuChannelHost::Create(this, |
| pending_request_->gpu_info(), |
| pending_request_->channel_handle(), |
| shutdown_event_.get(), |
| BrowserGpuMemoryBufferManager::current()); |
| } |
| gpu_host_id_ = pending_request_->gpu_host_id(); |
| pending_request_ = NULL; |
| |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/466866 is |
| // fixed. |
| tracked_objects::ScopedTracker tracking_profile2( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "466866 BrowserGpuChannelHostFactory::GpuChannelEstablished2")); |
| |
| for (size_t n = 0; n < established_callbacks_.size(); n++) |
| established_callbacks_[n].Run(); |
| |
| established_callbacks_.clear(); |
| } |
| |
| // static |
| void BrowserGpuChannelHostFactory::AddFilterOnIO( |
| int host_id, |
| scoped_refptr<IPC::MessageFilter> filter) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| GpuProcessHost* host = GpuProcessHost::FromID(host_id); |
| if (host) |
| host->AddFilter(filter.get()); |
| } |
| |
| bool BrowserGpuChannelHostFactory::IsGpuMemoryBufferConfigurationSupported( |
| gfx::GpuMemoryBuffer::Format format, |
| gfx::GpuMemoryBuffer::Usage usage) { |
| // Return early if usage is not enabled. |
| if (!IsGpuMemoryBufferFactoryUsageEnabled(usage)) |
| return false; |
| |
| // Preferred type is always used by factory. |
| std::vector<gfx::GpuMemoryBufferType> supported_types; |
| GpuMemoryBufferFactory::GetSupportedTypes(&supported_types); |
| DCHECK(!supported_types.empty()); |
| switch (supported_types[0]) { |
| case gfx::SHARED_MEMORY_BUFFER: |
| // Shared memory buffers must be created in-process. |
| return false; |
| #if defined(OS_MACOSX) |
| case gfx::IO_SURFACE_BUFFER: |
| return GpuMemoryBufferFactoryIOSurface:: |
| IsGpuMemoryBufferConfigurationSupported(format, usage); |
| #endif |
| #if defined(OS_ANDROID) |
| case gfx::SURFACE_TEXTURE_BUFFER: |
| return GpuMemoryBufferFactorySurfaceTexture:: |
| IsGpuMemoryBufferConfigurationSupported(format, usage); |
| #endif |
| #if defined(USE_OZONE) |
| case gfx::OZONE_NATIVE_BUFFER: |
| return GpuMemoryBufferFactoryOzoneNativeBuffer:: |
| IsGpuMemoryBufferConfigurationSupported(format, usage); |
| #endif |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| void BrowserGpuChannelHostFactory::CreateGpuMemoryBuffer( |
| gfx::GpuMemoryBufferId id, |
| const gfx::Size& size, |
| gfx::GpuMemoryBuffer::Format format, |
| gfx::GpuMemoryBuffer::Usage usage, |
| int client_id, |
| int32 surface_id, |
| const CreateGpuMemoryBufferCallback& callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); |
| if (!host) { |
| callback.Run(gfx::GpuMemoryBufferHandle()); |
| return; |
| } |
| |
| uint32 request_id = next_create_gpu_memory_buffer_request_id_++; |
| create_gpu_memory_buffer_requests_[request_id] = callback; |
| |
| host->CreateGpuMemoryBuffer( |
| id, size, format, usage, client_id, surface_id, |
| base::Bind(&BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated, |
| base::Unretained(this), request_id)); |
| } |
| |
| void BrowserGpuChannelHostFactory::DestroyGpuMemoryBuffer( |
| gfx::GpuMemoryBufferId id, |
| int client_id, |
| int32 sync_point) { |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO, |
| base::Unretained(this), |
| id, |
| client_id, |
| sync_point)); |
| } |
| |
| void BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO( |
| gfx::GpuMemoryBufferId id, |
| int client_id, |
| int32 sync_point) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); |
| if (!host) |
| return; |
| |
| host->DestroyGpuMemoryBuffer(id, client_id, sync_point); |
| } |
| |
| void BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated( |
| uint32 request_id, |
| const gfx::GpuMemoryBufferHandle& handle) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| CreateGpuMemoryBufferCallbackMap::iterator iter = |
| create_gpu_memory_buffer_requests_.find(request_id); |
| DCHECK(iter != create_gpu_memory_buffer_requests_.end()); |
| iter->second.Run(handle); |
| create_gpu_memory_buffer_requests_.erase(iter); |
| } |
| |
| } // namespace content |