blob: a4dc298346e411b2ad535f9647692ac7f28be970 [file]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GPU_COMMAND_BUFFER_CLIENT_MAPPED_MEMORY_H_
#define GPU_COMMAND_BUFFER_CLIENT_MAPPED_MEMORY_H_
#include <stddef.h>
#include <stdint.h>
#include <bit>
#include <memory>
#include <type_traits>
#include <utility>
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_span.h"
#include "base/numerics/checked_math.h"
#include "base/trace_event/memory_dump_provider.h"
#include "gpu/command_buffer/client/fenced_allocator.h"
#include "gpu/command_buffer/client/gpu_command_buffer_client_export.h"
#include "gpu/command_buffer/common/buffer.h"
#include "gpu/command_buffer/common/constants.h"
namespace gpu {
class CommandBufferHelper;
class ScopedDedicatedChunk;
// Manages a shared memory segment.
class GPU_COMMAND_BUFFER_CLIENT_EXPORT MemoryChunk {
public:
MemoryChunk(int32_t shm_id,
scoped_refptr<gpu::Buffer> shm,
CommandBufferHelper* helper);
MemoryChunk(const MemoryChunk&) = delete;
MemoryChunk& operator=(const MemoryChunk&) = delete;
~MemoryChunk();
// Gets the size of the largest free block that is available without waiting.
uint32_t GetLargestFreeSizeWithoutWaiting() {
return allocator_.GetLargestFreeSize();
}
// Gets the size of the largest free block that can be allocated if the
// caller can wait.
uint32_t GetLargestFreeSizeWithWaiting() {
return allocator_.GetLargestFreeOrPendingSize();
}
// Gets the size of the chunk.
uint32_t GetSize() const { return shm_->size(); }
// The shared memory id for this chunk.
int32_t shm_id() const { return shm_id_; }
gpu::Buffer* shared_memory() const { return shm_.get(); }
// Allocates a block of memory. If the buffer is out of directly available
// memory, this function may wait until memory that was freed "pending a
// token" can be re-used.
//
// Parameters:
// size: the size of the memory block to allocate.
//
// Returns:
// the span to the allocated memory block, or an empty span if out of
// memory.
base::span<uint8_t> Alloc(uint32_t size) { return allocator_.Alloc(size); }
// Gets the offset to a memory block given the base memory and the address.
// It translates nullptr to FencedAllocator::kInvalidOffset.
uint32_t GetOffset(void* pointer) { return allocator_.GetOffset(pointer); }
// Frees a block of memory.
//
// Parameters:
// pointer: the pointer to the memory block to free.
void Free(void* pointer) {
allocator_.Free(pointer);
}
// Frees a block of memory, pending the passage of a token. That memory won't
// be re-allocated until the token has passed through the command stream.
//
// Parameters:
// pointer: the pointer to the memory block to free.
// token: the token value to wait for before re-using the memory.
void FreePendingToken(void* pointer, uint32_t token) {
allocator_.FreePendingToken(pointer, token);
}
// Frees any blocks whose tokens have passed.
void FreeUnused() {
allocator_.FreeUnused();
}
// Gets the free size of the chunk.
uint32_t GetFreeSize() { return allocator_.GetFreeSize(); }
// Returns true if pointer is in the range of this block.
bool IsInChunk(void* pointer) const {
return pointer >= shm_->memory() && pointer <= &shm_->as_byte_span().back();
}
// Returns true of any memory in this chunk is in use or free pending token.
bool InUseOrFreePending() { return allocator_.InUseOrFreePending(); }
uint32_t bytes_in_use() const { return allocator_.bytes_in_use(); }
FencedAllocator::State GetPointerStatusForTest(void* pointer,
int32_t* token_if_pending) {
return allocator_.GetPointerStatusForTest(pointer, token_if_pending);
}
private:
int32_t shm_id_;
scoped_refptr<gpu::Buffer> shm_;
FencedAllocatorWrapper allocator_;
};
// Manages MemoryChunks.
class GPU_COMMAND_BUFFER_CLIENT_EXPORT MappedMemoryManager {
public:
enum MemoryLimit {
kNoLimit = 0,
};
// |unused_memory_reclaim_limit|: When exceeded this causes pending memory
// to be reclaimed before allocating more memory.
MappedMemoryManager(CommandBufferHelper* helper,
size_t unused_memory_reclaim_limit);
MappedMemoryManager(const MappedMemoryManager&) = delete;
MappedMemoryManager& operator=(const MappedMemoryManager&) = delete;
~MappedMemoryManager();
uint32_t chunk_size_multiple() const { return chunk_size_multiple_; }
void set_chunk_size_multiple(uint32_t multiple) {
DCHECK(std::has_single_bit(multiple));
DCHECK_GE(multiple, FencedAllocator::kAllocAlignment);
chunk_size_multiple_ = multiple;
}
size_t max_allocated_bytes() const {
return max_allocated_bytes_;
}
void set_max_allocated_bytes(size_t max_allocated_bytes) {
max_allocated_bytes_ = max_allocated_bytes;
}
// Caps the total size of dedicated chunks. AllocDedicatedChunk() fails once
// the cap is reached so callers can fall back to pooled chunks.
void set_max_dedicated_bytes_for_testing(size_t max_dedicated_bytes) {
max_dedicated_bytes_ = max_dedicated_bytes;
}
// Allocates a block of memory
// Parameters:
// size: size of memory to allocate.
// shm_id: pointer to variable to receive the shared memory id.
// shm_offset: pointer to variable to receive the shared memory offset.
// option: defaults to kLoseContextOnOOM, but may be kReturnNullOnOOM.
// Passing kReturnNullOnOOM will gracefully fail and return empty
// span on OOM instead of losing the context. Callers should be
// careful to check error conditions.
// Returns:
// span of allocated block of memory. Empty span if failure.
base::span<uint8_t> Alloc(
uint32_t size,
int32_t* shm_id,
uint32_t* shm_offset,
TransferBufferAllocationOption option =
TransferBufferAllocationOption::kLoseContextOnOOM);
// Allocates a block of typed memory, using reinterpret_span to convert
// the raw byte buffer to the desired type.
//
// Parameters:
// count: the number of T-typed elements to allocate.
// shm_id: pointer to variable to receive the shared memory id.
// shm_offset: pointer to variable to receive the shared memory offset.
// option: defaults to kLoseContextOnOOM, but may be kReturnNullOnOOM.
// Passing kReturnNullOnOOM will gracefully fail and return empty
// span on OOM instead of losing the context. Callers should be
// careful to check error conditions.
//
// Returns:
// span of allocated block of typed memory. Empty span if failure.
template <typename T>
base::span<T> AllocTyped(
uint32_t count,
int32_t* shm_id,
uint32_t* shm_offset,
TransferBufferAllocationOption option =
TransferBufferAllocationOption::kLoseContextOnOOM) {
static_assert(std::is_trivially_copyable_v<T>,
"AllocTyped only supports trivially copyable types");
uint32_t byte_size = 0;
if (!base::CheckMul(count, sizeof(T)).AssignIfValid(&byte_size)) {
return {};
}
base::span<uint8_t> buffer = Alloc(byte_size, shm_id, shm_offset, option);
if (buffer.empty()) {
return {};
}
return base::subtle::reinterpret_span<T>(buffer);
}
// Allocates a whole new chunk dedicated to a single allocation, guaranteeing
// that the shared memory offset is always 0.
//
// Parameters:
// size: size of memory to allocate.
// Returns:
// an RAII handle to the allocated chunk that releases it (and destroys
// its transfer buffer) on destruction. Invalid handle on OOM; callers
// should be careful to check error conditions.
ScopedDedicatedChunk AllocDedicatedChunk(uint32_t size);
// Frees a block of memory.
//
// Parameters:
// pointer: the pointer to the memory block to free.
void Free(void* pointer);
// Removes a dedicated chunk and destroys its transfer buffer. The caller
// must have already written all commands referencing `shm_id` into the
// command buffer.
//
// Normally called internally by ScopedDedicatedChunk's destructor; exposed
// for callers that need to release a chunk early.
void RemoveDedicatedChunk(int32_t shm_id);
// Frees a block of memory, pending the passage of a token. That memory won't
// be re-allocated until the token has passed through the command stream.
//
// Parameters:
// pointer: the pointer to the memory block to free.
// token: the token value to wait for before re-using the memory.
void FreePendingToken(void* pointer, int32_t token);
// Free Any Shared memory that is not in use.
void FreeUnused();
// Dump memory usage - called from GLES2Implementation.
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd);
// Used for testing
size_t num_chunks() const {
return chunks_.size() + dedicated_chunks_.size();
}
size_t bytes_in_use() const {
size_t bytes_in_use = 0;
for (size_t ii = 0; ii < chunks_.size(); ++ii) {
bytes_in_use += chunks_[ii]->bytes_in_use();
}
for (const auto& entry : dedicated_chunks_) {
bytes_in_use += entry.second->size();
}
return bytes_in_use;
}
// Used for testing
size_t allocated_memory() const {
return allocated_memory_;
}
size_t dedicated_memory_for_testing() const {
return dedicated_memory_for_testing_;
}
// Gets the status of a previous allocation, as well as the corresponding
// token if FREE_PENDING_TOKEN (and token_if_pending is not null).
FencedAllocator::State GetPointerStatusForTest(void* pointer,
int32_t* token_if_pending);
private:
typedef std::vector<std::unique_ptr<MemoryChunk>> MemoryChunkVector;
// size a chunk is rounded up to.
uint32_t chunk_size_multiple_;
raw_ptr<CommandBufferHelper> helper_;
// Chunks that can be sub-allocated from and reused; never dedicated.
MemoryChunkVector chunks_;
// Chunks allocated via AllocDedicatedChunk(), keyed by shm_id.
base::flat_map<int32_t, scoped_refptr<gpu::Buffer>> dedicated_chunks_;
size_t allocated_memory_;
size_t max_free_bytes_;
size_t max_allocated_bytes_;
size_t dedicated_memory_for_testing_;
size_t max_dedicated_bytes_;
// A process-unique ID used for disambiguating memory dumps from different
// mapped memory manager.
int tracing_id_;
};
// RAII handle for a chunk allocated via
// MappedMemoryManager::AllocDedicatedChunk(). Releases the chunk (and
// destroys its transfer buffer) when destroyed, moved-from, or Reset().
//
// Must not outlive the MappedMemoryManager it was allocated from: it holds a
// non-owning pointer back to the manager and calls RemoveDedicatedChunk() on
// it, so destroying the manager first would leave that pointer dangling.
// (MappedMemoryManager's destructor DCHECKs that no dedicated chunk is still
// outstanding.)
class GPU_COMMAND_BUFFER_CLIENT_EXPORT ScopedDedicatedChunk {
public:
ScopedDedicatedChunk();
ScopedDedicatedChunk(base::span<uint8_t> span,
int32_t shm_id,
MappedMemoryManager* manager);
ScopedDedicatedChunk(const ScopedDedicatedChunk&) = delete;
ScopedDedicatedChunk& operator=(const ScopedDedicatedChunk&) = delete;
ScopedDedicatedChunk(ScopedDedicatedChunk&& other);
ScopedDedicatedChunk& operator=(ScopedDedicatedChunk&& other);
~ScopedDedicatedChunk();
bool valid() const { return manager_ != nullptr; }
int32_t shm_id() const { return shm_id_; }
base::span<uint8_t> span() const { return span_; }
// Releases the chunk early. Safe to call on an already-empty instance.
void Reset();
private:
base::raw_span<uint8_t> span_;
int32_t shm_id_ = -1;
raw_ptr<MappedMemoryManager> manager_ = nullptr;
};
// A class that will manage the lifetime of a mapped memory allocation
class GPU_COMMAND_BUFFER_CLIENT_EXPORT ScopedMappedMemoryPtr {
public:
ScopedMappedMemoryPtr(uint32_t size,
CommandBufferHelper* helper,
MappedMemoryManager* mapped_memory_manager)
: helper_(helper), mapped_memory_manager_(mapped_memory_manager) {
Reset(size);
}
ScopedMappedMemoryPtr(const ScopedMappedMemoryPtr&) = delete;
ScopedMappedMemoryPtr& operator=(const ScopedMappedMemoryPtr&) = delete;
~ScopedMappedMemoryPtr() {
Release();
}
bool valid() const { return buffer_.data() != nullptr; }
void SetFlushAfterRelease(bool flush_after_release) {
flush_after_release_ = flush_after_release;
}
uint32_t size() const { return buffer_.size(); }
int32_t shm_id() const {
return shm_id_;
}
uint32_t offset() const {
return shm_offset_;
}
void* address() const { return buffer_.data(); }
base::span<uint8_t> as_byte_span() { return buffer_; }
base::span<const uint8_t> as_byte_span() const { return buffer_; }
void Release();
void Reset(uint32_t new_size);
private:
base::raw_span<uint8_t> buffer_;
int32_t shm_id_ = 0;
uint32_t shm_offset_ = 0;
bool flush_after_release_ = false;
raw_ptr<CommandBufferHelper> helper_;
raw_ptr<MappedMemoryManager> mapped_memory_manager_;
};
} // namespace gpu
#endif // GPU_COMMAND_BUFFER_CLIENT_MAPPED_MEMORY_H_