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

@ -49,6 +49,16 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AudioClipConverter.cs" />
<Compile Include="CSspv\Disassembler.cs" />
<Compile Include="CSspv\EnumValuesExtensions.cs" />
<Compile Include="CSspv\Instruction.cs" />
<Compile Include="CSspv\Module.cs" />
<Compile Include="CSspv\OperandType.cs" />
<Compile Include="CSspv\ParsedInstruction.cs" />
<Compile Include="CSspv\Reader.cs" />
<Compile Include="CSspv\SpirV.Core.Grammar.cs" />
<Compile Include="CSspv\SpirV.Meta.cs" />
<Compile Include="CSspv\Types.cs" />
<Compile Include="FMOD Studio API\fmod.cs" />
<Compile Include="FMOD Studio API\fmod_dsp.cs" />
<Compile Include="FMOD Studio API\fmod_errors.cs" />
@ -57,6 +67,10 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScriptDumper.cs" />
<Compile Include="ShaderConverter.cs" />
<Compile Include="Smolv\OpData.cs" />
<Compile Include="Smolv\SmolvDecoder.cs" />
<Compile Include="Smolv\SpvOp.cs" />
<Compile Include="SpirVShaderConverter.cs" />
<Compile Include="SpriteHelper.cs" />
<Compile Include="Texture2DConverter.cs" />
<Compile Include="Texture2DExtensions.cs" />

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
{
}
}

View File

@ -21,13 +21,13 @@ namespace AssetStudio
using (var blobReader = new BinaryReader(new MemoryStream(decompressedBytes)))
{
var program = new ShaderProgram(blobReader, shader.version);
return program.Export(Encoding.UTF8.GetString(shader.m_Script));
return header + program.Export(Encoding.UTF8.GetString(shader.m_Script));
}
}
if (shader.compressedBlob != null) //5.5 and up
{
return ConvertSerializedShader(shader);
return header + ConvertSerializedShader(shader);
}
return header + Encoding.UTF8.GetString(shader.m_Script);
@ -727,14 +727,14 @@ namespace AssetStudio
case ShaderGpuProgramType.kShaderGpuProgramDX11HullSM50:
case ShaderGpuProgramType.kShaderGpuProgramDX11DomainSM50:
{
int start = 6;
/*int start = 6;
if (m_Version == 201509030) // 5.3
{
start = 5;
}
var buff = new byte[m_ProgramCode.Length - start];
Buffer.BlockCopy(m_ProgramCode, start, buff, 0, buff.Length);
/*var shaderBytecode = new ShaderBytecode(buff);
var shaderBytecode = new ShaderBytecode(buff);
sb.Append(shaderBytecode.Disassemble());*/
sb.Append("// shader disassembly not supported on DXBC");
break;
@ -755,7 +755,14 @@ namespace AssetStudio
}
break;
case ShaderGpuProgramType.kShaderGpuProgramSPIRV:
sb.Append("// shader disassembly not supported on SPIR-V\n");
try
{
sb.Append(SpirVShaderConverter.Convert(m_ProgramCode));
}
catch (Exception e)
{
sb.Append($"// disassembly error {e.Message}\n");
}
break;
case ShaderGpuProgramType.kShaderGpuProgramConsoleVS:
case ShaderGpuProgramType.kShaderGpuProgramConsoleFS:

View File

