diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs index 588825d0..108cc197 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL.Texture; using Ryujinx.Graphics.Gpu.Image; +using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Shader; using System; @@ -25,7 +26,7 @@ namespace Ryujinx.Graphics.Gpu.Engine dispatchParams.UnpackBlockSizeY(), dispatchParams.UnpackBlockSizeZ()); - _context.Renderer.Pipeline.BindProgram(cs.Interface); + _context.Renderer.Pipeline.BindProgram(cs.HostProgram); var samplerPool = _context.State.Get(MethodOffset.SamplerPoolState); @@ -37,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Engine _textureManager.SetComputeTextureBufferIndex(_context.State.Get(MethodOffset.TextureBufferIndex)); - ShaderProgramInfo info = cs.Shader.Info; + ShaderProgramInfo info = cs.Shader.Program.Info; var textureBindings = new TextureBindingInfo[info.Textures.Count]; diff --git a/Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs b/Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs deleted file mode 100644 index cc7d4d99..00000000 --- a/Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; - -namespace Ryujinx.Graphics.Gpu.Engine -{ - class ComputeShader - { - public IProgram Interface { get; set; } - - public ShaderProgram Shader { get; } - - public ComputeShader(IProgram program, ShaderProgram shader) - { - Interface = program; - Shader = shader; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs b/Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs deleted file mode 100644 index a8ccc05a..00000000 --- a/Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; - -namespace Ryujinx.Graphics.Gpu.Engine -{ - class GraphicsShader - { - public IProgram Interface { get; set; } - - public ShaderProgram[] Shader { get; } - - public GraphicsShader() - { - Shader = new ShaderProgram[5]; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index b1326ec5..f48d0a7f 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.GAL.InputAssembler; using Ryujinx.Graphics.GAL.Texture; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Memory; +using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Shader; using System; @@ -609,11 +610,11 @@ namespace Ryujinx.Graphics.Gpu.Engine GraphicsShader gs = _shaderCache.GetGraphicsShader(addresses); - _vsUsesInstanceId = gs.Shader[0].Info.UsesInstanceId; + _vsUsesInstanceId = gs.Shader[0].Program.Info.UsesInstanceId; for (int stage = 0; stage < Constants.TotalShaderStages; stage++) { - ShaderProgramInfo info = gs.Shader[stage]?.Info; + ShaderProgramInfo info = gs.Shader[stage].Program?.Info; _currentProgramInfo[stage] = info; @@ -665,7 +666,7 @@ namespace Ryujinx.Graphics.Gpu.Engine _bufferManager.SetGraphicsUniformBufferEnableMask(stage, ubEnableMask); } - _context.Renderer.Pipeline.BindProgram(gs.Interface); + _context.Renderer.Pipeline.BindProgram(gs.HostProgram); } private static Target GetTarget(SamplerType type) diff --git a/Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs deleted file mode 100644 index 922f4a44..00000000 --- a/Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs +++ /dev/null @@ -1,249 +0,0 @@ -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Gpu.State; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Collections.Generic; -using System.Globalization; - -namespace Ryujinx.Graphics.Gpu.Engine -{ - class ShaderCache - { - private const int MaxProgramSize = 0x100000; - - private GpuContext _context; - - private ShaderDumper _dumper; - - private Dictionary _cpPrograms; - - private Dictionary _gpPrograms; - - public ShaderCache(GpuContext context) - { - _context = context; - - _dumper = new ShaderDumper(context); - - _cpPrograms = new Dictionary(); - - _gpPrograms = new Dictionary(); - } - - public ComputeShader GetComputeShader(ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ) - { - if (!_cpPrograms.TryGetValue(gpuVa, out ComputeShader cpShader)) - { - ShaderProgram shader = TranslateComputeShader(gpuVa); - - shader.Replace(DefineNames.LocalSizeX, localSizeX.ToString(CultureInfo.InvariantCulture)); - shader.Replace(DefineNames.LocalSizeY, localSizeY.ToString(CultureInfo.InvariantCulture)); - shader.Replace(DefineNames.LocalSizeZ, localSizeZ.ToString(CultureInfo.InvariantCulture)); - - IShader hostShader = _context.Renderer.CompileShader(shader); - - IProgram program = _context.Renderer.CreateProgram(new IShader[] { hostShader }); - - cpShader = new ComputeShader(program, shader); - - _cpPrograms.Add(gpuVa, cpShader); - } - - return cpShader; - } - - public GraphicsShader GetGraphicsShader(ShaderAddresses addresses) - { - if (!_gpPrograms.TryGetValue(addresses, out GraphicsShader gpShader)) - { - gpShader = new GraphicsShader(); - - if (addresses.VertexA != 0) - { - gpShader.Shader[0] = TranslateGraphicsShader(addresses.Vertex, addresses.VertexA); - } - else - { - gpShader.Shader[0] = TranslateGraphicsShader(addresses.Vertex); - } - - gpShader.Shader[1] = TranslateGraphicsShader(addresses.TessControl); - gpShader.Shader[2] = TranslateGraphicsShader(addresses.TessEvaluation); - gpShader.Shader[3] = TranslateGraphicsShader(addresses.Geometry); - gpShader.Shader[4] = TranslateGraphicsShader(addresses.Fragment); - - BackpropQualifiers(gpShader); - - List shaders = new List(); - - for (int stage = 0; stage < gpShader.Shader.Length; stage++) - { - if (gpShader.Shader[stage] == null) - { - continue; - } - - IShader shader = _context.Renderer.CompileShader(gpShader.Shader[stage]); - - shaders.Add(shader); - } - - gpShader.Interface = _context.Renderer.CreateProgram(shaders.ToArray()); - - _gpPrograms.Add(addresses, gpShader); - } - - return gpShader; - } - - private ShaderProgram TranslateComputeShader(ulong gpuVa) - { - if (gpuVa == 0) - { - return null; - } - - ShaderProgram program; - - const TranslationFlags flags = - TranslationFlags.Compute | - TranslationFlags.DebugMode | - TranslationFlags.Unspecialized; - - TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags); - - Span code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); - - program = Translator.Translate(code, translationConfig); - - _dumper.Dump(code, compute : true, out string fullPath, out string codePath); - - if (fullPath != null && codePath != null) - { - program.Prepend("// " + codePath); - program.Prepend("// " + fullPath); - } - - return program; - } - - private ShaderProgram TranslateGraphicsShader(ulong gpuVa, ulong gpuVaA = 0) - { - if (gpuVa == 0) - { - return null; - } - - ShaderProgram program; - - const TranslationFlags flags = - TranslationFlags.DebugMode | - TranslationFlags.Unspecialized; - - TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags); - - if (gpuVaA != 0) - { - Span codeA = _context.MemoryAccessor.Read(gpuVaA, MaxProgramSize); - Span codeB = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); - - program = Translator.Translate(codeA, codeB, translationConfig); - - _dumper.Dump(codeA, compute: false, out string fullPathA, out string codePathA); - _dumper.Dump(codeB, compute: false, out string fullPathB, out string codePathB); - - if (fullPathA != null && fullPathB != null && codePathA != null && codePathB != null) - { - program.Prepend("// " + codePathB); - program.Prepend("// " + fullPathB); - program.Prepend("// " + codePathA); - program.Prepend("// " + fullPathA); - } - } - else - { - Span code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); - - program = Translator.Translate(code, translationConfig); - - _dumper.Dump(code, compute: false, out string fullPath, out string codePath); - - if (fullPath != null && codePath != null) - { - program.Prepend("// " + codePath); - program.Prepend("// " + fullPath); - } - } - - 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); - } - - return program; - } - - private void BackpropQualifiers(GraphicsShader program) - { - ShaderProgram fragmentShader = program.Shader[4]; - - bool isFirst = true; - - for (int stage = 3; stage >= 0; stage--) - { - if (program.Shader[stage] == null) - { - continue; - } - - // We need to iterate backwards, since we do name replacement, - // and it would otherwise replace a subset of the longer names. - for (int attr = 31; attr >= 0; attr--) - { - string iq = fragmentShader?.Info.InterpolationQualifiers[attr].ToGlslQualifier() ?? string.Empty; - - if (isFirst && iq != string.Empty) - { - program.Shader[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr}", iq); - } - else - { - program.Shader[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr} ", string.Empty); - } - } - - isFirst = false; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedShader.cs b/Ryujinx.Graphics.Gpu/Shader/CachedShader.cs new file mode 100644 index 00000000..210d0720 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/CachedShader.cs @@ -0,0 +1,19 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.Gpu.Shader +{ + class CachedShader + { + public ShaderProgram Program { get; } + public IShader Shader { get; set; } + + public int[] Code { get; } + + public CachedShader(ShaderProgram program, int[] code) + { + Program = program; + Code = code; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ComputeShader.cs b/Ryujinx.Graphics.Gpu/Shader/ComputeShader.cs new file mode 100644 index 00000000..908b04b9 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/ComputeShader.cs @@ -0,0 +1,17 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.Shader +{ + class ComputeShader + { + public IProgram HostProgram { get; set; } + + public CachedShader Shader { get; } + + public ComputeShader(IProgram hostProgram, CachedShader shader) + { + HostProgram = hostProgram; + Shader = shader; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/GraphicsShader.cs b/Ryujinx.Graphics.Gpu/Shader/GraphicsShader.cs new file mode 100644 index 00000000..7bdf68f7 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/GraphicsShader.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.Shader +{ + class GraphicsShader + { + public IProgram HostProgram { get; set; } + + public CachedShader[] Shader { get; } + + public GraphicsShader() + { + Shader = new CachedShader[5]; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs similarity index 96% rename from Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs rename to Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs index 368b5a17..c0a9162a 100644 --- a/Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderAddresses.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.Graphics.Gpu.Engine +namespace Ryujinx.Graphics.Gpu.Shader { struct ShaderAddresses : IEquatable { diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs new file mode 100644 index 00000000..3fdd28b9 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -0,0 +1,350 @@ +using Ryujinx.Graphics.GAL; +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 +{ + class ShaderCache + { + private const int MaxProgramSize = 0x100000; + + private GpuContext _context; + + private ShaderDumper _dumper; + + private Dictionary> _cpPrograms; + + private Dictionary> _gpPrograms; + + public ShaderCache(GpuContext context) + { + _context = context; + + _dumper = new ShaderDumper(context); + + _cpPrograms = new Dictionary>(); + + _gpPrograms = new Dictionary>(); + } + + public ComputeShader GetComputeShader(ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ) + { + bool isCached = _cpPrograms.TryGetValue(gpuVa, out List list); + + if (isCached) + { + foreach (ComputeShader cachedCpShader in list) + { + if (!IsShaderDifferent(cachedCpShader, gpuVa)) + { + return cachedCpShader; + } + } + } + + CachedShader shader = TranslateComputeShader(gpuVa, localSizeX, localSizeY, localSizeZ); + + IShader hostShader = _context.Renderer.CompileShader(shader.Program); + + IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { hostShader }); + + ulong address = _context.MemoryManager.Translate(gpuVa); + + ComputeShader cpShader = new ComputeShader(hostProgram, shader); + + if (!isCached) + { + list = new List(); + + _cpPrograms.Add(gpuVa, list); + } + + list.Add(cpShader); + + return cpShader; + } + + public GraphicsShader GetGraphicsShader(ShaderAddresses addresses) + { + bool isCached = _gpPrograms.TryGetValue(addresses, out List list); + + if (isCached) + { + foreach (GraphicsShader cachedGpShaders in list) + { + if (!IsShaderDifferent(cachedGpShaders, addresses)) + { + return cachedGpShaders; + } + } + } + + GraphicsShader gpShaders = new GraphicsShader(); + + if (addresses.VertexA != 0) + { + gpShaders.Shader[0] = TranslateGraphicsShader(addresses.Vertex, addresses.VertexA); + } + else + { + gpShaders.Shader[0] = TranslateGraphicsShader(addresses.Vertex); + } + + gpShaders.Shader[1] = TranslateGraphicsShader(addresses.TessControl); + gpShaders.Shader[2] = TranslateGraphicsShader(addresses.TessEvaluation); + gpShaders.Shader[3] = TranslateGraphicsShader(addresses.Geometry); + gpShaders.Shader[4] = TranslateGraphicsShader(addresses.Fragment); + + BackpropQualifiers(gpShaders); + + List hostShaders = new List(); + + for (int stage = 0; stage < gpShaders.Shader.Length; stage++) + { + ShaderProgram program = gpShaders.Shader[stage].Program; + + if (program == null) + { + continue; + } + + IShader hostShader = _context.Renderer.CompileShader(program); + + gpShaders.Shader[stage].Shader = hostShader; + + hostShaders.Add(hostShader); + } + + gpShaders.HostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray()); + + if (!isCached) + { + list = new List(); + + _gpPrograms.Add(addresses, list); + } + + list.Add(gpShaders); + + return gpShaders; + } + + private bool IsShaderDifferent(ComputeShader cpShader, ulong gpuVa) + { + return IsShaderDifferent(cpShader.Shader, gpuVa); + } + + private bool IsShaderDifferent(GraphicsShader gpShaders, ShaderAddresses addresses) + { + for (int stage = 0; stage < gpShaders.Shader.Length; stage++) + { + CachedShader shader = gpShaders.Shader[stage]; + + if (shader.Code == null) + { + continue; + } + + ulong gpuVa = 0; + + switch (stage) + { + case 0: gpuVa = addresses.Vertex; break; + case 1: gpuVa = addresses.TessControl; break; + case 2: gpuVa = addresses.TessEvaluation; break; + case 3: gpuVa = addresses.Geometry; break; + case 4: gpuVa = addresses.Fragment; break; + } + + if (IsShaderDifferent(shader, gpuVa)) + { + return true; + } + } + + return false; + } + + private bool IsShaderDifferent(CachedShader shader, ulong gpuVa) + { + for (int offset = 0; offset < shader.Code.Length; offset += 4) + { + if (_context.MemoryAccessor.ReadInt32(gpuVa + (ulong)offset) != shader.Code[offset / 4]) + { + return true; + } + } + + return false; + } + + private CachedShader TranslateComputeShader(ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ) + { + if (gpuVa == 0) + { + return null; + } + + ShaderProgram program; + + const TranslationFlags flags = + TranslationFlags.Compute | + TranslationFlags.DebugMode | + TranslationFlags.Unspecialized; + + TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags); + + Span code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); + + program = Translator.Translate(code, translationConfig); + + int[] codeCached = MemoryMarshal.Cast(code.Slice(0, program.Size)).ToArray(); + + 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) + { + program.Prepend("// " + codePath); + program.Prepend("// " + fullPath); + } + + return new CachedShader(program, codeCached); + } + + private CachedShader TranslateGraphicsShader(ulong gpuVa, ulong gpuVaA = 0) + { + if (gpuVa == 0) + { + return new CachedShader(null, null); + } + + ShaderProgram program; + + const TranslationFlags flags = + TranslationFlags.DebugMode | + TranslationFlags.Unspecialized; + + TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags); + + int[] codeCached = null; + + if (gpuVaA != 0) + { + Span codeA = _context.MemoryAccessor.Read(gpuVaA, MaxProgramSize); + Span codeB = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); + + program = Translator.Translate(codeA, codeB, translationConfig); + + // TODO: We should also check "codeA" into account. + codeCached = MemoryMarshal.Cast(codeB.Slice(0, program.Size)).ToArray(); + + _dumper.Dump(codeA, compute: false, out string fullPathA, out string codePathA); + _dumper.Dump(codeB, compute: false, out string fullPathB, out string codePathB); + + if (fullPathA != null && fullPathB != null && codePathA != null && codePathB != null) + { + program.Prepend("// " + codePathB); + program.Prepend("// " + fullPathB); + program.Prepend("// " + codePathA); + program.Prepend("// " + fullPathA); + } + } + else + { + Span code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); + + program = Translator.Translate(code, translationConfig); + + codeCached = MemoryMarshal.Cast(code.Slice(0, program.Size)).ToArray(); + + _dumper.Dump(code, compute: false, out string fullPath, out string codePath); + + if (fullPath != null && codePath != null) + { + program.Prepend("// " + codePath); + program.Prepend("// " + fullPath); + } + } + + 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); + } + + private void BackpropQualifiers(GraphicsShader program) + { + ShaderProgram fragmentShader = program.Shader[4].Program; + + bool isFirst = true; + + for (int stage = 3; stage >= 0; stage--) + { + if (program.Shader[stage].Program == null) + { + continue; + } + + // We need to iterate backwards, since we do name replacement, + // and it would otherwise replace a subset of the longer names. + for (int attr = 31; attr >= 0; attr--) + { + string iq = fragmentShader?.Info.InterpolationQualifiers[attr].ToGlslQualifier() ?? string.Empty; + + if (isFirst && iq != string.Empty) + { + program.Shader[stage].Program.Replace($"{DefineNames.OutQualifierPrefixName}{attr}", iq); + } + else + { + program.Shader[stage].Program.Replace($"{DefineNames.OutQualifierPrefixName}{attr} ", string.Empty); + } + } + + isFirst = false; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs similarity index 98% rename from Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs rename to Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs index b2eb0f33..04ad645b 100644 --- a/Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs @@ -2,7 +2,7 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.IO; -namespace Ryujinx.Graphics.Gpu.Engine +namespace Ryujinx.Graphics.Gpu.Shader { class ShaderDumper {