mirror of https://github.com/snes9xgit/snes9x.git
vulkan/slang: Allow a cache for spv.
This commit is contained in:
parent
1c787cd309
commit
8907541f28
|
@ -22,6 +22,26 @@
|
|||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::string get_cache_dir()
|
||||
{
|
||||
char *env_home = getenv("HOME");
|
||||
char *env_cache = getenv("XDG_CACHE_HOME");
|
||||
|
||||
std::string dir;
|
||||
|
||||
if (env_cache)
|
||||
dir = std::string(env_cache) + "/snes9x";
|
||||
|
||||
else if (env_home)
|
||||
dir = std::string(env_home) + "/.cache/snes9x";
|
||||
|
||||
if (dir.empty())
|
||||
return dir;
|
||||
|
||||
fs::create_directories(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
std::string get_config_dir()
|
||||
{
|
||||
// Find config directory
|
||||
|
|
|
@ -159,6 +159,7 @@ class Snes9xConfig
|
|||
int joystick_threshold;
|
||||
};
|
||||
|
||||
std::string get_cache_dir();
|
||||
std::string get_config_dir();
|
||||
std::string get_config_file_name();
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ int S9xVulkanDisplayDriver::init()
|
|||
if (!gui_config->shader_filename.empty() && gui_config->use_shaders)
|
||||
{
|
||||
shaderchain = std::make_unique<Vulkan::ShaderChain>(context.get());
|
||||
if (!shaderchain->load_shader_preset(gui_config->shader_filename))
|
||||
if (!shaderchain->load_shader_preset(gui_config->shader_filename, get_cache_dir()))
|
||||
{
|
||||
fmt::print("Couldn't load shader preset file\n");
|
||||
shaderchain = nullptr;
|
||||
|
@ -94,6 +94,8 @@ int S9xVulkanDisplayDriver::init()
|
|||
window->enable_widget("shader_parameters_item", true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
simple_output = std::make_unique<Vulkan::SimpleOutput>(context.get(), vk::Format::eR5G6B5UnormPack16);
|
||||
|
|
|
@ -107,6 +107,10 @@ bool SlangPreset::load_preset_file(string filename)
|
|||
}
|
||||
}
|
||||
|
||||
if (!cache_directory.empty())
|
||||
for (auto &p : passes)
|
||||
p.set_cache_directory(cache_directory);
|
||||
|
||||
std::vector<std::future<bool>> futures;
|
||||
for (size_t i = 0; i < passes.size(); i++)
|
||||
{
|
||||
|
@ -135,6 +139,11 @@ bool SlangPreset::load_preset_file(string filename)
|
|||
return true;
|
||||
}
|
||||
|
||||
void SlangPreset::set_cache_directory(std::string filename)
|
||||
{
|
||||
cache_directory = filename;
|
||||
}
|
||||
|
||||
/*
|
||||
Aggregates the parameters from individual stages and separate shader files,
|
||||
resolving duplicates.
|
||||
|
|
|
@ -18,6 +18,7 @@ struct SlangPreset
|
|||
bool match_sampler_semantic(const std::string &name, int pass, SlangShader::Sampler::Type &type, int &specifier);
|
||||
void gather_parameters();
|
||||
bool save_to_file(std::string filename);
|
||||
void set_cache_directory(std::string filename);
|
||||
|
||||
struct Texture
|
||||
{
|
||||
|
@ -34,4 +35,5 @@ struct SlangPreset
|
|||
int oldest_previous_frame;
|
||||
bool uses_feedback;
|
||||
bool last_pass_uses_feedback;
|
||||
std::string cache_directory;
|
||||
};
|
|
@ -10,6 +10,8 @@
|
|||
#include "../external/glslang/glslang/Public/ShaderLang.h"
|
||||
#include "../external/glslang/SPIRV/GlslangToSpv.h"
|
||||
#include "../external/glslang/StandAlone/ResourceLimits.h"
|
||||
#include "../external/xxh64/xxh64.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
@ -23,6 +25,11 @@ SlangShader::~SlangShader()
|
|||
{
|
||||
}
|
||||
|
||||
void SlangShader::set_cache_directory(std::string dir)
|
||||
{
|
||||
cache_directory = dir;
|
||||
}
|
||||
|
||||
/*
|
||||
Recursively load shader file and included files into memory, applying
|
||||
#include and #pragma directives. Will strip all directives except
|
||||
|
@ -231,44 +238,76 @@ std::vector<uint32_t> SlangShader::generate_spirv(std::string shader_string, std
|
|||
|
||||
}
|
||||
|
||||
std::string SlangShader::get_hash_filename(const std::string &shader_string)
|
||||
{
|
||||
uint64_t hash = xxh64::hash(shader_string.c_str(), fragment_shader_string.size(), 56);
|
||||
char hash_hex_string[32];
|
||||
snprintf(hash_hex_string, 32, "%016lx.spv", hash);
|
||||
auto cache_path = std::filesystem::path(cache_directory);
|
||||
cache_path /= hash_hex_string;
|
||||
|
||||
return cache_path.string();
|
||||
}
|
||||
|
||||
static const char *header = "SPVCACHE1";
|
||||
static size_t header_size = 9;
|
||||
|
||||
bool SlangShader::load_cache(std::string hash_filename, std::vector<uint32_t> &output)
|
||||
{
|
||||
if (cache_directory.empty())
|
||||
return false;
|
||||
if (!std::filesystem::exists(hash_filename))
|
||||
return false;
|
||||
auto size = std::filesystem::file_size(hash_filename);
|
||||
size -= header_size;
|
||||
if ((size & 3) != 0)
|
||||
return false;
|
||||
|
||||
std::ifstream file(hash_filename, std::ios::binary);
|
||||
char headerblock[header_size];
|
||||
file.read(headerblock, header_size);
|
||||
if (memcmp(headerblock, header, header_size))
|
||||
{
|
||||
printf("Invalid cache file format %s.\n", headerblock);
|
||||
return false;
|
||||
}
|
||||
|
||||
output.resize(size / 4);
|
||||
file.read((char *)output.data(), size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SlangShader::save_cache(std::string hash_filename, std::vector<uint32_t> &spv)
|
||||
{
|
||||
if (cache_directory.empty())
|
||||
return;
|
||||
std::ofstream file(hash_filename, std::ios::binary);
|
||||
file.write(header, header_size);
|
||||
file.write((char *)spv.data(), spv.size() * 4);
|
||||
}
|
||||
|
||||
/*
|
||||
Generate SPIRV from separate preprocessed fragment and vertex shaders.
|
||||
Must have called divide_into_stages beforehand. Returns true on success.
|
||||
*/
|
||||
bool SlangShader::generate_spirv()
|
||||
{
|
||||
Initializeglslang();
|
||||
fragment_shader_spirv.clear();
|
||||
vertex_shader_spirv.clear();
|
||||
|
||||
const EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules | EShMsgDebugInfo | EShMsgAST | EShMsgEnhanced);
|
||||
auto forbid_includer = glslang::TShader::ForbidIncluder();
|
||||
auto vertex_cache_filename = get_hash_filename(vertex_shader_string);
|
||||
auto fragment_cache_filename = get_hash_filename(fragment_shader_string);
|
||||
|
||||
glslang::TShader vertexTShader(EShLangVertex);
|
||||
glslang::TShader fragmentTShader(EShLangFragment);
|
||||
if (load_cache(vertex_cache_filename, vertex_shader_spirv))
|
||||
if (load_cache(fragment_cache_filename, fragment_shader_spirv))
|
||||
return true;
|
||||
|
||||
auto compile = [&](glslang::TShader &shader, string &shader_string, std::vector<uint32_t> &spirv) -> bool {
|
||||
const char *source = shader_string.c_str();
|
||||
shader.setStrings(&source, 1);
|
||||
if (!shader.parse(&glslang::DefaultTBuiltInResource, 450, false, messages, forbid_includer))
|
||||
return false;
|
||||
vertex_shader_spirv = generate_spirv(vertex_shader_string, "vertex");
|
||||
fragment_shader_spirv = generate_spirv(fragment_shader_string, "fragment");
|
||||
|
||||
glslang::TProgram program;
|
||||
program.addShader(&shader);
|
||||
if (!program.link(messages))
|
||||
return false;
|
||||
save_cache(vertex_cache_filename, vertex_shader_spirv);
|
||||
save_cache(fragment_cache_filename, fragment_shader_spirv);
|
||||
|
||||
glslang::GlslangToSpv(*program.getIntermediate(shader.getStage()), spirv);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!compile(vertexTShader, vertex_shader_string, vertex_shader_spirv))
|
||||
{
|
||||
printf("%s\n%s\n", vertexTShader.getInfoLog(), vertexTShader.getInfoDebugLog());
|
||||
return false;
|
||||
}
|
||||
if (!compile(fragmentTShader, fragment_shader_string, fragment_shader_spirv))
|
||||
{
|
||||
printf("%s\n%s\n", fragmentTShader.getInfoLog(), fragmentTShader.getInfoDebugLog());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -72,6 +72,10 @@ struct SlangShader
|
|||
bool load_file(std::string new_filename = "");
|
||||
void divide_into_stages(const std::vector<std::string> &lines);
|
||||
bool generate_spirv();
|
||||
std::string get_hash_filename(const std::string &shader_string);
|
||||
bool load_cache(std::string hash_filename, std::vector<uint32_t> &output);
|
||||
void save_cache(std::string hash_filename, std::vector<uint32_t> &spv);
|
||||
void set_cache_directory(std::string dir);
|
||||
static std::vector<uint32_t> generate_spirv(std::string shader_string, std::string stage);
|
||||
|
||||
std::string filename;
|
||||
|
@ -100,4 +104,5 @@ struct SlangShader
|
|||
int ubo_binding;
|
||||
std::vector<Uniform> uniforms;
|
||||
std::vector<Sampler> samplers;
|
||||
std::string cache_directory;
|
||||
};
|
|
@ -187,13 +187,16 @@ void ShaderChain::update_and_propagate_sizes(int original_width_new, int origina
|
|||
}
|
||||
}
|
||||
|
||||
bool ShaderChain::load_shader_preset(std::string filename)
|
||||
bool ShaderChain::load_shader_preset(std::string filename, std::string cache_dir)
|
||||
{
|
||||
if (!ends_with(filename, ".slangp"))
|
||||
printf("Warning: loading preset without .slangp extension\n");
|
||||
|
||||
preset = std::make_unique<SlangPreset>();
|
||||
|
||||
if (!cache_dir.empty())
|
||||
preset->set_cache_directory(cache_dir);
|
||||
|
||||
if (!preset->load_preset_file(filename))
|
||||
{
|
||||
printf("Couldn't load preset file: %s\n", filename.c_str());
|
||||
|
|
|
@ -16,7 +16,7 @@ class ShaderChain
|
|||
|
||||
ShaderChain(Context *context_);
|
||||
~ShaderChain();
|
||||
bool load_shader_preset(std::string filename);
|
||||
bool load_shader_preset(std::string filename, std::string cache_dir = "");
|
||||
void update_and_propagate_sizes(int original_width_, int original_height_, int viewport_width_, int viewport_height_);
|
||||
bool load_lookup_textures();
|
||||
void do_frame(uint8_t *data, int width, int height, int stride, vk::Format format, int viewport_x, int viewport_y, int viewport_width, int viewport_height);
|
||||
|
|
Loading…
Reference in New Issue