feat(shader): per-entry defines and a named output variant
Crafter.Graphics has to ship each of its compute shaders in two builds
of one source: one that indexes VK_EXT_descriptor_heap and one that
reads the same slots from a descriptor set, for drivers without the
extension. A Shader was a path, an entry point and a stage, and its
output name was derived from the path alone, so one source could only
be compiled once.
Shader gains `defines`, spelled like the compiler's -D ("NAME" is
defined to 1, "NAME=VALUE" to VALUE) and handed to glslang as a
preamble, and `variant`, which names the output: "ui.comp.glsl" with
variant "sets" becomes "ui.comp.sets.spv". OutputName() is the one
place that name is formed; Check, Compile and the forwarding of a
dependency's shaders into the consumer's bin dir all use it.
Two fields make one mistake easy: defines set, variant forgotten. Both
entries then compiled in parallel into the same file, the build
reported success with whichever finished last, and Check() called both
up to date from then on. The same has always been true of a.vert next
to a.frag. Build() now refuses a configuration in which two entries
share an output name and says which.
A define without a name, or one spanning lines, and a variant with a
path separator are rejected by name. Left to glslang the first two
surface as "-1:<line>" with no file, and the last as a failed open
after the whole compile.
Not addressed: Check() still compares only the source's mtime, so a
change to the defines, or to an included file, does not recompile
(#35).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
e985f2724b
commit
ddaab5a32a
3 changed files with 77 additions and 4 deletions
|
|
@ -502,10 +502,28 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
|
||||
std::string buildError;
|
||||
std::atomic<bool> buildCancelled{false};
|
||||
// Two entries with the same output name would compile in parallel into
|
||||
// one file: whichever finished last won, the build reported success, and
|
||||
// Check() then called both up to date. The usual way in is one source
|
||||
// registered twice with different defines and no variant; a.vert next to
|
||||
// a.frag is another.
|
||||
{
|
||||
std::unordered_map<std::string, const Shader*> outputs;
|
||||
for (const Shader& shader : config.shaders) {
|
||||
const auto [existing, inserted] = outputs.emplace(shader.OutputName().string(), &shader);
|
||||
if (inserted) continue;
|
||||
buildError = std::format("shaders {} and {} both compile to {}: give one of them a distinct Shader::variant", existing->second->path.string(), shader.path.string(), existing->first);
|
||||
buildCancelled.store(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const Shader& shader : config.shaders) {
|
||||
if (buildCancelled.load(std::memory_order_relaxed)) break;
|
||||
if (shader.Check(outputDir)) continue;
|
||||
threads.emplace_back([&shader, &outputDir, &shaderIncludeDirs, &buildError, &buildCancelled]() {
|
||||
Progress::Task task(std::format("Compiling shader {}", shader.path.filename().string()));
|
||||
// A source registered more than once shows up once per variant;
|
||||
// name the variant so the lines can be told apart.
|
||||
Progress::Task task(shader.variant.empty() ? std::format("Compiling shader {}", shader.path.filename().string()) : std::format("Compiling shader {} ({})", shader.path.filename().string(), shader.variant));
|
||||
if (buildCancelled.load(std::memory_order_relaxed)) return;
|
||||
|
||||
std::string result = shader.Compile(outputDir, shaderIncludeDirs);
|
||||
|
|
@ -878,7 +896,7 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
if (!seen.insert(dep).second) return;
|
||||
fs::path depBinDir = dep->BinDir();
|
||||
for (const Shader& shader : dep->shaders) {
|
||||
fs::path src = depBinDir / shader.path.filename().replace_extension("spv");
|
||||
fs::path src = depBinDir / shader.OutputName();
|
||||
if (!fs::exists(src)) continue;
|
||||
copyTree(src, outputDir / src.filename());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue