diff --git a/Ryujinx.Graphics/Gal/Shader/SpirvAssembler.cs b/Ryujinx.Graphics/Gal/Shader/SpirvAssembler.cs new file mode 100644 index 0000000000..2fd0abcd47 --- /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 0000000000..dfccd168d7 --- /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 0000000000..bdd49a656c --- /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 0000000000..0502cb6c81 --- /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 0000000000..845096f398 --- /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.ImGui/Config.cs b/Ryujinx.ImGui/Config.cs new file mode 100644 index 0000000000..b437a006dd --- /dev/null +++ b/Ryujinx.ImGui/Config.cs @@ -0,0 +1,114 @@ +using Ryujinx.HLE.Input; +using Ryujinx.HLE.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace Ryujinx +{ + public static class Config + { + public static JoyCon FakeJoyCon { get; private set; } + + public static void Read(Logger Log) + { + string IniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + + string IniPath = Path.Combine(IniFolder, "Ryujinx.conf"); + + IniParser Parser = new IniParser(IniPath); + + AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks")); + + Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"))); + Log.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"))); + Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info"))); + Log.SetEnable(LogLevel.Warning, Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"))); + Log.SetEnable(LogLevel.Error, Convert.ToBoolean(Parser.Value("Logging_Enable_Error"))); + + string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes").Split(',', StringSplitOptions.RemoveEmptyEntries); + + //When the classes are specified on the list, we only + //enable the classes that are on the list. + //So, first disable everything, then enable + //the classes that the user added to the list. + if (FilteredLogClasses.Length > 0) + { + foreach (LogClass Class in Enum.GetValues(typeof(LogClass))) + { + Log.SetEnable(Class, false); + } + } + + foreach (string LogClass in FilteredLogClasses) + { + if (!string.IsNullOrEmpty(LogClass.Trim())) + { + foreach (LogClass Class in Enum.GetValues(typeof(LogClass))) + { + if (Class.ToString().ToLower().Contains(LogClass.Trim().ToLower())) + { + Log.SetEnable(Class, true); + } + } + } + } + + FakeJoyCon = new JoyCon + { + Left = new JoyConLeft + { + StickUp = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Up")), + StickDown = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Down")), + StickLeft = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Left")), + StickRight = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Right")), + StickButton = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Button")), + DPadUp = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Up")), + DPadDown = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Down")), + DPadLeft = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Left")), + DPadRight = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Right")), + ButtonMinus = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_Minus")), + ButtonL = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_L")), + ButtonZL = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_ZL")) + }, + + Right = new JoyConRight + { + StickUp = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Up")), + StickDown = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Down")), + StickLeft = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Left")), + StickRight = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Right")), + StickButton = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Button")), + ButtonA = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_A")), + ButtonB = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_B")), + ButtonX = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_X")), + ButtonY = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Y")), + ButtonPlus = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Plus")), + ButtonR = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_R")), + ButtonZR = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_ZR")) + } + }; + } + } + + // https://stackoverflow.com/a/37772571 + public class IniParser + { + private readonly Dictionary Values; + + public IniParser(string Path) + { + Values = File.ReadLines(Path) + .Where(Line => !string.IsNullOrWhiteSpace(Line) && !Line.StartsWith('#')) + .Select(Line => Line.Split('=', 2)) + .ToDictionary(Parts => Parts[0].Trim(), Parts => Parts.Length > 1 ? Parts[1].Trim() : null); + } + + public string Value(string Name) + { + return Values.TryGetValue(Name, out string Value) ? Value : null; + } + } +} diff --git a/Ryujinx.ImGui/ConsoleLog.cs b/Ryujinx.ImGui/ConsoleLog.cs new file mode 100644 index 0000000000..1a2899946b --- /dev/null +++ b/Ryujinx.ImGui/ConsoleLog.cs @@ -0,0 +1,51 @@ +using Ryujinx.HLE.Logging; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx +{ + static class ConsoleLog + { + private static Dictionary LogColors; + + private static object ConsoleLock; + + static ConsoleLog() + { + LogColors = new Dictionary() + { + { LogLevel.Stub, ConsoleColor.DarkGray }, + { LogLevel.Info, ConsoleColor.White }, + { LogLevel.Warning, ConsoleColor.Yellow }, + { LogLevel.Error, ConsoleColor.Red } + }; + + ConsoleLock = new object(); + } + + public static void PrintLog(object sender, LogEventArgs e) + { + string FormattedTime = e.Time.ToString(@"hh\:mm\:ss\.fff"); + + string CurrentThread = Thread.CurrentThread.ManagedThreadId.ToString("d4"); + + string Message = FormattedTime + " | " + CurrentThread + " " + e.Message; + + if (LogColors.TryGetValue(e.Level, out ConsoleColor Color)) + { + lock (ConsoleLock) + { + Console.ForegroundColor = Color; + + Console.WriteLine(Message); + Console.ResetColor(); + } + } + else + { + Console.WriteLine(Message); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.ImGui/GUI/MainUI.cs b/Ryujinx.ImGui/GUI/MainUI.cs new file mode 100644 index 0000000000..fae534831e --- /dev/null +++ b/Ryujinx.ImGui/GUI/MainUI.cs @@ -0,0 +1,312 @@ +using ImGuiNET; +using OpenTK; +using Ryujinx.Audio; +using Ryujinx.Audio.OpenAL; +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Gal.OpenGL; +using Ryujinx.HLE; +using Ryujinx.HLE.Input; +using OpenTK.Graphics; +using OpenTK.Input; +using System; +using System.IO; + +namespace Ryujinx.UI +{ + class MainUI : WindowHelper + { + //toggles + private bool ShowUI = true; + private bool ShowFileDialog = false; + private bool _isRunning = false; + private bool IsRunning + { + get => _isRunning; + set + { + _isRunning = value; + if (!value) + { + ShowUI = true; + } + } + } + + private string CurrentPath = Environment.CurrentDirectory; + private string PackagePath = string.Empty; + + private const int TouchScreenWidth = 1280; + private const int TouchScreenHeight = 720; + + private const float TouchScreenRatioX = (float)TouchScreenWidth / TouchScreenHeight; + private const float TouchScreenRatioY = (float)TouchScreenHeight / TouchScreenWidth; + + FilePicker FileDialog; + + IGalRenderer Renderer; + IAalOutput AudioOut; + Switch Ns; + + public MainUI() : base("Test") + { + FileDialog = FilePicker.GetFilePicker("rom",null); + + Renderer = new OpenGLRenderer(); + + AudioOut = new OpenALAudioOut(); + + Ns = new Switch(Renderer, AudioOut); + + Config.Read(Ns.Log); + + Ns.Log.Updated += ConsoleLog.PrintLog; + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + VSync = VSyncMode.On; + + Renderer.SetWindowSize(Width, Height); + } + + protected override void OnRenderFrame(FrameEventArgs e) + { + _deltaTime = (float)e.Time; + if (ShowUI) + { + StartFrame(); + RenderUI(); + EndFrame(); + } + else + { + Ns.Statistics.StartSystemFrame(); + + Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " + + $"{Ns.Statistics.GameFrameRate:0})"; + + Renderer.RunActions(); + Renderer.Render(); + + SwapBuffers(); + + Ns.Statistics.EndSystemFrame(); + + Ns.Os.SignalVsync(); + } + } + + protected override void OnUpdateFrame(FrameEventArgs e) + { + if (!ShowUI) + { + HidControllerButtons CurrentButton = 0; + HidJoystickPosition LeftJoystick; + HidJoystickPosition RightJoystick; + + int LeftJoystickDX = 0; + int LeftJoystickDY = 0; + int RightJoystickDX = 0; + int RightJoystickDY = 0; + + //RightJoystick + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickUp]) LeftJoystickDY = short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickDown]) LeftJoystickDY = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickLeft]) LeftJoystickDX = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickRight]) LeftJoystickDX = short.MaxValue; + + //LeftButtons + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickButton]) CurrentButton |= HidControllerButtons.KEY_LSTICK; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadUp]) CurrentButton |= HidControllerButtons.KEY_DUP; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadDown]) CurrentButton |= HidControllerButtons.KEY_DDOWN; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadLeft]) CurrentButton |= HidControllerButtons.KEY_DLEFT; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadRight]) CurrentButton |= HidControllerButtons.KEY_DRIGHT; + if (Keyboard[(Key)Config.FakeJoyCon.Left.ButtonMinus]) CurrentButton |= HidControllerButtons.KEY_MINUS; + if (Keyboard[(Key)Config.FakeJoyCon.Left.ButtonL]) CurrentButton |= HidControllerButtons.KEY_L; + if (Keyboard[(Key)Config.FakeJoyCon.Left.ButtonZL]) CurrentButton |= HidControllerButtons.KEY_ZL; + + //RightJoystick + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickUp]) RightJoystickDY = short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickDown]) RightJoystickDY = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickLeft]) RightJoystickDX = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickRight]) RightJoystickDX = short.MaxValue; + + //RightButtons + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickButton]) CurrentButton |= HidControllerButtons.KEY_RSTICK; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonA]) CurrentButton |= HidControllerButtons.KEY_A; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonB]) CurrentButton |= HidControllerButtons.KEY_B; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonX]) CurrentButton |= HidControllerButtons.KEY_X; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonY]) CurrentButton |= HidControllerButtons.KEY_Y; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonPlus]) CurrentButton |= HidControllerButtons.KEY_PLUS; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonR]) CurrentButton |= HidControllerButtons.KEY_R; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonZR]) CurrentButton |= HidControllerButtons.KEY_ZR; + + LeftJoystick = new HidJoystickPosition + { + DX = LeftJoystickDX, + DY = LeftJoystickDY + }; + + RightJoystick = new HidJoystickPosition + { + DX = RightJoystickDX, + DY = RightJoystickDY + }; + + bool HasTouch = false; + + //Get screen touch position from left mouse click + //OpenTK always captures mouse events, even if out of focus, so check if window is focused. + if (Focused && Mouse?.GetState().LeftButton == ButtonState.Pressed) + { + int ScrnWidth = Width; + int ScrnHeight = Height; + + if (Width > Height * TouchScreenRatioX) + { + ScrnWidth = (int)(Height * TouchScreenRatioX); + } + else + { + ScrnHeight = (int)(Width * TouchScreenRatioY); + } + + int StartX = (Width - ScrnWidth) >> 1; + int StartY = (Height - ScrnHeight) >> 1; + + int EndX = StartX + ScrnWidth; + int EndY = StartY + ScrnHeight; + + if (Mouse.X >= StartX && + Mouse.Y >= StartY && + Mouse.X < EndX && + Mouse.Y < EndY) + { + int ScrnMouseX = Mouse.X - StartX; + int ScrnMouseY = Mouse.Y - StartY; + + int MX = (int)(((float)ScrnMouseX / ScrnWidth) * TouchScreenWidth); + int MY = (int)(((float)ScrnMouseY / ScrnHeight) * TouchScreenHeight); + + HidTouchPoint CurrentPoint = new HidTouchPoint + { + X = MX, + Y = MY, + + //Placeholder values till more data is acquired + DiameterX = 10, + DiameterY = 10, + Angle = 90 + }; + + HasTouch = true; + + Ns.Hid.SetTouchPoints(CurrentPoint); + } + } + + if (!HasTouch) + { + Ns.Hid.SetTouchPoints(); + } + + Ns.Hid.SetJoyconButton( + HidControllerId.CONTROLLER_HANDHELD, + HidControllerLayouts.Handheld_Joined, + CurrentButton, + LeftJoystick, + RightJoystick); + + Ns.Hid.SetJoyconButton( + HidControllerId.CONTROLLER_HANDHELD, + HidControllerLayouts.Main, + CurrentButton, + LeftJoystick, + RightJoystick); + } + } + + private void RenderUI() + { + if (ShowUI) + { + ImGui.SetNextWindowPos(System.Numerics.Vector2.Zero, Condition.Always, + System.Numerics.Vector2.Zero); + ImGui.SetNextWindowSize(new System.Numerics.Vector2(Width, Height),Condition.Always); + if (ImGui.BeginWindow("MainWindow",ref ShowUI, WindowFlags.NoTitleBar + | WindowFlags.NoMove | WindowFlags.AlwaysAutoResize)) + { + if(ImGui.BeginChildFrame(0, new System.Numerics.Vector2(-1,-1), + WindowFlags.AlwaysAutoResize)) + { + ImGuiNative.igBeginGroup(); + if(ImGui.Button("Load Package", new System.Numerics.Vector2(Values.ButtonWidth, + Values.ButtonHeight))){ + ShowFileDialog = true; + } + ImGuiNative.igEndGroup(); + ImGui.SameLine(); + + if(ImGui.BeginChildFrame(1, ImGui.GetContentRegionAvailable(), + WindowFlags.AlwaysAutoResize)) + { + if (ShowFileDialog) + { + string output = CurrentPath; + if (FileDialog.Draw(ref output, false)) + { + if (!string.IsNullOrWhiteSpace(output)) + { + PackagePath = output; + ShowFileDialog = false; + LoadPackage(PackagePath); + } + } + } + ImGui.EndChildFrame(); + } + ImGui.EndChildFrame(); + } + ImGui.EndWindow(); + } + } + } + + public void LoadPackage(string path) + { + if (Directory.Exists(path)) + { + string[] RomFsFiles = Directory.GetFiles(path, "*.istorage"); + + if (RomFsFiles.Length == 0) + { + RomFsFiles = Directory.GetFiles(path, "*.romfs"); + } + + if (RomFsFiles.Length > 0) + { + Console.WriteLine("Loading as cart with RomFS."); + + Ns.LoadCart(path, RomFsFiles[0]); + } + else + { + Console.WriteLine("Loading as cart WITHOUT RomFS."); + + Ns.LoadCart(path); + } + } + else if (File.Exists(path)) + { + Console.WriteLine("Loading as homebrew."); + + Ns.LoadProgram(path); + } + IsRunning = true; + ShowUI = false; + } + + } +} diff --git a/Ryujinx.ImGui/GUI/Values.cs b/Ryujinx.ImGui/GUI/Values.cs new file mode 100644 index 0000000000..0f7460ab67 --- /dev/null +++ b/Ryujinx.ImGui/GUI/Values.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Numerics; + +namespace ImGuiNET +{ + public struct Values + { + public const float ButtonWidth = 170f; + public const float ButtonHeight = 50f; + public const float DefaultWindowScale = 1.0f; + public const float SelectibleHeight = 20.0f; + public static float CurrentWindowScale = 1.0f; + public static float CurrentFontScale = 1.2f; + + public struct Color + { + public static Vector4 Yellow = new Vector4(1.0f, 1.0f, 0, 1.0f); + } + } +} diff --git a/Ryujinx.ImGui/GUI/Widgets/FilePicker.cs b/Ryujinx.ImGui/GUI/Widgets/FilePicker.cs new file mode 100644 index 0000000000..804db77333 --- /dev/null +++ b/Ryujinx.ImGui/GUI/Widgets/FilePicker.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; + +namespace ImGuiNET +{ + /// + /// Adapted from Mellinoe's file picker for imgui + /// https://github.com/mellinoe/synthapp/blob/master/src/synthapp/Widgets/FilePicker.cs + /// + public class FilePicker + { + private const string FilePickerID = "###FilePicker"; + private static readonly Dictionary s_filePickers = new Dictionary(); + private static readonly Vector2 DefaultFilePickerSize = new Vector2(600, 400); + + public string CurrentFolder { get; set; } + public string SelectedFile { get; set; } + + public static FilePicker GetFilePicker(object o, string startingPath) + { + if (File.Exists(startingPath)) + { + startingPath = new FileInfo(startingPath).DirectoryName; + } + else if (string.IsNullOrEmpty(startingPath) || !Directory.Exists(startingPath)) + { + startingPath = Environment.CurrentDirectory; + if (string.IsNullOrEmpty(startingPath)) + { + startingPath = AppContext.BaseDirectory; + } + } + + if (!s_filePickers.TryGetValue(o, out FilePicker fp)) + { + fp = new FilePicker(); + fp.CurrentFolder = startingPath; + s_filePickers.Add(o, fp); + } + + return fp; + } + + public bool Draw(ref string selected, bool returnOnSelection) + { + bool result = false; + result = DrawFolder(ref selected, returnOnSelection); + return result; + } + + private bool DrawFolder(ref string selected, bool returnOnSelection = false) + { + ImGui.Text("Current Folder: " + CurrentFolder); + bool result = false; + + if (ImGui.BeginChildFrame(1, ImGui.GetContentRegionAvailable() - new Vector2(20, Values.ButtonHeight), + WindowFlags.Default)) + { + DirectoryInfo di = new DirectoryInfo(CurrentFolder); + if (di.Exists) + { + if (di.Parent != null) + { + ImGui.PushStyleColor(ColorTarget.Text, Values.Color.Yellow); + + if (ImGui.Selectable("../", false, SelectableFlags.DontClosePopups + , new Vector2(ImGui.GetContentRegionAvailableWidth(), Values.SelectibleHeight))) + { + CurrentFolder = di.Parent.FullName; + } + + ImGui.PopStyleColor(); + } + foreach (var dir in Directory.EnumerateFileSystemEntries(di.FullName)) + { + if (Directory.Exists(dir)) + { + string name = Path.GetFileName(dir); + bool isSelected = SelectedFile == dir; + + ImGui.PushStyleColor(ColorTarget.Text, Values.Color.Yellow); + + if (ImGui.Selectable(name + "/", isSelected, SelectableFlags.DontClosePopups + , new Vector2(ImGui.GetContentRegionAvailableWidth(), Values.SelectibleHeight))) + { + SelectedFile = dir; + selected = SelectedFile; + } + + if (SelectedFile != null) + if (ImGui.IsMouseDoubleClicked(0) && SelectedFile.Equals(dir)) + { + SelectedFile = null; + selected = null; + CurrentFolder = dir; + } + + ImGui.PopStyleColor(); + } + } + foreach (var file in Directory.EnumerateFiles(di.FullName)) + { + string name = Path.GetFileName(file); + bool isSelected = SelectedFile == file; + + if (ImGui.Selectable(name, isSelected, SelectableFlags.DontClosePopups + , new Vector2(ImGui.GetContentRegionAvailableWidth(), Values.SelectibleHeight))) + { + SelectedFile = file; + if (returnOnSelection) + { + selected = SelectedFile; + } + } + + if (SelectedFile != null) + if (ImGui.IsMouseDoubleClicked(0) && SelectedFile.Equals(file)) + { + selected = file; + result = true; + } + } + } + } + ImGui.EndChildFrame(); + + + if (ImGui.Button("Cancel", new Vector2(Values.ButtonWidth, Values.ButtonHeight))) + { + result = false; + } + + if (SelectedFile != null) + { + ImGui.SameLine(); + if (ImGui.Button("Open", new Vector2(Values.ButtonWidth, Values.ButtonHeight))) + { + result = true; + selected = SelectedFile; + } + } + + return result; + } + } +} diff --git a/Ryujinx.ImGui/GUI/WindowHelper.cs b/Ryujinx.ImGui/GUI/WindowHelper.cs new file mode 100644 index 0000000000..7a6717e1c7 --- /dev/null +++ b/Ryujinx.ImGui/GUI/WindowHelper.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenTK; +using OpenTK.Input; +using OpenTK.Graphics; +using OpenTK.Graphics.OpenGL; +using ImGuiNET; + +namespace Ryujinx.UI +{ + class WindowHelper : GameWindow + { + protected float _deltaTime; + bool IsWindowOpened = false; + int s_fontTexture; + float _wheelPosition; + + public WindowHelper(string title) : base(1280, 720, GraphicsMode.Default, title, GameWindowFlags.Default + , DisplayDevice.Default, 3, 3, GraphicsContextFlags.ForwardCompatible) + { + Title = title; + IsWindowOpened = true; + + Location = new Point( + (DisplayDevice.Default.Width / 2) - (Width / 2), + (DisplayDevice.Default.Height / 2) - (Height / 2)); + } + + public void ShowDemo() + { + ImGuiNative.igShowDemoWindow(ref IsWindowOpened); + } + + public void StartFrame() + { + IO io = ImGui.GetIO(); + io.DisplaySize = new System.Numerics.Vector2(Width, Height); + io.DisplayFramebufferScale = new System.Numerics.Vector2(Values.CurrentWindowScale); + io.DeltaTime = _deltaTime; + ImGui.NewFrame(); + HandleInput(io); + } + + public unsafe void EndFrame() + { + ImGui.Render(); + DrawData* data = ImGui.GetDrawData(); + RenderImDrawData(data); + } + + protected unsafe override void OnLoad(EventArgs e) + { + ImGui.GetIO().FontAtlas.AddDefaultFont(); + + IO io = ImGui.GetIO(); + + io.FontAllowUserScaling = true; + + ImGuiNative.igGetIO()->FontGlobalScale = Values.CurrentFontScale; + + // Build texture atlas + FontTextureData texData = io.FontAtlas.GetTexDataAsAlpha8(); + + // Create OpenGL texture + s_fontTexture = GL.GenTexture(); + GL.BindTexture(TextureTarget.Texture2D, s_fontTexture); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear); + GL.TexImage2D( + TextureTarget.Texture2D, + 0, + PixelInternalFormat.Alpha, + texData.Width, + texData.Height, + 0, + PixelFormat.Alpha, + PixelType.UnsignedByte, + new IntPtr(texData.Pixels)); + + // Store the texture identifier in the ImFontAtlas substructure. + io.FontAtlas.SetTexID(s_fontTexture); + + // Cleanup (don't clear the input data if you want to append new fonts later) + //io.Fonts->ClearInputData(); + io.FontAtlas.ClearTexData(); + GL.BindTexture(TextureTarget.Texture2D, 0); + } + + void HandleInput(IO io) + { + MouseState cursorState = Mouse.GetCursorState(); + MouseState mouseState = Mouse.GetState(); + + if (Focused) + { + Point windowPoint = PointToClient(new Point(cursorState.X, cursorState.Y)); + io.MousePosition = new System.Numerics.Vector2(windowPoint.X / io.DisplayFramebufferScale.X, windowPoint.Y / io.DisplayFramebufferScale.Y); + } + else + { + io.MousePosition = new System.Numerics.Vector2(-1f, -1f); + } + + io.MouseDown[0] = mouseState.LeftButton == ButtonState.Pressed; + io.MouseDown[1] = mouseState.RightButton == ButtonState.Pressed; + io.MouseDown[2] = mouseState.MiddleButton == ButtonState.Pressed; + + float newWheelPos = mouseState.WheelPrecise; + float delta = newWheelPos - _wheelPosition; + _wheelPosition = newWheelPos; + io.MouseWheel = delta; + } + + private unsafe void RenderImDrawData(DrawData* draw_data) + { + // Rendering + int display_w, display_h; + display_w = Width; + display_h = Height; + + Vector4 clear_color = new Vector4(114f / 255f, 144f / 255f, 154f / 255f, 1.0f); + GL.Viewport(0, 0, display_w, display_h); + GL.ClearColor(clear_color.X, clear_color.Y, clear_color.Z, clear_color.W); + GL.Clear(ClearBufferMask.ColorBufferBit); + + // We are using the OpenGL fixed pipeline to make the example code simpler to read! + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers. + int last_texture; + GL.GetInteger(GetPName.TextureBinding2D, out last_texture); + GL.PushAttrib(AttribMask.EnableBit | AttribMask.ColorBufferBit | AttribMask.TransformBit); + GL.Enable(EnableCap.Blend); + GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); + GL.Disable(EnableCap.CullFace); + GL.Disable(EnableCap.DepthTest); + GL.Enable(EnableCap.ScissorTest); + GL.EnableClientState(ArrayCap.VertexArray); + GL.EnableClientState(ArrayCap.TextureCoordArray); + GL.EnableClientState(ArrayCap.ColorArray); + GL.Enable(EnableCap.Texture2D); + + GL.UseProgram(0); + + // Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays) + IO io = ImGui.GetIO(); + ImGui.ScaleClipRects(draw_data, io.DisplayFramebufferScale); + + // Setup orthographic projection matrix + GL.MatrixMode(MatrixMode.Projection); + GL.PushMatrix(); + GL.LoadIdentity(); + GL.Ortho( + 0.0f, + io.DisplaySize.X / io.DisplayFramebufferScale.X, + io.DisplaySize.Y / io.DisplayFramebufferScale.Y, + 0.0f, + -1.0f, + 1.0f); + GL.MatrixMode(MatrixMode.Modelview); + GL.PushMatrix(); + GL.LoadIdentity(); + + // Render command lists + + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + NativeDrawList* cmd_list = draw_data->CmdLists[n]; + byte* vtx_buffer = (byte*)cmd_list->VtxBuffer.Data; + ushort* idx_buffer = (ushort*)cmd_list->IdxBuffer.Data; + + GL.VertexPointer(2, VertexPointerType.Float, sizeof(DrawVert), new IntPtr(vtx_buffer + DrawVert.PosOffset)); + GL.TexCoordPointer(2, TexCoordPointerType.Float, sizeof(DrawVert), new IntPtr(vtx_buffer + DrawVert.UVOffset)); + GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(DrawVert), new IntPtr(vtx_buffer + DrawVert.ColOffset)); + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + DrawCmd* pcmd = &(((DrawCmd*)cmd_list->CmdBuffer.Data)[cmd_i]); + if (pcmd->UserCallback != IntPtr.Zero) + { + throw new NotImplementedException(); + } + else + { + GL.BindTexture(TextureTarget.Texture2D, pcmd->TextureId.ToInt32()); + GL.Scissor( + (int)pcmd->ClipRect.X, + (int)(io.DisplaySize.Y - pcmd->ClipRect.W), + (int)(pcmd->ClipRect.Z - pcmd->ClipRect.X), + (int)(pcmd->ClipRect.W - pcmd->ClipRect.Y)); + GL.DrawElements(PrimitiveType.Triangles, (int)pcmd->ElemCount, DrawElementsType.UnsignedShort, new IntPtr(idx_buffer)); + } + idx_buffer += pcmd->ElemCount; + } + } + + // Restore modified state + GL.DisableClientState(ArrayCap.ColorArray); + GL.DisableClientState(ArrayCap.TextureCoordArray); + GL.DisableClientState(ArrayCap.VertexArray); + GL.BindTexture(TextureTarget.Texture2D, last_texture); + GL.MatrixMode(MatrixMode.Modelview); + GL.PopMatrix(); + GL.MatrixMode(MatrixMode.Projection); + GL.PopMatrix(); + GL.PopAttrib(); + + SwapBuffers(); + } + } +} diff --git a/Ryujinx.ImGui/Program.cs b/Ryujinx.ImGui/Program.cs new file mode 100644 index 0000000000..a874a7ed86 --- /dev/null +++ b/Ryujinx.ImGui/Program.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ryujinx.UI +{ + class Program + { + static void Main(string[] args) + { + MainUI mainUI = new MainUI(); + mainUI.Run(60.0, 60.0); + + Environment.Exit(0); + } + } +} diff --git a/Ryujinx.ImGui/Ryujinx.UI.csproj b/Ryujinx.ImGui/Ryujinx.UI.csproj new file mode 100644 index 0000000000..1cde6c2896 --- /dev/null +++ b/Ryujinx.ImGui/Ryujinx.UI.csproj @@ -0,0 +1,35 @@ + + + + Exe + netcoreapp2.1 + + + + true + + + + true + true + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/Ryujinx.ImGui/Ryujinx.conf b/Ryujinx.ImGui/Ryujinx.conf new file mode 100644 index 0000000000..611f320717 --- /dev/null +++ b/Ryujinx.ImGui/Ryujinx.conf @@ -0,0 +1,47 @@ +#Enable cpu memory checks (slow) +Enable_Memory_Checks = false + +#Enable print debug logs +Logging_Enable_Debug = false + +#Enable print stubbed calls logs +Logging_Enable_Stub = true + +#Enable print informations logs +Logging_Enable_Info = true + +#Enable print warning logs +Logging_Enable_Warn = true + +#Enable print error logs +Logging_Enable_Error = true + +#Filtered log classes, seperated by ", ", eg. `Logging_Filtered_Classes = Loader, ServiceFS` +Logging_Filtered_Classes = + +#https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs +Controls_Left_FakeJoycon_Stick_Up = 105 +Controls_Left_FakeJoycon_Stick_Down = 101 +Controls_Left_FakeJoycon_Stick_Left = 83 +Controls_Left_FakeJoycon_Stick_Right = 86 +Controls_Left_FakeJoycon_Stick_Button = 88 +Controls_Left_FakeJoycon_DPad_Up = 45 +Controls_Left_FakeJoycon_DPad_Down = 46 +Controls_Left_FakeJoycon_DPad_Left = 47 +Controls_Left_FakeJoycon_DPad_Right = 48 +Controls_Left_FakeJoycon_Button_Minus = 120 +Controls_Left_FakeJoycon_Button_L = 87 +Controls_Left_FakeJoycon_Button_ZL = 99 + +Controls_Right_FakeJoycon_Stick_Up = 91 +Controls_Right_FakeJoycon_Stick_Down = 93 +Controls_Right_FakeJoycon_Stick_Left = 92 +Controls_Right_FakeJoycon_Stick_Right = 94 +Controls_Right_FakeJoycon_Stick_Button = 90 +Controls_Right_FakeJoycon_Button_A = 108 +Controls_Right_FakeJoycon_Button_B = 106 +Controls_Right_FakeJoycon_Button_X = 85 +Controls_Right_FakeJoycon_Button_Y = 104 +Controls_Right_FakeJoycon_Button_Plus = 121 +Controls_Right_FakeJoycon_Button_R = 103 +Controls_Right_FakeJoycon_Button_ZR = 97 \ No newline at end of file diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs index 3597f25624..29926f2686 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"); } } } diff --git a/Ryujinx.sln b/Ryujinx.sln index cd04dabc20..97fe49d623 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -15,9 +15,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics", "Ryujinx EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{5C1D818E-682A-46A5-9D54-30006E26C270}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luea", "Ryujinx.LLE\Luea.csproj", "{8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luea", "Ryujinx.LLE\Luea.csproj", "{8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.UI", "Ryujinx.ImGui\Ryujinx.UI.csproj", "{00117502-1661-4C8B-8C07-177C1A8AA455}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -57,6 +59,10 @@ Global {8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}.Release|Any CPU.Build.0 = Release|Any CPU + {00117502-1661-4C8B-8C07-177C1A8AA455}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00117502-1661-4C8B-8C07-177C1A8AA455}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00117502-1661-4C8B-8C07-177C1A8AA455}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00117502-1661-4C8B-8C07-177C1A8AA455}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE