From b8eb6abeccbd4a468214a4d2ad3a9b6e5e06973c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 5 May 2020 22:02:28 -0300 Subject: Refactor shader GPU state and memory access (#1203) * Refactor shader GPU state and memory access * Fix NVDEC project build * Address PR feedback and add missing XML comments --- Ryujinx.Graphics.Gpu/Shader/CachedShader.cs | 37 --- Ryujinx.Graphics.Gpu/Shader/ComputeShader.cs | 31 -- Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs | 264 ++++++++++++++++ Ryujinx.Graphics.Gpu/Shader/GraphicsShader.cs | 28 -- Ryujinx.Graphics.Gpu/Shader/ShaderBundle.cs | 46 +++ Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 390 +++++------------------- Ryujinx.Graphics.Gpu/Shader/ShaderCodeHolder.cs | 44 +++ Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs | 53 ++-- 8 files changed, 456 insertions(+), 437 deletions(-) delete mode 100644 Ryujinx.Graphics.Gpu/Shader/CachedShader.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/ComputeShader.cs create mode 100644 Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/GraphicsShader.cs create mode 100644 Ryujinx.Graphics.Gpu/Shader/ShaderBundle.cs create mode 100644 Ryujinx.Graphics.Gpu/Shader/ShaderCodeHolder.cs (limited to 'Ryujinx.Graphics.Gpu/Shader') diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedShader.cs b/Ryujinx.Graphics.Gpu/Shader/CachedShader.cs deleted file mode 100644 index f8494045..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/CachedShader.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; - -namespace Ryujinx.Graphics.Gpu.Shader -{ - /// - /// Cached shader code for a single shader stage. - /// - class CachedShader - { - /// - /// Shader program containing translated code. - /// - public ShaderProgram Program { get; } - - /// - /// Host shader object. - /// - public IShader HostShader { get; set; } - - /// - /// Maxwell binary shader code. - /// - public int[] Code { get; } - - /// - /// Creates a new instace of the cached shader. - /// - /// Shader program - /// Maxwell binary shader code - 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 deleted file mode 100644 index fcc38d04..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/ComputeShader.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Ryujinx.Graphics.GAL; - -namespace Ryujinx.Graphics.Gpu.Shader -{ - /// - /// Cached compute shader code. - /// - class ComputeShader - { - /// - /// Host shader program object. - /// - public IProgram HostProgram { get; } - - /// - /// Cached shader. - /// - public CachedShader Shader { get; } - - /// - /// Creates a new instance of the compute shader. - /// - /// Host shader program - /// Cached shader - public ComputeShader(IProgram hostProgram, CachedShader shader) - { - HostProgram = hostProgram; - Shader = shader; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs new file mode 100644 index 00000000..7dc175e1 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -0,0 +1,264 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Image; +using Ryujinx.Graphics.Gpu.State; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.Gpu.Shader +{ + /// + /// Represents a GPU state and memory accessor. + /// + class GpuAccessor : IGpuAccessor + { + private readonly GpuContext _context; + private readonly GpuState _state; + private readonly int _stageIndex; + private readonly bool _compute; + private readonly int _localSizeX; + private readonly int _localSizeY; + private readonly int _localSizeZ; + private readonly int _localMemorySize; + private readonly int _sharedMemorySize; + + /// + /// Creates a new instance of the GPU state accessor for graphics shader translation. + /// + /// GPU context + /// Current GPU state + /// Graphics shader stage index (0 = Vertex, 4 = Fragment) + public GpuAccessor(GpuContext context, GpuState state, int stageIndex) + { + _context = context; + _state = state; + _stageIndex = stageIndex; + } + + /// + /// Creates a new instance of the GPU state accessor for compute shader translation. + /// + /// GPU context + /// Current GPU state + /// Local group size X of the compute shader + /// Local group size Y of the compute shader + /// Local group size Z of the compute shader + /// Local memory size of the compute shader + /// Shared memory size of the compute shader + public GpuAccessor( + GpuContext context, + GpuState state, + int localSizeX, + int localSizeY, + int localSizeZ, + int localMemorySize, + int sharedMemorySize) + { + _context = context; + _state = state; + _compute = true; + _localSizeX = localSizeX; + _localSizeY = localSizeY; + _localSizeZ = localSizeZ; + _localMemorySize = localMemorySize; + _sharedMemorySize = sharedMemorySize; + } + + /// + /// Prints a log message. + /// + /// Message to print + public void Log(string message) + { + Logger.PrintWarning(LogClass.Gpu, $"Shader translator: {message}"); + } + + /// + /// Reads data from GPU memory. + /// + /// Type of the data to be read + /// GPU virtual address of the data + /// Data at the memory location + public T MemoryRead(ulong address) where T : unmanaged + { + return _context.MemoryAccessor.Read(address); + } + + /// + /// Queries Local Size X for compute shaders. + /// + /// Local Size X + public int QueryComputeLocalSizeX() => _localSizeX; + + /// + /// Queries Local Size Y for compute shaders. + /// + /// Local Size Y + public int QueryComputeLocalSizeY() => _localSizeY; + + /// + /// Queries Local Size Z for compute shaders. + /// + /// Local Size Z + public int QueryComputeLocalSizeZ() => _localSizeZ; + + /// + /// Queries Local Memory size in bytes for compute shaders. + /// + /// Local Memory size in bytes + public int QueryComputeLocalMemorySize() => _localMemorySize; + + /// + /// Queries Shared Memory size in bytes for compute shaders. + /// + /// Shared Memory size in bytes + public int QueryComputeSharedMemorySize() => _sharedMemorySize; + + /// + /// Queries texture target information. + /// + /// Texture handle + /// True if the texture is a buffer texture, false otherwise + public bool QueryIsTextureBuffer(int handle) + { + return GetTextureDescriptor(handle).UnpackTextureTarget() == TextureTarget.TextureBuffer; + } + + /// + /// Queries texture target information. + /// + /// Texture handle + /// True if the texture is a rectangle texture, false otherwise + public bool QueryIsTextureRectangle(int handle) + { + var descriptor = GetTextureDescriptor(handle); + + TextureTarget target = descriptor.UnpackTextureTarget(); + + bool is2DTexture = target == TextureTarget.Texture2D || + target == TextureTarget.Texture2DRect; + + return !descriptor.UnpackTextureCoordNormalized() && is2DTexture; + } + + /// + /// Queries current primitive topology for geometry shaders. + /// + /// Current primitive topology + public InputTopology QueryPrimitiveTopology() + { + 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; + } + + /// + /// Queries host storage buffer alignment required. + /// + /// Host storage buffer alignment in bytes + public int QueryStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment; + + /// + /// Queries host GPU non-constant texture offset support. + /// + /// True if the GPU and driver supports non-constant texture offsets, false otherwise + public bool QuerySupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset; + + /// + /// Queries texture format information, for shaders using image load or store. + /// + /// + /// This only returns non-compressed color formats. + /// If the format of the texture is a compressed, depth or unsupported format, then a default value is returned. + /// + /// Texture handle + /// Color format of the non-compressed texture + public TextureFormat QueryTextureFormat(int handle) + { + var descriptor = GetTextureDescriptor(handle); + + if (!FormatTable.TryGetTextureFormat(descriptor.UnpackFormat(), descriptor.UnpackSrgb(), out FormatInfo formatInfo)) + { + return TextureFormat.Unknown; + } + + return formatInfo.Format switch + { + Format.R8Unorm => TextureFormat.R8Unorm, + Format.R8Snorm => TextureFormat.R8Snorm, + Format.R8Uint => TextureFormat.R8Uint, + Format.R8Sint => TextureFormat.R8Sint, + Format.R16Float => TextureFormat.R16Float, + Format.R16Unorm => TextureFormat.R16Unorm, + Format.R16Snorm => TextureFormat.R16Snorm, + Format.R16Uint => TextureFormat.R16Uint, + Format.R16Sint => TextureFormat.R16Sint, + Format.R32Float => TextureFormat.R32Float, + Format.R32Uint => TextureFormat.R32Uint, + Format.R32Sint => TextureFormat.R32Sint, + Format.R8G8Unorm => TextureFormat.R8G8Unorm, + Format.R8G8Snorm => TextureFormat.R8G8Snorm, + Format.R8G8Uint => TextureFormat.R8G8Uint, + Format.R8G8Sint => TextureFormat.R8G8Sint, + Format.R16G16Float => TextureFormat.R16G16Float, + Format.R16G16Unorm => TextureFormat.R16G16Unorm, + Format.R16G16Snorm => TextureFormat.R16G16Snorm, + Format.R16G16Uint => TextureFormat.R16G16Uint, + Format.R16G16Sint => TextureFormat.R16G16Sint, + Format.R32G32Float => TextureFormat.R32G32Float, + Format.R32G32Uint => TextureFormat.R32G32Uint, + Format.R32G32Sint => TextureFormat.R32G32Sint, + Format.R8G8B8A8Unorm => TextureFormat.R8G8B8A8Unorm, + Format.R8G8B8A8Snorm => TextureFormat.R8G8B8A8Snorm, + Format.R8G8B8A8Uint => TextureFormat.R8G8B8A8Uint, + Format.R8G8B8A8Sint => TextureFormat.R8G8B8A8Sint, + Format.R16G16B16A16Float => TextureFormat.R16G16B16A16Float, + Format.R16G16B16A16Unorm => TextureFormat.R16G16B16A16Unorm, + Format.R16G16B16A16Snorm => TextureFormat.R16G16B16A16Snorm, + Format.R16G16B16A16Uint => TextureFormat.R16G16B16A16Uint, + Format.R16G16B16A16Sint => TextureFormat.R16G16B16A16Sint, + Format.R32G32B32A32Float => TextureFormat.R32G32B32A32Float, + Format.R32G32B32A32Uint => TextureFormat.R32G32B32A32Uint, + Format.R32G32B32A32Sint => TextureFormat.R32G32B32A32Sint, + Format.R10G10B10A2Unorm => TextureFormat.R10G10B10A2Unorm, + Format.R10G10B10A2Uint => TextureFormat.R10G10B10A2Uint, + Format.R11G11B10Float => TextureFormat.R11G11B10Float, + _ => TextureFormat.Unknown + }; + } + + /// + /// Gets the texture descriptor for a given texture on the pool. + /// + /// Index of the texture (this is the shader "fake" handle) + /// Texture descriptor + private Image.TextureDescriptor GetTextureDescriptor(int handle) + { + if (_compute) + { + return _context.Methods.TextureManager.GetComputeTextureDescriptor(_state, handle); + } + else + { + return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(_state, _stageIndex, handle); + } + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Shader/GraphicsShader.cs b/Ryujinx.Graphics.Gpu/Shader/GraphicsShader.cs deleted file mode 100644 index e348f304..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/GraphicsShader.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Ryujinx.Graphics.GAL; - -namespace Ryujinx.Graphics.Gpu.Shader -{ - /// - /// Cached graphics shader code for all stages. - /// - class GraphicsShader - { - /// - /// Host shader program object. - /// - public IProgram HostProgram { get; set; } - - /// - /// Compiled shader for each shader stage. - /// - public CachedShader[] Shaders { get; } - - /// - /// Creates a new instance of cached graphics shader. - /// - public GraphicsShader() - { - Shaders = new CachedShader[Constants.ShaderStages]; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderBundle.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderBundle.cs new file mode 100644 index 00000000..de06e5e0 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderBundle.cs @@ -0,0 +1,46 @@ +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.Gpu.Shader +{ + /// + /// Represents a program composed of one or more shader stages (for graphics shaders), + /// or a single shader (for compute shaders). + /// + class ShaderBundle : IDisposable + { + /// + /// Host shader program object. + /// + public IProgram HostProgram { get; } + + /// + /// Compiled shader for each shader stage. + /// + public ShaderCodeHolder[] Shaders { get; } + + /// + /// Creates a new instance of the shader bundle. + /// + /// Host program with all the shader stages + /// Shaders + public ShaderBundle(IProgram hostProgram, params ShaderCodeHolder[] shaders) + { + HostProgram = hostProgram; + Shaders = shaders; + } + + /// + /// Dispose of the host shader resources. + /// + public void Dispose() + { + HostProgram.Dispose(); + + foreach (ShaderCodeHolder holder in Shaders) + { + holder?.HostShader.Dispose(); + } + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index d7144063..8a1abe32 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -1,33 +1,25 @@ -using Ryujinx.Common.Logging; 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.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Shader { - using TextureDescriptor = Image.TextureDescriptor; - /// /// Memory cache of shader code. /// class ShaderCache : IDisposable { - private const int MaxProgramSize = 0x100000; - private const TranslationFlags DefaultFlags = TranslationFlags.DebugMode; - private GpuContext _context; - - private ShaderDumper _dumper; + private readonly GpuContext _context; - private Dictionary> _cpPrograms; + private readonly ShaderDumper _dumper; - private Dictionary> _gpPrograms; + private readonly Dictionary> _cpPrograms; + private readonly Dictionary> _gpPrograms; /// /// Creates a new instance of the shader cache. @@ -39,9 +31,8 @@ namespace Ryujinx.Graphics.Gpu.Shader _dumper = new ShaderDumper(); - _cpPrograms = new Dictionary>(); - - _gpPrograms = new Dictionary>(); + _cpPrograms = new Dictionary>(); + _gpPrograms = new Dictionary>(); } /// @@ -58,7 +49,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Local memory size of the compute shader /// Shared memory size of the compute shader /// Compiled compute shader code - public ComputeShader GetComputeShader( + public ShaderBundle GetComputeShader( GpuState state, ulong gpuVa, int localSizeX, @@ -67,20 +58,20 @@ namespace Ryujinx.Graphics.Gpu.Shader int localMemorySize, int sharedMemorySize) { - bool isCached = _cpPrograms.TryGetValue(gpuVa, out List list); + bool isCached = _cpPrograms.TryGetValue(gpuVa, out List list); if (isCached) { - foreach (ComputeShader cachedCpShader in list) + foreach (ShaderBundle cachedCpShader in list) { - if (!IsShaderDifferent(cachedCpShader, gpuVa)) + if (IsShaderEqual(cachedCpShader, gpuVa)) { return cachedCpShader; } } } - CachedShader shader = TranslateComputeShader( + ShaderCodeHolder shader = TranslateComputeShader( state, gpuVa, localSizeX, @@ -93,11 +84,11 @@ namespace Ryujinx.Graphics.Gpu.Shader IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }); - ComputeShader cpShader = new ComputeShader(hostProgram, shader); + ShaderBundle cpShader = new ShaderBundle(hostProgram, shader); if (!isCached) { - list = new List(); + list = new List(); _cpPrograms.Add(gpuVa, list); } @@ -117,42 +108,42 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Current GPU state /// Addresses of the shaders for each stage /// Compiled graphics shader code - public GraphicsShader GetGraphicsShader(GpuState state, ShaderAddresses addresses) + public ShaderBundle GetGraphicsShader(GpuState state, ShaderAddresses addresses) { - bool isCached = _gpPrograms.TryGetValue(addresses, out List list); + bool isCached = _gpPrograms.TryGetValue(addresses, out List list); if (isCached) { - foreach (GraphicsShader cachedGpShaders in list) + foreach (ShaderBundle cachedGpShaders in list) { - if (!IsShaderDifferent(cachedGpShaders, addresses)) + if (IsShaderEqual(cachedGpShaders, addresses)) { return cachedGpShaders; } } } - GraphicsShader gpShaders = new GraphicsShader(); + ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages]; if (addresses.VertexA != 0) { - gpShaders.Shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA); + shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA); } else { - gpShaders.Shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex); + shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex); } - gpShaders.Shaders[1] = TranslateGraphicsShader(state, ShaderStage.TessellationControl, addresses.TessControl); - gpShaders.Shaders[2] = TranslateGraphicsShader(state, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); - gpShaders.Shaders[3] = TranslateGraphicsShader(state, ShaderStage.Geometry, addresses.Geometry); - gpShaders.Shaders[4] = TranslateGraphicsShader(state, ShaderStage.Fragment, addresses.Fragment); + shaders[1] = TranslateGraphicsShader(state, ShaderStage.TessellationControl, addresses.TessControl); + shaders[2] = TranslateGraphicsShader(state, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); + shaders[3] = TranslateGraphicsShader(state, ShaderStage.Geometry, addresses.Geometry); + shaders[4] = TranslateGraphicsShader(state, ShaderStage.Fragment, addresses.Fragment); List hostShaders = new List(); - for (int stage = 0; stage < gpShaders.Shaders.Length; stage++) + for (int stage = 0; stage < Constants.ShaderStages; stage++) { - ShaderProgram program = gpShaders.Shaders[stage]?.Program; + ShaderProgram program = shaders[stage]?.Program; if (program == null) { @@ -161,16 +152,18 @@ namespace Ryujinx.Graphics.Gpu.Shader IShader hostShader = _context.Renderer.CompileShader(program); - gpShaders.Shaders[stage].HostShader = hostShader; + shaders[stage].HostShader = hostShader; hostShaders.Add(hostShader); } - gpShaders.HostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray()); + IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray()); + + ShaderBundle gpShaders = new ShaderBundle(hostProgram, shaders); if (!isCached) { - list = new List(); + list = new List(); _gpPrograms.Add(addresses, list); } @@ -181,27 +174,27 @@ namespace Ryujinx.Graphics.Gpu.Shader } /// - /// Checks if compute shader code in memory is different from the cached shader. + /// Checks if compute shader code in memory is equal to the cached shader. /// /// Cached compute shader /// GPU virtual address of the shader code in memory /// True if the code is different, false otherwise - private bool IsShaderDifferent(ComputeShader cpShader, ulong gpuVa) + private bool IsShaderEqual(ShaderBundle cpShader, ulong gpuVa) { - return IsShaderDifferent(cpShader.Shader, gpuVa); + return IsShaderEqual(cpShader.Shaders[0], gpuVa); } /// - /// Checks if graphics shader code from all stages in memory is different from the cached shaders. + /// Checks if graphics shader code from all stages in memory are equal to the cached shaders. /// /// Cached graphics shaders /// GPU virtual addresses of all enabled shader stages /// True if the code is different, false otherwise - private bool IsShaderDifferent(GraphicsShader gpShaders, ShaderAddresses addresses) + private bool IsShaderEqual(ShaderBundle gpShaders, ShaderAddresses addresses) { for (int stage = 0; stage < gpShaders.Shaders.Length; stage++) { - CachedShader shader = gpShaders.Shaders[stage]; + ShaderCodeHolder shader = gpShaders.Shaders[stage]; ulong gpuVa = 0; @@ -214,13 +207,13 @@ namespace Ryujinx.Graphics.Gpu.Shader case 4: gpuVa = addresses.Fragment; break; } - if (IsShaderDifferent(shader, gpuVa)) + if (!IsShaderEqual(shader, gpuVa, addresses.VertexA)) { - return true; + return false; } } - return false; + return true; } /// @@ -228,17 +221,27 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Cached shader to compare with /// GPU virtual address of the binary shader code + /// Optional GPU virtual address of the "Vertex A" binary shader code /// True if the code is different, false otherwise - private bool IsShaderDifferent(CachedShader shader, ulong gpuVa) + private bool IsShaderEqual(ShaderCodeHolder shader, ulong gpuVa, ulong gpuVaA = 0) { if (shader == null) { - return false; + return true; } - ReadOnlySpan memoryCode = _context.MemoryAccessor.GetSpan(gpuVa, (ulong)shader.Code.Length * 4); + ReadOnlySpan memoryCode = _context.MemoryAccessor.GetSpan(gpuVa, shader.Code.Length); - return !MemoryMarshal.Cast(memoryCode).SequenceEqual(shader.Code); + bool equals = memoryCode.SequenceEqual(shader.Code); + + if (equals && shader.Code2 != null) + { + memoryCode = _context.MemoryAccessor.GetSpan(gpuVaA, shader.Code2.Length); + + equals = memoryCode.SequenceEqual(shader.Code2); + } + + return equals; } /// @@ -252,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Local memory size of the compute shader /// Shared memory size of the compute shader /// Compiled compute shader code - private CachedShader TranslateComputeShader( + private ShaderCodeHolder TranslateComputeShader( GpuState state, ulong gpuVa, int localSizeX, @@ -266,40 +269,13 @@ namespace Ryujinx.Graphics.Gpu.Shader return null; } - int QueryInfo(QueryInfoName info, int index) - { - return info switch - { - QueryInfoName.ComputeLocalSizeX - => localSizeX, - QueryInfoName.ComputeLocalSizeY - => localSizeY, - QueryInfoName.ComputeLocalSizeZ - => localSizeZ, - QueryInfoName.ComputeLocalMemorySize - => localMemorySize, - QueryInfoName.ComputeSharedMemorySize - => sharedMemorySize, - QueryInfoName.IsTextureBuffer - => Convert.ToInt32(QueryIsTextureBuffer(state, 0, index, compute: true)), - QueryInfoName.IsTextureRectangle - => Convert.ToInt32(QueryIsTextureRectangle(state, 0, index, compute: true)), - QueryInfoName.TextureFormat - => (int)QueryTextureFormat(state, 0, index, compute: true), - _ - => QueryInfoCommon(info) - }; - } - - TranslatorCallbacks callbacks = new TranslatorCallbacks(QueryInfo, PrintLog); + GpuAccessor gpuAccessor = new GpuAccessor(_context, state, localSizeX, localSizeY, localSizeZ, localMemorySize, sharedMemorySize); ShaderProgram program; - ReadOnlySpan code = _context.MemoryAccessor.GetSpan(gpuVa, MaxProgramSize); + program = Translator.Translate(gpuVa, gpuAccessor, DefaultFlags | TranslationFlags.Compute); - program = Translator.Translate(code, callbacks, DefaultFlags | TranslationFlags.Compute); - - int[] codeCached = MemoryMarshal.Cast(code.Slice(0, program.Size)).ToArray(); + byte[] code = _context.MemoryAccessor.ReadBytes(gpuVa, program.Size); _dumper.Dump(code, compute: true, out string fullPath, out string codePath); @@ -309,7 +285,7 @@ namespace Ryujinx.Graphics.Gpu.Shader program.Prepend("// " + fullPath); } - return new CachedShader(program, codeCached); + return new ShaderCodeHolder(program, code); } /// @@ -323,45 +299,21 @@ namespace Ryujinx.Graphics.Gpu.Shader /// GPU virtual address of the shader code /// Optional GPU virtual address of the "Vertex A" shader code /// Compiled graphics shader code - private CachedShader TranslateGraphicsShader(GpuState state, ShaderStage stage, ulong gpuVa, ulong gpuVaA = 0) + private ShaderCodeHolder TranslateGraphicsShader(GpuState state, ShaderStage stage, ulong gpuVa, ulong gpuVaA = 0) { if (gpuVa == 0) { return null; } - int QueryInfo(QueryInfoName info, int index) - { - return info switch - { - QueryInfoName.IsTextureBuffer - => Convert.ToInt32(QueryIsTextureBuffer(state, (int)stage - 1, index, compute: false)), - QueryInfoName.IsTextureRectangle - => Convert.ToInt32(QueryIsTextureRectangle(state, (int)stage - 1, index, compute: false)), - QueryInfoName.PrimitiveTopology - => (int)QueryPrimitiveTopology(), - QueryInfoName.TextureFormat - => (int)QueryTextureFormat(state, (int)stage - 1, index, compute: false), - _ - => QueryInfoCommon(info) - }; - } - - TranslatorCallbacks callbacks = new TranslatorCallbacks(QueryInfo, PrintLog); - - ShaderProgram program; - - int[] codeCached = null; + GpuAccessor gpuAccessor = new GpuAccessor(_context, state, (int)stage - 1); if (gpuVaA != 0) { - ReadOnlySpan codeA = _context.MemoryAccessor.GetSpan(gpuVaA, MaxProgramSize); - ReadOnlySpan codeB = _context.MemoryAccessor.GetSpan(gpuVa, MaxProgramSize); + ShaderProgram program = Translator.Translate(gpuVaA, gpuVa, gpuAccessor, DefaultFlags); - program = Translator.Translate(codeA, codeB, callbacks, DefaultFlags); - - // TODO: We should also take "codeA" into account. - codeCached = MemoryMarshal.Cast(codeB.Slice(0, program.Size)).ToArray(); + byte[] codeA = _context.MemoryAccessor.ReadBytes(gpuVaA, program.SizeA); + byte[] codeB = _context.MemoryAccessor.ReadBytes(gpuVa, program.Size); _dumper.Dump(codeA, compute: false, out string fullPathA, out string codePathA); _dumper.Dump(codeB, compute: false, out string fullPathB, out string codePathB); @@ -373,14 +325,14 @@ namespace Ryujinx.Graphics.Gpu.Shader program.Prepend("// " + codePathA); program.Prepend("// " + fullPathA); } + + return new ShaderCodeHolder(program, codeB, codeA); } else { - ReadOnlySpan code = _context.MemoryAccessor.GetSpan(gpuVa, MaxProgramSize); - - program = Translator.Translate(code, callbacks, DefaultFlags); + ShaderProgram program = Translator.Translate(gpuVa, gpuAccessor, DefaultFlags); - codeCached = MemoryMarshal.Cast(code.Slice(0, program.Size)).ToArray(); + byte[] code = _context.MemoryAccessor.ReadBytes(gpuVa, program.Size); _dumper.Dump(code, compute: false, out string fullPath, out string codePath); @@ -389,195 +341,9 @@ namespace Ryujinx.Graphics.Gpu.Shader program.Prepend("// " + codePath); program.Prepend("// " + fullPath); } - } - - ulong address = _context.MemoryManager.Translate(gpuVa); - - return new CachedShader(program, codeCached); - } - - /// - /// Gets the primitive topology for the current draw. - /// This is required by geometry shaders. - /// - /// Primitive topology - private InputTopology QueryPrimitiveTopology() - { - 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; - } - - /// - /// Check if the target of a given texture is texture buffer. - /// This is required as 1D textures and buffer textures shares the same sampler type on binary shader code, - /// but not on GLSL. - /// - /// Current GPU state - /// Index of the shader stage - /// Index of the texture (this is the shader "fake" handle) - /// Indicates whenever the texture descriptor is for the compute or graphics engine - /// True if the texture is a buffer texture, false otherwise - private bool QueryIsTextureBuffer(GpuState state, int stageIndex, int handle, bool compute) - { - return GetTextureDescriptor(state, stageIndex, handle, compute).UnpackTextureTarget() == TextureTarget.TextureBuffer; - } - - /// - /// Check if the target of a given texture is texture rectangle. - /// This is required as 2D textures and rectangle textures shares the same sampler type on binary shader code, - /// but not on GLSL. - /// - /// Current GPU state - /// Index of the shader stage - /// Index of the texture (this is the shader "fake" handle) - /// Indicates whenever the texture descriptor is for the compute or graphics engine - /// True if the texture is a rectangle texture, false otherwise - private bool QueryIsTextureRectangle(GpuState state, int stageIndex, int handle, bool compute) - { - var descriptor = GetTextureDescriptor(state, stageIndex, handle, compute); - - TextureTarget target = descriptor.UnpackTextureTarget(); - - bool is2DTexture = target == TextureTarget.Texture2D || - target == TextureTarget.Texture2DRect; - return !descriptor.UnpackTextureCoordNormalized() && is2DTexture; - } - - /// - /// Queries the format of a given texture. - /// - /// Current GPU state - /// Index of the shader stage. This is ignored if is true - /// Index of the texture (this is the shader "fake" handle) - /// Indicates whenever the texture descriptor is for the compute or graphics engine - /// The texture format - private TextureFormat QueryTextureFormat(GpuState state, int stageIndex, int handle, bool compute) - { - return QueryTextureFormat(GetTextureDescriptor(state, stageIndex, handle, compute)); - } - - /// - /// Queries the format of a given texture. - /// - /// Descriptor of the texture from the texture pool - /// The texture format - private static TextureFormat QueryTextureFormat(TextureDescriptor descriptor) - { - if (!FormatTable.TryGetTextureFormat(descriptor.UnpackFormat(), descriptor.UnpackSrgb(), out FormatInfo formatInfo)) - { - return TextureFormat.Unknown; + return new ShaderCodeHolder(program, code); } - - return formatInfo.Format switch - { - Format.R8Unorm => TextureFormat.R8Unorm, - Format.R8Snorm => TextureFormat.R8Snorm, - Format.R8Uint => TextureFormat.R8Uint, - Format.R8Sint => TextureFormat.R8Sint, - Format.R16Float => TextureFormat.R16Float, - Format.R16Unorm => TextureFormat.R16Unorm, - Format.R16Snorm => TextureFormat.R16Snorm, - Format.R16Uint => TextureFormat.R16Uint, - Format.R16Sint => TextureFormat.R16Sint, - Format.R32Float => TextureFormat.R32Float, - Format.R32Uint => TextureFormat.R32Uint, - Format.R32Sint => TextureFormat.R32Sint, - Format.R8G8Unorm => TextureFormat.R8G8Unorm, - Format.R8G8Snorm => TextureFormat.R8G8Snorm, - Format.R8G8Uint => TextureFormat.R8G8Uint, - Format.R8G8Sint => TextureFormat.R8G8Sint, - Format.R16G16Float => TextureFormat.R16G16Float, - Format.R16G16Unorm => TextureFormat.R16G16Unorm, - Format.R16G16Snorm => TextureFormat.R16G16Snorm, - Format.R16G16Uint => TextureFormat.R16G16Uint, - Format.R16G16Sint => TextureFormat.R16G16Sint, - Format.R32G32Float => TextureFormat.R32G32Float, - Format.R32G32Uint => TextureFormat.R32G32Uint, - Format.R32G32Sint => TextureFormat.R32G32Sint, - Format.R8G8B8A8Unorm => TextureFormat.R8G8B8A8Unorm, - Format.R8G8B8A8Snorm => TextureFormat.R8G8B8A8Snorm, - Format.R8G8B8A8Uint => TextureFormat.R8G8B8A8Uint, - Format.R8G8B8A8Sint => TextureFormat.R8G8B8A8Sint, - Format.R16G16B16A16Float => TextureFormat.R16G16B16A16Float, - Format.R16G16B16A16Unorm => TextureFormat.R16G16B16A16Unorm, - Format.R16G16B16A16Snorm => TextureFormat.R16G16B16A16Snorm, - Format.R16G16B16A16Uint => TextureFormat.R16G16B16A16Uint, - Format.R16G16B16A16Sint => TextureFormat.R16G16B16A16Sint, - Format.R32G32B32A32Float => TextureFormat.R32G32B32A32Float, - Format.R32G32B32A32Uint => TextureFormat.R32G32B32A32Uint, - Format.R32G32B32A32Sint => TextureFormat.R32G32B32A32Sint, - Format.R10G10B10A2Unorm => TextureFormat.R10G10B10A2Unorm, - Format.R10G10B10A2Uint => TextureFormat.R10G10B10A2Uint, - Format.R11G11B10Float => TextureFormat.R11G11B10Float, - _ => TextureFormat.Unknown - }; - } - - /// - /// Gets the texture descriptor for a given texture on the pool. - /// - /// Current GPU state - /// Index of the shader stage. This is ignored if is true - /// Index of the texture (this is the shader "fake" handle) - /// Indicates whenever the texture descriptor is for the compute or graphics engine - /// Texture descriptor - private TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle, bool compute) - { - if (compute) - { - return _context.Methods.TextureManager.GetComputeTextureDescriptor(state, handle); - } - else - { - return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(state, stageIndex, handle); - } - } - - /// - /// Returns information required by both compute and graphics shader compilation. - /// - /// Information queried - /// Requested information - private int QueryInfoCommon(QueryInfoName info) - { - return info switch - { - QueryInfoName.StorageBufferOffsetAlignment - => _context.Capabilities.StorageBufferOffsetAlignment, - QueryInfoName.SupportsNonConstantTextureOffset - => Convert.ToInt32(_context.Capabilities.SupportsNonConstantTextureOffset), - _ - => 0 - }; - } - - /// - /// Prints a warning from the shader code translator. - /// - /// Warning message - private static void PrintLog(string message) - { - Logger.PrintWarning(LogClass.Gpu, $"Shader translator: {message}"); } /// @@ -586,25 +352,19 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public void Dispose() { - foreach (List list in _cpPrograms.Values) + foreach (List list in _cpPrograms.Values) { - foreach (ComputeShader shader in list) + foreach (ShaderBundle bundle in list) { - shader.HostProgram.Dispose(); - shader.Shader?.HostShader.Dispose(); + bundle.Dispose(); } } - foreach (List list in _gpPrograms.Values) + foreach (List list in _gpPrograms.Values) { - foreach (GraphicsShader shader in list) + foreach (ShaderBundle bundle in list) { - shader.HostProgram.Dispose(); - - foreach (CachedShader cachedShader in shader.Shaders) - { - cachedShader?.HostShader.Dispose(); - } + bundle.Dispose(); } } } diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCodeHolder.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCodeHolder.cs new file mode 100644 index 00000000..dd90788e --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCodeHolder.cs @@ -0,0 +1,44 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.Gpu.Shader +{ + /// + /// Cached shader code for a single shader stage. + /// + class ShaderCodeHolder + { + /// + /// Shader program containing translated code. + /// + public ShaderProgram Program { get; } + + /// + /// Host shader object. + /// + public IShader HostShader { get; set; } + + /// + /// Maxwell binary shader code. + /// + public byte[] Code { get; } + + /// + /// Optional maxwell binary shader code for "Vertex A" shader. + /// + public byte[] Code2 { get; } + + /// + /// Creates a new instace of the shader code holder. + /// + /// Shader program + /// Maxwell binary shader code + /// Optional binary shader code of the "Vertex A" shader, when combined with "Vertex B" + public ShaderCodeHolder(ShaderProgram program, byte[] code, byte[] code2 = null) + { + Program = program; + Code = code; + Code2 = code2; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs index 0e22b07e..c170f9e2 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs @@ -1,4 +1,3 @@ -using Ryujinx.Graphics.Shader.Translation; using System; using System.IO; @@ -11,13 +10,19 @@ namespace Ryujinx.Graphics.Gpu.Shader { private string _runtimeDir; private string _dumpPath; - private int _dumpIndex; - public int CurrentDumpIndex => _dumpIndex; + /// + /// Current index of the shader dump binary file. + /// This is incremented after each save, in order to give unique names to the files. + /// + public int CurrentDumpIndex { get; private set; } + /// + /// Creates a new instance of the shader dumper. + /// public ShaderDumper() { - _dumpIndex = 1; + CurrentDumpIndex = 1; } /// @@ -27,7 +32,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// True for compute shader code, false for graphics shader code /// Output path for the shader code with header included /// Output path for the shader code without header - public void Dump(ReadOnlySpan code, bool compute, out string fullPath, out string codePath) + public void Dump(byte[] code, bool compute, out string fullPath, out string codePath) { _dumpPath = GraphicsConfig.ShadersDumpPath; @@ -39,38 +44,34 @@ namespace Ryujinx.Graphics.Gpu.Shader return; } - string fileName = "Shader" + _dumpIndex.ToString("d4") + ".bin"; + string fileName = "Shader" + CurrentDumpIndex.ToString("d4") + ".bin"; fullPath = Path.Combine(FullDir(), fileName); codePath = Path.Combine(CodeDir(), fileName); - _dumpIndex++; + CurrentDumpIndex++; - code = Translator.ExtractCode(code, compute, out int headerSize); + using MemoryStream stream = new MemoryStream(code); + BinaryReader codeReader = new BinaryReader(stream); - using (MemoryStream stream = new MemoryStream(code.ToArray())) - { - BinaryReader codeReader = new BinaryReader(stream); + using FileStream fullFile = File.Create(fullPath); + using FileStream codeFile = File.Create(codePath); + BinaryWriter fullWriter = new BinaryWriter(fullFile); + BinaryWriter codeWriter = new BinaryWriter(codeFile); - using (FileStream fullFile = File.Create(fullPath)) - using (FileStream codeFile = File.Create(codePath)) - { - BinaryWriter fullWriter = new BinaryWriter(fullFile); - BinaryWriter codeWriter = new BinaryWriter(codeFile); + int headerSize = compute ? 0 : 0x50; - fullWriter.Write(codeReader.ReadBytes(headerSize)); + fullWriter.Write(codeReader.ReadBytes(headerSize)); - byte[] temp = codeReader.ReadBytes(code.Length - headerSize); + byte[] temp = codeReader.ReadBytes(code.Length - headerSize); - fullWriter.Write(temp); - codeWriter.Write(temp); + fullWriter.Write(temp); + codeWriter.Write(temp); - // Align to meet nvdisasm requirements. - while (codeFile.Length % 0x20 != 0) - { - codeWriter.Write(0); - } - } + // Align to meet nvdisasm requirements. + while (codeFile.Length % 0x20 != 0) + { + codeWriter.Write(0); } } -- cgit v1.2.3