|  | // Copyright (c) 2011 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 "gpu/command_buffer/client/mapped_memory.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <functional> | 
|  |  | 
|  | #include "base/atomic_sequence_num.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "base/trace_event/memory_dump_manager.h" | 
|  | #include "base/trace_event/trace_event.h" | 
|  | #include "gpu/command_buffer/client/cmd_buffer_helper.h" | 
|  | #include "gpu/command_buffer/client/shared_memory_limits.h" | 
|  | #include "gpu/command_buffer/common/buffer.h" | 
|  |  | 
|  | namespace gpu { | 
|  | namespace { | 
|  |  | 
|  | // Generates process-unique IDs to use for tracing a MappedMemoryManager's | 
|  | // chunks. | 
|  | base::StaticAtomicSequenceNumber g_next_mapped_memory_manager_tracing_id; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | MemoryChunk::MemoryChunk(int32_t shm_id, | 
|  | scoped_refptr<gpu::Buffer> shm, | 
|  | CommandBufferHelper* helper) | 
|  | : shm_id_(shm_id), | 
|  | shm_(shm), | 
|  | allocator_(shm->size(), helper, shm->memory()) {} | 
|  |  | 
|  | MemoryChunk::~MemoryChunk() {} | 
|  |  | 
|  | MappedMemoryManager::MappedMemoryManager(CommandBufferHelper* helper, | 
|  | size_t unused_memory_reclaim_limit) | 
|  | : chunk_size_multiple_(FencedAllocator::kAllocAlignment), | 
|  | helper_(helper), | 
|  | allocated_memory_(0), | 
|  | max_free_bytes_(unused_memory_reclaim_limit), | 
|  | max_allocated_bytes_(SharedMemoryLimits::kNoLimit), | 
|  | tracing_id_(g_next_mapped_memory_manager_tracing_id.GetNext()) { | 
|  | // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview). | 
|  | // Don't register a dump provider in these cases. | 
|  | // TODO(ericrk): Get this working in Android Webview. crbug.com/517156 | 
|  | if (base::ThreadTaskRunnerHandle::IsSet()) { | 
|  | base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | 
|  | this, "gpu::MappedMemoryManager", base::ThreadTaskRunnerHandle::Get()); | 
|  | } | 
|  | } | 
|  |  | 
|  | MappedMemoryManager::~MappedMemoryManager() { | 
|  | base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | 
|  | this); | 
|  |  | 
|  | CommandBuffer* cmd_buf = helper_->command_buffer(); | 
|  | for (auto& chunk : chunks_) { | 
|  | cmd_buf->DestroyTransferBuffer(chunk->shm_id()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void* MappedMemoryManager::Alloc(unsigned int size, | 
|  | int32_t* shm_id, | 
|  | unsigned int* shm_offset) { | 
|  | DCHECK(shm_id); | 
|  | DCHECK(shm_offset); | 
|  | if (size <= allocated_memory_) { | 
|  | size_t total_bytes_in_use = 0; | 
|  | // See if any of the chunks can satisfy this request. | 
|  | for (auto& chunk : chunks_) { | 
|  | chunk->FreeUnused(); | 
|  | total_bytes_in_use += chunk->bytes_in_use(); | 
|  | if (chunk->GetLargestFreeSizeWithoutWaiting() >= size) { | 
|  | void* mem = chunk->Alloc(size); | 
|  | DCHECK(mem); | 
|  | *shm_id = chunk->shm_id(); | 
|  | *shm_offset = chunk->GetOffset(mem); | 
|  | return mem; | 
|  | } | 
|  | } | 
|  |  | 
|  | // If there is a memory limit being enforced and total free | 
|  | // memory (allocated_memory_ - total_bytes_in_use) is larger than | 
|  | // the limit try waiting. | 
|  | if (max_free_bytes_ != SharedMemoryLimits::kNoLimit && | 
|  | (allocated_memory_ - total_bytes_in_use) >= max_free_bytes_) { | 
|  | TRACE_EVENT0("gpu", "MappedMemoryManager::Alloc::wait"); | 
|  | for (auto& chunk : chunks_) { | 
|  | if (chunk->GetLargestFreeSizeWithWaiting() >= size) { | 
|  | void* mem = chunk->Alloc(size); | 
|  | DCHECK(mem); | 
|  | *shm_id = chunk->shm_id(); | 
|  | *shm_offset = chunk->GetOffset(mem); | 
|  | return mem; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (max_allocated_bytes_ != SharedMemoryLimits::kNoLimit && | 
|  | (allocated_memory_ + size) > max_allocated_bytes_) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Make a new chunk to satisfy the request. | 
|  | CommandBuffer* cmd_buf = helper_->command_buffer(); | 
|  | unsigned int chunk_size = | 
|  | ((size + chunk_size_multiple_ - 1) / chunk_size_multiple_) * | 
|  | chunk_size_multiple_; | 
|  | int32_t id = -1; | 
|  | scoped_refptr<gpu::Buffer> shm = | 
|  | cmd_buf->CreateTransferBuffer(chunk_size, &id); | 
|  | if (id  < 0) | 
|  | return NULL; | 
|  | DCHECK(shm.get()); | 
|  | MemoryChunk* mc = new MemoryChunk(id, shm, helper_); | 
|  | allocated_memory_ += mc->GetSize(); | 
|  | chunks_.push_back(base::WrapUnique(mc)); | 
|  | void* mem = mc->Alloc(size); | 
|  | DCHECK(mem); | 
|  | *shm_id = mc->shm_id(); | 
|  | *shm_offset = mc->GetOffset(mem); | 
|  | return mem; | 
|  | } | 
|  |  | 
|  | void MappedMemoryManager::Free(void* pointer) { | 
|  | for (auto& chunk : chunks_) { | 
|  | if (chunk->IsInChunk(pointer)) { | 
|  | chunk->Free(pointer); | 
|  | return; | 
|  | } | 
|  | } | 
|  | NOTREACHED(); | 
|  | } | 
|  |  | 
|  | void MappedMemoryManager::FreePendingToken(void* pointer, int32_t token) { | 
|  | for (auto& chunk : chunks_) { | 
|  | if (chunk->IsInChunk(pointer)) { | 
|  | chunk->FreePendingToken(pointer, token); | 
|  | return; | 
|  | } | 
|  | } | 
|  | NOTREACHED(); | 
|  | } | 
|  |  | 
|  | void MappedMemoryManager::FreeUnused() { | 
|  | CommandBuffer* cmd_buf = helper_->command_buffer(); | 
|  | MemoryChunkVector::iterator iter = chunks_.begin(); | 
|  | while (iter != chunks_.end()) { | 
|  | MemoryChunk* chunk = (*iter).get(); | 
|  | chunk->FreeUnused(); | 
|  | if (!chunk->InUse()) { | 
|  | cmd_buf->DestroyTransferBuffer(chunk->shm_id()); | 
|  | allocated_memory_ -= chunk->GetSize(); | 
|  | iter = chunks_.erase(iter); | 
|  | } else { | 
|  | ++iter; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool MappedMemoryManager::OnMemoryDump( | 
|  | const base::trace_event::MemoryDumpArgs& args, | 
|  | base::trace_event::ProcessMemoryDump* pmd) { | 
|  | const uint64_t tracing_process_id = | 
|  | base::trace_event::MemoryDumpManager::GetInstance() | 
|  | ->GetTracingProcessId(); | 
|  |  | 
|  | for (const auto& chunk : chunks_) { | 
|  | std::string dump_name = base::StringPrintf( | 
|  | "gpu/mapped_memory/manager_%d/chunk_%d", tracing_id_, chunk->shm_id()); | 
|  | base::trace_event::MemoryAllocatorDump* dump = | 
|  | pmd->CreateAllocatorDump(dump_name); | 
|  |  | 
|  | dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, | 
|  | base::trace_event::MemoryAllocatorDump::kUnitsBytes, | 
|  | chunk->GetSize()); | 
|  | dump->AddScalar("free_size", | 
|  | base::trace_event::MemoryAllocatorDump::kUnitsBytes, | 
|  | chunk->GetFreeSize()); | 
|  |  | 
|  | auto guid = GetBufferGUIDForTracing(tracing_process_id, chunk->shm_id()); | 
|  |  | 
|  | const int kImportance = 2; | 
|  | pmd->CreateSharedGlobalAllocatorDump(guid); | 
|  | pmd->AddOwnershipEdge(dump->guid(), guid, kImportance); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void ScopedMappedMemoryPtr::Release() { | 
|  | if (buffer_) { | 
|  | mapped_memory_manager_->FreePendingToken(buffer_, helper_->InsertToken()); | 
|  | buffer_ = nullptr; | 
|  | size_ = 0; | 
|  | shm_id_ = 0; | 
|  | shm_offset_ = 0; | 
|  |  | 
|  | if (flush_after_release_) | 
|  | helper_->CommandBufferHelper::Flush(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ScopedMappedMemoryPtr::Reset(uint32_t new_size) { | 
|  | Release(); | 
|  |  | 
|  | if (new_size) { | 
|  | buffer_ = mapped_memory_manager_->Alloc(new_size, &shm_id_, &shm_offset_); | 
|  | size_ = buffer_ ? new_size : 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace gpu |