Descriptor Residency 1: Add Pageable and ShaderVisibleDescriptorHeap

Refactors ShaderVisibleDescriptorAllocator to use d3d12::Heap to
represent ID3D12DescriptorHeaps.

Bug: dawn:193
Change-Id: If0a9df0bc138c4d6f1ad110750ab1e6e8084b80f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/20960
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Brandon Jones 2020-05-19 10:15:12 +00:00 committed by Commit Bot service account
parent 949f1e45f1
commit 9b54466be4
10 changed files with 223 additions and 137 deletions

View File

@ -312,6 +312,8 @@ source_set("dawn_native_sources") {
"d3d12/HeapD3D12.h",
"d3d12/NativeSwapChainImplD3D12.cpp",
"d3d12/NativeSwapChainImplD3D12.h",
"d3d12/PageableD3D12.cpp",
"d3d12/PageableD3D12.h",
"d3d12/PipelineLayoutD3D12.cpp",
"d3d12/PipelineLayoutD3D12.h",
"d3d12/PlatformFunctions.cpp",

View File

@ -193,6 +193,8 @@ if (DAWN_ENABLE_D3D12)
"d3d12/HeapD3D12.h"
"d3d12/NativeSwapChainImplD3D12.cpp"
"d3d12/NativeSwapChainImplD3D12.h"
"d3d12/PageableD3D12.cpp"
"d3d12/PageableD3D12.h"
"d3d12/PipelineLayoutD3D12.cpp"
"d3d12/PipelineLayoutD3D12.h"
"d3d12/PlatformFunctions.cpp"

View File

@ -16,16 +16,8 @@
namespace dawn_native { namespace d3d12 {
Heap::Heap(ComPtr<ID3D12Pageable> d3d12Pageable, MemorySegment memorySegment, uint64_t size)
: mD3d12Pageable(std::move(d3d12Pageable)), mMemorySegment(memorySegment), mSize(size) {
}
Heap::~Heap() {
// When a heap is destroyed, it no longer resides in resident memory, so we must evict
// it from the LRU cache. If this heap is not manually removed from the LRU-cache, the
// ResidencyManager will attempt to use it after it has been deallocated.
if (IsInResidencyLRUCache()) {
RemoveFromList();
}
: Pageable(std::move(d3d12Pageable), memorySegment, size) {
mD3d12Pageable.As(&mD3d12Heap);
}
// This function should only be used when mD3D12Pageable was initialized from a
@ -33,58 +25,7 @@ namespace dawn_native { namespace d3d12 {
// ID3D12Pageable was initially created as an ID3D12Resource (i.e. DirectAllocation), then
// use GetD3D12Pageable().
ID3D12Heap* Heap::GetD3D12Heap() const {
ComPtr<ID3D12Heap> heap;
HRESULT result = mD3d12Pageable.As(&heap);
ASSERT(SUCCEEDED(result));
return heap.Get();
}
ID3D12Pageable* Heap::GetD3D12Pageable() const {
return mD3d12Pageable.Get();
}
MemorySegment Heap::GetMemorySegment() const {
return mMemorySegment;
}
Serial Heap::GetLastUsage() const {
return mLastUsage;
}
void Heap::SetLastUsage(Serial serial) {
mLastUsage = serial;
}
uint64_t Heap::GetLastSubmission() const {
return mLastSubmission;
}
void Heap::SetLastSubmission(Serial serial) {
mLastSubmission = serial;
}
uint64_t Heap::GetSize() const {
return mSize;
}
bool Heap::IsInResidencyLRUCache() const {
return IsInList();
}
void Heap::IncrementResidencyLock() {
mResidencyLockRefCount++;
}
void Heap::DecrementResidencyLock() {
mResidencyLockRefCount--;
}
bool Heap::IsResidencyLocked() const {
if (mResidencyLockRefCount == 0) {
return false;
}
return true;
return mD3d12Heap.Get();
}
}} // namespace dawn_native::d3d12

View File

@ -15,67 +15,25 @@
#ifndef DAWNNATIVE_D3D12_HEAPD3D12_H_
#define DAWNNATIVE_D3D12_HEAPD3D12_H_
#include "common/LinkedList.h"
#include "common/Serial.h"
#include "dawn_native/D3D12Backend.h"
#include "dawn_native/ResourceHeap.h"
#include "dawn_native/d3d12/PageableD3D12.h"
#include "dawn_native/d3d12/d3d12_platform.h"
namespace dawn_native { namespace d3d12 {
class Device;
// This class is used to represent heap allocations, but also serves as a node within the
// ResidencyManager's LRU cache. This node is inserted into the LRU-cache when it is first
// allocated, and any time it is scheduled to be used by the GPU. This node is removed from the
// LRU cache when it is evicted from resident memory due to budget constraints, or when the heap
// is destroyed.
class Heap : public ResourceHeapBase, public LinkNode<Heap> {
// This class is used to represent ID3D12Heap allocations, as well as an implicit heap
// representing a directly allocated resource. It inherits from Pageable because each Heap must
// be represented in the ResidencyManager.
class Heap : public ResourceHeapBase, public Pageable {
public:
Heap(ComPtr<ID3D12Pageable> d3d12Pageable, MemorySegment memorySegment, uint64_t size);
~Heap();
ID3D12Heap* GetD3D12Heap() const;
ID3D12Pageable* GetD3D12Pageable() const;
MemorySegment GetMemorySegment() const;
// We set mLastRecordingSerial to denote the serial this heap was last recorded to be used.
// We must check this serial against the current serial when recording heap usages to ensure
// we do not process residency for this heap multiple times.
Serial GetLastUsage() const;
void SetLastUsage(Serial serial);
// The residency manager must know the last serial that any portion of the heap was
// submitted to be used so that we can ensure this heap stays resident in memory at least
// until that serial has completed.
uint64_t GetLastSubmission() const;
void SetLastSubmission(Serial serial);
uint64_t GetSize() const;
bool IsInResidencyLRUCache() const;
// In some scenarios, such as async buffer mapping, we must lock residency to ensure the
// heap cannot be evicted. Because multiple buffers may be mapped in a single heap, we must
// track the number of resources currently locked.
void IncrementResidencyLock();
void DecrementResidencyLock();
bool IsResidencyLocked() const;
private:
ComPtr<ID3D12Pageable> mD3d12Pageable;
MemorySegment mMemorySegment;
// mLastUsage denotes the last time this heap was recorded for use.
Serial mLastUsage = 0;
// mLastSubmission denotes the last time this heap was submitted to the GPU. Note that
// although this variable often contains the same value as mLastUsage, it can differ in some
// situations. When some asynchronous APIs (like SetSubData) are called, mLastUsage is
// updated upon the call, but the backend operation is deferred until the next submission
// to the GPU. This makes mLastSubmission unique from mLastUsage, and allows us to
// accurately identify when heaps are evictable.
Serial mLastSubmission = 0;
uint32_t mResidencyLockRefCount = 0;
uint64_t mSize = 0;
ComPtr<ID3D12Heap> mD3d12Heap;
};
}} // namespace dawn_native::d3d12

View File

@ -0,0 +1,76 @@
// Copyright 2020 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/d3d12/PageableD3D12.h"
namespace dawn_native { namespace d3d12 {
Pageable::Pageable(ComPtr<ID3D12Pageable> d3d12Pageable,
MemorySegment memorySegment,
uint64_t size)
: mD3d12Pageable(std::move(d3d12Pageable)), mMemorySegment(memorySegment), mSize(size) {
}
// When a pageable is destroyed, it no longer resides in resident memory, so we must evict
// it from the LRU cache. If this heap is not manually removed from the LRU-cache, the
// ResidencyManager will attempt to use it after it has been deallocated.
Pageable::~Pageable() {
if (IsInResidencyLRUCache()) {
RemoveFromList();
}
}
ID3D12Pageable* Pageable::GetD3D12Pageable() const {
return mD3d12Pageable.Get();
}
Serial Pageable::GetLastUsage() const {
return mLastUsage;
}
void Pageable::SetLastUsage(Serial serial) {
mLastUsage = serial;
}
uint64_t Pageable::GetLastSubmission() const {
return mLastSubmission;
}
void Pageable::SetLastSubmission(Serial serial) {
mLastSubmission = serial;
}
MemorySegment Pageable::GetMemorySegment() const {
return mMemorySegment;
}
uint64_t Pageable::GetSize() const {
return mSize;
}
bool Pageable::IsInResidencyLRUCache() const {
return IsInList();
}
void Pageable::IncrementResidencyLock() {
mResidencyLockRefCount++;
}
void Pageable::DecrementResidencyLock() {
mResidencyLockRefCount--;
}
bool Pageable::IsResidencyLocked() const {
return mResidencyLockRefCount != 0;
}
}} // namespace dawn_native::d3d12

View File

@ -0,0 +1,80 @@
// Copyright 2020 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_D3D12_PAGEABLED3D12_H_
#define DAWNNATIVE_D3D12_PAGEABLED3D12_H_
#include "common/LinkedList.h"
#include "common/Serial.h"
#include "dawn_native/D3D12Backend.h"
#include "dawn_native/d3d12/d3d12_platform.h"
namespace dawn_native { namespace d3d12 {
// This class is used to represent ID3D12Pageable allocations, and also serves as a node within
// the ResidencyManager's LRU cache. This node is inserted into the LRU-cache when it is first
// allocated, and any time it is scheduled to be used by the GPU. This node is removed from the
// LRU cache when it is evicted from resident memory due to budget constraints, or when the
// pageable allocation is released.
class Pageable : public LinkNode<Pageable> {
public:
Pageable(ComPtr<ID3D12Pageable> d3d12Pageable, MemorySegment memorySegment, uint64_t size);
~Pageable();
ID3D12Pageable* GetD3D12Pageable() const;
// We set mLastRecordingSerial to denote the serial this pageable was last recorded to be
// used. We must check this serial against the current serial when recording usages to
// ensure we do not process residency for this pageable multiple times.
Serial GetLastUsage() const;
void SetLastUsage(Serial serial);
// The residency manager must know the last serial that any portion of the pageable was
// submitted to be used so that we can ensure this pageable stays resident in memory at
// least until that serial has completed.
uint64_t GetLastSubmission() const;
void SetLastSubmission(Serial serial);
MemorySegment GetMemorySegment() const;
uint64_t GetSize() const;
bool IsInResidencyLRUCache() const;
// In some scenarios, such as async buffer mapping or descriptor heaps, we must lock
// residency to ensure the pageable cannot be evicted. Because multiple buffers may be
// mapped in a single heap, we must track the number of resources currently locked.
void IncrementResidencyLock();
void DecrementResidencyLock();
bool IsResidencyLocked() const;
protected:
ComPtr<ID3D12Pageable> mD3d12Pageable;
private:
// mLastUsage denotes the last time this pageable was recorded for use.
Serial mLastUsage = 0;
// mLastSubmission denotes the last time this pageable was submitted to the GPU. Note that
// although this variable often contains the same value as mLastUsage, it can differ in some
// situations. When some asynchronous APIs (like SetSubData) are called, mLastUsage is
// updated upon the call, but the backend operation is deferred until the next submission
// to the GPU. This makes mLastSubmission unique from mLastUsage, and allows us to
// accurately identify when a pageable can be evicted.
Serial mLastSubmission = 0;
MemorySegment mMemorySegment;
uint32_t mResidencyLockRefCount = 0;
uint64_t mSize = 0;
};
}} // namespace dawn_native::d3d12
#endif

View File

@ -144,12 +144,12 @@ namespace dawn_native { namespace d3d12 {
// Removes a heap from the LRU and returns the least recently used heap when possible. Returns
// nullptr when nothing further can be evicted.
ResultOrError<Heap*> ResidencyManager::RemoveSingleEntryFromLRU(
ResultOrError<Pageable*> ResidencyManager::RemoveSingleEntryFromLRU(
MemorySegmentInfo* memorySegment) {
ASSERT(!memorySegment->lruCache.empty());
Heap* heap = memorySegment->lruCache.head()->value();
Pageable* pageable = memorySegment->lruCache.head()->value();
Serial lastSubmissionSerial = heap->GetLastSubmission();
Serial lastSubmissionSerial = pageable->GetLastSubmission();
// If the next candidate for eviction was inserted into the LRU during the current serial,
// it is because more memory is being used in a single command list than is available.
@ -164,8 +164,8 @@ namespace dawn_native { namespace d3d12 {
DAWN_TRY(mDevice->WaitForSerial(lastSubmissionSerial));
}
heap->RemoveFromList();
return heap;
pageable->RemoveFromList();
return pageable;
}
MaybeError ResidencyManager::EnsureCanAllocate(uint64_t allocationSize,
@ -197,16 +197,16 @@ namespace dawn_native { namespace d3d12 {
uint64_t sizeNeededToBeUnderBudget = memoryUsageAfterMakeResident - memorySegment->budget;
uint64_t sizeEvicted = 0;
while (sizeEvicted < sizeNeededToBeUnderBudget) {
Heap* heap;
DAWN_TRY_ASSIGN(heap, RemoveSingleEntryFromLRU(memorySegment));
Pageable* pageable;
DAWN_TRY_ASSIGN(pageable, RemoveSingleEntryFromLRU(memorySegment));
// If no heap was returned, then nothing more can be evicted.
if (heap == nullptr) {
if (pageable == nullptr) {
break;
}
sizeEvicted += heap->GetSize();
resourcesToEvict.push_back(heap->GetD3D12Pageable());
sizeEvicted += pageable->GetSize();
resourcesToEvict.push_back(pageable->GetD3D12Pageable());
}
if (resourcesToEvict.size() > 0) {
@ -287,13 +287,13 @@ namespace dawn_native { namespace d3d12 {
// Inserts a heap at the bottom of the LRU. The passed heap must be resident or scheduled to
// become resident within the current serial.
void ResidencyManager::TrackResidentAllocation(Heap* heap) {
void ResidencyManager::TrackResidentAllocation(Pageable* pageable) {
if (!mResidencyManagementEnabled) {
return;
}
ASSERT(heap->IsInList() == false);
GetMemorySegmentInfo(heap->GetMemorySegment())->lruCache.Append(heap);
ASSERT(pageable->IsInList() == false);
GetMemorySegmentInfo(pageable->GetMemorySegment())->lruCache.Append(pageable);
}
// Places an artifical cap on Dawn's budget so we can test in a predictable manner. If used,

View File

@ -27,6 +27,7 @@ namespace dawn_native { namespace d3d12 {
class Device;
class Heap;
class Pageable;
class ResidencyManager {
public:
@ -41,14 +42,14 @@ namespace dawn_native { namespace d3d12 {
uint64_t SetExternalMemoryReservation(MemorySegment segment,
uint64_t requestedReservationSize);
void TrackResidentAllocation(Heap* heap);
void TrackResidentAllocation(Pageable* pageable);
void RestrictBudgetForTesting(uint64_t artificialBudgetCap);
private:
struct MemorySegmentInfo {
const DXGI_MEMORY_SEGMENT_GROUP dxgiSegment;
LinkedList<Heap> lruCache = {};
LinkedList<Pageable> lruCache = {};
uint64_t budget = 0;
uint64_t usage = 0;
uint64_t externalReservation = 0;
@ -62,7 +63,7 @@ namespace dawn_native { namespace d3d12 {
MemorySegmentInfo* GetMemorySegmentInfo(MemorySegment memorySegment);
MaybeError EnsureCanMakeResident(uint64_t allocationSize, MemorySegmentInfo* memorySegment);
ResultOrError<Heap*> RemoveSingleEntryFromLRU(MemorySegmentInfo* memorySegment);
ResultOrError<Pageable*> RemoveSingleEntryFromLRU(MemorySegmentInfo* memorySegment);
void UpdateVideoMemoryInfo();
void UpdateMemorySegmentInfo(MemorySegmentInfo* segmentInfo);

View File

@ -77,7 +77,7 @@ namespace dawn_native { namespace d3d12 {
return false;
}
ID3D12DescriptorHeap* descriptorHeap = mHeap.Get();
ID3D12DescriptorHeap* descriptorHeap = mHeap->GetD3D12DescriptorHeap();
const uint64_t heapOffset = mSizeIncrement * startOffset;
@ -99,7 +99,7 @@ namespace dawn_native { namespace d3d12 {
}
ID3D12DescriptorHeap* ShaderVisibleDescriptorAllocator::GetShaderVisibleHeap() const {
return mHeap.Get();
return mHeap->GetD3D12DescriptorHeap();
}
void ShaderVisibleDescriptorAllocator::Tick(uint64_t completedSerial) {
@ -108,7 +108,7 @@ namespace dawn_native { namespace d3d12 {
// Creates a GPU descriptor heap that manages descriptors in a FIFO queue.
MaybeError ShaderVisibleDescriptorAllocator::AllocateAndSwitchShaderVisibleHeap() {
ComPtr<ID3D12DescriptorHeap> heap;
std::unique_ptr<ShaderVisibleDescriptorHeap> descriptorHeap;
// Return the switched out heap to the pool and retrieve the oldest heap that is no longer
// used by GPU. This maintains a heap buffer to avoid frequently re-creating heaps for heavy
// users.
@ -119,7 +119,7 @@ namespace dawn_native { namespace d3d12 {
// Recycle existing heap if possible.
if (!mPool.empty() && mPool.front().heapSerial <= mDevice->GetCompletedCommandSerial()) {
heap = std::move(mPool.front().heap);
descriptorHeap = std::move(mPool.front().heap);
mPool.pop_front();
}
@ -129,19 +129,23 @@ namespace dawn_native { namespace d3d12 {
const uint32_t descriptorCount = GetD3D12ShaderVisibleHeapSize(
mHeapType, mDevice->IsToggleEnabled(Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
if (heap == nullptr) {
if (descriptorHeap == nullptr) {
ComPtr<ID3D12DescriptorHeap> d3d12DescriptorHeap;
D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor;
heapDescriptor.Type = mHeapType;
heapDescriptor.NumDescriptors = descriptorCount;
heapDescriptor.Flags = GetD3D12HeapFlags(mHeapType);
heapDescriptor.NodeMask = 0;
DAWN_TRY(CheckOutOfMemoryHRESULT(mDevice->GetD3D12Device()->CreateDescriptorHeap(
&heapDescriptor, IID_PPV_ARGS(&heap)),
"ID3D12Device::CreateDescriptorHeap"));
DAWN_TRY(
CheckOutOfMemoryHRESULT(mDevice->GetD3D12Device()->CreateDescriptorHeap(
&heapDescriptor, IID_PPV_ARGS(&d3d12DescriptorHeap)),
"ID3D12Device::CreateDescriptorHeap"));
descriptorHeap = std::make_unique<ShaderVisibleDescriptorHeap>(
std::move(d3d12DescriptorHeap), mSizeIncrement * descriptorCount);
}
// Create a FIFO buffer from the recently created heap.
mHeap = std::move(heap);
mHeap = std::move(descriptorHeap);
mAllocator = RingBufferAllocator(descriptorCount);
// Invalidate all bindgroup allocations on previously bound heaps by incrementing the heap
@ -171,4 +175,15 @@ namespace dawn_native { namespace d3d12 {
return (allocation.GetLastUsageSerial() > mDevice->GetCompletedCommandSerial() &&
allocation.GetHeapSerial() == mHeapSerial);
}
ShaderVisibleDescriptorHeap::ShaderVisibleDescriptorHeap(
ComPtr<ID3D12DescriptorHeap> d3d12DescriptorHeap,
uint64_t size)
: Pageable(d3d12DescriptorHeap, MemorySegment::Local, size),
mD3d12DescriptorHeap(std::move(d3d12DescriptorHeap)) {
}
ID3D12DescriptorHeap* ShaderVisibleDescriptorHeap::GetD3D12DescriptorHeap() const {
return mD3d12DescriptorHeap.Get();
}
}} // namespace dawn_native::d3d12

View File

@ -17,6 +17,7 @@
#include "dawn_native/Error.h"
#include "dawn_native/RingBufferAllocator.h"
#include "dawn_native/d3d12/PageableD3D12.h"
#include "dawn_native/d3d12/d3d12_platform.h"
#include <list>
@ -32,6 +33,16 @@ namespace dawn_native { namespace d3d12 {
class Device;
class GPUDescriptorHeapAllocation;
class ShaderVisibleDescriptorHeap : public Pageable {
public:
ShaderVisibleDescriptorHeap(ComPtr<ID3D12DescriptorHeap> d3d12DescriptorHeap,
uint64_t size);
ID3D12DescriptorHeap* GetD3D12DescriptorHeap() const;
private:
ComPtr<ID3D12DescriptorHeap> mD3d12DescriptorHeap;
};
class ShaderVisibleDescriptorAllocator {
public:
static ResultOrError<std::unique_ptr<ShaderVisibleDescriptorAllocator>> Create(
@ -62,10 +73,10 @@ namespace dawn_native { namespace d3d12 {
private:
struct SerialDescriptorHeap {
Serial heapSerial;
ComPtr<ID3D12DescriptorHeap> heap;
std::unique_ptr<ShaderVisibleDescriptorHeap> heap;
};
ComPtr<ID3D12DescriptorHeap> mHeap;
std::unique_ptr<ShaderVisibleDescriptorHeap> mHeap;
RingBufferAllocator mAllocator;
std::list<SerialDescriptorHeap> mPool;
D3D12_DESCRIPTOR_HEAP_TYPE mHeapType;