@ -0,0 +1,365 @@
namespace Smolv
{
public struct OpData
{
public OpData(byte hasResult, byte hasType, sbyte deltaFromResult, byte varrest)
{
this.hasResult = hasResult;
this.hasType = hasType;
this.deltaFromResult = deltaFromResult;
this.varrest = varrest;
}
/// <summary>
/// Does it have result ID?
/// </summary>
public byte hasResult;
/// <summary>
/// Does it have type ID?
/// </summary>
public byte hasType;
/// <summary>
/// How many words after (optional) type+result to write out as deltas from result?
/// </summary>
public sbyte deltaFromResult;
/// <summary>
/// Should the rest of words be written in varint encoding?
/// </summary>
public byte varrest;
public static readonly OpData[] SpirvOpData =
{
new OpData(0, 0, 0, 0), // Nop
new OpData(1, 1, 0, 0), // Undef
new OpData(0, 0, 0, 0), // SourceContinued
new OpData(0, 0, 0, 1), // Source
new OpData(0, 0, 0, 0), // SourceExtension
new OpData(0, 0, 0, 0), // Name
new OpData(0, 0, 0, 0), // MemberName
new OpData(0, 0, 0, 0), // String
new OpData(0, 0, 0, 1), // Line
new OpData(1, 1, 0, 0), // #9
new OpData(0, 0, 0, 0), // Extension
new OpData(1, 0, 0, 0), // ExtInstImport
new OpData(1, 1, 0, 1), // ExtInst
new OpData(1, 1, 2, 1), // VectorShuffleCompact - new in SMOLV
new OpData(0, 0, 0, 1), // MemoryModel
new OpData(0, 0, 0, 1), // EntryPoint
new OpData(0, 0, 0, 1), // ExecutionMode
new OpData(0, 0, 0, 1), // Capability
new OpData(1, 1, 0, 0), // #18
new OpData(1, 0, 0, 1), // TypeVoid
new OpData(1, 0, 0, 1), // TypeBool
new OpData(1, 0, 0, 1), // TypeInt
new OpData(1, 0, 0, 1), // TypeFloat
new OpData(1, 0, 0, 1), // TypeVector
new OpData(1, 0, 0, 1), // TypeMatrix
new OpData(1, 0, 0, 1), // TypeImage
new OpData(1, 0, 0, 1), // TypeSampler
new OpData(1, 0, 0, 1), // TypeSampledImage
new OpData(1, 0, 0, 1), // TypeArray
new OpData(1, 0, 0, 1), // TypeRuntimeArray
new OpData(1, 0, 0, 1), // TypeStruct
new OpData(1, 0, 0, 1), // TypeOpaque
new OpData(1, 0, 0, 1), // TypePointer
new OpData(1, 0, 0, 1), // TypeFunction
new OpData(1, 0, 0, 1), // TypeEvent
new OpData(1, 0, 0, 1), // TypeDeviceEvent
new OpData(1, 0, 0, 1), // TypeReserveId
new OpData(1, 0, 0, 1), // TypeQueue
new OpData(1, 0, 0, 1), // TypePipe
new OpData(0, 0, 0, 1), // TypeForwardPointer
new OpData(1, 1, 0, 0), // #40
new OpData(1, 1, 0, 0), // ConstantTrue
new OpData(1, 1, 0, 0), // ConstantFalse
new OpData(1, 1, 0, 0), // Constant
new OpData(1, 1, 9, 0), // ConstantComposite
new OpData(1, 1, 0, 1), // ConstantSampler
new OpData(1, 1, 0, 0), // ConstantNull
new OpData(1, 1, 0, 0), // #47
new OpData(1, 1, 0, 0), // SpecConstantTrue
new OpData(1, 1, 0, 0), // SpecConstantFalse
new OpData(1, 1, 0, 0), // SpecConstant
new OpData(1, 1, 9, 0), // SpecConstantComposite
new OpData(1, 1, 0, 0), // SpecConstantOp
new OpData(1, 1, 0, 0), // #53
new OpData(1, 1, 0, 1), // Function
new OpData(1, 1, 0, 0), // FunctionParameter
new OpData(0, 0, 0, 0), // FunctionEnd
new OpData(1, 1, 9, 0), // FunctionCall
new OpData(1, 1, 0, 0), // #58
new OpData(1, 1, 0, 1), // Variable
new OpData(1, 1, 0, 0), // ImageTexelPointer
new OpData(1, 1, 1, 1), // Load
new OpData(0, 0, 2, 1), // Store
new OpData(0, 0, 0, 0), // CopyMemory
new OpData(0, 0, 0, 0), // CopyMemorySized
new OpData(1, 1, 0, 1), // AccessChain
new OpData(1, 1, 0, 0), // InBoundsAccessChain
new OpData(1, 1, 0, 0), // PtrAccessChain
new OpData(1, 1, 0, 0), // ArrayLength
new OpData(1, 1, 0, 0), // GenericPtrMemSemantics
new OpData(1, 1, 0, 0), // InBoundsPtrAccessChain
new OpData(0, 0, 0, 1), // Decorate
new OpData(0, 0, 0, 1), // MemberDecorate
new OpData(1, 0, 0, 0), // DecorationGroup
new OpData(0, 0, 0, 0), // GroupDecorate
new OpData(0, 0, 0, 0), // GroupMemberDecorate
new OpData(1, 1, 0, 0), // #76
new OpData(1, 1, 1, 1), // VectorExtractDynamic
new OpData(1, 1, 2, 1), // VectorInsertDynamic
new OpData(1, 1, 2, 1), // VectorShuffle
new OpData(1, 1, 9, 0), // CompositeConstruct
new OpData(1, 1, 1, 1), // CompositeExtract
new OpData(1, 1, 2, 1), // CompositeInsert
new OpData(1, 1, 1, 0), // CopyObject
new OpData(1, 1, 0, 0), // Transpose
new OpData(1, 1, 0, 0), // #85
new OpData(1, 1, 0, 0), // SampledImage
new OpData(1, 1, 2, 1), // ImageSampleImplicitLod
new OpData(1, 1, 2, 1), // ImageSampleExplicitLod
new OpData(1, 1, 3, 1), // ImageSampleDrefImplicitLod
new OpData(1, 1, 3, 1), // ImageSampleDrefExplicitLod
new OpData(1, 1, 2, 1), // ImageSampleProjImplicitLod
new OpData(1, 1, 2, 1), // ImageSampleProjExplicitLod
new OpData(1, 1, 3, 1), // ImageSampleProjDrefImplicitLod
new OpData(1, 1, 3, 1), // ImageSampleProjDrefExplicitLod
new OpData(1, 1, 2, 1), // ImageFetch
new OpData(1, 1, 3, 1), // ImageGather
new OpData(1, 1, 3, 1), // ImageDrefGather
new OpData(1, 1, 2, 1), // ImageRead
new OpData(0, 0, 3, 1), // ImageWrite
new OpData(1, 1, 1, 0), // Image
new OpData(1, 1, 1, 0), // ImageQueryFormat
new OpData(1, 1, 1, 0), // ImageQueryOrder
new OpData(1, 1, 2, 0), // ImageQuerySizeLod
new OpData(1, 1, 1, 0), // ImageQuerySize
new OpData(1, 1, 2, 0), // ImageQueryLod
new OpData(1, 1, 1, 0), // ImageQueryLevels
new OpData(1, 1, 1, 0), // ImageQuerySamples
new OpData(1, 1, 0, 0), // #108
new OpData(1, 1, 1, 0), // ConvertFToU
new OpData(1, 1, 1, 0), // ConvertFToS
new OpData(1, 1, 1, 0), // ConvertSToF
new OpData(1, 1, 1, 0), // ConvertUToF
new OpData(1, 1, 1, 0), // UConvert
new OpData(1, 1, 1, 0), // SConvert
new OpData(1, 1, 1, 0), // FConvert
new OpData(1, 1, 1, 0), // QuantizeToF16
new OpData(1, 1, 1, 0), // ConvertPtrToU
new OpData(1, 1, 1, 0), // SatConvertSToU
new OpData(1, 1, 1, 0), // SatConvertUToS
new OpData(1, 1, 1, 0), // ConvertUToPtr
new OpData(1, 1, 1, 0), // PtrCastToGeneric
new OpData(1, 1, 1, 0), // GenericCastToPtr
new OpData(1, 1, 1, 1), // GenericCastToPtrExplicit
new OpData(1, 1, 1, 0), // Bitcast
new OpData(1, 1, 0, 0), // #125
new OpData(1, 1, 1, 0), // SNegate
new OpData(1, 1, 1, 0), // FNegate
new OpData(1, 1, 2, 0), // IAdd
new OpData(1, 1, 2, 0), // FAdd
new OpData(1, 1, 2, 0), // ISub
new OpData(1, 1, 2, 0), // FSub
new OpData(1, 1, 2, 0), // IMul
new OpData(1, 1, 2, 0), // FMul
new OpData(1, 1, 2, 0), // UDiv
new OpData(1, 1, 2, 0), // SDiv
new OpData(1, 1, 2, 0), // FDiv
new OpData(1, 1, 2, 0), // UMod
new OpData(1, 1, 2, 0), // SRem
new OpData(1, 1, 2, 0), // SMod
new OpData(1, 1, 2, 0), // FRem
new OpData(1, 1, 2, 0), // FMod
new OpData(1, 1, 2, 0), // VectorTimesScalar
new OpData(1, 1, 2, 0), // MatrixTimesScalar
new OpData(1, 1, 2, 0), // VectorTimesMatrix
new OpData(1, 1, 2, 0), // MatrixTimesVector
new OpData(1, 1, 2, 0), // MatrixTimesMatrix
new OpData(1, 1, 2, 0), // OuterProduct
new OpData(1, 1, 2, 0), // Dot
new OpData(1, 1, 2, 0), // IAddCarry
new OpData(1, 1, 2, 0), // ISubBorrow
new OpData(1, 1, 2, 0), // UMulExtended
new OpData(1, 1, 2, 0), // SMulExtended
new OpData(1, 1, 0, 0), // #153
new OpData(1, 1, 1, 0), // Any
new OpData(1, 1, 1, 0), // All
new OpData(1, 1, 1, 0), // IsNan
new OpData(1, 1, 1, 0), // IsInf
new OpData(1, 1, 1, 0), // IsFinite
new OpData(1, 1, 1, 0), // IsNormal
new OpData(1, 1, 1, 0), // SignBitSet
new OpData(1, 1, 2, 0), // LessOrGreater
new OpData(1, 1, 2, 0), // Ordered
new OpData(1, 1, 2, 0), // Unordered
new OpData(1, 1, 2, 0), // LogicalEqual
new OpData(1, 1, 2, 0), // LogicalNotEqual
new OpData(1, 1, 2, 0), // LogicalOr
new OpData(1, 1, 2, 0), // LogicalAnd
new OpData(1, 1, 1, 0), // LogicalNot
new OpData(1, 1, 3, 0), // Select
new OpData(1, 1, 2, 0), // IEqual
new OpData(1, 1, 2, 0), // INotEqual
new OpData(1, 1, 2, 0), // UGreaterThan
new OpData(1, 1, 2, 0), // SGreaterThan
new OpData(1, 1, 2, 0), // UGreaterThanEqual
new OpData(1, 1, 2, 0), // SGreaterThanEqual
new OpData(1, 1, 2, 0), // ULessThan
new OpData(1, 1, 2, 0), // SLessThan
new OpData(1, 1, 2, 0), // ULessThanEqual
new OpData(1, 1, 2, 0), // SLessThanEqual
new OpData(1, 1, 2, 0), // FOrdEqual
new OpData(1, 1, 2, 0), // FUnordEqual
new OpData(1, 1, 2, 0), // FOrdNotEqual
new OpData(1, 1, 2, 0), // FUnordNotEqual
new OpData(1, 1, 2, 0), // FOrdLessThan
new OpData(1, 1, 2, 0), // FUnordLessThan
new OpData(1, 1, 2, 0), // FOrdGreaterThan
new OpData(1, 1, 2, 0), // FUnordGreaterThan
new OpData(1, 1, 2, 0), // FOrdLessThanEqual
new OpData(1, 1, 2, 0), // FUnordLessThanEqual
new OpData(1, 1, 2, 0), // FOrdGreaterThanEqual
new OpData(1, 1, 2, 0), // FUnordGreaterThanEqual
new OpData(1, 1, 0, 0), // #192
new OpData(1, 1, 0, 0), // #193
new OpData(1, 1, 2, 0), // ShiftRightLogical
new OpData(1, 1, 2, 0), // ShiftRightArithmetic
new OpData(1, 1, 2, 0), // ShiftLeftLogical
new OpData(1, 1, 2, 0), // BitwiseOr
new OpData(1, 1, 2, 0), // BitwiseXor
new OpData(1, 1, 2, 0), // BitwiseAnd
new OpData(1, 1, 1, 0), // Not
new OpData(1, 1, 4, 0), // BitFieldInsert
new OpData(1, 1, 3, 0), // BitFieldSExtract
new OpData(1, 1, 3, 0), // BitFieldUExtract
new OpData(1, 1, 1, 0), // BitReverse
new OpData(1, 1, 1, 0), // BitCount
new OpData(1, 1, 0, 0), // #206
new OpData(1, 1, 0, 0), // DPdx
new OpData(1, 1, 0, 0), // DPdy
new OpData(1, 1, 0, 0), // Fwidth
new OpData(1, 1, 0, 0), // DPdxFine
new OpData(1, 1, 0, 0), // DPdyFine
new OpData(1, 1, 0, 0), // FwidthFine
new OpData(1, 1, 0, 0), // DPdxCoarse
new OpData(1, 1, 0, 0), // DPdyCoarse
new OpData(1, 1, 0, 0), // FwidthCoarse
new OpData(1, 1, 0, 0), // #216
new OpData(1, 1, 0, 0), // #217
new OpData(0, 0, 0, 0), // EmitVertex
new OpData(0, 0, 0, 0), // EndPrimitive
new OpData(0, 0, 0, 0), // EmitStreamVertex
new OpData(0, 0, 0, 0), // EndStreamPrimitive
new OpData(1, 1, 0, 0), // #222
new OpData(1, 1, 0, 0), // #223
new OpData(0, 0, -3, 0), // ControlBarrier
new OpData(0, 0, -2, 0), // MemoryBarrier
new OpData(1, 1, 0, 0), // #226
new OpData(1, 1, 0, 0), // AtomicLoad
new OpData(0, 0, 0, 0), // AtomicStore
new OpData(1, 1, 0, 0), // AtomicExchange
new OpData(1, 1, 0, 0), // AtomicCompareExchange
new OpData(1, 1, 0, 0), // AtomicCompareExchangeWeak
new OpData(1, 1, 0, 0), // AtomicIIncrement
new OpData(1, 1, 0, 0), // AtomicIDecrement
new OpData(1, 1, 0, 0), // AtomicIAdd
new OpData(1, 1, 0, 0), // AtomicISub
new OpData(1, 1, 0, 0), // AtomicSMin
new OpData(1, 1, 0, 0), // AtomicUMin
new OpData(1, 1, 0, 0), // AtomicSMax
new OpData(1, 1, 0, 0), // AtomicUMax
new OpData(1, 1, 0, 0), // AtomicAnd
new OpData(1, 1, 0, 0), // AtomicOr
new OpData(1, 1, 0, 0), // AtomicXor
new OpData(1, 1, 0, 0), // #243
new OpData(1, 1, 0, 0), // #244
new OpData(1, 1, 0, 0), // Phi
new OpData(0, 0, -2, 1), // LoopMerge
new OpData(0, 0, -1, 1), // SelectionMerge
new OpData(1, 0, 0, 0), // Label
new OpData(0, 0, -1, 0), // Branch
new OpData(0, 0, -3, 1), // BranchConditional
new OpData(0, 0, 0, 0), // Switch
new OpData(0, 0, 0, 0), // Kill
new OpData(0, 0, 0, 0), // Return
new OpData(0, 0, 0, 0), // ReturnValue
new OpData(0, 0, 0, 0), // Unreachable
new OpData(0, 0, 0, 0), // LifetimeStart
new OpData(0, 0, 0, 0), // LifetimeStop
new OpData(1, 1, 0, 0), // #258
new OpData(1, 1, 0, 0), // GroupAsyncCopy
new OpData(0, 0, 0, 0), // GroupWaitEvents
new OpData(1, 1, 0, 0), // GroupAll
new OpData(1, 1, 0, 0), // GroupAny
new OpData(1, 1, 0, 0), // GroupBroadcast
new OpData(1, 1, 0, 0), // GroupIAdd
new OpData(1, 1, 0, 0), // GroupFAdd
new OpData(1, 1, 0, 0), // GroupFMin
new OpData(1, 1, 0, 0), // GroupUMin
new OpData(1, 1, 0, 0), // GroupSMin
new OpData(1, 1, 0, 0), // GroupFMax
new OpData(1, 1, 0, 0), // GroupUMax
new OpData(1, 1, 0, 0), // GroupSMax
new OpData(1, 1, 0, 0), // #272
new OpData(1, 1, 0, 0), // #273
new OpData(1, 1, 0, 0), // ReadPipe
new OpData(1, 1, 0, 0), // WritePipe
new OpData(1, 1, 0, 0), // ReservedReadPipe
new OpData(1, 1, 0, 0), // ReservedWritePipe
new OpData(1, 1, 0, 0), // ReserveReadPipePackets
new OpData(1, 1, 0, 0), // ReserveWritePipePackets
new OpData(0, 0, 0, 0), // CommitReadPipe
new OpData(0, 0, 0, 0), // CommitWritePipe
new OpData(1, 1, 0, 0), // IsValidReserveId
new OpData(1, 1, 0, 0), // GetNumPipePackets
new OpData(1, 1, 0, 0), // GetMaxPipePackets
new OpData(1, 1, 0, 0), // GroupReserveReadPipePackets
new OpData(1, 1, 0, 0), // GroupReserveWritePipePackets
new OpData(0, 0, 0, 0), // GroupCommitReadPipe
new OpData(0, 0, 0, 0), // GroupCommitWritePipe
new OpData(1, 1, 0, 0), // #289
new OpData(1, 1, 0, 0), // #290
new OpData(1, 1, 0, 0), // EnqueueMarker
new OpData(1, 1, 0, 0), // EnqueueKernel
new OpData(1, 1, 0, 0), // GetKernelNDrangeSubGroupCount
new OpData(1, 1, 0, 0), // GetKernelNDrangeMaxSubGroupSize
new OpData(1, 1, 0, 0), // GetKernelWorkGroupSize
new OpData(1, 1, 0, 0), // GetKernelPreferredWorkGroupSizeMultiple
new OpData(0, 0, 0, 0), // RetainEvent
new OpData(0, 0, 0, 0), // ReleaseEvent
new OpData(1, 1, 0, 0), // CreateUserEvent
new OpData(1, 1, 0, 0), // IsValidEvent
new OpData(0, 0, 0, 0), // SetUserEventStatus
new OpData(0, 0, 0, 0), // CaptureEventProfilingInfo
new OpData(1, 1, 0, 0), // GetDefaultQueue
new OpData(1, 1, 0, 0), // BuildNDRange
new OpData(1, 1, 2, 1), // ImageSparseSampleImplicitLod
new OpData(1, 1, 2, 1), // ImageSparseSampleExplicitLod
new OpData(1, 1, 3, 1), // ImageSparseSampleDrefImplicitLod
new OpData(1, 1, 3, 1), // ImageSparseSampleDrefExplicitLod
new OpData(1, 1, 2, 1), // ImageSparseSampleProjImplicitLod
new OpData(1, 1, 2, 1), // ImageSparseSampleProjExplicitLod
new OpData(1, 1, 3, 1), // ImageSparseSampleProjDrefImplicitLod
new OpData(1, 1, 3, 1), // ImageSparseSampleProjDrefExplicitLod
new OpData(1, 1, 2, 1), // ImageSparseFetch
new OpData(1, 1, 3, 1), // ImageSparseGather
new OpData(1, 1, 3, 1), // ImageSparseDrefGather
new OpData(1, 1, 1, 0), // ImageSparseTexelsResident
new OpData(0, 0, 0, 0), // NoLine
new OpData(1, 1, 0, 0), // AtomicFlagTestAndSet
new OpData(0, 0, 0, 0), // AtomicFlagClear
new OpData(1, 1, 0, 0), // ImageSparseRead
new OpData(1, 1, 0, 0), // SizeOf
new OpData(1, 1, 0, 0), // TypePipeStorage
new OpData(1, 1, 0, 0), // ConstantPipeStorage
new OpData(1, 1, 0, 0), // CreatePipeFromPipeStorage
new OpData(1, 1, 0, 0), // GetKernelLocalSizeForSubgroupCount
new OpData(1, 1, 0, 0), // GetKernelMaxNumSubgroups
new OpData(1, 1, 0, 0), // TypeNamedBarrier
new OpData(1, 1, 0, 1), // NamedBarrierInitialize
new OpData(0, 0, -2, 1), // MemoryNamedBarrier
new OpData(1, 1, 0, 0), // ModuleProcessed
};
};
}

View File

@ -0,0 +1,479 @@
using System;
using System.IO;
using System.Text;
namespace Smolv
{
public static class SmolvDecoder
{
public static int GetDecodedBufferSize(byte[] data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (!CheckSmolHeader(data))
{
return 0;
}
int size = BitConverter.ToInt32(data, 5 * sizeof(uint));
return size;
}
public static int GetDecodedBufferSize(Stream stream)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
if (!stream.CanSeek)
{
throw new ArgumentException(nameof(stream));
}
if (stream.Position + HeaderSize > stream.Length)
{
return 0;
}
long initPosition = stream.Position;
stream.Position += HeaderSize - sizeof(uint);
int size = stream.ReadByte() | stream.ReadByte() << 8 | stream.ReadByte() << 16 | stream.ReadByte() << 24;
stream.Position = initPosition;
return size;
}
public static byte[] Decode(byte[] data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
int bufferSize = GetDecodedBufferSize(data);
if (bufferSize == 0)
{
// invalid SMOL-V
return null;
}
byte[] output = new byte[bufferSize];
if (Decode(data, output))
{
return output;
}
return null;
}
public static bool Decode(byte[] data, byte[] output)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (output == null)
{
throw new ArgumentNullException(nameof(output));
}
int bufferSize = GetDecodedBufferSize(data);
if (bufferSize > output.Length)
{
return false;
}
using (MemoryStream outputStream = new MemoryStream(output))
{
return Decode(data, outputStream);
}
}
public static bool Decode(byte[] data, Stream outputStream)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
using (MemoryStream inputStream = new MemoryStream(data))
{
return Decode(inputStream, data.Length, outputStream);
}
}
public static bool Decode(Stream inputStream, int inputSize, Stream outputStream)
{
if (inputStream == null)
{
throw new ArgumentNullException(nameof(inputStream));
}
if (outputStream == null)
{
throw new ArgumentNullException(nameof(outputStream));
}
if (inputStream.Length < HeaderSize)
{
return false;
}
using (BinaryReader input = new BinaryReader(inputStream, Encoding.UTF8, true))
{
using (BinaryWriter output = new BinaryWriter(outputStream, Encoding.UTF8, true))
{
long inputEndPosition = input.BaseStream.Position + inputSize;
long outputStartPosition = output.BaseStream.Position;
// Header
output.Write(SpirVHeaderMagic);
input.BaseStream.Position += sizeof(uint);
uint version = input.ReadUInt32();
output.Write(version);
uint generator = input.ReadUInt32();
output.Write(generator);
int bound = input.ReadInt32();
output.Write(bound);
uint schema = input.ReadUInt32();
output.Write(schema);
int decodedSize = input.ReadInt32();
// Body
int prevResult = 0;
int prevDecorate = 0;
while (input.BaseStream.Position < inputEndPosition)
{
// read length + opcode
if (!ReadLengthOp(input, out uint instrLen, out SpvOp op))
{
return false;
}
bool wasSwizzle = op == SpvOp.VectorShuffleCompact;
if (wasSwizzle)
{
op = SpvOp.VectorShuffle;
}
output.Write((instrLen << 16) | (uint)op);
uint ioffs = 1;
// read type as varint, if we have it
if (op.OpHasType())
{
if (!ReadVarint(input, out uint value))
{
return false;
}
output.Write(value);
ioffs++;
}
// read result as delta+varint, if we have it
if (op.OpHasResult())
{
if (!ReadVarint(input, out uint value))
{
return false;
}
int zds = prevResult + ZigDecode(value);
output.Write(zds);
prevResult = zds;
ioffs++;
}
// Decorate: IDs relative to previous decorate
if (op == SpvOp.Decorate || op == SpvOp.MemberDecorate)
{
if (!ReadVarint(input, out uint value))
{
return false;
}
int zds = prevDecorate + unchecked((int)value);
output.Write(zds);
prevDecorate = zds;
ioffs++;
}
// Read this many IDs, that are relative to result ID
int relativeCount = op.OpDeltaFromResult();
bool inverted = false;
if (relativeCount < 0)
{
inverted = true;
relativeCount = -relativeCount;
}
for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs)
{
if (!ReadVarint(input, out uint value))
{
return false;
}
int zd = inverted ? ZigDecode(value) : unchecked((int)value);
output.Write(prevResult - zd);
}
if (wasSwizzle && instrLen <= 9)
{
uint swizzle = input.ReadByte();
if (instrLen > 5) output.Write(swizzle >> 6);
if (instrLen > 6) output.Write((swizzle >> 4) & 3);
if (instrLen > 7) output.Write((swizzle >> 2) & 3);
if (instrLen > 8) output.Write(swizzle & 3);
}
else if (op.OpVarRest())
{
// read rest of words with variable encoding
for (; ioffs < instrLen; ++ioffs)
{
if (!ReadVarint(input, out uint value))
{
return false;
}
output.Write(value);
}
}
else
{
// read rest of words without any encoding
for (; ioffs < instrLen; ++ioffs)
{
if (input.BaseStream.Position + 4 > input.BaseStream.Length)
{
return false;
}
uint val = input.ReadUInt32();
output.Write(val);
}
}
}
if (output.BaseStream.Position != outputStartPosition + decodedSize)
{
// something went wrong during decoding? we should have decoded to exact output size
return false;
}
return true;
}
}
}
private static bool CheckSmolHeader(byte[] data)
{
if (!CheckGenericHeader(data, SmolHeaderMagic))
{
return false;
}
return true;
}
private static bool CheckGenericHeader(byte[] data, uint expectedMagic)
{
if (data == null)
{
return false;
}
if (data.Length < HeaderSize)
{
return false;
}
uint headerMagic = BitConverter.ToUInt32(data, 0 * sizeof(uint));
if (headerMagic != expectedMagic)
{
return false;
}
uint headerVersion = BitConverter.ToUInt32(data, 1 * sizeof(uint));
if (headerVersion < 0x00010000 || headerVersion > 0x00010300)
{
// only support 1.0 through 1.3
return false;
}
return true;
}
private static bool ReadVarint(BinaryReader input, out uint value)
{
uint v = 0;
int shift = 0;
while (input.BaseStream.Position < input.BaseStream.Length)
{
byte b = input.ReadByte();
v |= unchecked((uint)(b & 127) << shift);
shift += 7;
if ((b & 128) == 0)
{
break;
}
}
value = v;
// @TODO: report failures
return true;
}
private static bool ReadLengthOp(BinaryReader input, out uint len, out SpvOp op)
{
len = default;
op = default;
if (!ReadVarint(input, out uint value))
{
return false;
}
len = ((value >> 20) << 4) | ((value >> 4) & 0xF);
op = (SpvOp) (((value >> 4) & 0xFFF0) | (value & 0xF));
op = RemapOp(op);
len = DecodeLen(op, len);
return true;
}
/// <summary>
/// Remap most common Op codes (Load, Store, Decorate, VectorShuffle etc.) to be in &lt; 16 range, for
/// more compact varint encoding. This basically swaps rarely used op values that are &lt; 16 with the
/// ones that are common.
/// </summary>
private static SpvOp RemapOp(SpvOp op)
{
switch (op)
{
// 0: 24%
case SpvOp.Decorate:
return SpvOp.Nop;
case SpvOp.Nop:
return SpvOp.Decorate;
// 1: 17%
case SpvOp.Load:
return SpvOp.Undef;
case SpvOp.Undef:
return SpvOp.Load;
// 2: 9%
case SpvOp.Store:
return SpvOp.SourceContinued;
case SpvOp.SourceContinued:
return SpvOp.Store;
// 3: 7.2%
case SpvOp.AccessChain:
return SpvOp.Source;
case SpvOp.Source:
return SpvOp.AccessChain;
// 4: 5.0%
// Name - already small enum value - 5: 4.4%
// MemberName - already small enum value - 6: 2.9%
case SpvOp.VectorShuffle:
return SpvOp.SourceExtension;
case SpvOp.SourceExtension:
return SpvOp.VectorShuffle;
// 7: 4.0%
case SpvOp.MemberDecorate:
return SpvOp.String;
case SpvOp.String:
return SpvOp.MemberDecorate;
// 8: 0.9%
case SpvOp.Label:
return SpvOp.Line;
case SpvOp.Line:
return SpvOp.Label;
// 9: 3.9%
case SpvOp.Variable:
return (SpvOp)9;
case (SpvOp)9:
return SpvOp.Variable;
// 10: 3.9%
case SpvOp.FMul:
return SpvOp.Extension;
case SpvOp.Extension:
return SpvOp.FMul;
// 11: 2.5%
// ExtInst - already small enum value - 12: 1.2%
// VectorShuffleCompact - already small enum value - used for compact shuffle encoding
case SpvOp.FAdd:
return SpvOp.ExtInstImport;
case SpvOp.ExtInstImport:
return SpvOp.FAdd;
// 14: 2.2%
case SpvOp.TypePointer:
return SpvOp.MemoryModel;
case SpvOp.MemoryModel:
return SpvOp.TypePointer;
// 15: 1.1%
case SpvOp.FNegate:
return SpvOp.EntryPoint;
case SpvOp.EntryPoint:
return SpvOp.FNegate;
}
return op;
}
private static uint DecodeLen(SpvOp op, uint len)
{
len++;
switch (op)
{
case SpvOp.VectorShuffle:
len += 4;
break;
case SpvOp.VectorShuffleCompact:
len += 4;
break;
case SpvOp.Decorate:
len += 2;
break;
case SpvOp.Load:
len += 3;
break;
case SpvOp.AccessChain:
len += 3;
break;
}
return len;
}
private static int DecorationExtraOps(int dec)
{
// RelaxedPrecision, Block..ColMajor
if (dec == 0 || (dec >= 2 && dec <= 5))
{
return 0;
}
// Stream..XfbStride
if (dec >= 29 && dec <= 37)
{
return 1;
}
// unknown, encode length
return -1;
}
private static int ZigDecode(uint u)
{
return (u & 1) != 0 ? unchecked((int)(~(u >> 1))) : unchecked((int)(u >> 1));
}
public const uint SpirVHeaderMagic = 0x07230203;
/// <summary>
/// 'SMOL' ascii
/// </summary>
public const uint SmolHeaderMagic = 0x534D4F4C;
private const int HeaderSize = 6 * sizeof(uint);
}
}

