blob: e03df4f64be90d31c6e741727e3818bf6cd1eb4a [file] [log] [blame]
// Copyright (c) 2013 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 "components/viz/client/client_shared_bitmap_manager.h"
#include <stddef.h>
#include <utility>
#include "base/debug/alias.h"
#include "base/process/memory.h"
#include "base/process/process_metrics.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/geometry/size.h"
namespace viz {
namespace {
class ClientSharedBitmap : public SharedBitmap {
public:
ClientSharedBitmap(
scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
shared_bitmap_allocation_notifier,
base::SharedMemory* shared_memory,
const SharedBitmapId& id,
uint32_t sequence_number)
: SharedBitmap(static_cast<uint8_t*>(shared_memory->memory()),
id,
sequence_number),
shared_bitmap_allocation_notifier_(
std::move(shared_bitmap_allocation_notifier)) {}
ClientSharedBitmap(
scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
shared_bitmap_allocation_notifier,
std::unique_ptr<base::SharedMemory> shared_memory_holder,
const SharedBitmapId& id,
uint32_t sequence_number)
: ClientSharedBitmap(std::move(shared_bitmap_allocation_notifier),
shared_memory_holder.get(),
id,
sequence_number) {
shared_memory_holder_ = std::move(shared_memory_holder);
}
~ClientSharedBitmap() override {
(*shared_bitmap_allocation_notifier_)->DidDeleteSharedBitmap(id());
}
// SharedBitmap:
base::SharedMemoryHandle GetSharedMemoryHandle() const override {
if (!shared_memory_holder_)
return base::SharedMemoryHandle();
return shared_memory_holder_->handle();
}
private:
scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
shared_bitmap_allocation_notifier_;
std::unique_ptr<base::SharedMemory> shared_memory_holder_;
};
// Collect extra information for debugging bitmap creation failures.
void CollectMemoryUsageAndDie(const gfx::Size& size, size_t alloc_size) {
#if defined(OS_WIN)
int width = size.width();
int height = size.height();
DWORD last_error = GetLastError();
std::unique_ptr<base::ProcessMetrics> metrics(
base::ProcessMetrics::CreateProcessMetrics(
base::GetCurrentProcessHandle()));
size_t private_bytes = 0;
size_t shared_bytes = 0;
metrics->GetMemoryBytes(&private_bytes, &shared_bytes);
base::debug::Alias(&width);
base::debug::Alias(&height);
base::debug::Alias(&last_error);
base::debug::Alias(&private_bytes);
base::debug::Alias(&shared_bytes);
#endif
base::TerminateBecauseOutOfMemory(alloc_size);
}
// Allocates a block of shared memory of the given size. Returns nullptr on
// failure.
std::unique_ptr<base::SharedMemory> AllocateSharedMemory(size_t buf_size) {
mojo::ScopedSharedBufferHandle mojo_buf =
mojo::SharedBufferHandle::Create(buf_size);
if (!mojo_buf->is_valid()) {
LOG(WARNING) << "Browser failed to allocate shared memory";
return nullptr;
}
base::SharedMemoryHandle shared_buf;
if (mojo::UnwrapSharedMemoryHandle(std::move(mojo_buf), &shared_buf, nullptr,
nullptr) != MOJO_RESULT_OK) {
LOG(WARNING) << "Browser failed to allocate shared memory";
return nullptr;
}
return std::make_unique<base::SharedMemory>(shared_buf, false);
}
} // namespace
ClientSharedBitmapManager::ClientSharedBitmapManager(
scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
shared_bitmap_allocation_notifier)
: shared_bitmap_allocation_notifier_(
std::move(shared_bitmap_allocation_notifier)) {}
ClientSharedBitmapManager::~ClientSharedBitmapManager() {}
std::unique_ptr<SharedBitmap> ClientSharedBitmapManager::AllocateSharedBitmap(
const gfx::Size& size) {
TRACE_EVENT2("renderer", "ClientSharedBitmapManager::AllocateSharedBitmap",
"width", size.width(), "height", size.height());
size_t memory_size;
if (!SharedBitmap::SizeInBytes(size, &memory_size))
return nullptr;
SharedBitmapId id = SharedBitmap::GenerateId();
std::unique_ptr<base::SharedMemory> memory =
AllocateSharedMemory(memory_size);
if (!memory || !memory->Map(memory_size))
CollectMemoryUsageAndDie(size, memory_size);
uint32_t sequence_number = NotifyAllocatedSharedBitmap(memory.get(), id);
// Close the associated FD to save resources, the previously mapped memory
// remains available.
memory->Close();
return std::make_unique<ClientSharedBitmap>(
shared_bitmap_allocation_notifier_, std::move(memory), id,
sequence_number);
}
std::unique_ptr<SharedBitmap> ClientSharedBitmapManager::GetSharedBitmapFromId(
const gfx::Size&,
const SharedBitmapId&) {
NOTREACHED();
return nullptr;
}
std::unique_ptr<SharedBitmap>
ClientSharedBitmapManager::GetBitmapForSharedMemory(base::SharedMemory* mem) {
SharedBitmapId id = SharedBitmap::GenerateId();
uint32_t sequence_number = NotifyAllocatedSharedBitmap(mem, id);
return std::make_unique<ClientSharedBitmap>(
shared_bitmap_allocation_notifier_, mem, id, sequence_number);
}
// Notifies the browser process that a shared bitmap with the given ID was
// allocated. Caller keeps ownership of |memory|.
uint32_t ClientSharedBitmapManager::NotifyAllocatedSharedBitmap(
base::SharedMemory* memory,
const SharedBitmapId& id) {
base::SharedMemoryHandle handle_to_send =
base::SharedMemory::DuplicateHandle(memory->handle());
if (!base::SharedMemory::IsHandleValid(handle_to_send)) {
LOG(ERROR) << "Failed to duplicate shared memory handle for bitmap.";
return 0;
}
mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle(
handle_to_send, memory->mapped_size(),
mojo::UnwrappedSharedMemoryHandleProtection::kReadWrite);
{
base::AutoLock lock(lock_);
(*shared_bitmap_allocation_notifier_)
->DidAllocateSharedBitmap(std::move(buffer_handle), id);
return ++last_sequence_number_;
}
}
} // namespace viz