1 // Copyright 2014 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_memory_buffer_manager.h"
7 #include "base/atomic_sequence_num.h"
9 #include "base/command_line.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "base/trace_event/process_memory_dump.h"
14 #include "base/trace_event/trace_event.h"
15 #include "content/browser/gpu/gpu_process_host.h"
16 #include "content/common/child_process_host_impl.h"
17 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
18 #include "content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h"
19 #include "content/common/gpu/gpu_memory_buffer_factory_shared_memory.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/common/content_switches.h"
22 #include "gpu/GLES2/gl2extchromium.h"
24 #if defined(OS_MACOSX)
25 #include "content/common/gpu/gpu_memory_buffer_factory_io_surface.h"
28 #if defined(OS_ANDROID)
29 #include "content/common/gpu/gpu_memory_buffer_factory_surface_texture.h"
32 #if defined(USE_OZONE)
33 #include "content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h"
39 void GpuMemoryBufferDeleted(
40 scoped_refptr
<base::SingleThreadTaskRunner
> destruction_task_runner
,
41 const GpuMemoryBufferImpl::DestructionCallback
& destruction_callback
,
43 destruction_task_runner
->PostTask(
44 FROM_HERE
, base::Bind(destruction_callback
, sync_point
));
47 bool IsGpuMemoryBufferFactoryConfigurationSupported(
48 gfx::GpuMemoryBufferType type
,
49 const GpuMemoryBufferFactory::Configuration
& configuration
) {
51 case gfx::SHARED_MEMORY_BUFFER
:
52 return GpuMemoryBufferFactorySharedMemory::
53 IsGpuMemoryBufferConfigurationSupported(configuration
.format
,
55 #if defined(OS_MACOSX)
56 case gfx::IO_SURFACE_BUFFER
:
57 return GpuMemoryBufferFactoryIOSurface::
58 IsGpuMemoryBufferConfigurationSupported(configuration
.format
,
61 #if defined(OS_ANDROID)
62 case gfx::SURFACE_TEXTURE_BUFFER
:
63 return GpuMemoryBufferFactorySurfaceTexture::
64 IsGpuMemoryBufferConfigurationSupported(configuration
.format
,
67 #if defined(USE_OZONE)
68 case gfx::OZONE_NATIVE_PIXMAP
:
69 return GpuMemoryBufferFactoryOzoneNativePixmap::
70 IsGpuMemoryBufferConfigurationSupported(configuration
.format
,
79 gfx::GpuMemoryBufferType
GetGpuMemoryBufferFactoryType() {
80 std::vector
<gfx::GpuMemoryBufferType
> supported_types
;
81 GpuMemoryBufferFactory::GetSupportedTypes(&supported_types
);
82 DCHECK(!supported_types
.empty());
84 // The GPU service will always use the preferred type.
85 return supported_types
[0];
88 std::vector
<GpuMemoryBufferFactory::Configuration
>
89 GetSupportedGpuMemoryBufferConfigurations(gfx::GpuMemoryBufferType type
) {
90 std::vector
<GpuMemoryBufferFactory::Configuration
> configurations
;
91 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
92 switches::kEnableNativeGpuMemoryBuffers
)) {
93 const GpuMemoryBufferFactory::Configuration kNativeConfigurations
[] = {
94 {gfx::GpuMemoryBuffer::R_8
, gfx::GpuMemoryBuffer::MAP
},
95 {gfx::GpuMemoryBuffer::R_8
, gfx::GpuMemoryBuffer::PERSISTENT_MAP
},
96 {gfx::GpuMemoryBuffer::RGBA_4444
, gfx::GpuMemoryBuffer::MAP
},
97 {gfx::GpuMemoryBuffer::RGBA_4444
, gfx::GpuMemoryBuffer::PERSISTENT_MAP
},
98 {gfx::GpuMemoryBuffer::RGBA_8888
, gfx::GpuMemoryBuffer::MAP
},
99 {gfx::GpuMemoryBuffer::RGBA_8888
, gfx::GpuMemoryBuffer::PERSISTENT_MAP
},
100 {gfx::GpuMemoryBuffer::BGRA_8888
, gfx::GpuMemoryBuffer::MAP
},
101 {gfx::GpuMemoryBuffer::BGRA_8888
,
102 gfx::GpuMemoryBuffer::PERSISTENT_MAP
}};
103 for (auto& configuration
: kNativeConfigurations
) {
104 if (IsGpuMemoryBufferFactoryConfigurationSupported(type
, configuration
))
105 configurations
.push_back(configuration
);
109 #if defined(USE_OZONE) || defined(OS_MACOSX)
110 const GpuMemoryBufferFactory::Configuration kScanoutConfigurations
[] = {
111 {gfx::GpuMemoryBuffer::BGRA_8888
, gfx::GpuMemoryBuffer::SCANOUT
},
112 {gfx::GpuMemoryBuffer::RGBX_8888
, gfx::GpuMemoryBuffer::SCANOUT
}};
113 for (auto& configuration
: kScanoutConfigurations
) {
114 if (IsGpuMemoryBufferFactoryConfigurationSupported(type
, configuration
))
115 configurations
.push_back(configuration
);
119 return configurations
;
122 BrowserGpuMemoryBufferManager
* g_gpu_memory_buffer_manager
= nullptr;
124 // Global atomic to generate gpu memory buffer unique IDs.
125 base::StaticAtomicSequenceNumber g_next_gpu_memory_buffer_id
;
129 struct BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferRequest
{
130 AllocateGpuMemoryBufferRequest(const gfx::Size
& size
,
131 gfx::GpuMemoryBuffer::Format format
,
132 gfx::GpuMemoryBuffer::Usage usage
,
135 : event(true, false),
139 client_id(client_id
),
140 surface_id(surface_id
) {}
141 ~AllocateGpuMemoryBufferRequest() {}
142 base::WaitableEvent event
;
144 gfx::GpuMemoryBuffer::Format format
;
145 gfx::GpuMemoryBuffer::Usage usage
;
148 scoped_ptr
<gfx::GpuMemoryBuffer
> result
;
151 BrowserGpuMemoryBufferManager::BrowserGpuMemoryBufferManager(int gpu_client_id
)
152 : factory_type_(GetGpuMemoryBufferFactoryType()),
153 supported_configurations_(
154 GetSupportedGpuMemoryBufferConfigurations(factory_type_
)),
155 gpu_client_id_(gpu_client_id
),
157 DCHECK(!g_gpu_memory_buffer_manager
);
158 g_gpu_memory_buffer_manager
= this;
161 BrowserGpuMemoryBufferManager::~BrowserGpuMemoryBufferManager() {
162 g_gpu_memory_buffer_manager
= nullptr;
166 BrowserGpuMemoryBufferManager
* BrowserGpuMemoryBufferManager::current() {
167 return g_gpu_memory_buffer_manager
;
171 uint32
BrowserGpuMemoryBufferManager::GetImageTextureTarget(
172 gfx::GpuMemoryBuffer::Format format
,
173 gfx::GpuMemoryBuffer::Usage usage
) {
174 gfx::GpuMemoryBufferType type
= GetGpuMemoryBufferFactoryType();
175 for (auto& configuration
: GetSupportedGpuMemoryBufferConfigurations(type
)) {
176 if (configuration
.format
!= format
|| configuration
.usage
!= usage
)
180 case gfx::SURFACE_TEXTURE_BUFFER
:
181 case gfx::OZONE_NATIVE_PIXMAP
:
182 // GPU memory buffers that are shared with the GL using EGLImages
183 // require TEXTURE_EXTERNAL_OES.
184 return GL_TEXTURE_EXTERNAL_OES
;
185 case gfx::IO_SURFACE_BUFFER
:
186 // IOSurface backed images require GL_TEXTURE_RECTANGLE_ARB.
187 return GL_TEXTURE_RECTANGLE_ARB
;
189 return GL_TEXTURE_2D
;
193 return GL_TEXTURE_2D
;
196 scoped_ptr
<gfx::GpuMemoryBuffer
>
197 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
198 const gfx::Size
& size
,
199 gfx::GpuMemoryBuffer::Format format
,
200 gfx::GpuMemoryBuffer::Usage usage
) {
201 return AllocateGpuMemoryBufferForSurface(size
, format
, usage
, 0);
204 scoped_ptr
<gfx::GpuMemoryBuffer
>
205 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForScanout(
206 const gfx::Size
& size
,
207 gfx::GpuMemoryBuffer::Format format
,
209 DCHECK_GT(surface_id
, 0);
210 return AllocateGpuMemoryBufferForSurface(
211 size
, format
, gfx::GpuMemoryBuffer::SCANOUT
, surface_id
);
214 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForChildProcess(
215 const gfx::Size
& size
,
216 gfx::GpuMemoryBuffer::Format format
,
217 gfx::GpuMemoryBuffer::Usage usage
,
218 base::ProcessHandle child_process_handle
,
220 const AllocationCallback
& callback
) {
221 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
223 gfx::GpuMemoryBufferId new_id
= g_next_gpu_memory_buffer_id
.GetNext();
225 // Use service side allocation if this is a supported configuration.
226 if (IsGpuMemoryBufferConfigurationSupported(format
, usage
)) {
227 AllocateGpuMemoryBufferOnIO(new_id
, size
, format
, usage
, child_client_id
, 0,
232 // Early out if we cannot fallback to shared memory buffer.
233 if (!GpuMemoryBufferImplSharedMemory::IsFormatSupported(format
) ||
234 !GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage
) ||
235 !GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size
, format
)) {
236 callback
.Run(gfx::GpuMemoryBufferHandle());
240 BufferMap
& buffers
= clients_
[child_client_id
];
241 DCHECK(buffers
.find(new_id
) == buffers
.end());
243 // Allocate shared memory buffer as fallback.
245 BufferInfo(size
, gfx::SHARED_MEMORY_BUFFER
, format
, usage
, 0);
246 callback
.Run(GpuMemoryBufferImplSharedMemory::AllocateForChildProcess(
247 new_id
, size
, format
, child_process_handle
));
250 gfx::GpuMemoryBuffer
*
251 BrowserGpuMemoryBufferManager::GpuMemoryBufferFromClientBuffer(
252 ClientBuffer buffer
) {
253 return GpuMemoryBufferImpl::FromClientBuffer(buffer
);
256 void BrowserGpuMemoryBufferManager::SetDestructionSyncPoint(
257 gfx::GpuMemoryBuffer
* buffer
,
259 static_cast<GpuMemoryBufferImpl
*>(buffer
)
260 ->set_destruction_sync_point(sync_point
);
263 bool BrowserGpuMemoryBufferManager::OnMemoryDump(
264 base::trace_event::ProcessMemoryDump
* pmd
) {
265 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
267 for (const auto& client
: clients_
) {
268 int client_id
= client
.first
;
270 for (const auto& buffer
: client
.second
) {
271 if (buffer
.second
.type
== gfx::EMPTY_BUFFER
)
274 gfx::GpuMemoryBufferId buffer_id
= buffer
.first
;
275 base::trace_event::MemoryAllocatorDump
* dump
=
276 pmd
->CreateAllocatorDump(base::StringPrintf(
277 "gpumemorybuffer/client_%d/buffer_%d", client_id
, buffer_id
));
281 size_t buffer_size_in_bytes
= 0;
282 // Note: BufferSizeInBytes returns an approximated size for the buffer
283 // but the factory can be made to return the exact size if this
284 // approximation is not good enough.
285 bool valid_size
= GpuMemoryBufferImpl::BufferSizeInBytes(
286 buffer
.second
.size
, buffer
.second
.format
, &buffer_size_in_bytes
);
289 dump
->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize
,
290 base::trace_event::MemoryAllocatorDump::kUnitsBytes
,
291 buffer_size_in_bytes
);
293 // Create the cross-process ownership edge. If the client creates a
294 // corresponding dump for the same buffer, this will avoid to
295 // double-count them in tracing. If, instead, no other process will emit a
296 // dump with the same guid, the segment will be accounted to the browser.
297 const uint64 client_tracing_process_id
=
298 ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(
300 base::trace_event::MemoryAllocatorDumpGuid shared_buffer_guid
=
301 gfx::GetGpuMemoryBufferGUIDForTracing(client_tracing_process_id
,
303 pmd
->CreateSharedGlobalAllocatorDump(shared_buffer_guid
);
304 pmd
->AddOwnershipEdge(dump
->guid(), shared_buffer_guid
);
311 void BrowserGpuMemoryBufferManager::ChildProcessDeletedGpuMemoryBuffer(
312 gfx::GpuMemoryBufferId id
,
313 base::ProcessHandle child_process_handle
,
316 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
318 DestroyGpuMemoryBufferOnIO(id
, child_client_id
, sync_point
);
321 void BrowserGpuMemoryBufferManager::ProcessRemoved(
322 base::ProcessHandle process_handle
,
324 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
326 ClientMap::iterator client_it
= clients_
.find(client_id
);
327 if (client_it
== clients_
.end())
330 for (const auto& buffer
: client_it
->second
) {
331 // This might happen if buffer is currenlty in the process of being
332 // allocated. The buffer will in that case be cleaned up when allocation
334 if (buffer
.second
.type
== gfx::EMPTY_BUFFER
)
337 GpuProcessHost
* host
= GpuProcessHost::FromID(buffer
.second
.gpu_host_id
);
339 host
->DestroyGpuMemoryBuffer(buffer
.first
, client_id
, 0);
342 clients_
.erase(client_it
);
345 scoped_ptr
<gfx::GpuMemoryBuffer
>
346 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurface(
347 const gfx::Size
& size
,
348 gfx::GpuMemoryBuffer::Format format
,
349 gfx::GpuMemoryBuffer::Usage usage
,
351 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO
));
353 AllocateGpuMemoryBufferRequest
request(size
, format
, usage
, gpu_client_id_
,
355 BrowserThread::PostTask(
356 BrowserThread::IO
, FROM_HERE
,
358 &BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurfaceOnIO
,
359 base::Unretained(this), // Safe as we wait for result below.
360 base::Unretained(&request
)));
362 // We're blocking the UI thread, which is generally undesirable.
365 "BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurface");
366 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
367 request
.event
.Wait();
368 return request
.result
.Pass();
371 bool BrowserGpuMemoryBufferManager::IsGpuMemoryBufferConfigurationSupported(
372 gfx::GpuMemoryBuffer::Format format
,
373 gfx::GpuMemoryBuffer::Usage usage
) const {
374 for (auto& configuration
: supported_configurations_
) {
375 if (configuration
.format
== format
&& configuration
.usage
== usage
)
381 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurfaceOnIO(
382 AllocateGpuMemoryBufferRequest
* request
) {
383 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
385 gfx::GpuMemoryBufferId new_id
= g_next_gpu_memory_buffer_id
.GetNext();
387 // Use service side allocation if this is a supported configuration.
388 if (IsGpuMemoryBufferConfigurationSupported(request
->format
,
390 // Note: Unretained is safe as this is only used for synchronous allocation
391 // from a non-IO thread.
392 AllocateGpuMemoryBufferOnIO(
393 new_id
, request
->size
, request
->format
, request
->usage
,
394 request
->client_id
, request
->surface_id
, false,
395 base::Bind(&BrowserGpuMemoryBufferManager::
396 GpuMemoryBufferAllocatedForSurfaceOnIO
,
397 base::Unretained(this), base::Unretained(request
)));
401 DCHECK(GpuMemoryBufferImplSharedMemory::IsFormatSupported(request
->format
))
403 DCHECK(GpuMemoryBufferImplSharedMemory::IsUsageSupported(request
->usage
))
406 BufferMap
& buffers
= clients_
[request
->client_id
];
407 DCHECK(buffers
.find(new_id
) == buffers
.end());
409 // Allocate shared memory buffer as fallback.
410 buffers
[new_id
] = BufferInfo(request
->size
, gfx::SHARED_MEMORY_BUFFER
,
411 request
->format
, request
->usage
, 0);
412 // Note: Unretained is safe as IO thread is stopped before manager is
414 request
->result
= GpuMemoryBufferImplSharedMemory::Create(
415 new_id
, request
->size
, request
->format
,
417 &GpuMemoryBufferDeleted
,
418 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
),
419 base::Bind(&BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO
,
420 base::Unretained(this), new_id
, request
->client_id
)));
421 request
->event
.Signal();
424 void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedForSurfaceOnIO(
425 AllocateGpuMemoryBufferRequest
* request
,
426 const gfx::GpuMemoryBufferHandle
& handle
) {
427 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
429 // Early out if factory failed to allocate the buffer.
430 if (handle
.is_null()) {
431 request
->event
.Signal();
435 // Note: Unretained is safe as IO thread is stopped before manager is
437 request
->result
= GpuMemoryBufferImpl::CreateFromHandle(
438 handle
, request
->size
, request
->format
, request
->usage
,
440 &GpuMemoryBufferDeleted
,
441 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
),
442 base::Bind(&BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO
,
443 base::Unretained(this), handle
.id
, request
->client_id
)));
444 request
->event
.Signal();
447 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferOnIO(
448 gfx::GpuMemoryBufferId id
,
449 const gfx::Size
& size
,
450 gfx::GpuMemoryBuffer::Format format
,
451 gfx::GpuMemoryBuffer::Usage usage
,
454 bool reused_gpu_process
,
455 const AllocationCallback
& callback
) {
456 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
458 BufferMap
& buffers
= clients_
[client_id
];
459 DCHECK(buffers
.find(id
) == buffers
.end());
461 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id_
);
463 host
= GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED
,
464 CAUSE_FOR_GPU_LAUNCH_GPU_MEMORY_BUFFER_ALLOCATE
);
466 LOG(ERROR
) << "Failed to launch GPU process.";
467 callback
.Run(gfx::GpuMemoryBufferHandle());
470 gpu_host_id_
= host
->host_id();
471 reused_gpu_process
= false;
473 if (reused_gpu_process
) {
474 // We come here if we retried to allocate the buffer because of a
475 // failure in GpuMemoryBufferAllocatedOnIO, but we ended up with the
476 // same process ID, meaning the failure was not because of a channel
477 // error, but another reason. So fail now.
478 LOG(ERROR
) << "Failed to allocate GpuMemoryBuffer.";
479 callback
.Run(gfx::GpuMemoryBufferHandle());
482 reused_gpu_process
= true;
485 // Note: Handling of cases where the client is removed before the allocation
486 // completes is less subtle if we set the buffer type to EMPTY_BUFFER here
487 // and verify that this has not changed when allocation completes.
488 buffers
[id
] = BufferInfo(size
, gfx::EMPTY_BUFFER
, format
, usage
, 0);
490 // Note: Unretained is safe as IO thread is stopped before manager is
492 host
->CreateGpuMemoryBuffer(
493 id
, size
, format
, usage
, client_id
, surface_id
,
494 base::Bind(&BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO
,
495 base::Unretained(this), id
, client_id
, surface_id
,
496 gpu_host_id_
, reused_gpu_process
, callback
));
499 void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO(
500 gfx::GpuMemoryBufferId id
,
504 bool reused_gpu_process
,
505 const AllocationCallback
& callback
,
506 const gfx::GpuMemoryBufferHandle
& handle
) {
507 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
509 ClientMap::iterator client_it
= clients_
.find(client_id
);
511 // This can happen if client is removed while the buffer is being allocated.
512 if (client_it
== clients_
.end()) {
513 if (!handle
.is_null()) {
514 GpuProcessHost
* host
= GpuProcessHost::FromID(gpu_host_id
);
516 host
->DestroyGpuMemoryBuffer(handle
.id
, client_id
, 0);
518 callback
.Run(gfx::GpuMemoryBufferHandle());
522 BufferMap
& buffers
= client_it
->second
;
524 BufferMap::iterator buffer_it
= buffers
.find(id
);
525 DCHECK(buffer_it
!= buffers
.end());
526 DCHECK_EQ(buffer_it
->second
.type
, gfx::EMPTY_BUFFER
);
528 // If the handle isn't valid, that means that the GPU process crashed or is
530 bool valid_handle
= !handle
.is_null() && handle
.id
== id
;
532 // If we failed after re-using the GPU process, it may have died in the
533 // mean time. Retry to have a chance to create a fresh GPU process.
534 if (handle
.is_null() && reused_gpu_process
) {
535 DVLOG(1) << "Failed to create buffer through existing GPU process. "
536 "Trying to restart GPU process.";
537 // If the GPU process has already been restarted, retry without failure
538 // when GPU process host ID already exists.
539 if (gpu_host_id
!= gpu_host_id_
)
540 reused_gpu_process
= false;
541 gfx::Size size
= buffer_it
->second
.size
;
542 gfx::GpuMemoryBuffer::Format format
= buffer_it
->second
.format
;
543 gfx::GpuMemoryBuffer::Usage usage
= buffer_it
->second
.usage
;
544 // Remove the buffer entry and call AllocateGpuMemoryBufferOnIO again.
545 buffers
.erase(buffer_it
);
546 AllocateGpuMemoryBufferOnIO(id
, size
, format
, usage
, client_id
,
547 surface_id
, reused_gpu_process
, callback
);
549 // Remove the buffer entry and run the allocation callback with an empty
550 // handle to indicate failure.
551 buffers
.erase(buffer_it
);
552 callback
.Run(gfx::GpuMemoryBufferHandle());
557 // Store the type and host id of this buffer so it can be cleaned up if the
558 // client is removed.
559 buffer_it
->second
.type
= handle
.type
;
560 buffer_it
->second
.gpu_host_id
= gpu_host_id
;
562 callback
.Run(handle
);
565 void BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO(
566 gfx::GpuMemoryBufferId id
,
569 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
570 DCHECK(clients_
.find(client_id
) != clients_
.end());
572 BufferMap
& buffers
= clients_
[client_id
];
574 BufferMap::iterator buffer_it
= buffers
.find(id
);
575 if (buffer_it
== buffers
.end()) {
576 LOG(ERROR
) << "Invalid GpuMemoryBuffer ID for client.";
580 // This can happen if a client managed to call this while a buffer is in the
581 // process of being allocated.
582 if (buffer_it
->second
.type
== gfx::EMPTY_BUFFER
) {
583 LOG(ERROR
) << "Invalid GpuMemoryBuffer type.";
587 GpuProcessHost
* host
= GpuProcessHost::FromID(buffer_it
->second
.gpu_host_id
);
589 host
->DestroyGpuMemoryBuffer(id
, client_id
, sync_point
);
591 buffers
.erase(buffer_it
);
594 } // namespace content