1 // Copyright 2019 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "dawn_native/BuddyMemoryAllocator.h" 16 17 #include "common/Math.h" 18 #include "dawn_native/ResourceHeapAllocator.h" 19 20 namespace dawn_native { 21 BuddyMemoryAllocator(uint64_t maxSystemSize,uint64_t memoryBlockSize,ResourceHeapAllocator * heapAllocator)22 BuddyMemoryAllocator::BuddyMemoryAllocator(uint64_t maxSystemSize, 23 uint64_t memoryBlockSize, 24 ResourceHeapAllocator* heapAllocator) 25 : mMemoryBlockSize(memoryBlockSize), 26 mBuddyBlockAllocator(maxSystemSize), 27 mHeapAllocator(heapAllocator) { 28 ASSERT(memoryBlockSize <= maxSystemSize); 29 ASSERT(IsPowerOfTwo(mMemoryBlockSize)); 30 ASSERT(maxSystemSize % mMemoryBlockSize == 0); 31 32 mTrackedSubAllocations.resize(maxSystemSize / mMemoryBlockSize); 33 } 34 GetMemoryIndex(uint64_t offset) const35 uint64_t BuddyMemoryAllocator::GetMemoryIndex(uint64_t offset) const { 36 ASSERT(offset != BuddyAllocator::kInvalidOffset); 37 return offset / mMemoryBlockSize; 38 } 39 Allocate(uint64_t allocationSize,uint64_t alignment)40 ResultOrError<ResourceMemoryAllocation> BuddyMemoryAllocator::Allocate(uint64_t allocationSize, 41 uint64_t alignment) { 42 ResourceMemoryAllocation invalidAllocation = ResourceMemoryAllocation{}; 43 44 if (allocationSize == 0) { 45 return std::move(invalidAllocation); 46 } 47 48 // Check the unaligned size to avoid overflowing NextPowerOfTwo. 49 if (allocationSize > mMemoryBlockSize) { 50 return std::move(invalidAllocation); 51 } 52 53 // Round allocation size to nearest power-of-two. 54 allocationSize = NextPowerOfTwo(allocationSize); 55 56 // Allocation cannot exceed the memory size. 57 if (allocationSize > mMemoryBlockSize) { 58 return std::move(invalidAllocation); 59 } 60 61 // Attempt to sub-allocate a block of the requested size. 62 const uint64_t blockOffset = mBuddyBlockAllocator.Allocate(allocationSize, alignment); 63 if (blockOffset == BuddyAllocator::kInvalidOffset) { 64 return std::move(invalidAllocation); 65 } 66 67 const uint64_t memoryIndex = GetMemoryIndex(blockOffset); 68 if (mTrackedSubAllocations[memoryIndex].refcount == 0) { 69 // Transfer ownership to this allocator 70 std::unique_ptr<ResourceHeapBase> memory; 71 DAWN_TRY_ASSIGN(memory, mHeapAllocator->AllocateResourceHeap(mMemoryBlockSize)); 72 mTrackedSubAllocations[memoryIndex] = {/*refcount*/ 0, std::move(memory)}; 73 } 74 75 mTrackedSubAllocations[memoryIndex].refcount++; 76 77 AllocationInfo info; 78 info.mBlockOffset = blockOffset; 79 info.mMethod = AllocationMethod::kSubAllocated; 80 81 // Allocation offset is always local to the memory. 82 const uint64_t memoryOffset = blockOffset % mMemoryBlockSize; 83 84 return ResourceMemoryAllocation{ 85 info, memoryOffset, mTrackedSubAllocations[memoryIndex].mMemoryAllocation.get()}; 86 } 87 Deallocate(const ResourceMemoryAllocation & allocation)88 void BuddyMemoryAllocator::Deallocate(const ResourceMemoryAllocation& allocation) { 89 const AllocationInfo info = allocation.GetInfo(); 90 91 ASSERT(info.mMethod == AllocationMethod::kSubAllocated); 92 93 const uint64_t memoryIndex = GetMemoryIndex(info.mBlockOffset); 94 95 ASSERT(mTrackedSubAllocations[memoryIndex].refcount > 0); 96 mTrackedSubAllocations[memoryIndex].refcount--; 97 98 if (mTrackedSubAllocations[memoryIndex].refcount == 0) { 99 mHeapAllocator->DeallocateResourceHeap( 100 std::move(mTrackedSubAllocations[memoryIndex].mMemoryAllocation)); 101 } 102 103 mBuddyBlockAllocator.Deallocate(info.mBlockOffset); 104 } 105 GetMemoryBlockSize() const106 uint64_t BuddyMemoryAllocator::GetMemoryBlockSize() const { 107 return mMemoryBlockSize; 108 } 109 ComputeTotalNumOfHeapsForTesting() const110 uint64_t BuddyMemoryAllocator::ComputeTotalNumOfHeapsForTesting() const { 111 uint64_t count = 0; 112 for (const TrackedSubAllocations& allocation : mTrackedSubAllocations) { 113 if (allocation.refcount > 0) { 114 count++; 115 } 116 } 117 return count; 118 } 119 120 } // namespace dawn_native 121