diff --git a/src/dawn_native/d3d12/BufferD3D12.cpp b/src/dawn_native/d3d12/BufferD3D12.cpp index ba79052963..8b42c95aec 100644 --- a/src/dawn_native/d3d12/BufferD3D12.cpp +++ b/src/dawn_native/d3d12/BufferD3D12.cpp @@ -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 // evicted. This buffer should already have been made resident when it was created. Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); - DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockHeap(heap)); + DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap)); mWrittenMappedRange = {0, static_cast(GetSize())}; 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 // lock it to ensure it is never evicted. Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); - DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockHeap(heap)); + DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap)); mWrittenMappedRange = {}; D3D12_RANGE readRange = {0, static_cast(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 // lock it to ensure it is never evicted. Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); - DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockHeap(heap)); + DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap)); mWrittenMappedRange = {0, static_cast(GetSize())}; 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 // them when they are unmapped. Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); - ToBackend(GetDevice())->GetResidencyManager()->UnlockHeap(heap); + ToBackend(GetDevice())->GetResidencyManager()->UnlockAllocation(heap); mWrittenMappedRange = {}; mMappedData = nullptr; } @@ -302,7 +302,7 @@ namespace dawn_native { namespace d3d12 { // reference on its heap. if (IsMapped()) { Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); - ToBackend(GetDevice())->GetResidencyManager()->UnlockHeap(heap); + ToBackend(GetDevice())->GetResidencyManager()->UnlockAllocation(heap); } ToBackend(GetDevice())->DeallocateMemory(mResourceAllocation); diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp index b46000ebe9..7983b980ca 100644 --- a/src/dawn_native/d3d12/DeviceD3D12.cpp +++ b/src/dawn_native/d3d12/DeviceD3D12.cpp @@ -84,14 +84,6 @@ namespace dawn_native { namespace d3d12 { // Initialize backend services mCommandAllocatorManager = std::make_unique(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. for (uint32_t countIndex = 1; countIndex < kNumOfStagingDescriptorAllocators; countIndex++) { @@ -115,6 +107,15 @@ namespace dawn_native { namespace d3d12 { mResidencyManager = std::make_unique(this); mResourceAllocatorManager = std::make_unique(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()); // Initialize indirect commands diff --git a/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp b/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp index 9ba15df581..b790a80b5d 100644 --- a/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp +++ b/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp @@ -30,49 +30,49 @@ namespace dawn_native { namespace d3d12 { } // 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) { return {}; } // If the heap isn't already resident, make it resident. - if (!heap->IsInResidencyLRUCache() && !heap->IsResidencyLocked()) { - DAWN_TRY(EnsureCanMakeResident(heap->GetSize(), - GetMemorySegmentInfo(heap->GetMemorySegment()))); - ID3D12Pageable* pageable = heap->GetD3D12Pageable(); - DAWN_TRY(CheckHRESULT(mDevice->GetD3D12Device()->MakeResident(1, &pageable), + if (!pageable->IsInResidencyLRUCache() && !pageable->IsResidencyLocked()) { + DAWN_TRY(EnsureCanMakeResident(pageable->GetSize(), + GetMemorySegmentInfo(pageable->GetMemorySegment()))); + ID3D12Pageable* d3d12Pageable = pageable->GetD3D12Pageable(); + DAWN_TRY(CheckHRESULT(mDevice->GetD3D12Device()->MakeResident(1, &d3d12Pageable), "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. - if (heap->IsInResidencyLRUCache()) { - heap->RemoveFromList(); + if (pageable->IsInResidencyLRUCache()) { + pageable->RemoveFromList(); } - heap->IncrementResidencyLock(); + pageable->IncrementResidencyLock(); return {}; } // 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. - void ResidencyManager::UnlockHeap(Heap* heap) { + void ResidencyManager::UnlockAllocation(Pageable* pageable) { if (!mResidencyManagementEnabled) { return; } - ASSERT(heap->IsResidencyLocked()); - ASSERT(!heap->IsInResidencyLRUCache()); - heap->DecrementResidencyLock(); + ASSERT(pageable->IsResidencyLocked()); + ASSERT(!pageable->IsInResidencyLRUCache()); + pageable->DecrementResidencyLock(); // If another lock still exists on the heap, nothing further should be done. - if (heap->IsResidencyLocked()) { + if (pageable->IsResidencyLocked()) { return; } // When all locks have been removed, the resource remains resident and becomes tracked in // the corresponding LRU. - TrackResidentAllocation(heap); + TrackResidentAllocation(pageable); } // 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 // the EnqueueMakeResident function (which is not available on all Windows 10 // platforms). + // TODO(brandon1.jones@intel.com): If MakeResident fails, try evicting some more and + // call MakeResident again. DAWN_TRY(CheckHRESULT(mDevice->GetD3D12Device()->MakeResident( heapsToMakeResident.size(), heapsToMakeResident.data()), "Making scheduled-to-be-used resources resident")); diff --git a/src/dawn_native/d3d12/ResidencyManagerD3D12.h b/src/dawn_native/d3d12/ResidencyManagerD3D12.h index 1d97d4d593..632abc3a96 100644 --- a/src/dawn_native/d3d12/ResidencyManagerD3D12.h +++ b/src/dawn_native/d3d12/ResidencyManagerD3D12.h @@ -33,8 +33,8 @@ namespace dawn_native { namespace d3d12 { public: ResidencyManager(Device* device); - MaybeError LockHeap(Heap* heap); - void UnlockHeap(Heap* heap); + MaybeError LockAllocation(Pageable* pageable); + void UnlockAllocation(Pageable* pageable); MaybeError EnsureCanAllocate(uint64_t allocationSize, MemorySegment memorySegment); MaybeError EnsureHeapsAreResident(Heap** heaps, size_t heapCount); diff --git a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp index 7f4738649e..1b01a07e2e 100644 --- a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp +++ b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp @@ -282,7 +282,7 @@ namespace dawn_native { namespace d3d12 { // Before calling CreatePlacedResource, we must ensure the target heap is resident. // 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. // 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 // will insert it into the residency LRU. - mDevice->GetResidencyManager()->UnlockHeap(heap); + mDevice->GetResidencyManager()->UnlockAllocation(heap); return ResourceHeapAllocation{allocation.GetInfo(), allocation.GetOffset(), std::move(placedResource), heap}; diff --git a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp index 5a3015bdbf..258d5c3ea0 100644 --- a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp +++ b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp @@ -16,6 +16,7 @@ #include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" namespace dawn_native { namespace d3d12 { @@ -114,6 +115,7 @@ namespace dawn_native { namespace d3d12 { // users. // TODO(dawn:256): Consider periodically triming to avoid OOM. if (mHeap != nullptr) { + mDevice->GetResidencyManager()->UnlockAllocation(mHeap.get()); mPool.push_back({mDevice->GetPendingCommandSerial(), std::move(mHeap)}); } @@ -130,6 +132,14 @@ namespace dawn_native { namespace d3d12 { mHeapType, mDevice->IsToggleEnabled(Toggle::UseD3D12SmallShaderVisibleHeapForTesting)); 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 d3d12DescriptorHeap; D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor; heapDescriptor.Type = mHeapType; @@ -141,9 +151,10 @@ namespace dawn_native { namespace d3d12 { &heapDescriptor, IID_PPV_ARGS(&d3d12DescriptorHeap)), "ID3D12Device::CreateDescriptorHeap")); descriptorHeap = std::make_unique( - 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. mHeap = std::move(descriptorHeap); mAllocator = RingBufferAllocator(descriptorCount); @@ -168,6 +179,15 @@ namespace dawn_native { namespace d3d12 { 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( const GPUDescriptorHeapAllocation& allocation) const { // Consider valid if allocated for the pending submit and the shader visible heaps diff --git a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h index da8a5e36b7..aec20a3b44 100644 --- a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h +++ b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h @@ -67,6 +67,8 @@ namespace dawn_native { namespace d3d12 { Serial GetShaderVisibleHeapSerialForTesting() const; uint64_t GetShaderVisibleHeapSizeForTesting() const; uint64_t GetShaderVisiblePoolSizeForTesting() const; + bool IsShaderVisibleHeapLockedResidentForTesting() const; + bool IsLastShaderVisibleHeapInLRUForTesting() const; bool IsAllocationStillValid(const GPUDescriptorHeapAllocation& allocation) const; diff --git a/src/dawn_native/d3d12/StagingBufferD3D12.cpp b/src/dawn_native/d3d12/StagingBufferD3D12.cpp index 7df5a670ea..b3aec3f1d9 100644 --- a/src/dawn_native/d3d12/StagingBufferD3D12.cpp +++ b/src/dawn_native/d3d12/StagingBufferD3D12.cpp @@ -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 // evicted. This buffer should already have been made resident when it was created. - DAWN_TRY( - mDevice->GetResidencyManager()->LockHeap(ToBackend(mUploadHeap.GetResourceHeap()))); + DAWN_TRY(mDevice->GetResidencyManager()->LockAllocation( + ToBackend(mUploadHeap.GetResourceHeap()))); 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 // buffer becomes unmapped. - mDevice->GetResidencyManager()->UnlockHeap(ToBackend(mUploadHeap.GetResourceHeap())); + mDevice->GetResidencyManager()->UnlockAllocation(ToBackend(mUploadHeap.GetResourceHeap())); // Invalidate the CPU virtual address & flush cache (if needed). GetResource()->Unmap(0, nullptr); diff --git a/src/tests/white_box/D3D12ResidencyTests.cpp b/src/tests/white_box/D3D12ResidencyTests.cpp index 0a272d1737..e58e48a362 100644 --- a/src/tests/white_box/D3D12ResidencyTests.cpp +++ b/src/tests/white_box/D3D12ResidencyTests.cpp @@ -16,7 +16,9 @@ #include "dawn_native/d3d12/BufferD3D12.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/ResidencyManagerD3D12.h" +#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" #include "tests/DawnTest.h" +#include "utils/ComboRenderPipelineDescriptor.h" #include "utils/WGPUHelpers.h" #include @@ -32,7 +34,7 @@ constexpr wgpu::BufferUsage kMapWriteBufferUsage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; constexpr wgpu::BufferUsage kNonMappableBufferUsage = wgpu::BufferUsage::CopyDst; -class D3D12ResidencyTests : public DawnTest { +class D3D12ResidencyTestBase : public DawnTest { protected: void SetUp() override { DawnTest::SetUp(); @@ -62,23 +64,6 @@ class D3D12ResidencyTests : public DawnTest { return buffers; } - bool CheckIfBufferIsResident(wgpu::Buffer buffer) const { - dawn_native::d3d12::Buffer* d3dBuffer = - reinterpret_cast(buffer.Get()); - return d3dBuffer->CheckIsResidentForTesting(); - } - - bool CheckAllocationMethod(wgpu::Buffer buffer, - dawn_native::AllocationMethod allocationMethod) const { - dawn_native::d3d12::Buffer* d3dBuffer = - reinterpret_cast(buffer.Get()); - return d3dBuffer->CheckAllocationMethodForTesting(allocationMethod); - } - - bool IsUMA() const { - return reinterpret_cast(device.Get())->GetDeviceInfo().isUMA; - } - wgpu::Buffer CreateBuffer(uint32_t bufferSize, wgpu::BufferUsage usage) { wgpu::BufferDescriptor descriptor; @@ -88,26 +73,6 @@ class D3D12ResidencyTests : public DawnTest { 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(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(userdata)->mMappedWriteData = data; - } - void TouchBuffers(uint32_t beginIndex, uint32_t numBuffers, const std::vector& bufferSet) { @@ -126,8 +91,50 @@ class D3D12ResidencyTests : public DawnTest { 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(buffer.Get()); + return d3dBuffer->CheckAllocationMethodForTesting(allocationMethod); + } + + bool CheckIfBufferIsResident(wgpu::Buffer buffer) const { + dawn_native::d3d12::Buffer* d3dBuffer = + reinterpret_cast(buffer.Get()); + return d3dBuffer->CheckIsResidentForTesting(); + } + + bool IsUMA() const { + return reinterpret_cast(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(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(userdata)->mMappedWriteData = data; + } +}; + +class D3D12DescriptorResidencyTests : public D3D12ResidencyTestBase {}; + // 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. 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 // correctly. -TEST_P(D3D12ResidencyTests, OvercommitLargeResources) { +TEST_P(D3D12ResourceResidencyTests, OvercommitLargeResources) { // Create directly-allocated buffers to fill half the budget. std::vector bufferSet1 = AllocateBuffers( kDirectlyAllocatedResourceSize, @@ -205,7 +212,7 @@ TEST_P(D3D12ResidencyTests, OvercommitLargeResources) { } // 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. 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. -TEST_P(D3D12ResidencyTests, AsyncMappedBufferWrite) { +TEST_P(D3D12ResourceResidencyTests, AsyncMappedBufferWrite) { // Create a mappable buffer. wgpu::Buffer buffer = CreateBuffer(4, kMapWriteBufferUsage); // 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. -TEST_P(D3D12ResidencyTests, OvercommitInASingleSubmit) { +TEST_P(D3D12ResourceResidencyTests, OvercommitInASingleSubmit) { // Create enough buffers to exceed the budget constexpr uint32_t numberOfBuffersToOvercommit = 5; std::vector 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 // request. 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(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 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"}));