[D3D12] Detach UploadBufferPool and DescriptorHeapPool from D3D12Context

This commit is contained in:
Triang3l
2019-10-27 20:53:53 +03:00
parent f23ba862f6
commit b4af63fe31
12 changed files with 369 additions and 413 deletions

View File

@@ -287,9 +287,10 @@ bool D3D12ImmediateDrawer::Initialize() {
// Create pools for draws.
vertex_buffer_pool_ =
std::make_unique<UploadBufferPool>(context_, 2 * 1024 * 1024);
std::make_unique<UploadBufferPool>(device, 2 * 1024 * 1024);
texture_descriptor_pool_ = std::make_unique<DescriptorHeapPool>(
context_, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2048);
device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2048);
texture_descriptor_pool_heap_index_ = DescriptorHeapPool::kHeapIndexInvalid;
// Reset the current state.
current_command_list_ = nullptr;
@@ -460,9 +461,9 @@ void D3D12ImmediateDrawer::Begin(int render_target_width,
texture_uploads_pending_.pop_back();
}
vertex_buffer_pool_->BeginFrame();
texture_descriptor_pool_->BeginFrame();
texture_descriptor_pool_full_update_ = 0;
vertex_buffer_pool_->Reclaim(last_completed_frame);
texture_descriptor_pool_->Reclaim(last_completed_frame);
texture_descriptor_pool_heap_index_ = DescriptorHeapPool::kHeapIndexInvalid;
current_render_target_width_ = render_target_width;
current_render_target_height_ = render_target_height;
@@ -500,9 +501,9 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
vertex_buffer_view.StrideInBytes = UINT(sizeof(ImmediateVertex));
vertex_buffer_view.SizeInBytes =
batch.vertex_count * uint32_t(sizeof(ImmediateVertex));
void* vertex_buffer_mapping = vertex_buffer_pool_->RequestFull(
vertex_buffer_view.SizeInBytes, nullptr, nullptr,
&vertex_buffer_view.BufferLocation);
void* vertex_buffer_mapping = vertex_buffer_pool_->Request(
context_->GetCurrentFrame(), vertex_buffer_view.SizeInBytes, nullptr,
nullptr, &vertex_buffer_view.BufferLocation);
if (vertex_buffer_mapping == nullptr) {
XELOGE("Failed to get a buffer for %u vertices in the immediate drawer",
batch.vertex_count);
@@ -518,7 +519,8 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
D3D12_INDEX_BUFFER_VIEW index_buffer_view;
index_buffer_view.SizeInBytes = batch.index_count * sizeof(uint16_t);
index_buffer_view.Format = DXGI_FORMAT_R16_UINT;
void* index_buffer_mapping = vertex_buffer_pool_->RequestFull(
void* index_buffer_mapping = vertex_buffer_pool_->Request(
context_->GetCurrentFrame(),
xe::align(index_buffer_view.SizeInBytes, UINT(sizeof(uint32_t))),
nullptr, nullptr, &index_buffer_view.BufferLocation);
if (index_buffer_mapping == nullptr) {
@@ -560,15 +562,15 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
}
bool bind_texture = current_texture_ != texture;
uint32_t texture_descriptor_index;
uint64_t texture_full_update = texture_descriptor_pool_->Request(
texture_descriptor_pool_full_update_, bind_texture ? 1 : 0, 1,
texture_descriptor_index);
if (texture_full_update == 0) {
uint64_t texture_heap_index = texture_descriptor_pool_->Request(
context_->GetCurrentFrame(), texture_descriptor_pool_heap_index_,
bind_texture ? 1 : 0, 1, texture_descriptor_index);
if (texture_heap_index == DescriptorHeapPool::kHeapIndexInvalid) {
return;
}
if (texture_descriptor_pool_full_update_ != texture_full_update) {
if (texture_descriptor_pool_heap_index_ != texture_heap_index) {
bind_texture = true;
texture_descriptor_pool_full_update_ = texture_full_update;
texture_descriptor_pool_heap_index_ = texture_heap_index;
ID3D12DescriptorHeap* descriptor_heaps[] = {
texture_descriptor_pool_->GetLastRequestHeap(), sampler_heap_};
current_command_list_->SetDescriptorHeaps(2, descriptor_heaps);
@@ -673,9 +675,6 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
void D3D12ImmediateDrawer::EndDrawBatch() { batch_open_ = false; }
void D3D12ImmediateDrawer::End() {
texture_descriptor_pool_->EndFrame();
vertex_buffer_pool_->EndFrame();
current_command_list_ = nullptr;
}

View File

@@ -77,6 +77,7 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
std::unique_ptr<UploadBufferPool> vertex_buffer_pool_ = nullptr;
std::unique_ptr<DescriptorHeapPool> texture_descriptor_pool_ = nullptr;
uint64_t texture_descriptor_pool_heap_index_;
struct PendingTextureUpload {
ImmediateTexture* texture;
@@ -94,7 +95,6 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
int current_render_target_width_, current_render_target_height_;
bool batch_open_ = false;
bool batch_has_index_buffer_;
uint64_t texture_descriptor_pool_full_update_;
D3D_PRIMITIVE_TOPOLOGY current_primitive_topology_;
ImmediateTexture* current_texture_;
SamplerIndex current_sampler_index_;

View File

@@ -13,251 +13,212 @@
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/ui/d3d12/d3d12_context.h"
#include "xenia/ui/d3d12/d3d12_util.h"
namespace xe {
namespace ui {
namespace d3d12 {
UploadBufferPool::UploadBufferPool(D3D12Context* context, uint32_t page_size)
: context_(context), page_size_(page_size) {}
UploadBufferPool::UploadBufferPool(ID3D12Device* device, uint32_t page_size)
: device_(device), page_size_(page_size) {}
UploadBufferPool::~UploadBufferPool() {
// Allow mid-frame destruction in cases like device loss.
if (current_mapping_ != nullptr) {
// Don't care about the written range - destroying anyway.
D3D12_RANGE written_range;
written_range.Begin = 0;
written_range.End = 0;
unsent_->buffer->Unmap(0, &written_range);
current_mapping_ = nullptr;
}
current_size_ = 0;
ClearCache();
}
void UploadBufferPool::BeginFrame() {
// Recycle submitted pages not used by the GPU anymore.
uint64_t last_completed_frame = context_->GetLastCompletedFrame();
while (sent_first_ != nullptr) {
auto page = sent_first_;
if (page->frame_sent > last_completed_frame) {
void UploadBufferPool::Reclaim(uint64_t completed_fence_value) {
while (submitted_first_) {
if (submitted_first_->last_usage_fence_value > completed_fence_value) {
break;
}
sent_first_ = page->next;
page->next = unsent_;
unsent_ = page;
if (writable_last_) {
writable_last_->next = submitted_first_;
} else {
writable_first_ = submitted_first_;
}
writable_last_ = submitted_first_;
submitted_first_ = submitted_first_->next;
writable_last_->next = nullptr;
}
if (sent_first_ == nullptr) {
sent_last_ = nullptr;
if (!submitted_first_) {
submitted_last_ = nullptr;
}
// Try to create new pages again in this frame if failed in the previous.
page_creation_failed_ = false;
}
void UploadBufferPool::EndFrame() {
// If something is written to the current page, mark it as submitted.
EndPage();
}
void UploadBufferPool::ClearCache() {
assert(current_size_ == 0);
while (unsent_ != nullptr) {
auto next = unsent_->next;
unsent_->buffer->Release();
delete unsent_;
unsent_ = next;
current_page_used_ = 0;
// Deleting anyway, so assuming data not needed anymore.
D3D12_RANGE written_range;
written_range.Begin = 0;
written_range.End = 0;
while (submitted_first_) {
auto next = submitted_first_->next;
submitted_first_->buffer->Unmap(0, &written_range);
submitted_first_->buffer->Release();
delete submitted_first_;
submitted_first_ = next;
}
while (sent_first_ != nullptr) {
auto next = sent_first_->next;
sent_first_->buffer->Release();
delete sent_first_;
sent_first_ = next;
submitted_last_ = nullptr;
while (writable_first_) {
auto next = writable_first_->next;
writable_first_->buffer->Unmap(0, &written_range);
writable_first_->buffer->Release();
delete writable_first_;
writable_first_ = next;
}
sent_last_ = nullptr;
writable_last_ = nullptr;
}
uint8_t* UploadBufferPool::RequestFull(
uint32_t size, ID3D12Resource** buffer_out, uint32_t* offset_out,
D3D12_GPU_VIRTUAL_ADDRESS* gpu_address_out) {
uint8_t* UploadBufferPool::Request(uint64_t usage_fence_value, uint32_t size,
ID3D12Resource** buffer_out,
uint32_t* offset_out,
D3D12_GPU_VIRTUAL_ADDRESS* gpu_address_out) {
assert_true(size <= page_size_);
if (size > page_size_) {
return nullptr;
}
if (page_size_ - current_size_ < size || current_mapping_ == nullptr) {
assert_true(!current_page_used_ ||
usage_fence_value >= writable_first_->last_usage_fence_value);
assert_true(!submitted_last_ ||
usage_fence_value >= submitted_last_->last_usage_fence_value);
if (page_size_ - current_page_used_ < size || !writable_first_) {
// Start a new page if can't fit all the bytes or don't have an open page.
if (!BeginNextPage()) {
return nullptr;
if (writable_first_) {
// Close the page that was current.
if (submitted_last_) {
submitted_last_->next = writable_first_;
} else {
submitted_first_ = writable_first_;
}
submitted_last_ = writable_first_;
writable_first_ = writable_first_->next;
submitted_last_->next = nullptr;
if (!writable_first_) {
writable_last_ = nullptr;
}
}
}
if (buffer_out != nullptr) {
*buffer_out = unsent_->buffer;
}
if (offset_out != nullptr) {
*offset_out = current_size_;
}
if (gpu_address_out != nullptr) {
if (current_gpu_address_ == 0) {
current_gpu_address_ = unsent_->buffer->GetGPUVirtualAddress();
if (!writable_first_) {
// Create a new page if none available.
D3D12_RESOURCE_DESC new_buffer_desc;
util::FillBufferResourceDesc(new_buffer_desc, page_size_,
D3D12_RESOURCE_FLAG_NONE);
ID3D12Resource* new_buffer;
if (FAILED(device_->CreateCommittedResource(
&util::kHeapPropertiesUpload, D3D12_HEAP_FLAG_NONE,
&new_buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS(&new_buffer)))) {
XELOGE("Failed to create a D3D upload buffer with %u bytes",
page_size_);
return nullptr;
}
D3D12_RANGE read_range;
read_range.Begin = 0;
read_range.End = 0;
void* new_buffer_mapping;
if (FAILED(new_buffer->Map(0, &read_range, &new_buffer_mapping))) {
XELOGE("Failed to map a D3D upload buffer with %u bytes", page_size_);
new_buffer->Release();
return nullptr;
}
writable_first_ = new Page;
writable_first_->buffer = new_buffer;
writable_first_->gpu_address = new_buffer->GetGPUVirtualAddress();
writable_first_->mapping = new_buffer_mapping;
writable_first_->last_usage_fence_value = usage_fence_value;
writable_first_->next = nullptr;
writable_last_ = writable_first_;
}
*gpu_address_out = current_gpu_address_ + current_size_;
current_page_used_ = 0;
}
uint8_t* mapping = current_mapping_ + current_size_;
current_size_ += size;
writable_first_->last_usage_fence_value = usage_fence_value;
if (buffer_out) {
*buffer_out = writable_first_->buffer;
}
if (offset_out) {
*offset_out = current_page_used_;
}
if (gpu_address_out) {
*gpu_address_out = writable_first_->gpu_address + current_page_used_;
}
uint8_t* mapping =
reinterpret_cast<uint8_t*>(writable_first_->mapping) + current_page_used_;
current_page_used_ += size;
return mapping;
}
uint8_t* UploadBufferPool::RequestPartial(
uint32_t size, ID3D12Resource** buffer_out, uint32_t* offset_out,
uint32_t* size_out, D3D12_GPU_VIRTUAL_ADDRESS* gpu_address_out) {
if (current_size_ == page_size_ || current_mapping_ == nullptr) {
// Start a new page if can't fit any bytes or don't have an open page.
if (!BeginNextPage()) {
return nullptr;
}
uint64_t usage_fence_value, uint32_t size, ID3D12Resource** buffer_out,
uint32_t* offset_out, uint32_t* size_out,
D3D12_GPU_VIRTUAL_ADDRESS* gpu_address_out) {
size = std::min(size, page_size_);
if (current_page_used_ < page_size_) {
size = std::min(size, page_size_ - current_page_used_);
}
size = std::min(size, page_size_ - current_size_);
if (buffer_out != nullptr) {
*buffer_out = unsent_->buffer;
uint8_t* mapping =
Request(usage_fence_value, size, buffer_out, offset_out, gpu_address_out);
if (!mapping) {
return nullptr;
}
if (offset_out != nullptr) {
*offset_out = current_size_;
}
if (size_out != nullptr) {
if (size_out) {
*size_out = size;
}
if (gpu_address_out != nullptr) {
if (current_gpu_address_ == 0) {
current_gpu_address_ = unsent_->buffer->GetGPUVirtualAddress();
}
*gpu_address_out = current_gpu_address_ + current_size_;
}
uint8_t* mapping = current_mapping_ + current_size_;
current_size_ += size;
return mapping;
}
void UploadBufferPool::EndPage() {
if (current_mapping_ != nullptr) {
D3D12_RANGE written_range;
written_range.Begin = 0;
written_range.End = current_size_;
unsent_->buffer->Unmap(0, &written_range);
current_mapping_ = nullptr;
}
if (current_size_ != 0) {
auto page = unsent_;
page->frame_sent = context_->GetCurrentFrame();
unsent_ = page->next;
page->next = nullptr;
if (sent_last_ != nullptr) {
sent_last_->next = page;
} else {
sent_first_ = page;
}
sent_last_ = page;
current_size_ = 0;
}
}
constexpr uint64_t DescriptorHeapPool::kHeapIndexInvalid;
bool UploadBufferPool::BeginNextPage() {
EndPage();
if (page_creation_failed_) {
return false;
}
if (unsent_ == nullptr) {
auto device = context_->GetD3D12Provider()->GetDevice();
D3D12_RESOURCE_DESC buffer_desc;
util::FillBufferResourceDesc(buffer_desc, page_size_,
D3D12_RESOURCE_FLAG_NONE);
ID3D12Resource* buffer_resource;
if (FAILED(device->CreateCommittedResource(
&util::kHeapPropertiesUpload, D3D12_HEAP_FLAG_NONE, &buffer_desc,
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS(&buffer_resource)))) {
XELOGE("Failed to create a D3D upload buffer with %u bytes", page_size_);
page_creation_failed_ = true;
return false;
}
unsent_ = new UploadBuffer;
unsent_->buffer = buffer_resource;
unsent_->next = nullptr;
}
D3D12_RANGE read_range;
read_range.Begin = 0;
read_range.End = 0;
void* mapping;
if (FAILED(unsent_->buffer->Map(0, &read_range, &mapping))) {
XELOGE("Failed to map a D3D upload buffer with %u bytes", page_size_);
page_creation_failed_ = true;
return false;
}
current_mapping_ = reinterpret_cast<uint8_t*>(mapping);
current_gpu_address_ = 0;
return true;
}
DescriptorHeapPool::DescriptorHeapPool(D3D12Context* context,
DescriptorHeapPool::DescriptorHeapPool(ID3D12Device* device,
D3D12_DESCRIPTOR_HEAP_TYPE type,
uint32_t page_size)
: context_(context), type_(type), page_size_(page_size) {}
: device_(device), type_(type), page_size_(page_size) {}
DescriptorHeapPool::~DescriptorHeapPool() {
// Allow mid-frame destruction in cases like device loss.
current_size_ = 0;
ClearCache();
}
void DescriptorHeapPool::BeginFrame() {
// Don't hold old pages if few descriptors are written, also make 0 usable as
// an invalid page index.
++current_page_;
// Recycle submitted pages not used by the GPU anymore.
uint64_t last_completed_frame = context_->GetLastCompletedFrame();
while (sent_first_ != nullptr) {
auto page = sent_first_;
if (page->frame_sent > last_completed_frame) {
void DescriptorHeapPool::Reclaim(uint64_t completed_fence_value) {
while (submitted_first_) {
if (submitted_first_->last_usage_fence_value > completed_fence_value) {
break;
}
sent_first_ = page->next;
page->next = unsent_;
unsent_ = page;
if (writable_last_) {
writable_last_->next = submitted_first_;
} else {
writable_first_ = submitted_first_;
}
writable_last_ = submitted_first_;
submitted_first_ = submitted_first_->next;
writable_last_->next = nullptr;
}
if (sent_first_ == nullptr) {
sent_last_ = nullptr;
if (!submitted_first_) {
submitted_last_ = nullptr;
}
// Try to create new pages again in this frame if failed in the previous.
page_creation_failed_ = false;
}
void DescriptorHeapPool::EndFrame() { EndPage(); }
void DescriptorHeapPool::ClearCache() {
assert_true(current_size_ == 0);
while (unsent_ != nullptr) {
auto next = unsent_->next;
unsent_->heap->Release();
delete unsent_;
unsent_ = next;
// Not checking current_page_used_ != 0 because asking for 0 descriptors
// returns a valid heap also - but actually the new heap will be different now
// and the old one must be unbound since it doesn't exist anymore.
++current_heap_index_;
current_page_used_ = 0;
while (submitted_first_) {
auto next = submitted_first_->next;
submitted_first_->heap->Release();
delete submitted_first_;
submitted_first_ = next;
}
while (sent_first_ != nullptr) {
auto next = sent_first_->next;
sent_first_->heap->Release();
delete sent_first_;
sent_first_ = next;
submitted_last_ = nullptr;
while (writable_first_) {
auto next = writable_first_->next;
writable_first_->heap->Release();
delete writable_first_;
writable_first_ = next;
}
sent_last_ = nullptr;
writable_last_ = nullptr;
}
uint64_t DescriptorHeapPool::Request(uint64_t previous_full_update,
uint64_t DescriptorHeapPool::Request(uint64_t usage_fence_value,
uint64_t previous_heap_index,
uint32_t count_for_partial_update,
uint32_t count_for_full_update,
uint32_t& index_out) {
@@ -265,75 +226,63 @@ uint64_t DescriptorHeapPool::Request(uint64_t previous_full_update,
assert_true(count_for_full_update <= page_size_);
if (count_for_partial_update > count_for_full_update ||
count_for_full_update > page_size_) {
return 0;
return kHeapIndexInvalid;
}
if (page_creation_failed_) {
// Don't touch the page index every call if there was a failure as well.
return 0;
}
assert_true(!current_page_used_ ||
usage_fence_value >= writable_first_->last_usage_fence_value);
assert_true(!submitted_last_ ||
usage_fence_value >= submitted_last_->last_usage_fence_value);
// If the last full update happened on the current page, a partial update is
// possible.
uint32_t count = previous_full_update == current_page_
uint32_t count = previous_heap_index == current_heap_index_
? count_for_partial_update
: count_for_full_update;
// Go to the next page if there's not enough free space on the current one,
// or because the previous page may be outdated. In this case, a full update
// is necessary.
if (page_size_ - current_size_ < count) {
EndPage();
++current_page_;
if (page_size_ - current_page_used_ < count) {
// Close the page that was current.
if (submitted_last_) {
submitted_last_->next = writable_first_;
} else {
submitted_first_ = writable_first_;
}
submitted_last_ = writable_first_;
writable_first_ = writable_first_->next;
submitted_last_->next = nullptr;
if (!writable_first_) {
writable_last_ = nullptr;
}
++current_heap_index_;
current_page_used_ = 0;
count = count_for_full_update;
}
// Create the page if needed (may be the first call for the page).
if (unsent_ == nullptr) {
auto device = context_->GetD3D12Provider()->GetDevice();
D3D12_DESCRIPTOR_HEAP_DESC heap_desc;
heap_desc.Type = type_;
heap_desc.NumDescriptors = page_size_;
heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
heap_desc.NodeMask = 0;
ID3D12DescriptorHeap* heap;
if (FAILED(device->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&heap)))) {
if (!writable_first_) {
D3D12_DESCRIPTOR_HEAP_DESC new_heap_desc;
new_heap_desc.Type = type_;
new_heap_desc.NumDescriptors = page_size_;
new_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
new_heap_desc.NodeMask = 0;
ID3D12DescriptorHeap* new_heap;
if (FAILED(device_->CreateDescriptorHeap(&new_heap_desc,
IID_PPV_ARGS(&new_heap)))) {
XELOGE("Failed to create a heap for %u shader-visible descriptors",
page_size_);
page_creation_failed_ = true;
return 0;
return kHeapIndexInvalid;
}
unsent_ = new DescriptorHeap;
unsent_->heap = heap;
unsent_->next = nullptr;
}
// If starting a new page, get the handles to the beginning of it.
if (current_size_ == 0) {
current_heap_cpu_start_ =
unsent_->heap->GetCPUDescriptorHandleForHeapStart();
current_heap_gpu_start_ =
unsent_->heap->GetGPUDescriptorHandleForHeapStart();
}
index_out = current_size_;
current_size_ += count;
return current_page_;
}
void DescriptorHeapPool::EndPage() {
if (current_size_ != 0) {
auto page = unsent_;
page->frame_sent = context_->GetCurrentFrame();
unsent_ = page->next;
page->next = nullptr;
if (sent_last_ != nullptr) {
sent_last_->next = page;
} else {
sent_first_ = page;
}
sent_last_ = page;
current_size_ = 0;
writable_first_ = new Page;
writable_first_->heap = new_heap;
writable_first_->cpu_start = new_heap->GetCPUDescriptorHandleForHeapStart();
writable_first_->gpu_start = new_heap->GetGPUDescriptorHandleForHeapStart();
writable_first_->last_usage_fence_value = usage_fence_value;
writable_first_->next = nullptr;
writable_last_ = writable_first_;
}
writable_first_->last_usage_fence_value = usage_fence_value;
index_out = current_page_used_;
current_page_used_ += count;
return current_heap_index_;
}
} // namespace d3d12

View File

@@ -18,64 +18,57 @@ namespace xe {
namespace ui {
namespace d3d12 {
class D3D12Context;
class UploadBufferPool {
public:
UploadBufferPool(D3D12Context* context, uint32_t page_size);
UploadBufferPool(ID3D12Device* device, uint32_t page_size);
~UploadBufferPool();
void BeginFrame();
void EndFrame();
void Reclaim(uint64_t completed_fence_value);
void ClearCache();
// Request to write data in a single piece, creating a new page if the current
// one doesn't have enough free space.
uint8_t* RequestFull(uint32_t size, ID3D12Resource** buffer_out,
uint32_t* offset_out,
D3D12_GPU_VIRTUAL_ADDRESS* gpu_address_out);
uint8_t* Request(uint64_t usage_fence_value, uint32_t size,
ID3D12Resource** buffer_out, uint32_t* offset_out,
D3D12_GPU_VIRTUAL_ADDRESS* gpu_address_out);
// Request to write data in multiple parts, filling the buffer entirely.
uint8_t* RequestPartial(uint32_t size, ID3D12Resource** buffer_out,
uint32_t* offset_out, uint32_t* size_out,
uint8_t* RequestPartial(uint64_t usage_fence_value, uint32_t size,
ID3D12Resource** buffer_out, uint32_t* offset_out,
uint32_t* size_out,
D3D12_GPU_VIRTUAL_ADDRESS* gpu_address_out);
private:
D3D12Context* context_;
ID3D12Device* device_;
uint32_t page_size_;
void EndPage();
bool BeginNextPage();
struct UploadBuffer {
struct Page {
ID3D12Resource* buffer;
UploadBuffer* next;
uint64_t frame_sent;
D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
void* mapping;
uint64_t last_usage_fence_value;
Page* next;
};
// A list of unsent buffers, with the first one being the current.
UploadBuffer* unsent_ = nullptr;
// A list of sent buffers, moved to unsent in the beginning of a frame.
UploadBuffer* sent_first_ = nullptr;
UploadBuffer* sent_last_ = nullptr;
uint32_t current_size_ = 0;
uint8_t* current_mapping_ = nullptr;
// Not updated until actually requested.
D3D12_GPU_VIRTUAL_ADDRESS current_gpu_address_ = 0;
// Reset in the beginning of a frame - don't try and fail to create a new page
// if failed to create one in the current frame.
bool page_creation_failed_ = false;
// A list of buffers with free space, with the first buffer being the one
// currently being filled.
Page* writable_first_ = nullptr;
Page* writable_last_ = nullptr;
// A list of full buffers that can be reclaimed when the GPU doesn't use them
// anymore.
Page* submitted_first_ = nullptr;
Page* submitted_last_ = nullptr;
uint32_t current_page_used_ = 0;
};
class DescriptorHeapPool {
public:
DescriptorHeapPool(D3D12Context* context, D3D12_DESCRIPTOR_HEAP_TYPE type,
static constexpr uint64_t kHeapIndexInvalid = UINT64_MAX;
DescriptorHeapPool(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type,
uint32_t page_size);
~DescriptorHeapPool();
void BeginFrame();
void EndFrame();
void Reclaim(uint64_t completed_fence_value);
void ClearCache();
// Because all descriptors for a single draw call must be in the same heap,
@@ -88,64 +81,64 @@ class DescriptorHeapPool {
//
// If something uses this pool to do partial updates, it must let this
// function determine whether a partial update is possible. For this purpose,
// this function returns a full update number - and it must be called with its
// this function returns the heap reset index - and it must be called with its
// previous return value for the set of descriptors it's updating.
//
// If this function returns a value that is the same as previous_full_update,
// a partial update needs to be done - and space for count_for_partial_update
// is allocated.
// If this function returns a value that is the same as previous_heap_index, a
// partial update needs to be done - and space for count_for_partial_update is
// allocated.
//
// If it's different, all descriptors must be written again - and space for
// count_for_full_update is allocated.
//
// If 0 is returned, there was an error.
// If kHeapIndexInvalid is returned, there was an error.
//
// This MUST be called even if there's nothing to write in a partial update
// (with count_for_partial_update being 0), because a full update may still be
// required.
uint64_t Request(uint64_t previous_full_update,
uint64_t Request(uint64_t usage_fence_value, uint64_t previous_heap_index,
uint32_t count_for_partial_update,
uint32_t count_for_full_update, uint32_t& index_out);
// The current heap, for binding and actually writing - may be called only
// after a successful request because before a request, the heap may not exist
// yet.
ID3D12DescriptorHeap* GetLastRequestHeap() const { return unsent_->heap; }
ID3D12DescriptorHeap* GetLastRequestHeap() const {
return writable_first_->heap;
}
D3D12_CPU_DESCRIPTOR_HANDLE GetLastRequestHeapCPUStart() const {
return current_heap_cpu_start_;
return writable_first_->cpu_start;
}
D3D12_GPU_DESCRIPTOR_HANDLE GetLastRequestHeapGPUStart() const {
return current_heap_gpu_start_;
return writable_first_->gpu_start;
}
private:
D3D12Context* context_;
ID3D12Device* device_;
D3D12_DESCRIPTOR_HEAP_TYPE type_;
uint32_t page_size_;
void EndPage();
bool BeginNextPage();
struct DescriptorHeap {
struct Page {
ID3D12DescriptorHeap* heap;
DescriptorHeap* next;
uint64_t frame_sent;
D3D12_CPU_DESCRIPTOR_HANDLE cpu_start;
D3D12_GPU_DESCRIPTOR_HANDLE gpu_start;
uint64_t last_usage_fence_value;
Page* next;
};
// A list of unsent heaps, with the first one being the current.
DescriptorHeap* unsent_ = nullptr;
// A list of sent heaps, moved to unsent in the beginning of a frame.
DescriptorHeap* sent_first_ = nullptr;
DescriptorHeap* sent_last_ = nullptr;
uint64_t current_page_ = 0;
D3D12_CPU_DESCRIPTOR_HANDLE current_heap_cpu_start_ = {};
D3D12_GPU_DESCRIPTOR_HANDLE current_heap_gpu_start_ = {};
uint32_t current_size_ = 0;
// Reset in the beginning of a frame - don't try and fail to create a new page
// if failed to create one in the current frame.
bool page_creation_failed_ = false;
// A list of heap with free space, with the first buffer being the one
// currently being filled.
Page* writable_first_ = nullptr;
Page* writable_last_ = nullptr;
// A list of full heaps that can be reclaimed when the GPU doesn't use them
// anymore.
Page* submitted_first_ = nullptr;
Page* submitted_last_ = nullptr;
// Monotonically increased when a new request is going to a different
// ID3D12DescriptorHeap than the one that may be bound currently. See Request
// for more information.
uint64_t current_heap_index_ = 0;
uint32_t current_page_used_ = 0;
};
} // namespace d3d12