View File

@ -0,0 +1,369 @@
namespace Smolv
{
public enum SpvOp
{
Nop = 0,
Undef = 1,
SourceContinued = 2,
Source = 3,
SourceExtension = 4,
Name = 5,
MemberName = 6,
String = 7,
Line = 8,
Extension = 10,
ExtInstImport = 11,
ExtInst = 12,
/// <summary>
/// Not in SPIR-V, added for SMOL-V!
/// </summary>
VectorShuffleCompact = 13,
MemoryModel = 14,
EntryPoint = 15,
ExecutionMode = 16,
Capability = 17,
TypeVoid = 19,
TypeBool = 20,
TypeInt = 21,
TypeFloat = 22,
TypeVector = 23,
TypeMatrix = 24,
TypeImage = 25,
TypeSampler = 26,
TypeSampledImage = 27,
TypeArray = 28,
TypeRuntimeArray = 29,
TypeStruct = 30,
TypeOpaque = 31,
TypePointer = 32,
TypeFunction = 33,
TypeEvent = 34,
TypeDeviceEvent = 35,
TypeReserveId = 36,
TypeQueue = 37,
TypePipe = 38,
TypeForwardPointer = 39,
ConstantTrue = 41,
ConstantFalse = 42,
Constant = 43,
ConstantComposite = 44,
ConstantSampler = 45,
ConstantNull = 46,
SpecConstantTrue = 48,
SpecConstantFalse = 49,
SpecConstant = 50,
SpecConstantComposite = 51,
SpecConstantOp = 52,
Function = 54,
FunctionParameter = 55,
FunctionEnd = 56,
FunctionCall = 57,
Variable = 59,
ImageTexelPointer = 60,
Load = 61,
Store = 62,
CopyMemory = 63,
CopyMemorySized = 64,
AccessChain = 65,
InBoundsAccessChain = 66,
PtrAccessChain = 67,
ArrayLength = 68,
GenericPtrMemSemantics = 69,
InBoundsPtrAccessChain = 70,
Decorate = 71,
MemberDecorate = 72,
DecorationGroup = 73,
GroupDecorate = 74,
GroupMemberDecorate = 75,
VectorExtractDynamic = 77,
VectorInsertDynamic = 78,
VectorShuffle = 79,
CompositeConstruct = 80,
CompositeExtract = 81,
CompositeInsert = 82,
CopyObject = 83,
Transpose = 84,
SampledImage = 86,
ImageSampleImplicitLod = 87,
ImageSampleExplicitLod = 88,
ImageSampleDrefImplicitLod = 89,
ImageSampleDrefExplicitLod = 90,
ImageSampleProjImplicitLod = 91,
ImageSampleProjExplicitLod = 92,
ImageSampleProjDrefImplicitLod = 93,
ImageSampleProjDrefExplicitLod = 94,
ImageFetch = 95,
ImageGather = 96,
ImageDrefGather = 97,
ImageRead = 98,
ImageWrite = 99,
Image = 100,
ImageQueryFormat = 101,
ImageQueryOrder = 102,
ImageQuerySizeLod = 103,
ImageQuerySize = 104,
ImageQueryLod = 105,
ImageQueryLevels = 106,
ImageQuerySamples = 107,
ConvertFToU = 109,
ConvertFToS = 110,
ConvertSToF = 111,
ConvertUToF = 112,
UConvert = 113,
SConvert = 114,
FConvert = 115,
QuantizeToF16 = 116,
ConvertPtrToU = 117,
SatConvertSToU = 118,
SatConvertUToS = 119,
ConvertUToPtr = 120,
PtrCastToGeneric = 121,
GenericCastToPtr = 122,
GenericCastToPtrExplicit = 123,
Bitcast = 124,
SNegate = 126,
FNegate = 127,
IAdd = 128,
FAdd = 129,
ISub = 130,
FSub = 131,
IMul = 132,
FMul = 133,
UDiv = 134,
SDiv = 135,
FDiv = 136,
UMod = 137,
SRem = 138,
SMod = 139,
FRem = 140,
FMod = 141,
VectorTimesScalar = 142,
MatrixTimesScalar = 143,
VectorTimesMatrix = 144,
MatrixTimesVector = 145,
MatrixTimesMatrix = 146,
OuterProduct = 147,
Dot = 148,
IAddCarry = 149,
ISubBorrow = 150,
UMulExtended = 151,
SMulExtended = 152,
Any = 154,
All = 155,
IsNan = 156,
IsInf = 157,
IsFinite = 158,
IsNormal = 159,
SignBitSet = 160,
LessOrGreater = 161,
Ordered = 162,
Unordered = 163,
LogicalEqual = 164,
LogicalNotEqual = 165,
LogicalOr = 166,
LogicalAnd = 167,
LogicalNot = 168,
Select = 169,
IEqual = 170,
INotEqual = 171,
UGreaterThan = 172,
SGreaterThan = 173,
UGreaterThanEqual = 174,
SGreaterThanEqual = 175,
ULessThan = 176,
SLessThan = 177,
ULessThanEqual = 178,
SLessThanEqual = 179,
FOrdEqual = 180,
FUnordEqual = 181,
FOrdNotEqual = 182,
FUnordNotEqual = 183,
FOrdLessThan = 184,
FUnordLessThan = 185,
FOrdGreaterThan = 186,
FUnordGreaterThan = 187,
FOrdLessThanEqual = 188,
FUnordLessThanEqual = 189,
FOrdGreaterThanEqual = 190,
FUnordGreaterThanEqual = 191,
ShiftRightLogical = 194,
ShiftRightArithmetic = 195,
ShiftLeftLogical = 196,
BitwiseOr = 197,
BitwiseXor = 198,
BitwiseAnd = 199,
Not = 200,
BitFieldInsert = 201,
BitFieldSExtract = 202,
BitFieldUExtract = 203,
BitReverse = 204,
BitCount = 205,
DPdx = 207,
DPdy = 208,
Fwidth = 209,
DPdxFine = 210,
DPdyFine = 211,
FwidthFine = 212,
DPdxCoarse = 213,
DPdyCoarse = 214,
FwidthCoarse = 215,
EmitVertex = 218,
EndPrimitive = 219,
EmitStreamVertex = 220,
EndStreamPrimitive = 221,
ControlBarrier = 224,
MemoryBarrier = 225,
AtomicLoad = 227,
AtomicStore = 228,
AtomicExchange = 229,
AtomicCompareExchange = 230,
AtomicCompareExchangeWeak = 231,
AtomicIIncrement = 232,
AtomicIDecrement = 233,
AtomicIAdd = 234,
AtomicISub = 235,
AtomicSMin = 236,
AtomicUMin = 237,
AtomicSMax = 238,
AtomicUMax = 239,
AtomicAnd = 240,
AtomicOr = 241,
AtomicXor = 242,
Phi = 245,
LoopMerge = 246,
SelectionMerge = 247,
Label = 248,
Branch = 249,
BranchConditional = 250,
Switch = 251,
Kill = 252,
Return = 253,
ReturnValue = 254,
Unreachable = 255,
LifetimeStart = 256,
LifetimeStop = 257,
GroupAsyncCopy = 259,
GroupWaitEvents = 260,
GroupAll = 261,
GroupAny = 262,
GroupBroadcast = 263,
GroupIAdd = 264,
GroupFAdd = 265,
GroupFMin = 266,
GroupUMin = 267,
GroupSMin = 268,
GroupFMax = 269,
GroupUMax = 270,
GroupSMax = 271,
ReadPipe = 274,
WritePipe = 275,
ReservedReadPipe = 276,
ReservedWritePipe = 277,
ReserveReadPipePackets = 278,
ReserveWritePipePackets = 279,
CommitReadPipe = 280,
CommitWritePipe = 281,
IsValidReserveId = 282,
GetNumPipePackets = 283,
GetMaxPipePackets = 284,
GroupReserveReadPipePackets = 285,
GroupReserveWritePipePackets = 286,
GroupCommitReadPipe = 287,
GroupCommitWritePipe = 288,
EnqueueMarker = 291,
EnqueueKernel = 292,
GetKernelNDrangeSubGroupCount = 293,
GetKernelNDrangeMaxSubGroupSize = 294,
GetKernelWorkGroupSize = 295,
GetKernelPreferredWorkGroupSizeMultiple = 296,
RetainEvent = 297,
ReleaseEvent = 298,
CreateUserEvent = 299,
IsValidEvent = 300,
SetUserEventStatus = 301,
CaptureEventProfilingInfo = 302,
GetDefaultQueue = 303,
BuildNDRange = 304,
ImageSparseSampleImplicitLod = 305,
ImageSparseSampleExplicitLod = 306,
ImageSparseSampleDrefImplicitLod = 307,
ImageSparseSampleDrefExplicitLod = 308,
ImageSparseSampleProjImplicitLod = 309,
ImageSparseSampleProjExplicitLod = 310,
ImageSparseSampleProjDrefImplicitLod = 311,
ImageSparseSampleProjDrefExplicitLod = 312,
ImageSparseFetch = 313,
ImageSparseGather = 314,
ImageSparseDrefGather = 315,
ImageSparseTexelsResident = 316,
NoLine = 317,
AtomicFlagTestAndSet = 318,
AtomicFlagClear = 319,
ImageSparseRead = 320,
SizeOf = 321,
TypePipeStorage = 322,
ConstantPipeStorage = 323,
CreatePipeFromPipeStorage = 324,
GetKernelLocalSizeForSubgroupCount = 325,
GetKernelMaxNumSubgroups = 326,
TypeNamedBarrier = 327,
NamedBarrierInitialize = 328,
MemoryNamedBarrier = 329,
ModuleProcessed = 330,
KnownOpsCount,
}
public static class SpvOpExtensions
{
public static bool OpHasResult(this SpvOp _this)
{
if (_this < 0 || _this >= SpvOp.KnownOpsCount)
{
return false;
}
return OpData.SpirvOpData[(int)_this].hasResult != 0;
}
public static bool OpHasType(this SpvOp _this)
{
if (_this < 0 || _this >= SpvOp.KnownOpsCount)
{
return false;
}
return OpData.SpirvOpData[(int)_this].hasType != 0;
}
public static int OpDeltaFromResult(this SpvOp _this)
{
if (_this < 0 || _this >= SpvOp.KnownOpsCount)
{
return 0;
}
return OpData.SpirvOpData[(int)_this].deltaFromResult;
}
public static bool OpVarRest(this SpvOp _this)
{
if (_this < 0 || _this >= SpvOp.KnownOpsCount)
{
return false;
}
return OpData.SpirvOpData[(int)_this].varrest != 0;
}
public static bool OpDebugInfo(this SpvOp _this)
{
return
_this == SpvOp.SourceContinued ||
_this == SpvOp.Source ||
_this == SpvOp.SourceExtension ||
_this == SpvOp.Name ||
_this == SpvOp.MemberName ||
_this == SpvOp.String ||
_this == SpvOp.Line ||
_this == SpvOp.NoLine ||
_this == SpvOp.ModuleProcessed;
}
}
}

