implemented SPIR-V shader export

This commit is contained in:
Perfare
2020-08-12 22:11:26 +08:00
parent 0ec29f62ca
commit 729a8a8263
17 changed files with 6720 additions and 5 deletions

View File

@ -0,0 +1,221 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SpirV
{
public struct ModuleHeader
{
public Version Version { get; set; }
public string GeneratorVendor { get; set; }
public string GeneratorName { get; set; }
public int GeneratorVersion { get; set; }
public uint Bound { get; set; }
public uint Reserved { get; set; }
}
[Flags]
public enum DisassemblyOptions
{
None,
ShowTypes,
ShowNames,
Default = ShowTypes | ShowNames
}
public class Disassembler
{
public string Disassemble (Module module)
{
return Disassemble(module, DisassemblyOptions.Default);
}
public string Disassemble(Module module, DisassemblyOptions options)
{
m_sb.AppendLine("; SPIR-V");
m_sb.Append("; Version: ").Append(module.Header.Version).AppendLine();
if (module.Header.GeneratorName == null)
{
m_sb.Append("; Generator: unknown; ").Append(module.Header.GeneratorVersion).AppendLine();
}
else
{
m_sb.Append("; Generator: ").Append(module.Header.GeneratorVendor).Append(' ').
Append(module.Header.GeneratorName).Append("; ").Append(module.Header.GeneratorVersion).AppendLine();
}
m_sb.Append("; Bound: ").Append(module.Header.Bound).AppendLine();
m_sb.Append("; Schema: ").Append(module.Header.Reserved).AppendLine();
string[] lines = new string[module.Instructions.Count + 1];
lines[0] = m_sb.ToString();
m_sb.Clear();
for (int i = 0; i < module.Instructions.Count; i++)
{
ParsedInstruction instruction = module.Instructions[i];
PrintInstruction(m_sb, instruction, options);
lines[i + 1] = m_sb.ToString();
m_sb.Clear();
}
int longestPrefix = 0;
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
longestPrefix = Math.Max(longestPrefix, line.IndexOf('='));
if (longestPrefix > 50)
{
longestPrefix = 50;
break;
}
}
m_sb.Append(lines[0]);
for (int i = 1; i < lines.Length; i++)
{
string line = lines[i];
int index = line.IndexOf('=');
if (index == -1)
{
m_sb.Append(' ', longestPrefix + 4);
m_sb.Append(line);
}
else
{
int pad = Math.Max(0, longestPrefix - index);
m_sb.Append(' ', pad);
m_sb.Append(line, 0, index);
m_sb.Append('=');
m_sb.Append(line, index + 1, line.Length - index - 1);
}
m_sb.AppendLine();
}
string result = m_sb.ToString();
m_sb.Clear();
return result;
}
private static void PrintInstruction(StringBuilder sb, ParsedInstruction instruction, DisassemblyOptions options)
{
if (instruction.Operands.Count == 0)
{
sb.Append(instruction.Instruction.Name);
return;
}
int currentOperand = 0;
if (instruction.Instruction.Operands[currentOperand].Type is IdResultType)
{
if (options.HasFlag(DisassemblyOptions.ShowTypes))
{
instruction.ResultType.ToString(sb).Append(' ');
}
++currentOperand;
}
if (currentOperand < instruction.Operands.Count && instruction.Instruction.Operands[currentOperand].Type is IdResult)
{
if (!options.HasFlag(DisassemblyOptions.ShowNames) || string.IsNullOrWhiteSpace(instruction.Name))
{
PrintOperandValue(sb, instruction.Operands[currentOperand].Value, options);
}
else
{
sb.Append(instruction.Name);
}
sb.Append(" = ");
++currentOperand;
}
sb.Append(instruction.Instruction.Name);
sb.Append(' ');
for (; currentOperand < instruction.Operands.Count; ++currentOperand)
{
PrintOperandValue(sb, instruction.Operands[currentOperand].Value, options);
sb.Append(' ');
}
}
private static void PrintOperandValue(StringBuilder sb, object value, DisassemblyOptions options)
{
switch (value)
{
case System.Type t:
sb.Append(t.Name);
break;
case string s:
{
sb.Append('"');
sb.Append(s);
sb.Append('"');
}
break;
case ObjectReference or:
{
if (options.HasFlag(DisassemblyOptions.ShowNames) && or.Reference != null && !string.IsNullOrWhiteSpace(or.Reference.Name))
{
sb.Append(or.Reference.Name);
}
else
{
or.ToString(sb);
}
}
break;
case IBitEnumOperandValue beov:
PrintBitEnumValue(sb, beov, options);
break;
case IValueEnumOperandValue veov:
PrintValueEnumValue(sb, veov, options);
break;
case VaryingOperandValue varOpVal:
varOpVal.ToString(sb);
break;
default:
sb.Append(value);
break;
}
}
private static void PrintBitEnumValue(StringBuilder sb, IBitEnumOperandValue enumOperandValue, DisassemblyOptions options)
{
foreach (uint key in enumOperandValue.Values.Keys)
{
sb.Append(enumOperandValue.EnumerationType.GetEnumName(key));
IReadOnlyList<object> value = enumOperandValue.Values[key];
if (value.Count != 0)
{
sb.Append(' ');
foreach (object v in value)
{
PrintOperandValue(sb, v, options);
}
}
}
}
private static void PrintValueEnumValue(StringBuilder sb, IValueEnumOperandValue valueOperandValue, DisassemblyOptions options)
{
sb.Append(valueOperandValue.Key);
if (valueOperandValue.Value is IList<object> valueList && valueList.Count > 0)
{
sb.Append(' ');
foreach (object v in valueList)
{
PrintOperandValue(sb, v, options);
}
}
}
private readonly StringBuilder m_sb = new StringBuilder();
}
}

View File

@ -0,0 +1,42 @@
#if NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6
using System;
using System.Linq;
using System.Reflection;
namespace SpirV
{
public static class EnumValuesExtensions
{
public static Array GetEnumValues(this System.Type _this)
{
TypeInfo typeInfo = _this.GetTypeInfo ();
if (!typeInfo.IsEnum) {
throw new ArgumentException ("GetEnumValues: Type '" + _this.Name + "' is not an enum");
}
return
(
from field in typeInfo.DeclaredFields
where field.IsLiteral
select field.GetValue (null)
)
.ToArray();
}
public static string GetEnumName(this System.Type _this, object value)
{
TypeInfo typeInfo = _this.GetTypeInfo ();
if (!typeInfo.IsEnum) {
throw new ArgumentException ("GetEnumName: Type '" + _this.Name + "' is not an enum");
}
return
(
from field in typeInfo.DeclaredFields
where field.IsLiteral && (uint)field.GetValue(null) == (uint)value
select field.Name
)
.First();
}
}
}
#endif

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
namespace SpirV
{
public enum OperandQuantifier
{
/// <summary>
/// 1
/// </summary>
Default,
/// <summary>
/// 0 or 1
/// </summary>
Optional,
/// <summary>
/// 0+
/// </summary>
Varying
}
public class Operand
{
public Operand(OperandType kind, string name, OperandQuantifier quantifier)
{
Name = name;
Type = kind;
Quantifier = quantifier;
}
public string Name { get; }
public OperandType Type { get; }
public OperandQuantifier Quantifier { get; }
}
public class Instruction
{
public Instruction (string name)
: this (name, new List<Operand> ())
{
}
public Instruction (string name, IReadOnlyList<Operand> operands)
{
Operands = operands;
Name = name;
}
public string Name { get; }
public IReadOnlyList<Operand> Operands { get; }
}
}

View File

