citra-emu
/
citra
Archived
1
0
Fork 0

Only load precompiled shaders if their sanitize_mul setting matches

This commit is contained in:
James Rowe 2020-01-15 21:10:37 -07:00
parent 6945b6539f
commit cf4125a6a5
8 changed files with 85 additions and 50 deletions

View File

@ -557,7 +557,7 @@ std::optional<std::string> GetCurrentDir() {
#endif #endif
free(dir); free(dir);
return strDir; return strDir;
} } // namespace FileUtil
bool SetCurrentDir(const std::string& directory) { bool SetCurrentDir(const std::string& directory) {
#ifdef _WIN32 #ifdef _WIN32

View File

@ -889,16 +889,17 @@ bool exec_shader();
)"; )";
} }
std::optional<std::string> DecompileProgram(const Pica::Shader::ProgramCode& program_code, std::optional<ProgramResult> DecompileProgram(const Pica::Shader::ProgramCode& program_code,
const Pica::Shader::SwizzleData& swizzle_data, const Pica::Shader::SwizzleData& swizzle_data,
u32 main_offset, const RegGetter& inputreg_getter, u32 main_offset, const RegGetter& inputreg_getter,
const RegGetter& outputreg_getter, bool sanitize_mul) { const RegGetter& outputreg_getter,
bool sanitize_mul) {
try { try {
auto subroutines = ControlFlowAnalyzer(program_code, main_offset).MoveSubroutines(); auto subroutines = ControlFlowAnalyzer(program_code, main_offset).MoveSubroutines();
GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset, GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset,
inputreg_getter, outputreg_getter, sanitize_mul); inputreg_getter, outputreg_getter, sanitize_mul);
return generator.MoveShaderCode(); return {ProgramResult{generator.MoveShaderCode()}};
} catch (const DecompileFail& exception) { } catch (const DecompileFail& exception) {
LOG_INFO(HW_GPU, "Shader decompilation failed: {}", exception.what()); LOG_INFO(HW_GPU, "Shader decompilation failed: {}", exception.what());
return {}; return {};

View File

@ -12,7 +12,10 @@
namespace OpenGL::ShaderDecompiler { namespace OpenGL::ShaderDecompiler {
using RegGetter = std::function<std::string(u32)>; using RegGetter = std::function<std::string(u32)>;
using ProgramResult = std::string;
struct ProgramResult {
std::string code;
};
std::string GetCommonDeclarations(); std::string GetCommonDeclarations();

View File

@ -270,6 +270,12 @@ ShaderDiskCache::LoadPrecompiledFile(FileUtil::IOFile& file) {
} }
std::optional<ShaderDiskCacheDecompiled> ShaderDiskCache::LoadDecompiledEntry() { std::optional<ShaderDiskCacheDecompiled> ShaderDiskCache::LoadDecompiledEntry() {
bool sanitize_mul;
if (!LoadObjectFromPrecompiled(sanitize_mul)) {
return {};
}
u32 code_size{}; u32 code_size{};
if (!LoadObjectFromPrecompiled(code_size)) { if (!LoadObjectFromPrecompiled(code_size)) {
return {}; return {};
@ -281,17 +287,19 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCache::LoadDecompiledEntry()
} }
ShaderDiskCacheDecompiled entry; ShaderDiskCacheDecompiled entry;
entry.code = std::move(code); entry.result.code = std::move(code);
entry.sanitize_mul = sanitize_mul;
return entry; return entry;
} }
bool ShaderDiskCache::SaveDecompiledFile(u64 unique_identifier, bool ShaderDiskCache::SaveDecompiledFile(u64 unique_identifier,
const ShaderDecompiler::ProgramResult& code) { const ShaderDecompiler::ProgramResult& result,
bool sanitize_mul) {
if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) || if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) ||
!SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(sanitize_mul) ||
!SaveObjectToPrecompiled(static_cast<u32>(code.size())) || !SaveObjectToPrecompiled(static_cast<u32>(result.code.size())) ||
!SaveArrayToPrecompiled(code.data(), code.size())) { !SaveArrayToPrecompiled(result.code.data(), result.code.size())) {
return false; return false;
} }
@ -338,7 +346,8 @@ void ShaderDiskCache::SaveRaw(const ShaderDiskCacheRaw& entry) {
} }
void ShaderDiskCache::SaveDecompiled(u64 unique_identifier, void ShaderDiskCache::SaveDecompiled(u64 unique_identifier,
const ShaderDecompiler::ProgramResult& code) { const ShaderDecompiler::ProgramResult& code,
bool sanitize_mul) {
if (!IsUsable()) if (!IsUsable())
return; return;
@ -346,7 +355,7 @@ void ShaderDiskCache::SaveDecompiled(u64 unique_identifier,
SavePrecompiledHeaderToVirtualPrecompiledCache(); SavePrecompiledHeaderToVirtualPrecompiledCache();
} }
if (!SaveDecompiledFile(unique_identifier, code)) { if (!SaveDecompiledFile(unique_identifier, code, sanitize_mul)) {
LOG_ERROR(Render_OpenGL, LOG_ERROR(Render_OpenGL,
"Failed to save decompiled entry to the precompiled file - removing"); "Failed to save decompiled entry to the precompiled file - removing");
InvalidatePrecompiled(); InvalidatePrecompiled();

View File

@ -78,7 +78,8 @@ private:
/// Contains decompiled data from a shader /// Contains decompiled data from a shader
struct ShaderDiskCacheDecompiled { struct ShaderDiskCacheDecompiled {
ShaderDecompiler::ProgramResult code; ShaderDecompiler::ProgramResult result;
bool sanitize_mul;
}; };
/// Contains an OpenGL dumped binary program /// Contains an OpenGL dumped binary program
@ -108,7 +109,8 @@ public:
void SaveRaw(const ShaderDiskCacheRaw& entry); void SaveRaw(const ShaderDiskCacheRaw& entry);
/// Saves a decompiled entry to the precompiled file. Does not check for collisions. /// 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. /// Saves a dump entry to the precompiled file. Does not check for collisions.
void SaveDump(u64 unique_identifier, GLuint program); void SaveDump(u64 unique_identifier, GLuint program);
@ -126,7 +128,8 @@ private:
std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(); std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry();
/// Saves a decompiled entry to the passed file. Returns true on success. /// 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 /// Returns if the cache can be used
bool IsUsable() const; bool IsUsable() const;

View File

@ -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; const auto& state = config.state;
std::string out = R"( 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 // 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) { if (state.alpha_test_func == FramebufferRegs::CompareFunc::Never) {
out += "discard; }"; out += "discard; }";
return out; return {out};
} }
// Append the scissor test // Append the scissor test
@ -1546,7 +1547,7 @@ vec4 secondary_fragment_color = vec4(0.0);
"VideoCore_Pica_UseGasMode", true); "VideoCore_Pica_UseGasMode", true);
LOG_CRITICAL(Render_OpenGL, "Unimplemented gas mode"); LOG_CRITICAL(Render_OpenGL, "Unimplemented gas mode");
out += "discard; }"; out += "discard; }";
return out; return {out};
} }
if (state.shadow_rendering) { if (state.shadow_rendering) {
@ -1584,10 +1585,10 @@ do {
out += "}"; out += "}";
return out; return {out};
} }
std::string GenerateTrivialVertexShader(bool separable_shader) { ShaderDecompiler::ProgramResult GenerateTrivialVertexShader(bool separable_shader) {
std::string out = ""; std::string out = "";
if (separable_shader) { if (separable_shader) {
out += "#extension GL_ARB_separate_shader_objects : enable\n"; 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, std::optional<ShaderDecompiler::ProgramResult> GenerateVertexShader(
const PicaVSConfig& config, bool separable_shader) { const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config, bool separable_shader) {
std::string out = ""; std::string out = "";
if (separable_shader) { if (separable_shader) {
out += "#extension GL_ARB_separate_shader_objects : enable\n"; 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) if (!program_source_opt)
return {}; return {};
std::string& program_source = *program_source_opt; std::string& program_source = program_source_opt->code;
out += R"( out += R"(
#define uniforms vs_uniforms #define uniforms vs_uniforms
@ -1696,7 +1697,7 @@ layout (std140) uniform vs_config {
out += program_source; out += program_source;
return out; return {{out}};
} }
static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config, bool separable_shader) { static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config, bool separable_shader) {
@ -1784,7 +1785,8 @@ void EmitPrim(Vertex vtx0, Vertex vtx1, Vertex vtx2) {
return out; return out;
}; };
std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool separable_shader) { ShaderDecompiler::ProgramResult GenerateFixedGeometryShader(const PicaFixedGSConfig& config,
bool separable_shader) {
std::string out = ""; std::string out = "";
if (separable_shader) { if (separable_shader) {
out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 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 += " EmitPrim(prim_buffer[0], prim_buffer[1], prim_buffer[2]);\n";
out += "}\n"; out += "}\n";
return out; return {out};
} }
} // namespace OpenGL } // namespace OpenGL

View File

@ -16,6 +16,10 @@
namespace OpenGL { namespace OpenGL {
namespace ShaderDecompiler {
struct ProgramResult;
}
enum class ProgramType : u32 { VS, GS, FS }; enum class ProgramType : u32 { VS, GS, FS };
enum Attributes { enum Attributes {
@ -202,20 +206,21 @@ struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigCommonRaw> {
* @param separable_shader generates shader that can be used for separate shader object * @param separable_shader generates shader that can be used for separate shader object
* @returns String of the shader source code * @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 * Generates the GLSL vertex shader program source code for the given VS program
* @returns String of the shader source code; boost::none on failure * @returns String of the shader source code; boost::none on failure
*/ */
std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, std::optional<ShaderDecompiler::ProgramResult> GenerateVertexShader(
const PicaVSConfig& config, bool separable_shader); 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 * Generates the GLSL fixed geometry shader program source code for non-GS PICA pipeline
* @returns String of the shader source code * @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 * 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 * @param separable_shader generates shader that can be used for separate shader object
* @returns String of the shader source code * @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 } // namespace OpenGL

View File

@ -10,18 +10,19 @@
#include "core/core.h" #include "core/core.h"
#include "video_core/renderer_opengl/gl_shader_disk_cache.h" #include "video_core/renderer_opengl/gl_shader_disk_cache.h"
#include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_manager.h"
#include "video_core/video_core.h"
namespace OpenGL { namespace OpenGL {
static u64 GetUniqueIdentifier(const Pica::Regs& regs, const ProgramCode& code) { 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)); u64 regs_uid = Common::ComputeHash64(regs.reg_array.data(), Pica::Regs::NUM_REGS * sizeof(u32));
boost::hash_combine(hash, regs_uid); boost::hash_combine(hash, regs_uid);
if (code.size() > 0) { if (code.size() > 0) {
u64 code_uid = Common::ComputeHash64(code.data(), code.size() * sizeof(u32)); u64 code_uid = Common::ComputeHash64(code.data(), code.size() * sizeof(u32));
boost::hash_combine(hash, code_uid); boost::hash_combine(hash, code_uid);
} }
return hash; return static_cast<u64>(hash);
} }
static OGLProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, static OGLProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
@ -200,7 +201,7 @@ private:
class TrivialVertexShader { class TrivialVertexShader {
public: public:
explicit TrivialVertexShader(bool separable) : program(separable) { 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 { GLuint Get() const {
return program.GetHandle(); return program.GetHandle();
@ -210,7 +211,8 @@ private:
OGLShaderStage program; OGLShaderStage program;
}; };
template <typename KeyConfigType, std::string (*CodeGenerator)(const KeyConfigType&, bool), template <typename KeyConfigType,
ShaderDecompiler::ProgramResult (*CodeGenerator)(const KeyConfigType&, bool),
GLenum ShaderType> GLenum ShaderType>
class ShaderCache { class ShaderCache {
public: public:
@ -222,7 +224,7 @@ public:
std::optional<ShaderDecompiler::ProgramResult> result{}; std::optional<ShaderDecompiler::ProgramResult> result{};
if (new_shader) { if (new_shader) {
result = CodeGenerator(config, separable); result = CodeGenerator(config, separable);
cached_shader.Create(result->c_str(), ShaderType); cached_shader.Create(result->code.c_str(), ShaderType);
} }
return {cached_shader.GetHandle(), result}; return {cached_shader.GetHandle(), result};
} }
@ -244,8 +246,8 @@ private:
// program buffer from the previous shader, which is hashed into the config, resulting several // program buffer from the previous shader, which is hashed into the config, resulting several
// different config values from the same shader program. // different config values from the same shader program.
template <typename KeyConfigType, template <typename KeyConfigType,
std::optional<std::string> (*CodeGenerator)(const Pica::Shader::ShaderSetup&, std::optional<ShaderDecompiler::ProgramResult> (*CodeGenerator)(
const KeyConfigType&, bool), const Pica::Shader::ShaderSetup&, const KeyConfigType&, bool),
GLenum ShaderType> GLenum ShaderType>
class ShaderDoubleCache { class ShaderDoubleCache {
public: public:
@ -261,11 +263,11 @@ public:
return {0, {}}; return {0, {}};
} }
std::string& program = *program_opt; std::string& program = program_opt->code;
auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable}); auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable});
OGLShaderStage& cached_shader = iter->second; OGLShaderStage& cached_shader = iter->second;
if (new_shader) { if (new_shader) {
result = program; result->code = program;
cached_shader.Create(program.c_str(), ShaderType); cached_shader.Create(program.c_str(), ShaderType);
} }
shader_map[key] = &cached_shader; shader_map[key] = &cached_shader;
@ -336,6 +338,7 @@ public:
}; };
bool is_amd; bool is_amd;
bool separable;
ShaderTuple current; ShaderTuple current;
@ -345,8 +348,6 @@ public:
FixedGeometryShaders fixed_geometry_shaders; FixedGeometryShaders fixed_geometry_shaders;
FragmentShaders fragment_shaders; FragmentShaders fragment_shaders;
bool separable;
std::unordered_map<ShaderTuple, OGLProgram, ShaderTuple::Hash> program_cache; std::unordered_map<ShaderTuple, OGLProgram, ShaderTuple::Hash> program_cache;
OGLPipeline pipeline; OGLPipeline pipeline;
ShaderDiskCache disk_cache; ShaderDiskCache disk_cache;
@ -401,7 +402,7 @@ void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs) {
u64 unique_identifier = GetUniqueIdentifier(regs, {}); u64 unique_identifier = GetUniqueIdentifier(regs, {});
ShaderDiskCacheRaw raw{unique_identifier, ProgramType::FS, regs, {}}; ShaderDiskCacheRaw raw{unique_identifier, ProgramType::FS, regs, {}};
disk_cache.SaveRaw(raw); 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 dump{dumps.find(unique_identifier)};
const auto decomp{decompiled.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; OGLProgram shader;
if (dump != dumps.end() && decomp != decompiled.end()) { 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) { if (raw.GetProgramType() == ProgramType::VS) {
auto [conf, setup] = BuildVSConfigFromRaw(raw); auto [conf, setup] = BuildVSConfigFromRaw(raw);
std::scoped_lock lock(mutex); 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)); std::move(shader));
} else if (raw.GetProgramType() == ProgramType::FS) { } else if (raw.GetProgramType() == ProgramType::FS) {
PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig()); PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig());
std::scoped_lock lock(mutex); 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 { } else {
// Unsupported shader type got stored somehow so nuke the cache // 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 auto& raw{raws[i]};
const u64 unique_identifier{raw.GetUniqueIdentifier()}; const u64 unique_identifier{raw.GetUniqueIdentifier()};
bool sanitize_mul = false;
GLuint handle{0}; GLuint handle{0};
std::optional<ShaderDecompiler::ProgramResult> result; std::optional<ShaderDecompiler::ProgramResult> result;
// Otherwise decompile and build the shader at boot and save the result to the // 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); auto [h, r] = impl->programmable_vertex_shaders.Get(conf, setup);
handle = h; handle = h;
result = r; result = r;
sanitize_mul = conf.state.sanitize_mul;
} else if (raw.GetProgramType() == ProgramType::FS) { } else if (raw.GetProgramType() == ProgramType::FS) {
PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig()); PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig());
std::scoped_lock lock(mutex); 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 this is a new shader, add it the precompiled cache
if (result) { if (result) {
disk_cache.SaveDecompiled(unique_identifier, *result); disk_cache.SaveDecompiled(unique_identifier, *result, sanitize_mul);
disk_cache.SaveDump(unique_identifier, handle); disk_cache.SaveDump(unique_identifier, handle);
precompiled_cache_altered = true; precompiled_cache_altered = true;
} }
@ -607,6 +618,6 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
if (precompiled_cache_altered) { if (precompiled_cache_altered) {
disk_cache.SaveVirtualPrecompiledFile(); disk_cache.SaveVirtualPrecompiledFile();
} }
} } // namespace OpenGL
} // namespace OpenGL } // namespace OpenGL