feat(render): implement Campaign AR and terrain fidelity
This commit is contained in:
parent
99cf26e00c
commit
7a5f96ede5
368 changed files with 50611 additions and 950 deletions
51
tools/ShaderCompiler/GlslIncludeExpander.cs
Normal file
51
tools/ShaderCompiler/GlslIncludeExpander.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using System.Text;
|
||||
|
||||
namespace AcDream.Tools.ShaderCompiler;
|
||||
|
||||
/// <summary>
|
||||
/// Tiny deterministic include expander for checked-in shader ABI snippets.
|
||||
/// Only quoted, shader-directory-relative includes are accepted; traversal and
|
||||
/// cycles fail the compile rather than reaching shaderc with host-dependent
|
||||
/// search paths.
|
||||
/// </summary>
|
||||
internal static class GlslIncludeExpander
|
||||
{
|
||||
internal static string Expand(string source, string sourceDirectory) =>
|
||||
Expand(source, Path.GetFullPath(sourceDirectory), []);
|
||||
|
||||
private static string Expand(
|
||||
string source,
|
||||
string sourceDirectory,
|
||||
HashSet<string> active)
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
foreach (string line in source.Replace("\r\n", "\n").Split('\n'))
|
||||
{
|
||||
string trimmed = line.Trim();
|
||||
if (!trimmed.StartsWith("#include \"", StringComparison.Ordinal)
|
||||
|| !trimmed.EndsWith('"'))
|
||||
{
|
||||
output.AppendLine(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
string key = trimmed[10..^1];
|
||||
if (key.Length == 0
|
||||
|| Path.IsPathRooted(key)
|
||||
|| key.Contains("..", StringComparison.Ordinal)
|
||||
|| key.Contains('\\'))
|
||||
throw new InvalidDataException($"Unsafe GLSL include '{key}'.");
|
||||
string path = Path.GetFullPath(Path.Combine(sourceDirectory, key));
|
||||
if (!path.StartsWith(sourceDirectory + Path.DirectorySeparatorChar, StringComparison.Ordinal)
|
||||
|| !File.Exists(path))
|
||||
throw new FileNotFoundException($"GLSL include '{key}' was not found.", path);
|
||||
if (!active.Add(path))
|
||||
throw new InvalidDataException($"Cyclic GLSL include '{key}'.");
|
||||
output.AppendLine($"// ---- begin include: {key} ----");
|
||||
output.Append(Expand(File.ReadAllText(path), sourceDirectory, active));
|
||||
output.AppendLine($"// ---- end include: {key} ----");
|
||||
active.Remove(path);
|
||||
}
|
||||
return output.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -28,15 +28,21 @@ internal static class Program
|
|||
{
|
||||
private static unsafe int Main(string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
if (args.Length < 2
|
||||
|| args.Length > 3
|
||||
|| (args.Length == 3 && !string.Equals(args[2], "--force", StringComparison.Ordinal)))
|
||||
{
|
||||
Console.Error.WriteLine("usage: ShaderCompiler <shaders-dir> <output-dir>");
|
||||
Console.Error.WriteLine("usage: ShaderCompiler <shaders-dir> <output-dir> [--force]");
|
||||
return 2;
|
||||
}
|
||||
|
||||
string sourceDirectory = Path.GetFullPath(args[0]);
|
||||
string outputDirectory = Path.GetFullPath(args[1]);
|
||||
bool force = args.Length == 3;
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
string manifestPath = Path.Combine(outputDirectory, "shaders.manifest.json");
|
||||
IReadOnlyDictionary<(string Name, string Stage), ShaderStageResult> previousStages =
|
||||
force ? new Dictionary<(string, string), ShaderStageResult>() : LoadPreviousStages(manifestPath);
|
||||
|
||||
string[] names = Directory
|
||||
.EnumerateFiles(sourceDirectory, "*.vert")
|
||||
|
|
@ -70,7 +76,23 @@ internal static class Program
|
|||
{
|
||||
string path = Path.Combine(sourceDirectory, $"{name}.{stage}");
|
||||
string source = File.ReadAllText(path);
|
||||
if (source.Contains("#include \"", StringComparison.Ordinal))
|
||||
source = GlslIncludeExpander.Expand(source, sourceDirectory);
|
||||
string hash = Sha256(source);
|
||||
string target = Path.Combine(outputDirectory, $"{name}.{stage}.spv");
|
||||
|
||||
// An unchanged source already has the exact committed artifact
|
||||
// described by the prior manifest. Keeping it avoids compiler-
|
||||
// version-only SPIR-V decoration reordering on the retail path;
|
||||
// a real source edit changes the hash and recompiles normally.
|
||||
if (previousStages.TryGetValue((name, stage), out ShaderStageResult? previous)
|
||||
&& previous.Compiled
|
||||
&& string.Equals(previous.SourceSha256, hash, StringComparison.Ordinal)
|
||||
&& File.Exists(target))
|
||||
{
|
||||
stages.Add(new ShaderStageResult(stage, hash, true, null));
|
||||
continue;
|
||||
}
|
||||
|
||||
string transformed;
|
||||
try
|
||||
|
|
@ -88,13 +110,11 @@ internal static class Program
|
|||
|
||||
if (TryCompile(shaderc, compiler, options, transformed, $"{name}.{stage}", stage, out byte[] spirv, out string message))
|
||||
{
|
||||
string target = Path.Combine(outputDirectory, $"{name}.{stage}.spv");
|
||||
File.WriteAllBytes(target, spirv);
|
||||
stages.Add(new ShaderStageResult(stage, hash, true, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
string target = Path.Combine(outputDirectory, $"{name}.{stage}.spv");
|
||||
if (File.Exists(target))
|
||||
File.Delete(target);
|
||||
stages.Add(new ShaderStageResult(stage, hash, false, Summarise(message)));
|
||||
|
|
@ -140,7 +160,6 @@ internal static class Program
|
|||
var manifest = new ShaderManifest(
|
||||
"Campaign V slice V6c. Regenerate with tools/compile-shaders.ps1.",
|
||||
entries.OrderBy(entry => entry.Name, StringComparer.Ordinal).ToList());
|
||||
string manifestPath = Path.Combine(outputDirectory, "shaders.manifest.json");
|
||||
File.WriteAllText(
|
||||
manifestPath,
|
||||
JsonSerializer.Serialize(manifest, ShaderManifestJson.Options) + Environment.NewLine);
|
||||
|
|
@ -218,6 +237,30 @@ internal static class Program
|
|||
byte[] bytes = Encoding.UTF8.GetBytes(text.Replace("\r\n", "\n"));
|
||||
return Convert.ToHexStringLower(SHA256.HashData(bytes));
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<(string Name, string Stage), ShaderStageResult> LoadPreviousStages(
|
||||
string manifestPath)
|
||||
{
|
||||
if (!File.Exists(manifestPath))
|
||||
return new Dictionary<(string, string), ShaderStageResult>();
|
||||
|
||||
try
|
||||
{
|
||||
ShaderManifest? manifest = JsonSerializer.Deserialize<ShaderManifest>(
|
||||
File.ReadAllText(manifestPath),
|
||||
ShaderManifestJson.Options);
|
||||
return manifest?.Shaders
|
||||
.SelectMany(shader => shader.Stages.Select(stage => (shader.Name, Stage: stage)))
|
||||
.ToDictionary(item => (item.Name, item.Stage.Stage), item => item.Stage)
|
||||
?? new Dictionary<(string, string), ShaderStageResult>();
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// A malformed or obsolete manifest is not an authority. Recompile
|
||||
// everything and replace it with the current deterministic schema.
|
||||
return new Dictionary<(string, string), ShaderStageResult>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record ShaderStageResult(
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ namespace AcDream.Tools.ShaderCompiler;
|
|||
/// <c>BatchBuffer</c> (SSBO binding 1) and <c>SceneLighting</c> (UBO binding 1)
|
||||
/// can share a number. Vulkan has one namespace per set, so moving uniform
|
||||
/// buffers to set 1 preserves both numbers. <c>common.glsl</c> has carried this
|
||||
/// macro since V2 for exactly this moment.</item>
|
||||
/// macro since V2 for exactly this moment. <c>ACDREAM_PACK_UBO_SET</c> separately
|
||||
/// names opt-in set 3, which retail pipeline layouts do not contain.</item>
|
||||
/// <item>The texture table becomes a real descriptor array at set 2 binding 0,
|
||||
/// and <c>ACDREAM_TEXTURE_HANDLE</c> becomes an index rather than a packed
|
||||
/// bindless handle. <c>nonuniformEXT</c> is required, not optional: within one
|
||||
|
|
@ -59,6 +60,10 @@ internal static class VulkanGlslPreamble
|
|||
"uTextureIndexB",
|
||||
"uParamA",
|
||||
"uParamB",
|
||||
// Render-pack logical inputs C/D are packed into the existing spare
|
||||
// scalar words; no bytes are appended to retail's push block.
|
||||
"uTextureIndexC",
|
||||
"uTextureIndexD",
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -66,7 +71,13 @@ internal static class VulkanGlslPreamble
|
|||
/// <paramref name="stage"/> only affects which stage-specific rewrites are
|
||||
/// emitted.
|
||||
/// </summary>
|
||||
internal static string Build(string stage)
|
||||
internal static string Build(string stage) =>
|
||||
Build(stage, includePackUniformSet: false, includePackTextureIndices: false);
|
||||
|
||||
private static string Build(
|
||||
string stage,
|
||||
bool includePackUniformSet,
|
||||
bool includePackTextureIndices)
|
||||
{
|
||||
var text = new StringBuilder();
|
||||
text.AppendLine("// ---- injected by tools/compile-shaders.ps1 (Campaign V slice V6c) ----");
|
||||
|
|
@ -86,6 +97,11 @@ internal static class VulkanGlslPreamble
|
|||
text.AppendLine("// §3.4 set 1: every uniform buffer. Under GL this macro expands to nothing.");
|
||||
text.AppendLine("#undef ACDREAM_UBO_SET");
|
||||
text.AppendLine("#define ACDREAM_UBO_SET set = 1,");
|
||||
if (includePackUniformSet)
|
||||
{
|
||||
text.AppendLine("#undef ACDREAM_PACK_UBO_SET");
|
||||
text.AppendLine("#define ACDREAM_PACK_UBO_SET set = 3,");
|
||||
}
|
||||
text.AppendLine();
|
||||
text.AppendLine("// §4.4 set 2: the global sampled-texture table that replaces");
|
||||
text.AppendLine("// GL_ARB_bindless_texture. Variable count, partially bound,");
|
||||
|
|
@ -136,6 +152,11 @@ internal static class VulkanGlslPreamble
|
|||
text.AppendLine("#define uTextureIndexB acdreamPush.textureIndexB");
|
||||
text.AppendLine("#define uParamA acdreamPush.paramA");
|
||||
text.AppendLine("#define uParamB acdreamPush.paramB");
|
||||
if (includePackTextureIndices)
|
||||
{
|
||||
text.AppendLine("#define uTextureIndexC floatBitsToUint(acdreamPush.paramA)");
|
||||
text.AppendLine("#define uTextureIndexD floatBitsToUint(acdreamPush.paramB)");
|
||||
}
|
||||
text.AppendLine();
|
||||
text.AppendLine("// §4.6: gl_DrawIDARB stays as written — glslang exposes it for Vulkan");
|
||||
text.AppendLine("// under the same ARB extension name. gl_InstanceIndex already includes");
|
||||
|
|
@ -158,6 +179,10 @@ internal static class VulkanGlslPreamble
|
|||
internal static string Apply(string source, string stage)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
bool includePackUniformSet = source.Contains("ACDREAM_PACK_UBO_SET", StringComparison.Ordinal);
|
||||
bool includePackTextureIndices =
|
||||
source.Contains("uTextureIndexC", StringComparison.Ordinal)
|
||||
|| source.Contains("uTextureIndexD", StringComparison.Ordinal);
|
||||
string[] lines = source.Replace("\r\n", "\n").Split('\n');
|
||||
var output = new StringBuilder();
|
||||
bool injected = false;
|
||||
|
|
@ -171,7 +196,7 @@ internal static class VulkanGlslPreamble
|
|||
// more keeps it, because a shader that opted into 460 did so for
|
||||
// a feature and quietly downgrading it would be a silent change.
|
||||
output.AppendLine(HighestVersion(trimmed) >= 460 ? "#version 460 core" : "#version 450 core");
|
||||
output.Append(Build(stage));
|
||||
output.Append(Build(stage, includePackUniformSet, includePackTextureIndices));
|
||||
injected = true;
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
48
tools/ShaderCompiler/packages.win-x64.lock.json
Normal file
48
tools/ShaderCompiler/packages.win-x64.lock.json
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
"net10.0": {
|
||||
"Silk.NET.Shaderc": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.23.0, )",
|
||||
"resolved": "2.23.0",
|
||||
"contentHash": "+pXfOhmSCeeMECOo9HMi3C63LVbQ7FBxPFgxPKOT6mXD8Gg/90Wt4fLX4LqUuVbGid5LW6BXAUu1g17XQoawdA==",
|
||||
"dependencies": {
|
||||
"Silk.NET.Core": "2.23.0",
|
||||
"Silk.NET.Shaderc.Native": "2.23.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.DotNet.PlatformAbstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.1.6",
|
||||
"contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.9",
|
||||
"contentHash": "fNGvKct2De8ghm0Bpfq0iWthtzIWabgOTi+gJhNOPhNJIowXNEUE2eZNW/zNCzrHVA3PXg2yZ+3cWZndC2IqYA=="
|
||||
},
|
||||
"Silk.NET.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.23.0",
|
||||
"contentHash": "D7AT/nnwlB+4RZ84XY8QNGBZMJI5z9l4CSSETIJ1wCfRJzRt/341y3MRZ4HbnFz4r/IGaWOEZr86iE+0/65yyQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.DotNet.PlatformAbstractions": "3.1.6",
|
||||
"Microsoft.Extensions.DependencyModel": "9.0.9"
|
||||
}
|
||||
},
|
||||
"Silk.NET.Shaderc.Native": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.23.0",
|
||||
"contentHash": "H6OMLIWdh2HITvkmj+ALs8LTIdQvQ2/JTtkDXinVbJ3xxrQIBXhVmc9jnuTQ67YDybGlENSMrgthzhLwK0rjnQ=="
|
||||
}
|
||||
},
|
||||
"net10.0/win-x64": {
|
||||
"Silk.NET.Shaderc.Native": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.23.0",
|
||||
"contentHash": "H6OMLIWdh2HITvkmj+ALs8LTIdQvQ2/JTtkDXinVbJ3xxrQIBXhVmc9jnuTQ67YDybGlENSMrgthzhLwK0rjnQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue