blob: b628d4372bd8528f15a1c34bb2becda5b7ae0c5f [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 "base/atomic_sequence_num.h"
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_restrictions.h"
#include "base/trace_event/trace_event.h"
#include "content/common/gpu/client/gpu_memory_buffer_factory_host.h"
#include "content/common/gpu/client/gpu_memory_buffer_impl.h"
#include "content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h"
#include "content/public/browser/browser_thread.h"
namespace content {
namespace {
BrowserGpuMemoryBufferManager* g_gpu_memory_buffer_manager = nullptr;
// Global atomic to generate gpu memory buffer unique IDs.
base::StaticAtomicSequenceNumber g_next_gpu_memory_buffer_id;
} // namespace
struct BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferRequest {
AllocateGpuMemoryBufferRequest(const gfx::Size& size,
gfx::GpuMemoryBuffer::Format format,
gfx::GpuMemoryBuffer::Usage usage,
int client_id,
int surface_id)
: event(true, false),
size(size),
format(format),
usage(usage),
client_id(client_id),
surface_id(surface_id) {}
~AllocateGpuMemoryBufferRequest() {}
base::WaitableEvent event;
gfx::Size size;
gfx::GpuMemoryBuffer::Format format;
gfx::GpuMemoryBuffer::Usage usage;
int client_id;
int surface_id;
scoped_ptr<gfx::GpuMemoryBuffer> result;
};
BrowserGpuMemoryBufferManager::BrowserGpuMemoryBufferManager(
GpuMemoryBufferFactoryHost* gpu_memory_buffer_factory_host,
int gpu_client_id)
: gpu_memory_buffer_factory_host_(gpu_memory_buffer_factory_host),
gpu_client_id_(gpu_client_id),
weak_ptr_factory_(this) {
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;
}
scoped_ptr<gfx::GpuMemoryBuffer>
BrowserGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
const gfx::Size& size,
gfx::GpuMemoryBuffer::Format format,
gfx::GpuMemoryBuffer::Usage usage) {
return AllocateGpuMemoryBufferCommon(size, format, usage, 0);
}
scoped_ptr<gfx::GpuMemoryBuffer>
BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForScanout(
const gfx::Size& size,
gfx::GpuMemoryBuffer::Format format,
int32 surface_id) {
DCHECK_GT(surface_id, 0);
return AllocateGpuMemoryBufferCommon(
size, format, gfx::GpuMemoryBuffer::SCANOUT, surface_id);
}
scoped_ptr<gfx::GpuMemoryBuffer>
BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferCommon(
const gfx::Size& size,
gfx::GpuMemoryBuffer::Format format,
gfx::GpuMemoryBuffer::Usage usage,
int32 surface_id) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
// Fallback to shared memory buffer if |format| and |usage| are not supported
// by factory.
if (!gpu_memory_buffer_factory_host_->IsGpuMemoryBufferConfigurationSupported(
format, usage)) {
DCHECK(GpuMemoryBufferImplSharedMemory::IsFormatSupported(format));
DCHECK_EQ(usage, gfx::GpuMemoryBuffer::MAP);
return GpuMemoryBufferImplSharedMemory::Create(
g_next_gpu_memory_buffer_id.GetNext(), size, format);
}
AllocateGpuMemoryBufferRequest request(size, format, usage, gpu_client_id_,
surface_id);
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferOnIO,
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::AllocateGpuMemoryBuffer");
base::ThreadRestrictions::ScopedAllowWait allow_wait;
request.event.Wait();
return request.result.Pass();
}
void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForChildProcess(
const gfx::Size& size,
gfx::GpuMemoryBuffer::Format format,
gfx::GpuMemoryBuffer::Usage usage,
base::ProcessHandle child_process_handle,
int child_client_id,
const AllocationCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
gfx::GpuMemoryBufferId new_id = g_next_gpu_memory_buffer_id.GetNext();
BufferMap& buffers = clients_[child_client_id];
DCHECK(buffers.find(new_id) == buffers.end());
// Fallback to shared memory buffer if |format| and |usage| are not supported
// by factory.
if (!gpu_memory_buffer_factory_host_->IsGpuMemoryBufferConfigurationSupported(
format, usage)) {
// Early out if we cannot fallback to shared memory buffer.
if (!GpuMemoryBufferImplSharedMemory::IsFormatSupported(format) ||
!GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size, format) ||
usage != gfx::GpuMemoryBuffer::MAP) {
callback.Run(gfx::GpuMemoryBufferHandle());
return;
}
buffers[new_id] = gfx::SHARED_MEMORY_BUFFER;
callback.Run(GpuMemoryBufferImplSharedMemory::AllocateForChildProcess(
new_id, size, format, child_process_handle));
return;
}
// Note: Handling of cases where the child process 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 allocation
// completes.
buffers[new_id] = gfx::EMPTY_BUFFER;
gpu_memory_buffer_factory_host_->CreateGpuMemoryBuffer(
new_id, size, format, usage, child_client_id, 0,
base::Bind(&BrowserGpuMemoryBufferManager::
GpuMemoryBufferAllocatedForChildProcess,
weak_ptr_factory_.GetWeakPtr(), child_client_id, callback));
}
gfx::GpuMemoryBuffer*
BrowserGpuMemoryBufferManager::GpuMemoryBufferFromClientBuffer(
ClientBuffer buffer) {
return GpuMemoryBufferImpl::FromClientBuffer(buffer);
}
void BrowserGpuMemoryBufferManager::SetDestructionSyncPoint(
gfx::GpuMemoryBuffer* buffer,
uint32 sync_point) {
static_cast<GpuMemoryBufferImpl*>(buffer)
->set_destruction_sync_point(sync_point);
}
void BrowserGpuMemoryBufferManager::ChildProcessDeletedGpuMemoryBuffer(
gfx::GpuMemoryBufferId id,
base::ProcessHandle child_process_handle,
int child_client_id,
uint32 sync_point) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(clients_.find(child_client_id) != clients_.end());
BufferMap& buffers = clients_[child_client_id];
BufferMap::iterator buffer_it = buffers.find(id);
if (buffer_it == buffers.end()) {
LOG(ERROR) << "Invalid GpuMemoryBuffer ID for child process.";
return;
}
// This can happen if a child process managed to trigger a call to this while
// a buffer is in the process of being allocated.
if (buffer_it->second == gfx::EMPTY_BUFFER) {
LOG(ERROR) << "Invalid GpuMemoryBuffer type.";
return;
}
// Buffers allocated using the factory need to be destroyed through the
// factory.
if (buffer_it->second != gfx::SHARED_MEMORY_BUFFER) {
gpu_memory_buffer_factory_host_->DestroyGpuMemoryBuffer(id,
child_client_id,
sync_point);
}
buffers.erase(buffer_it);
}
void BrowserGpuMemoryBufferManager::ProcessRemoved(
base::ProcessHandle process_handle,
int client_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ClientMap::iterator client_it = clients_.find(client_id);
if (client_it == clients_.end())
return;
for (auto &buffer_it : 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_it.second == gfx::EMPTY_BUFFER)
continue;
// Skip shared memory buffers as they were not allocated using the factory.
if (buffer_it.second == gfx::SHARED_MEMORY_BUFFER)
continue;
gpu_memory_buffer_factory_host_->DestroyGpuMemoryBuffer(buffer_it.first,
client_id,
0);
}
clients_.erase(client_it);
}
void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferOnIO(
AllocateGpuMemoryBufferRequest* request) {
// Note: Unretained is safe as this is only used for synchronous allocation
// from a non-IO thread.
gpu_memory_buffer_factory_host_->CreateGpuMemoryBuffer(
g_next_gpu_memory_buffer_id.GetNext(), request->size, request->format,
request->usage, request->client_id, request->surface_id,
base::Bind(&BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO,
base::Unretained(this), base::Unretained(request)));
}
void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedOnIO(
AllocateGpuMemoryBufferRequest* request,
const gfx::GpuMemoryBufferHandle& handle) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Early out if factory failed to allocate the buffer.
if (handle.is_null()) {
request->event.Signal();
return;
}
DCHECK_NE(handle.type, gfx::SHARED_MEMORY_BUFFER);
request->result = GpuMemoryBufferImpl::CreateFromHandle(
handle,
request->size,
request->format,
base::Bind(&BrowserGpuMemoryBufferManager::GpuMemoryBufferDeleted,
weak_ptr_factory_.GetWeakPtr(),
handle.id,
request->client_id));
request->event.Signal();
}
void BrowserGpuMemoryBufferManager::GpuMemoryBufferDeleted(
gfx::GpuMemoryBufferId id,
int client_id,
uint32 sync_point) {
gpu_memory_buffer_factory_host_->DestroyGpuMemoryBuffer(id,
client_id,
sync_point);
}
void BrowserGpuMemoryBufferManager::GpuMemoryBufferAllocatedForChildProcess(
int child_client_id,
const AllocationCallback& callback,
const gfx::GpuMemoryBufferHandle& handle) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ClientMap::iterator client_it = clients_.find(child_client_id);
// This can happen if the child process is removed while the buffer is being
// allocated.
if (client_it == clients_.end()) {
if (!handle.is_null()) {
gpu_memory_buffer_factory_host_->DestroyGpuMemoryBuffer(
handle.id, child_client_id, 0);
}
callback.Run(gfx::GpuMemoryBufferHandle());
return;
}
BufferMap& buffers = client_it->second;
BufferMap::iterator buffer_it = buffers.find(handle.id);
DCHECK(buffer_it != buffers.end());
DCHECK_EQ(buffer_it->second, gfx::EMPTY_BUFFER);
if (handle.is_null()) {
buffers.erase(buffer_it);
callback.Run(gfx::GpuMemoryBufferHandle());
return;
}
// The factory should never return a shared memory backed buffer.
DCHECK_NE(handle.type, gfx::SHARED_MEMORY_BUFFER);
// Store the type of this buffer so it can be cleaned up if the child
// process is removed.
buffer_it->second = handle.type;
callback.Run(handle);
}
} // namespace content