@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2017, Matthäus G. Chajdas
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,426 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace SpirV
{
public class Module
{
[StructLayout(LayoutKind.Explicit)]
private struct FloatUIntUnion
{
[FieldOffset(0)]
public uint Int;
[FieldOffset(0)]
public float Float;
}
[StructLayout(LayoutKind.Explicit)]
private struct DoubleULongUnion
{
[FieldOffset(0)]
public ulong Long;
[FieldOffset(0)]
public double Double;
}
public Module(ModuleHeader header, IReadOnlyList<ParsedInstruction> instructions)
{
Header = header;
Instructions = instructions;
Read(Instructions, objects_);
}
public static bool IsDebugInstruction(ParsedInstruction instruction)
{
return debugInstructions_.Contains(instruction.Instruction.Name);
}
private static void Read(IReadOnlyList<ParsedInstruction> instructions, Dictionary<uint, ParsedInstruction> objects)
{
// Debug instructions can be only processed after everything
// else has been parsed, as they may reference types which haven't
// been seen in the file yet
List<ParsedInstruction> debugInstructions = new List<ParsedInstruction>();
// Entry points contain forward references
// Those need to be resolved afterwards
List<ParsedInstruction> entryPoints = new List<ParsedInstruction>();
foreach (var instruction in instructions)
{
if (IsDebugInstruction(instruction))
{
debugInstructions.Add(instruction);
continue;
}
if (instruction.Instruction is OpEntryPoint)
{
entryPoints.Add(instruction);
continue;
}
if (instruction.Instruction.Name.StartsWith("OpType", StringComparison.Ordinal))
{
ProcessTypeInstruction(instruction, objects);
}
instruction.ResolveResultType(objects);
if (instruction.HasResult)
{
objects[instruction.ResultId] = instruction;
}
switch (instruction.Instruction)
{
// Constants require that the result type has been resolved
case OpSpecConstant sc:
case OpConstant oc:
{
Type t = instruction.ResultType;
Debug.Assert (t != null);
Debug.Assert (t is ScalarType);
object constant = ConvertConstant(instruction.ResultType as ScalarType, instruction.Words, 3);
instruction.Operands[2].Value = constant;
instruction.Value = constant;
}
break;
}
}
foreach (ParsedInstruction instruction in debugInstructions)
{
switch (instruction.Instruction)
{
case OpMemberName mn:
{
StructType t = (StructType)objects[instruction.Words[1]].ResultType;
t.SetMemberName((uint)instruction.Operands[1].Value, (string)instruction.Operands[2].Value);
}
break;
case OpName n:
{
// We skip naming objects we don't know about
ParsedInstruction t = objects[instruction.Words[1]];
t.Name = (string)instruction.Operands[1].Value;
}
break;
}
}
foreach (ParsedInstruction instruction in instructions)
{
instruction.ResolveReferences(objects);
}
}
public static Module ReadFrom(Stream stream)
{
BinaryReader br = new BinaryReader(stream);
Reader reader = new Reader(br);
uint versionNumber = reader.ReadDWord();
int majorVersion = (int)(versionNumber >> 16);
int minorVersion = (int)((versionNumber >> 8) & 0xFF);
Version version = new Version(majorVersion, minorVersion);
uint generatorMagicNumber = reader.ReadDWord();
int generatorToolId = (int)(generatorMagicNumber >> 16);
string generatorVendor = "unknown";
string generatorName = null;
if (Meta.Tools.ContainsKey(generatorToolId))
{
Meta.ToolInfo toolInfo = Meta.Tools[generatorToolId];
generatorVendor = toolInfo.Vendor;
if (toolInfo.Name != null)
{
generatorName = toolInfo.Name;
}
}
// Read header
ModuleHeader header = new ModuleHeader();
header.Version = version;
header.GeneratorName = generatorName;
header.GeneratorVendor = generatorVendor;
header.GeneratorVersion = (int)(generatorMagicNumber & 0xFFFF);
header.Bound = reader.ReadDWord();
header.Reserved = reader.ReadDWord();
List<ParsedInstruction> instructions = new List<ParsedInstruction>();
while (!reader.EndOfStream)
{
uint instructionStart = reader.ReadDWord ();
ushort wordCount = (ushort)(instructionStart >> 16);
int opCode = (int)(instructionStart & 0xFFFF);
uint[] words = new uint[wordCount];
words[0] = instructionStart;
for (ushort i = 1; i < wordCount; ++i)
{
words[i] = reader.ReadDWord();
}
ParsedInstruction instruction = new ParsedInstruction(opCode, words);
instructions.Add(instruction);
}
return new Module(header, instructions);
}
/// <summary>
/// Collect types from OpType* instructions
/// </summary>
private static void ProcessTypeInstruction(ParsedInstruction i, IReadOnlyDictionary<uint, ParsedInstruction> objects)
{
switch (i.Instruction)
{
case OpTypeInt t:
{
i.ResultType = new IntegerType((int)i.Words[2], i.Words[3] == 1u);
}
break;
case OpTypeFloat t:
{
i.ResultType = new FloatingPointType((int)i.Words[2]);
}
break;
case OpTypeVector t:
{
i.ResultType = new VectorType((ScalarType)objects[i.Words[2]].ResultType, (int)i.Words[3]);
}
break;
case OpTypeMatrix t:
{
i.ResultType = new MatrixType((VectorType)objects[i.Words[2]].ResultType, (int)i.Words[3]);
}
break;
case OpTypeArray t:
{
object constant = objects[i.Words[3]].Value;
int size = 0;
switch (constant)
{
case ushort u16:
size = u16;
break;
case uint u32:
size = (int)u32;
break;
case ulong u64:
size = (int)u64;
break;
case short i16:
size = i16;
break;
case int i32:
size = i32;
break;
case long i64:
size = (int)i64;
break;
}
i.ResultType = new ArrayType(objects[i.Words[2]].ResultType, size);
}
break;
case OpTypeRuntimeArray t:
{
i.ResultType = new RuntimeArrayType((Type)objects[i.Words[2]].ResultType);
}
break;
case OpTypeBool t:
{
i.ResultType = new BoolType();
}
break;
case OpTypeOpaque t:
{
i.ResultType = new OpaqueType();
}
break;
case OpTypeVoid t:
{
i.ResultType = new VoidType();
}
break;
case OpTypeImage t:
{
Type sampledType = objects[i.Operands[1].GetId ()].ResultType;
Dim dim = i.Operands[2].GetSingleEnumValue<Dim>();
uint depth = (uint)i.Operands[3].Value;
bool isArray = (uint)i.Operands[4].Value != 0;
bool isMultiSampled = (uint)i.Operands[5].Value != 0;
uint sampled = (uint)i.Operands[6].Value;
ImageFormat imageFormat = i.Operands[7].GetSingleEnumValue<ImageFormat>();
i.ResultType = new ImageType(sampledType,
dim,
(int)depth, isArray, isMultiSampled,
(int)sampled, imageFormat,
i.Operands.Count > 8 ? i.Operands[8].GetSingleEnumValue<AccessQualifier>() : AccessQualifier.ReadOnly);
}
break;
case OpTypeSampler st:
{
i.ResultType = new SamplerType();
break;
}
case OpTypeSampledImage t:
{
i.ResultType = new SampledImageType((ImageType)objects[i.Words[2]].ResultType);
}
break;
case OpTypeFunction t:
{
List<Type> parameterTypes = new List<Type>();
for (int j = 3; j < i.Words.Count; ++j)
{
parameterTypes.Add(objects[i.Words[j]].ResultType);
}
i.ResultType = new FunctionType(objects[i.Words[2]].ResultType, parameterTypes);
}
break;
case OpTypeForwardPointer t:
{
// We create a normal pointer, but with unspecified type
// This will get resolved later on
i.ResultType = new PointerType((StorageClass)i.Words[2]);
}
break;
case OpTypePointer t:
{
if (objects.ContainsKey(i.Words[1]))
{
// If there is something present, it must have been
// a forward reference. The storage type must
// match
PointerType pt = (PointerType)i.ResultType;
Debug.Assert (pt != null);
Debug.Assert (pt.StorageClass == (StorageClass)i.Words[2]);
pt.ResolveForwardReference (objects[i.Words[3]].ResultType);
}
else
{
i.ResultType = new PointerType((StorageClass)i.Words[2], objects[i.Words[3]].ResultType);
}
}
break;
case OpTypeStruct t:
{
List<Type> memberTypes = new List<Type>();
for (int j = 2; j < i.Words.Count; ++j)
{
memberTypes.Add(objects[i.Words[j]].ResultType);
}
i.ResultType = new StructType(memberTypes);
}
break;
}
}
private static object ConvertConstant(ScalarType type, IReadOnlyList<uint> words, int index)
{
switch (type)
{
case IntegerType i:
{
if (i.Signed)
{
if (i.Width == 16)
{
return unchecked((short)(words[index]));
}
else if (i.Width == 32)
{
return unchecked((int)(words[index]));
}
else if (i.Width == 64)
{
return unchecked((long)(words[index] | (ulong)(words[index + 1]) << 32));
}
}
else
{
if (i.Width == 16)
{
return unchecked((ushort)(words[index]));
}
else if (i.Width == 32)
{
return words[index];
}
else if (i.Width == 64)
{
return words[index] | (ulong)(words[index + 1]) << 32;
}
}
throw new Exception ("Cannot construct integer literal.");
}
case FloatingPointType f:
{
if (f.Width == 32)
{
return new FloatUIntUnion { Int = words[0] }.Float;
}
else if (f.Width == 64)
{
return new DoubleULongUnion { Long = (words[index] | (ulong)(words[index + 1]) << 32) }.Double;
}
else
{
throw new Exception("Cannot construct floating point literal.");
}
}
}
return null;
}
public ModuleHeader Header { get; }
public IReadOnlyList<ParsedInstruction> Instructions { get; }
private static HashSet<string> debugInstructions_ = new HashSet<string>
{
"OpSourceContinued",
"OpSource",
"OpSourceExtension",
"OpName",
"OpMemberName",
"OpString",
"OpLine",
"OpNoLine",
"OpModuleProcessed"
};
private readonly Dictionary<uint, ParsedInstruction> objects_ = new Dictionary<uint, ParsedInstruction>();
}
}

