Descriptor Residency 2: Add Management Logic and Test
Adds logic to lock residency for bound descriptor heaps, then unlock and insert into the LRU cache when no longer bound. Adds a basic functional test. Bug: dawn:193 Change-Id: Idfaaee6b873374c07a0b94b1982ad65353218799 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/21400 Commit-Queue: Brandon Jones <brandon1.jones@intel.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
7355460100
commit
e7b30fdbeb
|
@ -242,7 +242,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// The mapped buffer can be accessed at any time, so it must be locked to ensure it is never
|
// The mapped buffer can be accessed at any time, so it must be locked to ensure it is never
|
||||||
// evicted. This buffer should already have been made resident when it was created.
|
// evicted. This buffer should already have been made resident when it was created.
|
||||||
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
||||||
DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockHeap(heap));
|
DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap));
|
||||||
|
|
||||||
mWrittenMappedRange = {0, static_cast<size_t>(GetSize())};
|
mWrittenMappedRange = {0, static_cast<size_t>(GetSize())};
|
||||||
DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &mWrittenMappedRange,
|
DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &mWrittenMappedRange,
|
||||||
|
@ -256,7 +256,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// The mapped buffer can be accessed at any time, so we must make the buffer resident and
|
// The mapped buffer can be accessed at any time, so we must make the buffer resident and
|
||||||
// lock it to ensure it is never evicted.
|
// lock it to ensure it is never evicted.
|
||||||
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
||||||
DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockHeap(heap));
|
DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap));
|
||||||
|
|
||||||
mWrittenMappedRange = {};
|
mWrittenMappedRange = {};
|
||||||
D3D12_RANGE readRange = {0, static_cast<size_t>(GetSize())};
|
D3D12_RANGE readRange = {0, static_cast<size_t>(GetSize())};
|
||||||
|
@ -272,7 +272,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// The mapped buffer can be accessed at any time, so we must make the buffer resident and
|
// The mapped buffer can be accessed at any time, so we must make the buffer resident and
|
||||||
// lock it to ensure it is never evicted.
|
// lock it to ensure it is never evicted.
|
||||||
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
||||||
DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockHeap(heap));
|
DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap));
|
||||||
|
|
||||||
mWrittenMappedRange = {0, static_cast<size_t>(GetSize())};
|
mWrittenMappedRange = {0, static_cast<size_t>(GetSize())};
|
||||||
DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &mWrittenMappedRange,
|
DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &mWrittenMappedRange,
|
||||||
|
@ -288,7 +288,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// When buffers are mapped, they are locked to keep them in resident memory. We must unlock
|
// When buffers are mapped, they are locked to keep them in resident memory. We must unlock
|
||||||
// them when they are unmapped.
|
// them when they are unmapped.
|
||||||
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
||||||
ToBackend(GetDevice())->GetResidencyManager()->UnlockHeap(heap);
|
ToBackend(GetDevice())->GetResidencyManager()->UnlockAllocation(heap);
|
||||||
mWrittenMappedRange = {};
|
mWrittenMappedRange = {};
|
||||||
mMappedData = nullptr;
|
mMappedData = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -302,7 +302,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// reference on its heap.
|
// reference on its heap.
|
||||||
if (IsMapped()) {
|
if (IsMapped()) {
|
||||||
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
|
||||||
ToBackend(GetDevice())->GetResidencyManager()->UnlockHeap(heap);
|
ToBackend(GetDevice())->GetResidencyManager()->UnlockAllocation(heap);
|
||||||
}
|
}
|
||||||
|
|
||||||
ToBackend(GetDevice())->DeallocateMemory(mResourceAllocation);
|
ToBackend(GetDevice())->DeallocateMemory(mResourceAllocation);
|
||||||
|
|
|
@ -84,14 +84,6 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// Initialize backend services
|
// Initialize backend services
|
||||||
mCommandAllocatorManager = std::make_unique<CommandAllocatorManager>(this);
|
mCommandAllocatorManager = std::make_unique<CommandAllocatorManager>(this);
|
||||||
|
|
||||||
DAWN_TRY_ASSIGN(
|
|
||||||
mViewShaderVisibleDescriptorAllocator,
|
|
||||||
ShaderVisibleDescriptorAllocator::Create(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV));
|
|
||||||
|
|
||||||
DAWN_TRY_ASSIGN(
|
|
||||||
mSamplerShaderVisibleDescriptorAllocator,
|
|
||||||
ShaderVisibleDescriptorAllocator::Create(this, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER));
|
|
||||||
|
|
||||||
// Zero sized allocator is never requested and does not need to exist.
|
// Zero sized allocator is never requested and does not need to exist.
|
||||||
for (uint32_t countIndex = 1; countIndex < kNumOfStagingDescriptorAllocators;
|
for (uint32_t countIndex = 1; countIndex < kNumOfStagingDescriptorAllocators;
|
||||||
countIndex++) {
|
countIndex++) {
|
||||||
|
@ -115,6 +107,15 @@ namespace dawn_native { namespace d3d12 {
|
||||||
mResidencyManager = std::make_unique<ResidencyManager>(this);
|
mResidencyManager = std::make_unique<ResidencyManager>(this);
|
||||||
mResourceAllocatorManager = std::make_unique<ResourceAllocatorManager>(this);
|
mResourceAllocatorManager = std::make_unique<ResourceAllocatorManager>(this);
|
||||||
|
|
||||||
|
// ShaderVisibleDescriptorAllocators use the ResidencyManager and must be initialized after.
|
||||||
|
DAWN_TRY_ASSIGN(
|
||||||
|
mSamplerShaderVisibleDescriptorAllocator,
|
||||||
|
ShaderVisibleDescriptorAllocator::Create(this, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER));
|
||||||
|
|
||||||
|
DAWN_TRY_ASSIGN(
|
||||||
|
mViewShaderVisibleDescriptorAllocator,
|
||||||
|
ShaderVisibleDescriptorAllocator::Create(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV));
|
||||||
|
|
||||||
DAWN_TRY(NextSerial());
|
DAWN_TRY(NextSerial());
|
||||||
|
|
||||||
// Initialize indirect commands
|
// Initialize indirect commands
|
||||||
|
|
|
@ -30,49 +30,49 @@ namespace dawn_native { namespace d3d12 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increments number of locks on a heap to ensure the heap remains resident.
|
// Increments number of locks on a heap to ensure the heap remains resident.
|
||||||
MaybeError ResidencyManager::LockHeap(Heap* heap) {
|
MaybeError ResidencyManager::LockAllocation(Pageable* pageable) {
|
||||||
if (!mResidencyManagementEnabled) {
|
if (!mResidencyManagementEnabled) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the heap isn't already resident, make it resident.
|
// If the heap isn't already resident, make it resident.
|
||||||
if (!heap->IsInResidencyLRUCache() && !heap->IsResidencyLocked()) {
|
if (!pageable->IsInResidencyLRUCache() && !pageable->IsResidencyLocked()) {
|
||||||
DAWN_TRY(EnsureCanMakeResident(heap->GetSize(),
|
DAWN_TRY(EnsureCanMakeResident(pageable->GetSize(),
|
||||||
GetMemorySegmentInfo(heap->GetMemorySegment())));
|
GetMemorySegmentInfo(pageable->GetMemorySegment())));
|
||||||
ID3D12Pageable* pageable = heap->GetD3D12Pageable();
|
ID3D12Pageable* d3d12Pageable = pageable->GetD3D12Pageable();
|
||||||
DAWN_TRY(CheckHRESULT(mDevice->GetD3D12Device()->MakeResident(1, &pageable),
|
DAWN_TRY(CheckHRESULT(mDevice->GetD3D12Device()->MakeResident(1, &d3d12Pageable),
|
||||||
"Making a scheduled-to-be-used resource resident"));
|
"Making a scheduled-to-be-used resource resident"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since we can't evict the heap, it's unnecessary to track the heap in the LRU Cache.
|
// Since we can't evict the heap, it's unnecessary to track the heap in the LRU Cache.
|
||||||
if (heap->IsInResidencyLRUCache()) {
|
if (pageable->IsInResidencyLRUCache()) {
|
||||||
heap->RemoveFromList();
|
pageable->RemoveFromList();
|
||||||
}
|
}
|
||||||
|
|
||||||
heap->IncrementResidencyLock();
|
pageable->IncrementResidencyLock();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrements number of locks on a heap. When the number of locks becomes zero, the heap is
|
// Decrements number of locks on a heap. When the number of locks becomes zero, the heap is
|
||||||
// inserted into the LRU cache and becomes eligible for eviction.
|
// inserted into the LRU cache and becomes eligible for eviction.
|
||||||
void ResidencyManager::UnlockHeap(Heap* heap) {
|
void ResidencyManager::UnlockAllocation(Pageable* pageable) {
|
||||||
if (!mResidencyManagementEnabled) {
|
if (!mResidencyManagementEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(heap->IsResidencyLocked());
|
ASSERT(pageable->IsResidencyLocked());
|
||||||
ASSERT(!heap->IsInResidencyLRUCache());
|
ASSERT(!pageable->IsInResidencyLRUCache());
|
||||||
heap->DecrementResidencyLock();
|
pageable->DecrementResidencyLock();
|
||||||
|
|
||||||
// If another lock still exists on the heap, nothing further should be done.
|
// If another lock still exists on the heap, nothing further should be done.
|
||||||
if (heap->IsResidencyLocked()) {
|
if (pageable->IsResidencyLocked()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When all locks have been removed, the resource remains resident and becomes tracked in
|
// When all locks have been removed, the resource remains resident and becomes tracked in
|
||||||
// the corresponding LRU.
|
// the corresponding LRU.
|
||||||
TrackResidentAllocation(heap);
|
TrackResidentAllocation(pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the appropriate MemorySegmentInfo for a given MemorySegment.
|
// Returns the appropriate MemorySegmentInfo for a given MemorySegment.
|
||||||
|
@ -277,6 +277,8 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// overhead by using MakeResident on a secondary thread, or by instead making use of
|
// overhead by using MakeResident on a secondary thread, or by instead making use of
|
||||||
// the EnqueueMakeResident function (which is not available on all Windows 10
|
// the EnqueueMakeResident function (which is not available on all Windows 10
|
||||||
// platforms).
|
// platforms).
|
||||||
|
// TODO(brandon1.jones@intel.com): If MakeResident fails, try evicting some more and
|
||||||
|
// call MakeResident again.
|
||||||
DAWN_TRY(CheckHRESULT(mDevice->GetD3D12Device()->MakeResident(
|
DAWN_TRY(CheckHRESULT(mDevice->GetD3D12Device()->MakeResident(
|
||||||
heapsToMakeResident.size(), heapsToMakeResident.data()),
|
heapsToMakeResident.size(), heapsToMakeResident.data()),
|
||||||
"Making scheduled-to-be-used resources resident"));
|
"Making scheduled-to-be-used resources resident"));
|
||||||
|
|
|
@ -33,8 +33,8 @@ namespace dawn_native { namespace d3d12 {
|
||||||
public:
|
public:
|
||||||
ResidencyManager(Device* device);
|
ResidencyManager(Device* device);
|
||||||
|
|
||||||
MaybeError LockHeap(Heap* heap);
|
MaybeError LockAllocation(Pageable* pageable);
|
||||||
void UnlockHeap(Heap* heap);
|
void UnlockAllocation(Pageable* pageable);
|
||||||
|
|
||||||
MaybeError EnsureCanAllocate(uint64_t allocationSize, MemorySegment memorySegment);
|
MaybeError EnsureCanAllocate(uint64_t allocationSize, MemorySegment memorySegment);
|
||||||
MaybeError EnsureHeapsAreResident(Heap** heaps, size_t heapCount);
|
MaybeError EnsureHeapsAreResident(Heap** heaps, size_t heapCount);
|
||||||
|
|
|
@ -282,7 +282,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
|
|
||||||
// Before calling CreatePlacedResource, we must ensure the target heap is resident.
|
// Before calling CreatePlacedResource, we must ensure the target heap is resident.
|
||||||
// CreatePlacedResource will fail if it is not.
|
// CreatePlacedResource will fail if it is not.
|
||||||
DAWN_TRY(mDevice->GetResidencyManager()->LockHeap(heap));
|
DAWN_TRY(mDevice->GetResidencyManager()->LockAllocation(heap));
|
||||||
|
|
||||||
// With placed resources, a single heap can be reused.
|
// With placed resources, a single heap can be reused.
|
||||||
// The resource placed at an offset is only reclaimed
|
// The resource placed at an offset is only reclaimed
|
||||||
|
@ -300,7 +300,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
|
|
||||||
// After CreatePlacedResource has finished, the heap can be unlocked from residency. This
|
// After CreatePlacedResource has finished, the heap can be unlocked from residency. This
|
||||||
// will insert it into the residency LRU.
|
// will insert it into the residency LRU.
|
||||||
mDevice->GetResidencyManager()->UnlockHeap(heap);
|
mDevice->GetResidencyManager()->UnlockAllocation(heap);
|
||||||
|
|
||||||
return ResourceHeapAllocation{allocation.GetInfo(), allocation.GetOffset(),
|
return ResourceHeapAllocation{allocation.GetInfo(), allocation.GetOffset(),
|
||||||
std::move(placedResource), heap};
|
std::move(placedResource), heap};
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "dawn_native/d3d12/D3D12Error.h"
|
#include "dawn_native/d3d12/D3D12Error.h"
|
||||||
#include "dawn_native/d3d12/DeviceD3D12.h"
|
#include "dawn_native/d3d12/DeviceD3D12.h"
|
||||||
#include "dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h"
|
#include "dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h"
|
||||||
|
#include "dawn_native/d3d12/ResidencyManagerD3D12.h"
|
||||||
|
|
||||||
namespace dawn_native { namespace d3d12 {
|
namespace dawn_native { namespace d3d12 {
|
||||||
|
|
||||||
|
@ -114,6 +115,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// users.
|
// users.
|
||||||
// TODO(dawn:256): Consider periodically triming to avoid OOM.
|
// TODO(dawn:256): Consider periodically triming to avoid OOM.
|
||||||
if (mHeap != nullptr) {
|
if (mHeap != nullptr) {
|
||||||
|
mDevice->GetResidencyManager()->UnlockAllocation(mHeap.get());
|
||||||
mPool.push_back({mDevice->GetPendingCommandSerial(), std::move(mHeap)});
|
mPool.push_back({mDevice->GetPendingCommandSerial(), std::move(mHeap)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +132,14 @@ namespace dawn_native { namespace d3d12 {
|
||||||
mHeapType, mDevice->IsToggleEnabled(Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
|
mHeapType, mDevice->IsToggleEnabled(Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
|
||||||
|
|
||||||
if (descriptorHeap == nullptr) {
|
if (descriptorHeap == nullptr) {
|
||||||
|
// The size in bytes of a descriptor heap is best calculated by the increment size
|
||||||
|
// multiplied by the number of descriptors. In practice, this is only an estimate and
|
||||||
|
// the actual size may vary depending on the driver.
|
||||||
|
const uint64_t kSize = mSizeIncrement * descriptorCount;
|
||||||
|
|
||||||
|
DAWN_TRY(
|
||||||
|
mDevice->GetResidencyManager()->EnsureCanAllocate(kSize, MemorySegment::Local));
|
||||||
|
|
||||||
ComPtr<ID3D12DescriptorHeap> d3d12DescriptorHeap;
|
ComPtr<ID3D12DescriptorHeap> d3d12DescriptorHeap;
|
||||||
D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor;
|
D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor;
|
||||||
heapDescriptor.Type = mHeapType;
|
heapDescriptor.Type = mHeapType;
|
||||||
|
@ -141,9 +151,10 @@ namespace dawn_native { namespace d3d12 {
|
||||||
&heapDescriptor, IID_PPV_ARGS(&d3d12DescriptorHeap)),
|
&heapDescriptor, IID_PPV_ARGS(&d3d12DescriptorHeap)),
|
||||||
"ID3D12Device::CreateDescriptorHeap"));
|
"ID3D12Device::CreateDescriptorHeap"));
|
||||||
descriptorHeap = std::make_unique<ShaderVisibleDescriptorHeap>(
|
descriptorHeap = std::make_unique<ShaderVisibleDescriptorHeap>(
|
||||||
std::move(d3d12DescriptorHeap), mSizeIncrement * descriptorCount);
|
std::move(d3d12DescriptorHeap), kSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DAWN_TRY(mDevice->GetResidencyManager()->LockAllocation(descriptorHeap.get()));
|
||||||
// Create a FIFO buffer from the recently created heap.
|
// Create a FIFO buffer from the recently created heap.
|
||||||
mHeap = std::move(descriptorHeap);
|
mHeap = std::move(descriptorHeap);
|
||||||
mAllocator = RingBufferAllocator(descriptorCount);
|
mAllocator = RingBufferAllocator(descriptorCount);
|
||||||
|
@ -168,6 +179,15 @@ namespace dawn_native { namespace d3d12 {
|
||||||
return mPool.size();
|
return mPool.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShaderVisibleDescriptorAllocator::IsShaderVisibleHeapLockedResidentForTesting() const {
|
||||||
|
return mHeap->IsResidencyLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderVisibleDescriptorAllocator::IsLastShaderVisibleHeapInLRUForTesting() const {
|
||||||
|
ASSERT(!mPool.empty());
|
||||||
|
return mPool.back().heap->IsInResidencyLRUCache();
|
||||||
|
}
|
||||||
|
|
||||||
bool ShaderVisibleDescriptorAllocator::IsAllocationStillValid(
|
bool ShaderVisibleDescriptorAllocator::IsAllocationStillValid(
|
||||||
const GPUDescriptorHeapAllocation& allocation) const {
|
const GPUDescriptorHeapAllocation& allocation) const {
|
||||||
// Consider valid if allocated for the pending submit and the shader visible heaps
|
// Consider valid if allocated for the pending submit and the shader visible heaps
|
||||||
|
|
|
@ -67,6 +67,8 @@ namespace dawn_native { namespace d3d12 {
|
||||||
Serial GetShaderVisibleHeapSerialForTesting() const;
|
Serial GetShaderVisibleHeapSerialForTesting() const;
|
||||||
uint64_t GetShaderVisibleHeapSizeForTesting() const;
|
uint64_t GetShaderVisibleHeapSizeForTesting() const;
|
||||||
uint64_t GetShaderVisiblePoolSizeForTesting() const;
|
uint64_t GetShaderVisiblePoolSizeForTesting() const;
|
||||||
|
bool IsShaderVisibleHeapLockedResidentForTesting() const;
|
||||||
|
bool IsLastShaderVisibleHeapInLRUForTesting() const;
|
||||||
|
|
||||||
bool IsAllocationStillValid(const GPUDescriptorHeapAllocation& allocation) const;
|
bool IsAllocationStillValid(const GPUDescriptorHeapAllocation& allocation) const;
|
||||||
|
|
||||||
|
|
|
@ -44,8 +44,8 @@ namespace dawn_native { namespace d3d12 {
|
||||||
|
|
||||||
// The mapped buffer can be accessed at any time, so it must be locked to ensure it is never
|
// The mapped buffer can be accessed at any time, so it must be locked to ensure it is never
|
||||||
// evicted. This buffer should already have been made resident when it was created.
|
// evicted. This buffer should already have been made resident when it was created.
|
||||||
DAWN_TRY(
|
DAWN_TRY(mDevice->GetResidencyManager()->LockAllocation(
|
||||||
mDevice->GetResidencyManager()->LockHeap(ToBackend(mUploadHeap.GetResourceHeap())));
|
ToBackend(mUploadHeap.GetResourceHeap())));
|
||||||
|
|
||||||
return CheckHRESULT(GetResource()->Map(0, nullptr, &mMappedPointer), "ID3D12Resource::Map");
|
return CheckHRESULT(GetResource()->Map(0, nullptr, &mMappedPointer), "ID3D12Resource::Map");
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
|
|
||||||
// The underlying heap was locked in residency upon creation. We must unlock it when this
|
// The underlying heap was locked in residency upon creation. We must unlock it when this
|
||||||
// buffer becomes unmapped.
|
// buffer becomes unmapped.
|
||||||
mDevice->GetResidencyManager()->UnlockHeap(ToBackend(mUploadHeap.GetResourceHeap()));
|
mDevice->GetResidencyManager()->UnlockAllocation(ToBackend(mUploadHeap.GetResourceHeap()));
|
||||||
|
|
||||||
// Invalidate the CPU virtual address & flush cache (if needed).
|
// Invalidate the CPU virtual address & flush cache (if needed).
|
||||||
GetResource()->Unmap(0, nullptr);
|
GetResource()->Unmap(0, nullptr);
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
#include "dawn_native/d3d12/BufferD3D12.h"
|
#include "dawn_native/d3d12/BufferD3D12.h"
|
||||||
#include "dawn_native/d3d12/DeviceD3D12.h"
|
#include "dawn_native/d3d12/DeviceD3D12.h"
|
||||||
#include "dawn_native/d3d12/ResidencyManagerD3D12.h"
|
#include "dawn_native/d3d12/ResidencyManagerD3D12.h"
|
||||||
|
#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
|
||||||
#include "tests/DawnTest.h"
|
#include "tests/DawnTest.h"
|
||||||
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
||||||
#include "utils/WGPUHelpers.h"
|
#include "utils/WGPUHelpers.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -32,7 +34,7 @@ constexpr wgpu::BufferUsage kMapWriteBufferUsage =
|
||||||
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
|
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
|
||||||
constexpr wgpu::BufferUsage kNonMappableBufferUsage = wgpu::BufferUsage::CopyDst;
|
constexpr wgpu::BufferUsage kNonMappableBufferUsage = wgpu::BufferUsage::CopyDst;
|
||||||
|
|
||||||
class D3D12ResidencyTests : public DawnTest {
|
class D3D12ResidencyTestBase : public DawnTest {
|
||||||
protected:
|
protected:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
DawnTest::SetUp();
|
DawnTest::SetUp();
|
||||||
|
@ -62,23 +64,6 @@ class D3D12ResidencyTests : public DawnTest {
|
||||||
return buffers;
|
return buffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckIfBufferIsResident(wgpu::Buffer buffer) const {
|
|
||||||
dawn_native::d3d12::Buffer* d3dBuffer =
|
|
||||||
reinterpret_cast<dawn_native::d3d12::Buffer*>(buffer.Get());
|
|
||||||
return d3dBuffer->CheckIsResidentForTesting();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CheckAllocationMethod(wgpu::Buffer buffer,
|
|
||||||
dawn_native::AllocationMethod allocationMethod) const {
|
|
||||||
dawn_native::d3d12::Buffer* d3dBuffer =
|
|
||||||
reinterpret_cast<dawn_native::d3d12::Buffer*>(buffer.Get());
|
|
||||||
return d3dBuffer->CheckAllocationMethodForTesting(allocationMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsUMA() const {
|
|
||||||
return reinterpret_cast<dawn_native::d3d12::Device*>(device.Get())->GetDeviceInfo().isUMA;
|
|
||||||
}
|
|
||||||
|
|
||||||
wgpu::Buffer CreateBuffer(uint32_t bufferSize, wgpu::BufferUsage usage) {
|
wgpu::Buffer CreateBuffer(uint32_t bufferSize, wgpu::BufferUsage usage) {
|
||||||
wgpu::BufferDescriptor descriptor;
|
wgpu::BufferDescriptor descriptor;
|
||||||
|
|
||||||
|
@ -88,26 +73,6 @@ class D3D12ResidencyTests : public DawnTest {
|
||||||
return device.CreateBuffer(&descriptor);
|
return device.CreateBuffer(&descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MapReadCallback(WGPUBufferMapAsyncStatus status,
|
|
||||||
const void* data,
|
|
||||||
uint64_t,
|
|
||||||
void* userdata) {
|
|
||||||
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
||||||
ASSERT_NE(nullptr, data);
|
|
||||||
|
|
||||||
static_cast<D3D12ResidencyTests*>(userdata)->mMappedReadData = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MapWriteCallback(WGPUBufferMapAsyncStatus status,
|
|
||||||
void* data,
|
|
||||||
uint64_t,
|
|
||||||
void* userdata) {
|
|
||||||
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
||||||
ASSERT_NE(nullptr, data);
|
|
||||||
|
|
||||||
static_cast<D3D12ResidencyTests*>(userdata)->mMappedWriteData = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TouchBuffers(uint32_t beginIndex,
|
void TouchBuffers(uint32_t beginIndex,
|
||||||
uint32_t numBuffers,
|
uint32_t numBuffers,
|
||||||
const std::vector<wgpu::Buffer>& bufferSet) {
|
const std::vector<wgpu::Buffer>& bufferSet) {
|
||||||
|
@ -126,8 +91,50 @@ class D3D12ResidencyTests : public DawnTest {
|
||||||
const void* mMappedReadData = nullptr;
|
const void* mMappedReadData = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class D3D12ResourceResidencyTests : public D3D12ResidencyTestBase {
|
||||||
|
protected:
|
||||||
|
bool CheckAllocationMethod(wgpu::Buffer buffer,
|
||||||
|
dawn_native::AllocationMethod allocationMethod) const {
|
||||||
|
dawn_native::d3d12::Buffer* d3dBuffer =
|
||||||
|
reinterpret_cast<dawn_native::d3d12::Buffer*>(buffer.Get());
|
||||||
|
return d3dBuffer->CheckAllocationMethodForTesting(allocationMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckIfBufferIsResident(wgpu::Buffer buffer) const {
|
||||||
|
dawn_native::d3d12::Buffer* d3dBuffer =
|
||||||
|
reinterpret_cast<dawn_native::d3d12::Buffer*>(buffer.Get());
|
||||||
|
return d3dBuffer->CheckIsResidentForTesting();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsUMA() const {
|
||||||
|
return reinterpret_cast<dawn_native::d3d12::Device*>(device.Get())->GetDeviceInfo().isUMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MapReadCallback(WGPUBufferMapAsyncStatus status,
|
||||||
|
const void* data,
|
||||||
|
uint64_t,
|
||||||
|
void* userdata) {
|
||||||
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
||||||
|
ASSERT_NE(nullptr, data);
|
||||||
|
|
||||||
|
static_cast<D3D12ResourceResidencyTests*>(userdata)->mMappedReadData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MapWriteCallback(WGPUBufferMapAsyncStatus status,
|
||||||
|
void* data,
|
||||||
|
uint64_t,
|
||||||
|
void* userdata) {
|
||||||
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
||||||
|
ASSERT_NE(nullptr, data);
|
||||||
|
|
||||||
|
static_cast<D3D12ResourceResidencyTests*>(userdata)->mMappedWriteData = data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class D3D12DescriptorResidencyTests : public D3D12ResidencyTestBase {};
|
||||||
|
|
||||||
// Check that resources existing on suballocated heaps are made resident and evicted correctly.
|
// Check that resources existing on suballocated heaps are made resident and evicted correctly.
|
||||||
TEST_P(D3D12ResidencyTests, OvercommitSmallResources) {
|
TEST_P(D3D12ResourceResidencyTests, OvercommitSmallResources) {
|
||||||
// TODO(http://crbug.com/dawn/416): Tests fails on Intel HD 630 bot.
|
// TODO(http://crbug.com/dawn/416): Tests fails on Intel HD 630 bot.
|
||||||
DAWN_SKIP_TEST_IF(IsIntel() && IsBackendValidationEnabled());
|
DAWN_SKIP_TEST_IF(IsIntel() && IsBackendValidationEnabled());
|
||||||
|
|
||||||
|
@ -169,7 +176,7 @@ TEST_P(D3D12ResidencyTests, OvercommitSmallResources) {
|
||||||
|
|
||||||
// Check that resources existing on directly allocated heaps are made resident and evicted
|
// Check that resources existing on directly allocated heaps are made resident and evicted
|
||||||
// correctly.
|
// correctly.
|
||||||
TEST_P(D3D12ResidencyTests, OvercommitLargeResources) {
|
TEST_P(D3D12ResourceResidencyTests, OvercommitLargeResources) {
|
||||||
// Create directly-allocated buffers to fill half the budget.
|
// Create directly-allocated buffers to fill half the budget.
|
||||||
std::vector<wgpu::Buffer> bufferSet1 = AllocateBuffers(
|
std::vector<wgpu::Buffer> bufferSet1 = AllocateBuffers(
|
||||||
kDirectlyAllocatedResourceSize,
|
kDirectlyAllocatedResourceSize,
|
||||||
|
@ -205,7 +212,7 @@ TEST_P(D3D12ResidencyTests, OvercommitLargeResources) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that calling MapReadAsync makes the buffer resident and keeps it locked resident.
|
// Check that calling MapReadAsync makes the buffer resident and keeps it locked resident.
|
||||||
TEST_P(D3D12ResidencyTests, AsyncMappedBufferRead) {
|
TEST_P(D3D12ResourceResidencyTests, AsyncMappedBufferRead) {
|
||||||
// Create a mappable buffer.
|
// Create a mappable buffer.
|
||||||
wgpu::Buffer buffer = CreateBuffer(4, kMapReadBufferUsage);
|
wgpu::Buffer buffer = CreateBuffer(4, kMapReadBufferUsage);
|
||||||
|
|
||||||
|
@ -248,7 +255,7 @@ TEST_P(D3D12ResidencyTests, AsyncMappedBufferRead) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that calling MapWriteAsync makes the buffer resident and keeps it locked resident.
|
// Check that calling MapWriteAsync makes the buffer resident and keeps it locked resident.
|
||||||
TEST_P(D3D12ResidencyTests, AsyncMappedBufferWrite) {
|
TEST_P(D3D12ResourceResidencyTests, AsyncMappedBufferWrite) {
|
||||||
// Create a mappable buffer.
|
// Create a mappable buffer.
|
||||||
wgpu::Buffer buffer = CreateBuffer(4, kMapWriteBufferUsage);
|
wgpu::Buffer buffer = CreateBuffer(4, kMapWriteBufferUsage);
|
||||||
// The mappable buffer should be resident.
|
// The mappable buffer should be resident.
|
||||||
|
@ -287,7 +294,7 @@ TEST_P(D3D12ResidencyTests, AsyncMappedBufferWrite) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that overcommitting in a single submit works, then make sure the budget is enforced after.
|
// Check that overcommitting in a single submit works, then make sure the budget is enforced after.
|
||||||
TEST_P(D3D12ResidencyTests, OvercommitInASingleSubmit) {
|
TEST_P(D3D12ResourceResidencyTests, OvercommitInASingleSubmit) {
|
||||||
// Create enough buffers to exceed the budget
|
// Create enough buffers to exceed the budget
|
||||||
constexpr uint32_t numberOfBuffersToOvercommit = 5;
|
constexpr uint32_t numberOfBuffersToOvercommit = 5;
|
||||||
std::vector<wgpu::Buffer> bufferSet1 = AllocateBuffers(
|
std::vector<wgpu::Buffer> bufferSet1 = AllocateBuffers(
|
||||||
|
@ -313,7 +320,7 @@ TEST_P(D3D12ResidencyTests, OvercommitInASingleSubmit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(D3D12ResidencyTests, SetExternalReservation) {
|
TEST_P(D3D12ResourceResidencyTests, SetExternalReservation) {
|
||||||
// Set an external reservation of 20% the budget. We should succesfully reserve the amount we
|
// Set an external reservation of 20% the budget. We should succesfully reserve the amount we
|
||||||
// request.
|
// request.
|
||||||
uint64_t amountReserved = dawn_native::d3d12::SetExternalMemoryReservation(
|
uint64_t amountReserved = dawn_native::d3d12::SetExternalMemoryReservation(
|
||||||
|
@ -328,4 +335,89 @@ TEST_P(D3D12ResidencyTests, SetExternalReservation) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DAWN_INSTANTIATE_TEST(D3D12ResidencyTests, D3D12Backend());
|
// Checks that when a descriptor heap is bound, it is locked resident. Also checks that when a
|
||||||
|
// previous descriptor heap becomes unbound, it is unlocked, placed in the LRU and can be evicted.
|
||||||
|
TEST_P(D3D12DescriptorResidencyTests, SwitchedViewHeapResidency) {
|
||||||
|
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
|
||||||
|
|
||||||
|
// Fill in a view heap with "view only" bindgroups (1x view per group) by creating a
|
||||||
|
// view bindgroup each draw. After HEAP_SIZE + 1 draws, the heaps must switch over.
|
||||||
|
renderPipelineDescriptor.vertexStage.module =
|
||||||
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
|
||||||
|
#version 450
|
||||||
|
void main() {
|
||||||
|
const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f));
|
||||||
|
gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f);
|
||||||
|
})");
|
||||||
|
|
||||||
|
renderPipelineDescriptor.cFragmentStage.module =
|
||||||
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
|
||||||
|
#version 450
|
||||||
|
layout (location = 0) out vec4 fragColor;
|
||||||
|
layout (set = 0, binding = 0) uniform colorBuffer {
|
||||||
|
vec4 color;
|
||||||
|
};
|
||||||
|
void main() {
|
||||||
|
fragColor = color;
|
||||||
|
})");
|
||||||
|
|
||||||
|
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
|
||||||
|
constexpr uint32_t kSize = 512;
|
||||||
|
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kSize, kSize);
|
||||||
|
|
||||||
|
wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
|
||||||
|
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
|
||||||
|
|
||||||
|
dawn_native::d3d12::Device* d3dDevice =
|
||||||
|
reinterpret_cast<dawn_native::d3d12::Device*>(device.Get());
|
||||||
|
|
||||||
|
dawn_native::d3d12::ShaderVisibleDescriptorAllocator* allocator =
|
||||||
|
d3dDevice->GetViewShaderVisibleDescriptorAllocator();
|
||||||
|
const uint64_t heapSize = allocator->GetShaderVisibleHeapSizeForTesting();
|
||||||
|
|
||||||
|
const Serial heapSerial = allocator->GetShaderVisibleHeapSerialForTesting();
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
{
|
||||||
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
||||||
|
|
||||||
|
pass.SetPipeline(renderPipeline);
|
||||||
|
|
||||||
|
std::array<float, 4> redColor = {1, 0, 0, 1};
|
||||||
|
wgpu::Buffer uniformBuffer = utils::CreateBufferFromData(
|
||||||
|
device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < heapSize + 1; ++i) {
|
||||||
|
pass.SetBindGroup(0, utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
|
||||||
|
{{0, uniformBuffer, 0, sizeof(redColor)}}));
|
||||||
|
pass.Draw(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
pass.EndPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
// Check the heap serial to ensure the heap has switched.
|
||||||
|
EXPECT_EQ(allocator->GetShaderVisibleHeapSerialForTesting(), heapSerial + 1);
|
||||||
|
|
||||||
|
// Check that currrently bound ShaderVisibleHeap is locked resident.
|
||||||
|
EXPECT_TRUE(allocator->IsShaderVisibleHeapLockedResidentForTesting());
|
||||||
|
// Check that the previously bound ShaderVisibleHeap was unlocked and was placed in the LRU
|
||||||
|
// cache.
|
||||||
|
EXPECT_TRUE(allocator->IsLastShaderVisibleHeapInLRUForTesting());
|
||||||
|
// Allocate enough buffers to exceed the budget, which will purge everything from the Residency
|
||||||
|
// LRU.
|
||||||
|
AllocateBuffers(kDirectlyAllocatedResourceSize,
|
||||||
|
kRestrictedBudgetSize / kDirectlyAllocatedResourceSize,
|
||||||
|
kNonMappableBufferUsage);
|
||||||
|
// Check that currrently bound ShaderVisibleHeap remained locked resident.
|
||||||
|
EXPECT_TRUE(allocator->IsShaderVisibleHeapLockedResidentForTesting());
|
||||||
|
// Check that the previously bound ShaderVisibleHeap has been evicted from the LRU cache.
|
||||||
|
EXPECT_FALSE(allocator->IsLastShaderVisibleHeapInLRUForTesting());
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_INSTANTIATE_TEST(D3D12ResourceResidencyTests, D3D12Backend());
|
||||||
|
DAWN_INSTANTIATE_TEST(D3D12DescriptorResidencyTests,
|
||||||
|
D3D12Backend({"use_d3d12_small_shader_visible_heap"}));
|
||||||
|
|
Loading…
Reference in New Issue