diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs index 46857b33ca..bbf1eeaae7 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Engine ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)dispatchParams.ShaderOffset; // Note: A size of 0 is also invalid, the size must be at least 1. - int sharedMemorySize = Math.Clamp(dispatchParams.SharedMemorySize & 0xffff, 4, _context.Capabilities.MaximumComputeSharedMemorySize); + int sharedMemorySize = Math.Clamp(dispatchParams.SharedMemorySize & 0xffff, 1, _context.Capabilities.MaximumComputeSharedMemorySize); ComputeShader cs = _shaderCache.GetComputeShader( shaderGpuVa, diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index 0fa3514f07..76b407cc2f 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -671,9 +671,7 @@ namespace Ryujinx.Graphics.Gpu.Engine addressesArray[index] = baseAddress + shader.Offset; } - bool viewportTransformEnable = GetViewportTransformEnable(state); - - GraphicsShader gs = _shaderCache.GetGraphicsShader(addresses, !viewportTransformEnable); + GraphicsShader gs = _shaderCache.GetGraphicsShader(state, addresses); _vsUsesInstanceId = gs.Shader[0].Program.Info.UsesInstanceId; @@ -734,7 +732,7 @@ namespace Ryujinx.Graphics.Gpu.Engine _context.Renderer.Pipeline.BindProgram(gs.HostProgram); } - private bool GetViewportTransformEnable(GpuState state) + public bool GetViewportTransformEnable(GpuState state) { // FIXME: We should read ViewportTransformEnable, but it seems that some games writes 0 there? // return state.Get(MethodOffset.ViewportTransformEnable) != 0; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 63a42709ef..ce58e5c54a 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -199,6 +199,21 @@ namespace Ryujinx.Graphics.Gpu.Image } } + public TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle) + { + int packedId = ReadPackedId(stageIndex, handle); + + int textureId = UnpackTextureId(packedId); + + var poolState = state.Get(MethodOffset.TexturePoolState); + + ulong poolAddress = _context.MemoryManager.Translate(poolState.Address.Pack()); + + TexturePool texturePool = _texturePoolCache.FindOrCreate(poolAddress, poolState.MaximumId); + + return texturePool.GetDescriptor(textureId); + } + private int ReadPackedId(int stage, int wordOffset) { ulong address; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs index 79e4f55eaf..6d1f0fb18d 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs @@ -101,6 +101,11 @@ namespace Ryujinx.Graphics.Gpu.Image return (int)((Word5 >> 16) & 0x3fff) + 1; } + public bool UnpackTextureCoordNormalized() + { + return (Word5 & (1 << 31)) != 0; + } + public int UnpackBaseLevel() { return (int)(Word7 & 0xf); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index ce0cc249ff..73067249ae 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -129,6 +129,11 @@ namespace Ryujinx.Graphics.Gpu.Image UpdateRenderTargets(); } + public TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle) + { + return _gpBindingsManager.GetTextureDescriptor(state, stageIndex, handle); + } + private void UpdateRenderTargets() { bool anyChanged = false; diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 6014f07cd6..47fa01b6a3 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -36,11 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (texture == null) { - ulong address = Address + (ulong)(uint)id * DescriptorSize; - - Span data = Context.PhysicalMemory.Read(address, DescriptorSize); - - TextureDescriptor descriptor = MemoryMarshal.Cast(data)[0]; + TextureDescriptor descriptor = GetDescriptor(id); TextureInfo info = GetInfo(descriptor); @@ -66,6 +62,15 @@ namespace Ryujinx.Graphics.Gpu.Image return texture; } + public TextureDescriptor GetDescriptor(int id) + { + ulong address = Address + (ulong)(uint)id * DescriptorSize; + + Span data = Context.PhysicalMemory.Read(address, DescriptorSize); + + return MemoryMarshal.Cast(data)[0]; + } + protected override void InvalidateRangeImpl(ulong address, ulong size) { ulong endAddress = address + size; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs b/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs index 8f5139034f..d42d1e774e 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Gpu.Image Texture1DArray, Texture2DArray, TextureBuffer, - Texture2DLinear, + Texture2DRect, CubemapArray } @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Gpu.Image { case TextureTarget.Texture1D: return Target.Texture1D; case TextureTarget.Texture2D: return Target.Texture2D; - case TextureTarget.Texture2DLinear: return Target.Texture2D; + case TextureTarget.Texture2DRect: return Target.Texture2D; case TextureTarget.Texture3D: return Target.Texture3D; case TextureTarget.Texture1DArray: return Target.Texture1DArray; case TextureTarget.Texture2DArray: return Target.Texture2DArray; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 648e073c77..422cc59727 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -1,10 +1,10 @@ using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; -using System.Globalization; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Shader @@ -13,6 +13,8 @@ namespace Ryujinx.Graphics.Gpu.Shader { private const int MaxProgramSize = 0x100000; + private const TranslationFlags DefaultFlags = TranslationFlags.DebugMode; + private GpuContext _context; private ShaderDumper _dumper; @@ -69,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader return cpShader; } - public GraphicsShader GetGraphicsShader(ShaderAddresses addresses, bool dividePosXY) + public GraphicsShader GetGraphicsShader(GpuState state, ShaderAddresses addresses) { bool isCached = _gpPrograms.TryGetValue(addresses, out List list); @@ -86,28 +88,19 @@ namespace Ryujinx.Graphics.Gpu.Shader GraphicsShader gpShaders = new GraphicsShader(); - TranslationFlags flags = - TranslationFlags.DebugMode | - TranslationFlags.Unspecialized; - - if (dividePosXY) - { - flags |= TranslationFlags.DividePosXY; - } - if (addresses.VertexA != 0) { - gpShaders.Shader[0] = TranslateGraphicsShader(flags, addresses.Vertex, addresses.VertexA); + gpShaders.Shader[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA); } else { - gpShaders.Shader[0] = TranslateGraphicsShader(flags, addresses.Vertex); + gpShaders.Shader[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex); } - gpShaders.Shader[1] = TranslateGraphicsShader(flags, addresses.TessControl); - gpShaders.Shader[2] = TranslateGraphicsShader(flags, addresses.TessEvaluation); - gpShaders.Shader[3] = TranslateGraphicsShader(flags, addresses.Geometry); - gpShaders.Shader[4] = TranslateGraphicsShader(flags, addresses.Fragment); + gpShaders.Shader[1] = TranslateGraphicsShader(state, ShaderStage.TessellationControl, addresses.TessControl); + gpShaders.Shader[2] = TranslateGraphicsShader(state, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); + gpShaders.Shader[3] = TranslateGraphicsShader(state, ShaderStage.Geometry, addresses.Geometry); + gpShaders.Shader[4] = TranslateGraphicsShader(state, ShaderStage.Fragment, addresses.Fragment); BackpropQualifiers(gpShaders); @@ -199,25 +192,31 @@ namespace Ryujinx.Graphics.Gpu.Shader return null; } - ShaderProgram program; + QueryInfoCallback queryInfo = (QueryInfoName info, int index) => + { + switch (info) + { + case QueryInfoName.ComputeLocalSizeX: + return localSizeX; + case QueryInfoName.ComputeLocalSizeY: + return localSizeY; + case QueryInfoName.ComputeLocalSizeZ: + return localSizeZ; + case QueryInfoName.ComputeSharedMemorySize: + return sharedMemorySize; + } - const TranslationFlags flags = - TranslationFlags.Compute | - TranslationFlags.DebugMode | - TranslationFlags.Unspecialized; + return QueryInfoCommon(info); + }; + + ShaderProgram program; Span code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); - program = Translator.Translate(code, GetShaderCapabilities(), flags); + program = Translator.Translate(code, queryInfo, DefaultFlags | TranslationFlags.Compute); int[] codeCached = MemoryMarshal.Cast(code.Slice(0, program.Size)).ToArray(); - program.Replace(DefineNames.SharedMemorySize, (sharedMemorySize / 4).ToString(CultureInfo.InvariantCulture)); - - program.Replace(DefineNames.LocalSizeX, localSizeX.ToString(CultureInfo.InvariantCulture)); - program.Replace(DefineNames.LocalSizeY, localSizeY.ToString(CultureInfo.InvariantCulture)); - program.Replace(DefineNames.LocalSizeZ, localSizeZ.ToString(CultureInfo.InvariantCulture)); - _dumper.Dump(code, compute: true, out string fullPath, out string codePath); if (fullPath != null && codePath != null) @@ -229,13 +228,30 @@ namespace Ryujinx.Graphics.Gpu.Shader return new CachedShader(program, codeCached); } - private CachedShader TranslateGraphicsShader(TranslationFlags flags, ulong gpuVa, ulong gpuVaA = 0) + private CachedShader TranslateGraphicsShader(GpuState state, ShaderStage stage, ulong gpuVa, ulong gpuVaA = 0) { if (gpuVa == 0) { return new CachedShader(null, null); } + QueryInfoCallback queryInfo = (QueryInfoName info, int index) => + { + switch (info) + { + case QueryInfoName.IsTextureBuffer: + return Convert.ToInt32(QueryIsTextureBuffer(state, (int)stage - 1, index)); + case QueryInfoName.IsTextureRectangle: + return Convert.ToInt32(QueryIsTextureRectangle(state, (int)stage - 1, index)); + case QueryInfoName.PrimitiveTopology: + return (int)GetPrimitiveTopology(); + case QueryInfoName.ViewportTransformEnable: + return Convert.ToInt32(_context.Methods.GetViewportTransformEnable(state)); + } + + return QueryInfoCommon(info); + }; + ShaderProgram program; int[] codeCached = null; @@ -245,9 +261,9 @@ namespace Ryujinx.Graphics.Gpu.Shader Span codeA = _context.MemoryAccessor.Read(gpuVaA, MaxProgramSize); Span codeB = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); - program = Translator.Translate(codeA, codeB, GetShaderCapabilities(), flags); + program = Translator.Translate(codeA, codeB, queryInfo, DefaultFlags); - // TODO: We should also check "codeA" into account. + // TODO: We should also take "codeA" into account. codeCached = MemoryMarshal.Cast(codeB.Slice(0, program.Size)).ToArray(); _dumper.Dump(codeA, compute: false, out string fullPathA, out string codePathA); @@ -265,7 +281,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { Span code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); - program = Translator.Translate(code, GetShaderCapabilities(), flags); + program = Translator.Translate(code, queryInfo, DefaultFlags); codeCached = MemoryMarshal.Cast(code.Slice(0, program.Size)).ToArray(); @@ -278,40 +294,6 @@ namespace Ryujinx.Graphics.Gpu.Shader } } - if (program.Stage == ShaderStage.Geometry) - { - PrimitiveType primitiveType = _context.Methods.PrimitiveType; - - string inPrimitive = "points"; - - switch (primitiveType) - { - case PrimitiveType.Points: - inPrimitive = "points"; - break; - case PrimitiveType.Lines: - case PrimitiveType.LineLoop: - case PrimitiveType.LineStrip: - inPrimitive = "lines"; - break; - case PrimitiveType.LinesAdjacency: - case PrimitiveType.LineStripAdjacency: - inPrimitive = "lines_adjacency"; - break; - case PrimitiveType.Triangles: - case PrimitiveType.TriangleStrip: - case PrimitiveType.TriangleFan: - inPrimitive = "triangles"; - break; - case PrimitiveType.TrianglesAdjacency: - case PrimitiveType.TriangleStripAdjacency: - inPrimitive = "triangles_adjacency"; - break; - } - - program.Replace(DefineNames.InputTopologyName, inPrimitive); - } - ulong address = _context.MemoryManager.Translate(gpuVa); return new CachedShader(program, codeCached); @@ -350,13 +332,66 @@ namespace Ryujinx.Graphics.Gpu.Shader } } - private ShaderCapabilities GetShaderCapabilities() + private InputTopology GetPrimitiveTopology() { - return new ShaderCapabilities( - _context.Capabilities.MaximumViewportDimensions, - _context.Capabilities.MaximumComputeSharedMemorySize, - _context.Capabilities.StorageBufferOffsetAlignment, - _context.Capabilities.SupportsNonConstantTextureOffset); + switch (_context.Methods.PrimitiveType) + { + case PrimitiveType.Points: + return InputTopology.Points; + case PrimitiveType.Lines: + case PrimitiveType.LineLoop: + case PrimitiveType.LineStrip: + return InputTopology.Lines; + case PrimitiveType.LinesAdjacency: + case PrimitiveType.LineStripAdjacency: + return InputTopology.LinesAdjacency; + case PrimitiveType.Triangles: + case PrimitiveType.TriangleStrip: + case PrimitiveType.TriangleFan: + return InputTopology.Triangles; + case PrimitiveType.TrianglesAdjacency: + case PrimitiveType.TriangleStripAdjacency: + return InputTopology.TrianglesAdjacency; + } + + return InputTopology.Points; + } + + private bool QueryIsTextureBuffer(GpuState state, int stageIndex, int index) + { + return GetTextureDescriptor(state, stageIndex, index).UnpackTextureTarget() == TextureTarget.TextureBuffer; + } + + private bool QueryIsTextureRectangle(GpuState state, int stageIndex, int index) + { + var descriptor = GetTextureDescriptor(state, stageIndex, index); + + TextureTarget target = descriptor.UnpackTextureTarget(); + + bool is2DTexture = target == TextureTarget.Texture2D || + target == TextureTarget.Texture2DRect; + + return !descriptor.UnpackTextureCoordNormalized() && is2DTexture; + } + + private Image.TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int index) + { + return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(state, stageIndex, index); + } + + private int QueryInfoCommon(QueryInfoName info) + { + switch (info) + { + case QueryInfoName.MaximumViewportDimensions: + return _context.Capabilities.MaximumViewportDimensions; + case QueryInfoName.StorageBufferOffsetAlignment: + return _context.Capabilities.StorageBufferOffsetAlignment; + case QueryInfoName.SupportsNonConstantTextureOffset: + return Convert.ToInt32(_context.Capabilities.SupportsNonConstantTextureOffset); + } + + return 0; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index b96aa1aebd..bad5c00c7c 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -35,23 +35,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.Stage == ShaderStage.Geometry) { - string inPrimitive = "points"; - - if ((context.Config.Flags & TranslationFlags.Unspecialized) != 0) - { - inPrimitive = DefineNames.InputTopologyName; - } + string inPrimitive = ((InputTopology)context.Config.QueryInfo(QueryInfoName.PrimitiveTopology)).ToGlslString(); context.AppendLine($"layout ({inPrimitive}) in;"); - string outPrimitive = "triangle_strip"; - - switch (context.Config.OutputTopology) - { - case OutputTopology.LineStrip: outPrimitive = "line_strip"; break; - case OutputTopology.PointList: outPrimitive = "points"; break; - case OutputTopology.TriangleStrip: outPrimitive = "triangle_strip"; break; - } + string outPrimitive = context.Config.OutputTopology.ToGlslString(); int maxOutputVertices = context.Config.MaxOutputVertices; @@ -75,16 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.Stage == ShaderStage.Compute) { - string size; - - if ((context.Config.Flags & TranslationFlags.Unspecialized) != 0) - { - size = DefineNames.SharedMemorySize; - } - else - { - size = NumberFormatter.FormatInt(context.Config.Capabilities.MaximumComputeSharedMemorySize / 4); - } + string size = NumberFormatter.FormatInt(BitUtils.DivRoundUp(context.Config.QueryInfo(QueryInfoName.ComputeSharedMemorySize), 4)); context.AppendLine($"shared uint {DefaultNames.SharedMemoryName}[{size}];"); context.AppendLine(); @@ -136,19 +115,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { - string localSizeX = "1"; - string localSizeY = "1"; - string localSizeZ = "1"; - - if ((context.Config.Flags & TranslationFlags.Unspecialized) != 0) - { - localSizeX = DefineNames.LocalSizeX; - localSizeY = DefineNames.LocalSizeY; - localSizeZ = DefineNames.LocalSizeZ; - } + string localSizeX = NumberFormatter.FormatInt(context.Config.QueryInfo(QueryInfoName.ComputeLocalSizeX)); + string localSizeY = NumberFormatter.FormatInt(context.Config.QueryInfo(QueryInfoName.ComputeLocalSizeY)); + string localSizeZ = NumberFormatter.FormatInt(context.Config.QueryInfo(QueryInfoName.ComputeLocalSizeZ)); context.AppendLine( - $"layout (" + + "layout (" + $"local_size_x = {localSizeX}, " + $"local_size_y = {localSizeY}, " + $"local_size_z = {localSizeZ}) in;"); diff --git a/Ryujinx.Graphics.Shader/DefineNames.cs b/Ryujinx.Graphics.Shader/DefineNames.cs index 67e8e1ee3c..b043049919 100644 --- a/Ryujinx.Graphics.Shader/DefineNames.cs +++ b/Ryujinx.Graphics.Shader/DefineNames.cs @@ -2,14 +2,6 @@ namespace Ryujinx.Graphics.Shader { public static class DefineNames { - public const string InputTopologyName = "S_INPUT_TOPOLOGY"; - public const string OutQualifierPrefixName = "S_OUT_QUALIFIER"; - - public const string SharedMemorySize = "S_SHARED_MEMORY_SIZE"; - - public const string LocalSizeX = "S_LOCAL_SIZE_X"; - public const string LocalSizeY = "S_LOCAL_SIZE_Y"; - public const string LocalSizeZ = "S_LOCAL_SIZE_Z"; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/InputTopology.cs b/Ryujinx.Graphics.Shader/InputTopology.cs new file mode 100644 index 0000000000..3b0dda45f1 --- /dev/null +++ b/Ryujinx.Graphics.Shader/InputTopology.cs @@ -0,0 +1,28 @@ +namespace Ryujinx.Graphics.Shader +{ + public enum InputTopology + { + Points, + Lines, + LinesAdjacency, + Triangles, + TrianglesAdjacency + } + + static class InputTopologyExtensions + { + public static string ToGlslString(this InputTopology topology) + { + switch (topology) + { + case InputTopology.Points: return "points"; + case InputTopology.Lines: return "lines"; + case InputTopology.LinesAdjacency: return "lines_adjacency"; + case InputTopology.Triangles: return "triangles"; + case InputTopology.TrianglesAdjacency: return "triangles_adjacency"; + } + + return "points"; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/OutputTopology.cs b/Ryujinx.Graphics.Shader/OutputTopology.cs index e8336aa3f3..6f977becb4 100644 --- a/Ryujinx.Graphics.Shader/OutputTopology.cs +++ b/Ryujinx.Graphics.Shader/OutputTopology.cs @@ -6,4 +6,19 @@ namespace Ryujinx.Graphics.Shader LineStrip = 6, TriangleStrip = 7 } + + static class OutputTopologyExtensions + { + public static string ToGlslString(this OutputTopology topology) + { + switch (topology) + { + case OutputTopology.LineStrip: return "line_strip"; + case OutputTopology.PointList: return "points"; + case OutputTopology.TriangleStrip: return "triangle_strip"; + } + + return "points"; + } + } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/QueryInfoCallback.cs b/Ryujinx.Graphics.Shader/QueryInfoCallback.cs new file mode 100644 index 0000000000..28261a77e2 --- /dev/null +++ b/Ryujinx.Graphics.Shader/QueryInfoCallback.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.Shader +{ + public delegate int QueryInfoCallback(QueryInfoName info, int index); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/QueryInfoName.cs b/Ryujinx.Graphics.Shader/QueryInfoName.cs new file mode 100644 index 0000000000..e976dcdbdb --- /dev/null +++ b/Ryujinx.Graphics.Shader/QueryInfoName.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.Shader +{ + public enum QueryInfoName + { + ComputeLocalSizeX, + ComputeLocalSizeY, + ComputeLocalSizeZ, + ComputeSharedMemorySize, + IsTextureBuffer, + IsTextureRectangle, + MaximumViewportDimensions, + PrimitiveTopology, + StorageBufferOffsetAlignment, + SupportsNonConstantTextureOffset, + ViewportTransformEnable + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/ShaderCapabilities.cs b/Ryujinx.Graphics.Shader/ShaderCapabilities.cs deleted file mode 100644 index 809481b50d..0000000000 --- a/Ryujinx.Graphics.Shader/ShaderCapabilities.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Ryujinx.Graphics.Shader -{ - public struct ShaderCapabilities - { - // Initialize with default values for Maxwell. - private static readonly ShaderCapabilities _default = new ShaderCapabilities(0x8000, 0xc000, 16, true); - - public static ShaderCapabilities Default => _default; - - public int MaximumViewportDimensions { get; } - public int MaximumComputeSharedMemorySize { get; } - public int StorageBufferOffsetAlignment { get; } - public bool SupportsNonConstantTextureOffset { get; } - - public ShaderCapabilities( - int maximumViewportDimensions, - int maximumComputeSharedMemorySize, - int storageBufferOffsetAlignment, - bool supportsNonConstantTextureOffset) - { - MaximumViewportDimensions = maximumViewportDimensions; - MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; - StorageBufferOffsetAlignment = storageBufferOffsetAlignment; - SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/ShaderConfig.cs b/Ryujinx.Graphics.Shader/ShaderConfig.cs index 3088cfbba6..6b3640dfcf 100644 --- a/Ryujinx.Graphics.Shader/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/ShaderConfig.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.Translation; +using System; namespace Ryujinx.Graphics.Shader { @@ -6,26 +7,100 @@ namespace Ryujinx.Graphics.Shader { public ShaderStage Stage { get; } - public ShaderCapabilities Capabilities { get; } - - public TranslationFlags Flags { get; } + public OutputTopology OutputTopology { get; } public int MaxOutputVertices { get; } - public OutputTopology OutputTopology { get; } + public OutputMapTarget[] OmapTargets { get; } + public bool OmapSampleMask { get; } + public bool OmapDepth { get; } - public ShaderConfig( - ShaderStage stage, - ShaderCapabilities capabilities, - TranslationFlags flags, - int maxOutputVertices, - OutputTopology outputTopology) + public TranslationFlags Flags { get; } + + private QueryInfoCallback _queryInfoCallback; + + public ShaderConfig(TranslationFlags flags, QueryInfoCallback queryInfoCallback) { - Stage = stage; - Capabilities = capabilities; - Flags = flags; - MaxOutputVertices = maxOutputVertices; - OutputTopology = outputTopology; + Stage = ShaderStage.Compute; + OutputTopology = OutputTopology.PointList; + MaxOutputVertices = 0; + OmapTargets = null; + OmapSampleMask = false; + OmapDepth = false; + Flags = flags; + _queryInfoCallback = queryInfoCallback; + } + + public ShaderConfig(ShaderHeader header, TranslationFlags flags, QueryInfoCallback queryInfoCallback) + { + Stage = header.Stage; + OutputTopology = header.OutputTopology; + MaxOutputVertices = header.MaxOutputVertexCount; + OmapTargets = header.OmapTargets; + OmapSampleMask = header.OmapSampleMask; + OmapDepth = header.OmapDepth; + Flags = flags; + _queryInfoCallback = queryInfoCallback; + } + + public int GetDepthRegister() + { + int count = 0; + + for (int index = 0; index < OmapTargets.Length; index++) + { + for (int component = 0; component < 4; component++) + { + if (OmapTargets[index].ComponentEnabled(component)) + { + count++; + } + } + } + + // The depth register is always two registers after the last color output. + return count + 1; + } + + public bool QueryInfoBool(QueryInfoName info, int index = 0) + { + return Convert.ToBoolean(QueryInfo(info, index)); + } + + public int QueryInfo(QueryInfoName info, int index = 0) + { + if (_queryInfoCallback != null) + { + return _queryInfoCallback(info, index); + } + else + { + switch (info) + { + case QueryInfoName.ComputeLocalSizeX: + case QueryInfoName.ComputeLocalSizeY: + case QueryInfoName.ComputeLocalSizeZ: + return 1; + case QueryInfoName.ComputeSharedMemorySize: + return 0xc000; + case QueryInfoName.IsTextureBuffer: + return Convert.ToInt32(false); + case QueryInfoName.IsTextureRectangle: + return Convert.ToInt32(false); + case QueryInfoName.MaximumViewportDimensions: + return 0x8000; + case QueryInfoName.PrimitiveTopology: + return (int)InputTopology.Points; + case QueryInfoName.StorageBufferOffsetAlignment: + return 16; + case QueryInfoName.SupportsNonConstantTextureOffset: + return Convert.ToInt32(true); + case QueryInfoName.ViewportTransformEnable: + return Convert.ToInt32(true); + } + } + + return 0; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/ShaderHeader.cs b/Ryujinx.Graphics.Shader/ShaderHeader.cs index 94c5743595..a8d01086aa 100644 --- a/Ryujinx.Graphics.Shader/ShaderHeader.cs +++ b/Ryujinx.Graphics.Shader/ShaderHeader.cs @@ -76,28 +76,6 @@ namespace Ryujinx.Graphics.Shader public bool OmapSampleMask { get; } public bool OmapDepth { get; } - public int DepthRegister - { - get - { - int count = 0; - - for (int index = 0; index < OmapTargets.Length; index++) - { - for (int component = 0; component < 4; component++) - { - if (OmapTargets[index].ComponentEnabled(component)) - { - count++; - } - } - } - - // Depth register is always two registers after the last color output. - return count + 1; - } - } - public ShaderHeader(Span code) { Span header = MemoryMarshal.Cast(code); diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 7ba7b697bc..9620145aff 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -11,25 +11,15 @@ namespace Ryujinx.Graphics.Shader.Translation public Block CurrBlock { get; set; } public OpCode CurrOp { get; set; } - private ShaderStage _stage; - private ShaderHeader _header; - private ShaderCapabilities _capabilities; - private TranslationFlags _flags; + private ShaderConfig _config; private List _operations; private Dictionary _labels; - public EmitterContext( - ShaderStage stage, - ShaderHeader header, - ShaderCapabilities capabilities, - TranslationFlags flags) + public EmitterContext(ShaderConfig config) { - _stage = stage; - _header = header; - _capabilities = capabilities; - _flags = flags; + _config = config; _operations = new List(); @@ -69,24 +59,24 @@ namespace Ryujinx.Graphics.Shader.Translation public void PrepareForReturn() { - if (_stage == ShaderStage.Vertex) + if (_config.Stage == ShaderStage.Vertex) { - if ((_flags & TranslationFlags.DividePosXY) != 0) + if (!_config.QueryInfoBool(QueryInfoName.ViewportTransformEnable)) { Operand posX = Attribute(AttributeConsts.PositionX); Operand posY = Attribute(AttributeConsts.PositionY); - this.Copy(posX, this.FPDivide(posX, ConstF(_capabilities.MaximumViewportDimensions / 2))); - this.Copy(posY, this.FPDivide(posY, ConstF(_capabilities.MaximumViewportDimensions / 2))); + this.Copy(posX, this.FPDivide(posX, ConstF(_config.QueryInfo(QueryInfoName.MaximumViewportDimensions) / 2))); + this.Copy(posY, this.FPDivide(posY, ConstF(_config.QueryInfo(QueryInfoName.MaximumViewportDimensions) / 2))); } } - else if (_stage == ShaderStage.Fragment) + else if (_config.Stage == ShaderStage.Fragment) { - if (_header.OmapDepth) + if (_config.OmapDepth) { Operand dest = Attribute(AttributeConsts.FragmentOutputDepth); - Operand src = Register(_header.DepthRegister, RegisterType.Gpr); + Operand src = Register(_config.GetDepthRegister(), RegisterType.Gpr); this.Copy(dest, src); } @@ -95,7 +85,7 @@ namespace Ryujinx.Graphics.Shader.Translation for (int attachment = 0; attachment < 8; attachment++) { - OutputMapTarget target = _header.OmapTargets[attachment]; + OutputMapTarget target = _config.OmapTargets[attachment]; for (int component = 0; component < 4; component++) { diff --git a/Ryujinx.Graphics.Shader/Translation/Lowering.cs b/Ryujinx.Graphics.Shader/Translation/Lowering.cs index 46884bc983..1ee21e0a04 100644 --- a/Ryujinx.Graphics.Shader/Translation/Lowering.cs +++ b/Ryujinx.Graphics.Shader/Translation/Lowering.cs @@ -27,9 +27,9 @@ namespace Ryujinx.Graphics.Shader.Translation node = RewriteGlobalAccess(node, config); } - if (!config.Capabilities.SupportsNonConstantTextureOffset && operation.Inst == Instruction.TextureSample) + if (operation.Inst == Instruction.TextureSample) { - node = RewriteTextureSample(node); + node = RewriteTextureSample(node, config); } } } @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation sbSlot = PrependOperation(Instruction.ConditionalSelect, inRange, Const(slot), sbSlot); } - Operand alignMask = Const(-config.Capabilities.StorageBufferOffsetAlignment); + Operand alignMask = Const(-config.QueryInfo(QueryInfoName.StorageBufferOffsetAlignment)); Operand baseAddrTrunc = PrependOperation(Instruction.BitwiseAnd, sbBaseAddrLow, Const(-64)); Operand byteOffset = PrependOperation(Instruction.Subtract, addrLow, baseAddrTrunc); @@ -124,23 +124,18 @@ namespace Ryujinx.Graphics.Shader.Translation return node; } - private static LinkedListNode RewriteTextureSample(LinkedListNode node) + private static LinkedListNode RewriteTextureSample(LinkedListNode node, ShaderConfig config) { - // Technically, non-constant texture offsets are not allowed (according to the spec), - // however some GPUs does support that. - // For GPUs where it is not supported, we can replace the instruction with the following: - // For texture*Offset, we replace it by texture*, and add the offset to the P coords. - // The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords). - // For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly. - // For textureGatherOffset, we take advantage of the fact that the operation is already broken down - // to read the 4 pixels separately, and just replace it with 4 textureGather with a different offset - // for each pixel. TextureOperation texOp = (TextureOperation)node.Value; bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; - if (!(hasOffset || hasOffsets)) + bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.QueryInfoBool(QueryInfoName.SupportsNonConstantTextureOffset); + + bool isRect = config.QueryInfoBool(QueryInfoName.IsTextureRectangle, texOp.Handle); + + if (!(hasInvalidOffset || isRect)) { return node; } @@ -159,14 +154,24 @@ namespace Ryujinx.Graphics.Shader.Translation int coordsCount = texOp.Type.GetDimensions(); - int offsetsCount = coordsCount * (hasOffsets ? 4 : 1); + int offsetsCount; + + if (hasOffsets) + { + offsetsCount = coordsCount * 4; + } + else if (hasOffset) + { + offsetsCount = coordsCount; + } + else + { + offsetsCount = 0; + } Operand[] offsets = new Operand[offsetsCount]; Operand[] sources = new Operand[texOp.SourcesCount - offsetsCount]; - int srcIndex = 0; - int dstIndex = 0; - int copyCount = 0; if (isBindless || isIndexed) @@ -207,6 +212,9 @@ namespace Ryujinx.Graphics.Shader.Translation copyCount++; } + int srcIndex = 0; + int dstIndex = 0; + for (int index = 0; index < copyCount; index++) { sources[dstIndex++] = texOp.GetSource(srcIndex++); @@ -223,7 +231,9 @@ namespace Ryujinx.Graphics.Shader.Translation offsets[index] = offset; } - if (areAllOffsetsConstant) + hasInvalidOffset &= !areAllOffsetsConstant; + + if (!(hasInvalidOffset || isRect)) { return node; } @@ -240,50 +250,32 @@ namespace Ryujinx.Graphics.Shader.Translation int coordsIndex = isBindless || isIndexed ? 1 : 0; - if (intCoords) + int componentIndex = texOp.Index; + + Operand Int(Operand value) { - for (int index = 0; index < coordsCount; index++) - { - Operand source = sources[coordsIndex + index]; + Operand res = Local(); - Operand coordPlusOffset = Local(); + node.List.AddBefore(node, new Operation(Instruction.ConvertFPToS32, res, value)); - node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); - - sources[coordsIndex + index] = coordPlusOffset; - } + return res; } - else + + Operand Float(Operand value) { - Operand lod = Local(); + Operand res = Local(); - node.List.AddBefore(node, new TextureOperation( - Instruction.Lod, - texOp.Type, - texOp.Flags, - texOp.Handle, - 1, - lod, - lodSources)); + node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP, res, value)); - Operand Int(Operand value) - { - Operand res = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ConvertFPToS32, res, value)); - - return res; - } - - Operand Float(Operand value) - { - Operand res = Local(); - - node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP, res, value)); - - return res; - } + return res; + } + // Emulate texture rectangle by normalizing the coordinates on the shader. + // When sampler*Rect is used, the coords are expected to the in the [0, W or H] range, + // and otherwise, it is expected to be in the [0, 1] range. + // We normalize by dividing the coords by the texture size. + if (isRect && !intCoords) + { for (int index = 0; index < coordsCount; index++) { Operand coordSize = Local(); @@ -292,11 +284,11 @@ namespace Ryujinx.Graphics.Shader.Translation if (isBindless || isIndexed) { - texSizeSources = new Operand[] { sources[0], Int(lod) }; + texSizeSources = new Operand[] { sources[0], Const(0) }; } else { - texSizeSources = new Operand[] { Int(lod) }; + texSizeSources = new Operand[] { Const(0) }; } node.List.AddBefore(node, new TextureOperation( @@ -308,35 +300,101 @@ namespace Ryujinx.Graphics.Shader.Translation coordSize, texSizeSources)); - Operand offset = Local(); - - Operand intOffset = offsets[index + (hasOffsets ? texOp.Index * coordsCount : 0)]; - - node.List.AddBefore(node, new Operation(Instruction.FP | Instruction.Divide, offset, Float(intOffset), Float(coordSize))); - Operand source = sources[coordsIndex + index]; - Operand coordPlusOffset = Local(); + Operand coordNormalized = Local(); - node.List.AddBefore(node, new Operation(Instruction.FP | Instruction.Add, coordPlusOffset, source, offset)); + node.List.AddBefore(node, new Operation(Instruction.FP | Instruction.Divide, coordNormalized, source, Float(coordSize))); - sources[coordsIndex + index] = coordPlusOffset; + sources[coordsIndex + index] = coordNormalized; } } - int componentIndex; - - if (isGather && !isShadow) + // Technically, non-constant texture offsets are not allowed (according to the spec), + // however some GPUs does support that. + // For GPUs where it is not supported, we can replace the instruction with the following: + // For texture*Offset, we replace it by texture*, and add the offset to the P coords. + // The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords). + // For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly. + // For textureGatherOffset, we take advantage of the fact that the operation is already broken down + // to read the 4 pixels separately, and just replace it with 4 textureGather with a different offset + // for each pixel. + if (hasInvalidOffset) { - Operand gatherComponent = sources[dstIndex - 1]; + if (intCoords) + { + for (int index = 0; index < coordsCount; index++) + { + Operand source = sources[coordsIndex + index]; - Debug.Assert(gatherComponent.Type == OperandType.Constant); + Operand coordPlusOffset = Local(); - componentIndex = gatherComponent.Value; - } - else - { - componentIndex = texOp.Index; + node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index])); + + sources[coordsIndex + index] = coordPlusOffset; + } + } + else + { + Operand lod = Local(); + + node.List.AddBefore(node, new TextureOperation( + Instruction.Lod, + texOp.Type, + texOp.Flags, + texOp.Handle, + 1, + lod, + lodSources)); + + for (int index = 0; index < coordsCount; index++) + { + Operand coordSize = Local(); + + Operand[] texSizeSources; + + if (isBindless || isIndexed) + { + texSizeSources = new Operand[] { sources[0], Int(lod) }; + } + else + { + texSizeSources = new Operand[] { Int(lod) }; + } + + node.List.AddBefore(node, new TextureOperation( + Instruction.TextureSize, + texOp.Type, + texOp.Flags, + texOp.Handle, + index, + coordSize, + texSizeSources)); + + Operand offset = Local(); + + Operand intOffset = offsets[index + (hasOffsets ? texOp.Index * coordsCount : 0)]; + + node.List.AddBefore(node, new Operation(Instruction.FP | Instruction.Divide, offset, Float(intOffset), Float(coordSize))); + + Operand source = sources[coordsIndex + index]; + + Operand coordPlusOffset = Local(); + + node.List.AddBefore(node, new Operation(Instruction.FP | Instruction.Add, coordPlusOffset, source, offset)); + + sources[coordsIndex + index] = coordPlusOffset; + } + } + + if (isGather && !isShadow) + { + Operand gatherComponent = sources[dstIndex - 1]; + + Debug.Assert(gatherComponent.Type == OperandType.Constant); + + componentIndex = gatherComponent.Value; + } } TextureOperation newTexOp = new TextureOperation( diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 639f9ba4b4..59261fbf06 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand baseAddrTrunc = Local(); - Operand alignMask = Const(-config.Capabilities.StorageBufferOffsetAlignment); + Operand alignMask = Const(-config.QueryInfo(QueryInfoName.StorageBufferOffsetAlignment)); Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); diff --git a/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs b/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs index 8faa43836b..1327abaed4 100644 --- a/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs +++ b/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs @@ -4,9 +4,7 @@ namespace Ryujinx.Graphics.Shader.Translation { None = 0, - Compute = 1 << 0, - DebugMode = 1 << 1, - Unspecialized = 1 << 2, - DividePosXY = 1 << 3 + Compute = 1 << 0, + DebugMode = 1 << 1 } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 69e63ae135..af209edf1d 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -16,14 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation public static Span ExtractCode(Span code, bool compute, out int headerSize) { - if (compute) - { - headerSize = 0; - } - else - { - headerSize = HeaderSize; - } + headerSize = compute ? 0 : HeaderSize; Block[] cfg = Decoder.Decode(code, (ulong)headerSize); @@ -47,56 +40,21 @@ namespace Ryujinx.Graphics.Shader.Translation return code.Slice(0, headerSize + (int)endAddress); } - public static ShaderProgram Translate(Span code, ShaderCapabilities capabilities, TranslationFlags flags) + public static ShaderProgram Translate(Span code, QueryInfoCallback queryInfoCallback, TranslationFlags flags) { bool compute = (flags & TranslationFlags.Compute) != 0; - Operation[] ops = DecodeShader(code, capabilities, flags, out ShaderHeader header, out int size); - - ShaderStage stage; - - if (compute) - { - stage = ShaderStage.Compute; - } - else - { - stage = header.Stage; - } - - int maxOutputVertexCount = 0; - - OutputTopology outputTopology = OutputTopology.LineStrip; - - if (!compute) - { - maxOutputVertexCount = header.MaxOutputVertexCount; - outputTopology = header.OutputTopology; - } - - ShaderConfig config = new ShaderConfig( - stage, - capabilities, - flags, - maxOutputVertexCount, - outputTopology); + Operation[] ops = DecodeShader(code, queryInfoCallback, flags, out ShaderConfig config, out int size); return Translate(ops, config, size); } - public static ShaderProgram Translate(Span vpACode, Span vpBCode, ShaderCapabilities capabilities, TranslationFlags flags) + public static ShaderProgram Translate(Span vpACode, Span vpBCode, QueryInfoCallback queryInfoCallback, TranslationFlags flags) { bool debugMode = (flags & TranslationFlags.DebugMode) != 0; - Operation[] vpAOps = DecodeShader(vpACode, capabilities, flags, out _, out _); - Operation[] vpBOps = DecodeShader(vpBCode, capabilities, flags, out ShaderHeader header, out int sizeB); - - ShaderConfig config = new ShaderConfig( - header.Stage, - capabilities, - flags, - header.MaxOutputVertexCount, - header.OutputTopology); + Operation[] vpAOps = DecodeShader(vpACode, queryInfoCallback, flags, out _, out _); + Operation[] vpBOps = DecodeShader(vpBCode, queryInfoCallback, flags, out ShaderConfig config, out int sizeB); return Translate(Combine(vpAOps, vpBOps), config, sizeB); } @@ -136,31 +94,25 @@ namespace Ryujinx.Graphics.Shader.Translation } private static Operation[] DecodeShader( - Span code, - ShaderCapabilities capabilities, - TranslationFlags flags, - out ShaderHeader header, - out int size) + Span code, + QueryInfoCallback queryInfoCallback, + TranslationFlags flags, + out ShaderConfig config, + out int size) { Block[] cfg; - EmitterContext context; - if ((flags & TranslationFlags.Compute) != 0) { - header = null; + config = new ShaderConfig(flags, queryInfoCallback); cfg = Decoder.Decode(code, 0); - - context = new EmitterContext(ShaderStage.Compute, header, capabilities, flags); } else { - header = new ShaderHeader(code); + config = new ShaderConfig(new ShaderHeader(code), flags, queryInfoCallback); cfg = Decoder.Decode(code, HeaderSize); - - context = new EmitterContext(header.Stage, header, capabilities, flags); } if (cfg == null) @@ -172,6 +124,8 @@ namespace Ryujinx.Graphics.Shader.Translation return new Operation[0]; } + EmitterContext context = new EmitterContext(config); + ulong maxEndAddress = 0; for (int blkIndex = 0; blkIndex < cfg.Length; blkIndex++) diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs index 275da794d3..445f6b46db 100644 --- a/Ryujinx.ShaderTools/Program.cs +++ b/Ryujinx.ShaderTools/Program.cs @@ -20,7 +20,7 @@ namespace Ryujinx.ShaderTools byte[] data = File.ReadAllBytes(args[args.Length - 1]); - string code = Translator.Translate(data, ShaderCapabilities.Default, flags).Code; + string code = Translator.Translate(data, null, flags).Code; Console.WriteLine(code); }