mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-09 13:39:20 +00:00
Resource Management 7: Device memory sub-allocation using buddy allocator.
Uses a large buddy system to allocate binary sized heaps on-demand. BUG=dawn:27 Change-Id: I72e425c23e601da6ee53827423bef7ff13be049c Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/10880 Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
This commit is contained in:
parent
277d2e15d5
commit
21dfc91954
4
BUILD.gn
4
BUILD.gn
@ -143,6 +143,8 @@ source_set("libdawn_native_sources") {
|
|||||||
"src/dawn_native/BindGroupTracker.h",
|
"src/dawn_native/BindGroupTracker.h",
|
||||||
"src/dawn_native/BuddyAllocator.cpp",
|
"src/dawn_native/BuddyAllocator.cpp",
|
||||||
"src/dawn_native/BuddyAllocator.h",
|
"src/dawn_native/BuddyAllocator.h",
|
||||||
|
"src/dawn_native/BuddyMemoryAllocator.cpp",
|
||||||
|
"src/dawn_native/BuddyMemoryAllocator.h",
|
||||||
"src/dawn_native/Buffer.cpp",
|
"src/dawn_native/Buffer.cpp",
|
||||||
"src/dawn_native/Buffer.h",
|
"src/dawn_native/Buffer.h",
|
||||||
"src/dawn_native/CommandAllocator.cpp",
|
"src/dawn_native/CommandAllocator.cpp",
|
||||||
@ -186,6 +188,7 @@ source_set("libdawn_native_sources") {
|
|||||||
"src/dawn_native/Forward.h",
|
"src/dawn_native/Forward.h",
|
||||||
"src/dawn_native/Instance.cpp",
|
"src/dawn_native/Instance.cpp",
|
||||||
"src/dawn_native/Instance.h",
|
"src/dawn_native/Instance.h",
|
||||||
|
"src/dawn_native/MemoryAllocator.h",
|
||||||
"src/dawn_native/ObjectBase.cpp",
|
"src/dawn_native/ObjectBase.cpp",
|
||||||
"src/dawn_native/ObjectBase.h",
|
"src/dawn_native/ObjectBase.h",
|
||||||
"src/dawn_native/PassResourceUsage.h",
|
"src/dawn_native/PassResourceUsage.h",
|
||||||
@ -762,6 +765,7 @@ test("dawn_unittests") {
|
|||||||
sources += [
|
sources += [
|
||||||
"src/tests/unittests/BitSetIteratorTests.cpp",
|
"src/tests/unittests/BitSetIteratorTests.cpp",
|
||||||
"src/tests/unittests/BuddyAllocatorTests.cpp",
|
"src/tests/unittests/BuddyAllocatorTests.cpp",
|
||||||
|
"src/tests/unittests/BuddyMemoryAllocatorTests.cpp",
|
||||||
"src/tests/unittests/CommandAllocatorTests.cpp",
|
"src/tests/unittests/CommandAllocatorTests.cpp",
|
||||||
"src/tests/unittests/EnumClassBitmasksTests.cpp",
|
"src/tests/unittests/EnumClassBitmasksTests.cpp",
|
||||||
"src/tests/unittests/ErrorTests.cpp",
|
"src/tests/unittests/ErrorTests.cpp",
|
||||||
|
104
src/dawn_native/BuddyMemoryAllocator.cpp
Normal file
104
src/dawn_native/BuddyMemoryAllocator.cpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// 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 "common/Math.h"
|
||||||
|
|
||||||
|
namespace dawn_native {
|
||||||
|
|
||||||
|
BuddyMemoryAllocator::BuddyMemoryAllocator(uint64_t maxBlockSize,
|
||||||
|
uint64_t memorySize,
|
||||||
|
std::unique_ptr<MemoryAllocator> client)
|
||||||
|
: mMemorySize(memorySize), mBuddyBlockAllocator(maxBlockSize), mClient(std::move(client)) {
|
||||||
|
ASSERT(memorySize <= maxBlockSize);
|
||||||
|
ASSERT(IsPowerOfTwo(mMemorySize));
|
||||||
|
ASSERT(maxBlockSize % mMemorySize == 0);
|
||||||
|
|
||||||
|
mTrackedSubAllocations.resize(maxBlockSize / mMemorySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t BuddyMemoryAllocator::GetMemoryIndex(uint64_t offset) const {
|
||||||
|
ASSERT(offset != BuddyAllocator::kInvalidOffset);
|
||||||
|
return offset / mMemorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultOrError<ResourceMemoryAllocation> BuddyMemoryAllocator::Allocate(uint64_t allocationSize,
|
||||||
|
uint64_t alignment,
|
||||||
|
int memoryFlags) {
|
||||||
|
ResourceMemoryAllocation invalidAllocation = ResourceMemoryAllocation{};
|
||||||
|
|
||||||
|
// Allocation cannot exceed the memory size.
|
||||||
|
if (allocationSize == 0 || allocationSize > mMemorySize) {
|
||||||
|
return invalidAllocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to sub-allocate a block of the requested size.
|
||||||
|
const uint64_t blockOffset = mBuddyBlockAllocator.Allocate(allocationSize, alignment);
|
||||||
|
if (blockOffset == BuddyAllocator::kInvalidOffset) {
|
||||||
|
return 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, mClient->Allocate(mMemorySize, memoryFlags));
|
||||||
|
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 % mMemorySize;
|
||||||
|
|
||||||
|
return ResourceMemoryAllocation{
|
||||||
|
info, memoryOffset, mTrackedSubAllocations[memoryIndex].mMemoryAllocation.get()};
|
||||||
|
} // namespace dawn_native
|
||||||
|
|
||||||
|
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) {
|
||||||
|
mClient->Deallocate(std::move(mTrackedSubAllocations[memoryIndex].mMemoryAllocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
mBuddyBlockAllocator.Deallocate(info.mBlockOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t BuddyMemoryAllocator::GetMemorySize() const {
|
||||||
|
return mMemorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t BuddyMemoryAllocator::ComputeTotalNumOfHeapsForTesting() const {
|
||||||
|
uint64_t count = 0;
|
||||||
|
for (const TrackedSubAllocations& allocation : mTrackedSubAllocations) {
|
||||||
|
if (allocation.refcount > 0) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
} // namespace dawn_native
|
73
src/dawn_native/BuddyMemoryAllocator.h
Normal file
73
src/dawn_native/BuddyMemoryAllocator.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef DAWNNATIVE_BUDDYMEMORYALLOCATOR_H_
|
||||||
|
#define DAWNNATIVE_BUDDYMEMORYALLOCATOR_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "dawn_native/BuddyAllocator.h"
|
||||||
|
#include "dawn_native/MemoryAllocator.h"
|
||||||
|
#include "dawn_native/ResourceMemoryAllocation.h"
|
||||||
|
|
||||||
|
namespace dawn_native {
|
||||||
|
// BuddyMemoryAllocator uses the buddy allocator to sub-allocate blocks of device
|
||||||
|
// memory created by MemoryAllocator clients. It creates a very large buddy system
|
||||||
|
// where backing device memory blocks equal a specified level in the system.
|
||||||
|
//
|
||||||
|
// Upon sub-allocating, the offset gets mapped to device memory by computing the corresponding
|
||||||
|
// memory index and should the memory not exist, it is created. If two sub-allocations share the
|
||||||
|
// same memory index, the memory refcount is incremented to ensure de-allocating one doesn't
|
||||||
|
// release the other prematurely.
|
||||||
|
//
|
||||||
|
// The device will only create up to Log2(kMaxResourceSize) allocators and can prefer speed
|
||||||
|
// over memory footprint by selecting an allocator with a higher memory threshold which results
|
||||||
|
// in pre-allocating more memory.
|
||||||
|
//
|
||||||
|
// The resource allocation is guaranteed by the device to have compatible memory flags.
|
||||||
|
class BuddyMemoryAllocator {
|
||||||
|
public:
|
||||||
|
BuddyMemoryAllocator(uint64_t maxBlockSize,
|
||||||
|
uint64_t memorySize,
|
||||||
|
std::unique_ptr<MemoryAllocator> client);
|
||||||
|
~BuddyMemoryAllocator() = default;
|
||||||
|
|
||||||
|
ResultOrError<ResourceMemoryAllocation> Allocate(uint64_t allocationSize,
|
||||||
|
uint64_t alignment,
|
||||||
|
int memoryFlags = 0);
|
||||||
|
void Deallocate(const ResourceMemoryAllocation& allocation);
|
||||||
|
|
||||||
|
uint64_t GetMemorySize() const;
|
||||||
|
|
||||||
|
// For testing purposes.
|
||||||
|
uint64_t ComputeTotalNumOfHeapsForTesting() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t GetMemoryIndex(uint64_t offset) const;
|
||||||
|
|
||||||
|
uint64_t mMemorySize = 0;
|
||||||
|
|
||||||
|
BuddyAllocator mBuddyBlockAllocator;
|
||||||
|
std::unique_ptr<MemoryAllocator> mClient;
|
||||||
|
|
||||||
|
struct TrackedSubAllocations {
|
||||||
|
size_t refcount = 0;
|
||||||
|
std::unique_ptr<ResourceHeapBase> mMemoryAllocation;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<TrackedSubAllocations> mTrackedSubAllocations;
|
||||||
|
};
|
||||||
|
} // namespace dawn_native
|
||||||
|
|
||||||
|
#endif // DAWNNATIVE_BUDDYMEMORYALLOCATOR_H_
|
33
src/dawn_native/MemoryAllocator.h
Normal file
33
src/dawn_native/MemoryAllocator.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef DAWNNATIVE_MEMORYALLOCATOR_H_
|
||||||
|
#define DAWNNATIVE_MEMORYALLOCATOR_H_
|
||||||
|
|
||||||
|
#include "dawn_native/Error.h"
|
||||||
|
#include "dawn_native/ResourceHeap.h"
|
||||||
|
|
||||||
|
namespace dawn_native {
|
||||||
|
// Interface for backend allocators that create physical device memory.
|
||||||
|
class MemoryAllocator {
|
||||||
|
public:
|
||||||
|
virtual ~MemoryAllocator() = default;
|
||||||
|
|
||||||
|
virtual ResultOrError<std::unique_ptr<ResourceHeapBase>> Allocate(uint64_t size,
|
||||||
|
int memoryFlags) = 0;
|
||||||
|
virtual void Deallocate(std::unique_ptr<ResourceHeapBase> allocation) = 0;
|
||||||
|
};
|
||||||
|
} // namespace dawn_native
|
||||||
|
|
||||||
|
#endif // DAWNNATIVE_MEMORYALLOCATOR_H_
|
@ -21,7 +21,7 @@ namespace dawn_native {
|
|||||||
|
|
||||||
// Wrapper for a resource backed by a heap.
|
// Wrapper for a resource backed by a heap.
|
||||||
class ResourceHeapBase {
|
class ResourceHeapBase {
|
||||||
protected:
|
public:
|
||||||
ResourceHeapBase() = default;
|
ResourceHeapBase() = default;
|
||||||
virtual ~ResourceHeapBase() = default;
|
virtual ~ResourceHeapBase() = default;
|
||||||
};
|
};
|
||||||
|
@ -15,41 +15,31 @@
|
|||||||
#include "dawn_native/ResourceMemoryAllocation.h"
|
#include "dawn_native/ResourceMemoryAllocation.h"
|
||||||
#include "common/Assert.h"
|
#include "common/Assert.h"
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
|
|
||||||
static constexpr uint64_t kInvalidOffset = std::numeric_limits<uint64_t>::max();
|
|
||||||
|
|
||||||
ResourceMemoryAllocation::ResourceMemoryAllocation()
|
ResourceMemoryAllocation::ResourceMemoryAllocation()
|
||||||
: mMethod(AllocationMethod::kInvalid),
|
: mOffset(0), mResourceHeap(nullptr), mMappedPointer(nullptr) {
|
||||||
mOffset(0),
|
|
||||||
mResourceHeap(nullptr),
|
|
||||||
mMappedPointer(nullptr) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceMemoryAllocation::ResourceMemoryAllocation(uint64_t offset,
|
ResourceMemoryAllocation::ResourceMemoryAllocation(const AllocationInfo& info,
|
||||||
|
uint64_t offset,
|
||||||
ResourceHeapBase* resourceHeap,
|
ResourceHeapBase* resourceHeap,
|
||||||
AllocationMethod method,
|
|
||||||
uint8_t* mappedPointer)
|
uint8_t* mappedPointer)
|
||||||
: mMethod(method),
|
: mInfo(info), mOffset(offset), mResourceHeap(resourceHeap), mMappedPointer(mappedPointer) {
|
||||||
mOffset(offset),
|
|
||||||
mResourceHeap(resourceHeap),
|
|
||||||
mMappedPointer(mappedPointer) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceHeapBase* ResourceMemoryAllocation::GetResourceHeap() const {
|
ResourceHeapBase* ResourceMemoryAllocation::GetResourceHeap() const {
|
||||||
ASSERT(mMethod != AllocationMethod::kInvalid);
|
ASSERT(mInfo.mMethod != AllocationMethod::kInvalid);
|
||||||
return mResourceHeap;
|
return mResourceHeap;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ResourceMemoryAllocation::GetOffset() const {
|
uint64_t ResourceMemoryAllocation::GetOffset() const {
|
||||||
ASSERT(mMethod != AllocationMethod::kInvalid);
|
ASSERT(mInfo.mMethod != AllocationMethod::kInvalid);
|
||||||
return mOffset;
|
return mOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
AllocationMethod ResourceMemoryAllocation::GetAllocationMethod() const {
|
AllocationInfo ResourceMemoryAllocation::GetInfo() const {
|
||||||
return mMethod;
|
return mInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* ResourceMemoryAllocation::GetMappedPointer() const {
|
uint8_t* ResourceMemoryAllocation::GetMappedPointer() const {
|
||||||
@ -58,7 +48,6 @@ namespace dawn_native {
|
|||||||
|
|
||||||
void ResourceMemoryAllocation::Invalidate() {
|
void ResourceMemoryAllocation::Invalidate() {
|
||||||
mResourceHeap = nullptr;
|
mResourceHeap = nullptr;
|
||||||
mMethod = AllocationMethod::kInvalid;
|
mInfo = {};
|
||||||
mOffset = kInvalidOffset;
|
|
||||||
}
|
}
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
@ -35,25 +35,36 @@ namespace dawn_native {
|
|||||||
kInvalid
|
kInvalid
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Metadata that describes how the allocation was allocated.
|
||||||
|
struct AllocationInfo {
|
||||||
|
// AllocationInfo contains a separate offset to not confuse block vs memory offsets.
|
||||||
|
// The block offset is within the entire allocator memory range and only required by the
|
||||||
|
// buddy sub-allocator to get the corresponding memory. Unlike the block offset, the
|
||||||
|
// allocation offset is always local to the memory.
|
||||||
|
uint64_t mBlockOffset = 0;
|
||||||
|
|
||||||
|
AllocationMethod mMethod = AllocationMethod::kInvalid;
|
||||||
|
};
|
||||||
|
|
||||||
// Handle into a resource heap pool.
|
// Handle into a resource heap pool.
|
||||||
class ResourceMemoryAllocation {
|
class ResourceMemoryAllocation {
|
||||||
public:
|
public:
|
||||||
ResourceMemoryAllocation();
|
ResourceMemoryAllocation();
|
||||||
ResourceMemoryAllocation(uint64_t offset,
|
ResourceMemoryAllocation(const AllocationInfo& info,
|
||||||
|
uint64_t offset,
|
||||||
ResourceHeapBase* resourceHeap,
|
ResourceHeapBase* resourceHeap,
|
||||||
AllocationMethod method,
|
|
||||||
uint8_t* mappedPointer = nullptr);
|
uint8_t* mappedPointer = nullptr);
|
||||||
~ResourceMemoryAllocation() = default;
|
~ResourceMemoryAllocation() = default;
|
||||||
|
|
||||||
ResourceHeapBase* GetResourceHeap() const;
|
ResourceHeapBase* GetResourceHeap() const;
|
||||||
uint64_t GetOffset() const;
|
uint64_t GetOffset() const;
|
||||||
AllocationMethod GetAllocationMethod() const;
|
|
||||||
uint8_t* GetMappedPointer() const;
|
uint8_t* GetMappedPointer() const;
|
||||||
|
AllocationInfo GetInfo() const;
|
||||||
|
|
||||||
void Invalidate();
|
void Invalidate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AllocationMethod mMethod;
|
AllocationInfo mInfo;
|
||||||
uint64_t mOffset;
|
uint64_t mOffset;
|
||||||
ResourceHeapBase* mResourceHeap;
|
ResourceHeapBase* mResourceHeap;
|
||||||
uint8_t* mMappedPointer;
|
uint8_t* mMappedPointer;
|
||||||
|
@ -40,9 +40,12 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate resource");
|
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate resource");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResourceMemoryAllocation(
|
AllocationInfo info;
|
||||||
/*offset*/ 0, new ResourceHeap(std::move(committedResource)),
|
info.mMethod = AllocationMethod::kDirect;
|
||||||
AllocationMethod::kDirect);
|
|
||||||
|
return ResourceMemoryAllocation{info,
|
||||||
|
/*offset*/ 0,
|
||||||
|
new ResourceHeap(std::move(committedResource))};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommittedResourceAllocator::Deallocate(ResourceMemoryAllocation& allocation) {
|
void CommittedResourceAllocator::Deallocate(ResourceMemoryAllocation& allocation) {
|
||||||
|
@ -51,7 +51,7 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ResourceAllocatorManager::DeallocateMemory(ResourceMemoryAllocation& allocation) {
|
void ResourceAllocatorManager::DeallocateMemory(ResourceMemoryAllocation& allocation) {
|
||||||
if (allocation.GetAllocationMethod() == AllocationMethod::kInvalid) {
|
if (allocation.GetInfo().mMethod == AllocationMethod::kInvalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CommittedResourceAllocator* allocator = nullptr;
|
CommittedResourceAllocator* allocator = nullptr;
|
||||||
|
@ -702,7 +702,7 @@ namespace dawn_native { namespace vulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Device::DeallocateMemory(ResourceMemoryAllocation& allocation) {
|
void Device::DeallocateMemory(ResourceMemoryAllocation& allocation) {
|
||||||
if (allocation.GetAllocationMethod() == AllocationMethod::kInvalid) {
|
if (allocation.GetInfo().mMethod == AllocationMethod::kInvalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mResourceAllocator->Deallocate(allocation);
|
mResourceAllocator->Deallocate(allocation);
|
||||||
|
@ -105,8 +105,10 @@ namespace dawn_native { namespace vulkan {
|
|||||||
"vkMapMemory"));
|
"vkMapMemory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResourceMemoryAllocation(/*offset*/ 0, new ResourceMemory(allocatedMemory),
|
AllocationInfo info;
|
||||||
AllocationMethod::kDirect,
|
info.mMethod = AllocationMethod::kDirect;
|
||||||
|
|
||||||
|
return ResourceMemoryAllocation(info, /*offset*/ 0, new ResourceMemory(allocatedMemory),
|
||||||
static_cast<uint8_t*>(mappedPointer));
|
static_cast<uint8_t*>(mappedPointer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
346
src/tests/unittests/BuddyMemoryAllocatorTests.cpp
Normal file
346
src/tests/unittests/BuddyMemoryAllocatorTests.cpp
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
// 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 <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "dawn_native/BuddyMemoryAllocator.h"
|
||||||
|
|
||||||
|
using namespace dawn_native;
|
||||||
|
|
||||||
|
class DummyMemoryAllocator : public MemoryAllocator {
|
||||||
|
public:
|
||||||
|
ResultOrError<std::unique_ptr<ResourceHeapBase>> Allocate(uint64_t size,
|
||||||
|
int memoryFlags = 0) override {
|
||||||
|
return std::make_unique<ResourceHeapBase>();
|
||||||
|
}
|
||||||
|
void Deallocate(std::unique_ptr<ResourceHeapBase> allocation) override {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DummyBuddyResourceAllocator {
|
||||||
|
public:
|
||||||
|
DummyBuddyResourceAllocator(uint64_t maxBlockSize, uint64_t memorySize)
|
||||||
|
: mAllocator(maxBlockSize, memorySize, std::make_unique<DummyMemoryAllocator>()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceMemoryAllocation Allocate(uint64_t allocationSize, uint64_t alignment = 1) {
|
||||||
|
ResultOrError<ResourceMemoryAllocation> result =
|
||||||
|
mAllocator.Allocate(allocationSize, alignment);
|
||||||
|
return (result.IsSuccess()) ? result.AcquireSuccess() : ResourceMemoryAllocation{};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deallocate(ResourceMemoryAllocation& allocation) {
|
||||||
|
mAllocator.Deallocate(allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ComputeTotalNumOfHeapsForTesting() const {
|
||||||
|
return mAllocator.ComputeTotalNumOfHeapsForTesting();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BuddyMemoryAllocator mAllocator;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify a single resource allocation in a single heap.
|
||||||
|
TEST(BuddyMemoryAllocatorTests, SingleHeap) {
|
||||||
|
// After one 128 byte resource allocation:
|
||||||
|
//
|
||||||
|
// max block size -> ---------------------------
|
||||||
|
// | A1/H0 | Hi - Heap at index i
|
||||||
|
// max heap size -> --------------------------- An - Resource allocation n
|
||||||
|
//
|
||||||
|
constexpr uint64_t heapSize = 128;
|
||||||
|
constexpr uint64_t maxBlockSize = heapSize;
|
||||||
|
DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize);
|
||||||
|
|
||||||
|
// Cannot allocate greater than heap size.
|
||||||
|
ResourceMemoryAllocation invalidAllocation = allocator.Allocate(heapSize * 2);
|
||||||
|
ASSERT_EQ(invalidAllocation.GetInfo().mMethod, AllocationMethod::kInvalid);
|
||||||
|
|
||||||
|
// Allocate one 128 byte allocation (same size as heap).
|
||||||
|
ResourceMemoryAllocation allocation1 = allocator.Allocate(128);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u);
|
||||||
|
|
||||||
|
// Cannot allocate when allocator is full.
|
||||||
|
invalidAllocation = allocator.Allocate(128);
|
||||||
|
ASSERT_EQ(invalidAllocation.GetInfo().mMethod, AllocationMethod::kInvalid);
|
||||||
|
|
||||||
|
allocator.Deallocate(allocation1);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that multiple allocation are created in separate heaps.
|
||||||
|
TEST(BuddyMemoryAllocatorTests, MultipleHeaps) {
|
||||||
|
// After two 128 byte resource allocations:
|
||||||
|
//
|
||||||
|
// max block size -> ---------------------------
|
||||||
|
// | | Hi - Heap at index i
|
||||||
|
// max heap size -> --------------------------- An - Resource allocation n
|
||||||
|
// | A1/H0 | A2/H1 |
|
||||||
|
// ---------------------------
|
||||||
|
//
|
||||||
|
constexpr uint64_t maxBlockSize = 256;
|
||||||
|
constexpr uint64_t heapSize = 128;
|
||||||
|
DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize);
|
||||||
|
|
||||||
|
// Cannot allocate greater than heap size.
|
||||||
|
ResourceMemoryAllocation invalidAllocation = allocator.Allocate(heapSize * 2);
|
||||||
|
ASSERT_EQ(invalidAllocation.GetInfo().mMethod, AllocationMethod::kInvalid);
|
||||||
|
|
||||||
|
// Cannot allocate greater than max block size.
|
||||||
|
invalidAllocation = allocator.Allocate(maxBlockSize * 2);
|
||||||
|
ASSERT_EQ(invalidAllocation.GetInfo().mMethod, AllocationMethod::kInvalid);
|
||||||
|
|
||||||
|
// Allocate two 128 byte allocations.
|
||||||
|
ResourceMemoryAllocation allocation1 = allocator.Allocate(heapSize);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
// First allocation creates first heap.
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation2 = allocator.Allocate(heapSize);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mBlockOffset, heapSize);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
// Second allocation creates second heap.
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u);
|
||||||
|
|
||||||
|
// Deallocate both allocations
|
||||||
|
allocator.Deallocate(allocation1);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); // Released H0
|
||||||
|
|
||||||
|
allocator.Deallocate(allocation2);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 0u); // Released H1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify multiple sub-allocations can re-use heaps.
|
||||||
|
TEST(BuddyMemoryAllocatorTests, MultipleSplitHeaps) {
|
||||||
|
// After two 64 byte allocations with 128 byte heaps.
|
||||||
|
//
|
||||||
|
// max block size -> ---------------------------
|
||||||
|
// | | Hi - Heap at index i
|
||||||
|
// max heap size -> --------------------------- An - Resource allocation n
|
||||||
|
// | H0 | H1 |
|
||||||
|
// ---------------------------
|
||||||
|
// | A1 | A2 | A3 | |
|
||||||
|
// ---------------------------
|
||||||
|
//
|
||||||
|
constexpr uint64_t maxBlockSize = 256;
|
||||||
|
constexpr uint64_t heapSize = 128;
|
||||||
|
DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize);
|
||||||
|
|
||||||
|
// Allocate two 64 byte sub-allocations.
|
||||||
|
ResourceMemoryAllocation allocation1 = allocator.Allocate(heapSize / 2);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
// First sub-allocation creates first heap.
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation2 = allocator.Allocate(heapSize / 2);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mBlockOffset, heapSize / 2);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
// Second allocation re-uses first heap.
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation3 = allocator.Allocate(heapSize / 2);
|
||||||
|
ASSERT_EQ(allocation3.GetInfo().mBlockOffset, heapSize);
|
||||||
|
ASSERT_EQ(allocation3.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
// Third allocation creates second heap.
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u);
|
||||||
|
|
||||||
|
// Deallocate all allocations in reverse order.
|
||||||
|
allocator.Deallocate(allocation1);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(),
|
||||||
|
2u); // A2 pins H0.
|
||||||
|
|
||||||
|
allocator.Deallocate(allocation2);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); // Released H0
|
||||||
|
|
||||||
|
allocator.Deallocate(allocation3);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 0u); // Released H1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify resource sub-allocation of various sizes over multiple heaps.
|
||||||
|
TEST(BuddyMemoryAllocatorTests, MultiplSplitHeapsVariableSizes) {
|
||||||
|
// After three 64 byte allocations and two 128 byte allocations.
|
||||||
|
//
|
||||||
|
// max block size -> -------------------------------------------------------
|
||||||
|
// | |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// | | |
|
||||||
|
// max heap size -> -------------------------------------------------------
|
||||||
|
// | H0 | A3/H1 | H2 | A5/H3 |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// | A1 | A2 | | A4 | | |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
//
|
||||||
|
constexpr uint64_t heapSize = 128;
|
||||||
|
constexpr uint64_t maxBlockSize = 512;
|
||||||
|
DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize);
|
||||||
|
|
||||||
|
// Allocate two 64-byte allocations.
|
||||||
|
ResourceMemoryAllocation allocation1 = allocator.Allocate(64);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u);
|
||||||
|
ASSERT_EQ(allocation1.GetOffset(), 0u);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation2 = allocator.Allocate(64);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mBlockOffset, 64u);
|
||||||
|
ASSERT_EQ(allocation2.GetOffset(), 64u);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
// A1 and A2 share H0
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation3 = allocator.Allocate(128);
|
||||||
|
ASSERT_EQ(allocation3.GetInfo().mBlockOffset, 128u);
|
||||||
|
ASSERT_EQ(allocation3.GetOffset(), 0u);
|
||||||
|
ASSERT_EQ(allocation3.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
// A3 creates and fully occupies a new heap.
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation4 = allocator.Allocate(64);
|
||||||
|
ASSERT_EQ(allocation4.GetInfo().mBlockOffset, 256u);
|
||||||
|
ASSERT_EQ(allocation4.GetOffset(), 0u);
|
||||||
|
ASSERT_EQ(allocation4.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u);
|
||||||
|
|
||||||
|
// R5 size forms 64 byte hole after R4.
|
||||||
|
ResourceMemoryAllocation allocation5 = allocator.Allocate(128);
|
||||||
|
ASSERT_EQ(allocation5.GetInfo().mBlockOffset, 384u);
|
||||||
|
ASSERT_EQ(allocation5.GetOffset(), 0u);
|
||||||
|
ASSERT_EQ(allocation5.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 4u);
|
||||||
|
|
||||||
|
// Deallocate allocations in staggered order.
|
||||||
|
allocator.Deallocate(allocation1);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 4u); // A2 pins H0
|
||||||
|
|
||||||
|
allocator.Deallocate(allocation5);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u); // Released H3
|
||||||
|
|
||||||
|
allocator.Deallocate(allocation2);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u); // Released H0
|
||||||
|
|
||||||
|
allocator.Deallocate(allocation4);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); // Released H2
|
||||||
|
|
||||||
|
allocator.Deallocate(allocation3);
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 0u); // Released H1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify resource sub-allocation of same sizes with various alignments.
|
||||||
|
TEST(BuddyMemoryAllocatorTests, SameSizeVariousAlignment) {
|
||||||
|
// After three 64 byte and one 128 byte resource allocations.
|
||||||
|
//
|
||||||
|
// max block size -> -------------------------------------------------------
|
||||||
|
// | |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// | | |
|
||||||
|
// max heap size -> -------------------------------------------------------
|
||||||
|
// | H0 | H1 | H2 | |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// | A1 | | A2 | | A3 | A4 | |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
//
|
||||||
|
constexpr uint64_t heapSize = 128;
|
||||||
|
constexpr uint64_t maxBlockSize = 512;
|
||||||
|
DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation1 = allocator.Allocate(64, 128);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u);
|
||||||
|
ASSERT_EQ(allocation1.GetOffset(), 0u);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation2 = allocator.Allocate(64, 128);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mBlockOffset, 128u);
|
||||||
|
ASSERT_EQ(allocation2.GetOffset(), 0u);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation3 = allocator.Allocate(64, 128);
|
||||||
|
ASSERT_EQ(allocation3.GetInfo().mBlockOffset, 256u);
|
||||||
|
ASSERT_EQ(allocation3.GetOffset(), 0u);
|
||||||
|
ASSERT_EQ(allocation3.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation4 = allocator.Allocate(64, 64);
|
||||||
|
ASSERT_EQ(allocation4.GetInfo().mBlockOffset, 320u);
|
||||||
|
ASSERT_EQ(allocation4.GetOffset(), 64u);
|
||||||
|
ASSERT_EQ(allocation4.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify resource sub-allocation of various sizes with same alignments.
|
||||||
|
TEST(BuddyMemoryAllocatorTests, VariousSizeSameAlignment) {
|
||||||
|
// After two 64 byte and two 128 byte resource allocations:
|
||||||
|
//
|
||||||
|
// max block size -> -------------------------------------------------------
|
||||||
|
// | |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// | | |
|
||||||
|
// max heap size -> -------------------------------------------------------
|
||||||
|
// | H0 | A3/H1 | A4/H2 | |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// | A1 | A2 | | | |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
//
|
||||||
|
constexpr uint64_t heapSize = 128;
|
||||||
|
constexpr uint64_t maxBlockSize = 512;
|
||||||
|
DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize);
|
||||||
|
|
||||||
|
constexpr uint64_t alignment = 64;
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation1 = allocator.Allocate(64, alignment);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u);
|
||||||
|
ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation2 = allocator.Allocate(64, alignment);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mBlockOffset, 64u);
|
||||||
|
ASSERT_EQ(allocation2.GetOffset(), 64u);
|
||||||
|
ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); // Reuses H0
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation3 = allocator.Allocate(128, alignment);
|
||||||
|
ASSERT_EQ(allocation3.GetInfo().mBlockOffset, 128u);
|
||||||
|
ASSERT_EQ(allocation3.GetOffset(), 0u);
|
||||||
|
ASSERT_EQ(allocation3.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u);
|
||||||
|
|
||||||
|
ResourceMemoryAllocation allocation4 = allocator.Allocate(128, alignment);
|
||||||
|
ASSERT_EQ(allocation4.GetInfo().mBlockOffset, 256u);
|
||||||
|
ASSERT_EQ(allocation4.GetOffset(), 0u);
|
||||||
|
ASSERT_EQ(allocation4.GetInfo().mMethod, AllocationMethod::kSubAllocated);
|
||||||
|
|
||||||
|
ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user