Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / browser / gpu / browser_gpu_channel_host_factory.cc
blob378925ad137a2356949316997eb5aef42e618324
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 "base/bind.h"
8 #include "base/location.h"
9 #include "base/profiler/scoped_tracker.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "base/trace_event/trace_event.h"
15 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
16 #include "content/browser/gpu/gpu_data_manager_impl.h"
17 #include "content/browser/gpu/gpu_process_host.h"
18 #include "content/browser/gpu/gpu_surface_tracker.h"
19 #include "content/common/child_process_host_impl.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/message_filter.h"
27 namespace content {
29 BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL;
31 struct BrowserGpuChannelHostFactory::CreateRequest {
32 CreateRequest(int32 route_id)
33 : event(true, false),
34 gpu_host_id(0),
35 route_id(route_id),
36 result(CREATE_COMMAND_BUFFER_FAILED) {}
37 ~CreateRequest() {}
38 base::WaitableEvent event;
39 int gpu_host_id;
40 int32 route_id;
41 CreateCommandBufferResult result;
44 class BrowserGpuChannelHostFactory::EstablishRequest
45 : public base::RefCountedThreadSafe<EstablishRequest> {
46 public:
47 static scoped_refptr<EstablishRequest> Create(CauseForGpuLaunch cause,
48 int gpu_client_id,
49 int gpu_host_id);
50 void Wait();
51 void Cancel();
53 int gpu_host_id() { return gpu_host_id_; }
54 IPC::ChannelHandle& channel_handle() { return channel_handle_; }
55 gpu::GPUInfo gpu_info() { return gpu_info_; }
57 private:
58 friend class base::RefCountedThreadSafe<EstablishRequest>;
59 explicit EstablishRequest(CauseForGpuLaunch cause,
60 int gpu_client_id,
61 int gpu_host_id);
62 ~EstablishRequest() {}
63 void EstablishOnIO();
64 void OnEstablishedOnIO(const IPC::ChannelHandle& channel_handle,
65 const gpu::GPUInfo& gpu_info);
66 void FinishOnIO();
67 void FinishOnMain();
69 base::WaitableEvent event_;
70 CauseForGpuLaunch cause_for_gpu_launch_;
71 const int gpu_client_id_;
72 int gpu_host_id_;
73 bool reused_gpu_process_;
74 IPC::ChannelHandle channel_handle_;
75 gpu::GPUInfo gpu_info_;
76 bool finished_;
77 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
80 scoped_refptr<BrowserGpuChannelHostFactory::EstablishRequest>
81 BrowserGpuChannelHostFactory::EstablishRequest::Create(CauseForGpuLaunch cause,
82 int gpu_client_id,
83 int gpu_host_id) {
84 scoped_refptr<EstablishRequest> establish_request =
85 new EstablishRequest(cause, gpu_client_id, gpu_host_id);
86 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
87 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
88 // PostTask outside the constructor to ensure at least one reference exists.
89 task_runner->PostTask(
90 FROM_HERE,
91 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO,
92 establish_request));
93 return establish_request;
96 BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
97 CauseForGpuLaunch cause,
98 int gpu_client_id,
99 int gpu_host_id)
100 : event_(false, false),
101 cause_for_gpu_launch_(cause),
102 gpu_client_id_(gpu_client_id),
103 gpu_host_id_(gpu_host_id),
104 reused_gpu_process_(false),
105 finished_(false),
106 main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
109 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
110 // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
111 tracked_objects::ScopedTracker tracking_profile(
112 FROM_HERE_WITH_EXPLICIT_FUNCTION(
113 "477117 "
114 "BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO"));
115 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
116 if (!host) {
117 host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
118 cause_for_gpu_launch_);
119 if (!host) {
120 LOG(ERROR) << "Failed to launch GPU process.";
121 FinishOnIO();
122 return;
124 gpu_host_id_ = host->host_id();
125 reused_gpu_process_ = false;
126 } else {
127 if (reused_gpu_process_) {
128 // We come here if we retried to establish the channel because of a
129 // failure in ChannelEstablishedOnIO, but we ended up with the same
130 // process ID, meaning the failure was not because of a channel error,
131 // but another reason. So fail now.
132 LOG(ERROR) << "Failed to create channel.";
133 FinishOnIO();
134 return;
136 reused_gpu_process_ = true;
139 host->EstablishGpuChannel(
140 gpu_client_id_,
141 true,
142 true,
143 base::Bind(
144 &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO,
145 this));
148 void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
149 const IPC::ChannelHandle& channel_handle,
150 const gpu::GPUInfo& gpu_info) {
151 if (channel_handle.name.empty() && reused_gpu_process_) {
152 // We failed after re-using the GPU process, but it may have died in the
153 // mean time. Retry to have a chance to create a fresh GPU process.
154 DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "
155 "restart GPU process.";
156 EstablishOnIO();
157 } else {
158 channel_handle_ = channel_handle;
159 gpu_info_ = gpu_info;
160 FinishOnIO();
164 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
165 event_.Signal();
166 main_task_runner_->PostTask(
167 FROM_HERE,
168 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain,
169 this));
172 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
173 if (!finished_) {
174 BrowserGpuChannelHostFactory* factory =
175 BrowserGpuChannelHostFactory::instance();
176 factory->GpuChannelEstablished();
177 finished_ = true;
181 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
182 DCHECK(main_task_runner_->BelongsToCurrentThread());
184 // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is fixed.
185 tracked_objects::ScopedTracker tracking_profile(
186 FROM_HERE_WITH_EXPLICIT_FUNCTION(
187 "125248 BrowserGpuChannelHostFactory::EstablishRequest::Wait"));
189 // We're blocking the UI thread, which is generally undesirable.
190 // In this case we need to wait for this before we can show any UI
191 // /anyway/, so it won't cause additional jank.
192 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
193 TRACE_EVENT0("browser",
194 "BrowserGpuChannelHostFactory::EstablishGpuChannelSync");
195 base::ThreadRestrictions::ScopedAllowWait allow_wait;
196 event_.Wait();
198 FinishOnMain();
201 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
202 DCHECK(main_task_runner_->BelongsToCurrentThread());
203 finished_ = true;
206 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
207 return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL);
210 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {
211 DCHECK(!instance_);
212 instance_ = new BrowserGpuChannelHostFactory();
213 if (establish_gpu_channel) {
214 instance_->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP,
215 base::Closure());
219 void BrowserGpuChannelHostFactory::Terminate() {
220 DCHECK(instance_);
221 delete instance_;
222 instance_ = NULL;
225 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
226 : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
227 shutdown_event_(new base::WaitableEvent(true, false)),
228 gpu_memory_buffer_manager_(
229 new BrowserGpuMemoryBufferManager(gpu_client_id_)),
230 gpu_host_id_(0) {
233 BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
234 DCHECK(IsMainThread());
235 if (pending_request_.get())
236 pending_request_->Cancel();
237 for (size_t n = 0; n < established_callbacks_.size(); n++)
238 established_callbacks_[n].Run();
239 shutdown_event_->Signal();
240 if (gpu_channel_) {
241 gpu_channel_->DestroyChannel();
242 gpu_channel_ = NULL;
246 bool BrowserGpuChannelHostFactory::IsMainThread() {
247 return BrowserThread::CurrentlyOn(BrowserThread::UI);
250 scoped_refptr<base::SingleThreadTaskRunner>
251 BrowserGpuChannelHostFactory::GetIOThreadTaskRunner() {
252 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
255 scoped_ptr<base::SharedMemory>
256 BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) {
257 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
258 if (!shm->CreateAnonymous(size))
259 return scoped_ptr<base::SharedMemory>();
260 return shm.Pass();
263 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
264 CreateRequest* request,
265 int32 surface_id,
266 const GPUCreateCommandBufferConfig& init_params) {
267 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
268 if (!host) {
269 request->event.Signal();
270 return;
273 gfx::GLSurfaceHandle surface =
274 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
276 host->CreateViewCommandBuffer(
277 surface,
278 surface_id,
279 gpu_client_id_,
280 init_params,
281 request->route_id,
282 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
283 request));
286 IPC::AttachmentBroker* BrowserGpuChannelHostFactory::GetAttachmentBroker() {
287 return content::ChildProcessHost::GetAttachmentBroker();
290 // static
291 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
292 CreateRequest* request, CreateCommandBufferResult result) {
293 request->result = result;
294 request->event.Signal();
297 CreateCommandBufferResult BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
298 int32 surface_id,
299 const GPUCreateCommandBufferConfig& init_params,
300 int32 route_id) {
301 CreateRequest request(route_id);
302 GetIOThreadTaskRunner()->PostTask(
303 FROM_HERE,
304 base::Bind(&BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
305 base::Unretained(this), &request, surface_id, init_params));
306 // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is fixed.
307 tracked_objects::ScopedTracker tracking_profile(
308 FROM_HERE_WITH_EXPLICIT_FUNCTION(
309 "125248 BrowserGpuChannelHostFactory::CreateViewCommandBuffer"));
311 // We're blocking the UI thread, which is generally undesirable.
312 // In this case we need to wait for this before we can show any UI /anyway/,
313 // so it won't cause additional jank.
314 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
315 TRACE_EVENT0("browser",
316 "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
317 base::ThreadRestrictions::ScopedAllowWait allow_wait;
318 request.event.Wait();
319 return request.result;
322 // Blocking the UI thread to open a GPU channel is not supported on Android.
323 // (Opening the initial channel to a child process involves handling a reply
324 // task on the UI thread first, so we cannot block here.)
325 #if !defined(OS_ANDROID)
326 GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
327 CauseForGpuLaunch cause_for_gpu_launch) {
328 EstablishGpuChannel(cause_for_gpu_launch, base::Closure());
330 if (pending_request_.get())
331 pending_request_->Wait();
333 return gpu_channel_.get();
335 #endif
337 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
338 CauseForGpuLaunch cause_for_gpu_launch,
339 const base::Closure& callback) {
340 if (gpu_channel_.get() && gpu_channel_->IsLost()) {
341 DCHECK(!pending_request_.get());
342 // Recreate the channel if it has been lost.
343 gpu_channel_->DestroyChannel();
344 gpu_channel_ = NULL;
347 if (!gpu_channel_.get() && !pending_request_.get()) {
348 // We should only get here if the context was lost.
349 pending_request_ = EstablishRequest::Create(
350 cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);
353 if (!callback.is_null()) {
354 if (gpu_channel_.get())
355 callback.Run();
356 else
357 established_callbacks_.push_back(callback);
361 GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() {
362 if (gpu_channel_.get() && !gpu_channel_->IsLost())
363 return gpu_channel_.get();
365 return NULL;
368 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
369 DCHECK(IsMainThread());
370 DCHECK(pending_request_.get());
371 if (pending_request_->channel_handle().name.empty()) {
372 DCHECK(!gpu_channel_.get());
373 } else {
374 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/466866
375 // is fixed.
376 tracked_objects::ScopedTracker tracking_profile1(
377 FROM_HERE_WITH_EXPLICIT_FUNCTION(
378 "466866 BrowserGpuChannelHostFactory::GpuChannelEstablished1"));
379 GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
380 gpu_channel_ = GpuChannelHost::Create(
381 this, pending_request_->gpu_info(), pending_request_->channel_handle(),
382 shutdown_event_.get(), gpu_memory_buffer_manager_.get());
384 gpu_host_id_ = pending_request_->gpu_host_id();
385 pending_request_ = NULL;
387 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/466866 is
388 // fixed.
389 tracked_objects::ScopedTracker tracking_profile2(
390 FROM_HERE_WITH_EXPLICIT_FUNCTION(
391 "466866 BrowserGpuChannelHostFactory::GpuChannelEstablished2"));
393 for (size_t n = 0; n < established_callbacks_.size(); n++)
394 established_callbacks_[n].Run();
396 established_callbacks_.clear();
399 // static
400 void BrowserGpuChannelHostFactory::AddFilterOnIO(
401 int host_id,
402 scoped_refptr<IPC::MessageFilter> filter) {
403 DCHECK_CURRENTLY_ON(BrowserThread::IO);
405 GpuProcessHost* host = GpuProcessHost::FromID(host_id);
406 if (host)
407 host->AddFilter(filter.get());
410 } // namespace content