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
free(dir);
return strDir;
}
} // namespace FileUtil
bool SetCurrentDir(const std::string& directory) {
#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,
u32 main_offset, const RegGetter& inputreg_getter,
const RegGetter& outputreg_getter, bool sanitize_mul) {
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 {};

View File

@ -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();

View File

@ -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();

View File

@ -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;

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;
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

View File

@ -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

View File

@ -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