base/threading: remove ScopedTracker placed for experiments
[chromium-blink-merge.git] / content / browser / gpu / browser_gpu_memory_buffer_manager.cc
blob20da57b8de2b443e9fe2faa90971cce08da0cb39
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"
8 #include "base/bind.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"
26 #endif
28 #if defined(OS_ANDROID)
29 #include "content/common/gpu/gpu_memory_buffer_factory_surface_texture.h"
30 #endif
32 #if defined(USE_OZONE)
33 #include "content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h"
34 #endif
36 namespace content {
37 namespace {
39 void GpuMemoryBufferDeleted(
40 scoped_refptr<base::SingleThreadTaskRunner> destruction_task_runner,
41 const GpuMemoryBufferImpl::DestructionCallback& destruction_callback,
42 uint32 sync_point) {
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) {
50 switch (type) {
51 case gfx::SHARED_MEMORY_BUFFER:
52 return GpuMemoryBufferFactorySharedMemory::
53 IsGpuMemoryBufferConfigurationSupported(configuration.format,
54 configuration.usage);
55 #if defined(OS_MACOSX)
56 case gfx::IO_SURFACE_BUFFER:
57 return GpuMemoryBufferFactoryIOSurface::
58 IsGpuMemoryBufferConfigurationSupported(configuration.format,
59 configuration.usage);
60 #endif
61 #if defined(OS_ANDROID)
62 case gfx::SURFACE_TEXTURE_BUFFER:
63 return GpuMemoryBufferFactorySurfaceTexture::
64 IsGpuMemoryBufferConfigurationSupported(configuration.format,
65 configuration.usage);
66 #endif
67 #if defined(USE_OZONE)
68 case gfx::OZONE_NATIVE_PIXMAP:
69 return GpuMemoryBufferFactoryOzoneNativePixmap::
70 IsGpuMemoryBufferConfigurationSupported(configuration.format,
71 configuration.usage);
72 #endif
73 default:
74 NOTREACHED();
75 return false;
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::BufferFormat::R_8, gfx::BufferUsage::MAP},
95 {gfx::BufferFormat::R_8, gfx::BufferUsage::PERSISTENT_MAP},
96 {gfx::BufferFormat::RGBA_4444, gfx::BufferUsage::MAP},
97 {gfx::BufferFormat::RGBA_4444, gfx::BufferUsage::PERSISTENT_MAP},
98 {gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::MAP},
99 {gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::PERSISTENT_MAP},
100 {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::MAP},
101 {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::PERSISTENT_MAP}};
102 for (auto& configuration : kNativeConfigurations) {
103 if (IsGpuMemoryBufferFactoryConfigurationSupported(type, configuration))
104 configurations.push_back(configuration);
108 #if defined(USE_OZONE) || defined(OS_MACOSX)
109 const GpuMemoryBufferFactory::Configuration kScanoutConfigurations[] = {
110 {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT},
111 {gfx::BufferFormat::RGBX_8888, gfx::BufferUsage::SCANOUT}};
112 for (auto& configuration : kScanoutConfigurations) {
113 if (IsGpuMemoryBufferFactoryConfigurationSupported(type, configuration))
114 configurations.push_back(configuration);
116 #endif
118 return configurations;
121 BrowserGpuMemoryBufferManager* g_gpu_memory_buffer_manager = nullptr;
123 // Global atomic to generate gpu memory buffer unique IDs.
124 base::StaticAtomicSequenceNumber g_next_gpu_memory_buffer_id;
126 } // namespace
128 struct BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferRequest {
129 AllocateGpuMemoryBufferRequest(const gfx::Size& size,
130 gfx::BufferFormat format,
131 gfx::BufferUsage usage,
132 int client_id,
133 int surface_id)
134 : event(true, false),
135 size(size),
136 format(format),
137 usage(usage),
138 client_id(client_id),
139 surface_id(surface_id) {}
140 ~AllocateGpuMemoryBufferRequest() {}
141 base::WaitableEvent event;
142 gfx::Size size;
143 gfx::BufferFormat format;
144 gfx::BufferUsage usage;
145 int client_id;
146 int surface_id;
147 scoped_ptr<gfx::GpuMemoryBuffer> result;
150 BrowserGpuMemoryBufferManager::BrowserGpuMemoryBufferManager(int gpu_client_id)
151 : factory_type_(GetGpuMemoryBufferFactoryType()),
152 supported_configurations_(
153 GetSupportedGpuMemoryBufferConfigurations(factory_type_)),
154 gpu_client_id_(gpu_client_id),
155 gpu_host_id_(0) {
156 DCHECK(!g_gpu_memory_buffer_manager);
157 g_gpu_memory_buffer_manager = this;
160 BrowserGpuMemoryBufferManager::~BrowserGpuMemoryBufferManager() {
161 g_gpu_memory_buffer_manager = nullptr;
164 // static
165 BrowserGpuMemoryBufferManager* BrowserGpuMemoryBufferManager::current() {
166 return g_gpu_memory_buffer_manager;
169 // static
170 uint32 BrowserGpuMemoryBufferManager::GetImageTextureTarget(
171 gfx::BufferFormat format,
172 gfx::BufferUsage usage) {
173 gfx::GpuMemoryBufferType type = GetGpuMemoryBufferFactoryType();
174 for (auto& configuration : GetSupportedGpuMemoryBufferConfigurations(type)) {
175 if (configuration.format != format || configuration.usage != usage)
176 continue;
178 switch (type) {
179 case gfx::SURFACE_TEXTURE_BUFFER:
180 case gfx::OZONE_NATIVE_PIXMAP:
181 // GPU memory buffers that are shared with the GL using EGLImages
182 // require TEXTURE_EXTERNAL_OES.
183 return GL_TEXTURE_EXTERNAL_OES;
184 case gfx::IO_SURFACE_BUFFER:
185 // IOSurface backed images require GL_TEXTURE_RECTANGLE_ARB.
186 return GL_TEXTURE_RECTANGLE_ARB;
187 default:
188 return GL_TEXTURE_2D;
192 return GL_TEXTURE_2D;
195 scoped_ptr<gfx::GpuMemoryBuffer>
196 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBuffer(const gfx::Size& size,
197 gfx::BufferFormat format,
198 gfx::BufferUsage usage) {
199 return AllocateGpuMemoryBufferForSurface(size, format, usage, 0);
202 scoped_ptr<gfx::GpuMemoryBuffer>
203 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForScanout(
204 const gfx::Size& size,
205 gfx::BufferFormat format,
206 int32 surface_id) {
207 DCHECK_GT(surface_id, 0);
208 return AllocateGpuMemoryBufferForSurface(
209 size, format, gfx::BufferUsage::SCANOUT, surface_id);
212 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForChildProcess(
213 const gfx::Size& size,
214 gfx::BufferFormat format,
215 gfx::BufferUsage usage,
216 base::ProcessHandle child_process_handle,
217 int child_client_id,
218 const AllocationCallback& callback) {
219 DCHECK_CURRENTLY_ON(BrowserThread::IO);
221 gfx::GpuMemoryBufferId new_id = g_next_gpu_memory_buffer_id.GetNext();
223 // Use service side allocation if this is a supported configuration.
224 if (IsGpuMemoryBufferConfigurationSupported(format, usage)) {
225 AllocateGpuMemoryBufferOnIO(new_id, size, format, usage, child_client_id, 0,
226 false, callback);
227 return;
230 // Early out if we cannot fallback to shared memory buffer.
231 if (!GpuMemoryBufferImplSharedMemory::IsFormatSupported(format) ||
232 !GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage) ||
233 !GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size, format)) {
234 callback.Run(gfx::GpuMemoryBufferHandle());
235 return;
238 BufferMap& buffers = clients_[child_client_id];
239 DCHECK(buffers.find(new_id) == buffers.end());
241 // Allocate shared memory buffer as fallback.
242 buffers[new_id] =
243 BufferInfo(size, gfx::SHARED_MEMORY_BUFFER, format, usage, 0);
244 callback.Run(GpuMemoryBufferImplSharedMemory::AllocateForChildProcess(
245 new_id, size, format, child_process_handle));
248 gfx::GpuMemoryBuffer*
249 BrowserGpuMemoryBufferManager::GpuMemoryBufferFromClientBuffer(
250 ClientBuffer buffer) {
251 return GpuMemoryBufferImpl::FromClientBuffer(buffer);
254 void BrowserGpuMemoryBufferManager::SetDestructionSyncPoint(
255 gfx::GpuMemoryBuffer* buffer,
256 uint32 sync_point) {
257 static_cast<GpuMemoryBufferImpl*>(buffer)
258 ->set_destruction_sync_point(sync_point);
261 bool BrowserGpuMemoryBufferManager::OnMemoryDump(
262 base::trace_event::ProcessMemoryDump* pmd) {
263 DCHECK_CURRENTLY_ON(BrowserThread::IO);
265 for (const auto& client : clients_) {
266 int client_id = client.first;
268 for (const auto& buffer : client.second) {
269 if (buffer.second.type == gfx::EMPTY_BUFFER)
270 continue;
272 gfx::GpuMemoryBufferId buffer_id = buffer.first;
273 base::trace_event::MemoryAllocatorDump* dump =
274 pmd->CreateAllocatorDump(base::StringPrintf(
275 "gpumemorybuffer/client_%d/buffer_%d", client_id, buffer_id));
276 if (!dump)
277 return false;
279 size_t buffer_size_in_bytes = 0;
280 // Note: BufferSizeInBytes returns an approximated size for the buffer
281 // but the factory can be made to return the exact size if this
282 // approximation is not good enough.
283 bool valid_size = GpuMemoryBufferImpl::BufferSizeInBytes(
284 buffer.second.size, buffer.second.format, &buffer_size_in_bytes);
285 DCHECK(valid_size);
287 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
288 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
289 buffer_size_in_bytes);
291 // Create the cross-process ownership edge. If the client creates a
292 // corresponding dump for the same buffer, this will avoid to
293 // double-count them in tracing. If, instead, no other process will emit a
294 // dump with the same guid, the segment will be accounted to the browser.
295 const uint64 client_tracing_process_id =
296 ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(
297 client_id);
298 base::trace_event::MemoryAllocatorDumpGuid shared_buffer_guid =
299 gfx::GetGpuMemoryBufferGUIDForTracing(client_tracing_process_id,
300 buffer_id);
301 pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid);
302 pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid);
306 return true;
309 void BrowserGpuMemoryBufferManager::ChildProcessDeletedGpuMemoryBuffer(
310 gfx::GpuMemoryBufferId id,
311 base::ProcessHandle child_process_handle,
312 int child_client_id,
313 uint32 sync_point) {
314 DCHECK_CURRENTLY_ON(BrowserThread::IO);
316 DestroyGpuMemoryBufferOnIO(id, child_client_id, sync_point);
319 void BrowserGpuMemoryBufferManager::ProcessRemoved(
320 base::ProcessHandle process_handle,
321 int client_id) {
322 DCHECK_CURRENTLY_ON(BrowserThread::IO);
324 ClientMap::iterator client_it = clients_.find(client_id);
325 if (client_it == clients_.end())
326 return;
328 for (const auto& buffer : client_it->second) {
329 // This might happen if buffer is currenlty in the process of being
330 // allocated. The buffer will in that case be cleaned up when allocation
331 // completes.
332 if (buffer.second.type == gfx::EMPTY_BUFFER)
333 continue;
335 GpuProcessHost* host = GpuProcessHost::FromID(buffer.second.gpu_host_id);
336 if (host)
337 host->DestroyGpuMemoryBuffer(buffer.first, client_id, 0);
340 clients_.erase(client_it);
343 scoped_ptr<gfx::GpuMemoryBuffer>
344 BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurface(
345 const gfx::Size& size,
346 gfx::BufferFormat format,
347 gfx::BufferUsage usage,
348 int32 surface_id) {
349 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
351 AllocateGpuMemoryBufferRequest request(size, format, usage, gpu_client_id_,
352 surface_id);
353 BrowserThread::PostTask(
354 BrowserThread::IO, FROM_HERE,
355 base::Bind(
356 &BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurfaceOnIO,
357 base::Unretained(this), // Safe as we wait for result below.
358 base::Unretained(&request)));
360 // We're blocking the UI thread, which is generally undesirable.
361 TRACE_EVENT0(
362 "browser",
363 "BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurface");
364 base::ThreadRestrictions::ScopedAllowWait allow_wait;
365 request.event.Wait();
366 return request.result.Pass();
369 bool BrowserGpuMemoryBufferManager::IsGpuMemoryBufferConfigurationSupported(
370 gfx::BufferFormat format,
371 gfx::BufferUsage usage) const {
372 for (auto& configuration : supported_configurations_) {
373 if (configuration.format == format && configuration.usage == usage)
374 return true;
376 return false;
379 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurfaceOnIO(
380 AllocateGpuMemoryBufferRequest* request) {
381 DCHECK_CURRENTLY_ON(BrowserThread::IO);
383 gfx::GpuMemoryBufferId new_id = g_next_gpu_memory_buffer_id.GetNext();
385 // Use service side allocation if this is a supported configuration.
386 if (IsGpuMemoryBufferConfigurationSupported(request->format,
387 request->usage)) {
388 // Note: Unretained is safe as this is only used for synchronous allocation
389 // from a non-IO thread.
390 AllocateGpuMemoryBufferOnIO(
391 new_id, request->size, request->format, request->usage,
392 request->client_id, request->surface_id, false,
393 base::Bind(&BrowserGpuMemoryBufferManager::
394 GpuMemoryBufferAllocatedForSurfaceOnIO,
395 base::Unretained(this), base::Unretained(request)));
396 return;
399 DCHECK(GpuMemoryBufferImplSharedMemory::IsFormatSupported(request->format))
400 << static_cast<int>(request->format);
401 DCHECK(GpuMemoryBufferImplSharedMemory::IsUsageSupported(request->usage))
402 << static_cast<int>(request->usage);
404 BufferMap& buffers = clients_[request->client_id];
405 DCHECK(buffers.find(new_id) == buffers.end());
407 // Allocate shared memory buffer as fallback.
408 buffers[new_id] = BufferInfo(request->size, gfx::SHARED_MEMORY_BUFFER,
409 request->format, request->usage, 0);
410 // Note: Unretained is safe as IO thread is stopped before manager is
411 // destroyed.
412 request->result = GpuMemoryBufferImplSharedMemory::Create(
413 new_id, request->size, request->format,
414 base::Bind(
415 &GpuMemoryBufferDeleted,
416 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
417 base::Bind(&BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO,
418 base::Unretained(this), new_id, request->client_id)));
419 request->event.Signal();
422 void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedForSurfaceOnIO(
423 AllocateGpuMemoryBufferRequest* request,
424 const gfx::GpuMemoryBufferHandle& handle) {
425 DCHECK_CURRENTLY_ON(BrowserThread::IO);
427 // Early out if factory failed to allocate the buffer.
428 if (handle.is_null()) {
429 request->event.Signal();
430 return;
433 // Note: Unretained is safe as IO thread is stopped before manager is
434 // destroyed.
435 request->result = GpuMemoryBufferImpl::CreateFromHandle(
436 handle, request->size, request->format, request->usage,
437 base::Bind(
438 &GpuMemoryBufferDeleted,
439 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
440 base::Bind(&BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO,
441 base::Unretained(this), handle.id, request->client_id)));
442 request->event.Signal();
445 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferOnIO(
446 gfx::GpuMemoryBufferId id,
447 const gfx::Size& size,
448 gfx::BufferFormat format,
449 gfx::BufferUsage usage,
450 int client_id,
451 int surface_id,
452 bool reused_gpu_process,
453 const AllocationCallback& callback) {
454 DCHECK_CURRENTLY_ON(BrowserThread::IO);
456 BufferMap& buffers = clients_[client_id];
457 DCHECK(buffers.find(id) == buffers.end());
459 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
460 if (!host) {
461 host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
462 CAUSE_FOR_GPU_LAUNCH_GPU_MEMORY_BUFFER_ALLOCATE);
463 if (!host) {
464 LOG(ERROR) << "Failed to launch GPU process.";
465 callback.Run(gfx::GpuMemoryBufferHandle());
466 return;
468 gpu_host_id_ = host->host_id();
469 reused_gpu_process = false;
470 } else {
471 if (reused_gpu_process) {
472 // We come here if we retried to allocate the buffer because of a
473 // failure in GpuMemoryBufferAllocatedOnIO, but we ended up with the
474 // same process ID, meaning the failure was not because of a channel
475 // error, but another reason. So fail now.
476 LOG(ERROR) << "Failed to allocate GpuMemoryBuffer.";
477 callback.Run(gfx::GpuMemoryBufferHandle());
478 return;
480 reused_gpu_process = true;
483 // Note: Handling of cases where the client is removed before the allocation
484 // completes is less subtle if we set the buffer type to EMPTY_BUFFER here
485 // and verify that this has not changed when allocation completes.
486 buffers[id] = BufferInfo(size, gfx::EMPTY_BUFFER, format, usage, 0);
488 // Note: Unretained is safe as IO thread is stopped before manager is
489 // destroyed.
490 host->CreateGpuMemoryBuffer(
491 id, size, format, usage, client_id, surface_id,
492 base::Bind(&BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO,
493 base::Unretained(this), id, client_id, surface_id,
494 gpu_host_id_, reused_gpu_process, callback));
497 void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO(
498 gfx::GpuMemoryBufferId id,
499 int client_id,
500 int surface_id,
501 int gpu_host_id,
502 bool reused_gpu_process,
503 const AllocationCallback& callback,
504 const gfx::GpuMemoryBufferHandle& handle) {
505 DCHECK_CURRENTLY_ON(BrowserThread::IO);
507 ClientMap::iterator client_it = clients_.find(client_id);
509 // This can happen if client is removed while the buffer is being allocated.
510 if (client_it == clients_.end()) {
511 if (!handle.is_null()) {
512 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id);
513 if (host)
514 host->DestroyGpuMemoryBuffer(handle.id, client_id, 0);
516 callback.Run(gfx::GpuMemoryBufferHandle());
517 return;
520 BufferMap& buffers = client_it->second;
522 BufferMap::iterator buffer_it = buffers.find(id);
523 DCHECK(buffer_it != buffers.end());
524 DCHECK_EQ(buffer_it->second.type, gfx::EMPTY_BUFFER);
526 // If the handle isn't valid, that means that the GPU process crashed or is
527 // misbehaving.
528 bool valid_handle = !handle.is_null() && handle.id == id;
529 if (!valid_handle) {
530 // If we failed after re-using the GPU process, it may have died in the
531 // mean time. Retry to have a chance to create a fresh GPU process.
532 if (handle.is_null() && reused_gpu_process) {
533 DVLOG(1) << "Failed to create buffer through existing GPU process. "
534 "Trying to restart GPU process.";
535 // If the GPU process has already been restarted, retry without failure
536 // when GPU process host ID already exists.
537 if (gpu_host_id != gpu_host_id_)
538 reused_gpu_process = false;
539 gfx::Size size = buffer_it->second.size;
540 gfx::BufferFormat format = buffer_it->second.format;
541 gfx::BufferUsage usage = buffer_it->second.usage;
542 // Remove the buffer entry and call AllocateGpuMemoryBufferOnIO again.
543 buffers.erase(buffer_it);
544 AllocateGpuMemoryBufferOnIO(id, size, format, usage, client_id,
545 surface_id, reused_gpu_process, callback);
546 } else {
547 // Remove the buffer entry and run the allocation callback with an empty
548 // handle to indicate failure.
549 buffers.erase(buffer_it);
550 callback.Run(gfx::GpuMemoryBufferHandle());
552 return;
555 // Store the type and host id of this buffer so it can be cleaned up if the
556 // client is removed.
557 buffer_it->second.type = handle.type;
558 buffer_it->second.gpu_host_id = gpu_host_id;
560 callback.Run(handle);
563 void BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO(
564 gfx::GpuMemoryBufferId id,
565 int client_id,
566 uint32 sync_point) {
567 DCHECK_CURRENTLY_ON(BrowserThread::IO);
568 DCHECK(clients_.find(client_id) != clients_.end());
570 BufferMap& buffers = clients_[client_id];
572 BufferMap::iterator buffer_it = buffers.find(id);
573 if (buffer_it == buffers.end()) {
574 LOG(ERROR) << "Invalid GpuMemoryBuffer ID for client.";
575 return;
578 // This can happen if a client managed to call this while a buffer is in the
579 // process of being allocated.
580 if (buffer_it->second.type == gfx::EMPTY_BUFFER) {
581 LOG(ERROR) << "Invalid GpuMemoryBuffer type.";
582 return;
585 GpuProcessHost* host = GpuProcessHost::FromID(buffer_it->second.gpu_host_id);
586 if (host)
587 host->DestroyGpuMemoryBuffer(id, client_id, sync_point);
589 buffers.erase(buffer_it);
592 } // namespace content