diff options
Diffstat (limited to 'content/browser/gpu')
-rw-r--r-- | content/browser/gpu/browser_gpu_channel_host_factory.cc | 248 | ||||
-rw-r--r-- | content/browser/gpu/browser_gpu_channel_host_factory.h | 58 | ||||
-rw-r--r-- | content/browser/gpu/gpu_ipc_browsertests.cc | 147 |
3 files changed, 350 insertions, 103 deletions
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc index 7bc6fca..8de6d11 100644 --- a/content/browser/gpu/browser_gpu_channel_host_factory.cc +++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc @@ -5,6 +5,7 @@ #include "content/browser/gpu/browser_gpu_channel_host_factory.h" #include "base/bind.h" +#include "base/debug/trace_event.h" #include "base/threading/thread_restrictions.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/gpu/gpu_process_host.h" @@ -20,7 +21,7 @@ namespace content { BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL; BrowserGpuChannelHostFactory::CreateRequest::CreateRequest() - : event(false, false), + : event(true, false), gpu_host_id(0), route_id(MSG_ROUTING_NONE) { } @@ -29,32 +30,137 @@ BrowserGpuChannelHostFactory::CreateRequest::~CreateRequest() { } BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest( - CauseForGpuLaunch cause) - : event(false, false), - cause_for_gpu_launch(cause), - gpu_host_id(0), - reused_gpu_process(true) { + 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()) { + scoped_refptr<base::MessageLoopProxy> loop = + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); + loop->PostTask( + FROM_HERE, + base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO, + this)); } BrowserGpuChannelHostFactory::EstablishRequest::~EstablishRequest() { } -void BrowserGpuChannelHostFactory::Initialize() { - instance_ = new BrowserGpuChannelHostFactory(); +void 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) { + 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. + FinishOnIO(); + return; + } + reused_gpu_process_ = true; + } + + host->EstablishGpuChannel( + gpu_client_id_, + 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. + 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()); + { + // 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; +} + +void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) { + DCHECK(!instance_); + instance_ = new BrowserGpuChannelHostFactory(establish_gpu_channel); } void BrowserGpuChannelHostFactory::Terminate() { + DCHECK(instance_); delete instance_; instance_ = NULL; } -BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory() +BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory( + bool establish_gpu_channel) : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()), shutdown_event_(new base::WaitableEvent(true, false)), gpu_host_id_(0) { + if (establish_gpu_channel) { + pending_request_ = new EstablishRequest( + CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP, gpu_client_id_, gpu_host_id_); + } } BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() { + DCHECK(IsMainThread()); + if (pending_request_) + pending_request_->Cancel(); + for (size_t n = 0; n < established_callbacks_.size(); n++) + established_callbacks_[n].Run(); shutdown_event_->Signal(); } @@ -126,6 +232,8 @@ int32 BrowserGpuChannelHostFactory::CreateViewCommandBuffer( // 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.route_id; @@ -197,91 +305,65 @@ void BrowserGpuChannelHostFactory::DeleteImage( sync_point)); } -void BrowserGpuChannelHostFactory::EstablishGpuChannelOnIO( - EstablishRequest* request) { - GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); - if (!host) { - host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, - request->cause_for_gpu_launch); - if (!host) { - request->event.Signal(); - return; - } - gpu_host_id_ = host->host_id(); - request->reused_gpu_process = false; - } else { - if (host->host_id() == request->gpu_host_id) { - // We come here if we retried to establish the channel because of a - // failure in GpuChannelEstablishedOnIO, 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. - request->event.Signal(); - return; - } - request->reused_gpu_process = true; - } - request->gpu_host_id = gpu_host_id_; +GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync( + CauseForGpuLaunch cause_for_gpu_launch) { + EstablishGpuChannel(cause_for_gpu_launch, base::Closure()); - host->EstablishGpuChannel( - gpu_client_id_, - true, - base::Bind(&BrowserGpuChannelHostFactory::GpuChannelEstablishedOnIO, - base::Unretained(this), - request)); -} + if (pending_request_) + pending_request_->Wait(); -void BrowserGpuChannelHostFactory::GpuChannelEstablishedOnIO( - EstablishRequest* request, - const IPC::ChannelHandle& channel_handle, - const gpu::GPUInfo& gpu_info) { - if (channel_handle.name.empty() && request->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. - EstablishGpuChannelOnIO(request); - } else { - request->channel_handle = channel_handle; - request->gpu_info = gpu_info; - request->event.Signal(); - } + return gpu_channel_.get(); } -GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync( - CauseForGpuLaunch cause_for_gpu_launch) { - if (gpu_channel_.get()) { +void BrowserGpuChannelHostFactory::EstablishGpuChannel( + CauseForGpuLaunch cause_for_gpu_launch, + const base::Closure& callback) { + if (gpu_channel_.get() && gpu_channel_->IsLost()) { + DCHECK(!pending_request_); // Recreate the channel if it has been lost. - if (gpu_channel_->IsLost()) - gpu_channel_ = NULL; - else - return gpu_channel_.get(); + gpu_channel_ = NULL; } - // Ensure initialization on the main thread. - GpuDataManagerImpl::GetInstance(); - EstablishRequest request(cause_for_gpu_launch); - GetIOLoopProxy()->PostTask( - FROM_HERE, - base::Bind( - &BrowserGpuChannelHostFactory::EstablishGpuChannelOnIO, - base::Unretained(this), - &request)); + if (!gpu_channel_ && !pending_request_) { + // We should only get here if the context was lost. + pending_request_ = new EstablishRequest( + cause_for_gpu_launch, gpu_client_id_, gpu_host_id_); + } - { - // 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). - base::ThreadRestrictions::ScopedAllowWait allow_wait; - request.event.Wait(); + if (!callback.is_null()) { + if (gpu_channel_) + callback.Run(); + else + established_callbacks_.push_back(callback); } +} - if (request.channel_handle.name.empty()) - return NULL; +GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() { + if (gpu_channel_ && !gpu_channel_->IsLost()) + return gpu_channel_; - GetContentClient()->SetGpuInfo(request.gpu_info); - gpu_channel_ = GpuChannelHost::Create( - this, request.gpu_host_id, gpu_client_id_, - request.gpu_info, request.channel_handle); - return gpu_channel_.get(); + return NULL; +} + +void BrowserGpuChannelHostFactory::GpuChannelEstablished() { + DCHECK(IsMainThread()); + DCHECK(pending_request_); + if (pending_request_->channel_handle().name.empty()) + return; + + GetContentClient()->SetGpuInfo(pending_request_->gpu_info()); + gpu_channel_ = GpuChannelHost::Create(this, + pending_request_->gpu_host_id(), + gpu_client_id_, + pending_request_->gpu_info(), + pending_request_->channel_handle()); + gpu_host_id_ = pending_request_->gpu_host_id(); + pending_request_ = NULL; + + for (size_t n = 0; n < established_callbacks_.size(); n++) + established_callbacks_[n].Run(); + + established_callbacks_.clear(); } // static diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.h b/content/browser/gpu/browser_gpu_channel_host_factory.h index ca31183..1944155 100644 --- a/content/browser/gpu/browser_gpu_channel_host_factory.h +++ b/content/browser/gpu/browser_gpu_channel_host_factory.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_GPU_BROWSER_GPU_CHANNEL_HOST_FACTORY_H_ #define CONTENT_BROWSER_GPU_BROWSER_GPU_CHANNEL_HOST_FACTORY_H_ +#include <vector> + #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/process/process.h" @@ -17,7 +19,7 @@ namespace content { class CONTENT_EXPORT BrowserGpuChannelHostFactory : public GpuChannelHostFactory { public: - static void Initialize(); + static void Initialize(bool establish_gpu_channel); static void Terminate(); static BrowserGpuChannelHostFactory* instance() { return instance_; } @@ -36,8 +38,6 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory int32 image_id, const CreateImageCallback& callback) OVERRIDE; virtual void DeleteImage(int32 image_idu, int32 sync_point) OVERRIDE; - virtual GpuChannelHost* EstablishGpuChannelSync( - CauseForGpuLaunch cause_for_gpu_launch) OVERRIDE; // Specify a task runner and callback to be used for a set of messages. The // callback will be set up on the current GpuProcessHost, identified by @@ -48,6 +48,11 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory const base::Callback<void(const IPC::Message&)>& handler, base::TaskRunner* target_task_runner); int GpuProcessHostId() { return gpu_host_id_; } + GpuChannelHost* EstablishGpuChannelSync( + CauseForGpuLaunch cause_for_gpu_launch); + void EstablishGpuChannel(CauseForGpuLaunch cause_for_gpu_launch, + const base::Closure& callback); + GpuChannelHost* GetGpuChannel(); private: struct CreateRequest { @@ -58,20 +63,42 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory int32 route_id; }; - struct EstablishRequest { - explicit EstablishRequest(CauseForGpuLaunch); + class EstablishRequest : public base::RefCountedThreadSafe<EstablishRequest> { + public: + explicit EstablishRequest(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>; ~EstablishRequest(); - base::WaitableEvent event; - CauseForGpuLaunch cause_for_gpu_launch; - int gpu_host_id; - bool reused_gpu_process; - IPC::ChannelHandle channel_handle; - gpu::GPUInfo gpu_info; + 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_; + 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_; }; - BrowserGpuChannelHostFactory(); + explicit BrowserGpuChannelHostFactory(bool establish_gpu_channel); virtual ~BrowserGpuChannelHostFactory(); + void GpuChannelEstablished(); void CreateViewCommandBufferOnIO( CreateRequest* request, int32 surface_id, @@ -86,11 +113,6 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory static void OnImageCreated( const CreateImageCallback& callback, const gfx::Size size); void DeleteImageOnIO(int32 image_id, int32 sync_point); - void EstablishGpuChannelOnIO(EstablishRequest* request); - void GpuChannelEstablishedOnIO( - EstablishRequest* request, - const IPC::ChannelHandle& channel_handle, - const gpu::GPUInfo& gpu_info); static void AddFilterOnIO( int gpu_host_id, scoped_refptr<IPC::ChannelProxy::MessageFilter> filter); @@ -99,6 +121,8 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory scoped_ptr<base::WaitableEvent> shutdown_event_; scoped_refptr<GpuChannelHost> gpu_channel_; int gpu_host_id_; + scoped_refptr<EstablishRequest> pending_request_; + std::vector<base::Closure> established_callbacks_; static BrowserGpuChannelHostFactory* instance_; diff --git a/content/browser/gpu/gpu_ipc_browsertests.cc b/content/browser/gpu/gpu_ipc_browsertests.cc index 0ee93db..3fda855 100644 --- a/content/browser/gpu/gpu_ipc_browsertests.cc +++ b/content/browser/gpu/gpu_ipc_browsertests.cc @@ -3,8 +3,13 @@ // found in the LICENSE file. #include "base/command_line.h" +#include "base/run_loop.h" #include "content/browser/gpu/browser_gpu_channel_host_factory.h" +#include "content/browser/gpu/gpu_process_host_ui_shim.h" +#include "content/common/gpu/client/context_provider_command_buffer.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" +#include "content/common/gpu/gpu_process_launch_causes.h" +#include "content/public/browser/browser_thread.h" #include "content/public/common/content_switches.h" #include "content/test/content_browser_test.h" #include "ui/gl/gl_switches.h" @@ -15,10 +20,19 @@ namespace { class ContextTestBase : public content::ContentBrowserTest { public: virtual void SetUpOnMainThread() OVERRIDE { - CHECK(content::BrowserGpuChannelHostFactory::instance()); + if (!content::BrowserGpuChannelHostFactory::instance()) + content::BrowserGpuChannelHostFactory::Initialize(true); + + content::BrowserGpuChannelHostFactory* factory = + content::BrowserGpuChannelHostFactory::instance(); + CHECK(factory); + scoped_refptr<content::GpuChannelHost> gpu_channel_host( + factory->EstablishGpuChannelSync( + content:: + CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE)); context_.reset( content::WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext( - content::BrowserGpuChannelHostFactory::instance(), + gpu_channel_host.get(), WebKit::WebGraphicsContext3D::Attributes(), GURL())); CHECK(context_.get()); @@ -38,6 +52,133 @@ class ContextTestBase : public content::ContentBrowserTest { } // namespace -// Include the actual tests. +// Include the shared tests. #define CONTEXT_TEST_F IN_PROC_BROWSER_TEST_F #include "content/common/gpu/client/gpu_context_tests.h" + +namespace content { + +class BrowserGpuChannelHostFactoryTest : public ContextTestBase { + public: + virtual void SetUpOnMainThread() OVERRIDE { + // Start all tests without a gpu channel so that the tests exercise a + // consistent codepath. + if (!content::BrowserGpuChannelHostFactory::instance()) + content::BrowserGpuChannelHostFactory::Initialize(false); + + CHECK(GetFactory()); + + ContentBrowserTest::SetUpOnMainThread(); + } + + virtual void TearDownOnMainThread() OVERRIDE { + ContextTestBase::TearDownOnMainThread(); + } + + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + // Start all tests without a gpu channel so that the tests exercise a + // consistent codepath. + command_line->AppendSwitch(switches::kDisableGpuProcessPrelaunch); + } + + void OnContextLost(const base::Closure callback, int* counter) { + (*counter)++; + callback.Run(); + } + + protected: + BrowserGpuChannelHostFactory* GetFactory() { + return BrowserGpuChannelHostFactory::instance(); + } + + bool IsChannelEstablished() { + return GetFactory()->GetGpuChannel() != NULL; + } + + void EstablishAndWait() { + base::RunLoop run_loop; + GetFactory()->EstablishGpuChannel( + CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE, + run_loop.QuitClosure()); + run_loop.Run(); + } + + GpuChannelHost* GetGpuChannel() { + return GetFactory()->GetGpuChannel(); + } + + static void Signal(bool *event) { + CHECK_EQ(*event, false); + *event = true; + } + + scoped_ptr<WebGraphicsContext3DCommandBufferImpl> CreateContext() { + return make_scoped_ptr( + WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext( + GetGpuChannel(), + WebKit::WebGraphicsContext3D::Attributes(), + GURL())); + } +}; + +IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest, Basic) { + DCHECK(!IsChannelEstablished()); + EstablishAndWait(); + EXPECT_TRUE(GetGpuChannel() != NULL); +} + +IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest, + EstablishAndTerminate) { + DCHECK(!IsChannelEstablished()); + base::RunLoop run_loop; + GetFactory()->EstablishGpuChannel( + CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE, + run_loop.QuitClosure()); + GetFactory()->Terminate(); + + // The callback should still trigger. + run_loop.Run(); +} + +IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest, AlreadyEstablished) { + DCHECK(!IsChannelEstablished()); + scoped_refptr<GpuChannelHost> gpu_channel = + GetFactory()->EstablishGpuChannelSync( + CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE); + + // Expect established callback immediately. + bool event = false; + GetFactory()->EstablishGpuChannel( + CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE, + base::Bind(&BrowserGpuChannelHostFactoryTest::Signal, &event)); + EXPECT_TRUE(event); + EXPECT_EQ(gpu_channel, GetGpuChannel()); +} + +IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest, CrashAndRecover) { + DCHECK(!IsChannelEstablished()); + EstablishAndWait(); + scoped_refptr<GpuChannelHost> host = GetGpuChannel(); + + scoped_refptr<ContextProviderCommandBuffer> provider = + ContextProviderCommandBuffer::Create(CreateContext(), + "BrowserGpuChannelHostFactoryTest"); + base::RunLoop run_loop; + int counter = 0; + provider->SetLostContextCallback( + base::Bind(&BrowserGpuChannelHostFactoryTest::OnContextLost, + base::Unretained(this), run_loop.QuitClosure(), &counter)); + EXPECT_TRUE(provider->BindToCurrentThread()); + GpuProcessHostUIShim* shim = + GpuProcessHostUIShim::FromID(GetFactory()->GpuProcessHostId()); + EXPECT_TRUE(shim != NULL); + shim->SimulateCrash(); + run_loop.Run(); + + EXPECT_EQ(1, counter); + EXPECT_FALSE(IsChannelEstablished()); + EstablishAndWait(); + EXPECT_TRUE(IsChannelEstablished()); +} + +} // namespace content |