mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-07-18 03:24:15 -04:00
implemented SPIR-V shader export
This commit is contained in:
221
AssetStudioUtility/CSspv/Disassembler.cs
Normal file
221
AssetStudioUtility/CSspv/Disassembler.cs
Normal 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();
|
||||
}
|
||||
}
|
42
AssetStudioUtility/CSspv/EnumValuesExtensions.cs
Normal file
42
AssetStudioUtility/CSspv/EnumValuesExtensions.cs
Normal 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
|
51
AssetStudioUtility/CSspv/Instruction.cs
Normal file
51
AssetStudioUtility/CSspv/Instruction.cs
Normal 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; }
|
||||
}
|
||||
}
|
25
AssetStudioUtility/CSspv/LICENSE
Normal file
25
AssetStudioUtility/CSspv/LICENSE
Normal 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.
|
426
AssetStudioUtility/CSspv/Module.cs
Normal file
426
AssetStudioUtility/CSspv/Module.cs
Normal 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>();
|
||||
}
|
||||
}
|
302
AssetStudioUtility/CSspv/OperandType.cs
Normal file
302
AssetStudioUtility/CSspv/OperandType.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
265
AssetStudioUtility/CSspv/ParsedInstruction.cs
Normal file
265
AssetStudioUtility/CSspv/ParsedInstruction.cs
Normal 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; }
|
||||
}
|
||||
}
|
50
AssetStudioUtility/CSspv/Reader.cs
Normal file
50
AssetStudioUtility/CSspv/Reader.cs
Normal 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_;
|
||||
}
|
||||
}
|
3543
AssetStudioUtility/CSspv/SpirV.Core.Grammar.cs
Normal file
3543
AssetStudioUtility/CSspv/SpirV.Core.Grammar.cs
Normal file
File diff suppressed because one or more lines are too long
54
AssetStudioUtility/CSspv/SpirV.Meta.cs
Normal file
54
AssetStudioUtility/CSspv/SpirV.Meta.cs
Normal 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") },
|
||||
};
|
||||
}
|
||||
}
|
428
AssetStudioUtility/CSspv/Types.cs
Normal file
428
AssetStudioUtility/CSspv/Types.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user