diff --git a/Ryujinx.Graphics/Gal/Shader/SpirvAssembler.cs b/Ryujinx.Graphics/Gal/Shader/SpirvAssembler.cs new file mode 100644 index 000000000..2fd0abcd4 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SpirvAssembler.cs @@ -0,0 +1,70 @@ +using System.IO; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader.SPIRV +{ + public class Assembler + { + private List Instructions; + + public Assembler() + { + Instructions = new List(); + } + + public void Write(Stream Output) + { + uint Bound = DoBindings(); + + var BinaryWriter = new BinaryWriter(Output); + + BinaryWriter.Write((uint)BinaryForm.MagicNumber); + BinaryWriter.Write((uint)BinaryForm.VersionNumber); + BinaryWriter.Write((uint)BinaryForm.GeneratorMagicNumber); + BinaryWriter.Write((uint)Bound); + BinaryWriter.Write((uint)0); // Reserved for instruction schema + + foreach (var Instruction in Instructions) + { + Instruction.Write(BinaryWriter); + } + } + + public void Add(Instruction Instruction) + { + Instructions.Add(Instruction); + } + + public void Add(Instruction[] Instructions) + { + foreach (Instruction Instruction in Instructions) + { + Add(Instruction); + } + } + + public void Add(List Instructions) + { + foreach (Instruction Instruction in Instructions) + { + Add(Instruction); + } + } + + private uint DoBindings() + { + uint Bind = 1; + + foreach (var Instruction in Instructions) + { + if (Instruction.HoldsResultId) + { + Instruction.ResultId = Bind; + Bind++; + } + } + + return Bind; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SpirvBinaryForm.cs b/Ryujinx.Graphics/Gal/Shader/SpirvBinaryForm.cs new file mode 100644 index 000000000..dfccd168d --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SpirvBinaryForm.cs @@ -0,0 +1,1121 @@ +namespace Ryujinx.Graphics.Gal.Shader.SPIRV +{ + public class BinaryForm + { + public const uint MagicNumber = 0x07230203; + + public const uint VersionNumber = 0x00010000; + + // I don't think Khronos would give us an Id + public const uint GeneratorMagicNumber = 0; + } + + // https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/spirv.h + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + Max = 0x7fffffff, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + Max = 0x7fffffff, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + Max = 0x7fffffff, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Max = 0x7fffffff, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + Max = 0x7fffffff, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + Max = 0x7fffffff, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + Max = 0x7fffffff, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + Max = 0x7fffffff, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + Max = 0x7fffffff, + } + + public enum ImageOperandsShift + { + BiasShift = 0, + LodShift = 1, + GradShift = 2, + ConstOffsetShift = 3, + OffsetShift = 4, + ConstOffsetsShift = 5, + SampleShift = 6, + MinLodShift = 7, + Max = 0x7fffffff, + } + + public enum ImageOperands + { + None = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + } + + public enum FPFastMathMode + { + None = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + Max = 0x7fffffff, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + Max = 0x7fffffff, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + NonUniformEXT = 5300, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + Max = 0x7fffffff, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + Max = 0x7fffffff, + } + + public enum SelectionControl + { + None = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + public enum LoopControl + { + None = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + } + + public enum FunctionControl + { + None = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + } + + public enum MemorySemantics + { + None = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + } + + public enum MemoryAccess + { + None = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + Max = 0x7fffffff, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + Max = 0x7fffffff, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, + } + + public enum KernelProfilingInfoShift + { + CmdExecTimeShift = 0, + Max = 0x7fffffff, + } + + public enum KernelProfilingInfo + { + None = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + Max = 0x7fffffff, + } + + public enum Opcode + { + Nop = 0, + Undef = 1, + SourceContinued = 2, + Source = 3, + SourceExtension = 4, + Name = 5, + MemberName = 6, + String = 7, + Line = 8, + Extension = 10, + ExtInstImport = 11, + ExtInst = 12, + 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, + ExecutionModeId = 331, + DecorateId = 332, + GroupNonUniformElect = 333, + GroupNonUniformAll = 334, + GroupNonUniformAny = 335, + GroupNonUniformAllEqual = 336, + GroupNonUniformBroadcast = 337, + GroupNonUniformBroadcastFirst = 338, + GroupNonUniformBallot = 339, + GroupNonUniformInverseBallot = 340, + GroupNonUniformBallotBitExtract = 341, + GroupNonUniformBallotBitCount = 342, + GroupNonUniformBallotFindLSB = 343, + GroupNonUniformBallotFindMSB = 344, + GroupNonUniformShuffle = 345, + GroupNonUniformShuffleXor = 346, + GroupNonUniformShuffleUp = 347, + GroupNonUniformShuffleDown = 348, + GroupNonUniformIAdd = 349, + GroupNonUniformFAdd = 350, + GroupNonUniformIMul = 351, + GroupNonUniformFMul = 352, + GroupNonUniformSMin = 353, + GroupNonUniformUMin = 354, + GroupNonUniformFMin = 355, + GroupNonUniformSMax = 356, + GroupNonUniformUMax = 357, + GroupNonUniformFMax = 358, + GroupNonUniformBitwiseAnd = 359, + GroupNonUniformBitwiseOr = 360, + GroupNonUniformBitwiseXor = 361, + GroupNonUniformLogicalAnd = 362, + GroupNonUniformLogicalOr = 363, + GroupNonUniformLogicalXor = 364, + GroupNonUniformQuadBroadcast = 365, + GroupNonUniformQuadSwap = 366, + SubgroupBallotKHR = 4421, + SubgroupFirstInvocationKHR = 4422, + SubgroupAllKHR = 4428, + SubgroupAnyKHR = 4429, + SubgroupAllEqualKHR = 4430, + SubgroupReadInvocationKHR = 4432, + GroupIAddNonUniformAMD = 5000, + GroupFAddNonUniformAMD = 5001, + GroupFMinNonUniformAMD = 5002, + GroupUMinNonUniformAMD = 5003, + GroupSMinNonUniformAMD = 5004, + GroupFMaxNonUniformAMD = 5005, + GroupUMaxNonUniformAMD = 5006, + GroupSMaxNonUniformAMD = 5007, + FragmentMaskFetchAMD = 5011, + FragmentFetchAMD = 5012, + GroupNonUniformPartitionNV = 5296, + SubgroupShuffleINTEL = 5571, + SubgroupShuffleDownINTEL = 5572, + SubgroupShuffleUpINTEL = 5573, + SubgroupShuffleXorINTEL = 5574, + SubgroupBlockReadINTEL = 5575, + SubgroupBlockWriteINTEL = 5576, + SubgroupImageBlockReadINTEL = 5577, + SubgroupImageBlockWriteINTEL = 5578, + DecorateStringGOOGLE = 5632, + MemberDecorateStringGOOGLE = 5633, + Max = 0x7fffffff, + } + + // https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/GLSL.std.450.h + public enum GLSLstd450 + { + Bad = 0, // Don't use + + Round = 1, + RoundEven = 2, + Trunc = 3, + FAbs = 4, + SAbs = 5, + FSign = 6, + SSign = 7, + Floor = 8, + Ceil = 9, + Fract = 10, + + Radians = 11, + Degrees = 12, + Sin = 13, + Cos = 14, + Tan = 15, + Asin = 16, + Acos = 17, + Atan = 18, + Sinh = 19, + Cosh = 20, + Tanh = 21, + Asinh = 22, + Acosh = 23, + Atanh = 24, + Atan2 = 25, + + Pow = 26, + Exp = 27, + Log = 28, + Exp2 = 29, + Log2 = 30, + Sqrt = 31, + InverseSqrt = 32, + + Determinant = 33, + MatrixInverse = 34, + + Modf = 35, // second operand needs an OpVariable to write to + ModfStruct = 36, // no OpVariable operand + FMin = 37, + UMin = 38, + SMin = 39, + FMax = 40, + UMax = 41, + SMax = 42, + FClamp = 43, + UClamp = 44, + SClamp = 45, + FMix = 46, + IMix = 47, // Reserved + Step = 48, + SmoothStep = 49, + + Fma = 50, + Frexp = 51, // second operand needs an OpVariable to write to + FrexpStruct = 52, // no OpVariable operand + Ldexp = 53, + + PackSnorm4x8 = 54, + PackUnorm4x8 = 55, + PackSnorm2x16 = 56, + PackUnorm2x16 = 57, + PackHalf2x16 = 58, + PackDouble2x32 = 59, + UnpackSnorm2x16 = 60, + UnpackUnorm2x16 = 61, + UnpackHalf2x16 = 62, + UnpackSnorm4x8 = 63, + UnpackUnorm4x8 = 64, + UnpackDouble2x32 = 65, + + Length = 66, + Distance = 67, + Cross = 68, + Normalize = 69, + FaceForward = 70, + Reflect = 71, + Refract = 72, + + FindILsb = 73, + FindSMsb = 74, + FindUMsb = 75, + + InterpolateAtCentroid = 76, + InterpolateAtSample = 77, + InterpolateAtOffset = 78, + + NMin = 79, + NMax = 80, + NClamp = 81, + + Count + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SpirvDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/SpirvDecompiler.cs new file mode 100644 index 000000000..bdd49a656 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SpirvDecompiler.cs @@ -0,0 +1,1146 @@ +using System; +using System.IO; +using System.Collections.Generic; +using Ryujinx.Graphics.Gal.Shader.SPIRV; + +namespace Ryujinx.Graphics.Gal.Shader +{ + public class SpirvDecompiler + { + private class SpirvVariable + { + public Instruction Id; + public StorageClass Storage; + public string Name; + public int Location = -1; + } + + private enum OperType + { + Bool, + F32, + I32 + } + + private delegate int LocationAllocator(); + + private delegate Instruction GetInstExpr(ShaderIrOp Op); + + private Dictionary InstsExpr; + + private GlslDecl Decl; + + private ShaderIrBlock[] Blocks; + + private Assembler Assembler; + + private int UniformCount = 0; + private int InAttributeCount = 0; + private int OutAttributeCount = 0; + + private Instruction TypeVoid, + TypeBool, TypeBool_Private, + TypeInt, TypeUInt, + TypeFloat, + TypeImage2D, TypeSampler2D, TypeSampler2D_Uniform; + + private Instruction[] TypeFloats, TypeFloats_In, TypeFloats_Out, TypeFloats_Uniform, TypeFloats_Private; + + private Instruction TrueConstant, FalseConstant; + + //Holds debug info, they are not needed but do not hurt to add + private List Names; + + //Types and constants have to be defined sequentially + //because some types require constants and most constants require types + private List TypesConstants; + + private List Decorates; + + //Variables declarations. They are different to "Variables" declared below + //These holds instructions + private List VarsDeclaration; + + private List Code; + + private List Variables; + + private Instruction Main; + + private Instruction PerVertexVar = null; + + private Instruction GlslExtension; + + private GLSLstd450Builder Glsl450; + + // + + private Instruction GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) + { + return GetExprWithCast(Op, Oper, GetSrcExpr(Oper)); + } + + private Instruction GetFcltExpr(ShaderIrOp Op) + { + return new OpFOrdLessThan( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + } + + private Instruction GetSrcNodeTypeId(ShaderIrOp Op) + { + switch (GetSrcNodeType(Op)) + { + case OperType.Bool: return TypeBool; + case OperType.F32: return TypeFloat; + case OperType.I32: return TypeInt; + default: + throw new InvalidOperationException(); + } + } + + private Instruction GetFcgtExpr(ShaderIrOp Op) + { + return new OpFOrdGreaterThan( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + } + + private Instruction GetFnegExpr(ShaderIrOp Op) + { + return new OpFNegate(TypeFloat, GetOperExpr(Op, Op.OperandA)); + } + + private Instruction GetFaddExpr(ShaderIrOp Op) + { + return new OpFAdd( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + } + + private Instruction GetFmulExpr(ShaderIrOp Op) + { + return new OpFMul( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + } + + private Instruction GetFfmaExpr(ShaderIrOp Op) + { + return Glsl450.Fma( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB), + GetOperExpr(Op, Op.OperandC)); + } + + // + + + public SpirvDecompiler() + { + InstsExpr = new Dictionary() + { + { ShaderIrInst.Fadd, GetFaddExpr }, + { ShaderIrInst.Fmul, GetFmulExpr }, + { ShaderIrInst.Fclt, GetFcltExpr }, + { ShaderIrInst.Fcgt, GetFcgtExpr }, + { ShaderIrInst.Fneg, GetFnegExpr }, + { ShaderIrInst.Ffma, GetFfmaExpr } + }; + + Assembler = new Assembler(); + + GlslExtension = new OpExtInstImport("GLSL.std.450"); + Glsl450 = new GLSLstd450Builder(GlslExtension); + + TypesConstants = new List(); + Names = new List(); + Decorates = new List(); + VarsDeclaration = new List(); + Code = new List(); + + Variables = new List(); + + //Declare types types + TypeVoid = new OpTypeVoid(); + + TypeBool = new OpTypeBool(); + TypeBool_Private = new OpTypePointer(StorageClass.Private, TypeBool); + + TrueConstant = new OpConstantTrue(TypeBool); + FalseConstant = new OpConstantFalse(TypeBool); + + TypeInt = new OpTypeInt(32, true); + TypeUInt = new OpTypeInt(32, false); + + TypeFloats = new Instruction[4]; + TypeFloats_In = new Instruction[4]; + TypeFloats_Out = new Instruction[4]; + TypeFloats_Uniform = new Instruction[4]; + TypeFloats_Private = new Instruction[4]; + + TypeFloat = new OpTypeFloat(32); + TypeFloats[0] = TypeFloat; + + for (int i = 0; i < 4; i++) + { + if (i > 0) + { + TypeFloats[i] = new OpTypeVector(TypeFloat, i+1); + } + + TypeFloats_In[i] = new OpTypePointer(StorageClass.Input, TypeFloats[i]); + + TypeFloats_Out[i] = new OpTypePointer(StorageClass.Output, TypeFloats[i]); + + TypeFloats_Uniform[i] = new OpTypePointer(StorageClass.UniformConstant, TypeFloats[i]); + + TypeFloats_Private[i] = new OpTypePointer(StorageClass.Private, TypeFloats[i]); + } + + TypeImage2D = new OpTypeImage(TypeFloat, Dim.Dim2D, 0, 0, 0, 0, ImageFormat.Unknown); + TypeSampler2D = new OpTypeSampledImage(TypeImage2D); + TypeSampler2D_Uniform = new OpTypePointer(StorageClass.UniformConstant, TypeSampler2D); + + //Add them (these do not need to be added safely) + TypesConstants.Add(TypeVoid); + + TypesConstants.Add(TypeBool); + TypesConstants.Add(TypeBool_Private); + TypesConstants.Add(TrueConstant); + TypesConstants.Add(FalseConstant); + + TypesConstants.Add(TypeInt); + TypesConstants.Add(TypeUInt); + + for (int i = 0; i < 4; i++) + { + TypesConstants.Add(TypeFloats[i]); + TypesConstants.Add(TypeFloats_In[i]); + TypesConstants.Add(TypeFloats_Out[i]); + TypesConstants.Add(TypeFloats_Uniform[i]); + TypesConstants.Add(TypeFloats_Private[i]); + } + + TypesConstants.Add(TypeImage2D); + TypesConstants.Add(TypeSampler2D); + TypesConstants.Add(TypeSampler2D_Uniform); + } + + public byte[] Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) + { + Blocks = ShaderDecoder.Decode(Memory, Position); + + Decl = new GlslDecl(Blocks, ShaderType); + + BuildBuiltIns(); + BuildTextures(); + BuildUniforms(); + BuildInAttributes(); + BuildOutAttributes(); + BuildGprs(); + BuildDeclPreds(); + BuildMain(); + BuildCode(); + + PrintHeader(); + PrintEntryPoint(); + + Assembler.Add(Names); + Assembler.Add(Decorates); + Assembler.Add(TypesConstants); + Assembler.Add(VarsDeclaration); + Assembler.Add(Code); + + //Temp code ahead + var Stream = new MemoryStream(); + Assembler.Write(Stream); + byte[] Bytecode = Stream.ToArray(); + Stream.Close(); + return Bytecode; + } + + private void BuildBuiltIns() + { + switch (Decl.ShaderType) + { + case GalShaderType.Vertex: + + Instruction One = AllocConstant(TypeUInt, new LiteralNumber((int)1)); + Instruction ArrFloatUInt1 = new OpTypeArray(TypeFloat, One); + + Instruction PerVertex = new OpTypeStruct(TypeFloats[3], TypeFloat, ArrFloatUInt1, ArrFloatUInt1); + + Instruction OutputPerVertex = new OpTypePointer(StorageClass.Output, PerVertex); + + Decorates.Add(new OpMemberDecorate(PerVertex, 0, Decoration.BuiltIn, BuiltIn.Position)); + Decorates.Add(new OpMemberDecorate(PerVertex, 1, Decoration.BuiltIn, BuiltIn.PointSize)); + Decorates.Add(new OpMemberDecorate(PerVertex, 2, Decoration.BuiltIn, BuiltIn.ClipDistance)); + Decorates.Add(new OpMemberDecorate(PerVertex, 3, Decoration.BuiltIn, BuiltIn.CullDistance)); + + Decorates.Add(new OpDecorate(PerVertex, Decoration.Block)); + + TypesConstants.Add(ArrFloatUInt1); + TypesConstants.Add(PerVertex); + TypesConstants.Add(OutputPerVertex); + + PerVertexVar = new OpVariable(OutputPerVertex, StorageClass.Output); + VarsDeclaration.Add(PerVertexVar); + + Names.Add(new OpName(PerVertex, "gl_PerVertex")); + + break; + } + } + + private void BuildTextures() + { + foreach (ShaderDeclInfo DeclInfo in Decl.Textures.Values) + { + Instruction Variable = AllocLocatedVariable( + TypeSampler2D_Uniform, + StorageClass.UniformConstant, + DeclInfo.Name, + AllocUniformLocation); + + //TODO What is a "DescriptorSet"? It sounds like something from Vulkan + Decorates.Add(new OpDecorate(Variable, Decoration.DescriptorSet, new LiteralNumber(0))); + } + } + + private void BuildUniforms() + { + if (Decl.ShaderType == GalShaderType.Vertex) + { + Instruction Float2 = TypeFloats_Uniform[1]; + AllocLocatedVariable( + Float2, + StorageClass.UniformConstant, + GalConsts.FlipUniformName, + AllocUniformLocation); + } + + foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values) + { + Instruction ElemType = TypeFloats[DeclInfo.Size - 1]; + + Operand Size = new LiteralNumber(DeclInfo.Index + 1); + Instruction Constant = AllocConstant(TypeInt, Size); + + Instruction ArrayType = AllocType(new OpTypeArray(ElemType, Constant)); + Instruction ArrayTypeUniform = AllocType(new OpTypePointer(StorageClass.UniformConstant, ArrayType)); + + AllocLocatedVariable( + ArrayTypeUniform, + StorageClass.UniformConstant, + DeclInfo.Name, + AllocUniformLocation); + } + } + + private void BuildInAttributes() + { + if (Decl.ShaderType == GalShaderType.Fragment) + { + AllocLocatedVariable( + TypeFloats_In[3], + StorageClass.Input, + GlslDecl.PositionOutAttrName, + AllocInAttributeLocation); + } + + BuildAttributes( + Decl.InAttributes.Values, + TypeFloats_In, + StorageClass.Input, + AllocInAttributeLocation); + } + + private void BuildOutAttributes() + { + if (Decl.ShaderType == GalShaderType.Vertex) + { + AllocLocatedVariable( + TypeFloats_In[3], + StorageClass.Output, + GlslDecl.PositionOutAttrName, + AllocOutAttributeLocation); + } + + BuildAttributes( + Decl.OutAttributes.Values, + TypeFloats_Out, + StorageClass.Output, + AllocOutAttributeLocation); + } + + private void BuildGprs() + { + foreach (ShaderDeclInfo DeclInfo in Decl.Gprs.Values) + { + Instruction Type = TypeFloats_Private[DeclInfo.Size - 1]; + + AllocVariable(Type, StorageClass.Private, DeclInfo.Name); + } + } + + private void BuildDeclPreds() + { + foreach (ShaderDeclInfo DeclInfo in Decl.Preds.Values) + { + AllocVariable(TypeBool_Private, StorageClass.Private, DeclInfo.Name); + } + } + + private void BuildAttributes( + IEnumerable Decls, + Instruction[] TypeFloats_InOut, + StorageClass StorageClass, + LocationAllocator Allocator) + { + foreach (ShaderDeclInfo DeclInfo in Decls) + { + if (DeclInfo.Index >= 0) + { + Instruction Type = TypeFloats_InOut[DeclInfo.Size - 1]; + + AllocLocatedVariable(Type, StorageClass, DeclInfo.Name, Allocator); + } + } + } + + private void BuildMain() + { + Instruction TypeFunction = AllocType(new OpTypeFunction(TypeVoid)); + + Main = new OpFunction(TypeVoid, FunctionControl.None, TypeFunction); + } + + private void BuildCode() + { + Code.Add(Main); + + // First label is implicit when building first block + + Dictionary Labels = new Dictionary(); + + foreach (ShaderIrBlock Block in Blocks) + { + Labels[Block] = new OpLabel(); + } + + Instruction EndLabel = new OpLabel(); + + for (int BlockIndex = 0; BlockIndex < Blocks.Length; BlockIndex++) + { + BuildBlock(BlockIndex, Labels, EndLabel); + } + + Code.Add(EndLabel); + + //TODO + //SB.AppendLine(Identation + "gl_Position.xy *= flip;"); + + //SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;"); + //SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + ".w = 1;"); + + Code.Add(new OpReturn()); + + Code.Add(new OpFunctionEnd()); + } + + private void BuildBlock( + int BlockIndex, + Dictionary Labels, + Instruction EndLabel) + { + ShaderIrBlock Block = Blocks[BlockIndex]; + + Code.Add(Labels[Block]); + + bool HasBranchTail = BuildNodes(Block, Block.Nodes, Labels, EndLabel); + + // No unconditional branch instruction found. Branch to next block + if (!HasBranchTail) + { + Instruction Label; + + if (Block.Next != null) + { + Label = Labels[Block.Next]; + } + else if (BlockIndex + 1 < Blocks.Length) + { + ShaderIrBlock NextBlock = Blocks[BlockIndex + 1]; + + Label = Labels[NextBlock]; + } + else + { + Label = EndLabel; + } + + Code.Add(new OpBranch(Label)); + } + } + + private bool BuildNodes( + ShaderIrBlock Block, + List Nodes, + Dictionary Labels, + Instruction EndLabel) + { + foreach (ShaderIrNode Node in Nodes) + { + if (Node is ShaderIrCond Cond) + { + Instruction CondExpr = GetSrcExpr(Cond.Pred, true); + + if (Cond.Not) + { + CondExpr = new OpLogicalNot(TypeBool, CondExpr); + Code.Add(CondExpr); + } + + if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra) + { + Instruction BranchLabel = Labels[Block.Branch]; + + Instruction SkipLabel = new OpLabel(); + + Instruction Branch = new OpBranchConditional(CondExpr, BranchLabel, SkipLabel); + + Code.Add(Branch); + + Code.Add(SkipLabel); + } + else + { + Instruction ExecuteLabel = new OpLabel(); + + Instruction SkipLabel = new OpLabel(); + + Instruction Branch = new OpBranchConditional(CondExpr, ExecuteLabel, SkipLabel); + + Code.Add(Branch); + + Code.Add(ExecuteLabel); + + List ChildList = new List(); + ChildList.Add(Cond.Child); + + bool HasBranchTail = BuildNodes(Block, ChildList, Labels, EndLabel); + + if (!HasBranchTail) + { + Code.Add(new OpBranch(SkipLabel)); + } + + Code.Add(SkipLabel); + } + } + else if (Node is ShaderIrAsg Asg) + { + if (IsValidOutOper(Asg.Dst)) + { + Instruction SrcExpr = GetSrcExpr(Asg.Src, true); + + Instruction Expr = GetExprWithCast(Asg.Dst, Asg.Src, SrcExpr); + + Instruction Target = GetDstOperName(Asg.Dst); + + Code.Add(new OpStore(Target, Expr)); + } + } + else if (Node is ShaderIrOp Op) + { + if (Op.Inst == ShaderIrInst.Bra) + { + Instruction BranchLabel = Labels[Block.Branch]; + + Code.Add(new OpBranch(BranchLabel)); + + //Unconditional branch found, ignore following nodes in this hierarchy + return true; + } + else if (Op.Inst == ShaderIrInst.Exit) + { + Code.Add(new OpBranch(EndLabel)); + + //Ignore following nodes, same as ^ + return true; + } + else + { + Instruction Operation = GetSrcExpr(Op, true); + } + } + else if (Node is ShaderIrCmnt Comment) + { + // Couldn't find a commentary OpCode in Spirv, for now just ignore it + } + else + { + throw new InvalidOperationException(); + } + } + + return false; + } + + private Instruction GetExprWithCast(ShaderIrNode Dst, ShaderIrNode Src, Instruction Expr) + { + //Note: The "DstType" (of the cast) is the type that the operation + //uses on the source operands, while the "SrcType" is the destination + //type of the operand result (if it is a operation) or just the type + //of the variable for registers/uniforms/attributes. + OperType DstType = GetSrcNodeType(Dst); + OperType SrcType = GetDstNodeType(Src); + + if (DstType != SrcType) + { + //Check for invalid casts + //(like bool to int/float and others). + if (SrcType != OperType.F32 && + SrcType != OperType.I32) + { + throw new InvalidOperationException(); + } + + switch (Src) + { + case ShaderIrOperGpr Gpr: + { + //When the Gpr is ZR, just return the 0 value directly, + //since the float encoding for 0 is 0. + if (Gpr.IsConst) + { + return AllocConstant(TypeFloat, new LiteralNumber(0f)); + } + break; + } + + case ShaderIrOperImm Imm: + { + if (DstType == OperType.F32) + { + float Value = BitConverter.Int32BitsToSingle(Imm.Value); + + if (!float.IsNaN(Value) && !float.IsInfinity(Value)) + { + return AllocConstant(TypeFloat, new LiteralNumber(Value)); + } + } + break; + } + } + + switch (DstType) + { + case OperType.F32: + return new OpBitcast(TypeFloat, Expr); + + case OperType.I32: + return new OpBitcast(TypeInt, Expr); + } + } + + return Expr; + } + + private static OperType GetDstNodeType(ShaderIrNode Node) + { + //Special case instructions with the result type different + //from the input types (like integer <-> float conversion) here. + if (Node is ShaderIrOp Op) + { + switch (Op.Inst) + { + case ShaderIrInst.Stof: + case ShaderIrInst.Txlf: + case ShaderIrInst.Utof: + return OperType.F32; + + case ShaderIrInst.Ftos: + case ShaderIrInst.Ftou: + return OperType.I32; + } + } + + return GetSrcNodeType(Node); + } + + private static OperType GetSrcNodeType(ShaderIrNode Node) + { + switch (Node) + { + case ShaderIrOperAbuf Abuf: + return Abuf.Offs == GlslDecl.VertexIdAttr + ? OperType.I32 + : OperType.F32; + + case ShaderIrOperCbuf Cbuf: return OperType.F32; + case ShaderIrOperGpr Gpr: return OperType.F32; + case ShaderIrOperImm Imm: return OperType.I32; + case ShaderIrOperImmf Immf: return OperType.F32; + case ShaderIrOperPred Pred: return OperType.Bool; + + case ShaderIrOp Op: + if (Op.Inst > ShaderIrInst.B_Start && + Op.Inst < ShaderIrInst.B_End) + { + return OperType.Bool; + } + else if (Op.Inst > ShaderIrInst.F_Start && + Op.Inst < ShaderIrInst.F_End) + { + return OperType.F32; + } + else if (Op.Inst > ShaderIrInst.I_Start && + Op.Inst < ShaderIrInst.I_End) + { + return OperType.I32; + } + break; + } + + throw new ArgumentException(nameof(Node)); + } + + private bool IsValidOutOper(ShaderIrNode Node) + { + if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst) + { + return false; + } + else if (Node is ShaderIrOperPred Pred && Pred.IsConst) + { + return false; + } + + return true; + } + + private Instruction GetDstOperName(ShaderIrNode Node) + { + if (Node is ShaderIrOperAbuf Abuf) + { + return GetOutAbufName(Abuf); + } + else if (Node is ShaderIrOperGpr Gpr) + { + return GetName(Gpr, true); + } + else if (Node is ShaderIrOperPred Pred) + { + return GetName(Pred, true); + } + + throw new ArgumentException(nameof(Node)); + } + + private Instruction GetOutAbufName(ShaderIrOperAbuf Abuf) + { + return GetName(Decl.OutAttributes, Abuf, true); + } + + private Instruction GetSrcExpr(ShaderIrNode Node, bool Entry = false) + { + switch (Node) + { + case ShaderIrOperAbuf Abuf: return GetName (Abuf, false); + case ShaderIrOperCbuf Cbuf: return GetName (Cbuf, false); + case ShaderIrOperGpr Gpr: return GetName (Gpr, false); + case ShaderIrOperImm Imm: return GetValue(Imm); + case ShaderIrOperImmf Immf: return GetValue(Immf); + case ShaderIrOperPred Pred: return GetName (Pred, false); + + case ShaderIrOp Op: + Instruction Expr; + + if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr)) + { + Expr = GetExpr(Op); + } + else + { + throw new NotImplementedException(Op.Inst.ToString()); + } + + //GetExpr does not add it to Code + Code.Add(Expr); + + return Expr; + + default: throw new ArgumentException(nameof(Node)); + } + } + + private Instruction GetName(ShaderIrOperAbuf Abuf, bool Pointer) + { + if (Decl.ShaderType == GalShaderType.Vertex) + { + switch (Abuf.Offs) + { + case GlslDecl.VertexIdAttr: return GetVariableValue(TypeInt, "gl_VertexID", Pointer); + case GlslDecl.InstanceIdAttr: return GetVariableValue(TypeInt, "gl_InstanceID", Pointer); + } + } + else if (Decl.ShaderType == GalShaderType.TessEvaluation) + { + switch (Abuf.Offs) + { + case GlslDecl.TessCoordAttrX: return GetVariableValue(TypeFloat, "gl_TessCoord", 0, Pointer); + case GlslDecl.TessCoordAttrY: return GetVariableValue(TypeFloat, "gl_TessCoord", 1, Pointer); + case GlslDecl.TessCoordAttrZ: return GetVariableValue(TypeFloat, "gl_TessCoord", 2, Pointer); + } + } + + return GetName(Decl.InAttributes, Abuf, Pointer); + } + + private Instruction GetName(IReadOnlyDictionary Dict, ShaderIrOperAbuf Abuf, bool Pointer) + { + int Index = Abuf.Offs >> 4; + int Elem = (Abuf.Offs >> 2) & 3; + + if (!Dict.TryGetValue(Index, out ShaderDeclInfo DeclInfo)) + { + throw new InvalidOperationException(); + } + + //Guess types are float-based + if (DeclInfo.Size > 1) + { + return GetVariableValue(TypeFloat, DeclInfo.Name, Elem, Pointer); + } + else + { + return GetVariableValue(TypeFloat, DeclInfo.Name, Pointer); + } + } + + private Instruction GetName(ShaderIrOperGpr Gpr, bool Pointer) + { + //Gprs are always float, right? + + if (Gpr.IsConst) + { + if (Pointer) + { + throw new InvalidOperationException("Can't return pointer to a constant"); + } + + return AllocConstant(TypeFloat, new LiteralNumber(0f)); + } + else + { + return GetNameWithSwizzle(TypeFloat, Decl.Gprs, Gpr.Index, Pointer); + } + } + + private Instruction GetValue(ShaderIrOperImm Imm) + { + return AllocConstant(TypeInt, new LiteralNumber(Imm.Value)); + } + + private Instruction GetValue(ShaderIrOperImmf Immf) + { + return AllocConstant(TypeFloat, new LiteralNumber(Immf.Value)); + } + + private Instruction GetName(ShaderIrOperPred Pred, bool Pointer) + { + if (Pred.IsConst) + { + return TrueConstant; + } + else + { + return GetNameWithSwizzle(TypeBool, Decl.Preds, Pred.Index, Pointer); + } + } + + private Instruction GetName(ShaderIrOperCbuf Cbuf, bool Pointer) + { + if (!Decl.Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo)) + { + throw new InvalidOperationException(); + } + + Instruction PosConstant = AllocConstant(TypeInt, new LiteralNumber(Cbuf.Pos)); + + if (Cbuf.Offs != null) + { + //Note: We assume that the register value is always a multiple of 4. + //This may not be always the case. + + Instruction ShiftConstant = AllocConstant(TypeInt, new LiteralNumber(2)); + + Instruction Source = GetSrcExpr(Cbuf.Offs); + + Instruction Casted = new OpBitcast(TypeInt, Source); + + Instruction Offset = new OpShiftRightLogical(TypeInt, Casted, ShiftConstant); + + Instruction Index = new OpIAdd(TypeInt, PosConstant, Offset); + + Code.Add(Source); + Code.Add(Casted); + Code.Add(Offset); + Code.Add(Index); + + return GetVariableValue(TypeFloat, DeclInfo.Name, Index, Pointer); + } + else + { + return GetVariableValue(TypeFloat, DeclInfo.Name, PosConstant, Pointer); + } + } + + private Instruction GetNameWithSwizzle( + Instruction Type, + IReadOnlyDictionary Dict, + int Index, + bool Pointer) + { + int VecIndex = Index >> 2; + + if (Dict.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo)) + { + if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size) + { + return GetVariableValue(Type, DeclInfo.Name, Index & 3, Pointer); + } + } + + if (!Dict.TryGetValue(Index, out DeclInfo)) + { + throw new InvalidOperationException(); + } + + return GetVariableValue(Type, DeclInfo.Name, Pointer); + } + + private void PrintHeader() + { + Assembler.Add(new OpCapability(Capability.Shader)); + + Assembler.Add(GlslExtension); + + Assembler.Add(new OpMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450)); + } + + private void PrintTypes() + { + Assembler.Add(TypesConstants); + } + + private void PrintEntryPoint() + { + List Interface = new List(); + + foreach (SpirvVariable Variable in Variables) + { + if (Variable.Storage == StorageClass.Input + || Variable.Storage == StorageClass.Output) + { + Interface.Add(Variable.Id); + } + } + + Assembler.Add(new OpEntryPoint( + GetExecutionModel(), + Main, + "main", + Interface.ToArray())); + } + + private ExecutionModel GetExecutionModel() + { + switch (Decl.ShaderType) + { + case GalShaderType.Vertex: return ExecutionModel.Vertex; + case GalShaderType.TessControl: return ExecutionModel.TessellationControl; + case GalShaderType.TessEvaluation: return ExecutionModel.TessellationEvaluation; + case GalShaderType.Geometry: return ExecutionModel.Geometry; + case GalShaderType.Fragment: return ExecutionModel.Fragment; + + default: + throw new InvalidOperationException(); + } + } + + private Instruction AllocConstant(Instruction Type, Operand Value) + { + Instruction NewConstant = new OpConstant(Type, Value); + + foreach (Instruction Constant in TypesConstants) + { + if (Constant.Equals(NewConstant)) + { + return Constant; + } + } + + TypesConstants.Add(NewConstant); + + return NewConstant; + } + + private Instruction GetVariableValue(Instruction ResultType, string Name, bool Pointer) + { + SpirvVariable Variable = GetVariable(Name); + + if (Pointer) + { + return Variable.Id; + } + + Instruction Value = new OpLoad(ResultType, Variable.Id); + + Code.Add(Value); + + return Value; + } + + private Instruction GetVariableValue(Instruction ResultType, string Name, int Index, bool Pointer) + { + Instruction InstIndex = AllocConstant(TypeInt, new LiteralNumber(Index)); + + return GetVariableValue(ResultType, Name, InstIndex, Pointer); + } + + private Instruction GetVariableValue(Instruction ResultType, string Name, Instruction Index, bool Pointer) + { + OpTypePointer AccessTypePointer; + OpAccessChain Component; + + switch (Name) + { + case "gl_Position": + + Instruction PositionIndex = AllocConstant(TypeInt, new LiteralNumber((int)0)); + + AccessTypePointer = (OpTypePointer)AllocType(new OpTypePointer(StorageClass.Output, ResultType)); + + Component = new OpAccessChain(AccessTypePointer, PerVertexVar, PositionIndex, Index); + + break; + + default: + + SpirvVariable Base = GetVariable(Name); + + AccessTypePointer = (OpTypePointer)AllocType(new OpTypePointer(Base.Storage, ResultType)); + + Component = new OpAccessChain(AccessTypePointer, Base.Id, Index); + + break; + } + + Code.Add(Component); + + if (Pointer) + { + return Component; + } + else + { + Instruction Value = new OpLoad(ResultType, Component); + + Code.Add(Value); + + return Value; + } + } + + private SpirvVariable GetVariable(string Name) + { + foreach (SpirvVariable Variable in Variables) + { + if (Variable.Name == Name) + { + return Variable; + } + } + + throw new InvalidOperationException($"Variable {Name} not declared"); + } + + private Instruction AllocVariable( + Instruction Type, + StorageClass StorageClass, + string Name, + int Location = -1) + { + Instruction InstVariable = new OpVariable(Type, StorageClass); + VarsDeclaration.Add(InstVariable); + + Names.Add(new OpName(InstVariable, Name)); + + if (Location >= 0) + { + Operand Literal = new LiteralNumber(Location); + Decorates.Add(new OpDecorate(InstVariable, Decoration.Location, Literal)); + } + + SpirvVariable Variable = new SpirvVariable(); + Variable.Id = InstVariable; + Variable.Storage = StorageClass; + Variable.Name = Name; + Variable.Location = Location; + + Variables.Add(Variable); + + return InstVariable; + } + + private Instruction AllocLocatedVariable( + Instruction Type, + StorageClass StorageClass, + string Name, + LocationAllocator Allocator) + { + return AllocVariable(Type, StorageClass, Name, Allocator()); + } + + private Instruction AllocType(Instruction NewType) + { + foreach (Instruction StoredType in TypesConstants) + { + if (StoredType.Equals(NewType)) + { + return StoredType; + } + } + + TypesConstants.Add(NewType); + + return NewType; + } + + private int AllocInAttributeLocation() + { + InAttributeCount += 1; + return InAttributeCount - 1; + } + + private int AllocOutAttributeLocation() + { + OutAttributeCount += 1; + return OutAttributeCount - 1; + } + + private int AllocUniformLocation() + { + UniformCount += 1; + return UniformCount - 1; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SpirvInstruction.cs b/Ryujinx.Graphics/Gal/Shader/SpirvInstruction.cs new file mode 100644 index 000000000..0502cb6c8 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SpirvInstruction.cs @@ -0,0 +1,1332 @@ +using System; +using System.IO; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader.SPIRV +{ + public class Instruction + { + public bool HoldsResultId { get; } + + private Opcode Opcode; + private List Operands; + private Instruction ResultType; + + public Instruction( + Opcode Opcode, + bool HoldsResultId, + Instruction ResultType = null) + { + this.Opcode = Opcode; + this.HoldsResultId = HoldsResultId; + this.ResultType = ResultType; + + Operands = new List(); + } + + public void Write(BinaryWriter BinaryWriter) + { + BinaryWriter.Write((ushort)Opcode); + BinaryWriter.Write(GetWordCount()); + + if (ResultType != null) + { + BinaryWriter.Write((uint)ResultType.ResultId); + } + + if (HoldsResultId) + { + BinaryWriter.Write((uint)ResultId); + } + + foreach (Operand Operand in Operands) + { + Operand.Write(BinaryWriter); + } + } + + public ushort GetWordCount() + { + int WordCount = 1; // Opcode and WordCount word + + if (ResultType != null) + { + WordCount++; + } + + if (HoldsResultId) + { + WordCount++; + } + + foreach (Operand Operand in Operands) + { + WordCount += Operand.GetWordCount(); + } + + return (ushort)WordCount; + } + + protected void AddOperand(Operand Operand) + { + Operands.Add(Operand); + } + + protected void AddLiteralInteger(int Value) + { + AddOperand(new LiteralNumber(Value)); + } + + protected void AddEnum(int Value) + { + AddLiteralInteger(Value); + } + + protected void AddString(string Value) + { + AddOperand(new LiteralString(Value)); + } + + protected void AddId(Instruction Instruction) + { + AddOperand(new Id(Instruction)); + } + + protected void AddOperands(Operand[] Operands) + { + foreach (var Operand in Operands) + { + AddOperand(Operand); + } + } + + protected void AddIds(Instruction[] Instructions) + { + foreach (var Instruction in Instructions) + { + AddId(Instruction); + } + } + + private uint _ResultId; + public uint ResultId + { + get + { + if (!HoldsResultId) + { + string Message = "Instruction does not hold a Result ID"; + throw new InvalidOperationException(Message); + } + else if (_ResultId == 0) + { + // You forgot to add this instruction to the Assembler + // and it was referenced from other instruction + string Message = "Instruction does not have a Result ID setted"; + throw new InvalidOperationException(Message); + } + + return _ResultId; + } + set + { + if (!HoldsResultId) + { + string Message = "Instruction does not take Result ID"; + throw new InvalidOperationException(Message); + } + + _ResultId = value; + } + } + } + + public class UnaryInstruction: Instruction + { + public UnaryInstruction( + Opcode Opcode, + Instruction ResultType, + Instruction Operand) + : base(Opcode, true, ResultType) + { + AddId(Operand); + } + } + + public class BinaryInstruction: Instruction + { + public BinaryInstruction( + Opcode Opcode, + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode, true, ResultType) + { + AddId(Operand1); + AddId(Operand2); + } + } + + public class OpExtInst: Instruction + { + public OpExtInst( + Instruction ResultType, + Instruction ExtensionSet, + int InstructionOpcode, + params Instruction[] Ids) + : base(Opcode.ExtInst, true, ResultType) + { + AddId(ExtensionSet); + AddEnum(InstructionOpcode); + AddIds(Ids); + } + } + + public class OpCapability: Instruction + { + public OpCapability(Capability Capability) + : base(Opcode.Capability, false) + { + AddEnum((int)Capability); + } + } + + public class OpExtInstImport: Instruction + { + public OpExtInstImport(string Name) + : base(Opcode.ExtInstImport, true) + { + AddString(Name); + } + } + + public class OpMemoryModel: Instruction + { + public OpMemoryModel(AddressingModel Addressing, MemoryModel Memory) + : base(Opcode.MemoryModel, false) + { + AddEnum((int)Addressing); + AddEnum((int)Memory); + } + } + + public class OpEntryPoint: Instruction + { + public OpEntryPoint( + ExecutionModel Execution, + Instruction EntryPoint, + string Name, + Instruction[] Interface) + : base(Opcode.EntryPoint, false) + { + AddEnum((int)Execution); + AddId(EntryPoint); + AddString(Name); + AddIds(Interface); + } + } + + public class OpExecutionMode: Instruction + { + public OpExecutionMode( + Instruction EntryPoint, + ExecutionMode Execution, + params Operand[] OptionalLiterals) + : base(Opcode.ExecutionMode, false) + { + AddId(EntryPoint); + AddEnum((int)Execution); + AddOperands(OptionalLiterals); + } + } + + public class OpDecorate: Instruction + { + public OpDecorate( + Instruction Target, + Decoration Decoration, + params Operand[] Literals) + : base(Opcode.Decorate, false) + { + AddId(Target); + AddEnum((int)Decoration); + AddOperands(Literals); + } + } + + public class OpTypeVoid: Instruction + { + public OpTypeVoid() + : base(Opcode.TypeVoid, true) + { + } + } + + public class OpTypeFunction: Instruction + { + public OpTypeFunction( + Instruction ReturnType, + params Instruction[] Parameters) + : base(Opcode.TypeFunction, true) + { + AddId(ReturnType); + AddIds(Parameters); + } + } + + public class OpTypeFloat: Instruction + { + public OpTypeFloat(int Width) + : base(Opcode.TypeFloat, true) + { + if (Width != 32 && Width != 64) + { + throw new ArgumentException("Float type size has to be 32 or 64"); + } + + AddLiteralInteger(Width); + } + } + + public class OpTypeVector: Instruction + { + public OpTypeVector(Instruction ComponentType, int ComponentCount) + : base(Opcode.TypeVector, true) + { + AddId(ComponentType); + AddLiteralInteger(ComponentCount); + } + } + + public class OpTypePointer: Instruction + { + public StorageClass Storage; + + public Instruction PointedType; + + public OpTypePointer(StorageClass Storage, Instruction Type) + : base(Opcode.TypePointer, true) + { + AddEnum((int)Storage); + AddId(Type); + + this.Storage = Storage; + this.PointedType = Type; + } + + public override bool Equals(object Object) + { + if (Object is OpTypePointer Other) + { + return this.Storage == Other.Storage + && this.PointedType.Equals(Other.PointedType); + } + + return false; + } + } + + public class OpVariable: Instruction + { + public StorageClass Storage; + + public OpVariable( + Instruction ResultType, + StorageClass Storage, + Instruction Initializer = null) + : base(Opcode.Variable, true, ResultType) + { + AddEnum((int)Storage); + + if (Initializer != null) + { + AddId(Initializer); + } + + this.Storage = Storage; + } + } + + public class OpConstant: Instruction + { + public Instruction ResultType; + + public Operand[] Literals; + + public OpConstant( + Instruction ResultType, + params Operand[] Literals) + : base(Opcode.Constant, true, ResultType) + { + AddOperands(Literals); + + this.ResultType = ResultType; + this.Literals = Literals; + } + + public override bool Equals(object Object) + { + if (Object is OpConstant Other) + { + return this.ResultType.Equals(Other.ResultType) + && this.Literals.Equals(Other.Literals); + } + + return false; + } + } + + public class OpConstantComposite: Instruction + { + public OpConstantComposite( + Instruction ResultType, + params Instruction[] Constituents) + : base(Opcode.ConstantComposite, true, ResultType) + { + AddIds(Constituents); + } + } + + public class OpFunction: Instruction + { + public OpFunction( + Instruction ResultType, + FunctionControl Control, + Instruction FunctionType) + : base(Opcode.Function, true, ResultType) + { + AddEnum((int)Control); + AddId(FunctionType); + } + } + + public class OpStore: Instruction + { + public OpStore( + Instruction Pointer, + Instruction Object, + int MemoryAccess = 0x0) + : base(Opcode.Store, false) + { + AddId(Pointer); + AddId(Object); + AddLiteralInteger(MemoryAccess); + } + } + + public class OpReturn: Instruction + { + public OpReturn() + : base(Opcode.Return, false) + { + } + } + + public class OpFunctionEnd: Instruction + { + public OpFunctionEnd() + : base(Opcode.FunctionEnd, false) + { + } + } + + public class OpLabel: Instruction + { + public OpLabel() + : base(Opcode.Label, true) + { + } + } + + public class OpMemberDecorate: Instruction + { + public OpMemberDecorate( + Instruction StructureType, + int Member, + Decoration Decoration, + params Operand[] Literals) + : base(Opcode.MemberDecorate, false) + { + AddId(StructureType); + AddLiteralInteger(Member); + AddEnum((int)Decoration); + AddOperands(Literals); + } + + public OpMemberDecorate( + Instruction StructureType, + int Member, + Decoration Decoration, + BuiltIn BuiltIn) + : this(StructureType, Member, Decoration, new LiteralNumber((int)BuiltIn)) + { + } + } + + public class OpTypeStruct: Instruction + { + public OpTypeStruct(params Instruction[] MemberIds) + : base(Opcode.TypeStruct, true) + { + AddIds(MemberIds); + } + } + + public class OpTypeInt: Instruction + { + public OpTypeInt(int Width, bool IsSigned) + : base(Opcode.TypeInt, true) + { + // Width shouldn't be checked here because the specification 1.0 does not define it + // but for safety it's locked to 32 and 64 bits + if (Width != 32 && Width != 64) + { + throw new ArgumentException("Integer type size is locked 32 and 64"); + } + + AddLiteralInteger(Width); + AddLiteralInteger(IsSigned ? 1 : 0); + } + } + + public class OpCompositeExtract: Instruction + { + public OpCompositeExtract( + Instruction ResultType, + Instruction Composite, + int[] Indexes) + : base(Opcode.CompositeExtract, true, ResultType) + { + AddId(Composite); + foreach (int Index in Indexes) + { + AddLiteralInteger(Index); + } + } + + public OpCompositeExtract( + Instruction ResultType, + Instruction Composite, + int Index) + : this(ResultType, Composite, new int[]{ Index }) + { + } + } + + public class OpCompositeConstruct: Instruction + { + public OpCompositeConstruct( + Instruction ResultType, + Instruction[] Constituents) + : base(Opcode.CompositeConstruct, true, ResultType) + { + AddIds(Constituents); + } + } + + public class OpLoad: Instruction + { + public OpLoad( + Instruction ResultType, + Instruction Pointer, + int MemoryAccess = 0x0) + : base(Opcode.Load, true, ResultType) + { + AddId(Pointer); + AddLiteralInteger(MemoryAccess); + } + } + + public class OpAccessChain: Instruction + { + public OpAccessChain( + Instruction ResultType, + Instruction Base, + params Instruction[] Indexes) + : base(Opcode.AccessChain, true, ResultType) + { + if (Base is OpVariable Variable) + { + if (ResultType is OpTypePointer Pointer) + { + if (Variable.Storage != Pointer.Storage) + { + throw new ArgumentException("Result type and base have to share the same storage"); + } + } + else + { + throw new ArgumentException("Result type has to be a pointer"); + } + } + else + { + throw new ArgumentException("Base has to be a variable"); + } + + AddId(Base); + AddIds(Indexes); + } + } + + public class OpTypeArray: Instruction + { + public OpTypeArray( + Instruction ElementType, + Instruction Length) + : base(Opcode.TypeArray, true) + { + AddId(ElementType); + AddId(Length); + } + } + + public class OpIAdd: BinaryInstruction + { + public OpIAdd( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.IAdd, ResultType, Operand1, Operand2) + { + } + } + + public class OpFAdd: BinaryInstruction + { + public OpFAdd( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.FAdd, ResultType, Operand1, Operand2) + { + } + } + + public class OpISub: BinaryInstruction + { + public OpISub( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.ISub, ResultType, Operand1, Operand2) + { + } + } + + public class OpFSub: BinaryInstruction + { + public OpFSub( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.FSub, ResultType, Operand1, Operand2) + { + } + } + + public class OpIMul: BinaryInstruction + { + public OpIMul( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.IMul, ResultType, Operand1, Operand2) + { + } + } + + public class OpFMul: BinaryInstruction + { + public OpFMul( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.FMul, ResultType, Operand1, Operand2) + { + } + } + + public class OpUDiv: BinaryInstruction + { + public OpUDiv( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.UDiv, ResultType, Operand1, Operand2) + { + } + } + + public class OpSDiv: BinaryInstruction + { + public OpSDiv( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.SDiv, ResultType, Operand1, Operand2) + { + } + } + + public class OpFDiv: BinaryInstruction + { + public OpFDiv( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.FDiv, ResultType, Operand1, Operand2) + { + } + } + + public class OpUMod: BinaryInstruction + { + public OpUMod( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.UMod, ResultType, Operand1, Operand2) + { + } + } + + public class OpSRem: BinaryInstruction + { + public OpSRem( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.SRem, ResultType, Operand1, Operand2) + { + } + } + + public class OpSMod: BinaryInstruction + { + public OpSMod( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.SMod, ResultType, Operand1, Operand2) + { + } + } + + public class OpFRem: BinaryInstruction + { + public OpFRem( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.FRem, ResultType, Operand1, Operand2) + { + } + } + + public class OpFMod: BinaryInstruction + { + public OpFMod( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.FMod, ResultType, Operand1, Operand2) + { + } + } + + public class OpTypeImage: Instruction + { + public OpTypeImage( + Instruction SampledType, + Dim Dim, + int Depth, + int Arrayed, + int Multisampled, + int Sampled, + ImageFormat ImageFormat, + AccessQualifier AccessQualifier = AccessQualifier.Max) + : base(Opcode.TypeImage, true) + { + AddId(SampledType); + AddEnum((int)Dim); + AddLiteralInteger(Depth); + AddLiteralInteger(Arrayed); + AddLiteralInteger(Multisampled); + AddLiteralInteger(Sampled); + AddEnum((int)ImageFormat); + + if (AccessQualifier != AccessQualifier.Max) + { + AddEnum((int)AccessQualifier); + } + } + } + + public class OpTypeSampledImage: Instruction + { + public OpTypeSampledImage(Instruction ImageType) + : base(Opcode.TypeSampledImage, true) + { + AddId(ImageType); + } + } + + public class OpName: Instruction + { + public OpName( + Instruction Target, + string Name) + : base(Opcode.Name, false) + { + AddId(Target); + AddString(Name); + } + } + + public class OpTypeBool: Instruction + { + public OpTypeBool() + : base(Opcode.TypeBool, true) + { + } + } + + public class OpConstantTrue: Instruction + { + public OpConstantTrue(Instruction ResultType) + : base(Opcode.ConstantTrue, true, ResultType) + { + } + } + + public class OpConstantFalse: Instruction + { + public OpConstantFalse(Instruction ResultType) + : base(Opcode.ConstantFalse, true, ResultType) + { + } + } + + public class OpBitcast: Instruction + { + public OpBitcast( + Instruction ResultType, + Instruction Operand) + : base(Opcode.Bitcast, true, ResultType) + { + AddId(Operand); + } + } + + public class OpShiftRightLogical: Instruction + { + public OpShiftRightLogical( + Instruction ResultType, + Instruction Base, + Instruction Shift) + : base(Opcode.ShiftRightLogical, true, ResultType) + { + AddId(Base); + AddId(Shift); + } + } + + public class OpLogicalNot: Instruction + { + public OpLogicalNot( + Instruction ResultType, + Instruction Operand) + : base(Opcode.LogicalNot, true, ResultType) + { + AddId(Operand); + } + } + + public class OpBranch: Instruction + { + public OpBranch(Instruction TargetLabel) + : base(Opcode.Branch, false) + { + AddId(TargetLabel); + } + } + + public class OpBranchConditional: Instruction + { + public OpBranchConditional( + Instruction Condition, + Instruction TrueLabel, + Instruction FalseLabel, + Operand TrueWeight = null, + Operand FalseWeight = null) + : base(Opcode.BranchConditional, false) + { + AddId(Condition); + AddId(TrueLabel); + AddId(FalseLabel); + + if (TrueWeight != null || FalseWeight != null) + { + if (TrueWeight == null || FalseWeight == null) + { + throw new InvalidOperationException("There must be either no Weights or two"); + } + + AddOperand(TrueWeight); + AddOperand(FalseWeight); + } + + } + } + + public class OpNop: Instruction + { + public OpNop() + : base(Opcode.Nop, false) + { + } + } + + public class OpFOrdLessThan: BinaryInstruction + { + public OpFOrdLessThan( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.FOrdLessThan, ResultType, Operand1, Operand2) + { + } + } + + public class OpFOrdGreaterThan: BinaryInstruction + { + public OpFOrdGreaterThan( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode.FOrdGreaterThan, ResultType, Operand1, Operand2) + { + } + } + + public class OpFNegate: UnaryInstruction + { + public OpFNegate( + Instruction ResultType, + Instruction Operand) + : base(Opcode.FNegate, ResultType, Operand) + { + } + } + + public class GLSLstd450Builder + { + private Instruction ExtensionSet; + + public GLSLstd450Builder(Instruction ExtensionSet) + { + this.ExtensionSet = ExtensionSet; + } + + public OpExtInst Round(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Round, X); + } + + public OpExtInst RoundEven(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.RoundEven, X); + } + + public OpExtInst Trunc(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Trunc, X); + } + + public OpExtInst FAbs(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FAbs, X); + } + + public OpExtInst SAbs(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SAbs, X); + } + + public OpExtInst FSign(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FSign, X); + } + + public OpExtInst SSign(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SSign, X); + } + + public OpExtInst Floor(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Floor, X); + } + + public OpExtInst Ceil(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Ceil, X); + } + + public OpExtInst Fract(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Fract, X); + } + + public OpExtInst Radians(Instruction ResultType, Instruction Degrees) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Radians, Degrees); + } + + public OpExtInst Degrees(Instruction ResultType, Instruction Radians) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Degrees, Radians); + } + + public OpExtInst Sin(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Sin, X); + } + + public OpExtInst Cos(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Cos, X); + } + + public OpExtInst Tan(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Tan, X); + } + + public OpExtInst Asin(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Asin, X); + } + + public OpExtInst Acos(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Acos, X); + } + + public OpExtInst Atan(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Atan, X); + } + + public OpExtInst Sinh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Sinh, X); + } + + public OpExtInst Cosh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Cosh, X); + } + + public OpExtInst Tanh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Tanh, X); + } + + public OpExtInst Asinh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Asinh, X); + } + + public OpExtInst Acosh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Acosh, X); + } + + public OpExtInst Atanh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Atanh, X); + } + + public OpExtInst Atan2(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Atan2, X, Y); + } + + public OpExtInst Pow(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Pow, X, Y); + } + + public OpExtInst Exp(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Exp, X); + } + + public OpExtInst Log(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Log, X); + } + + public OpExtInst Exp2(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Exp2, X); + } + + public OpExtInst Log2(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Log2, X); + } + + public OpExtInst Sqrt(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Sqrt, X); + } + + public OpExtInst InverseSqrt(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.InverseSqrt, X); + } + + public OpExtInst Determinant(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Determinant, X); + } + + public OpExtInst MatrixInverse(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.MatrixInverse, X); + } + + public OpExtInst Modf(Instruction ResultType, Instruction X, Instruction I) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Modf, X, I); + } + + public OpExtInst ModfStruct(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.ModfStruct, X); + } + + public OpExtInst FMin(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FMin, X, Y); + } + + public OpExtInst UMin(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UMin, X, Y); + } + + public OpExtInst SMin(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SMin, X, Y); + } + + public OpExtInst FMax(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FMax, X, Y); + } + + public OpExtInst UMax(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UMax, X, Y); + } + + public OpExtInst SMax(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SMax, X, Y); + } + + public OpExtInst FClamp(Instruction ResultType, Instruction X, Instruction MinVal, Instruction MaxVal) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FClamp, X, MinVal, MaxVal); + } + + public OpExtInst UClamp(Instruction ResultType, Instruction X, Instruction MinVal, Instruction MaxVal) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UClamp, X, MinVal, MaxVal); + } + + public OpExtInst SClamp(Instruction ResultType, Instruction X, Instruction MinVal, Instruction MaxVal) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SClamp, X, MinVal, MaxVal); + } + + public OpExtInst FMix(Instruction ResultType, Instruction X, Instruction Y, Instruction A) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FMix, X, Y, A); + } + + public OpExtInst Step(Instruction ResultType, Instruction Edge, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Step, Edge, X); + } + + public OpExtInst SmoothStep(Instruction ResultType, Instruction Edge1, Instruction Edge2, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SmoothStep, Edge1, Edge2, X); + } + + public OpExtInst Fma(Instruction ResultType, Instruction A, Instruction B, Instruction C) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Fma, A, B, C); + } + + public OpExtInst Frexp(Instruction ResultType, Instruction X, Instruction Exp) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Frexp, X, Exp); + } + + public OpExtInst FrexpStruct(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FrexpStruct, X); + } + + public OpExtInst Ldexp(Instruction ResultType, Instruction X, Instruction Exp) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Ldexp, X, Exp); + } + + public OpExtInst PackSnorm4x8(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackSnorm4x8, V); + } + + public OpExtInst PackUnorm4x8(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackUnorm4x8, V); + } + + public OpExtInst PackSnorm2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackSnorm2x16, V); + } + + public OpExtInst PackUnorm2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackUnorm2x16, V); + } + + public OpExtInst PackHalf2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackHalf2x16, V); + } + + public OpExtInst PackDouble2x32(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackDouble2x32, V); + } + + public OpExtInst UnpackSnorm4x8(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackSnorm4x8, V); + } + + public OpExtInst UnpackUnorm4x8(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackUnorm4x8, V); + } + + public OpExtInst UnpackSnorm2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackSnorm2x16, V); + } + + public OpExtInst UnpackUnorm2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackUnorm2x16, V); + } + + public OpExtInst UnpackHalf2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackHalf2x16, V); + } + + public OpExtInst UnpackDouble2x32(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackDouble2x32, V); + } + + public OpExtInst Length(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Length, X); + } + + public OpExtInst Distance(Instruction ResultType, Instruction Point1, Instruction Point2) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Distance, Point1, Point2); + } + + public OpExtInst Cross(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Cross, X, Y); + } + + public OpExtInst Normalize(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Normalize, X); + } + + public OpExtInst FaceForward(Instruction ResultType, Instruction N, Instruction I, Instruction Nref) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FaceForward, N, I, Nref); + } + + public OpExtInst Reflect(Instruction ResultType, Instruction I, Instruction N) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Reflect, I, N); + } + + public OpExtInst Refract(Instruction ResultType, Instruction I, Instruction N, Instruction Eta) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Refract, I, N, Eta); + } + + public OpExtInst FindILsb(Instruction ResultType, Instruction Value) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FindILsb, Value); + } + + public OpExtInst FindSMsb(Instruction ResultType, Instruction Value) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FindSMsb, Value); + } + + public OpExtInst FindUMsb(Instruction ResultType, Instruction Value) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FindUMsb, Value); + } + + public OpExtInst InterpolateAtCentroid(Instruction ResultType, Instruction Interpolant) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.InterpolateAtCentroid, Interpolant); + } + + public OpExtInst InterpolateAtSample(Instruction ResultType, Instruction Interpolant, Instruction Sample) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.InterpolateAtSample, Interpolant, Sample); + } + + public OpExtInst InterpolateAtOffset(Instruction ResultType, Instruction Interpolant, Instruction Offset) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.InterpolateAtOffset, Interpolant, Offset); + } + + public OpExtInst NMin(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.NMin, X, Y); + } + + public OpExtInst NMax(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.NMax, X, Y); + } + + public OpExtInst NClamp(Instruction ResultType, Instruction X, Instruction MinVal, Instruction MaxVal) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.NClamp, X, MinVal, MaxVal); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SpirvOperand.cs b/Ryujinx.Graphics/Gal/Shader/SpirvOperand.cs new file mode 100644 index 000000000..845096f39 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SpirvOperand.cs @@ -0,0 +1,151 @@ +using System; +using System.IO; +using System.Text; + +namespace Ryujinx.Graphics.Gal.Shader.SPIRV +{ + public abstract class Operand + { + public abstract int GetWordCount(); + + public abstract void Write(BinaryWriter BinaryWriter); + } + + public class Id: Operand + { + private Instruction Instruction; + + public Id(Instruction Instruction) + { + this.Instruction = Instruction; + } + + public override void Write(BinaryWriter BinaryWriter) + { + BinaryWriter.Write((uint)Instruction.ResultId); + } + + public override int GetWordCount() + { + return 1; + } + } + + public abstract class Literal: Operand + { + } + + public class LiteralString: Literal + { + public byte[] Value; + + public LiteralString(string String) + { + Value = Encoding.UTF8.GetBytes(String); + } + + public override void Write(BinaryWriter BinaryWriter) + { + BinaryWriter.Write(Value); + + // Write remaining zero bytes + for (int i = 0; i < 4 - (Value.Length % 4); i++) + { + BinaryWriter.Write((byte)0); + } + } + + public override int GetWordCount() + { + return Value.Length / 4 + 1; + } + + public override bool Equals(object Object) + { + if (Object is LiteralString Other) + { + return this.Value == Other.Value; + } + + return false; + } + } + + public class LiteralNumber: Literal + { + public TypeCode Type; + + public int Integer; + + public float Float32; + + public double Float64; + + public LiteralNumber(int Value) + { + Integer = Value; + Type = Value.GetTypeCode(); + } + + public LiteralNumber(float Value) + { + Float32 = Value; + Type = Value.GetTypeCode(); + } + + public LiteralNumber(double Value) + { + Float64 = Value; + Type = Value.GetTypeCode(); + } + + public override void Write(BinaryWriter BinaryWriter) + { + switch (Type) + { + case TypeCode.Int32: + BinaryWriter.Write(Integer); + break; + + case TypeCode.Single: + BinaryWriter.Write(Float32); + break; + + case TypeCode.Double: + BinaryWriter.Write(Float64); + break; + + default: + throw new NotImplementedException(); + } + } + + public override int GetWordCount() + { + switch (Type) + { + case TypeCode.Int32: + case TypeCode.Single: + return 1; + + case TypeCode.Double: + return 2; + + default: + throw new NotImplementedException(); + } + } + + public override bool Equals(object Object) + { + if (Object is LiteralNumber Other && this.Type == Other.Type) + { + return this.Integer == Other.Integer + && this.Float32 == Other.Float32 + && this.Float64 == Other.Float64; + } + + return false; + } + } +} diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs index 3597f2562..29926f268 100644 --- a/Ryujinx.ShaderTools/Program.cs +++ b/Ryujinx.ShaderTools/Program.cs @@ -2,6 +2,7 @@ using Ryujinx.Graphics.Gal.Shader; using System; using System.IO; +using System.Text; namespace Ryujinx.ShaderTools { @@ -9,13 +10,11 @@ namespace Ryujinx.ShaderTools { static void Main(string[] args) { - if (args.Length == 2) + if (args.Length == 4) { - GlslDecompiler Decompiler = new GlslDecompiler(); - GalShaderType ShaderType = GalShaderType.Vertex; - switch (args[0].ToLower()) + switch (args[1].ToLower()) { case "v": ShaderType = GalShaderType.Vertex; break; case "tc": ShaderType = GalShaderType.TessControl; break; @@ -24,18 +23,34 @@ namespace Ryujinx.ShaderTools case "f": ShaderType = GalShaderType.Fragment; break; } - using (FileStream FS = new FileStream(args[1], FileMode.Open, FileAccess.Read)) + using (FileStream Output = new FileStream(args[3], FileMode.Create)) + using (FileStream FS = new FileStream(args[2], FileMode.Open, FileAccess.Read)) { Memory Mem = new Memory(FS); - GlslProgram Program = Decompiler.Decompile(Mem, 0, ShaderType); + switch (args[0].ToLower()) + { + case "glsl": + GlslDecompiler GlslDecompiler = new GlslDecompiler(); - Console.WriteLine(Program.Code); + GlslProgram Program = GlslDecompiler.Decompile(Mem, 0, ShaderType); + + Output.Write(System.Text.Encoding.UTF8.GetBytes(Program.Code)); + + break; + + case "spirv": + SpirvDecompiler SpirvDecompiler = new SpirvDecompiler(); + + Output.Write(SpirvDecompiler.Decompile(Mem, 0, ShaderType)); + + break; + } } } else { - Console.WriteLine("Usage: Ryujinx.ShaderTools [v|tc|te|g|f] shader.bin"); + Console.WriteLine("Usage: Ryujinx.ShaderTools [spirv|glsl] [v|tc|te|g|f] shader.bin output.bin"); } } }