From 131525e44d8e67b462d4f2e80bb0185fbe24b72d Mon Sep 17 00:00:00 2001 From: Triang3l Date: Thu, 4 Oct 2018 14:36:48 +0300 Subject: [PATCH] [D3D12] Don't create SRVs if textures not changed --- .../gpu/d3d12/d3d12_command_processor.cc | 98 +++++++++++-------- src/xenia/gpu/d3d12/d3d12_command_processor.h | 8 +- src/xenia/gpu/d3d12/texture_cache.cc | 37 ++++++- src/xenia/gpu/d3d12/texture_cache.h | 9 +- 4 files changed, 108 insertions(+), 44 deletions(-) diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.cc b/src/xenia/gpu/d3d12/d3d12_command_processor.cc index c8a8edf30..e7fd98c8a 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.cc +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.cc @@ -1166,9 +1166,11 @@ bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type, } // Update the textures - this may bind pipelines. + uint64_t new_texture_bindings_hash_vertex, new_texture_bindings_hash_pixel; texture_cache_->RequestTextures( vertex_shader->GetUsedTextureMask(), - pixel_shader != nullptr ? pixel_shader->GetUsedTextureMask() : 0); + pixel_shader != nullptr ? pixel_shader->GetUsedTextureMask() : 0, + new_texture_bindings_hash_vertex, new_texture_bindings_hash_pixel); // Update viewport, scissor, blend factor and stencil reference. UpdateFixedFunctionState(command_list); @@ -1185,8 +1187,9 @@ bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type, pipeline_render_targets); // Update constant buffers, descriptors and root parameters. - if (!UpdateBindings(command_list, vertex_shader, pixel_shader, - root_signature)) { + if (!UpdateBindings(command_list, vertex_shader, pixel_shader, root_signature, + new_texture_bindings_hash_vertex, + new_texture_bindings_hash_pixel)) { return false; } @@ -1335,6 +1338,8 @@ bool D3D12CommandProcessor::BeginFrame() { cbuffer_bindings_fetch_.up_to_date = false; draw_view_full_update_ = 0; draw_sampler_full_update_ = 0; + texture_bindings_hash_vertex_ = texture_bindings_hash_pixel_ = + texture_cache_->GetEmptyTextureBindingsHash(); primitive_topology_ = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; command_lists_[current_queue_frame_]->BeginRecording(); @@ -1797,7 +1802,9 @@ void D3D12CommandProcessor::UpdateSystemConstantValues( bool D3D12CommandProcessor::UpdateBindings( ID3D12GraphicsCommandList* command_list, const D3D12Shader* vertex_shader, - const D3D12Shader* pixel_shader, ID3D12RootSignature* root_signature) { + const D3D12Shader* pixel_shader, ID3D12RootSignature* root_signature, + uint64_t new_texture_bindings_hash_vertex, + uint64_t new_texture_bindings_hash_pixel) { auto provider = GetD3D12Context()->GetD3D12Provider(); auto device = provider->GetDevice(); auto& regs = *register_file_; @@ -1834,7 +1841,6 @@ bool D3D12CommandProcessor::UpdateBindings( vertex_shader->GetTextureSRVs(vertex_texture_count); const D3D12Shader::SamplerBinding* vertex_samplers = vertex_shader->GetSamplerBindings(vertex_sampler_count); - uint32_t texture_count = pixel_texture_count + vertex_texture_count; uint32_t sampler_count = pixel_sampler_count + vertex_sampler_count; // Begin updating descriptors. @@ -1843,9 +1849,14 @@ bool D3D12CommandProcessor::UpdateBindings( bool write_pixel_float_constant_view = false; bool write_bool_loop_constant_view = false; bool write_fetch_constant_view = false; - // TODO(Triang3l): Update textures and samplers only if shaders or binding + // TODO(Triang3l): Update samplers only if shaders or binding // hash change. - bool write_textures = texture_count != 0; + bool write_vertex_textures = + vertex_texture_count != 0 && + (texture_bindings_hash_vertex_ != new_texture_bindings_hash_vertex); + bool write_pixel_textures = + pixel_texture_count != 0 && + (texture_bindings_hash_pixel_ != new_texture_bindings_hash_pixel); bool write_samplers = sampler_count != 0; // Check if the float constant layout is still the same and get the counts. @@ -2016,11 +2027,15 @@ bool D3D12CommandProcessor::UpdateBindings( if (write_fetch_constant_view) { ++view_count_partial_update; } - if (write_textures) { - view_count_partial_update += texture_count; + if (write_vertex_textures) { + view_count_partial_update += vertex_texture_count; + } + if (write_pixel_textures) { + view_count_partial_update += pixel_texture_count; } // All the constants + shared memory + textures. - uint32_t view_count_full_update = 6 + texture_count; + uint32_t view_count_full_update = + 6 + vertex_texture_count + pixel_texture_count; D3D12_CPU_DESCRIPTOR_HANDLE view_cpu_handle; D3D12_GPU_DESCRIPTOR_HANDLE view_gpu_handle; uint32_t descriptor_size_view = provider->GetViewDescriptorSize(); @@ -2046,13 +2061,13 @@ bool D3D12CommandProcessor::UpdateBindings( } if (draw_view_full_update_ != view_full_update_index) { // Need to update all view descriptors. - draw_view_full_update_ = view_full_update_index; write_system_constant_view = true; write_fetch_constant_view = true; write_vertex_float_constant_view = true; write_pixel_float_constant_view = true; write_bool_loop_constant_view = true; - write_textures = texture_count != 0; + write_vertex_textures = vertex_texture_count != 0; + write_pixel_textures = pixel_texture_count != 0; // If updating fully, write the shared memory descriptor (t0). shared_memory_->CreateSRV(view_cpu_handle); gpu_handle_shared_memory_ = view_gpu_handle; @@ -2062,7 +2077,6 @@ bool D3D12CommandProcessor::UpdateBindings( } if (sampler_count != 0 && draw_sampler_full_update_ != sampler_full_update_index) { - draw_sampler_full_update_ = sampler_full_update_index; write_samplers = true; } @@ -2128,35 +2142,35 @@ bool D3D12CommandProcessor::UpdateBindings( view_gpu_handle.ptr += descriptor_size_view; current_graphics_root_up_to_date_ &= ~(1u << kRootParameter_FetchConstants); } - if (write_textures) { - if (pixel_texture_count != 0) { - assert_true(current_graphics_root_extras_.pixel_textures != - RootExtraParameterIndices::kUnavailable); - gpu_handle_pixel_textures_ = view_gpu_handle; - for (uint32_t i = 0; i < pixel_texture_count; ++i) { - const D3D12Shader::TextureSRV& srv = pixel_textures[i]; - texture_cache_->WriteTextureSRV(srv.fetch_constant, srv.dimension, - view_cpu_handle); - view_cpu_handle.ptr += descriptor_size_view; - view_gpu_handle.ptr += descriptor_size_view; - } - current_graphics_root_up_to_date_ &= - ~(1u << current_graphics_root_extras_.pixel_textures); + if (write_pixel_textures) { + assert_true(current_graphics_root_extras_.pixel_textures != + RootExtraParameterIndices::kUnavailable); + gpu_handle_pixel_textures_ = view_gpu_handle; + for (uint32_t i = 0; i < pixel_texture_count; ++i) { + const D3D12Shader::TextureSRV& srv = pixel_textures[i]; + texture_cache_->WriteTextureSRV(srv.fetch_constant, srv.dimension, + view_cpu_handle); + view_cpu_handle.ptr += descriptor_size_view; + view_gpu_handle.ptr += descriptor_size_view; } - if (vertex_texture_count != 0) { - assert_true(current_graphics_root_extras_.vertex_textures != - RootExtraParameterIndices::kUnavailable); - gpu_handle_vertex_textures_ = view_gpu_handle; - for (uint32_t i = 0; i < vertex_texture_count; ++i) { - const D3D12Shader::TextureSRV& srv = vertex_textures[i]; - texture_cache_->WriteTextureSRV(srv.fetch_constant, srv.dimension, - view_cpu_handle); - view_cpu_handle.ptr += descriptor_size_view; - view_gpu_handle.ptr += descriptor_size_view; - } - current_graphics_root_up_to_date_ &= - ~(1u << current_graphics_root_extras_.vertex_textures); + texture_bindings_hash_pixel_ = new_texture_bindings_hash_pixel; + current_graphics_root_up_to_date_ &= + ~(1u << current_graphics_root_extras_.pixel_textures); + } + if (write_vertex_textures) { + assert_true(current_graphics_root_extras_.vertex_textures != + RootExtraParameterIndices::kUnavailable); + gpu_handle_vertex_textures_ = view_gpu_handle; + for (uint32_t i = 0; i < vertex_texture_count; ++i) { + const D3D12Shader::TextureSRV& srv = vertex_textures[i]; + texture_cache_->WriteTextureSRV(srv.fetch_constant, srv.dimension, + view_cpu_handle); + view_cpu_handle.ptr += descriptor_size_view; + view_gpu_handle.ptr += descriptor_size_view; } + texture_bindings_hash_vertex_ = new_texture_bindings_hash_vertex; + current_graphics_root_up_to_date_ &= + ~(1u << current_graphics_root_extras_.vertex_textures); } if (write_samplers) { if (pixel_sampler_count != 0) { @@ -2191,6 +2205,10 @@ bool D3D12CommandProcessor::UpdateBindings( } } + // Wrote new descriptors on the current page. + draw_view_full_update_ = view_full_update_index; + draw_sampler_full_update_ = sampler_full_update_index; + // Update the root parameters. if (!(current_graphics_root_up_to_date_ & (1u << kRootParameter_FetchConstants))) { diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.h b/src/xenia/gpu/d3d12/d3d12_command_processor.h index 45b24e8e0..287b2ea83 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.h +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.h @@ -193,7 +193,9 @@ class D3D12CommandProcessor : public CommandProcessor { bool UpdateBindings(ID3D12GraphicsCommandList* command_list, const D3D12Shader* vertex_shader, const D3D12Shader* pixel_shader, - ID3D12RootSignature* root_signature); + ID3D12RootSignature* root_signature, + uint64_t new_texture_bindings_hash_vertex, + uint64_t new_texture_bindings_hash_pixel); bool cache_clear_requested_ = false; @@ -301,6 +303,10 @@ class D3D12CommandProcessor : public CommandProcessor { uint64_t draw_view_full_update_; uint64_t draw_sampler_full_update_; + // Hashes of the current texture descriptor layout. + uint64_t texture_bindings_hash_vertex_; + uint64_t texture_bindings_hash_pixel_; + // Latest descriptor handles used for handling Xenos draw calls. D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle_system_constants_; D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle_vertex_float_constants_; diff --git a/src/xenia/gpu/d3d12/texture_cache.cc b/src/xenia/gpu/d3d12/texture_cache.cc index 32284cd6d..ddcf51d82 100644 --- a/src/xenia/gpu/d3d12/texture_cache.cc +++ b/src/xenia/gpu/d3d12/texture_cache.cc @@ -9,6 +9,8 @@ #include "xenia/gpu/d3d12/texture_cache.h" +#include "third_party/xxhash/xxhash.h" + #include #include @@ -292,7 +294,11 @@ TextureCache::TextureCache(D3D12CommandProcessor* command_processor, SharedMemory* shared_memory) : command_processor_(command_processor), register_file_(register_file), - shared_memory_(shared_memory) {} + shared_memory_(shared_memory) { + XXH64_state_t hash_state; + XXH64_reset(&hash_state, 0); + texture_bindings_empty_hash_ = XXH64_digest(&hash_state); +} TextureCache::~TextureCache() { Shutdown(); } @@ -427,7 +433,9 @@ void TextureCache::EndFrame() { } void TextureCache::RequestTextures(uint32_t used_vertex_texture_mask, - uint32_t used_pixel_texture_mask) { + uint32_t used_pixel_texture_mask, + uint64_t& descriptor_layout_vertex_hash_out, + uint64_t& descriptor_layout_pixel_hash_out) { auto command_list = command_processor_->GetCurrentCommandList(); if (command_list == nullptr) { return; @@ -498,6 +506,31 @@ void TextureCache::RequestTextures(uint32_t used_vertex_texture_mask, state); texture->state = state; } + + // Compute descriptor layout hashes so descriptors don't have to be written + // again if the bindings are the same. The hash is computed from indices, keys + // and swizzles. + XXH64_state_t hash_state; + XXH64_reset(&hash_state, 0); + used_texture_mask = used_vertex_texture_mask; + while (xe::bit_scan_forward(used_texture_mask, &index)) { + used_texture_mask &= ~(1u << index); + XXH64_update(&hash_state, &index, sizeof(index)); + const TextureBinding& binding = texture_bindings_[index]; + XXH64_update(&hash_state, &binding.key, sizeof(binding.key)); + XXH64_update(&hash_state, &binding.swizzle, sizeof(binding.swizzle)); + } + descriptor_layout_vertex_hash_out = XXH64_digest(&hash_state); + XXH64_reset(&hash_state, 0); + used_texture_mask = used_pixel_texture_mask; + while (xe::bit_scan_forward(used_texture_mask, &index)) { + used_texture_mask &= ~(1u << index); + XXH64_update(&hash_state, &index, sizeof(index)); + const TextureBinding& binding = texture_bindings_[index]; + XXH64_update(&hash_state, &binding.key, sizeof(binding.key)); + XXH64_update(&hash_state, &binding.swizzle, sizeof(binding.swizzle)); + } + descriptor_layout_pixel_hash_out = XXH64_digest(&hash_state); } void TextureCache::WriteTextureSRV(uint32_t fetch_constant, diff --git a/src/xenia/gpu/d3d12/texture_cache.h b/src/xenia/gpu/d3d12/texture_cache.h index 4b2ab3edf..d383db412 100644 --- a/src/xenia/gpu/d3d12/texture_cache.h +++ b/src/xenia/gpu/d3d12/texture_cache.h @@ -72,7 +72,12 @@ class TextureCache { // (notifying the command processor about that), so this must be called before // binding the actual drawing pipeline. void RequestTextures(uint32_t used_vertex_texture_mask, - uint32_t used_pixel_texture_mask); + uint32_t used_pixel_texture_mask, + uint64_t& descriptor_layout_vertex_hash_out, + uint64_t& descriptor_layout_pixel_hash_out); + inline uint64_t GetEmptyTextureBindingsHash() const { + return texture_bindings_empty_hash_; + } void WriteTextureSRV(uint32_t fetch_constant, TextureDimension shader_dimension, @@ -366,6 +371,8 @@ class TextureCache { std::unordered_multimap textures_; TextureBinding texture_bindings_[32] = {}; + // Hash for no textures bound. + uint64_t texture_bindings_empty_hash_; // Bit vector with bits reset on fetch constant writes to avoid getting // texture keys from the fetch constants again and again. uint32_t texture_keys_in_sync_ = 0;