mirror of
https://github.com/citra-emu/citra.git
synced 2024-12-18 14:00:05 +00:00
Only load precompiled shaders if their sanitize_mul setting matches
This commit is contained in:
parent
6945b6539f
commit
cf4125a6a5
@ -557,7 +557,7 @@ std::optional<std::string> GetCurrentDir() {
|
||||
#endif
|
||||
free(dir);
|
||||
return strDir;
|
||||
}
|
||||
} // namespace FileUtil
|
||||
|
||||
bool SetCurrentDir(const std::string& directory) {
|
||||
#ifdef _WIN32
|
||||
|
@ -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…
Reference in New Issue
Block a user