View File

@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reflection;
namespace SpirV
{
public class OperandType
{
public virtual bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
// This returns the dynamic type
value = GetType();
wordsUsed = 1;
return true;
}
}
public class Literal : OperandType
{
}
public class LiteralNumber : Literal
{
}
// The SPIR-V JSON file uses only literal integers
public class LiteralInteger : LiteralNumber
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = words[index];
wordsUsed = 1;
return true;
}
}
public class LiteralString : Literal
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
// This is just a fail-safe -- the loop below must terminate
wordsUsed = 1;
int bytesUsed = 0;
byte[] bytes = new byte[(words.Count - index) * 4];
for (int i = index; i < words.Count; ++i)
{
uint word = words[i];
byte b0 = (byte)(word & 0xFF);
if (b0 == 0)
{
break;
}
else
{
bytes[bytesUsed++] = b0;
}
byte b1 = (byte)((word >> 8) & 0xFF);
if (b1 == 0)
{
break;
}
else
{
bytes[bytesUsed++] = b1;
}
byte b2 = (byte)((word >> 16) & 0xFF);
if (b2 == 0)
{
break;
}
else
{
bytes[bytesUsed++] = b2;
}
byte b3 = (byte)(word >> 24);
if (b3 == 0)
{
break;
}
else
{
bytes[bytesUsed++] = b3;
}
wordsUsed++;
}
value = Encoding.UTF8.GetString(bytes, 0, bytesUsed);
return true;
}
}
public class LiteralContextDependentNumber : Literal
{
// This is handled during parsing by ConvertConstant
}
public class LiteralExtInstInteger : Literal
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = words[index];
wordsUsed = 1;
return true;
}
}
public class LiteralSpecConstantOpInteger : Literal
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
List<ObjectReference> result = new List<ObjectReference>();
for (int i = index; i < words.Count; i++)
{
ObjectReference objRef = new ObjectReference(words[i]);
result.Add(objRef);
}
value = result;
wordsUsed = words.Count - index;
return true;
}
}
public class Parameter
{
public virtual IReadOnlyList<OperandType> OperandTypes { get; }
}
public class ParameterFactory
{
public virtual Parameter CreateParameter(object value)
{
return null;
}
}
public class EnumType<T> : EnumType<T, ParameterFactory>
where T : Enum
{
};
public class EnumType<T, U> : OperandType
where T : Enum
where U : ParameterFactory, new ()
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
int wordsUsedForParameters = 0;
if (typeof(T).GetTypeInfo().GetCustomAttributes<FlagsAttribute>().Any())
{
Dictionary<uint, IReadOnlyList<object>> result = new Dictionary<uint, IReadOnlyList<object>>();
foreach (object enumValue in EnumerationType.GetEnumValues())
{
uint bit = (uint)enumValue;
// bit == 0 and words[0] == 0 handles the 0x0 = None cases
if ((words[index] & bit) != 0 || (bit == 0 && words[index] == 0))
{
Parameter p = parameterFactory_.CreateParameter(bit);
if (p == null)
{
result.Add(bit, Array.Empty<object>());
}
else
{
object[] resultItems = new object[p.OperandTypes.Count];
for (int j = 0; j < p.OperandTypes.Count; ++j)
{
p.OperandTypes[j].ReadValue(words, 1 + wordsUsedForParameters, out object pValue, out int pWordsUsed);
wordsUsedForParameters += pWordsUsed;
resultItems[j] = pValue;
}
result.Add(bit, resultItems);
}
}
}
value = new BitEnumOperandValue<T>(result);
}
else
{
object[] resultItems;
Parameter p = parameterFactory_.CreateParameter(words[index]);
if (p == null)
{
resultItems = Array.Empty<object>();
}
else
{
resultItems = new object[p.OperandTypes.Count];
for (int j = 0; j < p.OperandTypes.Count; ++j)
{
p.OperandTypes[j].ReadValue(words, 1 + wordsUsedForParameters, out object pValue, out int pWordsUsed);
wordsUsedForParameters += pWordsUsed;
resultItems[j] = pValue;
}
}
value = new ValueEnumOperandValue<T>((T)(object)words[index], resultItems);
}
wordsUsed = wordsUsedForParameters + 1;
return true;
}
public System.Type EnumerationType => typeof(T);
private U parameterFactory_ = new U();
}
public class IdScope : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = (Scope)words[index];
wordsUsed = 1;
return true;
}
}
public class IdMemorySemantics : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = (MemorySemantics)words[index];
wordsUsed = 1;
return true;
}
}
public class IdType : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = words[index];
wordsUsed = 1;
return true;
}
}
public class IdResult : IdType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = new ObjectReference(words[index]);
wordsUsed = 1;
return true;
}
}
public class IdResultType : IdType
{
}
public class IdRef : IdType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = new ObjectReference(words[index]);
wordsUsed = 1;
return true;
}
}
public class PairIdRefIdRef : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
ObjectReference variable = new ObjectReference(words[index]);
ObjectReference parent = new ObjectReference(words[index + 1]);
value = new { Variable = variable, Parent = parent };
wordsUsed = 2;
return true;
}
}
public class PairIdRefLiteralInteger : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
ObjectReference type = new ObjectReference(words[index]);
uint word = words[index + 1];
value = new { Type = type, Member = word };
wordsUsed = 2;
return true;
}
}
public class PairLiteralIntegerIdRef : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
uint selector = words[index];
ObjectReference label = new ObjectReference(words[index + 1]);
value = new { Selector = selector, Label = label };
wordsUsed = 2;
return true;
}
}
}

