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/synchronization/waitable_event.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "content/browser/gpu/gpu_data_manager_impl.h"
12 #include "content/browser/gpu/gpu_process_host.h"
13 #include "content/browser/gpu/gpu_surface_tracker.h"
14 #include "content/common/child_process_host_impl.h"
15 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
16 #include "content/common/gpu/gpu_messages.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/gpu_data_manager.h"
19 #include "content/public/common/content_client.h"
20 #include "ipc/ipc_channel_handle.h"
21 #include "ipc/ipc_forwarding_message_filter.h"
22 #include "ipc/message_filter.h"
23
24 namespace content {
25
26 BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL;
27
28 struct BrowserGpuChannelHostFactory::CreateRequest {
CreateRequestcontent::BrowserGpuChannelHostFactory::CreateRequest29 CreateRequest()
30 : event(true, false), gpu_host_id(0), route_id(MSG_ROUTING_NONE),
31 result(CREATE_COMMAND_BUFFER_FAILED) {}
~CreateRequestcontent::BrowserGpuChannelHostFactory::CreateRequest32 ~CreateRequest() {}
33 base::WaitableEvent event;
34 int gpu_host_id;
35 int32 route_id;
36 CreateCommandBufferResult result;
37 };
38
39 class BrowserGpuChannelHostFactory::EstablishRequest
40 : public base::RefCountedThreadSafe<EstablishRequest> {
41 public:
42 static scoped_refptr<EstablishRequest> Create(CauseForGpuLaunch cause,
43 int gpu_client_id,
44 int gpu_host_id);
45 void Wait();
46 void Cancel();
47
gpu_host_id()48 int gpu_host_id() { return gpu_host_id_; }
channel_handle()49 IPC::ChannelHandle& channel_handle() { return channel_handle_; }
gpu_info()50 gpu::GPUInfo gpu_info() { return gpu_info_; }
51
52 private:
53 friend class base::RefCountedThreadSafe<EstablishRequest>;
54 explicit EstablishRequest(CauseForGpuLaunch cause,
55 int gpu_client_id,
56 int gpu_host_id);
~EstablishRequest()57 ~EstablishRequest() {}
58 void EstablishOnIO();
59 void OnEstablishedOnIO(const IPC::ChannelHandle& channel_handle,
60 const gpu::GPUInfo& gpu_info);
61 void FinishOnIO();
62 void FinishOnMain();
63
64 base::WaitableEvent event_;
65 CauseForGpuLaunch cause_for_gpu_launch_;
66 const int gpu_client_id_;
67 int gpu_host_id_;
68 bool reused_gpu_process_;
69 IPC::ChannelHandle channel_handle_;
70 gpu::GPUInfo gpu_info_;
71 bool finished_;
72 scoped_refptr<base::MessageLoopProxy> main_loop_;
73 };
74
75 scoped_refptr<BrowserGpuChannelHostFactory::EstablishRequest>
Create(CauseForGpuLaunch cause,int gpu_client_id,int gpu_host_id)76 BrowserGpuChannelHostFactory::EstablishRequest::Create(CauseForGpuLaunch cause,
77 int gpu_client_id,
78 int gpu_host_id) {
79 scoped_refptr<EstablishRequest> establish_request =
80 new EstablishRequest(cause, gpu_client_id, gpu_host_id);
81 scoped_refptr<base::MessageLoopProxy> loop =
82 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
83 // PostTask outside the constructor to ensure at least one reference exists.
84 loop->PostTask(
85 FROM_HERE,
86 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO,
87 establish_request));
88 return establish_request;
89 }
90
EstablishRequest(CauseForGpuLaunch cause,int gpu_client_id,int gpu_host_id)91 BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
92 CauseForGpuLaunch cause,
93 int gpu_client_id,
94 int gpu_host_id)
95 : event_(false, false),
96 cause_for_gpu_launch_(cause),
97 gpu_client_id_(gpu_client_id),
98 gpu_host_id_(gpu_host_id),
99 reused_gpu_process_(false),
100 finished_(false),
101 main_loop_(base::MessageLoopProxy::current()) {
102 }
103
EstablishOnIO()104 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
105 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
106 if (!host) {
107 host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
108 cause_for_gpu_launch_);
109 if (!host) {
110 LOG(ERROR) << "Failed to launch GPU process.";
111 FinishOnIO();
112 return;
113 }
114 gpu_host_id_ = host->host_id();
115 reused_gpu_process_ = false;
116 } else {
117 if (reused_gpu_process_) {
118 // We come here if we retried to establish the channel because of a
119 // failure in ChannelEstablishedOnIO, but we ended up with the same
120 // process ID, meaning the failure was not because of a channel error,
121 // but another reason. So fail now.
122 LOG(ERROR) << "Failed to create channel.";
123 FinishOnIO();
124 return;
125 }
126 reused_gpu_process_ = true;
127 }
128
129 host->EstablishGpuChannel(
130 gpu_client_id_,
131 true,
132 base::Bind(
133 &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO,
134 this));
135 }
136
OnEstablishedOnIO(const IPC::ChannelHandle & channel_handle,const gpu::GPUInfo & gpu_info)137 void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
138 const IPC::ChannelHandle& channel_handle,
139 const gpu::GPUInfo& gpu_info) {
140 if (channel_handle.name.empty() && reused_gpu_process_) {
141 // We failed after re-using the GPU process, but it may have died in the
142 // mean time. Retry to have a chance to create a fresh GPU process.
143 DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "
144 "restart GPU process.";
145 EstablishOnIO();
146 } else {
147 channel_handle_ = channel_handle;
148 gpu_info_ = gpu_info;
149 FinishOnIO();
150 }
151 }
152
FinishOnIO()153 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {
154 event_.Signal();
155 main_loop_->PostTask(
156 FROM_HERE,
157 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain,
158 this));
159 }
160
FinishOnMain()161 void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {
162 if (!finished_) {
163 BrowserGpuChannelHostFactory* factory =
164 BrowserGpuChannelHostFactory::instance();
165 factory->GpuChannelEstablished();
166 finished_ = true;
167 }
168 }
169
Wait()170 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
171 DCHECK(main_loop_->BelongsToCurrentThread());
172 {
173 // We're blocking the UI thread, which is generally undesirable.
174 // In this case we need to wait for this before we can show any UI
175 // /anyway/, so it won't cause additional jank.
176 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
177 TRACE_EVENT0("browser",
178 "BrowserGpuChannelHostFactory::EstablishGpuChannelSync");
179 base::ThreadRestrictions::ScopedAllowWait allow_wait;
180 event_.Wait();
181 }
182 FinishOnMain();
183 }
184
Cancel()185 void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() {
186 DCHECK(main_loop_->BelongsToCurrentThread());
187 finished_ = true;
188 }
189
CanUseForTesting()190 bool BrowserGpuChannelHostFactory::CanUseForTesting() {
191 return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL);
192 }
193
Initialize(bool establish_gpu_channel)194 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {
195 DCHECK(!instance_);
196 instance_ = new BrowserGpuChannelHostFactory();
197 if (establish_gpu_channel) {
198 instance_->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP,
199 base::Closure());
200 }
201 }
202
Terminate()203 void BrowserGpuChannelHostFactory::Terminate() {
204 DCHECK(instance_);
205 delete instance_;
206 instance_ = NULL;
207 }
208
BrowserGpuChannelHostFactory()209 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
210 : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
211 shutdown_event_(new base::WaitableEvent(true, false)),
212 gpu_host_id_(0),
213 next_create_gpu_memory_buffer_request_id_(0) {
214 }
215
~BrowserGpuChannelHostFactory()216 BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
217 DCHECK(IsMainThread());
218 if (pending_request_)
219 pending_request_->Cancel();
220 for (size_t n = 0; n < established_callbacks_.size(); n++)
221 established_callbacks_[n].Run();
222 shutdown_event_->Signal();
223 }
224
IsMainThread()225 bool BrowserGpuChannelHostFactory::IsMainThread() {
226 return BrowserThread::CurrentlyOn(BrowserThread::UI);
227 }
228
GetMainLoop()229 base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() {
230 return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI);
231 }
232
233 scoped_refptr<base::MessageLoopProxy>
GetIOLoopProxy()234 BrowserGpuChannelHostFactory::GetIOLoopProxy() {
235 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
236 }
237
238 scoped_ptr<base::SharedMemory>
AllocateSharedMemory(size_t size)239 BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) {
240 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
241 if (!shm->CreateAnonymous(size))
242 return scoped_ptr<base::SharedMemory>();
243 return shm.Pass();
244 }
245
CreateViewCommandBufferOnIO(CreateRequest * request,int32 surface_id,const GPUCreateCommandBufferConfig & init_params)246 void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
247 CreateRequest* request,
248 int32 surface_id,
249 const GPUCreateCommandBufferConfig& init_params) {
250 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
251 if (!host) {
252 request->event.Signal();
253 return;
254 }
255
256 gfx::GLSurfaceHandle surface =
257 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
258
259 host->CreateViewCommandBuffer(
260 surface,
261 surface_id,
262 gpu_client_id_,
263 init_params,
264 request->route_id,
265 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
266 request));
267 }
268
269 // static
CommandBufferCreatedOnIO(CreateRequest * request,CreateCommandBufferResult result)270 void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
271 CreateRequest* request, CreateCommandBufferResult result) {
272 request->result = result;
273 request->event.Signal();
274 }
275
CreateViewCommandBuffer(int32 surface_id,const GPUCreateCommandBufferConfig & init_params,int32 route_id)276 CreateCommandBufferResult BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
277 int32 surface_id,
278 const GPUCreateCommandBufferConfig& init_params,
279 int32 route_id) {
280 CreateRequest request;
281 request.route_id = route_id;
282 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
283 &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
284 base::Unretained(this),
285 &request,
286 surface_id,
287 init_params));
288 // We're blocking the UI thread, which is generally undesirable.
289 // In this case we need to wait for this before we can show any UI /anyway/,
290 // so it won't cause additional jank.
291 // TODO(piman): Make this asynchronous (http://crbug.com/125248).
292 TRACE_EVENT0("browser",
293 "BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
294 base::ThreadRestrictions::ScopedAllowWait allow_wait;
295 request.event.Wait();
296 return request.result;
297 }
298
CreateImageOnIO(gfx::PluginWindowHandle window,int32 image_id,const CreateImageCallback & callback)299 void BrowserGpuChannelHostFactory::CreateImageOnIO(
300 gfx::PluginWindowHandle window,
301 int32 image_id,
302 const CreateImageCallback& callback) {
303 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
304 if (!host) {
305 ImageCreatedOnIO(callback, gfx::Size());
306 return;
307 }
308
309 host->CreateImage(
310 window,
311 gpu_client_id_,
312 image_id,
313 base::Bind(&BrowserGpuChannelHostFactory::ImageCreatedOnIO, callback));
314 }
315
316 // static
ImageCreatedOnIO(const CreateImageCallback & callback,const gfx::Size size)317 void BrowserGpuChannelHostFactory::ImageCreatedOnIO(
318 const CreateImageCallback& callback, const gfx::Size size) {
319 BrowserThread::PostTask(
320 BrowserThread::UI,
321 FROM_HERE,
322 base::Bind(&BrowserGpuChannelHostFactory::OnImageCreated,
323 callback, size));
324 }
325
326 // static
OnImageCreated(const CreateImageCallback & callback,const gfx::Size size)327 void BrowserGpuChannelHostFactory::OnImageCreated(
328 const CreateImageCallback& callback, const gfx::Size size) {
329 callback.Run(size);
330 }
331
CreateImage(gfx::PluginWindowHandle window,int32 image_id,const CreateImageCallback & callback)332 void BrowserGpuChannelHostFactory::CreateImage(
333 gfx::PluginWindowHandle window,
334 int32 image_id,
335 const CreateImageCallback& callback) {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
338 &BrowserGpuChannelHostFactory::CreateImageOnIO,
339 base::Unretained(this),
340 window,
341 image_id,
342 callback));
343 }
344
DeleteImageOnIO(int32 image_id,int32 sync_point)345 void BrowserGpuChannelHostFactory::DeleteImageOnIO(
346 int32 image_id, int32 sync_point) {
347 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
348 if (!host) {
349 return;
350 }
351
352 host->DeleteImage(gpu_client_id_, image_id, sync_point);
353 }
354
DeleteImage(int32 image_id,int32 sync_point)355 void BrowserGpuChannelHostFactory::DeleteImage(
356 int32 image_id, int32 sync_point) {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
359 &BrowserGpuChannelHostFactory::DeleteImageOnIO,
360 base::Unretained(this),
361 image_id,
362 sync_point));
363 }
364
EstablishGpuChannelSync(CauseForGpuLaunch cause_for_gpu_launch)365 GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync(
366 CauseForGpuLaunch cause_for_gpu_launch) {
367 EstablishGpuChannel(cause_for_gpu_launch, base::Closure());
368
369 if (pending_request_)
370 pending_request_->Wait();
371
372 return gpu_channel_.get();
373 }
374
EstablishGpuChannel(CauseForGpuLaunch cause_for_gpu_launch,const base::Closure & callback)375 void BrowserGpuChannelHostFactory::EstablishGpuChannel(
376 CauseForGpuLaunch cause_for_gpu_launch,
377 const base::Closure& callback) {
378 if (gpu_channel_.get() && gpu_channel_->IsLost()) {
379 DCHECK(!pending_request_);
380 // Recreate the channel if it has been lost.
381 gpu_channel_ = NULL;
382 }
383
384 if (!gpu_channel_ && !pending_request_) {
385 // We should only get here if the context was lost.
386 pending_request_ = EstablishRequest::Create(
387 cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);
388 }
389
390 if (!callback.is_null()) {
391 if (gpu_channel_)
392 callback.Run();
393 else
394 established_callbacks_.push_back(callback);
395 }
396 }
397
GetGpuChannel()398 GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() {
399 if (gpu_channel_ && !gpu_channel_->IsLost())
400 return gpu_channel_;
401
402 return NULL;
403 }
404
GpuChannelEstablished()405 void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
406 DCHECK(IsMainThread());
407 DCHECK(pending_request_);
408 if (pending_request_->channel_handle().name.empty()) {
409 DCHECK(!gpu_channel_);
410 } else {
411 GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
412 gpu_channel_ = GpuChannelHost::Create(this,
413 pending_request_->gpu_info(),
414 pending_request_->channel_handle(),
415 shutdown_event_.get());
416 }
417 gpu_host_id_ = pending_request_->gpu_host_id();
418 pending_request_ = NULL;
419
420 for (size_t n = 0; n < established_callbacks_.size(); n++)
421 established_callbacks_[n].Run();
422
423 established_callbacks_.clear();
424 }
425
426 scoped_ptr<gfx::GpuMemoryBuffer>
AllocateGpuMemoryBuffer(size_t width,size_t height,unsigned internalformat,unsigned usage)427 BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(size_t width,
428 size_t height,
429 unsigned internalformat,
430 unsigned usage) {
431 if (!GpuMemoryBufferImpl::IsFormatValid(internalformat) ||
432 !GpuMemoryBufferImpl::IsUsageValid(usage))
433 return scoped_ptr<gfx::GpuMemoryBuffer>();
434
435 return GpuMemoryBufferImpl::Create(gfx::Size(width, height),
436 internalformat,
437 usage).PassAs<gfx::GpuMemoryBuffer>();
438 }
439
440 // static
AddFilterOnIO(int host_id,scoped_refptr<IPC::MessageFilter> filter)441 void BrowserGpuChannelHostFactory::AddFilterOnIO(
442 int host_id,
443 scoped_refptr<IPC::MessageFilter> filter) {
444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
445
446 GpuProcessHost* host = GpuProcessHost::FromID(host_id);
447 if (host)
448 host->AddFilter(filter.get());
449 }
450
SetHandlerForControlMessages(const uint32 * message_ids,size_t num_messages,const base::Callback<void (const IPC::Message &)> & handler,base::TaskRunner * target_task_runner)451 void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
452 const uint32* message_ids,
453 size_t num_messages,
454 const base::Callback<void(const IPC::Message&)>& handler,
455 base::TaskRunner* target_task_runner) {
456 DCHECK(gpu_host_id_)
457 << "Do not call"
458 << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
459 << " until the GpuProcessHost has been set up.";
460
461 scoped_refptr<IPC::ForwardingMessageFilter> filter =
462 new IPC::ForwardingMessageFilter(message_ids,
463 num_messages,
464 target_task_runner);
465 filter->AddRoute(MSG_ROUTING_CONTROL, handler);
466
467 GetIOLoopProxy()->PostTask(
468 FROM_HERE,
469 base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO,
470 gpu_host_id_,
471 filter));
472 }
473
CreateGpuMemoryBuffer(const gfx::GpuMemoryBufferHandle & handle,const gfx::Size & size,unsigned internalformat,unsigned usage,const CreateGpuMemoryBufferCallback & callback)474 void BrowserGpuChannelHostFactory::CreateGpuMemoryBuffer(
475 const gfx::GpuMemoryBufferHandle& handle,
476 const gfx::Size& size,
477 unsigned internalformat,
478 unsigned usage,
479 const CreateGpuMemoryBufferCallback& callback) {
480 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
481 uint32 request_id = next_create_gpu_memory_buffer_request_id_++;
482 create_gpu_memory_buffer_requests_[request_id] = callback;
483 GetIOLoopProxy()->PostTask(
484 FROM_HERE,
485 base::Bind(&BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO,
486 base::Unretained(this),
487 handle,
488 size,
489 internalformat,
490 usage,
491 request_id));
492 }
493
DestroyGpuMemoryBuffer(const gfx::GpuMemoryBufferHandle & handle,int32 sync_point)494 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBuffer(
495 const gfx::GpuMemoryBufferHandle& handle,
496 int32 sync_point) {
497 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
498 GetIOLoopProxy()->PostTask(
499 FROM_HERE,
500 base::Bind(&BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO,
501 base::Unretained(this),
502 handle,
503 sync_point));
504 }
505
CreateGpuMemoryBufferOnIO(const gfx::GpuMemoryBufferHandle & handle,const gfx::Size & size,unsigned internalformat,unsigned usage,uint32 request_id)506 void BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO(
507 const gfx::GpuMemoryBufferHandle& handle,
508 const gfx::Size& size,
509 unsigned internalformat,
510 unsigned usage,
511 uint32 request_id) {
512 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
513 if (!host) {
514 GpuMemoryBufferCreatedOnIO(request_id, gfx::GpuMemoryBufferHandle());
515 return;
516 }
517
518 host->CreateGpuMemoryBuffer(
519 handle,
520 size,
521 internalformat,
522 usage,
523 base::Bind(&BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO,
524 base::Unretained(this),
525 request_id));
526 }
527
GpuMemoryBufferCreatedOnIO(uint32 request_id,const gfx::GpuMemoryBufferHandle & handle)528 void BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO(
529 uint32 request_id,
530 const gfx::GpuMemoryBufferHandle& handle) {
531 BrowserThread::PostTask(
532 BrowserThread::UI,
533 FROM_HERE,
534 base::Bind(&BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated,
535 base::Unretained(this),
536 request_id,
537 handle));
538 }
539
OnGpuMemoryBufferCreated(uint32 request_id,const gfx::GpuMemoryBufferHandle & handle)540 void BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated(
541 uint32 request_id,
542 const gfx::GpuMemoryBufferHandle& handle) {
543 CreateGpuMemoryBufferCallbackMap::iterator iter =
544 create_gpu_memory_buffer_requests_.find(request_id);
545 DCHECK(iter != create_gpu_memory_buffer_requests_.end());
546 iter->second.Run(handle);
547 create_gpu_memory_buffer_requests_.erase(iter);
548 }
549
DestroyGpuMemoryBufferOnIO(const gfx::GpuMemoryBufferHandle & handle,int32 sync_point)550 void BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO(
551 const gfx::GpuMemoryBufferHandle& handle,
552 int32 sync_point) {
553 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
554 if (!host)
555 return;
556
557 host->DestroyGpuMemoryBuffer(handle, sync_point);
558 }
559
560 } // namespace content
561