View File

@ -0,0 +1,74 @@
using Smolv;
using SpirV;
using System;
using System.IO;
using System.Text;
namespace AssetStudio
{
public static class SpirVShaderConverter
{
public static string Convert(byte[] m_ProgramCode)
{
var sb = new StringBuilder();
using (var ms = new MemoryStream(m_ProgramCode))
{
using (var reader = new BinaryReader(ms))
{
int requirements = reader.ReadInt32();
int minOffset = m_ProgramCode.Length;
int snippetCount = 5;
/*if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up
{
snippetCount = 6;
}*/
for (int i = 0; i < snippetCount; i++)
{
if (reader.BaseStream.Position >= minOffset)
{
break;
}
int offset = reader.ReadInt32();
int size = reader.ReadInt32();
if (size > 0)
{
if (offset < minOffset)
{
minOffset = offset;
}
var pos = ms.Position;
sb.Append(ExportSnippet(ms, offset, size));
ms.Position = pos;
}
}
}
}
return sb.ToString();
}
private static string ExportSnippet(Stream stream, int offset, int size)
{
stream.Position = offset;
int decodedSize = SmolvDecoder.GetDecodedBufferSize(stream);
if (decodedSize == 0)
{
throw new Exception("Invalid SMOL-V shader header");
}
using (var decodedStream = new MemoryStream(new byte[decodedSize]))
{
if (SmolvDecoder.Decode(stream, size, decodedStream))
{
decodedStream.Position = 0;
var module = Module.ReadFrom(decodedStream);
var disassembler = new Disassembler();
return disassembler.Disassemble(module, DisassemblyOptions.Default).Replace("\r\n", "\n");
}
else
{
throw new Exception("Unable to decode SMOL-V shader");
}
}
}
}
}