Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions include/gpgmm_d3d12.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,15 @@ namespace gpgmm::d3d12 {
*/
struct RESIDENCY_HEAP_INFO {
/** \brief Created size, in bytes, of the heap.

Must be non-zero. SizeInBytes is always a multiple of the alignment.
*/
UINT64 SizeInBytes;

/** \brief Created alignment, in bytes, of the heap.

Must be non-zero.
*/
UINT64 Alignment;

/** \brief Check if the heap is currently locked for residency.

A locked heap means the heap is not eligible for eviction.
*/
/** \brief Determine if the heap is currently locked for residency.
*/
bool IsLocked;

/** \brief Determine if the heap is resident or not.
Expand Down Expand Up @@ -151,24 +145,26 @@ namespace gpgmm::d3d12 {
Specifies creation options for a residency managed heap.
*/
struct RESIDENCY_HEAP_DESC {
/** \brief Specifies the memory segment the heap will reside in.

Required parameter. Must be local or non-local segment.
*/
DXGI_MEMORY_SEGMENT_GROUP HeapSegment;

/** \brief Created size of the heap, in bytes.

Must be non-zero. SizeInBytes is always a multiple of the alignment.
SizeInBytes is always a multiple of the alignment.

Optional parameter. By default, the size is inferred by type of the created heap.
*/
UINT64 SizeInBytes;

/** \brief Created alignment of the heap, in bytes.

Must be non-zero.
Optional parameter. By default, the alignment is inferred by type of the created heap.
*/
UINT64 Alignment;

/** \brief Specifies the memory segment the heap will reside in.

Required parameter.
*/
DXGI_MEMORY_SEGMENT_GROUP HeapSegment;

/** \brief Specifies heaps options.

Optional parameter. By default, no flags are specified or RESIDENCY_HEAP_FLAG_NONE.
Expand Down
84 changes: 78 additions & 6 deletions src/gpgmm/d3d12/ResidencyHeapD3D12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

namespace gpgmm::d3d12 {

// D3D12 doesn't provide a allocation info definition for non-resource heap types (eg.
// descriptor heaps) but the info is the otherwise the same as resource heaps.
using HEAP_ALLOCATION_INFO = D3D12_RESOURCE_ALLOCATION_INFO;

namespace {

// Returns the resource heap flags or E_INVALIDARG, when the memory type doesn't allow
Expand All @@ -45,6 +49,53 @@ namespace gpgmm::d3d12 {

return E_INVALIDARG;
}

HEAP_ALLOCATION_INFO GetResourceHeapAllocationInfo(ComPtr<ID3D12Pageable> pageable) {
ComPtr<ID3D12Heap> heap;
if (SUCCEEDED(pageable.As(&heap))) {
const D3D12_HEAP_DESC desc = heap->GetDesc();
return {desc.SizeInBytes, desc.Alignment};
}

ComPtr<ID3D12Resource> committedResource;
if (SUCCEEDED(pageable.As(&committedResource))) {
const D3D12_RESOURCE_DESC desc = committedResource->GetDesc();
const D3D12_RESOURCE_ALLOCATION_INFO info =
GetDevice(committedResource.Get())->GetResourceAllocationInfo(0, 1, &desc);
return info;
}

return {kInvalidSize, kInvalidSize};
}

HEAP_ALLOCATION_INFO GetDescriptorHeapAllocationInfo(ComPtr<ID3D12Pageable> pageable) {
ComPtr<ID3D12DescriptorHeap> heap;
if (SUCCEEDED(pageable.As(&heap))) {
const D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc();
const uint64_t sizePerDescriptor =
GetDevice(heap.Get())->GetDescriptorHandleIncrementSize(desc.Type);
return {desc.NumDescriptors * sizePerDescriptor, sizePerDescriptor};
}

return {kInvalidSize, kInvalidSize};
}

HEAP_ALLOCATION_INFO GetHeapAllocationInfo(ComPtr<ID3D12Pageable> pageable) {
const D3D12_RESOURCE_ALLOCATION_INFO resourceHeapInfo =
GetResourceHeapAllocationInfo(pageable);
if (resourceHeapInfo.SizeInBytes != kInvalidSize) {
return resourceHeapInfo;
}

const HEAP_ALLOCATION_INFO descriptorHeapInfo =
GetDescriptorHeapAllocationInfo(pageable);
if (descriptorHeapInfo.SizeInBytes != kInvalidSize) {
return descriptorHeapInfo;
}

return {kInvalidSize, kInvalidSize};
}

} // namespace

RESIDENCY_HEAP_FLAGS GetHeapFlags(D3D12_HEAP_FLAGS heapFlags, bool alwaysCreatedInBudget) {
Expand Down Expand Up @@ -85,8 +136,29 @@ namespace gpgmm::d3d12 {
ResidencyManager* residencyManager = static_cast<ResidencyManager*>(pResidencyManager);
const bool isResidencyDisabled = (pResidencyManager == nullptr);

RESIDENCY_HEAP_DESC newDescriptor = descriptor;
const HEAP_ALLOCATION_INFO heapInfo = GetHeapAllocationInfo(pPageable);
if (descriptor.SizeInBytes == 0) {
newDescriptor.SizeInBytes = heapInfo.SizeInBytes;
}

if (newDescriptor.SizeInBytes == kInvalidSize) {
ErrorLog(MessageId::kInvalidArgument, true) << "Heap size for residency was invalid";
return E_INVALIDARG;
}

if (descriptor.Alignment == 0) {
newDescriptor.Alignment = heapInfo.Alignment;
}

if (newDescriptor.Alignment == kInvalidSize) {
ErrorLog(MessageId::kInvalidArgument, true)
<< "Heap alignment for residency was invalid.";
return E_INVALIDARG;
}

std::unique_ptr<ResidencyHeap> heap(
new ResidencyHeap(pPageable, descriptor, isResidencyDisabled));
new ResidencyHeap(pPageable, newDescriptor, isResidencyDisabled));

if (!isResidencyDisabled) {
// Check if the underlying memory was implicitly made resident.
Expand All @@ -103,7 +175,7 @@ namespace gpgmm::d3d12 {

// Heap created not resident requires no budget to be created.
if (heap->GetInfo().Status == RESIDENCY_HEAP_STATUS_EVICTED &&
(descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET)) {
(newDescriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET)) {
ErrorLog(heap.get(), MessageId::kInvalidArgument)
<< "Creating a heap always in budget cannot be used with "
"D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT.";
Expand All @@ -118,7 +190,7 @@ namespace gpgmm::d3d12 {
GPGMM_RETURN_IF_FAILED(residencyManager->InsertHeap(heap.get()),
GetDevice(pPageable));
} else {
if (descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) {
if (newDescriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) {
GPGMM_RETURN_IF_FAILED(residencyManager->LockHeap(heap.get()),
GetDevice(pPageable));
GPGMM_RETURN_IF_FAILED(residencyManager->UnlockHeap(heap.get()),
Expand All @@ -127,15 +199,15 @@ namespace gpgmm::d3d12 {
}
}
} else {
if (descriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) {
if (newDescriptor.Flags & RESIDENCY_HEAP_FLAG_CREATE_RESIDENT) {
WarnLog(heap.get(), MessageId::kInvalidArgument)
<< "RESIDENCY_HEAP_FLAG_CREATE_RESIDENT was specified but had no effect "
"becauase residency management is not being used.";
}
}

GPGMM_RETURN_IF_FAILED(heap->SetDebugName(descriptor.DebugName), GetDevice(pPageable));
GPGMM_TRACE_EVENT_OBJECT_SNAPSHOT(heap.get(), descriptor);
GPGMM_RETURN_IF_FAILED(heap->SetDebugName(newDescriptor.DebugName), GetDevice(pPageable));
GPGMM_TRACE_EVENT_OBJECT_SNAPSHOT(heap.get(), newDescriptor);

DebugLog(heap.get(), MessageId::kObjectCreated)
<< "Created heap, Size=" << heap->GetInfo().SizeInBytes
Expand Down
53 changes: 33 additions & 20 deletions src/tests/end2end/D3D12ResidencyManagerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,30 +158,29 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) {
ComPtr<IResidencyManager> residencyManager;
ASSERT_SUCCEEDED(CreateResidencyManager(CreateBasicResidencyDesc(kDefaultBudget), mDevice.Get(),
mAdapter.Get(), &residencyManager));

constexpr uint64_t kHeapSize = GPGMM_MB_TO_BYTES(10);
ASSERT_NE(residencyManager.Get(), nullptr);

D3D12_HEAP_PROPERTIES heapProperties = {};
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;

D3D12_HEAP_DESC heapDesc = {};
heapDesc.Properties = heapProperties;
heapDesc.SizeInBytes = kHeapSize;
heapDesc.SizeInBytes = GPGMM_MB_TO_BYTES(10);

// Assume tier 1, which all adapters support.
heapDesc.Flags |= D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;

CreateResourceHeapCallbackContext createHeapContext(mDevice.Get(), &heapDesc);
BadCreateHeapCallbackContext badCreateHeapCallbackContext;

RESIDENCY_HEAP_DESC residencyHeapDesc = {};
residencyHeapDesc.SizeInBytes = kHeapSize;
residencyHeapDesc.HeapSegment = DXGI_MEMORY_SEGMENT_GROUP_LOCAL;

ASSERT_FAILED(CreateResidencyHeap(residencyHeapDesc, residencyManager.Get(),
BadCreateHeapCallbackContext::CreateHeap,
&badCreateHeapCallbackContext, nullptr));
{
BadCreateHeapCallbackContext badCreateHeapCallbackContext;
ASSERT_FAILED(CreateResidencyHeap(residencyHeapDesc, residencyManager.Get(),
BadCreateHeapCallbackContext::CreateHeap,
&badCreateHeapCallbackContext, nullptr));
}

CreateResourceHeapCallbackContext createHeapContext(mDevice.Get(), &heapDesc);
ASSERT_SUCCEEDED(CreateResidencyHeap(residencyHeapDesc, residencyManager.Get(),
CreateResourceHeapCallbackContext::CreateHeap,
&createHeapContext, nullptr));
Expand All @@ -193,12 +192,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) {
ASSERT_SUCCEEDED(CreateResidencyHeap(residencyHeapDesc, nullptr, pageable.Get(), nullptr));
}

// Create a resource heap without residency.
ComPtr<IResidencyHeap> resourceHeap;
ASSERT_SUCCEEDED(CreateResidencyHeap(residencyHeapDesc, nullptr,
CreateResourceHeapCallbackContext::CreateHeap,
&createHeapContext, &resourceHeap));

// Residency manager must exist to create the heap resident.
{
RESIDENCY_HEAP_DESC invalidResidencyHeapDesc = residencyHeapDesc;
invalidResidencyHeapDesc.Flags |= RESIDENCY_HEAP_FLAG_CREATE_RESIDENT;
Expand All @@ -208,6 +202,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) {
&createHeapContext, nullptr));
}

// Residency manager must exist to create the heap in budget.
{
RESIDENCY_HEAP_DESC invalidResidencyHeapDesc = residencyHeapDesc;
invalidResidencyHeapDesc.Flags |= RESIDENCY_HEAP_FLAG_CREATE_IN_BUDGET;
Expand All @@ -217,6 +212,17 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) {
&createHeapContext, nullptr));
}

// Create a resource heap without residency.
ComPtr<IResidencyHeap> resourceHeap;
ASSERT_SUCCEEDED(CreateResidencyHeap(residencyHeapDesc, nullptr,
CreateResourceHeapCallbackContext::CreateHeap,
&createHeapContext, &resourceHeap));
ASSERT_NE(resourceHeap.Get(), nullptr);

EXPECT_EQ(resourceHeap->GetInfo().SizeInBytes, heapDesc.SizeInBytes);
EXPECT_EQ(resourceHeap->GetInfo().Alignment,
static_cast<uint64_t>(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT));

// Ensure the unmanaged resource heap state is always unknown. Even though D3D12 implicitly
// creates heaps as resident, untrack resource heaps would never transition out from
// RESIDENCY_HEAP_STATUS_RESIDENT and must be left RESIDENCY_HEAP_STATUS_UNKNOWN.
Expand All @@ -229,11 +235,15 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) {
&createHeapContext, &resourceHeap));
ASSERT_NE(resourceHeap, nullptr);

EXPECT_EQ(resourceHeap->GetInfo().SizeInBytes, heapDesc.SizeInBytes);
EXPECT_EQ(resourceHeap->GetInfo().Alignment,
static_cast<uint64_t>(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT));

EXPECT_EQ(resourceHeap->GetInfo().Status, gpgmm::d3d12::RESIDENCY_HEAP_STATUS_RESIDENT);
EXPECT_EQ(resourceHeap->GetInfo().IsLocked, false);

// Residency status of resource heap types is always known.
EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, residencyHeapDesc.SizeInBytes);
EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, heapDesc.SizeInBytes);
EXPECT_EQ(GetStats(residencyManager).CurrentHeapCount, 1u);

ComPtr<ID3D12Heap> heap;
Expand All @@ -245,7 +255,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) {
EXPECT_EQ(resourceHeap->GetInfo().Status, gpgmm::d3d12::RESIDENCY_HEAP_STATUS_RESIDENT);
EXPECT_EQ(resourceHeap->GetInfo().IsLocked, true);

EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, residencyHeapDesc.SizeInBytes);
EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, heapDesc.SizeInBytes);
EXPECT_EQ(GetStats(residencyManager).CurrentHeapCount, 1u);

ASSERT_SUCCEEDED(residencyManager->UnlockHeap(resourceHeap.Get()));
Expand All @@ -254,7 +264,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) {
EXPECT_EQ(resourceHeap->GetInfo().IsLocked, false);

// Unlocking a heap does not evict it, the memory usage should not change.
EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, residencyHeapDesc.SizeInBytes);
EXPECT_EQ(GetStats(residencyManager).CurrentHeapUsage, heapDesc.SizeInBytes);
EXPECT_EQ(GetStats(residencyManager).CurrentHeapCount, 1u);

ASSERT_SUCCEEDED(residencyManager->UnlockHeap(resourceHeap.Get())); // Not locked
Expand Down Expand Up @@ -747,7 +757,7 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetExecuteCommandList) {
ASSERT_SUCCEEDED(mDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&commandAllocator)));

ComPtr<ID3D12CommandList> commandList;
ComPtr<ID3D12GraphicsCommandList> commandList;
ASSERT_SUCCEEDED(mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
commandAllocator.Get(), nullptr,
IID_PPV_ARGS(&commandList)));
Expand All @@ -756,6 +766,9 @@ TEST_F(D3D12ResidencyManagerTests, OverBudgetExecuteCommandList) {
ComPtr<ID3D12CommandQueue> queue;
ASSERT_SUCCEEDED(mDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&queue)));

// Command list must be closed before calling ExecuteCommandLists.
ASSERT_SUCCEEDED(commandList->Close());

{
IResidencyList* residencyLists[] = {firstSetOfHeapsWorkingSet.Get()};
ID3D12CommandList* commandLists[] = {commandList.Get()};
Expand Down