diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc index dbea30936..19981eb1d 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc @@ -29,27 +29,34 @@ namespace d3d12 { #include "xenia/ui/shaders/bytecode/d3d12_5_1/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(); } - handle = reinterpret_cast(this); } 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 +240,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 +257,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 +274,8 @@ void D3D12ImmediateDrawer::Shutdown() { } std::unique_ptr 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 +364,22 @@ std::unique_ptr 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 texture = - std::make_unique(width, height, resource, filter, - repeat); + std::make_unique( + 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 +397,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 +542,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_. - auto texture = reinterpret_cast(draw.texture_handle); + uint64_t current_fence_value = context_.GetSwapCurrentFenceValue(); + auto texture = static_cast(draw.texture); 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 +599,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 +653,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()) { diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h index c811c0ae6..4300af76e 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h @@ -12,6 +12,7 @@ #include #include +#include #include #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 vertex_buffer_pool_; std::unique_ptr texture_descriptor_pool_; + // Only with non-null resources. + std::vector textures_; + struct PendingTextureUpload { ID3D12Resource* texture; ID3D12Resource* buffer; @@ -108,6 +131,8 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { }; std::deque texture_uploads_submitted_; + std::vector> textures_deleted_; + ID3D12GraphicsCommandList* current_command_list_ = nullptr; int current_render_target_width_, current_render_target_height_; bool batch_open_ = false; diff --git a/src/xenia/ui/imgui_drawer.cc b/src/xenia/ui/imgui_drawer.cc index cc58994e0..158c9248a 100644 --- a/src/xenia/ui/imgui_drawer.cc +++ b/src/xenia/ui/imgui_drawer.cc @@ -163,7 +163,7 @@ void ImGuiDrawer::SetupFont() { width, height, ImmediateTextureFilter::kLinear, true, reinterpret_cast(pixels)); - io.Fonts->TexID = reinterpret_cast(font_texture_->handle); + io.Fonts->TexID = reinterpret_cast(font_texture_.get()); } void ImGuiDrawer::RenderDrawLists(ImDrawData* data) { @@ -198,11 +198,7 @@ void ImGuiDrawer::RenderDrawLists(ImDrawData* data) { draw.primitive_type = ImmediatePrimitiveType::kTriangles; draw.count = cmd.ElemCount; draw.index_offset = index_offset; - draw.texture_handle = - reinterpret_cast(cmd.TextureId) & ~kIgnoreAlpha; - draw.alpha_blend = - reinterpret_cast(cmd.TextureId) & kIgnoreAlpha ? false - : true; + draw.texture = reinterpret_cast(cmd.TextureId); draw.scissor = true; draw.scissor_rect[0] = static_cast(cmd.ClipRect.x); draw.scissor_rect[1] = static_cast(height - cmd.ClipRect.w); diff --git a/src/xenia/ui/imgui_drawer.h b/src/xenia/ui/imgui_drawer.h index e2038f1bf..7163e7a1a 100644 --- a/src/xenia/ui/imgui_drawer.h +++ b/src/xenia/ui/imgui_drawer.h @@ -36,8 +36,6 @@ class ImGuiDrawer : public WindowListener { ImGuiIO& GetIO(); void RenderDrawLists(); - static const uint64_t kIgnoreAlpha = (1ull << 63); - protected: void Initialize(); void SetupFont(); diff --git a/src/xenia/ui/immediate_drawer.h b/src/xenia/ui/immediate_drawer.h index 65606c134..544f16aa1 100644 --- a/src/xenia/ui/immediate_drawer.h +++ b/src/xenia/ui/immediate_drawer.h @@ -32,12 +32,10 @@ class ImmediateTexture { uint32_t width; // Texture height, in pixels. uint32_t height; - // Internal handle. Can be passed with the ImmediateDrawBatch. - uintptr_t handle; protected: ImmediateTexture(uint32_t width, uint32_t height) - : width(width), height(height), handle(0ULL) {} + : width(width), height(height) {} }; // Describes the primitive type used by a draw call. @@ -78,16 +76,12 @@ struct ImmediateDraw { int base_vertex = 0; // Texture used when drawing, or nullptr if color only. - // This is most commonly the handle of an ImmediateTexture. - uintptr_t texture_handle = 0; + ImmediateTexture* texture = nullptr; // True to enable scissoring using the region defined by scissor_rect. bool scissor = false; // Scissoring region in framebuffer pixels as (x, y, w, h). int scissor_rect[4] = {0}; - - // Blends this draw with the background depending on its alpha (if true). - bool alpha_blend = true; }; class ImmediateDrawer { @@ -97,7 +91,7 @@ class ImmediateDrawer { // Creates a new texture with the given attributes and R8G8B8A8 data. virtual std::unique_ptr CreateTexture( uint32_t width, uint32_t height, ImmediateTextureFilter filter, - bool repeat, const uint8_t* data) = 0; + bool is_repeated, const uint8_t* data) = 0; // Begins drawing in immediate mode using the given projection matrix. virtual void Begin(int render_target_width, int render_target_height) = 0; diff --git a/src/xenia/ui/microprofile_drawer.cc b/src/xenia/ui/microprofile_drawer.cc index de97e4a03..fb6588348 100644 --- a/src/xenia/ui/microprofile_drawer.cc +++ b/src/xenia/ui/microprofile_drawer.cc @@ -216,7 +216,7 @@ void MicroprofileDrawer::Flush() { ImmediateDraw draw; draw.primitive_type = current_primitive_type_; draw.count = vertex_count_; - draw.texture_handle = font_texture_->handle; + draw.texture = font_texture_.get(); drawer->Draw(draw); drawer->EndDrawBatch(); diff --git a/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc b/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc index bd8ac449f..703930de4 100644 --- a/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc +++ b/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc @@ -364,7 +364,7 @@ void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) { // Bind the texture. uint32_t texture_descriptor_index; VulkanImmediateTexture* texture = - reinterpret_cast(draw.texture_handle); + static_cast(draw.texture); if (texture && texture->immediate_drawer_ == this) { texture_descriptor_index = texture->resource_.descriptor_index; texture->last_usage_submission_ = context_.swap_submission_current(); diff --git a/src/xenia/ui/vulkan/vulkan_immediate_drawer.h b/src/xenia/ui/vulkan/vulkan_immediate_drawer.h index e68bbc554..85fbe8731 100644 --- a/src/xenia/ui/vulkan/vulkan_immediate_drawer.h +++ b/src/xenia/ui/vulkan/vulkan_immediate_drawer.h @@ -36,7 +36,7 @@ class VulkanImmediateDrawer : public ImmediateDrawer { std::unique_ptr CreateTexture(uint32_t width, uint32_t height, ImmediateTextureFilter filter, - bool repeat, + bool is_repeated, const uint8_t* data) override; void Begin(int render_target_width, int render_target_height) override; @@ -62,9 +62,7 @@ class VulkanImmediateDrawer : public ImmediateDrawer { }; VulkanImmediateTexture(uint32_t width, uint32_t height) - : ImmediateTexture(width, height), immediate_drawer_(nullptr) { - handle = reinterpret_cast(this); - } + : ImmediateTexture(width, height), immediate_drawer_(nullptr) {} ~VulkanImmediateTexture() override; // If null, this is either a blank texture, or the immediate drawer has been