PostProcessing/FX: Support reading shaders from resources
This commit is contained in:
parent
1a79a2f196
commit
7bbf04ab99
|
@ -350,6 +350,12 @@ s32 Host::Internal::GetTranslatedStringImpl(const std::string_view& context, con
|
||||||
return static_cast<s32>(msg.size());
|
return static_cast<s32>(msg.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Host::ResourceFileExists(const char* filename)
|
||||||
|
{
|
||||||
|
const std::string path(Path::Combine(EmuFolders::Resources, filename));
|
||||||
|
return FileSystem::FileExists(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::vector<u8>> Host::ReadResourceFile(const char* filename)
|
std::optional<std::vector<u8>> Host::ReadResourceFile(const char* filename)
|
||||||
{
|
{
|
||||||
const std::string path(Path::Combine(EmuFolders::Resources, filename));
|
const std::string path(Path::Combine(EmuFolders::Resources, filename));
|
||||||
|
|
|
@ -1441,6 +1441,12 @@ void Host::OnInputDeviceDisconnected(const std::string_view& identifier)
|
||||||
identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size()));
|
identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Host::ResourceFileExists(const char* filename)
|
||||||
|
{
|
||||||
|
const std::string path(Path::Combine(EmuFolders::Resources, filename));
|
||||||
|
return FileSystem::FileExists(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::vector<u8>> Host::ReadResourceFile(const char* filename)
|
std::optional<std::vector<u8>> Host::ReadResourceFile(const char* filename)
|
||||||
{
|
{
|
||||||
const std::string path(Path::Combine(EmuFolders::Resources, filename));
|
const std::string path(Path::Combine(EmuFolders::Resources, filename));
|
||||||
|
|
|
@ -171,6 +171,12 @@ void Host::CommitBaseSettingChanges()
|
||||||
// noop, in memory
|
// noop, in memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Host::ResourceFileExists(const char* filename)
|
||||||
|
{
|
||||||
|
const std::string path(Path::Combine(EmuFolders::Resources, filename));
|
||||||
|
return FileSystem::FileExists(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::vector<u8>> Host::ReadResourceFile(const char* filename)
|
std::optional<std::vector<u8>> Host::ReadResourceFile(const char* filename)
|
||||||
{
|
{
|
||||||
const std::string path(Path::Combine(EmuFolders::Resources, filename));
|
const std::string path(Path::Combine(EmuFolders::Resources, filename));
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Host {
|
namespace Host {
|
||||||
|
/// Returns true if the specified resource file exists.
|
||||||
|
bool ResourceFileExists(const char* filename);
|
||||||
|
|
||||||
/// Reads a file from the resources directory of the application.
|
/// Reads a file from the resources directory of the application.
|
||||||
/// This may be outside of the "normal" filesystem on platforms such as Mac.
|
/// This may be outside of the "normal" filesystem on platforms such as Mac.
|
||||||
std::optional<std::vector<u8>> ReadResourceFile(const char* filename);
|
std::optional<std::vector<u8>> ReadResourceFile(const char* filename);
|
||||||
|
|
|
@ -368,16 +368,6 @@ std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const s
|
||||||
filename = Path::Combine(
|
filename = Path::Combine(
|
||||||
EmuFolders::Shaders,
|
EmuFolders::Shaders,
|
||||||
fmt::format("reshade" FS_OSPATH_SEPARATOR_STR "Shaders" FS_OSPATH_SEPARATOR_STR "{}.fx", shader_name));
|
fmt::format("reshade" FS_OSPATH_SEPARATOR_STR "Shaders" FS_OSPATH_SEPARATOR_STR "{}.fx", shader_name));
|
||||||
|
|
||||||
// TODO: Won't work on Android. Who cares? All the homies are tired of demanding Android users.
|
|
||||||
if (!FileSystem::FileExists(filename.c_str()))
|
|
||||||
{
|
|
||||||
filename = Path::Combine(EmuFolders::Resources,
|
|
||||||
fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "reshade" FS_OSPATH_SEPARATOR_STR
|
|
||||||
"Shaders" FS_OSPATH_SEPARATOR_STR "{}.fx",
|
|
||||||
shader_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FileSystem::FileExists(filename.c_str()))
|
if (FileSystem::FileExists(filename.c_str()))
|
||||||
{
|
{
|
||||||
std::unique_ptr<ReShadeFXShader> shader = std::make_unique<ReShadeFXShader>();
|
std::unique_ptr<ReShadeFXShader> shader = std::make_unique<ReShadeFXShader>();
|
||||||
|
@ -393,8 +383,21 @@ std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const s
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
resource_str =
|
filename =
|
||||||
Host::ReadResourceFileToString(fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name).c_str());
|
fmt::format("shaders/reshade" FS_OSPATH_SEPARATOR_STR "Shaders" FS_OSPATH_SEPARATOR_STR "{}.fx", shader_name);
|
||||||
|
resource_str = Host::ReadResourceFileToString(filename.c_str());
|
||||||
|
if (resource_str.has_value())
|
||||||
|
{
|
||||||
|
std::unique_ptr<ReShadeFXShader> shader = std::make_unique<ReShadeFXShader>();
|
||||||
|
if (shader->LoadFromString(std::string(shader_name), std::move(filename), std::move(resource_str.value()),
|
||||||
|
only_config, error))
|
||||||
|
{
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name);
|
||||||
|
resource_str = Host::ReadResourceFileToString(filename.c_str());
|
||||||
if (resource_str.has_value())
|
if (resource_str.has_value())
|
||||||
{
|
{
|
||||||
std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>();
|
std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>();
|
||||||
|
|
|
@ -39,6 +39,28 @@ static RenderAPI GetRenderAPI()
|
||||||
return g_gpu_device ? g_gpu_device->GetRenderAPI() : RenderAPI::D3D11;
|
return g_gpu_device ? g_gpu_device->GetRenderAPI() : RenderAPI::D3D11;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool PreprocessorFileExistsCallback(const std::string& path)
|
||||||
|
{
|
||||||
|
if (Path::IsAbsolute(path))
|
||||||
|
return FileSystem::FileExists(path.c_str());
|
||||||
|
|
||||||
|
return Host::ResourceFileExists(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool PreprocessorReadFileCallback(const std::string& path, std::string& data)
|
||||||
|
{
|
||||||
|
std::optional<std::string> rdata;
|
||||||
|
if (Path::IsAbsolute(path))
|
||||||
|
rdata = FileSystem::ReadFileToString(path.c_str());
|
||||||
|
else
|
||||||
|
rdata = Host::ReadResourceFileToString(path.c_str());
|
||||||
|
if (!rdata.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
data = std::move(rdata.value());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static std::unique_ptr<reshadefx::codegen> CreateRFXCodegen()
|
static std::unique_ptr<reshadefx::codegen> CreateRFXCodegen()
|
||||||
{
|
{
|
||||||
const bool debug_info = g_gpu_device ? g_gpu_device->IsDebugDevice() : false;
|
const bool debug_info = g_gpu_device ? g_gpu_device->IsDebugDevice() : false;
|
||||||
|
@ -253,18 +275,38 @@ PostProcessing::ReShadeFXShader::ReShadeFXShader() = default;
|
||||||
|
|
||||||
PostProcessing::ReShadeFXShader::~ReShadeFXShader() = default;
|
PostProcessing::ReShadeFXShader::~ReShadeFXShader() = default;
|
||||||
|
|
||||||
bool PostProcessing::ReShadeFXShader::LoadFromFile(std::string name, const char* filename, bool only_config,
|
bool PostProcessing::ReShadeFXShader::LoadFromFile(std::string name, std::string filename, bool only_config,
|
||||||
Error* error)
|
Error* error)
|
||||||
|
{
|
||||||
|
std::optional<std::string> data = FileSystem::ReadFileToString(filename.c_str(), error);
|
||||||
|
if (!data.has_value())
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("Failed to read '{}'.", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadFromString(std::move(name), std::move(filename), std::move(data.value()), only_config, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessing::ReShadeFXShader::LoadFromString(std::string name, std::string filename, std::string code,
|
||||||
|
bool only_config, Error* error)
|
||||||
{
|
{
|
||||||
DebugAssert(only_config || g_gpu_device);
|
DebugAssert(only_config || g_gpu_device);
|
||||||
|
|
||||||
m_filename = filename;
|
|
||||||
m_name = std::move(name);
|
m_name = std::move(name);
|
||||||
|
m_filename = std::move(filename);
|
||||||
|
|
||||||
|
// Reshade's preprocessor expects this.
|
||||||
|
if (code.empty() || code.back() != '\n')
|
||||||
|
code.push_back('\n');
|
||||||
|
|
||||||
reshadefx::module temp_module;
|
reshadefx::module temp_module;
|
||||||
if (!CreateModule(only_config ? DEFAULT_BUFFER_WIDTH : g_gpu_device->GetWindowWidth(),
|
if (!CreateModule(only_config ? DEFAULT_BUFFER_WIDTH : g_gpu_device->GetWindowWidth(),
|
||||||
only_config ? DEFAULT_BUFFER_HEIGHT : g_gpu_device->GetWindowHeight(), &temp_module, error))
|
only_config ? DEFAULT_BUFFER_HEIGHT : g_gpu_device->GetWindowHeight(), &temp_module,
|
||||||
|
std::move(code), error))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CreateOptions(temp_module, error))
|
if (!CreateOptions(temp_module, error))
|
||||||
return false;
|
return false;
|
||||||
|
@ -318,12 +360,27 @@ bool PostProcessing::ReShadeFXShader::IsValid() const
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessing::ReShadeFXShader::CreateModule(s32 buffer_width, s32 buffer_height, reshadefx::module* mod,
|
bool PostProcessing::ReShadeFXShader::CreateModule(s32 buffer_width, s32 buffer_height, reshadefx::module* mod,
|
||||||
Error* error)
|
std::string code, Error* error)
|
||||||
{
|
{
|
||||||
reshadefx::preprocessor pp;
|
reshadefx::preprocessor pp;
|
||||||
pp.add_include_path(std::string(Path::GetDirectory(m_filename)));
|
pp.set_include_callbacks(PreprocessorFileExistsCallback, PreprocessorReadFileCallback);
|
||||||
pp.add_include_path(std::string(Path::Combine(
|
|
||||||
EmuFolders::Resources, "shaders" FS_OSPATH_SEPARATOR_STR "reshade" FS_OSPATH_SEPARATOR_STR "Shaders")));
|
if (Path::IsAbsolute(m_filename))
|
||||||
|
{
|
||||||
|
// we're a real file, so include that directory
|
||||||
|
pp.add_include_path(std::string(Path::GetDirectory(m_filename)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we're a resource, include the resource subdirectory, if there is one
|
||||||
|
if (std::string_view resdir = Path::GetDirectory(m_filename); !resdir.empty())
|
||||||
|
pp.add_include_path(std::string(resdir));
|
||||||
|
}
|
||||||
|
|
||||||
|
// root of the user directory, and resources
|
||||||
|
pp.add_include_path(Path::Combine(EmuFolders::Shaders, "reshade" FS_OSPATH_SEPARATOR_STR "Shaders"));
|
||||||
|
pp.add_include_path("shaders/reshade/Shaders");
|
||||||
|
|
||||||
pp.add_macro_definition("__RESHADE__", "50901");
|
pp.add_macro_definition("__RESHADE__", "50901");
|
||||||
pp.add_macro_definition("BUFFER_WIDTH", std::to_string(buffer_width)); // TODO: can we make these uniforms?
|
pp.add_macro_definition("BUFFER_WIDTH", std::to_string(buffer_width)); // TODO: can we make these uniforms?
|
||||||
pp.add_macro_definition("BUFFER_HEIGHT", std::to_string(buffer_height));
|
pp.add_macro_definition("BUFFER_HEIGHT", std::to_string(buffer_height));
|
||||||
|
@ -350,7 +407,7 @@ bool PostProcessing::ReShadeFXShader::CreateModule(s32 buffer_width, s32 buffer_
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pp.append_file(m_filename))
|
if (!pp.append_string(std::move(code), m_filename))
|
||||||
{
|
{
|
||||||
Error::SetString(error, fmt::format("Failed to preprocess:\n{}", pp.errors()));
|
Error::SetString(error, fmt::format("Failed to preprocess:\n{}", pp.errors()));
|
||||||
return false;
|
return false;
|
||||||
|
@ -1013,9 +1070,20 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format,
|
||||||
m_textures.clear();
|
m_textures.clear();
|
||||||
m_passes.clear();
|
m_passes.clear();
|
||||||
|
|
||||||
|
std::string fxcode;
|
||||||
|
if (!PreprocessorReadFileCallback(m_filename, fxcode))
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("Failed to re-read shader for pipeline: '{}'", m_filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reshade's preprocessor expects this.
|
||||||
|
if (fxcode.empty() || fxcode.back() != '\n')
|
||||||
|
fxcode.push_back('\n');
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
reshadefx::module mod;
|
reshadefx::module mod;
|
||||||
if (!CreateModule(width, height, &mod, &error))
|
if (!CreateModule(width, height, &mod, std::move(fxcode), &error))
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to create module for '%s': %s", m_name.c_str(), error.GetDescription().c_str());
|
Log_ErrorPrintf("Failed to create module for '%s': %s", m_name.c_str(), error.GetDescription().c_str());
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -24,7 +24,8 @@ public:
|
||||||
|
|
||||||
bool IsValid() const override;
|
bool IsValid() const override;
|
||||||
|
|
||||||
bool LoadFromFile(std::string name, const char* filename, bool only_config, Error* error);
|
bool LoadFromFile(std::string name, std::string filename, bool only_config, Error* error);
|
||||||
|
bool LoadFromString(std::string name, std::string filename, std::string code, bool only_config, Error* error);
|
||||||
|
|
||||||
bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) override;
|
bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) override;
|
||||||
bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height) override;
|
bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height) override;
|
||||||
|
@ -73,7 +74,7 @@ private:
|
||||||
ShaderOption::ValueVector value;
|
ShaderOption::ValueVector value;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool CreateModule(s32 buffer_width, s32 buffer_height, reshadefx::module* mod, Error* error);
|
bool CreateModule(s32 buffer_width, s32 buffer_height, reshadefx::module* mod, std::string code, Error* error);
|
||||||
bool CreateOptions(const reshadefx::module& mod, Error* error);
|
bool CreateOptions(const reshadefx::module& mod, Error* error);
|
||||||
bool GetSourceOption(const reshadefx::uniform_info& ui, SourceOptionType* si, Error* error);
|
bool GetSourceOption(const reshadefx::uniform_info& ui, SourceOptionType* si, Error* error);
|
||||||
bool CreatePasses(GPUTexture::Format backbuffer_format, reshadefx::module& mod, Error* error);
|
bool CreatePasses(GPUTexture::Format backbuffer_format, reshadefx::module& mod, Error* error);
|
||||||
|
|
Loading…
Reference in New Issue