From b8eb6abeccbd4a468214a4d2ad3a9b6e5e06973c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 5 May 2020 22:02:28 -0300 Subject: [PATCH] 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/DmaPusher.cs | 2 +- Ryujinx.Graphics.Gpu/Engine/Compute.cs | 4 +- .../Engine/MethodCopyBuffer.cs | 2 +- Ryujinx.Graphics.Gpu/Engine/Methods.cs | 2 +- Ryujinx.Graphics.Gpu/Image/Texture.cs | 2 +- Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 2 +- Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs | 17 +- Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs | 41 -- Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs | 4 +- 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 | 392 ++++-------------- .../{CachedShader.cs => ShaderCodeHolder.cs} | 15 +- Ryujinx.Graphics.Gpu/Shader/ShaderDumper.cs | 61 +-- Ryujinx.Graphics.Nvdec/VDec/VideoDecoder.cs | 21 +- .../CodeGen/Glsl/Declarations.cs | 12 +- Ryujinx.Graphics.Shader/Decoders/Decoder.cs | 34 +- Ryujinx.Graphics.Shader/IGpuAccessor.cs | 67 +++ .../Instructions/InstEmitConversion.cs | 4 +- .../Instructions/InstEmitFArith.cs | 2 +- .../Instructions/InstEmitMemory.cs | 22 +- .../Instructions/InstEmitMove.cs | 2 +- .../Instructions/InstEmitTexture.cs | 16 +- .../Instructions/InstEmitVote.cs | 2 +- Ryujinx.Graphics.Shader/QueryInfoName.cs | 17 - Ryujinx.Graphics.Shader/ShaderProgram.cs | 4 +- .../Translation/Lowering.cs | 6 +- .../Optimizations/GlobalToStorage.cs | 4 +- .../Translation/ShaderConfig.cs | 58 +-- .../Translation/ShaderHeader.cs | 36 +- .../Translation/Translator.cs | 62 +-- .../Translation/TranslatorCallbacks.cs | 17 - Ryujinx.ShaderTools/Program.cs | 18 +- 35 files changed, 633 insertions(+), 684 deletions(-) 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 rename Ryujinx.Graphics.Gpu/Shader/{CachedShader.cs => ShaderCodeHolder.cs} (60%) create mode 100644 Ryujinx.Graphics.Shader/IGpuAccessor.cs delete mode 100644 Ryujinx.Graphics.Shader/QueryInfoName.cs delete mode 100644 Ryujinx.Graphics.Shader/Translation/TranslatorCallbacks.cs diff --git a/Ryujinx.Graphics.Gpu/DmaPusher.cs b/Ryujinx.Graphics.Gpu/DmaPusher.cs index 6d53e42c37..3b5ac830db 100644 --- a/Ryujinx.Graphics.Gpu/DmaPusher.cs +++ b/Ryujinx.Graphics.Gpu/DmaPusher.cs @@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Gpu { if (Words == null) { - Words = MemoryMarshal.Cast(context.MemoryAccessor.GetSpan(EntryAddress, EntryCount * 4)).ToArray(); + Words = MemoryMarshal.Cast(context.MemoryAccessor.GetSpan(EntryAddress, (int)EntryCount * 4)).ToArray(); } } diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs index 920cf0dd91..7b4b1e8a43 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Gpu.Engine BufferManager.SetComputeUniformBuffer(index, gpuVa, size); } - ComputeShader cs = ShaderCache.GetComputeShader( + ShaderBundle cs = ShaderCache.GetComputeShader( state, shaderGpuVa, qmd.CtaThreadDimension0, @@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Engine TextureManager.SetComputeTextureBufferIndex(state.Get(MethodOffset.TextureBufferIndex)); - ShaderProgramInfo info = cs.Shader.Program.Info; + ShaderProgramInfo info = cs.Shaders[0].Program.Info; for (int index = 0; index < info.CBuffers.Count; index++) { diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs index 9f638f504c..7244db3242 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Engine ulong srcAddress = srcBaseAddress + (ulong)srcOffset; ulong dstAddress = dstBaseAddress + (ulong)dstOffset; - ReadOnlySpan pixel = _context.PhysicalMemory.GetSpan(srcAddress, (ulong)srcBpp); + ReadOnlySpan pixel = _context.PhysicalMemory.GetSpan(srcAddress, srcBpp); _context.PhysicalMemory.Write(dstAddress, pixel); } diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index 59678be59f..5ead87a0ed 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -839,7 +839,7 @@ namespace Ryujinx.Graphics.Gpu.Engine addressesArray[index] = baseAddress + shader.Offset; } - GraphicsShader gs = ShaderCache.GetGraphicsShader(state, addresses); + ShaderBundle gs = ShaderCache.GetGraphicsShader(state, addresses); _vsUsesInstanceId = gs.Shaders[0]?.Program.Info.UsesInstanceId ?? false; diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 9554bc6b06..0f952ffd4f 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -315,7 +315,7 @@ namespace Ryujinx.Graphics.Gpu.Image return; } - ReadOnlySpan data = _context.PhysicalMemory.GetSpan(Address, Size); + ReadOnlySpan data = _context.PhysicalMemory.GetSpan(Address, (int)Size); // If the texture was modified by the host GPU, we do partial invalidation // of the texture by getting GPU data and merging in the pages of memory diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index 959e1a10c4..4dd96878a3 100644 --- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Gpu.Memory int offset = (int)(mAddress - Address); - HostBuffer.SetData(offset, _context.PhysicalMemory.GetSpan(mAddress, mSize)); + HostBuffer.SetData(offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize)); } } diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs index fbe2cbc423..cfc6f7f50a 100644 --- a/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Memory @@ -25,7 +26,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// GPU virtual address where the data is located /// Size of the data in bytes /// Byte array with the data - public byte[] ReadBytes(ulong gpuVa, ulong size) + public byte[] ReadBytes(ulong gpuVa, int size) { return GetSpan(gpuVa, size).ToArray(); } @@ -35,14 +36,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// This reads as much data as possible, up to the specified maximum size. /// /// GPU virtual address where the data is located - /// Maximum size of the data + /// Size of the data /// The span of the data at the specified memory location - public ReadOnlySpan GetSpan(ulong gpuVa, ulong maxSize) + public ReadOnlySpan GetSpan(ulong gpuVa, int size) { ulong processVa = _context.MemoryManager.Translate(gpuVa); - ulong size = _context.MemoryManager.GetSubSize(gpuVa, maxSize); - return _context.PhysicalMemory.GetSpan(processVa, size); } @@ -52,13 +51,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Type of the structure /// GPU virtual address where the structure is located /// The structure at the specified memory location - public T Read(ulong gpuVa) where T : struct + public T Read(ulong gpuVa) where T : unmanaged { ulong processVa = _context.MemoryManager.Translate(gpuVa); - ulong size = (uint)Marshal.SizeOf(); - - return MemoryMarshal.Cast(_context.PhysicalMemory.GetSpan(processVa, size))[0]; + return MemoryMarshal.Cast(_context.PhysicalMemory.GetSpan(processVa, Unsafe.SizeOf()))[0]; } /// @@ -114,7 +111,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// GPU virtual address to write the data into /// The data to be written - public void Write(ulong gpuVa, Span data) + public void Write(ulong gpuVa, ReadOnlySpan data) { ulong processVa = _context.MemoryManager.Translate(gpuVa); diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 6f9ee6a446..a9a8fbac71 100644 --- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -240,28 +240,6 @@ namespace Ryujinx.Graphics.Gpu.Memory return PteUnmapped; } - /// - /// Gets the number of mapped or reserved pages on a given region. - /// - /// Start GPU virtual address of the region - /// Maximum size of the data - /// Mapped size in bytes of the specified region - internal ulong GetSubSize(ulong gpuVa, ulong maxSize) - { - ulong size = 0; - - while (GetPte(gpuVa + size) != PteUnmapped) - { - size += PageSize; - if (size >= maxSize) - { - return maxSize; - } - } - - return size; - } - /// /// Translates a GPU virtual address to a CPU virtual address. /// @@ -279,25 +257,6 @@ namespace Ryujinx.Graphics.Gpu.Memory return baseAddress + (gpuVa & PageMask); } - /// - /// Checks if a given memory region is currently unmapped. - /// - /// Start GPU virtual address of the region - /// Size in bytes of the region - /// True if the region is unmapped (free), false otherwise - public bool IsRegionFree(ulong gpuVa, ulong size) - { - for (ulong offset = 0; offset < size; offset += PageSize) - { - if (IsPageInUse(gpuVa + offset)) - { - return false; - } - } - - return true; - } - /// /// Checks if a given memory page is mapped or reserved. /// diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index a787305dc3..5d9b556181 100644 --- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -28,9 +28,9 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Start address of the range /// Size in bytes to be range /// A read only span of the data at the specified memory location - public ReadOnlySpan GetSpan(ulong address, ulong size) + public ReadOnlySpan GetSpan(ulong address, int size) { - return _cpuMemory.GetSpan(address, (int)size); + return _cpuMemory.GetSpan(address, size); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ComputeShader.cs b/Ryujinx.Graphics.Gpu/Shader/ComputeShader.cs deleted file mode 100644 index fcc38d0488..0000000000 --- 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 0000000000..7dc175e1d4 --- /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 e348f304f3..0000000000 --- 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 0000000000..de06e5e079 --- /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 d71440634b..8a1abe32f3 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 readonly GpuContext _context; - private ShaderDumper _dumper; + private readonly ShaderDumper _dumper; - private Dictionary> _cpPrograms; - - 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); + ShaderProgram program = Translator.Translate(gpuVa, gpuAccessor, DefaultFlags); - program = Translator.Translate(code, callbacks, 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); } + + return new ShaderCodeHolder(program, code); } - - 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 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/CachedShader.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCodeHolder.cs similarity index 60% rename from Ryujinx.Graphics.Gpu/Shader/CachedShader.cs rename to Ryujinx.Graphics.Gpu/Shader/ShaderCodeHolder.cs index f849404564..dd90788e90 100644 --- a/Ryujinx.Graphics.Gpu/Shader/CachedShader.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCodeHolder.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Cached shader code for a single shader stage. /// - class CachedShader + class ShaderCodeHolder { /// /// Shader program containing translated code. @@ -21,17 +21,24 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Maxwell binary shader code. /// - public int[] Code { get; } + public byte[] Code { get; } /// - /// Creates a new instace of the cached shader. + /// 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 - public CachedShader(ShaderProgram program, int[] 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 0e22b07e3a..c170f9e218 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())) + 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)); + + byte[] temp = codeReader.ReadBytes(code.Length - headerSize); + + fullWriter.Write(temp); + codeWriter.Write(temp); + + // Align to meet nvdisasm requirements. + while (codeFile.Length % 0x20 != 0) { - 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); - - fullWriter.Write(codeReader.ReadBytes(headerSize)); - - byte[] temp = codeReader.ReadBytes(code.Length - headerSize); - - fullWriter.Write(temp); - codeWriter.Write(temp); - - // Align to meet nvdisasm requirements. - while (codeFile.Length % 0x20 != 0) - { - codeWriter.Write(0); - } - } + codeWriter.Write(0); } } diff --git a/Ryujinx.Graphics.Nvdec/VDec/VideoDecoder.cs b/Ryujinx.Graphics.Nvdec/VDec/VideoDecoder.cs index b4a89d8f3d..9afc948559 100644 --- a/Ryujinx.Graphics.Nvdec/VDec/VideoDecoder.cs +++ b/Ryujinx.Graphics.Nvdec/VDec/VideoDecoder.cs @@ -1,6 +1,8 @@ using Ryujinx.Graphics.Gpu; +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Vic; using System; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.VDec { @@ -70,7 +72,7 @@ namespace Ryujinx.Graphics.VDec ScalingMatrix8 = gpu.MemoryAccessor.ReadBytes(_decoderContextAddress + 0x220, 2 * 64) }; - byte[] frameData = gpu.MemoryAccessor.ReadBytes(_frameDataAddress, (ulong)frameDataSize); + byte[] frameData = gpu.MemoryAccessor.ReadBytes(_frameDataAddress, frameDataSize); _h264Decoder.Decode(Params, matrices, frameData); } @@ -86,7 +88,7 @@ namespace Ryujinx.Graphics.VDec Ref2Key = (long)gpu.MemoryManager.Translate(_vpxRef2LumaAddress) }; - Vp9FrameHeader header = gpu.MemoryAccessor.Read(_decoderContextAddress + 0x48); + Vp9FrameHeader header = ReadStruct(gpu.MemoryAccessor, _decoderContextAddress + 0x48); Vp9ProbabilityTables probs = new Vp9ProbabilityTables() { @@ -117,7 +119,7 @@ namespace Ryujinx.Graphics.VDec MvHpProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x54a, 0x2) }; - byte[] frameData = gpu.MemoryAccessor.ReadBytes(_frameDataAddress, (ulong)frameDataSize); + byte[] frameData = gpu.MemoryAccessor.ReadBytes(_frameDataAddress, frameDataSize); _vp9Decoder.Decode(keys, header, probs, frameData); } @@ -127,6 +129,19 @@ namespace Ryujinx.Graphics.VDec } } + private T ReadStruct(MemoryAccessor accessor, ulong address) where T : struct + { + byte[] data = accessor.ReadBytes(address, Marshal.SizeOf()); + + unsafe + { + fixed (byte* ptr = data) + { + return Marshal.PtrToStructure((IntPtr)ptr); + } + } + } + private void SetDecoderCtxAddr(int[] arguments) { _decoderContextAddress = GetAddress(arguments); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 36aff3fc42..da902aa304 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.Stage == ShaderStage.Geometry) { - string inPrimitive = ((InputTopology)context.Config.QueryInfo(QueryInfoName.PrimitiveTopology)).ToGlslString(); + string inPrimitive = context.Config.GpuAccessor.QueryPrimitiveTopology().ToGlslString(); context.AppendLine($"layout ({inPrimitive}) in;"); @@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.Stage == ShaderStage.Compute) { - int localMemorySize = BitUtils.DivRoundUp(context.Config.QueryInfo(QueryInfoName.ComputeLocalMemorySize), 4); + int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4); if (localMemorySize != 0) { @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - int sharedMemorySize = BitUtils.DivRoundUp(context.Config.QueryInfo(QueryInfoName.ComputeSharedMemorySize), 4); + int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4); if (sharedMemorySize != 0) { @@ -124,9 +124,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { - 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)); + string localSizeX = NumberFormatter.FormatInt(context.Config.GpuAccessor.QueryComputeLocalSizeX()); + string localSizeY = NumberFormatter.FormatInt(context.Config.GpuAccessor.QueryComputeLocalSizeY()); + string localSizeZ = NumberFormatter.FormatInt(context.Config.GpuAccessor.QueryComputeLocalSizeZ()); context.AppendLine( "layout (" + diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 3e322e453e..e2c9212b3c 100644 --- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -1,6 +1,5 @@ using Ryujinx.Graphics.Shader.Instructions; using System; -using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; @@ -10,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { static class Decoder { - public static Block[] Decode(ReadOnlySpan code, ulong headerSize) + public static Block[] Decode(IGpuAccessor gpuAccessor, ulong startAddress) { List blocks = new List(); @@ -18,8 +17,6 @@ namespace Ryujinx.Graphics.Shader.Decoders Dictionary visited = new Dictionary(); - ulong maxAddress = (ulong)code.Length - headerSize; - Block GetBlock(ulong blkAddress) { if (!visited.TryGetValue(blkAddress, out Block block)) @@ -56,7 +53,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } // If we have a block after the current one, set the limit address. - ulong limitAddress = maxAddress; + ulong limitAddress = ulong.MaxValue; if (nBlkIndex != blocks.Count) { @@ -74,7 +71,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } } - FillBlock(code, currBlock, limitAddress, headerSize); + FillBlock(gpuAccessor, currBlock, limitAddress, startAddress); if (currBlock.OpCodes.Count != 0) { @@ -82,11 +79,6 @@ namespace Ryujinx.Graphics.Shader.Decoders // including those from SSY/PBK instructions. foreach (OpCodePush pushOp in currBlock.PushOpCodes) { - if (pushOp.GetAbsoluteAddress() >= maxAddress) - { - return null; - } - GetBlock(pushOp.GetAbsoluteAddress()); } @@ -98,11 +90,6 @@ namespace Ryujinx.Graphics.Shader.Decoders if (lastOp is OpCodeBranch opBr) { - if (opBr.GetAbsoluteAddress() >= maxAddress) - { - return null; - } - currBlock.Branch = GetBlock(opBr.GetAbsoluteAddress()); } else if (lastOp is OpCodeBranchIndir opBrIndir) @@ -141,7 +128,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } // Do we have a block after the current one? - if (!IsExit(currBlock.GetLastOp()) && currBlock.BrIndir != null && currBlock.EndAddress < maxAddress) + if (!IsExit(currBlock.GetLastOp()) && currBlock.BrIndir != null) { bool targetVisited = visited.ContainsKey(currBlock.EndAddress); @@ -203,10 +190,10 @@ namespace Ryujinx.Graphics.Shader.Decoders } private static void FillBlock( - ReadOnlySpan code, - Block block, - ulong limitAddress, - ulong startAddress) + IGpuAccessor gpuAccessor, + Block block, + ulong limitAddress, + ulong startAddress) { ulong address = block.Address; @@ -225,14 +212,11 @@ namespace Ryujinx.Graphics.Shader.Decoders continue; } - uint word0 = BinaryPrimitives.ReadUInt32LittleEndian(code.Slice((int)(startAddress + address))); - uint word1 = BinaryPrimitives.ReadUInt32LittleEndian(code.Slice((int)(startAddress + address + 4))); - ulong opAddress = address; address += 8; - long opCode = word0 | (long)word1 << 32; + long opCode = gpuAccessor.MemoryRead(startAddress + opAddress); (InstEmitter emitter, OpCodeTable.OpActivator opActivator) = OpCodeTable.GetEmitter(opCode); diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs new file mode 100644 index 0000000000..13281bf0f7 --- /dev/null +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -0,0 +1,67 @@ +namespace Ryujinx.Graphics.Shader +{ + public interface IGpuAccessor + { + public void Log(string message) + { + // No default log output. + } + + T MemoryRead(ulong address) where T : unmanaged; + + public int QueryComputeLocalSizeX() + { + return 1; + } + + public int QueryComputeLocalSizeY() + { + return 1; + } + + public int QueryComputeLocalSizeZ() + { + return 1; + } + + public int QueryComputeLocalMemorySize() + { + return 0x1000; + } + + public int QueryComputeSharedMemorySize() + { + return 0xc000; + } + + public bool QueryIsTextureBuffer(int handle) + { + return false; + } + + public bool QueryIsTextureRectangle(int handle) + { + return false; + } + + public InputTopology QueryPrimitiveTopology() + { + return InputTopology.Points; + } + + public int QueryStorageBufferOffsetAlignment() + { + return 16; + } + + public bool QuerySupportsNonConstantTextureOffset() + { + return true; + } + + public TextureFormat QueryTextureFormat(int handle) + { + return TextureFormat.R8G8B8A8Unorm; + } + } +} diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs index 8716d4e734..c4c6c55bb9 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (intType == IntegerType.U64) { - context.Config.PrintLog("Unimplemented 64-bits F2I."); + context.Config.GpuAccessor.Log("Unimplemented 64-bits F2I."); return; } @@ -184,7 +184,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (srcType == IntegerType.U64 || dstType == IntegerType.U64) { - context.Config.PrintLog("Invalid I2I encoding."); + context.Config.GpuAccessor.Log("Invalid I2I encoding."); return; } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs index 1da5158f81..6dc4c09326 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs @@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (scale.AsFloat() == 1) { - context.Config.PrintLog($"Invalid FP multiply scale \"{op.Scale}\"."); + context.Config.GpuAccessor.Log($"Invalid FP multiply scale \"{op.Scale}\"."); } if (isFP64) diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 7eb8888316..2418293d46 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.PrintLog($"Invalid barrier mode: {op.Mode}."); + context.Config.GpuAccessor.Log($"Invalid barrier mode: {op.Mode}."); } } @@ -141,7 +141,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.Size > IntegerSize.B64) { - context.Config.PrintLog($"Invalid LDC size: {op.Size}."); + context.Config.GpuAccessor.Log($"Invalid LDC size: {op.Size}."); } bool isSmallInt = op.Size < IntegerSize.B32; @@ -209,7 +209,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (!(emit || cut)) { - context.Config.PrintLog("Invalid OUT encoding."); + context.Config.GpuAccessor.Log("Invalid OUT encoding."); } if (emit) @@ -274,7 +274,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.PrintLog($"Invalid reduction type: {type}."); + context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomicOp.BitwiseAnd: @@ -284,7 +284,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.PrintLog($"Invalid reduction type: {type}."); + context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomicOp.BitwiseExclusiveOr: @@ -294,7 +294,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.PrintLog($"Invalid reduction type: {type}."); + context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomicOp.BitwiseOr: @@ -304,7 +304,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.PrintLog($"Invalid reduction type: {type}."); + context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomicOp.Maximum: @@ -318,7 +318,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.PrintLog($"Invalid reduction type: {type}."); + context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; case AtomicOp.Minimum: @@ -332,7 +332,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.PrintLog($"Invalid reduction type: {type}."); + context.Config.GpuAccessor.Log($"Invalid reduction type: {type}."); } break; } @@ -346,7 +346,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.Size > IntegerSize.B128) { - context.Config.PrintLog($"Invalid load size: {op.Size}."); + context.Config.GpuAccessor.Log($"Invalid load size: {op.Size}."); } bool isSmallInt = op.Size < IntegerSize.B32; @@ -432,7 +432,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (op.Size > IntegerSize.B128) { - context.Config.PrintLog($"Invalid store size: {op.Size}."); + context.Config.GpuAccessor.Log($"Invalid store size: {op.Size}."); } bool isSmallInt = op.Size < IntegerSize.B32; diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index efc80b0cb6..264c732dfd 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (isCC) { // TODO: Support Register to condition code flags copy. - context.Config.PrintLog("R2P.CC not implemented."); + context.Config.GpuAccessor.Log("R2P.CC not implemented."); } else { diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 18552d0a10..7f7a48a667 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.None) { - context.Config.PrintLog("Invalid image store sampler type."); + context.Config.GpuAccessor.Log("Invalid image store sampler type."); return; } @@ -158,7 +158,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.None) { - context.Config.PrintLog("Invalid image store sampler type."); + context.Config.GpuAccessor.Log("Invalid image store sampler type."); return; } @@ -344,7 +344,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.None) { - context.Config.PrintLog("Invalid texture sampler type."); + context.Config.GpuAccessor.Log("Invalid texture sampler type."); return; } @@ -423,14 +423,14 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.None) { - context.Config.PrintLog("Invalid texel fetch sampler type."); + context.Config.GpuAccessor.Log("Invalid texel fetch sampler type."); return; } flags = ConvertTextureFlags(tldsOp.Target) | TextureFlags.IntCoords; - if (tldsOp.Target == TexelLoadTarget.Texture1DLodZero && context.Config.QueryInfoBool(QueryInfoName.IsTextureBuffer, tldsOp.Immediate)) + if (tldsOp.Target == TexelLoadTarget.Texture1DLodZero && context.Config.GpuAccessor.QueryIsTextureBuffer(tldsOp.Immediate)) { type = SamplerType.TextureBuffer; flags &= ~TextureFlags.LodLevel; @@ -1020,7 +1020,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if (type == SamplerType.Texture1D && flags == TextureFlags.IntCoords && !isBindless) { - bool isTypeBuffer = context.Config.QueryInfoBool(QueryInfoName.IsTextureBuffer, op.Immediate); + bool isTypeBuffer = context.Config.GpuAccessor.QueryIsTextureBuffer(op.Immediate); if (isTypeBuffer) { @@ -1093,11 +1093,11 @@ namespace Ryujinx.Graphics.Shader.Instructions private static TextureFormat GetTextureFormat(EmitterContext context, int handle) { - var format = (TextureFormat)context.Config.QueryInfo(QueryInfoName.TextureFormat, handle); + var format = context.Config.GpuAccessor.QueryTextureFormat(handle); if (format == TextureFormat.Unknown) { - context.Config.PrintLog($"Unknown format for texture {handle}."); + context.Config.GpuAccessor.Log($"Unknown format for texture {handle}."); format = TextureFormat.R8G8B8A8Unorm; } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs index 8f81ecb4df..5c16770aba 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - context.Config.PrintLog($"Invalid vote operation: {op.VoteOp}."); + context.Config.GpuAccessor.Log($"Invalid vote operation: {op.VoteOp}."); } if (!op.Rd.IsRZ) diff --git a/Ryujinx.Graphics.Shader/QueryInfoName.cs b/Ryujinx.Graphics.Shader/QueryInfoName.cs deleted file mode 100644 index 41f42cbd61..0000000000 --- a/Ryujinx.Graphics.Shader/QueryInfoName.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Ryujinx.Graphics.Shader -{ - public enum QueryInfoName - { - ComputeLocalSizeX, - ComputeLocalSizeY, - ComputeLocalSizeZ, - ComputeLocalMemorySize, - ComputeSharedMemorySize, - IsTextureBuffer, - IsTextureRectangle, - PrimitiveTopology, - StorageBufferOffsetAlignment, - SupportsNonConstantTextureOffset, - TextureFormat - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/ShaderProgram.cs b/Ryujinx.Graphics.Shader/ShaderProgram.cs index 7a800d8c73..30f4017535 100644 --- a/Ryujinx.Graphics.Shader/ShaderProgram.cs +++ b/Ryujinx.Graphics.Shader/ShaderProgram.cs @@ -10,13 +10,15 @@ namespace Ryujinx.Graphics.Shader public string Code { get; private set; } + public int SizeA { get; } public int Size { get; } - internal ShaderProgram(ShaderProgramInfo info, ShaderStage stage, string code, int size) + internal ShaderProgram(ShaderProgramInfo info, ShaderStage stage, string code, int size, int sizeA) { Info = info; Stage = stage; Code = code; + SizeA = sizeA; Size = size; } diff --git a/Ryujinx.Graphics.Shader/Translation/Lowering.cs b/Ryujinx.Graphics.Shader/Translation/Lowering.cs index 0b5ec59273..6f52ce9652 100644 --- a/Ryujinx.Graphics.Shader/Translation/Lowering.cs +++ b/Ryujinx.Graphics.Shader/Translation/Lowering.cs @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation sbSlot = PrependOperation(Instruction.ConditionalSelect, inRange, Const(slot), sbSlot); } - Operand alignMask = Const(-config.QueryInfo(QueryInfoName.StorageBufferOffsetAlignment)); + Operand alignMask = Const(-config.GpuAccessor.QueryStorageBufferOffsetAlignment()); Operand baseAddrTrunc = PrependOperation(Instruction.BitwiseAnd, sbBaseAddrLow, alignMask); Operand byteOffset = PrependOperation(Instruction.Subtract, addrLow, baseAddrTrunc); @@ -131,9 +131,9 @@ namespace Ryujinx.Graphics.Shader.Translation bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; - bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.QueryInfoBool(QueryInfoName.SupportsNonConstantTextureOffset); + bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.GpuAccessor.QuerySupportsNonConstantTextureOffset(); - bool isRect = config.QueryInfoBool(QueryInfoName.IsTextureRectangle, texOp.Handle); + bool isRect = config.GpuAccessor.QueryIsTextureRectangle(texOp.Handle); if (!(hasInvalidOffset || isRect)) { diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 7988ef6c13..254bd0b342 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand baseAddrTrunc = Local(); - Operand alignMask = Const(-config.QueryInfo(QueryInfoName.StorageBufferOffsetAlignment)); + Operand alignMask = Const(-config.GpuAccessor.QueryStorageBufferOffsetAlignment()); Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); @@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand baseAddrTrunc = Local(); - Operand alignMask = Const(-config.QueryInfo(QueryInfoName.StorageBufferOffsetAlignment)); + Operand alignMask = Const(-config.GpuAccessor.QueryStorageBufferOffsetAlignment()); Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 3035ebf670..9e8329de99 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -18,11 +18,11 @@ namespace Ryujinx.Graphics.Shader.Translation public bool OmapSampleMask { get; } public bool OmapDepth { get; } + public IGpuAccessor GpuAccessor { get; } + public TranslationFlags Flags { get; } - private TranslatorCallbacks _callbacks; - - public ShaderConfig(TranslationFlags flags, TranslatorCallbacks callbacks) + public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags) { Stage = ShaderStage.Compute; OutputTopology = OutputTopology.PointList; @@ -32,11 +32,11 @@ namespace Ryujinx.Graphics.Shader.Translation OmapTargets = null; OmapSampleMask = false; OmapDepth = false; + GpuAccessor = gpuAccessor; Flags = flags; - _callbacks = callbacks; } - public ShaderConfig(ShaderHeader header, TranslationFlags flags, TranslatorCallbacks callbacks) + public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags) { Stage = header.Stage; OutputTopology = header.OutputTopology; @@ -46,8 +46,8 @@ namespace Ryujinx.Graphics.Shader.Translation OmapTargets = header.OmapTargets; OmapSampleMask = header.OmapSampleMask; OmapDepth = header.OmapDepth; + GpuAccessor = gpuAccessor; Flags = flags; - _callbacks = callbacks; } public int GetDepthRegister() @@ -68,51 +68,5 @@ namespace Ryujinx.Graphics.Shader.Translation // 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 (_callbacks.QueryInfo != null) - { - return _callbacks.QueryInfo(info, index); - } - else - { - switch (info) - { - case QueryInfoName.ComputeLocalSizeX: - case QueryInfoName.ComputeLocalSizeY: - case QueryInfoName.ComputeLocalSizeZ: - return 1; - case QueryInfoName.ComputeLocalMemorySize: - return 0x1000; - case QueryInfoName.ComputeSharedMemorySize: - return 0xc000; - case QueryInfoName.IsTextureBuffer: - return Convert.ToInt32(false); - case QueryInfoName.IsTextureRectangle: - return Convert.ToInt32(false); - case QueryInfoName.PrimitiveTopology: - return (int)InputTopology.Points; - case QueryInfoName.StorageBufferOffsetAlignment: - return 16; - case QueryInfoName.SupportsNonConstantTextureOffset: - return Convert.ToInt32(true); - case QueryInfoName.TextureFormat: - return (int)TextureFormat.R8G8B8A8Unorm; - } - } - - return 0; - } - - public void PrintLog(string message) - { - _callbacks.PrintLog?.Invoke(message); - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs b/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs index a3b861c979..1218d591b9 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs @@ -1,6 +1,5 @@ using Ryujinx.Graphics.Shader.Decoders; using System; -using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Shader.Translation { @@ -110,15 +109,13 @@ namespace Ryujinx.Graphics.Shader.Translation public bool OmapSampleMask { get; } public bool OmapDepth { get; } - public ShaderHeader(ReadOnlySpan code) + public ShaderHeader(IGpuAccessor gpuAccessor, ulong address) { - ReadOnlySpan header = MemoryMarshal.Cast(code); - - int commonWord0 = header[0]; - int commonWord1 = header[1]; - int commonWord2 = header[2]; - int commonWord3 = header[3]; - int commonWord4 = header[4]; + int commonWord0 = gpuAccessor.MemoryRead(address + 0); + int commonWord1 = gpuAccessor.MemoryRead(address + 4); + int commonWord2 = gpuAccessor.MemoryRead(address + 8); + int commonWord3 = gpuAccessor.MemoryRead(address + 12); + int commonWord4 = gpuAccessor.MemoryRead(address + 16); SphType = commonWord0.Extract(0, 5); Version = commonWord0.Extract(5, 5); @@ -163,22 +160,19 @@ namespace Ryujinx.Graphics.Shader.Translation ImapTypes = new ImapPixelType[32]; - for (int i = 0; i < 8; i++) + for (ulong i = 0; i < 32; i++) { - for (int j = 0; j < 4; j++) - { - byte imap = (byte)(header[6 + i] >> (j * 8)); + byte imap = gpuAccessor.MemoryRead(address + 0x18 + i); - ImapTypes[i * 4 + j] = new ImapPixelType( - (PixelImap)((imap >> 0) & 3), - (PixelImap)((imap >> 2) & 3), - (PixelImap)((imap >> 4) & 3), - (PixelImap)((imap >> 6) & 3)); - } + ImapTypes[i] = new ImapPixelType( + (PixelImap)((imap >> 0) & 3), + (PixelImap)((imap >> 2) & 3), + (PixelImap)((imap >> 4) & 3), + (PixelImap)((imap >> 6) & 3)); } - int type2OmapTarget = header[18]; - int type2Omap = header[19]; + int type2OmapTarget = gpuAccessor.MemoryRead(address + 0x48); + int type2Omap = gpuAccessor.MemoryRead(address + 0x4c); OmapTargets = new OmapTarget[8]; diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 164e10a7cc..fb6935afc9 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -14,46 +14,22 @@ namespace Ryujinx.Graphics.Shader.Translation { private const int HeaderSize = 0x50; - public static ReadOnlySpan ExtractCode(ReadOnlySpan code, bool compute, out int headerSize) + public static ShaderProgram Translate(ulong address, IGpuAccessor gpuAccessor, TranslationFlags flags) { - headerSize = compute ? 0 : HeaderSize; - - Block[] cfg = Decoder.Decode(code, (ulong)headerSize); - - if (cfg == null) - { - return code; - } - - ulong endAddress = 0; - - foreach (Block block in cfg) - { - if (endAddress < block.EndAddress) - { - endAddress = block.EndAddress; - } - } - - return code.Slice(0, headerSize + (int)endAddress); - } - - public static ShaderProgram Translate(ReadOnlySpan code, TranslatorCallbacks callbacks, TranslationFlags flags) - { - Operation[] ops = DecodeShader(code, callbacks, flags, out ShaderConfig config, out int size); + Operation[] ops = DecodeShader(address, gpuAccessor, flags, out ShaderConfig config, out int size); return Translate(ops, config, size); } - public static ShaderProgram Translate(ReadOnlySpan vpACode, ReadOnlySpan vpBCode, TranslatorCallbacks callbacks, TranslationFlags flags) + public static ShaderProgram Translate(ulong addressA, ulong addressB, IGpuAccessor gpuAccessor, TranslationFlags flags) { - Operation[] vpAOps = DecodeShader(vpACode, callbacks, flags, out _, out _); - Operation[] vpBOps = DecodeShader(vpBCode, callbacks, flags, out ShaderConfig config, out int sizeB); + Operation[] opsA = DecodeShader(addressA, gpuAccessor, flags, out _, out int sizeA); + Operation[] opsB = DecodeShader(addressB, gpuAccessor, flags, out ShaderConfig config, out int sizeB); - return Translate(Combine(vpAOps, vpBOps), config, sizeB); + return Translate(Combine(opsA, opsB), config, sizeB, sizeA); } - private static ShaderProgram Translate(Operation[] ops, ShaderConfig config, int size) + private static ShaderProgram Translate(Operation[] ops, ShaderConfig config, int size, int sizeA = 0) { BasicBlock[] blocks = ControlFlowGraph.MakeCfg(ops); @@ -83,34 +59,34 @@ namespace Ryujinx.Graphics.Shader.Translation string glslCode = program.Code; - return new ShaderProgram(spInfo, config.Stage, glslCode, size); + return new ShaderProgram(spInfo, config.Stage, glslCode, size, sizeA); } private static Operation[] DecodeShader( - ReadOnlySpan code, - TranslatorCallbacks callbacks, - TranslationFlags flags, - out ShaderConfig config, - out int size) + ulong address, + IGpuAccessor gpuAccessor, + TranslationFlags flags, + out ShaderConfig config, + out int size) { Block[] cfg; if ((flags & TranslationFlags.Compute) != 0) { - config = new ShaderConfig(flags, callbacks); + config = new ShaderConfig(gpuAccessor, flags); - cfg = Decoder.Decode(code, 0); + cfg = Decoder.Decode(gpuAccessor, address); } else { - config = new ShaderConfig(new ShaderHeader(code), flags, callbacks); + config = new ShaderConfig(new ShaderHeader(gpuAccessor, address), gpuAccessor, flags); - cfg = Decoder.Decode(code, HeaderSize); + cfg = Decoder.Decode(gpuAccessor, address + HeaderSize); } if (cfg == null) { - config.PrintLog("Invalid branch detected, failed to build CFG."); + gpuAccessor.Log("Invalid branch detected, failed to build CFG."); size = 0; @@ -150,7 +126,7 @@ namespace Ryujinx.Graphics.Shader.Translation { instName = "???"; - config.PrintLog($"Invalid instruction at 0x{op.Address:X6} (0x{op.RawOpCode:X16})."); + gpuAccessor.Log($"Invalid instruction at 0x{op.Address:X6} (0x{op.RawOpCode:X16})."); } string dbgComment = $"0x{op.Address:X6}: 0x{op.RawOpCode:X16} {instName}"; diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorCallbacks.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorCallbacks.cs deleted file mode 100644 index e0e9852fa3..0000000000 --- a/Ryujinx.Graphics.Shader/Translation/TranslatorCallbacks.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Shader.Translation -{ - public struct TranslatorCallbacks - { - internal Func QueryInfo { get; } - - internal Action PrintLog { get; } - - public TranslatorCallbacks(Func queryInfoCallback, Action printLogCallback) - { - QueryInfo = queryInfoCallback; - PrintLog = printLogCallback; - } - } -} diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs index 25ac8d2af8..567083e485 100644 --- a/Ryujinx.ShaderTools/Program.cs +++ b/Ryujinx.ShaderTools/Program.cs @@ -2,11 +2,27 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.IO; +using System.Runtime.InteropServices; namespace Ryujinx.ShaderTools { class Program { + private class GpuAccessor : IGpuAccessor + { + private readonly byte[] _data; + + public GpuAccessor(byte[] data) + { + _data = data; + } + + public T MemoryRead(ulong address) where T : unmanaged + { + return MemoryMarshal.Cast(new ReadOnlySpan(_data).Slice((int)address))[0]; + } + } + static void Main(string[] args) { if (args.Length == 1 || args.Length == 2) @@ -20,7 +36,7 @@ namespace Ryujinx.ShaderTools byte[] data = File.ReadAllBytes(args[^1]); - string code = Translator.Translate(data, new TranslatorCallbacks(null, null), flags).Code; + string code = Translator.Translate(0, new GpuAccessor(data), flags).Code; Console.WriteLine(code); }