mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Only load precompiled shaders if their sanitize_mul setting matches
This commit is contained in:
		
							parent
							
								
									6945b6539f
								
							
						
					
					
						commit
						cf4125a6a5
					
				
					 8 changed files with 85 additions and 50 deletions
				
			
		|  | @ -889,16 +889,17 @@ bool exec_shader(); | |||
| )"; | ||||
| } | ||||
| 
 | ||||
| std::optional<std::string> DecompileProgram(const Pica::Shader::ProgramCode& program_code, | ||||
|                                             const Pica::Shader::SwizzleData& swizzle_data, | ||||
|                                             u32 main_offset, const RegGetter& inputreg_getter, | ||||
|                                             const RegGetter& outputreg_getter, bool sanitize_mul) { | ||||
| std::optional<ProgramResult> DecompileProgram(const Pica::Shader::ProgramCode& program_code, | ||||
|                                               const Pica::Shader::SwizzleData& swizzle_data, | ||||
|                                               u32 main_offset, const RegGetter& inputreg_getter, | ||||
|                                               const RegGetter& outputreg_getter, | ||||
|                                               bool sanitize_mul) { | ||||
| 
 | ||||
|     try { | ||||
|         auto subroutines = ControlFlowAnalyzer(program_code, main_offset).MoveSubroutines(); | ||||
|         GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset, | ||||
|                                 inputreg_getter, outputreg_getter, sanitize_mul); | ||||
|         return generator.MoveShaderCode(); | ||||
|         return {ProgramResult{generator.MoveShaderCode()}}; | ||||
|     } catch (const DecompileFail& exception) { | ||||
|         LOG_INFO(HW_GPU, "Shader decompilation failed: {}", exception.what()); | ||||
|         return {}; | ||||
|  |  | |||
|  | @ -12,7 +12,10 @@ | |||
| namespace OpenGL::ShaderDecompiler { | ||||
| 
 | ||||
| using RegGetter = std::function<std::string(u32)>; | ||||
| using ProgramResult = std::string; | ||||
| 
 | ||||
| struct ProgramResult { | ||||
|     std::string code; | ||||
| }; | ||||
| 
 | ||||
| std::string GetCommonDeclarations(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -270,6 +270,12 @@ ShaderDiskCache::LoadPrecompiledFile(FileUtil::IOFile& file) { | |||
| } | ||||
| 
 | ||||
| std::optional<ShaderDiskCacheDecompiled> ShaderDiskCache::LoadDecompiledEntry() { | ||||
| 
 | ||||
|     bool sanitize_mul; | ||||
|     if (!LoadObjectFromPrecompiled(sanitize_mul)) { | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     u32 code_size{}; | ||||
|     if (!LoadObjectFromPrecompiled(code_size)) { | ||||
|         return {}; | ||||
|  | @ -281,17 +287,19 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCache::LoadDecompiledEntry() | |||
|     } | ||||
| 
 | ||||
|     ShaderDiskCacheDecompiled entry; | ||||
|     entry.code = std::move(code); | ||||
|     entry.result.code = std::move(code); | ||||
|     entry.sanitize_mul = sanitize_mul; | ||||
| 
 | ||||
|     return entry; | ||||
| } | ||||
| 
 | ||||
| bool ShaderDiskCache::SaveDecompiledFile(u64 unique_identifier, | ||||
|                                          const ShaderDecompiler::ProgramResult& code) { | ||||
|                                          const ShaderDecompiler::ProgramResult& result, | ||||
|                                          bool sanitize_mul) { | ||||
|     if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) || | ||||
|         !SaveObjectToPrecompiled(unique_identifier) || | ||||
|         !SaveObjectToPrecompiled(static_cast<u32>(code.size())) || | ||||
|         !SaveArrayToPrecompiled(code.data(), code.size())) { | ||||
|         !SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(sanitize_mul) || | ||||
|         !SaveObjectToPrecompiled(static_cast<u32>(result.code.size())) || | ||||
|         !SaveArrayToPrecompiled(result.code.data(), result.code.size())) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|  | @ -338,7 +346,8 @@ void ShaderDiskCache::SaveRaw(const ShaderDiskCacheRaw& entry) { | |||
| } | ||||
| 
 | ||||
| void ShaderDiskCache::SaveDecompiled(u64 unique_identifier, | ||||
|                                      const ShaderDecompiler::ProgramResult& code) { | ||||
|                                      const ShaderDecompiler::ProgramResult& code, | ||||
|                                      bool sanitize_mul) { | ||||
|     if (!IsUsable()) | ||||
|         return; | ||||
| 
 | ||||
|  | @ -346,7 +355,7 @@ void ShaderDiskCache::SaveDecompiled(u64 unique_identifier, | |||
|         SavePrecompiledHeaderToVirtualPrecompiledCache(); | ||||
|     } | ||||
| 
 | ||||
|     if (!SaveDecompiledFile(unique_identifier, code)) { | ||||
|     if (!SaveDecompiledFile(unique_identifier, code, sanitize_mul)) { | ||||
|         LOG_ERROR(Render_OpenGL, | ||||
|                   "Failed to save decompiled entry to the precompiled file - removing"); | ||||
|         InvalidatePrecompiled(); | ||||
|  |  | |||
|  | @ -78,7 +78,8 @@ private: | |||
| 
 | ||||
| /// Contains decompiled data from a shader
 | ||||
| struct ShaderDiskCacheDecompiled { | ||||
|     ShaderDecompiler::ProgramResult code; | ||||
|     ShaderDecompiler::ProgramResult result; | ||||
|     bool sanitize_mul; | ||||
| }; | ||||
| 
 | ||||
| /// Contains an OpenGL dumped binary program
 | ||||
|  | @ -108,7 +109,8 @@ public: | |||
|     void SaveRaw(const ShaderDiskCacheRaw& entry); | ||||
| 
 | ||||
|     /// Saves a decompiled entry to the precompiled file. Does not check for collisions.
 | ||||
|     void SaveDecompiled(u64 unique_identifier, const ShaderDecompiler::ProgramResult& code); | ||||
|     void SaveDecompiled(u64 unique_identifier, const ShaderDecompiler::ProgramResult& code, | ||||
|                         bool sanitize_mul); | ||||
| 
 | ||||
|     /// Saves a dump entry to the precompiled file. Does not check for collisions.
 | ||||
|     void SaveDump(u64 unique_identifier, GLuint program); | ||||
|  | @ -126,7 +128,8 @@ private: | |||
|     std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(); | ||||
| 
 | ||||
|     /// Saves a decompiled entry to the passed file. Returns true on success.
 | ||||
|     bool SaveDecompiledFile(u64 unique_identifier, const ShaderDecompiler::ProgramResult& code); | ||||
|     bool SaveDecompiledFile(u64 unique_identifier, const ShaderDecompiler::ProgramResult& code, | ||||
|                             bool sanitize_mul); | ||||
| 
 | ||||
|     /// Returns if the cache can be used
 | ||||
|     bool IsUsable() const; | ||||
|  |  | |||
|  | @ -1231,7 +1231,8 @@ float ProcTexNoiseCoef(vec2 x) { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_shader) { | ||||
| ShaderDecompiler::ProgramResult GenerateFragmentShader(const PicaFSConfig& config, | ||||
|                                                        bool separable_shader) { | ||||
|     const auto& state = config.state; | ||||
| 
 | ||||
|     std::string out = R"( | ||||
|  | @ -1482,7 +1483,7 @@ vec4 secondary_fragment_color = vec4(0.0); | |||
|     // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
 | ||||
|     if (state.alpha_test_func == FramebufferRegs::CompareFunc::Never) { | ||||
|         out += "discard; }"; | ||||
|         return out; | ||||
|         return {out}; | ||||
|     } | ||||
| 
 | ||||
|     // Append the scissor test
 | ||||
|  | @ -1546,7 +1547,7 @@ vec4 secondary_fragment_color = vec4(0.0); | |||
|                                                                 "VideoCore_Pica_UseGasMode", true); | ||||
|         LOG_CRITICAL(Render_OpenGL, "Unimplemented gas mode"); | ||||
|         out += "discard; }"; | ||||
|         return out; | ||||
|         return {out}; | ||||
|     } | ||||
| 
 | ||||
|     if (state.shadow_rendering) { | ||||
|  | @ -1584,10 +1585,10 @@ do { | |||
| 
 | ||||
|     out += "}"; | ||||
| 
 | ||||
|     return out; | ||||
|     return {out}; | ||||
| } | ||||
| 
 | ||||
| std::string GenerateTrivialVertexShader(bool separable_shader) { | ||||
| ShaderDecompiler::ProgramResult GenerateTrivialVertexShader(bool separable_shader) { | ||||
|     std::string out = ""; | ||||
|     if (separable_shader) { | ||||
|         out += "#extension GL_ARB_separate_shader_objects : enable\n"; | ||||
|  | @ -1630,11 +1631,11 @@ void main() { | |||
| } | ||||
| )"; | ||||
| 
 | ||||
|     return out; | ||||
|     return {out}; | ||||
| } | ||||
| 
 | ||||
| std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, | ||||
|                                                 const PicaVSConfig& config, bool separable_shader) { | ||||
| std::optional<ShaderDecompiler::ProgramResult> GenerateVertexShader( | ||||
|     const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config, bool separable_shader) { | ||||
|     std::string out = ""; | ||||
|     if (separable_shader) { | ||||
|         out += "#extension GL_ARB_separate_shader_objects : enable\n"; | ||||
|  | @ -1664,7 +1665,7 @@ std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& | |||
|     if (!program_source_opt) | ||||
|         return {}; | ||||
| 
 | ||||
|     std::string& program_source = *program_source_opt; | ||||
|     std::string& program_source = program_source_opt->code; | ||||
| 
 | ||||
|     out += R"( | ||||
| #define uniforms vs_uniforms | ||||
|  | @ -1696,7 +1697,7 @@ layout (std140) uniform vs_config { | |||
| 
 | ||||
|     out += program_source; | ||||
| 
 | ||||
|     return out; | ||||
|     return {{out}}; | ||||
| } | ||||
| 
 | ||||
| static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config, bool separable_shader) { | ||||
|  | @ -1784,7 +1785,8 @@ void EmitPrim(Vertex vtx0, Vertex vtx1, Vertex vtx2) { | |||
|     return out; | ||||
| }; | ||||
| 
 | ||||
| std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool separable_shader) { | ||||
| ShaderDecompiler::ProgramResult GenerateFixedGeometryShader(const PicaFixedGSConfig& config, | ||||
|                                                             bool separable_shader) { | ||||
|     std::string out = ""; | ||||
|     if (separable_shader) { | ||||
|         out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; | ||||
|  | @ -1814,6 +1816,6 @@ void main() { | |||
|     out += "    EmitPrim(prim_buffer[0], prim_buffer[1], prim_buffer[2]);\n"; | ||||
|     out += "}\n"; | ||||
| 
 | ||||
|     return out; | ||||
|     return {out}; | ||||
| } | ||||
| } // namespace OpenGL
 | ||||
|  |  | |||
|  | @ -16,6 +16,10 @@ | |||
| 
 | ||||
| namespace OpenGL { | ||||
| 
 | ||||
| namespace ShaderDecompiler { | ||||
| struct ProgramResult; | ||||
| } | ||||
| 
 | ||||
| enum class ProgramType : u32 { VS, GS, FS }; | ||||
| 
 | ||||
| enum Attributes { | ||||
|  | @ -202,20 +206,21 @@ struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigCommonRaw> { | |||
|  * @param separable_shader generates shader that can be used for separate shader object | ||||
|  * @returns String of the shader source code | ||||
|  */ | ||||
| std::string GenerateTrivialVertexShader(bool separable_shader); | ||||
| ShaderDecompiler::ProgramResult GenerateTrivialVertexShader(bool separable_shader); | ||||
| 
 | ||||
| /**
 | ||||
|  * Generates the GLSL vertex shader program source code for the given VS program | ||||
|  * @returns String of the shader source code; boost::none on failure | ||||
|  */ | ||||
| std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, | ||||
|                                                 const PicaVSConfig& config, bool separable_shader); | ||||
| std::optional<ShaderDecompiler::ProgramResult> GenerateVertexShader( | ||||
|     const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config, bool separable_shader); | ||||
| 
 | ||||
| /*
 | ||||
|  * Generates the GLSL fixed geometry shader program source code for non-GS PICA pipeline | ||||
|  * @returns String of the shader source code | ||||
|  */ | ||||
| std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool separable_shader); | ||||
| ShaderDecompiler::ProgramResult GenerateFixedGeometryShader(const PicaFixedGSConfig& config, | ||||
|                                                             bool separable_shader); | ||||
| 
 | ||||
| /**
 | ||||
|  * Generates the GLSL fragment shader program source code for the current Pica state | ||||
|  | @ -224,7 +229,8 @@ std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool se | |||
|  * @param separable_shader generates shader that can be used for separate shader object | ||||
|  * @returns String of the shader source code | ||||
|  */ | ||||
| std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_shader); | ||||
| ShaderDecompiler::ProgramResult GenerateFragmentShader(const PicaFSConfig& config, | ||||
|                                                        bool separable_shader); | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,18 +10,19 @@ | |||
| #include "core/core.h" | ||||
| #include "video_core/renderer_opengl/gl_shader_disk_cache.h" | ||||
| #include "video_core/renderer_opengl/gl_shader_manager.h" | ||||
| #include "video_core/video_core.h" | ||||
| 
 | ||||
| namespace OpenGL { | ||||
| 
 | ||||
| static u64 GetUniqueIdentifier(const Pica::Regs& regs, const ProgramCode& code) { | ||||
|     u64 hash = 0; | ||||
|     std::size_t hash = 0; | ||||
|     u64 regs_uid = Common::ComputeHash64(regs.reg_array.data(), Pica::Regs::NUM_REGS * sizeof(u32)); | ||||
|     boost::hash_combine(hash, regs_uid); | ||||
|     if (code.size() > 0) { | ||||
|         u64 code_uid = Common::ComputeHash64(code.data(), code.size() * sizeof(u32)); | ||||
|         boost::hash_combine(hash, code_uid); | ||||
|     } | ||||
|     return hash; | ||||
|     return static_cast<u64>(hash); | ||||
| } | ||||
| 
 | ||||
| static OGLProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, | ||||
|  | @ -200,7 +201,7 @@ private: | |||
| class TrivialVertexShader { | ||||
| public: | ||||
|     explicit TrivialVertexShader(bool separable) : program(separable) { | ||||
|         program.Create(GenerateTrivialVertexShader(separable).c_str(), GL_VERTEX_SHADER); | ||||
|         program.Create(GenerateTrivialVertexShader(separable).code.c_str(), GL_VERTEX_SHADER); | ||||
|     } | ||||
|     GLuint Get() const { | ||||
|         return program.GetHandle(); | ||||
|  | @ -210,7 +211,8 @@ private: | |||
|     OGLShaderStage program; | ||||
| }; | ||||
| 
 | ||||
| template <typename KeyConfigType, std::string (*CodeGenerator)(const KeyConfigType&, bool), | ||||
| template <typename KeyConfigType, | ||||
|           ShaderDecompiler::ProgramResult (*CodeGenerator)(const KeyConfigType&, bool), | ||||
|           GLenum ShaderType> | ||||
| class ShaderCache { | ||||
| public: | ||||
|  | @ -222,7 +224,7 @@ public: | |||
|         std::optional<ShaderDecompiler::ProgramResult> result{}; | ||||
|         if (new_shader) { | ||||
|             result = CodeGenerator(config, separable); | ||||
|             cached_shader.Create(result->c_str(), ShaderType); | ||||
|             cached_shader.Create(result->code.c_str(), ShaderType); | ||||
|         } | ||||
|         return {cached_shader.GetHandle(), result}; | ||||
|     } | ||||
|  | @ -244,8 +246,8 @@ private: | |||
| // program buffer from the previous shader, which is hashed into the config, resulting several
 | ||||
| // different config values from the same shader program.
 | ||||
| template <typename KeyConfigType, | ||||
|           std::optional<std::string> (*CodeGenerator)(const Pica::Shader::ShaderSetup&, | ||||
|                                                       const KeyConfigType&, bool), | ||||
|           std::optional<ShaderDecompiler::ProgramResult> (*CodeGenerator)( | ||||
|               const Pica::Shader::ShaderSetup&, const KeyConfigType&, bool), | ||||
|           GLenum ShaderType> | ||||
| class ShaderDoubleCache { | ||||
| public: | ||||
|  | @ -261,11 +263,11 @@ public: | |||
|                 return {0, {}}; | ||||
|             } | ||||
| 
 | ||||
|             std::string& program = *program_opt; | ||||
|             std::string& program = program_opt->code; | ||||
|             auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable}); | ||||
|             OGLShaderStage& cached_shader = iter->second; | ||||
|             if (new_shader) { | ||||
|                 result = program; | ||||
|                 result->code = program; | ||||
|                 cached_shader.Create(program.c_str(), ShaderType); | ||||
|             } | ||||
|             shader_map[key] = &cached_shader; | ||||
|  | @ -336,6 +338,7 @@ public: | |||
|     }; | ||||
| 
 | ||||
|     bool is_amd; | ||||
|     bool separable; | ||||
| 
 | ||||
|     ShaderTuple current; | ||||
| 
 | ||||
|  | @ -345,8 +348,6 @@ public: | |||
|     FixedGeometryShaders fixed_geometry_shaders; | ||||
| 
 | ||||
|     FragmentShaders fragment_shaders; | ||||
| 
 | ||||
|     bool separable; | ||||
|     std::unordered_map<ShaderTuple, OGLProgram, ShaderTuple::Hash> program_cache; | ||||
|     OGLPipeline pipeline; | ||||
|     ShaderDiskCache disk_cache; | ||||
|  | @ -401,7 +402,7 @@ void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs) { | |||
|         u64 unique_identifier = GetUniqueIdentifier(regs, {}); | ||||
|         ShaderDiskCacheRaw raw{unique_identifier, ProgramType::FS, regs, {}}; | ||||
|         disk_cache.SaveRaw(raw); | ||||
|         disk_cache.SaveDecompiled(unique_identifier, *result); | ||||
|         disk_cache.SaveDecompiled(unique_identifier, *result, false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -489,6 +490,12 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 
 | ||||
|                 const auto dump{dumps.find(unique_identifier)}; | ||||
|                 const auto decomp{decompiled.find(unique_identifier)}; | ||||
| 
 | ||||
|                 // Only load this shader if its sanitize_mul setting matches
 | ||||
|                 if (decomp->second.sanitize_mul == VideoCore::g_hw_shader_accurate_mul) { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 OGLProgram shader; | ||||
| 
 | ||||
|                 if (dump != dumps.end() && decomp != decompiled.end()) { | ||||
|  | @ -505,12 +512,14 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
|                     if (raw.GetProgramType() == ProgramType::VS) { | ||||
|                         auto [conf, setup] = BuildVSConfigFromRaw(raw); | ||||
|                         std::scoped_lock lock(mutex); | ||||
|                         impl->programmable_vertex_shaders.Inject(conf, decomp->second.code, | ||||
| 
 | ||||
|                         impl->programmable_vertex_shaders.Inject(conf, decomp->second.result.code, | ||||
|                                                                  std::move(shader)); | ||||
|                     } else if (raw.GetProgramType() == ProgramType::FS) { | ||||
|                         PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig()); | ||||
|                         std::scoped_lock lock(mutex); | ||||
|                         impl->fragment_shaders.Inject(conf, decomp->second.code, std::move(shader)); | ||||
|                         impl->fragment_shaders.Inject(conf, decomp->second.result.code, | ||||
|                                                       std::move(shader)); | ||||
|                     } else { | ||||
|                         // Unsupported shader type got stored somehow so nuke the cache
 | ||||
| 
 | ||||
|  | @ -554,6 +563,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
|             const auto& raw{raws[i]}; | ||||
|             const u64 unique_identifier{raw.GetUniqueIdentifier()}; | ||||
| 
 | ||||
|             bool sanitize_mul = false; | ||||
|             GLuint handle{0}; | ||||
|             std::optional<ShaderDecompiler::ProgramResult> result; | ||||
|             // Otherwise decompile and build the shader at boot and save the result to the
 | ||||
|  | @ -566,6 +576,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
|                 auto [h, r] = impl->programmable_vertex_shaders.Get(conf, setup); | ||||
|                 handle = h; | ||||
|                 result = r; | ||||
|                 sanitize_mul = conf.state.sanitize_mul; | ||||
|             } else if (raw.GetProgramType() == ProgramType::FS) { | ||||
|                 PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig()); | ||||
|                 std::scoped_lock lock(mutex); | ||||
|  | @ -587,7 +598,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
|             } | ||||
|             // If this is a new shader, add it the precompiled cache
 | ||||
|             if (result) { | ||||
|                 disk_cache.SaveDecompiled(unique_identifier, *result); | ||||
|                 disk_cache.SaveDecompiled(unique_identifier, *result, sanitize_mul); | ||||
|                 disk_cache.SaveDump(unique_identifier, handle); | ||||
|                 precompiled_cache_altered = true; | ||||
|             } | ||||
|  | @ -607,6 +618,6 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
|     if (precompiled_cache_altered) { | ||||
|         disk_cache.SaveVirtualPrecompiledFile(); | ||||
|     } | ||||
| } | ||||
| } // namespace OpenGL
 | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue