PostProcessing/Slang: Support parsing simple/reference presets

pull/3729/head
Stenzek 5 months ago
parent 45460b9063
commit ae44fa6f77
No known key found for this signature in database

@ -8,6 +8,8 @@
#include "shadergen.h"
#include "spirv_module.h"
#include "core/host.h" // TODO: Remove after removing ReadResourceFile()
#include "common/assert.h"
#include "common/bitutils.h"
#include "common/error.h"
@ -31,6 +33,8 @@
LOG_CHANNEL(PostProcessing);
using namespace std::string_view_literals;
// TODO:
// - Need some sort of cache for the UBO/push constant layout so we don't need to go through SPIR-V Cross every time.
@ -70,7 +74,7 @@ public:
SlangPresetParser();
~SlangPresetParser();
bool Parse(std::string_view path, std::string_view contents, Error* error);
bool Parse(std::string_view path, std::string_view contents, u32 reference_nesting_level, Error* error);
bool ContainsValue(std::string_view key) const;
std::string_view GetStringValue(std::string_view key, std::string_view def) const;
@ -89,6 +93,9 @@ public:
private:
static bool GetLine(const std::string_view& contents, std::string_view* line, size_t& offset);
bool ParsePresetReference(const std::string_view& path, const std::string_view& line, u32 reference_nesting_level,
Error* error);
UnorderedStringMap<std::string> m_options;
};
@ -196,7 +203,8 @@ inline bool PostProcessing::SlangPresetParser::GetLine(const std::string_view& c
return true;
}
inline bool PostProcessing::SlangPresetParser::Parse(std::string_view path, std::string_view contents, Error* error)
inline bool PostProcessing::SlangPresetParser::Parse(std::string_view path, std::string_view contents,
u32 reference_nesting_level, Error* error)
{
u32 line_number = 0;
size_t offset = 0;
@ -210,6 +218,14 @@ inline bool PostProcessing::SlangPresetParser::Parse(std::string_view path, std:
if (clean_line.empty())
continue;
if (clean_line.starts_with("#reference "))
{
if (!ParsePresetReference(path, clean_line, reference_nesting_level, error))
return false;
continue;
}
// despite having c-style comments, presets can also use # as a comment, but #reference is a thing
// ughhhhhh what a mess
if (clean_line.starts_with('#'))
@ -240,6 +256,110 @@ inline bool PostProcessing::SlangPresetParser::Parse(std::string_view path, std:
return true;
}
inline bool PostProcessing::SlangPresetParser::ParsePresetReference(const std::string_view& path,
const std::string_view& line,
u32 reference_nesting_level, Error* error)
{
if (reference_nesting_level == MAX_SLANG_INCLUDE_DEPTH)
{
Error::SetStringFmt(error, "{}:{} Too many nested references", Path::GetFileName(path), line);
return false;
}
const std::string_view reference_quoted_path = StringUtil::StripWhitespace(line.substr(11));
if (reference_quoted_path.size() < 3 || !reference_quoted_path.starts_with('"') ||
!reference_quoted_path.ends_with('"'))
{
Error::SetStringFmt(error, "{}:{} Malformed preset reference", Path::GetFileName(path), line);
return false;
}
const std::string_view reference_unquoted_path = reference_quoted_path.substr(1, reference_quoted_path.size() - 2);
std::string reference_path = Path::BuildRelativePath(path, reference_unquoted_path);
Path::ToNativePath(&reference_path);
std::optional<std::string> reference_contents;
if (!Path::IsAbsolute(reference_path))
reference_contents = Host::ReadResourceFileToString(reference_path, true, error);
else
reference_contents = FileSystem::ReadFileToString(reference_path.c_str(), error);
if (!reference_contents.has_value())
{
Error::AddPrefixFmt(error, "Failed to read referenced preset {}: ", reference_unquoted_path);
return false;
}
SlangPresetParser pp;
if (!pp.Parse(reference_path, reference_contents.value(), reference_nesting_level + 1, error))
{
Error::AddPrefixFmt(error, "In referenced preset {}: ", Path::GetFileName(reference_path));
return false;
}
// once we hit a full preset, we need to fix up the paths so that they're relative to the original preset
if (const auto iter = pp.m_options.find("shaders"sv); iter != pp.m_options.end())
{
const u32 num_shaders = StringUtil::FromChars<u32>(iter->second).value_or(0);
for (u32 i = 0; i < num_shaders; i++)
{
const TinyString key = TinyString::from_format("shader{}", i);
const auto siter = pp.m_options.find(key.view());
if (siter == pp.m_options.end())
continue;
// if it's already absolute, no need to do anything
if (Path::IsAbsolute(siter->second))
continue;
// if not, we need to make it absolute, relative to the current preset file
std::string fixed_path = Path::BuildRelativePath(reference_path, siter->second);
Path::ToNativePath(&fixed_path);
DEV_LOG("Fixing up shader path in reference '{}' to '{}' (ref {})", siter->second, fixed_path,
reference_unquoted_path);
siter->second = std::move(fixed_path);
}
}
// same for textures...
if (const auto iter = pp.m_options.find("textures"sv); iter != pp.m_options.end())
{
const std::vector<std::string_view> texture_names = StringUtil::SplitString(iter->second, ';');
for (const std::string_view orig_name : texture_names)
{
const std::string_view name = StringUtil::StripWhitespace(orig_name);
if (name.empty())
continue;
const auto titer = pp.m_options.find(name);
if (titer == pp.m_options.end())
continue;
// if it's already absolute, no need to do anything
if (Path::IsAbsolute(titer->second))
continue;
// if not, we need to make it absolute, relative to the current preset file
std::string fixed_path = Path::BuildRelativePath(reference_path, titer->second);
Path::ToNativePath(&fixed_path);
DEV_LOG("Fixing up texture path in reference '{}' to '{}' (ref {})", titer->second, fixed_path,
reference_unquoted_path);
titer->second = std::move(fixed_path);
}
}
// now merge the options back
for (auto iter = pp.m_options.begin(); iter != pp.m_options.end(); ++iter)
{
const auto biter = m_options.find(iter->first);
if (biter != m_options.end())
biter->second = std::move(iter->second);
else
m_options.emplace(iter->first, std::move(iter->second));
}
return true;
}
inline bool PostProcessing::SlangPresetParser::ContainsValue(std::string_view key) const
{
return (m_options.find(key) != m_options.end());
@ -795,7 +915,7 @@ bool PostProcessing::SlangShader::LoadFromString(std::string name, std::string_v
bool PostProcessing::SlangShader::ParsePresetFile(std::string_view path, std::string_view code, Error* error)
{
SlangPresetParser pp;
if (!pp.Parse(path, code, error))
if (!pp.Parse(path, code, 0, error))
return false;
const u32 num_shaders = pp.GetUIntValue("shaders", 0);
@ -1069,6 +1189,24 @@ bool PostProcessing::SlangShader::ParsePresetPass(std::string_view preset_path,
m_options.push_back(std::move(option));
}
}
// Extract any options from the preset if overridden.
for (ShaderOption& option : m_options)
{
if (!parser.ContainsValue(option.name))
continue;
if (option.type == ShaderOption::Type::Bool)
{
option.default_value[0].int_value = option.value[0].int_value =
parser.GetBoolValue(option.name, option.default_value[0].int_value);
}
else
{
option.default_value[0].float_value = option.value[0].float_value =
parser.GetFloatValue(option.name, option.default_value[0].float_value);
}
}
}
m_passes.push_back(std::move(pass));

Loading…
Cancel
Save