diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 4bc3da632..d59e34dae 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -557,7 +557,7 @@ std::optional GetCurrentDir() { #endif free(dir); return strDir; -} +} // namespace FileUtil bool SetCurrentDir(const std::string& directory) { #ifdef _WIN32 diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 693c74cc3..9e436b0f5 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -889,16 +889,17 @@ bool exec_shader(); )"; } -std::optional 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 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 {}; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index ea86386ab..67499960e 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h @@ -12,7 +12,10 @@ namespace OpenGL::ShaderDecompiler { using RegGetter = std::function; -using ProgramResult = std::string; + +struct ProgramResult { + std::string code; +}; std::string GetCommonDeclarations(); 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 adf1cc885..66a06ceaf 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -270,6 +270,12 @@ ShaderDiskCache::LoadPrecompiledFile(FileUtil::IOFile& file) { } std::optional ShaderDiskCache::LoadDecompiledEntry() { + + bool sanitize_mul; + if (!LoadObjectFromPrecompiled(sanitize_mul)) { + return {}; + } + u32 code_size{}; if (!LoadObjectFromPrecompiled(code_size)) { return {}; @@ -281,17 +287,19 @@ std::optional 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(PrecompiledEntryKind::Decompiled)) || - !SaveObjectToPrecompiled(unique_identifier) || - !SaveObjectToPrecompiled(static_cast(code.size())) || - !SaveArrayToPrecompiled(code.data(), code.size())) { + !SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(sanitize_mul) || + !SaveObjectToPrecompiled(static_cast(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(); 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 df01c0ee5..a3743d1a0 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h @@ -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 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; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 377b9915b..08015d5fc 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -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 GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, - const PicaVSConfig& config, bool separable_shader) { +std::optional 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 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 diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 436ce70e7..ebf54fe9f 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -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 { * @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 GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, - const PicaVSConfig& config, bool separable_shader); +std::optional 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 diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index a72ec5b96..89fb52f73 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -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(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 class ShaderCache { public: @@ -222,7 +224,7 @@ public: std::optional 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 (*CodeGenerator)(const Pica::Shader::ShaderSetup&, - const KeyConfigType&, bool), + std::optional (*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 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 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