| // Copyright 2019 The Dawn Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "dawn/native/BuddyMemoryAllocator.h" | |
| #include <utility> | |
| #include "dawn/common/Math.h" | |
| #include "dawn/native/ResourceHeapAllocator.h" | |
| namespace dawn::native { | |
| BuddyMemoryAllocator::BuddyMemoryAllocator(uint64_t maxSystemSize, | |
| uint64_t memoryBlockSize, | |
| ResourceHeapAllocator* heapAllocator) | |
| : mMemoryBlockSize(memoryBlockSize), | |
| mBuddyBlockAllocator(maxSystemSize), | |
| mHeapAllocator(heapAllocator) { | |
| ASSERT(memoryBlockSize <= maxSystemSize); | |
| ASSERT(IsPowerOfTwo(mMemoryBlockSize)); | |
| ASSERT(maxSystemSize % mMemoryBlockSize == 0); | |
| mTrackedSubAllocations.resize(maxSystemSize / mMemoryBlockSize); | |
| } | |
| BuddyMemoryAllocator::~BuddyMemoryAllocator() = default; | |
| uint64_t BuddyMemoryAllocator::GetMemoryIndex(uint64_t offset) const { | |
| ASSERT(offset != BuddyAllocator::kInvalidOffset); | |
| return offset / mMemoryBlockSize; | |
| } | |
| ResultOrError<ResourceMemoryAllocation> BuddyMemoryAllocator::Allocate(uint64_t allocationSize, | |
| uint64_t alignment) { | |
| ResourceMemoryAllocation invalidAllocation = ResourceMemoryAllocation{}; | |
| if (allocationSize == 0) { | |
| return std::move(invalidAllocation); | |
| } | |
| // Check the unaligned size to avoid overflowing NextPowerOfTwo. | |
| if (allocationSize > mMemoryBlockSize) { | |
| return std::move(invalidAllocation); | |
| } | |
| // Round allocation size to nearest power-of-two. | |
| allocationSize = NextPowerOfTwo(allocationSize); | |
| // Allocation cannot exceed the memory size. | |
| if (allocationSize > mMemoryBlockSize) { | |
| return std::move(invalidAllocation); | |
| } | |
| // Attempt to sub-allocate a block of the requested size. | |
| const uint64_t blockOffset = mBuddyBlockAllocator.Allocate(allocationSize, alignment); | |
| if (blockOffset == BuddyAllocator::kInvalidOffset) { | |
| return std::move(invalidAllocation); | |
| } | |
| const uint64_t memoryIndex = GetMemoryIndex(blockOffset); | |
| if (mTrackedSubAllocations[memoryIndex].refcount == 0) { | |
| // Transfer ownership to this allocator | |
| std::unique_ptr<ResourceHeapBase> memory; | |
| DAWN_TRY_ASSIGN(memory, mHeapAllocator->AllocateResourceHeap(mMemoryBlockSize)); | |
| mTrackedSubAllocations[memoryIndex] = {/*refcount*/ 0, std::move(memory)}; | |
| } | |
| mTrackedSubAllocations[memoryIndex].refcount++; | |
| AllocationInfo info; | |
| info.mBlockOffset = blockOffset; | |
| info.mMethod = AllocationMethod::kSubAllocated; | |
| // Allocation offset is always local to the memory. | |
| const uint64_t memoryOffset = blockOffset % mMemoryBlockSize; | |
| return ResourceMemoryAllocation{info, memoryOffset, | |
| mTrackedSubAllocations[memoryIndex].mMemoryAllocation.get()}; | |
| } | |
| void BuddyMemoryAllocator::Deallocate(const ResourceMemoryAllocation& allocation) { | |
| const AllocationInfo info = allocation.GetInfo(); | |
| ASSERT(info.mMethod == AllocationMethod::kSubAllocated); | |
| const uint64_t memoryIndex = GetMemoryIndex(info.mBlockOffset); | |
| ASSERT(mTrackedSubAllocations[memoryIndex].refcount > 0); | |
| mTrackedSubAllocations[memoryIndex].refcount--; | |
| if (mTrackedSubAllocations[memoryIndex].refcount == 0) { | |
| mHeapAllocator->DeallocateResourceHeap( | |
| std::move(mTrackedSubAllocations[memoryIndex].mMemoryAllocation)); | |
| } | |
| mBuddyBlockAllocator.Deallocate(info.mBlockOffset); | |
| } | |
| uint64_t BuddyMemoryAllocator::GetMemoryBlockSize() const { | |
| return mMemoryBlockSize; | |
| } | |
| uint64_t BuddyMemoryAllocator::ComputeTotalNumOfHeapsForTesting() const { | |
| uint64_t count = 0; | |
| for (const TrackedSubAllocations& allocation : mTrackedSubAllocations) { | |
| if (allocation.refcount > 0) { | |
| count++; | |
| } | |
| } | |
| return count; | |
| } | |
| } // namespace dawn::native |