mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-05-25 05:40:21 -04:00
implemented SPIR-V shader export
This commit is contained in:
parent
0ec29f62ca
commit
729a8a8263
@ -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" />
|
||||
|
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
|
||||
{
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
365
AssetStudioUtility/Smolv/OpData.cs
Normal file
365
AssetStudioUtility/Smolv/OpData.cs
Normal 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
|
||||
};
|
||||
};
|
||||
}
|
479
AssetStudioUtility/Smolv/SmolvDecoder.cs
Normal file
479
AssetStudioUtility/Smolv/SmolvDecoder.cs
Normal 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 < 16 range, for
|
||||
/// more compact varint encoding. This basically swaps rarely used op values that are < 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);
|
||||
}
|
||||
}
|
369
AssetStudioUtility/Smolv/SpvOp.cs
Normal file
369
AssetStudioUtility/Smolv/SpvOp.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
74
AssetStudioUtility/SpirVShaderConverter.cs
Normal file
74
AssetStudioUtility/SpirVShaderConverter.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user