sharpemu/tests/SharpEmu.ShaderCompiler.Tests/Resources/SpirvModuleInspector.cs

89 lines
3.3 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Text;
using SharpEmu.ShaderCompiler.Vulkan;
namespace SharpEmu.ShaderCompiler.Tests.Resources;
// Reads the declarations of a SPIR-V module: names, decorations, capabilities, memory model.
internal sealed class SpirvModuleInspector
{
public Dictionary<uint, string> Names { get; } = [];
public Dictionary<uint, uint> Bindings { get; } = [];
public Dictionary<uint, uint> DescriptorSets { get; } = [];
public HashSet<uint> Capabilities { get; } = [];
public HashSet<ushort> Opcodes { get; } = [];
public Dictionary<uint, uint> VariableStorageClasses { get; } = [];
public uint AddressingModel { get; private set; }
public SpirvModuleInspector(byte[] spirv)
{
var words = new uint[spirv.Length / 4];
Buffer.BlockCopy(spirv, 0, words, 0, spirv.Length);
var offset = 5;
while (offset < words.Length)
{
var wordCount = (int)(words[offset] >> 16);
var opcode = (ushort)(words[offset] & 0xFFFF);
Opcodes.Add(opcode);
switch ((SpirvOp)opcode)
{
case SpirvOp.Name:
Names[words[offset + 1]] = DecodeString(words, offset + 2, offset + wordCount);
break;
case SpirvOp.Decorate when wordCount >= 4 && words[offset + 2] == (uint)SpirvDecoration.Binding:
Bindings[words[offset + 1]] = words[offset + 3];
break;
case SpirvOp.Decorate when wordCount >= 4 && words[offset + 2] == (uint)SpirvDecoration.DescriptorSet:
DescriptorSets[words[offset + 1]] = words[offset + 3];
break;
case SpirvOp.Capability:
Capabilities.Add(words[offset + 1]);
break;
case SpirvOp.MemoryModel:
AddressingModel = words[offset + 1];
break;
case SpirvOp.Variable when wordCount >= 4:
VariableStorageClasses[words[offset + 2]] = words[offset + 3];
break;
}
offset += Math.Max(wordCount, 1);
}
}
// The (set, binding) pairs of every decorated variable.
public HashSet<(uint Set, uint Binding)> DescriptorBindings =>
Bindings.Select(pair => (DescriptorSets.GetValueOrDefault(pair.Key, uint.MaxValue), pair.Value)).ToHashSet();
public uint BindingOf(string name)
{
var id = Names.First(pair => pair.Value == name).Key;
return Bindings[id];
}
public bool HasVariableInStorageClass(SpirvStorageClass storageClass) =>
VariableStorageClasses.ContainsValue((uint)storageClass);
private static string DecodeString(uint[] words, int start, int end)
{
var bytes = new List<byte>();
for (var index = start; index < end; index++)
{
foreach (var shift in new[] { 0, 8, 16, 24 })
{
var value = (byte)(words[index] >> shift);
if (value == 0)
{
return Encoding.UTF8.GetString(bytes.ToArray());
}
bytes.Add(value);
}
}
return Encoding.UTF8.GetString(bytes.ToArray());
}
}