View File

@ -0,0 +1,265 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SpirV
{
public class ParsedOperand
{
public ParsedOperand(IReadOnlyList<uint> words, int index, int count, object value, Operand operand)
{
uint[] array = new uint[count];
for (int i = 0; i < count; i++)
{
array[i] = words[index + i];
}
Words = array;
Value = value;
Operand = operand;
}
public T GetSingleEnumValue<T>()
where T : Enum
{
IValueEnumOperandValue v = (IValueEnumOperandValue)Value;
if (v.Value.Count == 0)
{
// If there's no value at all, the enum is probably something like ImageFormat.
// In which case we just return the enum value
return (T)v.Key;
}
else
{
// This means the enum has a value attached to it, so we return the attached value
return (T)((IValueEnumOperandValue)Value).Value[0];
}
}
public uint GetId()
{
return ((ObjectReference)Value).Id;
}
public T GetBitEnumValue<T>()
where T : Enum
{
var v = Value as IBitEnumOperandValue;
uint result = 0;
foreach (var k in v.Values.Keys)
{
result |= k;
}
return (T)(object)result;
}
public IReadOnlyList<uint> Words { get; }
public object Value { get; set; }
public Operand Operand { get; }
}
public class VaryingOperandValue
{
public VaryingOperandValue(IReadOnlyList<object> values)
{
Values = values;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
ToString(sb);
return sb.ToString();
}
public StringBuilder ToString(StringBuilder sb)
{
for (int i = 0; i < Values.Count; ++i)
{
if (Values[i] is ObjectReference objRef)
{
objRef.ToString(sb);
}
else
{
sb.Append(Values[i]);
}
if (i < (Values.Count - 1))
{
sb.Append(' ');
}
}
return sb;
}
public IReadOnlyList<object> Values { get; }
}
public interface IEnumOperandValue
{
System.Type EnumerationType { get; }
}
public interface IBitEnumOperandValue : IEnumOperandValue
{
IReadOnlyDictionary<uint, IReadOnlyList<object>> Values { get; }
}
public interface IValueEnumOperandValue : IEnumOperandValue
{
object Key { get; }
IReadOnlyList<object> Value { get; }
}
public class ValueEnumOperandValue<T> : IValueEnumOperandValue
where T : Enum
{
public ValueEnumOperandValue(T key, IReadOnlyList<object> value)
{
Key = key;
Value = value;
}
public System.Type EnumerationType => typeof(T);
public object Key { get; }
public IReadOnlyList<object> Value { get; }
}
public class BitEnumOperandValue<T> : IBitEnumOperandValue
where T : Enum
{
public BitEnumOperandValue(Dictionary<uint, IReadOnlyList<object>> values)
{
Values = values;
}
public IReadOnlyDictionary<uint, IReadOnlyList<object>> Values { get; }
public System.Type EnumerationType => typeof(T);
}
public class ObjectReference
{
public ObjectReference(uint id)
{
Id = id;
}
public void Resolve(IReadOnlyDictionary<uint, ParsedInstruction> objects)
{
Reference = objects[Id];
}
public override string ToString()
{
return $"%{Id}";
}
public StringBuilder ToString(StringBuilder sb)
{
return sb.Append('%').Append(Id);
}
public uint Id { get; }
public ParsedInstruction Reference { get; private set; }
}
public class ParsedInstruction
{
public ParsedInstruction(int opCode, IReadOnlyList<uint> words)
{
Words = words;
Instruction = Instructions.OpcodeToInstruction[opCode];
ParseOperands();
}
private void ParseOperands()
{
if (Instruction.Operands.Count == 0)
{
return;
}
// Word 0 describes this instruction so we can ignore it
int currentWord = 1;
int currentOperand = 0;
List<object> varyingOperandValues = new List<object>();
int varyingWordStart = 0;
Operand varyingOperand = null;
while (currentWord < Words.Count)
{
Operand operand = Instruction.Operands[currentOperand];
operand.Type.ReadValue(Words, currentWord, out object value, out int wordsUsed);
if (operand.Quantifier == OperandQuantifier.Varying)
{
varyingOperandValues.Add(value);
varyingWordStart = currentWord;
varyingOperand = operand;
}
else
{
int wordCount = Math.Min(Words.Count - currentWord, wordsUsed);
ParsedOperand parsedOperand = new ParsedOperand(Words, currentWord, wordCount, value, operand);
Operands.Add(parsedOperand);
}
currentWord += wordsUsed;
if (operand.Quantifier != OperandQuantifier.Varying)
{
++currentOperand;
}
}
if (varyingOperand != null)
{
VaryingOperandValue varOperantValue = new VaryingOperandValue(varyingOperandValues);
ParsedOperand parsedOperand = new ParsedOperand(Words, currentWord, Words.Count - currentWord, varOperantValue, varyingOperand);
Operands.Add(parsedOperand);
}
}
public void ResolveResultType(IReadOnlyDictionary<uint, ParsedInstruction> objects)
{
if (Instruction.Operands.Count > 0 && Instruction.Operands[0].Type is IdResultType)
{
ResultType = objects[(uint)Operands[0].Value].ResultType;
}
}
public void ResolveReferences (IReadOnlyDictionary<uint, ParsedInstruction> objects)
{
foreach (var operand in Operands)
{
if (operand.Value is ObjectReference objectReference)
{
objectReference.Resolve (objects);
}
}
}
public Type ResultType { get; set; }
public uint ResultId
{
get
{
for (int i = 0; i < Instruction.Operands.Count; ++i)
{
if (Instruction.Operands[i].Type is IdResult)
{
return Operands[i].GetId();
}
}
return 0;
}
}
public bool HasResult => ResultId != 0;
public IReadOnlyList<uint> Words { get; }
public Instruction Instruction { get; }
public IList<ParsedOperand> Operands { get; } = new List<ParsedOperand>();
public string Name { get; set; }
public object Value { get; set; }
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
namespace SpirV
{
internal sealed class Reader
{
public Reader(BinaryReader reader)
{
reader_ = reader;
uint magicNumber = reader_.ReadUInt32();
if (magicNumber == Meta.MagicNumber)
{
littleEndian_ = true;
}
else if (Reverse(magicNumber) == Meta.MagicNumber)
{
littleEndian_ = false;
}
else
{
throw new Exception("Invalid magic number");
}
}
public uint ReadDWord()
{
if (littleEndian_)
{
return reader_.ReadUInt32 ();
}
else
{
return Reverse(reader_.ReadUInt32());
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Reverse(uint u)
{
return (u << 24) | (u & 0xFF00U) << 8 | (u >> 8) & 0xFF00U | (u >> 24);
}
public bool EndOfStream => reader_.BaseStream.Position == reader_.BaseStream.Length;
private readonly BinaryReader reader_;
private readonly bool littleEndian_;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,54 @@
using System.Collections.Generic;
namespace SpirV
{
internal class Meta
{
public class ToolInfo
{
public ToolInfo(string vendor)
{
Vendor = vendor;
}
public ToolInfo(string vendor, string name)
{
Vendor = vendor;
Name = name;
}
public string Name { get; }
public string Vendor { get; }
}
public static uint MagicNumber => 119734787U;
public static uint Version => 66048U;
public static uint Revision => 2U;
public static uint OpCodeMask => 65535U;
public static uint WordCountShift => 16U;
public static IReadOnlyDictionary<int, ToolInfo> Tools => toolInfos_;
private readonly static Dictionary<int, ToolInfo> toolInfos_ = new Dictionary<int, ToolInfo>
{
{ 0, new ToolInfo("Khronos") },
{ 1, new ToolInfo("LunarG") },
{ 2, new ToolInfo("Valve") },
{ 3, new ToolInfo("Codeplay") },
{ 4, new ToolInfo("NVIDIA") },
{ 5, new ToolInfo("ARM") },
{ 6, new ToolInfo("Khronos", "LLVM/SPIR-V Translator") },
{ 7, new ToolInfo("Khronos", "SPIR-V Tools Assembler") },
{ 8, new ToolInfo("Khronos", "Glslang Reference Front End") },
{ 9, new ToolInfo("Qualcomm") },
{ 10, new ToolInfo("AMD") },
{ 11, new ToolInfo("Intel") },
{ 12, new ToolInfo("Imagination") },
{ 13, new ToolInfo("Google", "Shaderc over Glslang") },
{ 14, new ToolInfo("Google", "spiregg") },
{ 15, new ToolInfo("Google", "rspirv") },
{ 16, new ToolInfo("X-LEGEND", "Mesa-IR/SPIR-V Translator") },
{ 17, new ToolInfo("Khronos", "SPIR-V Tools Linker") },
};
}
}

View File

@ -0,0 +1,428 @@
using System.Collections.Generic;
using System.Text;
namespace SpirV
{
public class Type
{
public virtual StringBuilder ToString(StringBuilder sb)
{
return sb;
}
}
public class VoidType : Type
{
public override string ToString()
{
return "void";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append("void");
}
}
public class ScalarType : Type
{
}
public class BoolType : ScalarType
{
public override string ToString()
{
return "bool";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append("bool");
}
}
public class IntegerType : ScalarType
{
public IntegerType (int width, bool signed)
{
Width = width;
Signed = signed;
}
public override string ToString()
{
if (Signed)
{
return $"i{Width}";
}
else
{
return $"u{Width}";
}
}
public override StringBuilder ToString(StringBuilder sb)
{
if (Signed)
{
sb.Append('i').Append(Width);
}
else
{
sb.Append('u').Append(Width);
}
return sb;
}
public int Width { get; }
public bool Signed { get; }
}
public class FloatingPointType : ScalarType
{
public FloatingPointType (int width)
{
Width = width;
}
public override string ToString()
{
return $"f{Width}";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append('f').Append(Width);
}
public int Width { get; }
}
public class VectorType : Type
{
public VectorType (ScalarType scalarType, int componentCount)
{
ComponentType = scalarType;
ComponentCount = componentCount;
}
public override string ToString()
{
return $"{ComponentType}_{ComponentCount}";
}
public override StringBuilder ToString(StringBuilder sb)
{
return ComponentType.ToString(sb).Append('_').Append(ComponentCount);
}
public ScalarType ComponentType { get; }
public int ComponentCount { get; }
}
public class MatrixType : Type
{
public MatrixType (VectorType vectorType, int columnCount)
{
ColumnType = vectorType;
ColumnCount = columnCount;
}
public override string ToString ()
{
return $"{ColumnType}x{ColumnCount}";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append(ColumnType).Append('x').Append(ColumnCount);
}
public VectorType ColumnType { get; }
public int ColumnCount { get; }
public int RowCount => ColumnType.ComponentCount;
}
public class ImageType : Type
{
public ImageType (Type sampledType, Dim dim, int depth, bool isArray, bool isMultisampled, int sampleCount,
ImageFormat imageFormat, AccessQualifier accessQualifier)
{
SampledType = sampledType;
Dim = dim;
Depth = depth;
IsArray = isArray;
IsMultisampled = isMultisampled;
SampleCount = sampleCount;
Format = imageFormat;
AccessQualifier = accessQualifier;
}
public override string ToString ()
{
StringBuilder sb = new StringBuilder ();
ToString(sb);
return sb.ToString();
}
public override StringBuilder ToString(StringBuilder sb)
{
switch (AccessQualifier)
{
case AccessQualifier.ReadWrite:
sb.Append("read_write ");
break;
case AccessQualifier.WriteOnly:
sb.Append("write_only ");
break;
case AccessQualifier.ReadOnly:
sb.Append("read_only ");
break;
}
sb.Append("Texture");
switch (Dim)
{
case Dim.Dim1D:
sb.Append("1D");
break;
case Dim.Dim2D:
sb.Append("2D");
break;
case Dim.Dim3D:
sb.Append("3D");
break;
case Dim.Cube:
sb.Append("Cube");
break;
}
if (IsMultisampled)
{
sb.Append("MS");
}
if (IsArray)
{
sb.Append("Array");
}
return sb;
}
public Type SampledType { get; }
public Dim Dim { get; }
public int Depth { get; }
public bool IsArray { get; }
public bool IsMultisampled { get; }
public int SampleCount { get; }
public ImageFormat Format { get; }
public AccessQualifier AccessQualifier { get; }
}
public class SamplerType : Type
{
public override string ToString()
{
return "sampler";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append("sampler");
}
}
public class SampledImageType : Type
{
public SampledImageType (ImageType imageType)
{
ImageType = imageType;
}
public override string ToString()
{
return $"{ImageType}Sampled";
}
public override StringBuilder ToString(StringBuilder sb)
{
return ImageType.ToString(sb).Append("Sampled");
}
public ImageType ImageType { get; }
}
public class ArrayType : Type
{
public ArrayType (Type elementType, int elementCount)
{
ElementType = elementType;
ElementCount = elementCount;
}
public override string ToString()
{
return $"{ElementType}[{ElementCount}]";
}
public override StringBuilder ToString(StringBuilder sb)
{
return ElementType.ToString(sb).Append('[').Append(ElementCount).Append(']');
}
public int ElementCount { get; }
public Type ElementType { get; }
}
public class RuntimeArrayType : Type
{
public RuntimeArrayType(Type elementType)
{
ElementType = elementType;
}
public Type ElementType { get; }
}
public class StructType : Type
{
public StructType(IReadOnlyList<Type> memberTypes)
{
MemberTypes = memberTypes;
memberNames_ = new List<string>();
for (int i = 0; i < memberTypes.Count; ++i)
{
memberNames_.Add(string.Empty);
}
}
public void SetMemberName(uint member, string name)
{
memberNames_[(int)member] = name;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
ToString(sb);
return sb.ToString();
}
public override StringBuilder ToString(StringBuilder sb)
{
sb.Append("struct {");
for (int i = 0; i < MemberTypes.Count; ++i)
{
Type memberType = MemberTypes[i];
memberType.ToString(sb);
if (!string.IsNullOrEmpty(memberNames_[i]))
{
sb.Append(' ');
sb.Append(MemberNames[i]);
}
sb.Append(';');
if (i < (MemberTypes.Count - 1))
{
sb.Append(' ');
}
}
sb.Append('}');
return sb;
}
public IReadOnlyList<Type> MemberTypes { get; }
public IReadOnlyList<string> MemberNames => memberNames_;
private List<string> memberNames_;
}
public class OpaqueType : Type
{
}
public class PointerType : Type
{
public PointerType(StorageClass storageClass, Type type)
{
StorageClass = storageClass;
Type = type;
}
public PointerType(StorageClass storageClass)
{
StorageClass = storageClass;
}
public void ResolveForwardReference(Type t)
{
Type = t;
}
public override string ToString()
{
if (Type == null)
{
return $"{StorageClass} *";
}
else
{
return $"{StorageClass} {Type}*";
}
}
public override StringBuilder ToString(StringBuilder sb)
{
sb.Append(StorageClass.ToString()).Append(' ');
if (Type != null)
{
Type.ToString(sb);
}
sb.Append('*');
return sb;
}
public StorageClass StorageClass { get; }
public Type Type { get; private set; }
}
public class FunctionType : Type
{
public FunctionType(Type returnType, IReadOnlyList<Type> parameterTypes)
{
ReturnType = returnType;
ParameterTypes = parameterTypes;
}
public Type ReturnType { get; }
public IReadOnlyList<Type> ParameterTypes { get; }
}
public class EventType : Type
{
}
public class DeviceEventType : Type
{
}
public class ReserveIdType : Type
{
}
public class QueueType : Type
{
}
public class PipeType : Type
{
}
public class PipeStorage : Type
{
}
public class NamedBarrier : Type
{
}
}