2024-07-13 13:45:12 +02:00
|
|
|
using Vortice.ShaderCompiler;
|
|
|
|
using Vortice.SPIRV.Reflect;
|
2024-07-06 23:33:04 +02:00
|
|
|
|
|
|
|
namespace Nerfed.Builder;
|
|
|
|
|
2024-07-13 13:45:12 +02:00
|
|
|
// Values should match the ShaderStage enum in the runtime.
|
|
|
|
public enum ShaderStage
|
|
|
|
{
|
|
|
|
Vertex,
|
|
|
|
Fragment
|
|
|
|
}
|
|
|
|
|
|
|
|
// Values should match the ShaderFormat enum in the runtime.
|
|
|
|
public enum ShaderFormat
|
|
|
|
{
|
|
|
|
Invalid,
|
|
|
|
SPIRV,
|
|
|
|
HLSL,
|
|
|
|
DXBC,
|
|
|
|
DXIL,
|
|
|
|
MSL,
|
|
|
|
METALLIB,
|
|
|
|
SECRET
|
|
|
|
}
|
|
|
|
|
2024-07-06 23:33:04 +02:00
|
|
|
public class ShaderImporter : IImporter
|
|
|
|
{
|
2024-07-13 13:45:12 +02:00
|
|
|
private readonly ShaderStage shaderStage;
|
|
|
|
|
|
|
|
public ShaderImporter(ShaderStage shaderStage)
|
2024-07-06 23:33:04 +02:00
|
|
|
{
|
2024-07-13 13:45:12 +02:00
|
|
|
this.shaderStage = shaderStage;
|
|
|
|
}
|
|
|
|
|
|
|
|
public unsafe void Import(string inFile, string outFile)
|
|
|
|
{
|
|
|
|
string name = Path.GetFileNameWithoutExtension(inFile);
|
|
|
|
string nameWithExt = Path.GetFileName(inFile);
|
|
|
|
|
|
|
|
// Compile the shader.
|
|
|
|
Result compileResult;
|
|
|
|
using (Compiler compiler = new Compiler())
|
2024-07-06 23:33:04 +02:00
|
|
|
{
|
2024-07-13 13:45:12 +02:00
|
|
|
string shaderSource = File.ReadAllText(inFile);
|
|
|
|
compileResult = compiler.Compile(shaderSource, nameWithExt, ToShaderKind(shaderStage));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (compileResult.Status != CompilationStatus.Success)
|
|
|
|
{
|
|
|
|
Console.Error.WriteLine($"Failed to compile {nameWithExt}\n{compileResult.ErrorMessage}");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (compileResult.ErrorsCount > 0 || compileResult.WarningsCount > 0)
|
|
|
|
{
|
|
|
|
Console.Error.WriteLine(compileResult.ErrorMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
Span<byte> byteCode = compileResult.GetBytecode();
|
|
|
|
|
|
|
|
// Inspect SPIR-V bytecode for information which the runtime requires to create a shader resource.
|
|
|
|
SpvReflectShaderModule module = new SpvReflectShaderModule();
|
|
|
|
if (!CheckReflectResult(SPIRVReflectApi.spvReflectCreateShaderModule(byteCode, &module), name))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint descriptorSetCount = 0;
|
|
|
|
if (!CheckReflectResult(SPIRVReflectApi.spvReflectEnumerateDescriptorSets(&module, &descriptorSetCount, null), name))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int uniformBufferCount = 0;
|
|
|
|
int storageBufferCount = 0;
|
|
|
|
int storageTextureCount = 0;
|
|
|
|
int samplerCount = 0;
|
|
|
|
|
|
|
|
if (descriptorSetCount > 0)
|
|
|
|
{
|
|
|
|
SpvReflectDescriptorSet* descriptorSets = stackalloc SpvReflectDescriptorSet[(int)descriptorSetCount];
|
|
|
|
if (!CheckReflectResult(
|
|
|
|
SPIRVReflectApi.spvReflectEnumerateDescriptorSets(&module, &descriptorSetCount, &descriptorSets),
|
|
|
|
name
|
|
|
|
))
|
2024-07-06 23:33:04 +02:00
|
|
|
{
|
2024-07-13 13:45:12 +02:00
|
|
|
return;
|
2024-07-06 23:33:04 +02:00
|
|
|
}
|
2024-07-13 13:45:12 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < descriptorSetCount; i++)
|
2024-07-06 23:33:04 +02:00
|
|
|
{
|
2024-07-13 13:45:12 +02:00
|
|
|
SpvReflectDescriptorSet set = descriptorSets[i];
|
|
|
|
for (int j = 0; j < set.binding_count; j++)
|
|
|
|
{
|
|
|
|
SpvReflectDescriptorBinding binding = *set.bindings[j];
|
|
|
|
if (binding.descriptor_type == SpvReflectDescriptorType.UniformBuffer)
|
|
|
|
{
|
|
|
|
uniformBufferCount++;
|
|
|
|
}
|
|
|
|
else if (binding.descriptor_type == SpvReflectDescriptorType.StorageBuffer)
|
|
|
|
{
|
|
|
|
storageBufferCount++;
|
|
|
|
}
|
|
|
|
else if (binding.descriptor_type == SpvReflectDescriptorType.StorageTexelBuffer)
|
|
|
|
{
|
|
|
|
storageTextureCount++;
|
|
|
|
}
|
|
|
|
else if (binding.descriptor_type == SpvReflectDescriptorType.Sampler ||
|
|
|
|
binding.descriptor_type == SpvReflectDescriptorType.CombinedImageSampler)
|
|
|
|
{
|
|
|
|
samplerCount++;
|
|
|
|
}
|
|
|
|
}
|
2024-07-06 23:33:04 +02:00
|
|
|
}
|
2024-07-13 13:45:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//TODO: Convert SPIR-V to other bytecode formats here (DX/Consoles).
|
|
|
|
ShaderFormat format = ShaderFormat.SPIRV;
|
|
|
|
|
|
|
|
// Write shader meta-data and bytecode to the output file.
|
|
|
|
using (FileStream stream = new FileStream(outFile, FileMode.Create, FileAccess.Write))
|
|
|
|
{
|
|
|
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
2024-07-06 23:33:04 +02:00
|
|
|
{
|
2024-07-13 13:45:12 +02:00
|
|
|
writer.Write((int)format);
|
|
|
|
writer.Write((int)shaderStage);
|
|
|
|
writer.Write(uniformBufferCount);
|
|
|
|
writer.Write(storageBufferCount);
|
|
|
|
writer.Write(storageTextureCount);
|
|
|
|
writer.Write(samplerCount);
|
|
|
|
writer.Write(byteCode.Length);
|
|
|
|
writer.Write(byteCode);
|
2024-07-06 23:33:04 +02:00
|
|
|
}
|
2024-07-13 13:45:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool CheckReflectResult(SpvReflectResult result, string name)
|
|
|
|
{
|
|
|
|
if (result != SpvReflectResult.Success)
|
|
|
|
{
|
|
|
|
Console.Error.WriteLine($"SpirV-Reflect failure for '{name}': {result}");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2024-07-06 23:33:04 +02:00
|
|
|
|
2024-07-13 13:45:12 +02:00
|
|
|
private ShaderKind ToShaderKind(ShaderStage shaderStage)
|
|
|
|
{
|
|
|
|
switch (shaderStage)
|
|
|
|
{
|
|
|
|
case ShaderStage.Vertex: return ShaderKind.VertexShader;
|
|
|
|
case ShaderStage.Fragment: return ShaderKind.FragmentShader;
|
|
|
|
default: throw new ArgumentOutOfRangeException(nameof(shaderStage));
|
2024-07-06 23:33:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|