blob: f08278ce47093fe4d68eb1191b6cdf45bdeb076d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_restrictions.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/generic_shared_memory_id_generator.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/ipc/client/gpu_memory_buffer_impl.h"
#include "gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h"
#include "gpu/ipc/common/gpu_memory_buffer_support.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gl/gl_switches.h"
namespace content {
namespace {
void HostCreateGpuMemoryBuffer(
gpu::SurfaceHandle surface_handle,
GpuProcessHost* host,
gfx::GpuMemoryBufferId id,
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
int client_id,
const BrowserGpuMemoryBufferManager::CreateCallback& callback) {
host->CreateGpuMemoryBuffer(id, size, format, usage, client_id,
surface_handle, callback);
}
void GpuMemoryBufferDeleted(
scoped_refptr<base::SingleThreadTaskRunner> destruction_task_runner,
const gpu::GpuMemoryBufferImpl::DestructionCallback& destruction_callback,
const gpu::SyncToken& sync_token) {
destruction_task_runner->PostTask(
FROM_HERE, base::Bind(destruction_callback, sync_token));
}
GpuMemoryBufferConfigurationSet GetNativeGpuMemoryBufferConfigurations() {
GpuMemoryBufferConfigurationSet configurations;
if (BrowserGpuMemoryBufferManager::IsNativeGpuMemoryBuffersEnabled()) {
const gfx::BufferFormat kNativeFormats[] = {
gfx::BufferFormat::R_8, gfx::BufferFormat::BGR_565,
gfx::BufferFormat::RGBA_4444, gfx::BufferFormat::RGBA_8888,
gfx::BufferFormat::BGRA_8888, gfx::BufferFormat::UYVY_422,
gfx::BufferFormat::YVU_420, gfx::BufferFormat::YUV_420_BIPLANAR};
const gfx::BufferUsage kNativeUsages[] = {
gfx::BufferUsage::GPU_READ, gfx::BufferUsage::SCANOUT,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT};
for (auto& format : kNativeFormats) {
for (auto& usage : kNativeUsages) {
if (gpu::IsNativeGpuMemoryBufferConfigurationSupported(format, usage))
configurations.insert(std::make_pair(format, usage));
}
}
}
#if defined(USE_OZONE) || defined(OS_MACOSX)
// Disable native buffers only when using Mesa.
bool force_native_gpu_read_write_formats =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kUseGL) != gl::kGLImplementationOSMesaName;
#else
bool force_native_gpu_read_write_formats = false;
#endif
if (force_native_gpu_read_write_formats) {
const gfx::BufferFormat kGPUReadWriteFormats[] = {
gfx::BufferFormat::BGR_565, gfx::BufferFormat::RGBA_8888,
gfx::BufferFormat::RGBX_8888, gfx::BufferFormat::BGRA_8888,
gfx::BufferFormat::BGRX_8888, gfx::BufferFormat::UYVY_422,
gfx::BufferFormat::YVU_420, gfx::BufferFormat::YUV_420_BIPLANAR};
const gfx::BufferUsage kGPUReadWriteUsages[] = {
gfx::BufferUsage::GPU_READ, gfx::BufferUsage::SCANOUT};
for (auto& format : kGPUReadWriteFormats) {
for (auto& usage : kGPUReadWriteUsages) {
if (gpu::IsNativeGpuMemoryBufferConfigurationSupported(format, usage))
configurations.insert(std::make_pair(format, usage));
}
}
}
return configurations;
}
BrowserGpuMemoryBufferManager* g_gpu_memory_buffer_manager = nullptr;
} // namespace
struct BrowserGpuMemoryBufferManager::CreateGpuMemoryBufferRequest {
CreateGpuMemoryBufferRequest(const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
int client_id,
gpu::SurfaceHandle surface_handle)
: event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED),
size(size),
format(format),
usage(usage),
client_id(client_id),
surface_handle(surface_handle) {}
~CreateGpuMemoryBufferRequest() {}
base::WaitableEvent event;
gfx::Size size;
gfx::BufferFormat format;
gfx::BufferUsage usage;
int client_id;
gpu::SurfaceHandle surface_handle;
std::unique_ptr<gfx::GpuMemoryBuffer> result;
};
struct BrowserGpuMemoryBufferManager::CreateGpuMemoryBufferFromHandleRequest
: public CreateGpuMemoryBufferRequest {
CreateGpuMemoryBufferFromHandleRequest(
const gfx::GpuMemoryBufferHandle& handle,
const gfx::Size& size,
gfx::BufferFormat format,
int client_id)
: CreateGpuMemoryBufferRequest(size,
format,
gfx::BufferUsage::GPU_READ,
client_id,
gpu::kNullSurfaceHandle),
handle(handle) {}
~CreateGpuMemoryBufferFromHandleRequest() {}
gfx::GpuMemoryBufferHandle handle;
};
BrowserGpuMemoryBufferManager::BrowserGpuMemoryBufferManager(
int gpu_client_id,
uint64_t gpu_client_tracing_id)
: native_configurations_(GetNativeGpuMemoryBufferConfigurations()),
gpu_client_id_(gpu_client_id),
gpu_client_tracing_id_(gpu_client_tracing_id),
gpu_host_id_(0) {
DCHECK(!g_gpu_memory_buffer_manager);
g_gpu_memory_buffer_manager = this;
}
BrowserGpuMemoryBufferManager::~BrowserGpuMemoryBufferManager() {
g_gpu_memory_buffer_manager = nullptr;
}
// static
BrowserGpuMemoryBufferManager* BrowserGpuMemoryBufferManager::current() {
return g_gpu_memory_buffer_manager;
}
// static
bool BrowserGpuMemoryBufferManager::IsNativeGpuMemoryBuffersEnabled() {
// Disable native buffers when using Mesa.
if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kUseGL) == gl::kGLImplementationOSMesaName) {
return false;
}
#if defined(OS_MACOSX)
return !base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableNativeGpuMemoryBuffers);
#else
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableNativeGpuMemoryBuffers);
#endif
}
// static
uint32_t BrowserGpuMemoryBufferManager::GetImageTextureTarget(
gfx::BufferFormat format,
gfx::BufferUsage usage) {
GpuMemoryBufferConfigurationSet native_configurations =
GetNativeGpuMemoryBufferConfigurations();
if (native_configurations.find(std::make_pair(format, usage)) ==
native_configurations.end()) {
return GL_TEXTURE_2D;
}
switch (gpu::GetNativeGpuMemoryBufferType()) {
case gfx::SURFACE_TEXTURE_BUFFER:
case gfx::OZONE_NATIVE_PIXMAP:
// GPU memory buffers that are shared with the GL using EGLImages
// require TEXTURE_EXTERNAL_OES.
return GL_TEXTURE_EXTERNAL_OES;
case gfx::IO_SURFACE_BUFFER:
// IOSurface backed images require GL_TEXTURE_RECTANGLE_ARB.
return GL_TEXTURE_RECTANGLE_ARB;
case gfx::SHARED_MEMORY_BUFFER:
return GL_TEXTURE_2D;
case gfx::EMPTY_BUFFER:
NOTREACHED();
return GL_TEXTURE_2D;
}
NOTREACHED();
return GL_TEXTURE_2D;
}
std::unique_ptr<gfx::GpuMemoryBuffer>
BrowserGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
gpu::SurfaceHandle surface_handle) {
return AllocateGpuMemoryBufferForSurface(size, format, usage, surface_handle);
}
std::unique_ptr<gfx::GpuMemoryBuffer>
BrowserGpuMemoryBufferManager::CreateGpuMemoryBufferFromHandle(
const gfx::GpuMemoryBufferHandle& handle,
const gfx::Size& size,
gfx::BufferFormat format) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
CreateGpuMemoryBufferFromHandleRequest request(handle, size, format,
gpu_client_id_);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&BrowserGpuMemoryBufferManager::
HandleCreateGpuMemoryBufferFromHandleOnIO,
base::Unretained(this), // Safe as we wait for result below.
base::Unretained(&request)));
// We're blocking the UI thread, which is generally undesirable.
TRACE_EVENT0(
"browser",
"BrowserGpuMemoryBufferManager::CreateGpuMemoryBufferFromHandle");
base::ThreadRestrictions::ScopedAllowWait allow_wait;
request.event.Wait();
return std::move(request.result);
}
void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForChildProcess(
gfx::GpuMemoryBufferId id,
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
base::ProcessHandle child_process_handle,
int child_client_id,
const AllocationCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Use service side allocation for native configurations.
if (IsNativeGpuMemoryBufferConfiguration(format, usage)) {
CreateGpuMemoryBufferOnIO(
base::Bind(&HostCreateGpuMemoryBuffer, gpu::kNullSurfaceHandle), id,
size, format, usage, child_client_id, false, callback);
return;
}
// Early out if we cannot fallback to shared memory buffer.
if (!gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage) ||
!gpu::GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size,
format)) {
callback.Run(gfx::GpuMemoryBufferHandle());
return;
}
BufferMap& buffers = clients_[child_client_id];
// Allocate shared memory buffer as fallback.
auto insert_result = buffers.insert(std::make_pair(
id, BufferInfo(size, gfx::SHARED_MEMORY_BUFFER, format, usage, 0)));
if (!insert_result.second) {
DLOG(ERROR) << "Child process attempted to allocate a GpuMemoryBuffer with "
"an existing ID.";
callback.Run(gfx::GpuMemoryBufferHandle());
return;
}
callback.Run(gpu::GpuMemoryBufferImplSharedMemory::AllocateForChildProcess(
id, size, format, child_process_handle));
}
gfx::GpuMemoryBuffer*
BrowserGpuMemoryBufferManager::GpuMemoryBufferFromClientBuffer(
ClientBuffer buffer) {
return gpu::GpuMemoryBufferImpl::FromClientBuffer(buffer);
}
void BrowserGpuMemoryBufferManager::SetDestructionSyncToken(
gfx::GpuMemoryBuffer* buffer,
const gpu::SyncToken& sync_token) {
static_cast<gpu::GpuMemoryBufferImpl*>(buffer)->set_destruction_sync_token(
sync_token);
}
bool BrowserGpuMemoryBufferManager::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (const auto& client : clients_) {
int client_id = client.first;
for (const auto& buffer : client.second) {
if (buffer.second.type == gfx::EMPTY_BUFFER)
continue;
gfx::GpuMemoryBufferId buffer_id = buffer.first;
base::trace_event::MemoryAllocatorDump* dump =
pmd->CreateAllocatorDump(base::StringPrintf(
"gpumemorybuffer/client_%d/buffer_%d", client_id, buffer_id.id));
if (!dump)
return false;
size_t buffer_size_in_bytes = gfx::BufferSizeForBufferFormat(
buffer.second.size, buffer.second.format);
dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes,
buffer_size_in_bytes);
// Create the cross-process ownership edge. If the client creates a
// corresponding dump for the same buffer, this will avoid to
// double-count them in tracing. If, instead, no other process will emit a
// dump with the same guid, the segment will be accounted to the browser.
uint64_t client_tracing_process_id =
ClientIdToTracingProcessId(client_id);
base::trace_event::MemoryAllocatorDumpGuid shared_buffer_guid =
gfx::GetGpuMemoryBufferGUIDForTracing(client_tracing_process_id,
buffer_id);
pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid);
pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid);
}
}
return true;
}
void BrowserGpuMemoryBufferManager::ChildProcessDeletedGpuMemoryBuffer(
gfx::GpuMemoryBufferId id,
base::ProcessHandle child_process_handle,
int child_client_id,
const gpu::SyncToken& sync_token) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DestroyGpuMemoryBufferOnIO(id, child_client_id, sync_token);
}
void BrowserGpuMemoryBufferManager::ProcessRemoved(
base::ProcessHandle process_handle,
int client_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ClientMap::iterator client_it = clients_.find(client_id);
if (client_it == clients_.end())
return;
for (const auto& buffer : client_it->second) {
// This might happen if buffer is currenlty in the process of being
// allocated. The buffer will in that case be cleaned up when allocation
// completes.
if (buffer.second.type == gfx::EMPTY_BUFFER)
continue;
GpuProcessHost* host = GpuProcessHost::FromID(buffer.second.gpu_host_id);
if (host)
host->DestroyGpuMemoryBuffer(buffer.first, client_id, gpu::SyncToken());
}
clients_.erase(client_it);
}
bool BrowserGpuMemoryBufferManager::IsNativeGpuMemoryBufferConfiguration(
gfx::BufferFormat format,
gfx::BufferUsage usage) const {
return native_configurations_.find(std::make_pair(format, usage)) !=
native_configurations_.end();
}
std::unique_ptr<gfx::GpuMemoryBuffer>
BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurface(
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
gpu::SurfaceHandle surface_handle) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
CreateGpuMemoryBufferRequest request(size, format, usage, gpu_client_id_,
surface_handle);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(
&BrowserGpuMemoryBufferManager::HandleCreateGpuMemoryBufferOnIO,
base::Unretained(this), // Safe as we wait for result below.
base::Unretained(&request)));
// We're blocking the UI thread, which is generally undesirable.
TRACE_EVENT0(
"browser",
"BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurface");
base::ThreadRestrictions::ScopedAllowWait allow_wait;
request.event.Wait();
return std::move(request.result);
}
void BrowserGpuMemoryBufferManager::HandleCreateGpuMemoryBufferOnIO(
CreateGpuMemoryBufferRequest* request) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
gfx::GpuMemoryBufferId new_id = content::GetNextGenericSharedMemoryId();
// Use service side allocation for native configurations.
if (IsNativeGpuMemoryBufferConfiguration(request->format, request->usage)) {
// Note: Unretained is safe as this is only used for synchronous allocation
// from a non-IO thread.
CreateGpuMemoryBufferOnIO(
base::Bind(&HostCreateGpuMemoryBuffer, request->surface_handle), new_id,
request->size, request->format, request->usage, request->client_id,
false,
base::Bind(
&BrowserGpuMemoryBufferManager::HandleGpuMemoryBufferCreatedOnIO,
base::Unretained(this), base::Unretained(request)));
return;
}
DCHECK(gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(request->usage))
<< static_cast<int>(request->usage);
BufferMap& buffers = clients_[request->client_id];
// Allocate shared memory buffer as fallback.
auto insert_result = buffers.insert(std::make_pair(
new_id, BufferInfo(request->size, gfx::SHARED_MEMORY_BUFFER,
request->format, request->usage, 0)));
DCHECK(insert_result.second);
// Note: Unretained is safe as IO thread is stopped before manager is
// destroyed.
request->result = gpu::GpuMemoryBufferImplSharedMemory::Create(
new_id, request->size, request->format,
base::Bind(
&GpuMemoryBufferDeleted,
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
base::Bind(&BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO,
base::Unretained(this), new_id, request->client_id)));
request->event.Signal();
}
void BrowserGpuMemoryBufferManager::HandleCreateGpuMemoryBufferFromHandleOnIO(
CreateGpuMemoryBufferFromHandleRequest* request) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
gfx::GpuMemoryBufferId new_id = content::GetNextGenericSharedMemoryId();
BufferMap& buffers = clients_[request->client_id];
auto insert_result = buffers.insert(std::make_pair(
new_id, BufferInfo(request->size, request->handle.type,
request->format, request->usage, 0)));
DCHECK(insert_result.second);
gfx::GpuMemoryBufferHandle handle = request->handle;
handle.id = new_id;
// Note: Unretained is safe as IO thread is stopped before manager is
// destroyed.
request->result = gpu::GpuMemoryBufferImpl::CreateFromHandle(
handle, request->size, request->format, request->usage,
base::Bind(
&GpuMemoryBufferDeleted,
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
base::Bind(&BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO,
base::Unretained(this), new_id, request->client_id)));
request->event.Signal();
}
void BrowserGpuMemoryBufferManager::HandleGpuMemoryBufferCreatedOnIO(
CreateGpuMemoryBufferRequest* request,
const gfx::GpuMemoryBufferHandle& handle) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Early out if factory failed to create the buffer.
if (handle.is_null()) {
request->event.Signal();
return;
}
// Note: Unretained is safe as IO thread is stopped before manager is
// destroyed.
request->result = gpu::GpuMemoryBufferImpl::CreateFromHandle(
handle, request->size, request->format, request->usage,
base::Bind(
&GpuMemoryBufferDeleted,
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
base::Bind(&BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO,
base::Unretained(this), handle.id, request->client_id)));
request->event.Signal();
}
void BrowserGpuMemoryBufferManager::CreateGpuMemoryBufferOnIO(
const CreateDelegate& create_delegate,
gfx::GpuMemoryBufferId id,
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
int client_id,
bool reused_gpu_process,
const CreateCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
if (!host) {
host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED);
if (!host) {
LOG(ERROR) << "Failed to launch GPU process.";
callback.Run(gfx::GpuMemoryBufferHandle());
return;
}
gpu_host_id_ = host->host_id();
reused_gpu_process = false;
} else {
if (reused_gpu_process) {
// We come here if we retried to create the buffer because of a failure
// in GpuMemoryBufferCreatedOnIO, but we ended up with the same process
// ID, meaning the failure was not because of a channel error, but
// another reason. So fail now.
LOG(ERROR) << "Failed to create GpuMemoryBuffer.";
callback.Run(gfx::GpuMemoryBufferHandle());
return;
}
reused_gpu_process = true;
}
BufferMap& buffers = clients_[client_id];
// Note: Handling of cases where the client is removed before the allocation
// completes is less subtle if we set the buffer type to EMPTY_BUFFER here
// and verify that this has not changed when creation completes.
auto insert_result = buffers.insert(std::make_pair(
id, BufferInfo(size, gfx::EMPTY_BUFFER, format, usage, 0)));
if (!insert_result.second) {
DLOG(ERROR) << "Child process attempted to create a GpuMemoryBuffer with "
"an existing ID.";
callback.Run(gfx::GpuMemoryBufferHandle());
return;
}
// Note: Unretained is safe as IO thread is stopped before manager is
// destroyed.
create_delegate.Run(
host, id, size, format, usage, client_id,
base::Bind(&BrowserGpuMemoryBufferManager::GpuMemoryBufferCreatedOnIO,
base::Unretained(this), create_delegate, id, client_id,
gpu_host_id_, reused_gpu_process, callback));
}
void BrowserGpuMemoryBufferManager::GpuMemoryBufferCreatedOnIO(
const CreateDelegate& create_delegate,
gfx::GpuMemoryBufferId id,
int client_id,
int gpu_host_id,
bool reused_gpu_process,
const CreateCallback& callback,
const gfx::GpuMemoryBufferHandle& handle) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ClientMap::iterator client_it = clients_.find(client_id);
// This can happen if client is removed while the buffer is being allocated.
if (client_it == clients_.end()) {
if (!handle.is_null()) {
GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id);
if (host)
host->DestroyGpuMemoryBuffer(handle.id, client_id, gpu::SyncToken());
}
callback.Run(gfx::GpuMemoryBufferHandle());
return;
}
BufferMap& buffers = client_it->second;
BufferMap::iterator buffer_it = buffers.find(id);
DCHECK(buffer_it != buffers.end());
DCHECK_EQ(buffer_it->second.type, gfx::EMPTY_BUFFER);
// If the handle isn't valid, that means that the GPU process crashed or is
// misbehaving.
bool valid_handle = !handle.is_null() && handle.id == id;
if (!valid_handle) {
// If we failed after re-using the GPU process, it may have died in the
// mean time. Retry to have a chance to create a fresh GPU process.
if (handle.is_null() && reused_gpu_process) {
DVLOG(1) << "Failed to create buffer through existing GPU process. "
"Trying to restart GPU process.";
// If the GPU process has already been restarted, retry without failure
// when GPU process host ID already exists.
if (gpu_host_id != gpu_host_id_)
reused_gpu_process = false;
gfx::Size size = buffer_it->second.size;
gfx::BufferFormat format = buffer_it->second.format;
gfx::BufferUsage usage = buffer_it->second.usage;
// Remove the buffer entry and call CreateGpuMemoryBufferOnIO again.
buffers.erase(buffer_it);
CreateGpuMemoryBufferOnIO(create_delegate, id, size, format, usage,
client_id, reused_gpu_process, callback);
} else {
// Remove the buffer entry and run the allocation callback with an empty
// handle to indicate failure.
buffers.erase(buffer_it);
callback.Run(gfx::GpuMemoryBufferHandle());
}
return;
}
// Store the type and host id of this buffer so it can be cleaned up if the
// client is removed.
buffer_it->second.type = handle.type;
buffer_it->second.gpu_host_id = gpu_host_id;
callback.Run(handle);
}
void BrowserGpuMemoryBufferManager::DestroyGpuMemoryBufferOnIO(
gfx::GpuMemoryBufferId id,
int client_id,
const gpu::SyncToken& sync_token) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(clients_.find(client_id) != clients_.end());
BufferMap& buffers = clients_[client_id];
BufferMap::iterator buffer_it = buffers.find(id);
if (buffer_it == buffers.end()) {
LOG(ERROR) << "Invalid GpuMemoryBuffer ID for client.";
return;
}
// This can happen if a client managed to call this while a buffer is in the
// process of being allocated.
if (buffer_it->second.type == gfx::EMPTY_BUFFER) {
LOG(ERROR) << "Invalid GpuMemoryBuffer type.";
return;
}
GpuProcessHost* host = GpuProcessHost::FromID(buffer_it->second.gpu_host_id);
if (host)
host->DestroyGpuMemoryBuffer(id, client_id, sync_token);
buffers.erase(buffer_it);
}
uint64_t BrowserGpuMemoryBufferManager::ClientIdToTracingProcessId(
int client_id) const {
if (client_id == gpu_client_id_) {
// The gpu_client uses a fixed tracing ID.
return gpu_client_tracing_id_;
}
// In normal cases, |client_id| is a child process id, so we can perform
// the standard conversion.
return ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(
client_id);
}
BrowserGpuMemoryBufferManager::BufferInfo::BufferInfo() = default;
BrowserGpuMemoryBufferManager::BufferInfo::BufferInfo(
const gfx::Size& size,
gfx::GpuMemoryBufferType type,
gfx::BufferFormat format,
gfx::BufferUsage usage,
int gpu_host_id)
: size(size),
type(type),
format(format),
usage(usage),
gpu_host_id(gpu_host_id) {}
BrowserGpuMemoryBufferManager::BufferInfo::BufferInfo(const BufferInfo& other) =
default;
BrowserGpuMemoryBufferManager::BufferInfo::~BufferInfo() {}
} // namespace content