diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 3baf272f92..1da34b9671 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.GAL void SetIndexBuffer(BufferRange buffer, IndexType type); - void SetImage(int index, ShaderStage stage, ITexture texture, Format imageFormat); + void SetImage(int binding, ITexture texture, Format imageFormat); void SetLogicOpState(bool enable, LogicalOp op); @@ -64,19 +64,19 @@ namespace Ryujinx.Graphics.GAL void SetRenderTargetColorMasks(ReadOnlySpan componentMask); void SetRenderTargets(ITexture[] colors, ITexture depthStencil); - void SetSampler(int index, ShaderStage stage, ISampler sampler); + void SetSampler(int binding, ISampler sampler); void SetScissorEnable(int index, bool enable); void SetScissor(int index, int x, int y, int width, int height); void SetStencilTest(StencilTestDescriptor stencilTest); - void SetStorageBuffer(int index, ShaderStage stage, BufferRange buffer); + void SetStorageBuffers(ReadOnlySpan buffers); - void SetTexture(int index, ShaderStage stage, ITexture texture); + void SetTexture(int binding, ITexture texture); void SetTransformFeedbackBuffers(ReadOnlySpan buffers); - void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer); + void SetUniformBuffers(ReadOnlySpan buffers); void SetUserClipDistance(int index, bool enableClip); diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs index 9fc39b4be5..35c2146f2c 100644 --- a/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/Ryujinx.Graphics.GAL/IRenderer.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.GAL void BackgroundContextAction(Action action); - IShader CompileShader(ShaderProgram shader); + IShader CompileShader(ShaderStage stage, string code); BufferHandle CreateBuffer(int size); diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs index 1219ef0cfe..3ea98f3e48 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -28,9 +28,6 @@ namespace Ryujinx.Graphics.Gpu.Engine int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize); - uint sbEnableMask = 0; - uint ubEnableMask = 0; - for (int index = 0; index < Constants.TotalCpUniformBuffers; index++) { if (!qmd.ConstantBufferValid(index)) @@ -38,8 +35,6 @@ namespace Ryujinx.Graphics.Gpu.Engine continue; } - ubEnableMask |= 1u << index; - ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32; ulong size = (ulong)qmd.ConstantBufferSize(index); @@ -58,13 +53,10 @@ namespace Ryujinx.Graphics.Gpu.Engine _context.Renderer.Pipeline.SetProgram(cs.HostProgram); var samplerPool = state.Get(MethodOffset.SamplerPoolState); - - TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, qmd.SamplerIndex); - var texturePool = state.Get(MethodOffset.TexturePoolState); + TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, qmd.SamplerIndex); TextureManager.SetComputeTexturePool(texturePool.Address.Pack(), texturePool.MaximumId); - TextureManager.SetComputeTextureBufferIndex(state.Get(MethodOffset.TextureBufferIndex)); ShaderProgramInfo info = cs.Shaders[0].Program.Info; @@ -82,8 +74,6 @@ namespace Ryujinx.Graphics.Gpu.Engine continue; } - ubEnableMask |= 1u << cb.Slot; - ulong cbDescAddress = BufferManager.GetComputeUniformBufferAddress(0); int cbDescOffset = 0x260 + (cb.Slot - 8) * 0x10; @@ -99,8 +89,6 @@ namespace Ryujinx.Graphics.Gpu.Engine { BufferDescriptor sb = info.SBuffers[index]; - sbEnableMask |= 1u << sb.Slot; - ulong sbDescAddress = BufferManager.GetComputeUniformBufferAddress(0); int sbDescOffset = 0x310 + sb.Slot * 0x10; @@ -112,15 +100,8 @@ namespace Ryujinx.Graphics.Gpu.Engine BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size); } - ubEnableMask = 0; - - for (int index = 0; index < info.CBuffers.Count; index++) - { - ubEnableMask |= 1u << info.CBuffers[index].Slot; - } - - BufferManager.SetComputeStorageBufferEnableMask(sbEnableMask); - BufferManager.SetComputeUniformBufferEnableMask(ubEnableMask); + BufferManager.SetComputeStorageBufferBindings(info.SBuffers); + BufferManager.SetComputeUniformBufferBindings(info.CBuffers); var textureBindings = new TextureBindingInfo[info.Textures.Count]; @@ -132,11 +113,16 @@ namespace Ryujinx.Graphics.Gpu.Engine if (descriptor.IsBindless) { - textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufOffset, descriptor.CbufSlot, descriptor.Flags); + textureBindings[index] = new TextureBindingInfo( + target, + descriptor.Binding, + descriptor.CbufOffset, + descriptor.CbufSlot, + descriptor.Flags); } else { - textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex, descriptor.Flags); + textureBindings[index] = new TextureBindingInfo(target, descriptor.Binding, descriptor.HandleIndex, descriptor.Flags); } } @@ -151,7 +137,7 @@ namespace Ryujinx.Graphics.Gpu.Engine Target target = ShaderTexture.GetTarget(descriptor.Type); Format format = ShaderTexture.GetFormat(descriptor.Format); - imageBindings[index] = new TextureBindingInfo(target, format, descriptor.HandleIndex, descriptor.Flags); + imageBindings[index] = new TextureBindingInfo(target, format, descriptor.Binding, descriptor.HandleIndex, descriptor.Flags); } TextureManager.SetComputeImages(imageBindings); diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index d4988f356d..8bc2c22d0b 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -6,6 +6,7 @@ using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Shader; using System; +using System.Linq; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Engine @@ -997,6 +998,9 @@ namespace Ryujinx.Graphics.Gpu.Engine _vsUsesInstanceId = gs.Shaders[0]?.Program.Info.UsesInstanceId ?? false; + int storageBufferBindingsCount = 0; + int uniformBufferBindingsCount = 0; + for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgramInfo info = gs.Shaders[stage]?.Program.Info; @@ -1005,6 +1009,10 @@ namespace Ryujinx.Graphics.Gpu.Engine if (info == null) { + TextureManager.SetGraphicsTextures(stage, Array.Empty()); + TextureManager.SetGraphicsImages(stage, Array.Empty()); + BufferManager.SetGraphicsStorageBufferBindings(stage, null); + BufferManager.SetGraphicsUniformBufferBindings(stage, null); continue; } @@ -1018,11 +1026,11 @@ namespace Ryujinx.Graphics.Gpu.Engine if (descriptor.IsBindless) { - textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufSlot, descriptor.CbufOffset, descriptor.Flags); + textureBindings[index] = new TextureBindingInfo(target, descriptor.Binding, descriptor.CbufSlot, descriptor.CbufOffset, descriptor.Flags); } else { - textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex, descriptor.Flags); + textureBindings[index] = new TextureBindingInfo(target, descriptor.Binding, descriptor.HandleIndex, descriptor.Flags); } } @@ -1037,28 +1045,28 @@ namespace Ryujinx.Graphics.Gpu.Engine Target target = ShaderTexture.GetTarget(descriptor.Type); Format format = ShaderTexture.GetFormat(descriptor.Format); - imageBindings[index] = new TextureBindingInfo(target, format, descriptor.HandleIndex, descriptor.Flags); + imageBindings[index] = new TextureBindingInfo(target, format, descriptor.Binding, descriptor.HandleIndex, descriptor.Flags); } TextureManager.SetGraphicsImages(stage, imageBindings); - uint sbEnableMask = 0; - uint ubEnableMask = 0; + BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers); + BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers); - for (int index = 0; index < info.SBuffers.Count; index++) + if (info.SBuffers.Count != 0) { - sbEnableMask |= 1u << info.SBuffers[index].Slot; + storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1); } - for (int index = 0; index < info.CBuffers.Count; index++) + if (info.CBuffers.Count != 0) { - ubEnableMask |= 1u << info.CBuffers[index].Slot; + uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1); } - - BufferManager.SetGraphicsStorageBufferEnableMask(stage, sbEnableMask); - BufferManager.SetGraphicsUniformBufferEnableMask(stage, ubEnableMask); } + BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount); + BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount); + _context.Renderer.Pipeline.SetProgram(gs.HostProgram); } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs index 422b66e20a..a328fc2b69 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs @@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// public Format Format { get; } + /// + /// Shader texture host binding point. + /// + public int Binding { get; } + /// /// Shader texture handle. /// This is an index into the texture constant buffer. @@ -53,13 +58,15 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// The shader sampler target type /// Format of the image as declared on the shader + /// The shader texture binding point /// The shader texture handle (read index into the texture constant buffer) /// The texture's usage flags, indicating how it is used in the shader - public TextureBindingInfo(Target target, Format format, int handle, TextureUsageFlags flags) + public TextureBindingInfo(Target target, Format format, int binding, int handle, TextureUsageFlags flags) { - Target = target; - Format = format; - Handle = handle; + Target = target; + Format = format; + Binding = binding; + Handle = handle; IsBindless = false; @@ -73,9 +80,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// Constructs the texture binding information structure. /// /// The shader sampler target type + /// The shader texture binding point /// The shader texture handle (read index into the texture constant buffer) /// The texture's usage flags, indicating how it is used in the shader - public TextureBindingInfo(Target target, int handle, TextureUsageFlags flags) : this(target, (Format)0, handle, flags) + public TextureBindingInfo(Target target, int binding, int handle, TextureUsageFlags flags) : this(target, (Format)0, binding, handle, flags) { } @@ -83,14 +91,16 @@ namespace Ryujinx.Graphics.Gpu.Image /// Constructs the bindless texture binding information structure. /// /// The shader sampler target type + /// The shader texture binding point /// Constant buffer slot where the bindless texture handle is located /// Constant buffer offset of the bindless texture handle /// The texture's usage flags, indicating how it is used in the shader - public TextureBindingInfo(Target target, int cbufSlot, int cbufOffset, TextureUsageFlags flags) + public TextureBindingInfo(Target target, int binding, int cbufSlot, int cbufOffset, TextureUsageFlags flags) { - Target = target; - Format = 0; - Handle = 0; + Target = target; + Format = 0; + Binding = binding; + Handle = 0; IsBindless = true; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 08c4082ef0..bfb6da2513 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -265,11 +265,11 @@ namespace Ryujinx.Graphics.Gpu.Image for (int index = 0; index < _textureBindings[stageIndex].Length; index++) { - TextureBindingInfo binding = _textureBindings[stageIndex][index]; + TextureBindingInfo bindingInfo = _textureBindings[stageIndex][index]; int packedId; - if (binding.IsBindless) + if (bindingInfo.IsBindless) { ulong address; @@ -277,18 +277,18 @@ namespace Ryujinx.Graphics.Gpu.Image if (_isCompute) { - address = bufferManager.GetComputeUniformBufferAddress(binding.CbufSlot); + address = bufferManager.GetComputeUniformBufferAddress(bindingInfo.CbufSlot); } else { - address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, binding.CbufSlot); + address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, bindingInfo.CbufSlot); } - packedId = _context.PhysicalMemory.Read(address + (ulong)binding.CbufOffset * 4); + packedId = _context.PhysicalMemory.Read(address + (ulong)bindingInfo.CbufOffset * 4); } else { - packedId = ReadPackedId(stageIndex, binding.Handle, _textureBufferIndex); + packedId = ReadPackedId(stageIndex, bindingInfo.Handle, _textureBufferIndex); } int textureId = UnpackTextureId(packedId); @@ -305,18 +305,18 @@ namespace Ryujinx.Graphics.Gpu.Image Texture texture = pool.Get(textureId); - ITexture hostTexture = texture?.GetTargetTexture(binding.Target); + ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); if (_textureState[stageIndex][index].Texture != hostTexture || _rebind) { - if (UpdateScale(texture, binding, index, stage)) + if (UpdateScale(texture, bindingInfo, index, stage)) { - hostTexture = texture?.GetTargetTexture(binding.Target); + hostTexture = texture?.GetTargetTexture(bindingInfo.Target); } _textureState[stageIndex][index].Texture = hostTexture; - _context.Renderer.Pipeline.SetTexture(index, stage, hostTexture); + _context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture); } if (hostTexture != null && texture.Info.Target == Target.TextureBuffer) @@ -335,7 +335,7 @@ namespace Ryujinx.Graphics.Gpu.Image { _textureState[stageIndex][index].Sampler = hostSampler; - _context.Renderer.Pipeline.SetSampler(index, stage, hostSampler); + _context.Renderer.Pipeline.SetSampler(bindingInfo.Binding, hostSampler); } } } @@ -359,14 +359,14 @@ namespace Ryujinx.Graphics.Gpu.Image for (int index = 0; index < _imageBindings[stageIndex].Length; index++) { - TextureBindingInfo binding = _imageBindings[stageIndex][index]; + TextureBindingInfo bindingInfo = _imageBindings[stageIndex][index]; - int packedId = ReadPackedId(stageIndex, binding.Handle, _textureBufferIndex); + int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, _textureBufferIndex); int textureId = UnpackTextureId(packedId); Texture texture = pool.Get(textureId); - ITexture hostTexture = texture?.GetTargetTexture(binding.Target); + ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); if (hostTexture != null && texture.Info.Target == Target.TextureBuffer) { @@ -378,21 +378,21 @@ namespace Ryujinx.Graphics.Gpu.Image if (_imageState[stageIndex][index].Texture != hostTexture || _rebind) { - if (UpdateScale(texture, binding, baseScaleIndex + index, stage)) + if (UpdateScale(texture, bindingInfo, baseScaleIndex + index, stage)) { - hostTexture = texture?.GetTargetTexture(binding.Target); + hostTexture = texture?.GetTargetTexture(bindingInfo.Target); } _imageState[stageIndex][index].Texture = hostTexture; - Format format = binding.Format; + Format format = bindingInfo.Format; if (format == 0 && texture != null) { format = texture.Format; } - _context.Renderer.Pipeline.SetImage(index, stage, hostTexture, format); + _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format); } } } diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs index 42500342fc..060171fb08 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs @@ -5,7 +5,25 @@ namespace Ryujinx.Graphics.Gpu.Memory /// struct BufferBounds { - public ulong Address; - public ulong Size; + /// + /// Region virtual address. + /// + public ulong Address { get; } + + /// + /// Region size in bytes. + /// + public ulong Size { get; } + + /// + /// Creates a new buffer region. + /// + /// Region address + /// Region size + public BufferBounds(ulong address, ulong size) + { + Address = address; + Size = size; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index eec545b917..568133ca54 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -4,6 +4,8 @@ using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Shader; using Ryujinx.Memory.Range; using System; +using System.Collections.ObjectModel; +using System.Linq; namespace Ryujinx.Graphics.Gpu.Memory { @@ -12,6 +14,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// class BufferManager { + private const int StackToHeapThreshold = 16; + private const int OverlapsBufferInitialCapacity = 10; private const int OverlapsBufferMaxCapacity = 10000; @@ -28,21 +32,61 @@ namespace Ryujinx.Graphics.Gpu.Memory private VertexBuffer[] _vertexBuffers; private BufferBounds[] _transformFeedbackBuffers; + /// + /// Holds shader stage buffer state and binding information. + /// private class BuffersPerStage { - public uint EnableMask { get; set; } + /// + /// Shader buffer binding information. + /// + public BufferDescriptor[] Bindings { get; } + /// + /// Buffer regions. + /// public BufferBounds[] Buffers { get; } + /// + /// Total amount of buffers used on the shader. + /// + public int Count { get; private set; } + + /// + /// Creates a new instance of the shader stage buffer information. + /// + /// Maximum amount of buffers that the shader stage can use public BuffersPerStage(int count) { + Bindings = new BufferDescriptor[count]; Buffers = new BufferBounds[count]; } - public void Bind(int index, ulong address, ulong size) + /// + /// Sets the region of a buffer at a given slot. + /// + /// Buffer slot + /// Region virtual address + /// Region size in bytes + public void SetBounds(int index, ulong address, ulong size) { - Buffers[index].Address = address; - Buffers[index].Size = size; + Buffers[index] = new BufferBounds(address, size); + } + + /// + /// Sets shader buffer binding information. + /// + /// Buffer binding information + public void SetBindings(ReadOnlyCollection descriptors) + { + if (descriptors == null) + { + Count = 0; + return; + } + + descriptors.CopyTo(Bindings, 0); + Count = descriptors.Count; } } @@ -51,6 +95,11 @@ namespace Ryujinx.Graphics.Gpu.Memory private BuffersPerStage[] _gpStorageBuffers; private BuffersPerStage[] _gpUniformBuffers; + private int _cpStorageBufferBindings; + private int _cpUniformBufferBindings; + private int _gpStorageBufferBindings; + private int _gpUniformBufferBindings; + private bool _gpStorageBuffersDirty; private bool _gpUniformBuffersDirty; @@ -159,9 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { ulong address = TranslateAndCreateBuffer(gpuVa, size); - _transformFeedbackBuffers[index].Address = address; - _transformFeedbackBuffers[index].Size = size; - + _transformFeedbackBuffers[index] = new BufferBounds(address, size); _transformFeedbackBuffersDirty = true; } @@ -180,7 +227,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong address = TranslateAndCreateBuffer(gpuVa, size); - _cpStorageBuffers.Bind(index, address, size); + _cpStorageBuffers.SetBounds(index, address, size); } /// @@ -205,7 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _gpStorageBuffersDirty = true; } - _gpStorageBuffers[stage].Bind(index, address, size); + _gpStorageBuffers[stage].SetBounds(index, address, size); } /// @@ -219,7 +266,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { ulong address = TranslateAndCreateBuffer(gpuVa, size); - _cpUniformBuffers.Bind(index, address, size); + _cpUniformBuffers.SetBounds(index, address, size); } /// @@ -234,42 +281,69 @@ namespace Ryujinx.Graphics.Gpu.Memory { ulong address = TranslateAndCreateBuffer(gpuVa, size); - _gpUniformBuffers[stage].Bind(index, address, size); - + _gpUniformBuffers[stage].SetBounds(index, address, size); _gpUniformBuffersDirty = true; } /// - /// Sets the enabled storage buffers mask on the compute pipeline. - /// Each bit set on the mask indicates that the respective buffer index is enabled. + /// Sets the binding points for the storage buffers bound on the compute pipeline. /// - /// Buffer enable mask - public void SetComputeStorageBufferEnableMask(uint mask) + /// Buffer descriptors with the binding point values + public void SetComputeStorageBufferBindings(ReadOnlyCollection descriptors) { - _cpStorageBuffers.EnableMask = mask; + _cpStorageBuffers.SetBindings(descriptors); + _cpStorageBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0; } /// - /// Sets the enabled storage buffers mask on the graphics pipeline. - /// Each bit set on the mask indicates that the respective buffer index is enabled. + /// Sets the binding points for the storage buffers bound on the graphics pipeline. /// /// Index of the shader stage - /// Buffer enable mask - public void SetGraphicsStorageBufferEnableMask(int stage, uint mask) + /// Buffer descriptors with the binding point values + public void SetGraphicsStorageBufferBindings(int stage, ReadOnlyCollection descriptors) { - _gpStorageBuffers[stage].EnableMask = mask; - + _gpStorageBuffers[stage].SetBindings(descriptors); _gpStorageBuffersDirty = true; } /// - /// Sets the enabled uniform buffers mask on the compute pipeline. + /// Sets the total number of storage buffer bindings used. + /// + /// Number of storage buffer bindings used + public void SetGraphicsStorageBufferBindingsCount(int count) + { + _gpStorageBufferBindings = count; + } + + /// + /// Sets the binding points for the uniform buffers bound on the compute pipeline. + /// + /// Buffer descriptors with the binding point values + public void SetComputeUniformBufferBindings(ReadOnlyCollection descriptors) + { + _cpUniformBuffers.SetBindings(descriptors); + _cpUniformBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0; + } + + /// + /// Sets the enabled uniform buffers mask on the graphics pipeline. /// Each bit set on the mask indicates that the respective buffer index is enabled. /// - /// Buffer enable mask - public void SetComputeUniformBufferEnableMask(uint mask) + /// Index of the shader stage + /// Buffer descriptors with the binding point values + public void SetGraphicsUniformBufferBindings(int stage, ReadOnlyCollection descriptors) { - _cpUniformBuffers.EnableMask = mask; + _gpUniformBuffers[stage].SetBindings(descriptors); + _gpUniformBuffersDirty = true; + } + + /// + /// Sets the total number of uniform buffer bindings used. + /// + /// Number of uniform buffer bindings used + public void SetGraphicsUniformBufferBindingsCount(int count) + { + _gpUniformBufferBindings = count; } /// @@ -291,19 +365,6 @@ namespace Ryujinx.Graphics.Gpu.Memory return mask; } - /// - /// Sets the enabled uniform buffers mask on the graphics pipeline. - /// Each bit set on the mask indicates that the respective buffer index is enabled. - /// - /// Index of the shader stage - /// Buffer enable mask - public void SetGraphicsUniformBufferEnableMask(int stage, uint mask) - { - _gpUniformBuffers[stage].EnableMask = mask; - - _gpUniformBuffersDirty = true; - } - /// /// Gets a bit mask indicating which graphics uniform buffers are currently bound. /// @@ -476,48 +537,42 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public void CommitComputeBindings() { - uint enableMask = _cpStorageBuffers.EnableMask; + int sCount = _cpStorageBufferBindings; - for (int index = 0; (enableMask >> index) != 0; index++) + Span sRanges = sCount < StackToHeapThreshold ? stackalloc BufferRange[sCount] : new BufferRange[sCount]; + + for (int index = 0; index < _cpStorageBuffers.Count; index++) { - if ((enableMask & (1u << index)) == 0) + ref var bindingInfo = ref _cpStorageBuffers.Bindings[index]; + + BufferBounds bounds = _cpStorageBuffers.Buffers[bindingInfo.Slot]; + + if (bounds.Address != 0) { - continue; + sRanges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size); } - - BufferBounds bounds = _cpStorageBuffers.Buffers[index]; - - if (bounds.Address == 0) - { - continue; - } - - BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size); - - _context.Renderer.Pipeline.SetStorageBuffer(index, ShaderStage.Compute, buffer); } - enableMask = _cpUniformBuffers.EnableMask; + _context.Renderer.Pipeline.SetStorageBuffers(sRanges); - for (int index = 0; (enableMask >> index) != 0; index++) + int uCount = _cpUniformBufferBindings; + + Span uRanges = uCount < StackToHeapThreshold ? stackalloc BufferRange[uCount] : new BufferRange[uCount]; + + for (int index = 0; index < _cpUniformBuffers.Count; index++) { - if ((enableMask & (1u << index)) == 0) + ref var bindingInfo = ref _cpUniformBuffers.Bindings[index]; + + BufferBounds bounds = _cpUniformBuffers.Buffers[bindingInfo.Slot]; + + if (bounds.Address != 0) { - continue; + uRanges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size); } - - BufferBounds bounds = _cpUniformBuffers.Buffers[index]; - - if (bounds.Address == 0) - { - continue; - } - - BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size); - - _context.Renderer.Pipeline.SetUniformBuffer(index, ShaderStage.Compute, buffer); } + _context.Renderer.Pipeline.SetUniformBuffers(uRanges); + // Force rebind after doing compute work. _rebind = true; } @@ -651,7 +706,35 @@ namespace Ryujinx.Graphics.Gpu.Memory /// True to bind as storage buffer, false to bind as uniform buffers private void BindBuffers(BuffersPerStage[] bindings, bool isStorage) { - BindOrUpdateBuffers(bindings, bind: true, isStorage); + int count = isStorage ? _gpStorageBufferBindings : _gpUniformBufferBindings; + + Span ranges = count < StackToHeapThreshold ? stackalloc BufferRange[count] : new BufferRange[count]; + + for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++) + { + ref var buffers = ref bindings[(int)stage - 1]; + + for (int index = 0; index < buffers.Count; index++) + { + ref var bindingInfo = ref buffers.Bindings[index]; + + BufferBounds bounds = buffers.Buffers[bindingInfo.Slot]; + + if (bounds.Address != 0) + { + ranges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size); + } + } + } + + if (isStorage) + { + _context.Renderer.Pipeline.SetStorageBuffers(ranges); + } + else + { + _context.Renderer.Pipeline.SetUniformBuffers(ranges); + } } /// @@ -659,74 +742,27 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Bindings to update private void UpdateBuffers(BuffersPerStage[] bindings) - { - BindOrUpdateBuffers(bindings, bind: false); - } - - /// - /// This binds buffers into the host API, or updates data for already bound buffers. - /// - /// Bindings to bind or update - /// True to bind, false to update - /// True to bind as storage buffer, false to bind as uniform buffer - private void BindOrUpdateBuffers(BuffersPerStage[] bindings, bool bind, bool isStorage = false) { for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++) { - uint enableMask = bindings[(int)stage - 1].EnableMask; + ref var buffers = ref bindings[(int)stage - 1]; - if (enableMask == 0) + for (int index = 0; index < buffers.Count; index++) { - continue; - } + ref var binding = ref buffers.Bindings[index]; - for (int index = 0; (enableMask >> index) != 0; index++) - { - if ((enableMask & (1u << index)) == 0) - { - continue; - } - - BufferBounds bounds = bindings[(int)stage - 1].Buffers[index]; + BufferBounds bounds = buffers.Buffers[binding.Slot]; if (bounds.Address == 0) { continue; } - if (bind) - { - BindBuffer(index, stage, bounds, isStorage); - } - else - { - SynchronizeBufferRange(bounds.Address, bounds.Size); - } + SynchronizeBufferRange(bounds.Address, bounds.Size); } } } - /// - /// Binds a buffer on the host API. - /// - /// Index to bind the buffer into - /// Shader stage to bind the buffer into - /// Buffer address and size - /// True to bind as storage buffer, false to bind as uniform buffer - private void BindBuffer(int index, ShaderStage stage, BufferBounds bounds, bool isStorage) - { - BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size); - - if (isStorage) - { - _context.Renderer.Pipeline.SetStorageBuffer(index, stage, buffer); - } - else - { - _context.Renderer.Pipeline.SetUniformBuffer(index, stage, buffer); - } - } - /// /// Sets the buffer storage of a buffer texture. /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 853351dbac..131aa6b7f5 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Gpu.Shader localMemorySize, sharedMemorySize); - shader.HostShader = _context.Renderer.CompileShader(shader.Program); + shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code); IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null); @@ -134,19 +134,21 @@ namespace Ryujinx.Graphics.Gpu.Shader flags |= TranslationFlags.Feedback; } + TranslationCounts counts = new TranslationCounts(); + if (addresses.VertexA != 0) { - shaders[0] = TranslateGraphicsShader(state, flags, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA); + shaders[0] = TranslateGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA); } else { - shaders[0] = TranslateGraphicsShader(state, flags, ShaderStage.Vertex, addresses.Vertex); + shaders[0] = TranslateGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex); } - shaders[1] = TranslateGraphicsShader(state, flags, ShaderStage.TessellationControl, addresses.TessControl); - shaders[2] = TranslateGraphicsShader(state, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); - shaders[3] = TranslateGraphicsShader(state, flags, ShaderStage.Geometry, addresses.Geometry); - shaders[4] = TranslateGraphicsShader(state, flags, ShaderStage.Fragment, addresses.Fragment); + shaders[1] = TranslateGraphicsShader(state, counts, flags, ShaderStage.TessellationControl, addresses.TessControl); + shaders[2] = TranslateGraphicsShader(state, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); + shaders[3] = TranslateGraphicsShader(state, counts, flags, ShaderStage.Geometry, addresses.Geometry); + shaders[4] = TranslateGraphicsShader(state, counts, flags, ShaderStage.Fragment, addresses.Fragment); List hostShaders = new List(); @@ -159,7 +161,7 @@ namespace Ryujinx.Graphics.Gpu.Shader continue; } - IShader hostShader = _context.Renderer.CompileShader(program); + IShader hostShader = _context.Renderer.CompileShader(program.Stage, program.Code); shaders[stage].HostShader = hostShader; @@ -334,12 +336,19 @@ namespace Ryujinx.Graphics.Gpu.Shader /// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader. /// /// Current GPU state + /// Cumulative shader resource counts /// Flags that controls shader translation /// Shader stage /// GPU virtual address of the shader code /// Optional GPU virtual address of the "Vertex A" shader code /// Compiled graphics shader code - private ShaderCodeHolder TranslateGraphicsShader(GpuState state, TranslationFlags flags, ShaderStage stage, ulong gpuVa, ulong gpuVaA = 0) + private ShaderCodeHolder TranslateGraphicsShader( + GpuState state, + TranslationCounts counts, + TranslationFlags flags, + ShaderStage stage, + ulong gpuVa, + ulong gpuVaA = 0) { if (gpuVa == 0) { @@ -350,7 +359,7 @@ namespace Ryujinx.Graphics.Gpu.Shader if (gpuVaA != 0) { - ShaderProgram program = Translator.Translate(gpuVaA, gpuVa, gpuAccessor, flags); + ShaderProgram program = Translator.Translate(gpuVaA, gpuVa, gpuAccessor, flags, counts); byte[] codeA = _context.MemoryManager.GetSpan(gpuVaA, program.SizeA).ToArray(); byte[] codeB = _context.MemoryManager.GetSpan(gpuVa, program.Size).ToArray(); @@ -370,7 +379,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } else { - ShaderProgram program = Translator.Translate(gpuVa, gpuAccessor, flags); + ShaderProgram program = Translator.Translate(gpuVa, gpuAccessor, flags, counts); byte[] code = _context.MemoryManager.GetSpan(gpuVa, program.Size).ToArray(); diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 06cf5ef420..5687329102 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -697,20 +697,20 @@ namespace Ryujinx.Graphics.OpenGL SetFrontFace(_frontFace = frontFace.Convert()); } - public void SetImage(int index, ShaderStage stage, ITexture texture, Format imageFormat) + public void SetImage(int binding, ITexture texture, Format imageFormat) { - int unit = _program.GetImageUnit(stage, index); - - if (unit != -1 && texture != null) + if (texture == null) { - TextureBase texBase = (TextureBase)texture; + return; + } - SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat); + TextureBase texBase = (TextureBase)texture; - if (format != 0) - { - GL.BindImageTexture(unit, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format); - } + SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat); + + if (format != 0) + { + GL.BindImageTexture(binding, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format); } } @@ -866,14 +866,14 @@ namespace Ryujinx.Graphics.OpenGL UpdateDepthTest(); } - public void SetSampler(int index, ShaderStage stage, ISampler sampler) + public void SetSampler(int binding, ISampler sampler) { - int unit = _program.GetTextureUnit(stage, index); - - if (unit != -1 && sampler != null) + if (sampler == null) { - ((Sampler)sampler).Bind(unit); + return; } + + ((Sampler)sampler).Bind(binding); } public void SetScissorEnable(int index, bool enable) @@ -939,25 +939,25 @@ namespace Ryujinx.Graphics.OpenGL _stencilFrontMask = stencilTest.FrontMask; } - public void SetStorageBuffer(int index, ShaderStage stage, BufferRange buffer) + public void SetStorageBuffers(ReadOnlySpan buffers) { - SetBuffer(index, stage, buffer, isStorage: true); + SetBuffers(buffers, isStorage: true); } - public void SetTexture(int index, ShaderStage stage, ITexture texture) + public void SetTexture(int binding, ITexture texture) { - int unit = _program.GetTextureUnit(stage, index); - - if (unit != -1 && texture != null) + if (texture == null) { - if (unit == 0) - { - _unit0Texture = (TextureBase)texture; - } - else - { - ((TextureBase)texture).Bind(unit); - } + return; + } + + if (binding == 0) + { + _unit0Texture = (TextureBase)texture; + } + else + { + ((TextureBase)texture).Bind(binding); } } @@ -997,9 +997,9 @@ namespace Ryujinx.Graphics.OpenGL } } - public void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer) + public void SetUniformBuffers(ReadOnlySpan buffers) { - SetBuffer(index, stage, buffer, isStorage: false); + SetBuffers(buffers, isStorage: false); } public void SetUserClipDistance(int index, bool enableClip) @@ -1077,30 +1077,22 @@ namespace Ryujinx.Graphics.OpenGL GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit); } - private void SetBuffer(int index, ShaderStage stage, BufferRange buffer, bool isStorage) + private void SetBuffers(ReadOnlySpan buffers, bool isStorage) { - int bindingPoint = isStorage - ? _program.GetStorageBufferBindingPoint(stage, index) - : _program.GetUniformBufferBindingPoint(stage, index); + BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer; - if (bindingPoint == -1) + for (int index = 0; index < buffers.Length; index++) { - return; + BufferRange buffer = buffers[index]; + + if (buffer.Handle == BufferHandle.Null) + { + GL.BindBufferRange(target, index, 0, IntPtr.Zero, 0); + continue; + } + + GL.BindBufferRange(target, index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size); } - - BufferRangeTarget target = isStorage - ? BufferRangeTarget.ShaderStorageBuffer - : BufferRangeTarget.UniformBuffer; - - if (buffer.Handle == BufferHandle.Null) - { - GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0); - return; - } - - IntPtr bufferOffset = (IntPtr)buffer.Offset; - - GL.BindBufferRange(target, bindingPoint, buffer.Handle.ToInt32(), bufferOffset, buffer.Size); } private void SetOrigin(ClipOrigin origin) diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/Ryujinx.Graphics.OpenGL/Program.cs index babe17a0d7..17e14df6db 100644 --- a/Ryujinx.Graphics.OpenGL/Program.cs +++ b/Ryujinx.Graphics.OpenGL/Program.cs @@ -1,7 +1,6 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.CodeGen.Glsl; using System; using System.Collections.Generic; @@ -11,18 +10,6 @@ namespace Ryujinx.Graphics.OpenGL { class Program : IProgram { - private const int ShaderStages = 6; - - private const int UbStageShift = 5; - private const int SbStageShift = 4; - private const int TexStageShift = 5; - private const int ImgStageShift = 3; - - private const int UbsPerStage = 1 << UbStageShift; - private const int SbsPerStage = 1 << SbStageShift; - private const int TexsPerStage = 1 << TexStageShift; - private const int ImgsPerStage = 1 << ImgStageShift; - public int Handle { get; private set; } public int FragmentIsBgraUniform { get; } @@ -31,38 +18,8 @@ namespace Ryujinx.Graphics.OpenGL public bool IsLinked { get; private set; } - private int[] _ubBindingPoints; - private int[] _sbBindingPoints; - private int[] _textureUnits; - private int[] _imageUnits; - public Program(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors) { - _ubBindingPoints = new int[UbsPerStage * ShaderStages]; - _sbBindingPoints = new int[SbsPerStage * ShaderStages]; - _textureUnits = new int[TexsPerStage * ShaderStages]; - _imageUnits = new int[ImgsPerStage * ShaderStages]; - - for (int index = 0; index < _ubBindingPoints.Length; index++) - { - _ubBindingPoints[index] = -1; - } - - for (int index = 0; index < _sbBindingPoints.Length; index++) - { - _sbBindingPoints[index] = -1; - } - - for (int index = 0; index < _textureUnits.Length; index++) - { - _textureUnits[index] = -1; - } - - for (int index = 0; index < _imageUnits.Length; index++) - { - _imageUnits[index] = -1; - } - Handle = GL.CreateProgram(); for (int index = 0; index < shaders.Length; index++) @@ -131,92 +88,6 @@ namespace Ryujinx.Graphics.OpenGL CheckProgramLink(); - int ubBindingPoint = 0; - int sbBindingPoint = 0; - int textureUnit = 0; - int imageUnit = 0; - - for (int index = 0; index < shaders.Length; index++) - { - Shader shader = (Shader)shaders[index]; - - foreach (BufferDescriptor descriptor in shader.Info.CBuffers) - { - int location = GL.GetUniformBlockIndex(Handle, descriptor.Name); - - if (location < 0) - { - continue; - } - - GL.UniformBlockBinding(Handle, location, ubBindingPoint); - - int bpIndex = (int)shader.Stage << UbStageShift | descriptor.Slot; - - _ubBindingPoints[bpIndex] = ubBindingPoint; - - ubBindingPoint++; - } - - foreach (BufferDescriptor descriptor in shader.Info.SBuffers) - { - int location = GL.GetProgramResourceIndex(Handle, ProgramInterface.ShaderStorageBlock, descriptor.Name); - - if (location < 0) - { - continue; - } - - GL.ShaderStorageBlockBinding(Handle, location, sbBindingPoint); - - int bpIndex = (int)shader.Stage << SbStageShift | descriptor.Slot; - - _sbBindingPoints[bpIndex] = sbBindingPoint; - - sbBindingPoint++; - } - - int samplerIndex = 0; - - foreach (TextureDescriptor descriptor in shader.Info.Textures) - { - int location = GL.GetUniformLocation(Handle, descriptor.Name); - - if (location < 0) - { - continue; - } - - GL.ProgramUniform1(Handle, location, textureUnit); - - int uIndex = (int)shader.Stage << TexStageShift | samplerIndex++; - - _textureUnits[uIndex] = textureUnit; - - textureUnit++; - } - - int imageIndex = 0; - - foreach (TextureDescriptor descriptor in shader.Info.Images) - { - int location = GL.GetUniformLocation(Handle, descriptor.Name); - - if (location < 0) - { - continue; - } - - GL.ProgramUniform1(Handle, location, imageUnit); - - int uIndex = (int)shader.Stage << ImgStageShift | imageIndex++; - - _imageUnits[uIndex] = imageUnit; - - imageUnit++; - } - } - FragmentIsBgraUniform = GL.GetUniformLocation(Handle, "is_bgra"); FragmentRenderScaleUniform = GL.GetUniformLocation(Handle, "fp_renderScale"); ComputeRenderScaleUniform = GL.GetUniformLocation(Handle, "cp_renderScale"); @@ -227,26 +98,6 @@ namespace Ryujinx.Graphics.OpenGL GL.UseProgram(Handle); } - public int GetUniformBufferBindingPoint(ShaderStage stage, int index) - { - return _ubBindingPoints[(int)stage << UbStageShift | index]; - } - - public int GetStorageBufferBindingPoint(ShaderStage stage, int index) - { - return _sbBindingPoints[(int)stage << SbStageShift | index]; - } - - public int GetTextureUnit(ShaderStage stage, int index) - { - return _textureUnits[(int)stage << TexStageShift | index]; - } - - public int GetImageUnit(ShaderStage stage, int index) - { - return _imageUnits[(int)stage << ImgStageShift | index]; - } - private void CheckProgramLink() { GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out int status); diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs index aac3c69e4a..75bcda122a 100644 --- a/Ryujinx.Graphics.OpenGL/Renderer.cs +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -42,9 +42,9 @@ namespace Ryujinx.Graphics.OpenGL ResourcePool = new ResourcePool(); } - public IShader CompileShader(ShaderProgram shader) + public IShader CompileShader(ShaderStage stage, string code) { - return new Shader(shader); + return new Shader(stage, code); } public BufferHandle CreateBuffer(int size) diff --git a/Ryujinx.Graphics.OpenGL/Shader.cs b/Ryujinx.Graphics.OpenGL/Shader.cs index f25845cfd4..1df07ee42d 100644 --- a/Ryujinx.Graphics.OpenGL/Shader.cs +++ b/Ryujinx.Graphics.OpenGL/Shader.cs @@ -8,31 +8,22 @@ namespace Ryujinx.Graphics.OpenGL { public int Handle { get; private set; } - private ShaderProgram _program; - - public ShaderProgramInfo Info => _program.Info; - - public ShaderStage Stage => _program.Stage; - - public Shader(ShaderProgram program) + public Shader(ShaderStage stage, string code) { - _program = program; - - ShaderType type = ShaderType.VertexShader; - - switch (program.Stage) + ShaderType type = stage switch { - case ShaderStage.Compute: type = ShaderType.ComputeShader; break; - case ShaderStage.Vertex: type = ShaderType.VertexShader; break; - case ShaderStage.TessellationControl: type = ShaderType.TessControlShader; break; - case ShaderStage.TessellationEvaluation: type = ShaderType.TessEvaluationShader; break; - case ShaderStage.Geometry: type = ShaderType.GeometryShader; break; - case ShaderStage.Fragment: type = ShaderType.FragmentShader; break; - } + ShaderStage.Compute => ShaderType.ComputeShader, + ShaderStage.Vertex => ShaderType.VertexShader, + ShaderStage.TessellationControl => ShaderType.TessControlShader, + ShaderStage.TessellationEvaluation => ShaderType.TessEvaluationShader, + ShaderStage.Geometry => ShaderType.GeometryShader, + ShaderStage.Fragment => ShaderType.FragmentShader, + _ => ShaderType.VertexShader + }; Handle = GL.CreateShader(type); - GL.ShaderSource(Handle, program.Code); + GL.ShaderSource(Handle, code); GL.CompileShader(Handle); } diff --git a/Ryujinx.Graphics.Shader/BufferDescriptor.cs b/Ryujinx.Graphics.Shader/BufferDescriptor.cs index 4cc999989e..99855518f2 100644 --- a/Ryujinx.Graphics.Shader/BufferDescriptor.cs +++ b/Ryujinx.Graphics.Shader/BufferDescriptor.cs @@ -2,13 +2,12 @@ namespace Ryujinx.Graphics.Shader { public struct BufferDescriptor { - public string Name { get; } - + public int Binding { get; } public int Slot { get; } - public BufferDescriptor(string name, int slot) + public BufferDescriptor(int binding, int slot) { - Name = name; + Binding = binding; Slot = slot; } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 734546a0ee..56a0126412 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -218,50 +218,46 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (info.UsesCbIndexing) { + int count = info.CBuffers.Max() + 1; + + int[] bindings = new int[count]; + + for (int i = 0; i < count; i++) + { + bindings[i] = context.Config.Counts.IncrementUniformBuffersCount(); + } + + foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) + { + context.CBufferDescriptors.Add(new BufferDescriptor(bindings[cbufSlot], cbufSlot)); + } + string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage); ubName += "_" + DefaultNames.UniformNamePrefix; string blockName = $"{ubName}_{DefaultNames.BlockSuffix}"; - int maxSlot = 0; - - foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) - { - context.CBufferDescriptors.Add(new BufferDescriptor($"{blockName}[{cbufSlot}]", cbufSlot)); - - if (maxSlot < cbufSlot) - { - maxSlot = cbufSlot; - } - } - - context.AppendLine("layout (std140) uniform " + blockName); - + context.AppendLine($"layout (binding = {bindings[0]}, std140) uniform {blockName}"); context.EnterScope(); - context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";"); - - string arraySize = NumberFormatter.FormatInt(maxSlot + 1); - - context.LeaveScope($" {ubName}[{arraySize}];"); + context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(count)}];"); } else { foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) { + int binding = context.Config.Counts.IncrementUniformBuffersCount(); + + context.CBufferDescriptors.Add(new BufferDescriptor(binding, cbufSlot)); + string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage); ubName += "_" + DefaultNames.UniformNamePrefix + cbufSlot; - context.CBufferDescriptors.Add(new BufferDescriptor(ubName, cbufSlot)); - - context.AppendLine("layout (std140) uniform " + ubName); - + context.AppendLine($"layout (binding = {binding}, std140) uniform {ubName}"); context.EnterScope(); - context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, cbufSlot, false) + ubSize + ";"); - context.LeaveScope(";"); } } @@ -275,32 +271,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string blockName = $"{sbName}_{DefaultNames.BlockSuffix}"; - int maxSlot = 0; + int count = info.SBuffers.Max() + 1; + + int[] bindings = new int[count]; + + for (int i = 0; i < count; i++) + { + bindings[i] = context.Config.Counts.IncrementStorageBuffersCount(); + } foreach (int sbufSlot in info.SBuffers) { - context.SBufferDescriptors.Add(new BufferDescriptor($"{blockName}[{sbufSlot}]", sbufSlot)); - - if (maxSlot < sbufSlot) - { - maxSlot = sbufSlot; - } + context.SBufferDescriptors.Add(new BufferDescriptor(bindings[sbufSlot], sbufSlot)); } - context.AppendLine("layout (std430) buffer " + blockName); - + context.AppendLine($"layout (binding = {bindings[0]}, std430) buffer {blockName}"); context.EnterScope(); - context.AppendLine("uint " + DefaultNames.DataName + "[];"); - - string arraySize = NumberFormatter.FormatInt(maxSlot + 1); - - context.LeaveScope($" {sbName}[{arraySize}];"); + context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(count)}];"); } private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo info) { - Dictionary samplers = new Dictionary(); + HashSet samplers = new HashSet(); // Texture instructions other than TextureSample (like TextureSize) // may have incomplete sampler type information. In those cases, @@ -312,29 +305,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); - if (!samplers.TryAdd(samplerName, texOp)) + if (!samplers.Add(samplerName)) { continue; } - string samplerTypeName = texOp.Type.ToGlslSamplerType(); - - context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";"); - } - - foreach (KeyValuePair kv in samplers) - { - string samplerName = kv.Key; - - AstTextureOperation texOp = kv.Value; - - TextureDescriptor desc; + int firstBinding = -1; if ((texOp.Flags & TextureFlags.Bindless) != 0) { AstOperand operand = texOp.GetSource(0) as AstOperand; - desc = new TextureDescriptor(samplerName, texOp.Type, operand.CbufSlot, operand.CbufOffset); + firstBinding = context.Config.Counts.IncrementTexturesCount(); + + var desc = new TextureDescriptor(firstBinding, texOp.Type, operand.CbufSlot, operand.CbufOffset); context.TextureDescriptors.Add(desc); } @@ -342,27 +326,36 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { for (int index = 0; index < texOp.ArraySize; index++) { - string indexExpr = NumberFormatter.FormatInt(index); + int binding = context.Config.Counts.IncrementTexturesCount(); - string indexedSamplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); + if (firstBinding < 0) + { + firstBinding = binding; + } - desc = new TextureDescriptor(indexedSamplerName, texOp.Type, texOp.Format, texOp.Handle + index * 2); + var desc = new TextureDescriptor(binding, texOp.Type, texOp.Format, texOp.Handle + index * 2); context.TextureDescriptors.Add(desc); } } else { - desc = new TextureDescriptor(samplerName, texOp.Type, texOp.Format, texOp.Handle); + firstBinding = context.Config.Counts.IncrementTexturesCount(); + + var desc = new TextureDescriptor(firstBinding, texOp.Type, texOp.Format, texOp.Handle); context.TextureDescriptors.Add(desc); } + + string samplerTypeName = texOp.Type.ToGlslSamplerType(); + + context.AppendLine($"layout (binding = {firstBinding}) uniform {samplerTypeName} {samplerName};"); } } private static void DeclareImages(CodeGenContext context, StructuredProgramInfo info) { - Dictionary images = new Dictionary(); + HashSet images = new HashSet(); foreach (AstTextureOperation texOp in info.Images.OrderBy(x => x.Handle)) { @@ -370,48 +363,48 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr); - if (!images.TryAdd(imageName, texOp)) + if (!images.Add(imageName)) { continue; } + int firstBinding = -1; + + if ((texOp.Type & SamplerType.Indexed) != 0) + { + for (int index = 0; index < texOp.ArraySize; index++) + { + int binding = context.Config.Counts.IncrementImagesCount(); + + if (firstBinding < 0) + { + firstBinding = binding; + } + + var desc = new TextureDescriptor(binding, texOp.Type, texOp.Format, texOp.Handle + index * 2); + + context.ImageDescriptors.Add(desc); + } + } + else + { + firstBinding = context.Config.Counts.IncrementImagesCount(); + + var desc = new TextureDescriptor(firstBinding, texOp.Type, texOp.Format, texOp.Handle); + + context.ImageDescriptors.Add(desc); + } + string layout = texOp.Format.ToGlslFormat(); if (!string.IsNullOrEmpty(layout)) { - layout = "layout(" + layout + ") "; + layout = ", " + layout; } string imageTypeName = texOp.Type.ToGlslImageType(texOp.Format.GetComponentType()); - context.AppendLine("uniform " + layout + imageTypeName + " " + imageName + ";"); - } - - foreach (KeyValuePair kv in images) - { - string imageName = kv.Key; - - AstTextureOperation texOp = kv.Value; - - if ((texOp.Type & SamplerType.Indexed) != 0) - { - for (int index = 0; index < texOp.ArraySize; index++) - { - string indexExpr = NumberFormatter.FormatInt(index); - - string indexedSamplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); - - var desc = new TextureDescriptor(indexedSamplerName, texOp.Type, texOp.Format, texOp.Handle + index * 2); - - context.TextureDescriptors.Add(desc); - } - } - else - { - var desc = new TextureDescriptor(imageName, texOp.Type, texOp.Format, texOp.Handle); - - context.ImageDescriptors.Add(desc); - } + context.AppendLine($"layout (binding = {firstBinding}{layout}) uniform {imageTypeName} {imageName};"); } } diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index ea936c6281..cb9db92218 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -2,79 +2,79 @@ { public interface IGpuAccessor { - public void Log(string message) + void Log(string message) { // No default log output. } T MemoryRead(ulong address) where T : unmanaged; - public bool MemoryMapped(ulong address) + bool MemoryMapped(ulong address) { return true; } - public int QueryComputeLocalSizeX() + int QueryComputeLocalSizeX() { return 1; } - public int QueryComputeLocalSizeY() + int QueryComputeLocalSizeY() { return 1; } - public int QueryComputeLocalSizeZ() + int QueryComputeLocalSizeZ() { return 1; } - public int QueryComputeLocalMemorySize() + int QueryComputeLocalMemorySize() { return 0x1000; } - public int QueryComputeSharedMemorySize() + int QueryComputeSharedMemorySize() { return 0xc000; } - public uint QueryConstantBufferUse() + uint QueryConstantBufferUse() { return 0xffff; } - public bool QueryIsTextureBuffer(int handle) + bool QueryIsTextureBuffer(int handle) { return false; } - public bool QueryIsTextureRectangle(int handle) + bool QueryIsTextureRectangle(int handle) { return false; } - public InputTopology QueryPrimitiveTopology() + InputTopology QueryPrimitiveTopology() { return InputTopology.Points; } - public int QueryStorageBufferOffsetAlignment() + int QueryStorageBufferOffsetAlignment() { return 16; } - public bool QuerySupportsImageLoadFormatted() + bool QuerySupportsImageLoadFormatted() { return true; } - public bool QuerySupportsNonConstantTextureOffset() + bool QuerySupportsNonConstantTextureOffset() { return true; } - public TextureFormat QueryTextureFormat(int handle) + TextureFormat QueryTextureFormat(int handle) { return TextureFormat.R8G8B8A8Unorm; } diff --git a/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/Ryujinx.Graphics.Shader/TextureDescriptor.cs index 7cf868efae..b39d6c3d1a 100644 --- a/Ryujinx.Graphics.Shader/TextureDescriptor.cs +++ b/Ryujinx.Graphics.Shader/TextureDescriptor.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Shader { public struct TextureDescriptor { - public string Name { get; } + public int Binding { get; } public SamplerType Type { get; } @@ -17,9 +17,9 @@ namespace Ryujinx.Graphics.Shader public TextureUsageFlags Flags { get; set; } - public TextureDescriptor(string name, SamplerType type, TextureFormat format, int handleIndex) + public TextureDescriptor(int binding, SamplerType type, TextureFormat format, int handleIndex) { - Name = name; + Binding = binding; Type = type; Format = format; HandleIndex = handleIndex; @@ -32,9 +32,9 @@ namespace Ryujinx.Graphics.Shader Flags = TextureUsageFlags.None; } - public TextureDescriptor(string name, SamplerType type, int cbufSlot, int cbufOffset) + public TextureDescriptor(int binding, SamplerType type, int cbufSlot, int cbufOffset) { - Name = name; + Binding = binding; Type = type; Format = TextureFormat.Unknown; HandleIndex = 0; diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 7b1587ae77..08e7df3b1a 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -20,11 +20,13 @@ namespace Ryujinx.Graphics.Shader.Translation public TranslationFlags Flags { get; } + public TranslationCounts Counts { get; } + public int Size { get; private set; } public FeatureFlags UsedFeatures { get; private set; } - public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags) + public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) { Stage = ShaderStage.Compute; OutputTopology = OutputTopology.PointList; @@ -38,9 +40,10 @@ namespace Ryujinx.Graphics.Shader.Translation Flags = flags; Size = 0; UsedFeatures = FeatureFlags.None; + Counts = counts; } - public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags) + public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) { Stage = header.Stage; OutputTopology = header.OutputTopology; @@ -54,6 +57,7 @@ namespace Ryujinx.Graphics.Shader.Translation Flags = flags; Size = 0; UsedFeatures = FeatureFlags.None; + Counts = counts; } public int GetDepthRegister() diff --git a/Ryujinx.Graphics.Shader/Translation/TranslationCounts.cs b/Ryujinx.Graphics.Shader/Translation/TranslationCounts.cs new file mode 100644 index 0000000000..18f4725d53 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Translation/TranslationCounts.cs @@ -0,0 +1,30 @@ +namespace Ryujinx.Graphics.Shader.Translation +{ + public class TranslationCounts + { + public int UniformBuffersCount { get; private set; } + public int StorageBuffersCount { get; private set; } + public int TexturesCount { get; private set; } + public int ImagesCount { get; private set; } + + internal int IncrementUniformBuffersCount() + { + return UniformBuffersCount++; + } + + internal int IncrementStorageBuffersCount() + { + return StorageBuffersCount++; + } + + internal int IncrementTexturesCount() + { + return TexturesCount++; + } + + internal int IncrementImagesCount() + { + return ImagesCount++; + } + } +} diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index f8093c8440..f86b69647c 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -24,15 +24,28 @@ namespace Ryujinx.Graphics.Shader.Translation } } - public static ShaderProgram Translate(ulong address, IGpuAccessor gpuAccessor, TranslationFlags flags) + public static ShaderProgram Translate( + ulong address, + IGpuAccessor gpuAccessor, + TranslationFlags flags, + TranslationCounts counts = null) { - return Translate(DecodeShader(address, gpuAccessor, flags, out ShaderConfig config), config); + counts ??= new TranslationCounts(); + + return Translate(DecodeShader(address, gpuAccessor, flags, counts, out ShaderConfig config), config); } - public static ShaderProgram Translate(ulong addressA, ulong addressB, IGpuAccessor gpuAccessor, TranslationFlags flags) + public static ShaderProgram Translate( + ulong addressA, + ulong addressB, + IGpuAccessor gpuAccessor, + TranslationFlags flags, + TranslationCounts counts = null) { - FunctionCode[] funcA = DecodeShader(addressA, gpuAccessor, flags | TranslationFlags.VertexA, out ShaderConfig configA); - FunctionCode[] funcB = DecodeShader(addressB, gpuAccessor, flags, out ShaderConfig config); + counts ??= new TranslationCounts(); + + FunctionCode[] funcA = DecodeShader(addressA, gpuAccessor, flags | TranslationFlags.VertexA, counts, out ShaderConfig configA); + FunctionCode[] funcB = DecodeShader(addressB, gpuAccessor, flags, counts, out ShaderConfig config); config.SetUsedFeature(configA.UsedFeatures); @@ -105,19 +118,24 @@ namespace Ryujinx.Graphics.Shader.Translation return new ShaderProgram(spInfo, config.Stage, glslCode, config.Size, sizeA); } - private static FunctionCode[] DecodeShader(ulong address, IGpuAccessor gpuAccessor, TranslationFlags flags, out ShaderConfig config) + private static FunctionCode[] DecodeShader( + ulong address, + IGpuAccessor gpuAccessor, + TranslationFlags flags, + TranslationCounts counts, + out ShaderConfig config) { Block[][] cfg; if ((flags & TranslationFlags.Compute) != 0) { - config = new ShaderConfig(gpuAccessor, flags); + config = new ShaderConfig(gpuAccessor, flags, counts); cfg = Decoder.Decode(gpuAccessor, address); } else { - config = new ShaderConfig(new ShaderHeader(gpuAccessor, address), gpuAccessor, flags); + config = new ShaderConfig(new ShaderHeader(gpuAccessor, address), gpuAccessor, flags, counts); cfg = Decoder.Decode(gpuAccessor, address + HeaderSize); }