[D3D12] Delayed ImmediateTexture releasing

This commit is contained in:
Triang3l 2020-09-27 16:21:14 +03:00
parent d437555036
commit f5f8714c3f
2 changed files with 126 additions and 44 deletions

View File

@ -29,12 +29,14 @@ namespace d3d12 {
#include "xenia/ui/d3d12/shaders/dxbc/immediate_vs.h"
D3D12ImmediateDrawer::D3D12ImmediateTexture::D3D12ImmediateTexture(
uint32_t width, uint32_t height, ID3D12Resource* resource_if_exists,
ImmediateTextureFilter filter, bool is_repeated)
uint32_t width, uint32_t height, ID3D12Resource* resource,
SamplerIndex sampler_index, D3D12ImmediateDrawer* immediate_drawer,
size_t immediate_drawer_index)
: ImmediateTexture(width, height),
resource_(resource_if_exists),
filter_(filter),
is_repeated_(is_repeated) {
resource_(resource),
sampler_index_(sampler_index),
immediate_drawer_(immediate_drawer),
immediate_drawer_index_(immediate_drawer_index) {
if (resource_) {
resource_->AddRef();
}
@ -42,14 +44,20 @@ D3D12ImmediateDrawer::D3D12ImmediateTexture::D3D12ImmediateTexture(
}
D3D12ImmediateDrawer::D3D12ImmediateTexture::~D3D12ImmediateTexture() {
if (immediate_drawer_) {
immediate_drawer_->OnImmediateTextureDestroyed(*this);
}
if (resource_) {
resource_->Release();
// TODO(Triang3l): Track last usage submission because it turns out that
// deletion in the ImGui and the profiler actually happens before after
// awaiting submission completion.
}
}
void D3D12ImmediateDrawer::D3D12ImmediateTexture::OnImmediateDrawerShutdown() {
immediate_drawer_ = nullptr;
// Lifetime is not managed anymore, so don't keep the resource either.
util::ReleaseAndNull(resource_);
}
D3D12ImmediateDrawer::D3D12ImmediateDrawer(D3D12Context& graphics_context)
: ImmediateDrawer(&graphics_context), context_(graphics_context) {}
@ -233,6 +241,11 @@ bool D3D12ImmediateDrawer::Initialize() {
}
void D3D12ImmediateDrawer::Shutdown() {
for (auto& deleted_texture : textures_deleted_) {
deleted_texture.first->Release();
}
textures_deleted_.clear();
for (auto& texture_upload : texture_uploads_submitted_) {
texture_upload.buffer->Release();
texture_upload.texture->Release();
@ -245,6 +258,11 @@ void D3D12ImmediateDrawer::Shutdown() {
}
texture_uploads_pending_.clear();
for (D3D12ImmediateTexture* texture : textures_) {
texture->OnImmediateDrawerShutdown();
}
textures_.clear();
texture_descriptor_pool_.reset();
vertex_buffer_pool_.reset();
@ -257,8 +275,8 @@ void D3D12ImmediateDrawer::Shutdown() {
}
std::unique_ptr<ImmediateTexture> D3D12ImmediateDrawer::CreateTexture(
uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat,
const uint8_t* data) {
uint32_t width, uint32_t height, ImmediateTextureFilter filter,
bool is_repeated, const uint8_t* data) {
const D3D12Provider& provider = context_.GetD3D12Provider();
ID3D12Device* device = provider.GetDevice();
D3D12_HEAP_FLAGS heap_flag_create_not_zeroed =
@ -347,10 +365,22 @@ std::unique_ptr<ImmediateTexture> D3D12ImmediateDrawer::CreateTexture(
resource = nullptr;
}
SamplerIndex sampler_index;
if (filter == ImmediateTextureFilter::kLinear) {
sampler_index =
is_repeated ? SamplerIndex::kLinearRepeat : SamplerIndex::kLinearClamp;
} else {
sampler_index = is_repeated ? SamplerIndex::kNearestRepeat
: SamplerIndex::kNearestClamp;
}
// Manage by this immediate drawer if successfully created a resource.
std::unique_ptr<D3D12ImmediateTexture> texture =
std::make_unique<D3D12ImmediateTexture>(width, height, resource, filter,
repeat);
std::make_unique<D3D12ImmediateTexture>(
width, height, resource, sampler_index, resource ? this : nullptr,
textures_.size());
if (resource) {
textures_.push_back(texture.get());
// D3D12ImmediateTexture now holds a reference.
resource->Release();
}
@ -368,7 +398,19 @@ void D3D12ImmediateDrawer::Begin(int render_target_width,
current_command_list_ = context_.GetSwapCommandList();
uint64_t completed_fence_value = context_.GetSwapCompletedFenceValue();
uint64_t current_fence_value = context_.GetSwapCurrentFenceValue();
// Release deleted textures.
for (auto it = textures_deleted_.begin(); it != textures_deleted_.end();) {
if (it->second > completed_fence_value) {
++it;
continue;
}
it->first->Release();
if (std::next(it) != textures_deleted_.end()) {
*it = textures_deleted_.back();
}
textures_deleted_.pop_back();
}
// Release upload buffers for completed texture uploads.
auto erase_uploads_end = texture_uploads_submitted_.begin();
@ -501,17 +543,20 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
// Bind the texture. If this is the first draw in a frame, the descriptor heap
// index will be invalid initially, and the texture will be bound regardless
// of what's in current_texture_.
uint64_t current_fence_value = context_.GetSwapCurrentFenceValue();
auto texture = reinterpret_cast<D3D12ImmediateTexture*>(draw.texture_handle);
ID3D12Resource* texture_resource = texture ? texture->resource() : nullptr;
bool bind_texture = current_texture_ != texture_resource;
uint32_t texture_descriptor_index;
uint64_t texture_heap_index = texture_descriptor_pool_->Request(
context_.GetSwapCurrentFenceValue(),
current_texture_descriptor_heap_index_, bind_texture ? 1 : 0, 1,
texture_descriptor_index);
current_fence_value, current_texture_descriptor_heap_index_,
bind_texture ? 1 : 0, 1, texture_descriptor_index);
if (texture_heap_index == D3D12DescriptorHeapPool::kHeapIndexInvalid) {
return;
}
if (texture_resource) {
texture->SetLastUsageFenceValue(current_fence_value);
}
if (current_texture_descriptor_heap_index_ != texture_heap_index) {
current_texture_descriptor_heap_index_ = texture_heap_index;
bind_texture = true;
@ -555,19 +600,10 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {
texture_descriptor_index));
}
// Bind the sampler.
SamplerIndex sampler_index;
if (texture != nullptr) {
if (texture->filter() == ImmediateTextureFilter::kLinear) {
sampler_index = texture->is_repeated() ? SamplerIndex::kLinearRepeat
: SamplerIndex::kLinearClamp;
} else {
sampler_index = texture->is_repeated() ? SamplerIndex::kNearestRepeat
: SamplerIndex::kNearestClamp;
}
} else {
sampler_index = SamplerIndex::kNearestClamp;
}
// Bind the sampler. If the resource doesn't exist (solid color drawing), use
// nearest-neighbor and clamp so fetching is simpler.
SamplerIndex sampler_index =
texture_resource ? texture->sampler_index() : SamplerIndex::kNearestClamp;
if (current_sampler_index_ != sampler_index) {
current_sampler_index_ = sampler_index;
current_command_list_->SetGraphicsRootDescriptorTable(
@ -618,6 +654,27 @@ void D3D12ImmediateDrawer::End() {
}
}
void D3D12ImmediateDrawer::OnImmediateTextureDestroyed(
D3D12ImmediateTexture& texture) {
// Remove from the texture list.
size_t texture_index = texture.immediate_drawer_index();
assert_true(texture_index != SIZE_MAX);
D3D12ImmediateTexture*& texture_at_index = textures_[texture_index];
texture_at_index = textures_.back();
texture_at_index->SetImmediateDrawerIndex(texture_index);
textures_.pop_back();
// Queue for delayed release.
ID3D12Resource* resource = texture.resource();
uint64_t last_usage_fence_value = texture.last_usage_fence_value();
if (resource &&
last_usage_fence_value > context_.GetSwapCompletedFenceValue()) {
resource->AddRef();
textures_deleted_.push_back(
std::make_pair(resource, last_usage_fence_value));
}
}
void D3D12ImmediateDrawer::UploadTextures() {
assert_not_null(current_command_list_);
if (texture_uploads_pending_.empty()) {

View File

@ -12,6 +12,7 @@
#include <deque>
#include <memory>
#include <utility>
#include <vector>
#include "xenia/ui/d3d12/d3d12_api.h"
@ -46,23 +47,51 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
void End() override;
private:
enum class SamplerIndex {
kNearestClamp,
kLinearClamp,
kNearestRepeat,
kLinearRepeat,
kCount,
kInvalid = kCount
};
class D3D12ImmediateTexture : public ImmediateTexture {
public:
static constexpr DXGI_FORMAT kFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
D3D12ImmediateTexture(uint32_t width, uint32_t height,
ID3D12Resource* resource_if_exists,
ImmediateTextureFilter filter, bool is_repeated);
ID3D12Resource* resource, SamplerIndex sampler_index,
D3D12ImmediateDrawer* immediate_drawer,
size_t immediate_drawer_index);
~D3D12ImmediateTexture() override;
ID3D12Resource* resource() const { return resource_; }
ImmediateTextureFilter filter() const { return filter_; }
bool is_repeated() const { return is_repeated_; }
SamplerIndex sampler_index() const { return sampler_index_; }
size_t immediate_drawer_index() const { return immediate_drawer_index_; }
void SetImmediateDrawerIndex(size_t index) {
immediate_drawer_index_ = index;
}
void OnImmediateDrawerShutdown();
uint64_t last_usage_fence_value() const { return last_usage_fence_value_; }
void SetLastUsageFenceValue(uint64_t fence_value) {
last_usage_fence_value_ = fence_value;
}
private:
ID3D12Resource* resource_;
ImmediateTextureFilter filter_;
bool is_repeated_;
SamplerIndex sampler_index_;
D3D12ImmediateDrawer* immediate_drawer_;
size_t immediate_drawer_index_;
uint64_t last_usage_fence_value_ = 0;
};
void OnImmediateTextureDestroyed(D3D12ImmediateTexture& texture);
void UploadTextures();
D3D12Context& context_;
@ -79,15 +108,6 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
ID3D12PipelineState* pipeline_state_triangle_ = nullptr;
ID3D12PipelineState* pipeline_state_line_ = nullptr;
enum class SamplerIndex {
kNearestClamp,
kLinearClamp,
kNearestRepeat,
kLinearRepeat,
kCount,
kInvalid = kCount
};
ID3D12DescriptorHeap* sampler_heap_ = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE sampler_heap_cpu_start_;
D3D12_GPU_DESCRIPTOR_HANDLE sampler_heap_gpu_start_;
@ -95,6 +115,9 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
std::unique_ptr<D3D12UploadBufferPool> vertex_buffer_pool_;
std::unique_ptr<D3D12DescriptorHeapPool> texture_descriptor_pool_;
// Only with non-null resources.
std::vector<D3D12ImmediateTexture*> textures_;
struct PendingTextureUpload {
ID3D12Resource* texture;
ID3D12Resource* buffer;
@ -108,6 +131,8 @@ class D3D12ImmediateDrawer : public ImmediateDrawer {
};
std::deque<SubmittedTextureUpload> texture_uploads_submitted_;
std::vector<std::pair<ID3D12Resource*, uint64_t>> textures_deleted_;
ID3D12GraphicsCommandList* current_command_list_ = nullptr;
int current_render_target_width_, current_render_target_height_;
bool batch_open_ = false;