From 1a58f45d76fe7756dd365e099d1536da769c1eab Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 23 Sep 2019 14:02:02 -0400 Subject: VideoCore: Unify const buffer accessing along engines and provide ConstBufferLocker class to shaders. --- .../engines/const_buffer_engine_interface.h | 26 ++++++++++++++++++++++ src/video_core/engines/kepler_compute.cpp | 3 ++- src/video_core/engines/kepler_compute.h | 5 +++-- src/video_core/engines/maxwell_3d.cpp | 3 ++- src/video_core/engines/maxwell_3d.h | 5 +++-- 5 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 src/video_core/engines/const_buffer_engine_interface.h (limited to 'src/video_core/engines') diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h new file mode 100644 index 000000000..cc41a9cac --- /dev/null +++ b/src/video_core/engines/const_buffer_engine_interface.h @@ -0,0 +1,26 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Tegra::Engines { + +enum class ShaderType : u32 { + Vertex = 0, + TesselationControl = 1, + TesselationEval = 2, + Geometry = 3, + Fragment = 4, + Compute = 5, +}; + +class ConstBufferEngineInterface { +public: + virtual ~ConstBufferEngineInterface() {} + virtual u32 AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const = 0; +}; + +} diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 63d449135..ba97c2894 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -70,7 +70,8 @@ Texture::FullTextureInfo KeplerCompute::GetTextureInfo(const Texture::TextureHan GetTSCEntry(tex_handle.tsc_id)}; } -u32 KeplerCompute::AccessConstBuffer32(u64 const_buffer, u64 offset) const { +u32 KeplerCompute::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const { + ASSERT(stage == ShaderType::Compute); const auto& buffer = launch_description.const_buffer_config[const_buffer]; u32 result; std::memcpy(&result, memory_manager.GetPointer(buffer.Address() + offset), sizeof(u32)); diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 90cf650d2..d7e0dfcd6 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h @@ -11,6 +11,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "video_core/engines/engine_upload.h" +#include "video_core/engines/const_buffer_engine_interface.h" #include "video_core/gpu.h" #include "video_core/textures/texture.h" @@ -37,7 +38,7 @@ namespace Tegra::Engines { #define KEPLER_COMPUTE_REG_INDEX(field_name) \ (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) -class KeplerCompute final { +class KeplerCompute final : public ConstBufferEngineInterface { public: explicit KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); @@ -201,7 +202,7 @@ public: Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle, std::size_t offset) const; - u32 AccessConstBuffer32(u64 const_buffer, u64 offset) const; + u32 AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const override; private: Core::System& system; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 59976943a..92e38b071 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -847,7 +847,8 @@ void Maxwell3D::ProcessClearBuffers() { rasterizer.Clear(); } -u32 Maxwell3D::AccessConstBuffer32(Regs::ShaderStage stage, u64 const_buffer, u64 offset) const { +u32 Maxwell3D::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const { + ASSERT(stage != ShaderType::Compute); const auto& shader_stage = state.shader_stages[static_cast(stage)]; const auto& buffer = shader_stage.const_buffers[const_buffer]; u32 result; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index e3f1047d5..04d02d208 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -16,6 +16,7 @@ #include "common/common_types.h" #include "common/math_util.h" #include "video_core/engines/const_buffer_info.h" +#include "video_core/engines/const_buffer_engine_interface.h" #include "video_core/engines/engine_upload.h" #include "video_core/gpu.h" #include "video_core/macro_interpreter.h" @@ -44,7 +45,7 @@ namespace Tegra::Engines { #define MAXWELL3D_REG_INDEX(field_name) \ (offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32)) -class Maxwell3D final { +class Maxwell3D final : public ConstBufferEngineInterface { public: explicit Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); @@ -1257,7 +1258,7 @@ public: /// Returns the texture information for a specific texture in a specific shader stage. Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; - u32 AccessConstBuffer32(Regs::ShaderStage stage, u64 const_buffer, u64 offset) const; + u32 AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const override; /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than /// we've seen used. -- cgit v1.2.3 From 33fcec3502f5dd5a99b7a8337128b7c99bfba908 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 25 Sep 2019 09:53:18 -0400 Subject: Shader_IR: allow lookup of texture samplers within the shader_ir for instructions that don't provide it --- .../engines/const_buffer_engine_interface.h | 95 +++++++++++++++++- src/video_core/engines/kepler_compute.cpp | 18 ++++ src/video_core/engines/kepler_compute.h | 13 ++- src/video_core/engines/maxwell_3d.cpp | 18 ++++ src/video_core/engines/maxwell_3d.h | 11 ++- src/video_core/shader/const_buffer_locker.cpp | 110 ++++++++++++++++++--- src/video_core/shader/const_buffer_locker.h | 60 ++++++++++- src/video_core/shader/decode/texture.cpp | 72 ++++++++++---- src/video_core/shader/shader_ir.h | 12 ++- 9 files changed, 363 insertions(+), 46 deletions(-) (limited to 'src/video_core/engines') diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h index cc41a9cac..c0e3a3a17 100644 --- a/src/video_core/engines/const_buffer_engine_interface.h +++ b/src/video_core/engines/const_buffer_engine_interface.h @@ -4,7 +4,10 @@ #pragma once +#include "common/bit_field.h" #include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/textures/texture.h" namespace Tegra::Engines { @@ -17,10 +20,100 @@ enum class ShaderType : u32 { Compute = 5, }; +struct SamplerDescriptor { + union { + BitField<0, 20, Tegra::Shader::TextureType> texture_type; + BitField<20, 1, u32> is_array; + BitField<21, 1, u32> is_buffer; + BitField<22, 1, u32> is_shadow; + u32 raw{}; + }; + + static SamplerDescriptor FromTicTexture(Tegra::Texture::TextureType tic_texture_type) { + SamplerDescriptor result{}; + switch (tic_texture_type) { + case Tegra::Texture::TextureType::Texture1D: { + result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); + result.is_array.Assign(0); + result.is_buffer.Assign(0); + result.is_shadow.Assign(0); + return result; + } + case Tegra::Texture::TextureType::Texture2D: { + result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); + result.is_array.Assign(0); + result.is_buffer.Assign(0); + result.is_shadow.Assign(0); + return result; + } + case Tegra::Texture::TextureType::Texture3D: { + result.texture_type.Assign(Tegra::Shader::TextureType::Texture3D); + result.is_array.Assign(0); + result.is_buffer.Assign(0); + result.is_shadow.Assign(0); + return result; + } + case Tegra::Texture::TextureType::TextureCubemap: { + result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube); + result.is_array.Assign(0); + result.is_buffer.Assign(0); + result.is_shadow.Assign(0); + return result; + } + case Tegra::Texture::TextureType::Texture1DArray: { + result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); + result.is_array.Assign(1); + result.is_buffer.Assign(0); + result.is_shadow.Assign(0); + return result; + } + case Tegra::Texture::TextureType::Texture2DArray: { + result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); + result.is_array.Assign(1); + result.is_buffer.Assign(0); + result.is_shadow.Assign(0); + return result; + } + case Tegra::Texture::TextureType::Texture1DBuffer: { + result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); + result.is_array.Assign(0); + result.is_buffer.Assign(1); + result.is_shadow.Assign(0); + return result; + } + case Tegra::Texture::TextureType::Texture2DNoMipmap: { + result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); + result.is_array.Assign(0); + result.is_buffer.Assign(0); + result.is_shadow.Assign(0); + return result; + } + case Tegra::Texture::TextureType::TextureCubeArray: { + result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube); + result.is_array.Assign(1); + result.is_buffer.Assign(0); + result.is_shadow.Assign(0); + return result; + } + default: { + result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); + result.is_array.Assign(0); + result.is_buffer.Assign(0); + result.is_shadow.Assign(0); + return result; + } + } + } +}; + class ConstBufferEngineInterface { public: virtual ~ConstBufferEngineInterface() {} virtual u32 AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const = 0; + virtual SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const = 0; + virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, + u64 offset) const = 0; + virtual u32 GetBoundBuffer() const = 0; }; -} +} // namespace Tegra::Engines diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index ba97c2894..6f00db1c1 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -78,6 +78,24 @@ u32 KeplerCompute::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 o return result; } +SamplerDescriptor KeplerCompute::AccessBoundSampler(ShaderType stage, u64 offset) const { + return AccessBindlessSampler(stage, regs.tex_cb_index, offset * sizeof(Texture::TextureHandle)); +} + +SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 const_buffer, + u64 offset) const { + ASSERT(stage == ShaderType::Compute); + const auto& tex_info_buffer = launch_description.const_buffer_config[const_buffer]; + const GPUVAddr tex_info_address = + tex_info_buffer.Address() + offset; + + const Texture::TextureHandle tex_handle{memory_manager.Read(tex_info_address)}; + const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle, offset); + SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value()); + result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value()); + return result; +} + void KeplerCompute::ProcessLaunch() { const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description, diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index d7e0dfcd6..8e7182727 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h @@ -10,8 +10,8 @@ #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" -#include "video_core/engines/engine_upload.h" #include "video_core/engines/const_buffer_engine_interface.h" +#include "video_core/engines/engine_upload.h" #include "video_core/gpu.h" #include "video_core/textures/texture.h" @@ -38,7 +38,7 @@ namespace Tegra::Engines { #define KEPLER_COMPUTE_REG_INDEX(field_name) \ (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) -class KeplerCompute final : public ConstBufferEngineInterface { +class KeplerCompute final : public ConstBufferEngineInterface { public: explicit KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); @@ -204,6 +204,15 @@ public: u32 AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const override; + SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const override; + + SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, + u64 offset) const override; + + u32 GetBoundBuffer() const override { + return regs.tex_cb_index; + } + private: Core::System& system; VideoCore::RasterizerInterface& rasterizer; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 92e38b071..558955451 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -856,4 +856,22 @@ u32 Maxwell3D::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offse return result; } +SamplerDescriptor Maxwell3D::AccessBoundSampler(ShaderType stage, u64 offset) const { + return AccessBindlessSampler(stage, regs.tex_cb_index, offset * sizeof(Texture::TextureHandle)); +} + +SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_buffer, + u64 offset) const { + ASSERT(stage != ShaderType::Compute); + const auto& shader = state.shader_stages[static_cast(stage)]; + const auto& tex_info_buffer = shader.const_buffers[const_buffer]; + const GPUVAddr tex_info_address = tex_info_buffer.address + offset; + + const Texture::TextureHandle tex_handle{memory_manager.Read(tex_info_address)}; + const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle, offset); + SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value()); + result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value()); + return result; +} + } // namespace Tegra::Engines diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 04d02d208..fa846a621 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -15,8 +15,8 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/math_util.h" -#include "video_core/engines/const_buffer_info.h" #include "video_core/engines/const_buffer_engine_interface.h" +#include "video_core/engines/const_buffer_info.h" #include "video_core/engines/engine_upload.h" #include "video_core/gpu.h" #include "video_core/macro_interpreter.h" @@ -1260,6 +1260,15 @@ public: u32 AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const override; + SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const override; + + SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, + u64 offset) const override; + + u32 GetBoundBuffer() const override { + return regs.tex_cb_index; + } + /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than /// we've seen used. using MacroMemory = std::array; diff --git a/src/video_core/shader/const_buffer_locker.cpp b/src/video_core/shader/const_buffer_locker.cpp index 6a9e0ed5e..4f5de8ae9 100644 --- a/src/video_core/shader/const_buffer_locker.cpp +++ b/src/video_core/shader/const_buffer_locker.cpp @@ -27,43 +27,121 @@ void ConstBufferLocker::SetEngine(Tegra::Engines::ConstBufferEngineInterface* en } std::optional ConstBufferLocker::ObtainKey(u32 buffer, u32 offset) { + if (!keys) { + keys = std::make_shared(); + } + auto& key_map = *keys; const std::pair key = {buffer, offset}; - const auto iter = keys.find(key); - if (iter != keys.end()) { + const auto iter = key_map.find(key); + if (iter != key_map.end()) { return {iter->second}; } if (!IsEngineSet()) { return {}; } const u32 value = engine->AccessConstBuffer32(shader_stage, buffer, offset); - keys.emplace(key, value); + key_map.emplace(key, value); + return {value}; +} + +std::optional ConstBufferLocker::ObtainBoundSampler(u32 offset) { + if (!bound_samplers) { + bound_samplers = std::make_shared(); + } + auto& key_map = *bound_samplers; + const u32 key = offset; + const auto iter = key_map.find(key); + if (iter != key_map.end()) { + return {iter->second}; + } + if (!IsEngineSet()) { + return {}; + } + const Tegra::Engines::SamplerDescriptor value = + engine->AccessBoundSampler(shader_stage, offset); + key_map.emplace(key, value); + return {value}; +} + +std::optional ConstBufferLocker::ObtainBindlessSampler( + u32 buffer, u32 offset) { + if (!bindless_samplers) { + bindless_samplers = std::make_shared(); + } + auto& key_map = *bindless_samplers; + const std::pair key = {buffer, offset}; + const auto iter = key_map.find(key); + if (iter != key_map.end()) { + return {iter->second}; + } + if (!IsEngineSet()) { + return {}; + } + const Tegra::Engines::SamplerDescriptor value = + engine->AccessBindlessSampler(shader_stage, buffer, offset); + key_map.emplace(key, value); return {value}; } void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) { + if (!keys) { + keys = std::make_shared(); + } const std::pair key = {buffer, offset}; - keys[key] = value; + (*keys)[key] = value; } -u32 ConstBufferLocker::NumKeys() const { - return keys.size(); +void ConstBufferLocker::InsertBoundSampler(u32 offset, Tegra::Engines::SamplerDescriptor sampler) { + if (!bound_samplers) { + bound_samplers = std::make_shared(); + } + (*bound_samplers)[offset] = sampler; } -const std::unordered_map, u32, Common::PairHash>& -ConstBufferLocker::AccessKeys() const { - return keys; +void ConstBufferLocker::InsertBindlessSampler(u32 buffer, u32 offset, + Tegra::Engines::SamplerDescriptor sampler) { + if (!bindless_samplers) { + bindless_samplers = std::make_shared(); + } + const std::pair key = {buffer, offset}; + (*bindless_samplers)[key] = sampler; } -bool ConstBufferLocker::AreKeysConsistant() const { +bool ConstBufferLocker::IsConsistant() const { if (!IsEngineSet()) { return false; } - for (const auto& key_val : keys) { - const std::pair key = key_val.first; - const u32 value = key_val.second; - const u32 other_value = engine->AccessConstBuffer32(shader_stage, key.first, key.second); - if (other_value != value) { - return false; + if (keys) { + for (const auto& key_val : *keys) { + const std::pair key = key_val.first; + const u32 value = key_val.second; + const u32 other_value = + engine->AccessConstBuffer32(shader_stage, key.first, key.second); + if (other_value != value) { + return false; + } + } + } + if (bound_samplers) { + for (const auto& sampler_val : *bound_samplers) { + const u32 key = sampler_val.first; + const Tegra::Engines::SamplerDescriptor value = sampler_val.second; + const Tegra::Engines::SamplerDescriptor other_value = + engine->AccessBoundSampler(shader_stage, key); + if (other_value.raw != value.raw) { + return false; + } + } + } + if (bindless_samplers) { + for (const auto& sampler_val : *bindless_samplers) { + const std::pair key = sampler_val.first; + const Tegra::Engines::SamplerDescriptor value = sampler_val.second; + const Tegra::Engines::SamplerDescriptor other_value = + engine->AccessBindlessSampler(shader_stage, key.first, key.second); + if (other_value.raw != value.raw) { + return false; + } } } return true; diff --git a/src/video_core/shader/const_buffer_locker.h b/src/video_core/shader/const_buffer_locker.h index 39e62584d..0bc257781 100644 --- a/src/video_core/shader/const_buffer_locker.h +++ b/src/video_core/shader/const_buffer_locker.h @@ -11,6 +11,11 @@ namespace VideoCommon::Shader { +using KeyMap = std::unordered_map, u32, Common::PairHash>; +using BoundSamplerMap = std::unordered_map; +using BindlessSamplerMap = + std::unordered_map, Tegra::Engines::SamplerDescriptor, Common::PairHash>; + class ConstBufferLocker { public: explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage); @@ -29,22 +34,67 @@ public: // registered value, if not it will obtain it from maxwell3d and register it. std::optional ObtainKey(u32 buffer, u32 offset); + std::optional ObtainBoundSampler(u32 offset); + + std::optional ObtainBindlessSampler(u32 buffer, u32 offset); + // Manually inserts a key. void InsertKey(u32 buffer, u32 offset, u32 value); + void InsertBoundSampler(u32 offset, Tegra::Engines::SamplerDescriptor sampler); + + void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler); + // Retrieves the number of keys registered. - u32 NumKeys() const; + std::size_t NumKeys() const { + if (!keys) { + return 0; + } + return keys->size(); + } + + std::size_t NumBoundSamplers() const { + if (!bound_samplers) { + return 0; + } + return bound_samplers->size(); + } + + std::size_t NumBindlessSamplers() const { + if (!bindless_samplers) { + return 0; + } + return bindless_samplers->size(); + } // Gives an accessor to the key's database. - const std::unordered_map, u32, Common::PairHash>& AccessKeys() const; + // Pre: NumKeys > 0 + const KeyMap& AccessKeys() const { + return *keys; + } + + // Gives an accessor to the sampler's database. + // Pre: NumBindlessSamplers > 0 + const BoundSamplerMap& AccessBoundSamplers() const { + return *bound_samplers; + } + + // Gives an accessor to the sampler's database. + // Pre: NumBindlessSamplers > 0 + const BindlessSamplerMap& AccessBindlessSamplers() const { + return *bindless_samplers; + } - // Checks keys against maxwell3d's current const buffers. Returns true if they + // Checks keys & samplers against engine's current const buffers. Returns true if they // are the same value, false otherwise; - bool AreKeysConsistant() const; + bool IsConsistant() const; private: Tegra::Engines::ConstBufferEngineInterface* engine; Tegra::Engines::ShaderType shader_stage; - std::unordered_map, u32, Common::PairHash> keys{}; + // All containers are lazy initialized as most shaders don't use them. + std::shared_ptr keys{}; + std::shared_ptr bound_samplers{}; + std::shared_ptr bindless_samplers{}; }; } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index 0b934a069..c369e23ad 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp @@ -141,7 +141,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { const Node component = Immediate(static_cast(instr.tld4s.component)); const auto& sampler = - GetSampler(instr.sampler, TextureType::Texture2D, false, depth_compare); + GetSampler(instr.sampler, {{TextureType::Texture2D, false, depth_compare}}); Node4 values; for (u32 element = 0; element < values.size(); ++element) { @@ -165,10 +165,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { // Sadly, not all texture instructions specify the type of texture their sampler // uses. This must be fixed at a later instance. const auto& sampler = - is_bindless - ? GetBindlessSampler(instr.gpr8, Tegra::Shader::TextureType::Texture2D, false, - false) - : GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); + is_bindless ? GetBindlessSampler(instr.gpr8, {}) : GetSampler(instr.sampler, {}); u32 indexer = 0; switch (instr.txq.query_type) { @@ -207,9 +204,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { auto texture_type = instr.tmml.texture_type.Value(); const bool is_array = instr.tmml.array != 0; - const auto& sampler = is_bindless - ? GetBindlessSampler(instr.gpr20, texture_type, is_array, false) - : GetSampler(instr.sampler, texture_type, is_array, false); + const auto& sampler = + is_bindless ? GetBindlessSampler(instr.gpr20, {{texture_type, is_array, false}}) + : GetSampler(instr.sampler, {{texture_type, is_array, false}}); std::vector coords; @@ -285,10 +282,30 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { return pc; } -const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, TextureType type, - bool is_array, bool is_shadow) { +const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, + std::optional sampler_info) { const auto offset = static_cast(sampler.index.Value()); + Tegra::Shader::TextureType type; + bool is_array; + bool is_shadow; + if (sampler_info) { + type = sampler_info->type; + is_array = sampler_info->is_array; + is_shadow = sampler_info->is_shadow; + } else { + auto sampler = locker.ObtainBoundSampler(offset); + if (sampler) { + type = sampler->texture_type.Value(); + is_array = sampler->is_array.Value() != 0; + is_shadow = sampler->is_shadow.Value() != 0; + } else { + type = Tegra::Shader::TextureType::Texture2D; + is_array = false; + is_shadow = false; + } + } + // If this sampler has already been used, return the existing mapping. const auto itr = std::find_if(used_samplers.begin(), used_samplers.end(), @@ -305,13 +322,32 @@ const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, Textu return *used_samplers.emplace(entry).first; } -const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg, TextureType type, - bool is_array, bool is_shadow) { +const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg, + std::optional sampler_info) { const Node sampler_register = GetRegister(reg); const auto [base_sampler, cbuf_index, cbuf_offset] = TrackCbuf(sampler_register, global_code, static_cast(global_code.size())); ASSERT(base_sampler != nullptr); const auto cbuf_key = (static_cast(cbuf_index) << 32) | static_cast(cbuf_offset); + Tegra::Shader::TextureType type; + bool is_array; + bool is_shadow; + if (sampler_info) { + type = sampler_info->type; + is_array = sampler_info->is_array; + is_shadow = sampler_info->is_shadow; + } else { + auto sampler = locker.ObtainBindlessSampler(cbuf_index, cbuf_offset); + if (sampler) { + type = sampler->texture_type.Value(); + is_array = sampler->is_array.Value() != 0; + is_shadow = sampler->is_shadow.Value() != 0; + } else { + type = Tegra::Shader::TextureType::Texture2D; + is_array = false; + is_shadow = false; + } + } // If this sampler has already been used, return the existing mapping. const auto itr = @@ -411,9 +447,9 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, (texture_type == TextureType::TextureCube && is_array && is_shadow), "This method is not supported."); - const auto& sampler = is_bindless - ? GetBindlessSampler(*bindless_reg, texture_type, is_array, is_shadow) - : GetSampler(instr.sampler, texture_type, is_array, is_shadow); + const auto& sampler = + is_bindless ? GetBindlessSampler(*bindless_reg, {{texture_type, is_array, is_shadow}}) + : GetSampler(instr.sampler, {{texture_type, is_array, is_shadow}}); const bool lod_needed = process_mode == TextureProcessMode::LZ || process_mode == TextureProcessMode::LL || @@ -577,7 +613,7 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de dc = GetRegister(parameter_register++); } - const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare); + const auto& sampler = GetSampler(instr.sampler, {{texture_type, is_array, depth_compare}}); Node4 values; for (u32 element = 0; element < values.size(); ++element) { @@ -610,7 +646,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) { // const Node aoffi_register{is_aoffi ? GetRegister(gpr20_cursor++) : nullptr}; // const Node multisample{is_multisample ? GetRegister(gpr20_cursor++) : nullptr}; - const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, false); + const auto& sampler = GetSampler(instr.sampler, {{texture_type, is_array, false}}); Node4 values; for (u32 element = 0; element < values.size(); ++element) { @@ -646,7 +682,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is // When lod is used always is in gpr20 const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0); - const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, false); + const auto& sampler = GetSampler(instr.sampler, {{texture_type, is_array, false}}); Node4 values; for (u32 element = 0; element < values.size(); ++element) { diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index e3b568d3e..3a3e381d2 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -173,6 +173,13 @@ public: private: friend class ASTDecoder; + + struct SamplerInfo { + Tegra::Shader::TextureType type; + bool is_array; + bool is_shadow; + }; + void Decode(); NodeBlock DecodeRange(u32 begin, u32 end); @@ -297,12 +304,11 @@ private: /// Accesses a texture sampler const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler, - Tegra::Shader::TextureType type, bool is_array, bool is_shadow); + std::optional sampler_info); // Accesses a texture sampler for a bindless texture. const Sampler& GetBindlessSampler(const Tegra::Shader::Register& reg, - Tegra::Shader::TextureType type, bool is_array, - bool is_shadow); + std::optional sampler_info); /// Accesses an image. Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type); -- cgit v1.2.3 From ec85648af3316d5e43c7b57fca55d0dad3d03f96 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Wed, 25 Sep 2019 21:46:34 -0300 Subject: gl_shader_disk_cache: Store and load fast BRX --- .../engines/const_buffer_engine_interface.h | 35 ++-- src/video_core/renderer_opengl/gl_shader_cache.cpp | 19 ++- src/video_core/renderer_opengl/gl_shader_cache.h | 7 +- .../renderer_opengl/gl_shader_disk_cache.cpp | 183 ++++++++++++++++++--- .../renderer_opengl/gl_shader_disk_cache.h | 12 +- src/video_core/shader/const_buffer_locker.cpp | 4 +- 6 files changed, 210 insertions(+), 50 deletions(-) (limited to 'src/video_core/engines') diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h index c0e3a3a17..80f470777 100644 --- a/src/video_core/engines/const_buffer_engine_interface.h +++ b/src/video_core/engines/const_buffer_engine_interface.h @@ -4,6 +4,7 @@ #pragma once +#include #include "common/bit_field.h" #include "common/common_types.h" #include "video_core/engines/shader_bytecode.h" @@ -29,51 +30,49 @@ struct SamplerDescriptor { u32 raw{}; }; + bool operator==(const SamplerDescriptor& rhs) const noexcept { + return raw == rhs.raw; + } + static SamplerDescriptor FromTicTexture(Tegra::Texture::TextureType tic_texture_type) { - SamplerDescriptor result{}; + SamplerDescriptor result; switch (tic_texture_type) { - case Tegra::Texture::TextureType::Texture1D: { + case Tegra::Texture::TextureType::Texture1D: result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); result.is_array.Assign(0); result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; - } - case Tegra::Texture::TextureType::Texture2D: { + case Tegra::Texture::TextureType::Texture2D: result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); result.is_array.Assign(0); result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; - } - case Tegra::Texture::TextureType::Texture3D: { + case Tegra::Texture::TextureType::Texture3D: result.texture_type.Assign(Tegra::Shader::TextureType::Texture3D); result.is_array.Assign(0); result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; - } - case Tegra::Texture::TextureType::TextureCubemap: { + case Tegra::Texture::TextureType::TextureCubemap: result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube); result.is_array.Assign(0); result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; - } - case Tegra::Texture::TextureType::Texture1DArray: { + case Tegra::Texture::TextureType::Texture1DArray: result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); result.is_array.Assign(1); result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; - } - case Tegra::Texture::TextureType::Texture2DArray: { + case Tegra::Texture::TextureType::Texture2DArray: result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); result.is_array.Assign(1); result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; - } case Tegra::Texture::TextureType::Texture1DBuffer: { result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); result.is_array.Assign(0); @@ -81,30 +80,28 @@ struct SamplerDescriptor { result.is_shadow.Assign(0); return result; } - case Tegra::Texture::TextureType::Texture2DNoMipmap: { + case Tegra::Texture::TextureType::Texture2DNoMipmap: result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); result.is_array.Assign(0); result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; - } - case Tegra::Texture::TextureType::TextureCubeArray: { + case Tegra::Texture::TextureType::TextureCubeArray: result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube); result.is_array.Assign(1); result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; - } - default: { + default: result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); result.is_array.Assign(0); result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; } - } } }; +static_assert(std::is_trivially_copyable_v); class ConstBufferEngineInterface { public: diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 6402d6763..7e7aea15f 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -392,7 +392,7 @@ std::tuple CachedShader::GetProgramHandle(const ProgramVar ConstBufferLocker locker(GetEnginesShaderType(program_type), *engine); program = BuildShader(device, unique_identifier, program_type, program_code, program_code_b, variant, locker); - disk_cache.SaveUsage(GetUsage(variant)); + disk_cache.SaveUsage(GetUsage(variant, locker)); LabelGLObject(GL_PROGRAM, program->handle, cpu_addr); } @@ -408,10 +408,14 @@ std::tuple CachedShader::GetProgramHandle(const ProgramVar return {program->handle, base_bindings}; } -ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant) const { +ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant, + const ConstBufferLocker& locker) const { ShaderDiskCacheUsage usage; usage.unique_identifier = unique_identifier; usage.variant = variant; + usage.keys = locker.GetKeys(); + usage.bound_samplers = locker.GetBoundSamplers(); + usage.bindless_samplers = locker.GetBindlessSamplers(); return usage; } @@ -472,6 +476,17 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, } if (!shader) { ConstBufferLocker locker(GetEnginesShaderType(unspecialized.program_type)); + for (const auto& key : usage.keys) { + const auto [buffer, offset] = key.first; + locker.InsertKey(buffer, offset, key.second); + } + for (const auto& [offset, sampler] : usage.bound_samplers) { + locker.InsertBoundSampler(offset, sampler); + } + for (const auto& [key, sampler] : usage.bindless_samplers) { + const auto [buffer, offset] = key; + locker.InsertBindlessSampler(buffer, offset, sampler); + } shader = BuildShader(device, usage.unique_identifier, unspecialized.program_type, unspecialized.code, unspecialized.code_b, usage.variant, locker, true); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 700a83853..2935e6831 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -31,6 +31,10 @@ namespace Core::Frontend { class EmuWindow; } +namespace VideoCommon::Shader { +class ConstBufferLocker; +} + namespace OpenGL { class CachedShader; @@ -92,7 +96,8 @@ private: GLShader::ShaderEntries entries, ProgramCode program_code, ProgramCode program_code_b); - ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant) const; + ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant, + const VideoCommon::Shader::ConstBufferLocker& locker) const; Core::System& system; ShaderDiskCacheOpenGL& disk_cache; diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index ddc19dccd..184a565e6 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -22,6 +22,29 @@ namespace OpenGL { +using VideoCommon::Shader::BindlessSamplerMap; +using VideoCommon::Shader::BoundSamplerMap; +using VideoCommon::Shader::KeyMap; + +namespace { + +struct ConstBufferKey { + u32 cbuf; + u32 offset; + u32 value; +}; + +struct BoundSamplerKey { + u32 offset; + Tegra::Engines::SamplerDescriptor sampler; +}; + +struct BindlessSamplerKey { + u32 cbuf; + u32 offset; + Tegra::Engines::SamplerDescriptor sampler; +}; + using ShaderCacheVersionHash = std::array; enum class TransferableEntryKind : u32 { @@ -33,9 +56,6 @@ constexpr u32 NativeVersion = 5; // Making sure sizes doesn't change by accident static_assert(sizeof(BaseBindings) == 16); -static_assert(sizeof(ShaderDiskCacheUsage) == 40); - -namespace { ShaderCacheVersionHash GetShaderCacheVersionHash() { ShaderCacheVersionHash hash{}; @@ -121,13 +141,13 @@ ShaderDiskCacheOpenGL::LoadTransferable() { u32 version{}; if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) { LOG_ERROR(Render_OpenGL, - "Failed to get transferable cache version for title id={} - skipping", + "Failed to get transferable cache version for title id={}, skipping", GetTitleID()); return {}; } if (version < NativeVersion) { - LOG_INFO(Render_OpenGL, "Transferable shader cache is old - removing"); + LOG_INFO(Render_OpenGL, "Transferable shader cache is old, removing"); file.Close(); InvalidateTransferable(); is_usable = true; @@ -135,17 +155,18 @@ ShaderDiskCacheOpenGL::LoadTransferable() { } if (version > NativeVersion) { LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version " - "of the emulator - skipping"); + "of the emulator, skipping"); return {}; } // Version is valid, load the shaders + constexpr const char error_loading[] = "Failed to load transferable raw entry, skipping"; std::vector raws; std::vector usages; while (file.Tell() < file.GetSize()) { TransferableEntryKind kind{}; if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) { - LOG_ERROR(Render_OpenGL, "Failed to read transferable file - skipping"); + LOG_ERROR(Render_OpenGL, "Failed to read transferable file, skipping"); return {}; } @@ -153,7 +174,7 @@ ShaderDiskCacheOpenGL::LoadTransferable() { case TransferableEntryKind::Raw: { ShaderDiskCacheRaw entry; if (!entry.Load(file)) { - LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry - skipping"); + LOG_ERROR(Render_OpenGL, error_loading); return {}; } transferable.insert({entry.GetUniqueIdentifier(), {}}); @@ -161,16 +182,45 @@ ShaderDiskCacheOpenGL::LoadTransferable() { break; } case TransferableEntryKind::Usage: { - ShaderDiskCacheUsage usage{}; - if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) { - LOG_ERROR(Render_OpenGL, "Failed to load transferable usage entry - skipping"); + ShaderDiskCacheUsage usage; + + u32 num_keys{}; + u32 num_bound_samplers{}; + u32 num_bindless_samplers{}; + if (file.ReadArray(&usage.unique_identifier, 1) != 1 || + file.ReadArray(&usage.variant, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 || + file.ReadArray(&num_bound_samplers, 1) != 1 || + file.ReadArray(&num_bindless_samplers, 1) != 1) { + LOG_ERROR(Render_OpenGL, error_loading); + return {}; + } + + std::vector keys(num_keys); + std::vector bound_samplers(num_bound_samplers); + std::vector bindless_samplers(num_bindless_samplers); + if (file.ReadArray(keys.data(), keys.size()) != keys.size() || + file.ReadArray(bound_samplers.data(), bound_samplers.size()) != + bound_samplers.size() || + file.ReadArray(bindless_samplers.data(), bindless_samplers.size()) != + bindless_samplers.size()) { + LOG_ERROR(Render_OpenGL, error_loading); return {}; } + for (const auto& key : keys) { + usage.keys.insert({{key.cbuf, key.offset}, key.value}); + } + for (const auto& key : bound_samplers) { + usage.bound_samplers.emplace(key.offset, key.sampler); + } + for (const auto& key : bindless_samplers) { + usage.bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler}); + } + usages.push_back(std::move(usage)); break; } default: - LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={} - skipping", + LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={}, skipping", static_cast(kind)); return {}; } @@ -197,7 +247,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() { const auto result = LoadPrecompiledFile(file); if (!result) { LOG_INFO(Render_OpenGL, - "Failed to load precompiled cache for game with title id={} - removing", + "Failed to load precompiled cache for game with title id={}, removing", GetTitleID()); file.Close(); InvalidatePrecompiled(); @@ -228,10 +278,35 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { ShaderDumpsMap dumps; while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) { + u32 num_keys{}; + u32 num_bound_samplers{}; + u32 num_bindless_samplers{}; ShaderDiskCacheUsage usage; - if (!LoadObjectFromPrecompiled(usage)) { + if (!LoadObjectFromPrecompiled(usage.unique_identifier) || + !LoadObjectFromPrecompiled(usage.variant) || !LoadObjectFromPrecompiled(num_keys) || + !LoadObjectFromPrecompiled(num_bound_samplers) || + !LoadObjectFromPrecompiled(num_bindless_samplers)) { return {}; } + std::vector keys(num_keys); + std::vector bound_samplers(num_bound_samplers); + std::vector bindless_samplers(num_bindless_samplers); + if (!LoadArrayFromPrecompiled(keys.data(), keys.size()) || + !LoadArrayFromPrecompiled(bound_samplers.data(), bound_samplers.size()) != + bound_samplers.size() || + !LoadArrayFromPrecompiled(bindless_samplers.data(), bindless_samplers.size()) != + bindless_samplers.size()) { + return {}; + } + for (const auto& key : keys) { + usage.keys.insert({{key.cbuf, key.offset}, key.value}); + } + for (const auto& key : bound_samplers) { + usage.bound_samplers.emplace(key.offset, key.sampler); + } + for (const auto& key : bindless_samplers) { + usage.bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler}); + } ShaderDiskCacheDump dump; if (!LoadObjectFromPrecompiled(dump.binary_format)) { @@ -248,7 +323,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { return {}; } - dumps.emplace(usage, dump); + dumps.emplace(std::move(usage), dump); } return dumps; } @@ -282,10 +357,11 @@ void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) { } FileUtil::IOFile file = AppendTransferableFile(); - if (!file.IsOpen()) + if (!file.IsOpen()) { return; + } if (file.WriteObject(TransferableEntryKind::Raw) != 1 || !entry.Save(file)) { - LOG_ERROR(Render_OpenGL, "Failed to save raw transferable cache entry - removing"); + LOG_ERROR(Render_OpenGL, "Failed to save raw transferable cache entry, removing"); file.Close(); InvalidateTransferable(); return; @@ -311,13 +387,40 @@ void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { FileUtil::IOFile file = AppendTransferableFile(); if (!file.IsOpen()) return; - - if (file.WriteObject(TransferableEntryKind::Usage) != 1 || file.WriteObject(usage) != 1) { - LOG_ERROR(Render_OpenGL, "Failed to save usage transferable cache entry - removing"); + const auto Close = [&] { + LOG_ERROR(Render_OpenGL, "Failed to save usage transferable cache entry, removing"); file.Close(); InvalidateTransferable(); + }; + + if (file.WriteObject(TransferableEntryKind::Usage) != 1 || + file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 || + file.WriteObject(static_cast(usage.keys.size())) != 1 || + file.WriteObject(static_cast(usage.bound_samplers.size())) != 1 || + file.WriteObject(static_cast(usage.bindless_samplers.size())) != 1) { + Close(); return; } + for (const auto& [pair, value] : usage.keys) { + const auto [cbuf, offset] = pair; + if (file.WriteObject(ConstBufferKey{cbuf, offset, value}) != 1) { + Close(); + return; + } + } + for (const auto& [offset, sampler] : usage.bound_samplers) { + if (file.WriteObject(BoundSamplerKey{offset, sampler}) != 1) { + Close(); + return; + } + } + for (const auto& [pair, sampler] : usage.bindless_samplers) { + const auto [cbuf, offset] = pair; + if (file.WriteObject(BindlessSamplerKey{cbuf, offset, sampler}) != 1) { + Close(); + return; + } + } } void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) { @@ -339,15 +442,45 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p std::vector binary(binary_length); glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); - if (!SaveObjectToPrecompiled(usage) || - !SaveObjectToPrecompiled(static_cast(binary_format)) || - !SaveObjectToPrecompiled(static_cast(binary_length)) || - !SaveArrayToPrecompiled(binary.data(), binary.size())) { - LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", + const auto Close = [&] { + LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016X}, removing", usage.unique_identifier); InvalidatePrecompiled(); + }; + + if (!SaveObjectToPrecompiled(usage.unique_identifier) || + !SaveObjectToPrecompiled(usage.variant) || + !SaveObjectToPrecompiled(static_cast(usage.keys.size())) || + !SaveObjectToPrecompiled(static_cast(usage.bound_samplers.size())) || + !SaveObjectToPrecompiled(static_cast(usage.bindless_samplers.size()))) { + Close(); return; } + for (const auto& [pair, value] : usage.keys) { + const auto [cbuf, offset] = pair; + if (SaveObjectToPrecompiled(ConstBufferKey{cbuf, offset, value}) != 1) { + Close(); + return; + } + } + for (const auto& [offset, sampler] : usage.bound_samplers) { + if (SaveObjectToPrecompiled(BoundSamplerKey{offset, sampler}) != 1) { + Close(); + return; + } + } + for (const auto& [pair, sampler] : usage.bindless_samplers) { + const auto [cbuf, offset] = pair; + if (SaveObjectToPrecompiled(BindlessSamplerKey{cbuf, offset, sampler}) != 1) { + Close(); + return; + } + } + if (!SaveObjectToPrecompiled(static_cast(binary_format)) || + !SaveObjectToPrecompiled(static_cast(binary_length)) || + !SaveArrayToPrecompiled(binary.data(), binary.size())) { + Close(); + } } FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h index 61b46d728..db23ada93 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include "common/common_types.h" #include "core/file_sys/vfs_vector.h" #include "video_core/renderer_opengl/gl_shader_gen.h" +#include "video_core/shader/const_buffer_locker.h" namespace Core { class System; @@ -53,6 +55,7 @@ struct BaseBindings { return !operator==(rhs); } }; +static_assert(std::is_trivially_copyable_v); /// Describes the different variants a single program can be compiled. struct ProgramVariant { @@ -70,13 +73,20 @@ struct ProgramVariant { } }; +static_assert(std::is_trivially_copyable_v); + /// Describes how a shader is used. struct ShaderDiskCacheUsage { u64 unique_identifier{}; ProgramVariant variant; + VideoCommon::Shader::KeyMap keys; + VideoCommon::Shader::BoundSamplerMap bound_samplers; + VideoCommon::Shader::BindlessSamplerMap bindless_samplers; bool operator==(const ShaderDiskCacheUsage& rhs) const { - return std::tie(unique_identifier, variant) == std::tie(rhs.unique_identifier, rhs.variant); + return std::tie(unique_identifier, variant, keys, bound_samplers, bindless_samplers) == + std::tie(rhs.unique_identifier, rhs.variant, rhs.keys, rhs.bound_samplers, + rhs.bindless_samplers); } bool operator!=(const ShaderDiskCacheUsage& rhs) const { diff --git a/src/video_core/shader/const_buffer_locker.cpp b/src/video_core/shader/const_buffer_locker.cpp index ebeba102d..fda9e3c38 100644 --- a/src/video_core/shader/const_buffer_locker.cpp +++ b/src/video_core/shader/const_buffer_locker.cpp @@ -90,14 +90,14 @@ bool ConstBufferLocker::IsConsistent() const { [this](const auto& sampler) { const auto [key, value] = sampler; const auto other_value = engine->AccessBoundSampler(stage, key); - return value.raw == other_value.raw; + return value == other_value; }) && std::all_of( bindless_samplers.begin(), bindless_samplers.end(), [this](const auto& sampler) { const auto [cbuf, offset] = sampler.first; const auto value = sampler.second; const auto other_value = engine->AccessBindlessSampler(stage, cbuf, offset); - return value.raw == other_value.raw; + return value == other_value; }); } -- cgit v1.2.3 From e3afd6595a3b7d67e554aaa21f929b64c9fe4b8b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 30 Sep 2019 20:55:25 -0400 Subject: Shader_IR: Clang format --- src/video_core/engines/kepler_compute.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/video_core/engines') diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 6f00db1c1..91adef360 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -86,8 +86,7 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con u64 offset) const { ASSERT(stage == ShaderType::Compute); const auto& tex_info_buffer = launch_description.const_buffer_config[const_buffer]; - const GPUVAddr tex_info_address = - tex_info_buffer.Address() + offset; + const GPUVAddr tex_info_address = tex_info_buffer.Address() + offset; const Texture::TextureHandle tex_handle{memory_manager.Read(tex_info_address)}; const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle, offset); -- cgit v1.2.3 From be856a38d6b0c7c90c861baf3204ac48a108f3d2 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 17 Oct 2019 10:35:16 -0400 Subject: Shader_IR: Address Feedback. --- src/common/hash.h | 2 +- .../engines/const_buffer_engine_interface.h | 9 +++-- src/video_core/shader/const_buffer_locker.cpp | 8 ++-- src/video_core/shader/const_buffer_locker.h | 7 ++++ src/video_core/shader/control_flow.cpp | 46 ++++++++++------------ src/video_core/shader/control_flow.h | 6 ++- src/video_core/shader/decode.cpp | 2 +- src/video_core/shader/decode/texture.cpp | 38 ++++++++---------- src/video_core/shader/expr.h | 4 ++ 9 files changed, 66 insertions(+), 56 deletions(-) (limited to 'src/video_core/engines') diff --git a/src/common/hash.h b/src/common/hash.h index c939709bc..ebd4125e2 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -72,7 +72,7 @@ struct HashableStruct { struct PairHash { template - std::size_t operator()(const std::pair& pair) const { + std::size_t operator()(const std::pair& pair) const noexcept { std::size_t seed = std::hash()(pair.first); boost::hash_combine(seed, std::hash()(pair.second)); return seed; diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h index 80f470777..ac27b6cbe 100644 --- a/src/video_core/engines/const_buffer_engine_interface.h +++ b/src/video_core/engines/const_buffer_engine_interface.h @@ -34,6 +34,10 @@ struct SamplerDescriptor { return raw == rhs.raw; } + bool operator!=(const SamplerDescriptor& rhs) const noexcept { + return !operator==(rhs); + } + static SamplerDescriptor FromTicTexture(Tegra::Texture::TextureType tic_texture_type) { SamplerDescriptor result; switch (tic_texture_type) { @@ -73,13 +77,12 @@ struct SamplerDescriptor { result.is_buffer.Assign(0); result.is_shadow.Assign(0); return result; - case Tegra::Texture::TextureType::Texture1DBuffer: { + case Tegra::Texture::TextureType::Texture1DBuffer: result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); result.is_array.Assign(0); result.is_buffer.Assign(1); result.is_shadow.Assign(0); return result; - } case Tegra::Texture::TextureType::Texture2DNoMipmap: result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); result.is_array.Assign(0); @@ -105,7 +108,7 @@ static_assert(std::is_trivially_copyable_v); class ConstBufferEngineInterface { public: - virtual ~ConstBufferEngineInterface() {} + virtual ~ConstBufferEngineInterface() = default; virtual u32 AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const = 0; virtual SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const = 0; virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, diff --git a/src/video_core/shader/const_buffer_locker.cpp b/src/video_core/shader/const_buffer_locker.cpp index 592bbf657..fe467608e 100644 --- a/src/video_core/shader/const_buffer_locker.cpp +++ b/src/video_core/shader/const_buffer_locker.cpp @@ -22,6 +22,8 @@ ConstBufferLocker::ConstBufferLocker(Tegra::Engines::ShaderType shader_stage, Tegra::Engines::ConstBufferEngineInterface& engine) : stage{shader_stage}, engine{&engine} {} +ConstBufferLocker::~ConstBufferLocker() = default; + std::optional ConstBufferLocker::ObtainKey(u32 buffer, u32 offset) { const std::pair key = {buffer, offset}; const auto iter = keys.find(key); @@ -29,7 +31,7 @@ std::optional ConstBufferLocker::ObtainKey(u32 buffer, u32 offset) { return iter->second; } if (!engine) { - return {}; + return std::nullopt; } const u32 value = engine->AccessConstBuffer32(stage, buffer, offset); keys.emplace(key, value); @@ -43,7 +45,7 @@ std::optional ConstBufferLocker::ObtainBoundSampler(u32 offse return iter->second; } if (!engine) { - return {}; + return std::nullopt; } const SamplerDescriptor value = engine->AccessBoundSampler(stage, offset); bound_samplers.emplace(key, value); @@ -58,7 +60,7 @@ std::optional ConstBufferLocker::ObtainBindle return iter->second; } if (!engine) { - return {}; + return std::nullopt; } const SamplerDescriptor value = engine->AccessBindlessSampler(stage, buffer, offset); bindless_samplers.emplace(key, value); diff --git a/src/video_core/shader/const_buffer_locker.h b/src/video_core/shader/const_buffer_locker.h index 966537fd6..600e2f3c3 100644 --- a/src/video_core/shader/const_buffer_locker.h +++ b/src/video_core/shader/const_buffer_locker.h @@ -16,6 +16,11 @@ using BoundSamplerMap = std::unordered_map, Tegra::Engines::SamplerDescriptor, Common::PairHash>; +/** + * The ConstBufferLocker is a class use to interface the 3D and compute engines with the shader + * compiler. with it, the shader can obtain required data from GPU state and store it for disk + * shader compilation. + **/ class ConstBufferLocker { public: explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage); @@ -23,6 +28,8 @@ public: explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage, Tegra::Engines::ConstBufferEngineInterface& engine); + ~ConstBufferLocker(); + /// Retrieves a key from the locker, if it's registered, it will give the registered value, if /// not it will obtain it from maxwell3d and register it. std::optional ObtainKey(u32 buffer, u32 offset); diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index 6c698bcff..d47c63d9f 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -41,14 +41,10 @@ BlockBranchInfo MakeBranchInfo(Args&&... args) { return std::make_shared(T(std::forward(args)...)); } -bool BlockBranchInfoAreEqual(BlockBranchInfo first, BlockBranchInfo second) { - return false; //(*first) == (*second); -} - bool BlockBranchIsIgnored(BlockBranchInfo first) { bool ignore = false; if (std::holds_alternative(*first)) { - auto branch = std::get_if(first.get()); + const auto branch = std::get_if(first.get()); ignore = branch->ignore; } return ignore; @@ -151,10 +147,10 @@ std::optional TrackBranchIndirectInfo(const CFGRebuildState& const Instruction instr = {state.program_code[pos]}; const auto opcode = OpCode::Decode(instr); if (opcode->get().GetId() != OpCode::Id::BRX) { - return {}; + return std::nullopt; } if (instr.brx.constant_buffer != 0) { - return {}; + return std::nullopt; } track_register = instr.gpr8.Value(); result.relative_position = instr.brx.GetBranchExtend(); @@ -172,8 +168,8 @@ std::optional TrackBranchIndirectInfo(const CFGRebuildState& if (opcode->get().GetId() == OpCode::Id::LD_C) { if (instr.gpr0.Value() == track_register && instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single) { - result.buffer = instr.cbuf36.index; - result.offset = instr.cbuf36.GetOffset(); + result.buffer = instr.cbuf36.index.Value(); + result.offset = static_cast(instr.cbuf36.GetOffset()); track_register = instr.gpr8.Value(); pos--; found_track = true; @@ -184,7 +180,7 @@ std::optional TrackBranchIndirectInfo(const CFGRebuildState& } if (!found_track) { - return {}; + return std::nullopt; } found_track = false; @@ -194,7 +190,7 @@ std::optional TrackBranchIndirectInfo(const CFGRebuildState& pos--; continue; } - const Instruction instr = {state.program_code[pos]}; + const Instruction instr = state.program_code[pos]; const auto opcode = OpCode::Decode(instr); if (opcode->get().GetId() == OpCode::Id::SHL_IMM) { if (instr.gpr0.Value() == track_register) { @@ -208,7 +204,7 @@ std::optional TrackBranchIndirectInfo(const CFGRebuildState& } if (!found_track) { - return {}; + return std::nullopt; } found_track = false; @@ -218,7 +214,7 @@ std::optional TrackBranchIndirectInfo(const CFGRebuildState& pos--; continue; } - const Instruction instr = {state.program_code[pos]}; + const Instruction instr = state.program_code[pos]; const auto opcode = OpCode::Decode(instr); if (opcode->get().GetId() == OpCode::Id::IMNMX_IMM) { if (instr.gpr0.Value() == track_register) { @@ -233,9 +229,9 @@ std::optional TrackBranchIndirectInfo(const CFGRebuildState& } if (!found_track) { - return {}; + return std::nullopt; } - return {result}; + return result; } std::pair ParseCode(CFGRebuildState& state, u32 address) { @@ -440,8 +436,8 @@ std::pair ParseCode(CFGRebuildState& state, u32 address) branches.emplace_back(value, target); } parse_info.end_address = offset; - parse_info.branch_info = - MakeBranchInfo(static_cast(instr.gpr8.Value()), branches); + parse_info.branch_info = MakeBranchInfo( + static_cast(instr.gpr8.Value()), std::move(branches)); return {ParseResult::ControlCaught, parse_info}; } else { @@ -486,7 +482,7 @@ bool TryInspectAddress(CFGRebuildState& state) { current_block.end = address - 1; new_block.branch = current_block.branch; BlockBranchInfo forward_branch = MakeBranchInfo(); - auto branch = std::get_if(forward_branch.get()); + const auto branch = std::get_if(forward_branch.get()); branch->address = address; branch->ignore = true; current_block.branch = forward_branch; @@ -504,7 +500,7 @@ bool TryInspectAddress(CFGRebuildState& state) { BlockInfo& block_info = CreateBlockInfo(state, address, parse_info.end_address); block_info.branch = parse_info.branch_info; if (std::holds_alternative(*block_info.branch)) { - auto branch = std::get_if(block_info.branch.get()); + const auto branch = std::get_if(block_info.branch.get()); if (branch->condition.IsUnconditional()) { return true; } @@ -550,7 +546,7 @@ bool TryQuery(CFGRebuildState& state) { gather_labels(q2.ssy_stack, state.ssy_labels, block); gather_labels(q2.pbk_stack, state.pbk_labels, block); if (std::holds_alternative(*block.branch)) { - auto branch = std::get_if(block.branch.get()); + const auto branch = std::get_if(block.branch.get()); if (!branch->condition.IsUnconditional()) { q2.address = block.end + 1; state.queries.push_back(q2); @@ -573,8 +569,8 @@ bool TryQuery(CFGRebuildState& state) { state.queries.push_back(std::move(conditional_query)); return true; } - auto multi_branch = std::get_if(block.branch.get()); - for (auto& branch_case : multi_branch->branches) { + const auto multi_branch = std::get_if(block.branch.get()); + for (const auto& branch_case : multi_branch->branches) { Query conditional_query{q2}; conditional_query.address = branch_case.address; state.queries.push_back(std::move(conditional_query)); @@ -612,7 +608,7 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) { return MakeExpr(true); }); if (std::holds_alternative(*branch_info)) { - auto branch = std::get_if(branch_info.get()); + const auto branch = std::get_if(branch_info.get()); if (branch->address < 0) { if (branch->kill) { mm.InsertReturn(get_expr(branch->condition), true); @@ -624,8 +620,8 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) { mm.InsertGoto(get_expr(branch->condition), branch->address); return; } - auto multi_branch = std::get_if(branch_info.get()); - for (auto& branch_case : multi_branch->branches) { + const auto multi_branch = std::get_if(branch_info.get()); + for (const auto& branch_case : multi_branch->branches) { mm.InsertGoto(MakeExpr(multi_branch->gpr, branch_case.cmp_value), branch_case.address); } diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index 288ee68af..5304998b9 100644 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h @@ -51,6 +51,10 @@ public: std::tie(b.condition, b.address, b.kill, b.is_sync, b.is_brk, b.ignore); } + bool operator!=(const SingleBranch& b) const { + return !operator==(b); + } + Condition condition{}; s32 address{exit_branch}; bool kill{}; @@ -67,7 +71,7 @@ struct CaseBranch { class MultiBranch { public: - MultiBranch(u32 gpr, std::vector& branches) + MultiBranch(u32 gpr, std::vector&& branches) : gpr{gpr}, branches{std::move(branches)} {} u32 gpr{}; diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index e1afa4582..21fb9cb83 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp @@ -155,7 +155,7 @@ void ShaderIR::Decode() { [[fallthrough]]; case CompileDepth::BruteForce: { coverage_begin = main_offset; - const u32 shader_end = program_code.size(); + const std::size_t shader_end = program_code.size(); coverage_end = shader_end; for (u32 label = main_offset; label < shader_end; label++) { basic_blocks.insert({label, DecodeRange(label, label + 1)}); diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index c369e23ad..f33e9c67c 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp @@ -284,7 +284,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, std::optional sampler_info) { - const auto offset = static_cast(sampler.index.Value()); + const auto offset = static_cast(sampler.index.Value()); Tegra::Shader::TextureType type; bool is_array; @@ -293,17 +293,14 @@ const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, type = sampler_info->type; is_array = sampler_info->is_array; is_shadow = sampler_info->is_shadow; + } else if (auto sampler = locker.ObtainBoundSampler(offset); sampler) { + type = sampler->texture_type.Value(); + is_array = sampler->is_array.Value() != 0; + is_shadow = sampler->is_shadow.Value() != 0; } else { - auto sampler = locker.ObtainBoundSampler(offset); - if (sampler) { - type = sampler->texture_type.Value(); - is_array = sampler->is_array.Value() != 0; - is_shadow = sampler->is_shadow.Value() != 0; - } else { - type = Tegra::Shader::TextureType::Texture2D; - is_array = false; - is_shadow = false; - } + type = Tegra::Shader::TextureType::Texture2D; + is_array = false; + is_shadow = false; } // If this sampler has already been used, return the existing mapping. @@ -320,7 +317,7 @@ const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, const std::size_t next_index = used_samplers.size(); const Sampler entry{offset, next_index, type, is_array, is_shadow}; return *used_samplers.emplace(entry).first; -} +} // namespace VideoCommon::Shader const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg, std::optional sampler_info) { @@ -336,17 +333,14 @@ const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg, type = sampler_info->type; is_array = sampler_info->is_array; is_shadow = sampler_info->is_shadow; + } else if (auto sampler = locker.ObtainBindlessSampler(cbuf_index, cbuf_offset); sampler) { + type = sampler->texture_type.Value(); + is_array = sampler->is_array.Value() != 0; + is_shadow = sampler->is_shadow.Value() != 0; } else { - auto sampler = locker.ObtainBindlessSampler(cbuf_index, cbuf_offset); - if (sampler) { - type = sampler->texture_type.Value(); - is_array = sampler->is_array.Value() != 0; - is_shadow = sampler->is_shadow.Value() != 0; - } else { - type = Tegra::Shader::TextureType::Texture2D; - is_array = false; - is_shadow = false; - } + type = Tegra::Shader::TextureType::Texture2D; + is_array = false; + is_shadow = false; } // If this sampler has already been used, return the existing mapping. diff --git a/src/video_core/shader/expr.h b/src/video_core/shader/expr.h index e41d23e93..4e8264367 100644 --- a/src/video_core/shader/expr.h +++ b/src/video_core/shader/expr.h @@ -127,6 +127,10 @@ public: return gpr == b.gpr && value == b.value; } + bool operator!=(const ExprGprEqual& b) const { + return !operator==(b); + } + u32 gpr; u32 value; }; -- cgit v1.2.3