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.
4
5 #include "content/browser/gpu/browser_gpu_channel_host_factory.h"
6
7 #include "base/bind.h"
8 #include "base/debug/trace_event.h"
9 #include "base/threading/thread_restrictions.h"
10 #include "content/browser/gpu/gpu_data_manager_impl.h"
11 #include "content/browser/gpu/gpu_process_host.h"
12 #include "content/browser/gpu/gpu_surface_tracker.h"
13 #include "content/common/child_process_host_impl.h"
14 #include "content/common/gpu/client/gpu_memory_buffer_impl_shm.h"
15 #include "content/common/gpu/gpu_messages.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/gpu_data_manager.h"
18 #include "content/public/common/content_client.h"
19 #include "ipc/ipc_forwarding_message_filter.h"
20
21 namespace content {
22
23 BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL;
24
CreateRequest()25 BrowserGpuChannelHostFactory::CreateRequest::CreateRequest()
26 : event(true, false),
27 gpu_host_id(0),
28 route_id(MSG_ROUTING_NONE) {
29 }
30
~CreateRequest()31 BrowserGpuChannelHostFactory::CreateRequest::~CreateRequest() {
32 }
33
EstablishRequest(CauseForGpuLaunch cause,int gpu_client_id,int gpu_host_id)34 BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
35 CauseForGpuLaunch cause,
36 int gpu_client_id,
37 int gpu_host_id)
38 : event_(false, false),
39 cause_for_gpu_launch_(cause),
40 gpu_client_id_(gpu_client_id),
41 gpu_host_id_(gpu_host_id),
42 reused_gpu_process_(false),
43 finished_(false),
44 main_loop_(base::MessageLoopProxy::current()) {
45 scoped_refptr<base::MessageLoopProxy> loop =
46 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
47 loop->PostTask(
48 FROM_HERE,
49 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO,
50 this));
51 }
52
~EstablishRequest()53 BrowserGpuChannelHostFactory::EstablishRequest::~EstablishRequest() {
54 }
55
EstablishOnIO()56 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
57 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
58 if (!host) {
59 host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
60 cause_for_gpu_launch_);
61 if (!host) {
62 FinishOnIO();
63 return;
64 }
65 gpu_host_id_ = host->host_id();
66 reused_gpu_process_ = false;
67 } else {
68 if (reused_gpu_process_) {
69 // We come here if we retried to establish the channel because of a
70 // failure in ChannelEstablishedOnIO, but we ended up with the same
71 // process ID, meaning the failure was not because of a channel error,
72 // but another reason. So fail now.
73 FinishOnIO();
74 return;
75 }
76 reused_gpu_process_ = true;
77 }
78
79 host->EstablishGpuChannel(
80 gpu_client_id_,
81 true,
82 base::Bind(
83 &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO,
84 this));
85 }
86
OnEstablishedOnIO(const IPC::ChannelHandle & channel_handle,const gpu::GPUInfo & gpu_info)87 void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
88 const IPC::ChannelHandle& channel_handle,
89 const gpu::GPUInfo& gpu_info) {
90 if (channel_handle.name.empty() && reused_gpu_process_) {
91 // We failed after re-using the GPU process, but it may have died in the
92 // mean time. Retry to have a chance to create a fresh GPU process.
93 EstablishOnIO();
94 } else {
95 channel_handle_ = channel_handle;
96 gpu_info_ = gpu_info;
97 FinishOnIO();
98 }
99 }
100
FinishOnIO()101 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
102 event_.Signal();
103 main_loop_->PostTask(
104 FROM_HERE,
105 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain,
106 this));
107 }
108
FinishOnMain()109 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
110 if (!finished_) {
111 BrowserGpuChannelHostFactory* factory =
112 BrowserGpuChannelHostFactory::instance();
113 factory->GpuChannelEstablished();
114 finished_ = true;
115 }
116 }
117
Wait()118 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
119 DCHECK(main_loop_->BelongsToCurrentThread());
120 {
121 // We're blocking the UI thread, which is generally undesirable.
122 // In this case we need to wait for this before we can show any UI
123 // /anyway/, so it won't cause additional jank.
124 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
125 TRACE_EVENT0("browser",
126 "BrowserGpuChannelHostFactory::EstablishGpuChannelSync");
127 base::ThreadRestrictions::ScopedAllowWait allow_wait;
128 event_.Wait();
129 }
130 FinishOnMain();
131 }
132
Cancel()133 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
134 DCHECK(main_loop_->BelongsToCurrentThread());
135 finished_ = true;
136 }
137
CanUseForTesting()138 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
139 return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL);
140 }
141
Initialize(bool establish_gpu_channel)142 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {
143 DCHECK(!instance_);
144 instance_ = new BrowserGpuChannelHostFactory(establish_gpu_channel);
145 }
146
Terminate()147 void BrowserGpuChannelHostFactory::Terminate() {
148 DCHECK(instance_);
149 delete instance_;
150 instance_ = NULL;
151 }
152
BrowserGpuChannelHostFactory(bool establish_gpu_channel)153 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory(
154 bool establish_gpu_channel)
155 : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
156 shutdown_event_(new base::WaitableEvent(true, false)),
157 gpu_host_id_(0) {
158 if (establish_gpu_channel) {
159 pending_request_ = new EstablishRequest(
160 CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP, gpu_client_id_, gpu_host_id_);
161 }
162 }
163
~BrowserGpuChannelHostFactory()164 BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
165 DCHECK(IsMainThread());
166 if (pending_request_)
167 pending_request_->Cancel();
168 for (size_t n = 0; n < established_callbacks_.size(); n++)
169 established_callbacks_[n].Run();
170 shutdown_event_->Signal();
171 }
172
IsMainThread()173 bool BrowserGpuChannelHostFactory::IsMainThread() {
174 return BrowserThread::CurrentlyOn(BrowserThread::UI);
175 }
176
GetMainLoop()177 base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() {
178 return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI);
179 }
180
181 scoped_refptr<base::MessageLoopProxy>
GetIOLoopProxy()182 BrowserGpuChannelHostFactory::GetIOLoopProxy() {
183 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
184 }
185
GetShutDownEvent()186 base::WaitableEvent* BrowserGpuChannelHostFactory::GetShutDownEvent() {
187 return shutdown_event_.get();
188 }
189
190 scoped_ptr<base::SharedMemory>
AllocateSharedMemory(size_t size)191 BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) {
192 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
193 if (!shm->CreateAnonymous(size))
194 return scoped_ptr<base::SharedMemory>();
195 return shm.Pass();
196 }
197
CreateViewCommandBufferOnIO(CreateRequest * request,int32 surface_id,const GPUCreateCommandBufferConfig & init_params)198 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
199 CreateRequest* request,
200 int32 surface_id,
201 const GPUCreateCommandBufferConfig& init_params) {
202 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
203 if (!host) {
204 request->event.Signal();
205 return;
206 }
207
208 gfx::GLSurfaceHandle surface =
209 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
210
211 host->CreateViewCommandBuffer(
212 surface,
213 surface_id,
214 gpu_client_id_,
215 init_params,
216 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
217 request));
218 }
219
220 // static
CommandBufferCreatedOnIO(CreateRequest * request,int32 route_id)221 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
222 CreateRequest* request, int32 route_id) {
223 request->route_id = route_id;
224 request->event.Signal();
225 }
226
CreateViewCommandBuffer(int32 surface_id,const GPUCreateCommandBufferConfig & init_params)227 int32 BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
228 int32 surface_id,
229 const GPUCreateCommandBufferConfig& init_params) {
230 CreateRequest request;
231 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
232 &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
233 base::Unretained(this),
234 &request,
235 surface_id,
236 init_params));
237 // We're blocking the UI thread, which is generally undesirable.
238 // In this case we need to wait for this before we can show any UI /anyway/,
239 // so it won't cause additional jank.
240 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
241 TRACE_EVENT0("browser",
242 "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
243 base::ThreadRestrictions::ScopedAllowWait allow_wait;
244 request.event.Wait();
245 return request.route_id;
246 }
247
CreateImageOnIO(gfx::PluginWindowHandle window,int32 image_id,const CreateImageCallback & callback)248 void BrowserGpuChannelHostFactory::CreateImageOnIO(
249 gfx::PluginWindowHandle window,
250 int32 image_id,
251 const CreateImageCallback& callback) {
252 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
253 if (!host) {
254 ImageCreatedOnIO(callback, gfx::Size());
255 return;
256 }
257
258 host->CreateImage(
259 window,
260 gpu_client_id_,
261 image_id,
262 base::Bind(&BrowserGpuChannelHostFactory::ImageCreatedOnIO, callback));
263 }
264
265 // static
ImageCreatedOnIO(const CreateImageCallback & callback,const gfx::Size size)266 void BrowserGpuChannelHostFactory::ImageCreatedOnIO(
267 const CreateImageCallback& callback, const gfx::Size size) {
268 BrowserThread::PostTask(
269 BrowserThread::UI,
270 FROM_HERE,
271 base::Bind(&BrowserGpuChannelHostFactory::OnImageCreated,
272 callback, size));
273 }
274
275 // static
OnImageCreated(const CreateImageCallback & callback,const gfx::Size size)276 void BrowserGpuChannelHostFactory::OnImageCreated(
277 const CreateImageCallback& callback, const gfx::Size size) {
278 callback.Run(size);
279 }
280
CreateImage(gfx::PluginWindowHandle window,int32 image_id,const CreateImageCallback & callback)281 void BrowserGpuChannelHostFactory::CreateImage(
282 gfx::PluginWindowHandle window,
283 int32 image_id,
284 const CreateImageCallback& callback) {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
286 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
287 &BrowserGpuChannelHostFactory::CreateImageOnIO,
288 base::Unretained(this),
289 window,
290 image_id,
291 callback));
292 }
293
DeleteImageOnIO(int32 image_id,int32 sync_point)294 void BrowserGpuChannelHostFactory::DeleteImageOnIO(
295 int32 image_id, int32 sync_point) {
296 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
297 if (!host) {
298 return;
299 }
300
301 host->DeleteImage(gpu_client_id_, image_id, sync_point);
302 }
303
DeleteImage(int32 image_id,int32 sync_point)304 void BrowserGpuChannelHostFactory::DeleteImage(
305 int32 image_id, int32 sync_point) {
306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
307 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
308 &BrowserGpuChannelHostFactory::DeleteImageOnIO,
309 base::Unretained(this),
310 image_id,
311 sync_point));
312 }
313
EstablishGpuChannelSync(CauseForGpuLaunch cause_for_gpu_launch)314 GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
315 CauseForGpuLaunch cause_for_gpu_launch) {
316 EstablishGpuChannel(cause_for_gpu_launch, base::Closure());
317
318 if (pending_request_)
319 pending_request_->Wait();
320
321 return gpu_channel_.get();
322 }
323
EstablishGpuChannel(CauseForGpuLaunch cause_for_gpu_launch,const base::Closure & callback)324 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
325 CauseForGpuLaunch cause_for_gpu_launch,
326 const base::Closure& callback) {
327 if (gpu_channel_.get() && gpu_channel_->IsLost()) {
328 DCHECK(!pending_request_);
329 // Recreate the channel if it has been lost.
330 gpu_channel_ = NULL;
331 }
332
333 if (!gpu_channel_ && !pending_request_) {
334 // We should only get here if the context was lost.
335 pending_request_ = new EstablishRequest(
336 cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);
337 }
338
339 if (!callback.is_null()) {
340 if (gpu_channel_)
341 callback.Run();
342 else
343 established_callbacks_.push_back(callback);
344 }
345 }
346
GetGpuChannel()347 GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() {
348 if (gpu_channel_ && !gpu_channel_->IsLost())
349 return gpu_channel_;
350
351 return NULL;
352 }
353
GpuChannelEstablished()354 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
355 DCHECK(IsMainThread());
356 DCHECK(pending_request_);
357 if (pending_request_->channel_handle().name.empty())
358 return;
359
360 GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
361 gpu_channel_ = GpuChannelHost::Create(this,
362 pending_request_->gpu_host_id(),
363 pending_request_->gpu_info(),
364 pending_request_->channel_handle());
365 gpu_host_id_ = pending_request_->gpu_host_id();
366 pending_request_ = NULL;
367
368 for (size_t n = 0; n < established_callbacks_.size(); n++)
369 established_callbacks_[n].Run();
370
371 established_callbacks_.clear();
372 }
373
374 scoped_ptr<gfx::GpuMemoryBuffer>
AllocateGpuMemoryBuffer(size_t width,size_t height,unsigned internalformat)375 BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(
376 size_t width,
377 size_t height,
378 unsigned internalformat) {
379 if (!GpuMemoryBufferImpl::IsFormatValid(internalformat))
380 return scoped_ptr<gfx::GpuMemoryBuffer>();
381
382 size_t size = width * height *
383 GpuMemoryBufferImpl::BytesPerPixel(internalformat);
384 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
385 if (!shm->CreateAnonymous(size))
386 return scoped_ptr<gfx::GpuMemoryBuffer>();
387
388 scoped_ptr<GpuMemoryBufferImplShm> buffer(
389 new GpuMemoryBufferImplShm(gfx::Size(width, height), internalformat));
390 if (!buffer->InitializeFromSharedMemory(shm.Pass()))
391 return scoped_ptr<gfx::GpuMemoryBuffer>();
392
393 return buffer.PassAs<gfx::GpuMemoryBuffer>();
394 }
395
396 // static
AddFilterOnIO(int host_id,scoped_refptr<IPC::ChannelProxy::MessageFilter> filter)397 void BrowserGpuChannelHostFactory::AddFilterOnIO(
398 int host_id,
399 scoped_refptr<IPC::ChannelProxy::MessageFilter> filter) {
400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
401
402 GpuProcessHost* host = GpuProcessHost::FromID(host_id);
403 if (host)
404 host->AddFilter(filter.get());
405 }
406
SetHandlerForControlMessages(const uint32 * message_ids,size_t num_messages,const base::Callback<void (const IPC::Message &)> & handler,base::TaskRunner * target_task_runner)407 void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
408 const uint32* message_ids,
409 size_t num_messages,
410 const base::Callback<void(const IPC::Message&)>& handler,
411 base::TaskRunner* target_task_runner) {
412 DCHECK(gpu_host_id_)
413 << "Do not call"
414 << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
415 << " until the GpuProcessHost has been set up.";
416
417 scoped_refptr<IPC::ForwardingMessageFilter> filter =
418 new IPC::ForwardingMessageFilter(message_ids,
419 num_messages,
420 target_task_runner);
421 filter->AddRoute(MSG_ROUTING_CONTROL, handler);
422
423 GetIOLoopProxy()->PostTask(
424 FROM_HERE,
425 base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO,
426 gpu_host_id_,
427 filter));
428 }
429
430 } // namespace content
431