diff --git a/src/xenia/ui/d3d12/pools.cc b/src/xenia/ui/d3d12/pools.cc index 9805e5b6f..de938b055 100644 --- a/src/xenia/ui/d3d12/pools.cc +++ b/src/xenia/ui/d3d12/pools.cc @@ -40,7 +40,7 @@ void UploadBufferPool::BeginFrame() { } // Try to create new pages again in this frame if failed in the previous. - creation_failed_ = false; + page_creation_failed_ = false; } void UploadBufferPool::EndFrame() { @@ -117,16 +117,16 @@ void UploadBufferPool::EndPage() { current_mapping_ = nullptr; } if (current_size_ != 0) { - auto buffer = unsent_; - buffer->frame_sent = context_->GetCurrentFrame(); - unsent_ = buffer->next; - buffer->next = nullptr; + auto page = unsent_; + page->frame_sent = context_->GetCurrentFrame(); + unsent_ = page->next; + page->next = nullptr; if (sent_last_ != nullptr) { - sent_last_->next = buffer; + sent_last_->next = page; } else { - sent_first_ = buffer; + sent_first_ = page; } - sent_last_ = buffer; + sent_last_ = page; current_size_ = 0; } } @@ -134,6 +134,10 @@ void UploadBufferPool::EndPage() { bool UploadBufferPool::BeginNextPage() { EndPage(); + if (page_creation_failed_) { + return false; + } + if (unsent_ == nullptr) { auto device = context_->GetD3D12Provider()->GetDevice(); D3D12_HEAP_PROPERTIES heap_properties = {}; @@ -156,7 +160,7 @@ bool UploadBufferPool::BeginNextPage() { D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&buffer_resource)))) { XELOGE("Failed to create a D3D upload buffer with %u bytes", page_size_); - creation_failed_ = true; + page_creation_failed_ = true; return false; } unsent_ = new UploadBuffer; @@ -170,7 +174,7 @@ bool UploadBufferPool::BeginNextPage() { void* mapping; if (FAILED(unsent_->buffer->Map(0, &read_range, &mapping))) { XELOGE("Failed to map a D3D upload buffer with %u bytes", page_size_); - creation_failed_ = true; + page_creation_failed_ = true; return false; } current_mapping_ = reinterpret_cast(mapping); @@ -178,6 +182,132 @@ bool UploadBufferPool::BeginNextPage() { return true; } +DescriptorHeapPool::DescriptorHeapPool(D3D12Context* context, + D3D12_DESCRIPTOR_HEAP_TYPE type, + uint32_t page_size) + : context_(context), type_(type), page_size_(page_size) {} + +DescriptorHeapPool::~DescriptorHeapPool() { 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) { + break; + } + sent_first_ = page->next; + page->next = unsent_; + unsent_ = page; + } + if (sent_first_ == nullptr) { + sent_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(current_size_ == 0); + while (unsent_ != nullptr) { + auto next = unsent_->next; + unsent_->heap->Release(); + delete unsent_; + unsent_ = next; + } + while (sent_first_ != nullptr) { + auto next = sent_first_->next; + sent_first_->heap->Release(); + delete sent_first_; + sent_first_ = next; + } + sent_last_ = nullptr; +} + +uint64_t DescriptorHeapPool::GetPageForRequest(uint32_t count) const { + uint64_t page = current_page_; + if (page_size_ - current_size_ < count) { + ++page; + } + return page; +} + +bool DescriptorHeapPool::Request(uint32_t count, uint32_t& index_out) { + assert_true(count != 0 && count <= page_size_); + if (count == 0 || count > page_size_) { + return false; + } + + if (page_creation_failed_) { + // Don't increment the page index every call if there was a failure as well. + return false; + } + + // Go to the next page if there's not enough free space on the current one. + if (page_size_ - current_size_ < count) { + EndPage(); + ++current_page_; + } + + // Create the page if needed (may be the first call for the page). + if (unsent_ == nullptr) { + if (page_creation_failed_) { + return false; + } + 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)))) { + XELOGE("Failed to create a heap for %u shader-visible descriptors", + page_size_); + page_creation_failed_ = true; + return false; + } + 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 true; +} + +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; + } +} + } // namespace d3d12 } // namespace ui } // namespace xe diff --git a/src/xenia/ui/d3d12/pools.h b/src/xenia/ui/d3d12/pools.h index 9cfa6196b..51d552e80 100644 --- a/src/xenia/ui/d3d12/pools.h +++ b/src/xenia/ui/d3d12/pools.h @@ -56,9 +56,69 @@ class UploadBufferPool { uint32_t current_size_ = 0; uint8_t* current_mapping_ = nullptr; - // Reset in the beginning of a frame - don't try and fail to create a new - // buffer if failed to create one in the current frame. - bool creation_failed_ = false; + // 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; +}; + +class DescriptorHeapPool { + public: + DescriptorHeapPool(D3D12Context* context, D3D12_DESCRIPTOR_HEAP_TYPE type, + uint32_t page_size); + ~DescriptorHeapPool(); + + void BeginFrame(); + void EndFrame(); + void ClearCache(); + + // To check if a rebind will be required, and thus may possibly need to write + // all the descriptors needed for a draw call rather than only the modified + // ones. The page number can never be 0 if a frame has started, and it's + // changed every frame, so it's safe to use 0 to indicate that the descriptors + // for some data have never been written. + uint64_t GetPageForRequest(uint32_t count) const; + bool Request(uint32_t count, 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; } + D3D12_CPU_DESCRIPTOR_HANDLE GetLastRequestHeapCPUStart() const { + return current_heap_cpu_start_; + } + D3D12_GPU_DESCRIPTOR_HANDLE GetLastRequestHeapGPUStart() const { + return current_heap_gpu_start_; + } + uint64_t GetLastRequestPageNumber() const { return current_page_; } + + private: + D3D12Context* context_; + D3D12_DESCRIPTOR_HEAP_TYPE type_; + uint32_t page_size_; + + void EndPage(); + bool BeginNextPage(); + + struct DescriptorHeap { + ID3D12DescriptorHeap* heap; + DescriptorHeap* next; + uint64_t frame_sent; + }; + + // 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; }; } // namespace d3d12