diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 867092776..158b791a5 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 2290; + private const ulong ShaderCodeGenVersion = 2298; // Progress reporting helpers private volatile int _shaderCount; @@ -415,7 +415,7 @@ namespace Ryujinx.Graphics.Gpu.Shader if (activeTasks.Count == maxTaskCount) { // Wait for a task to be done, or for 1ms. - // Host shader compilation cannot signal when it is done, + // Host shader compilation cannot signal when it is done, // so the 1ms timeout is required to poll status. taskDoneEvent.WaitOne(1); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 6e67b6829..cb17f18de 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -262,10 +262,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string blockName = $"{ubName}_{DefaultNames.BlockSuffix}"; - context.AppendLine($"layout (binding = {descriptors[0].Binding}, std140) uniform {blockName}"); + context.AppendLine($"layout (binding = {context.Config.FirstConstantBufferBinding}, std140) uniform {blockName}"); context.EnterScope(); context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";"); - context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(descriptors.Length)}];"); + context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(descriptors.Max(x => x.Slot) + 1)}];"); } else { @@ -291,10 +291,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string blockName = $"{sbName}_{DefaultNames.BlockSuffix}"; - context.AppendLine($"layout (binding = {descriptors[0].Binding}, std430) buffer {blockName}"); + context.AppendLine($"layout (binding = {context.Config.FirstStorageBufferBinding}, std430) buffer {blockName}"); context.EnterScope(); context.AppendLine("uint " + DefaultNames.DataName + "[];"); - context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(descriptors.Length)}];"); + context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(descriptors.Max(x => x.Slot) + 1)}];"); } private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors) diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 1e05cdcd6..6b584e533 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -41,7 +41,7 @@ uint QueryConstantBufferUse() { - return 0xffff; + return 0; } bool QueryIsTextureBuffer(int handle, int cbufSlot = -1) diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 25e5edc9a..4acfa80a6 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -293,7 +293,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Instruction.Branch or Instruction.BranchIfFalse or Instruction.BranchIfTrue => true, - _ => false, + _ => false }; } diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 3230f4e68..8004e3133 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -91,6 +91,9 @@ namespace Ryujinx.Graphics.Shader.Translation private TextureDescriptor[] _cachedTextureDescriptors; private TextureDescriptor[] _cachedImageDescriptors; + public int FirstConstantBufferBinding { get; private set; } + public int FirstStorageBufferBinding { get; private set; } + public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) { Stage = ShaderStage.Compute; @@ -215,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Translation } } - private static void SetUsedTextureOrImage( + private void SetUsedTextureOrImage( Dictionary dict, int cbufSlot, int handle, @@ -235,7 +238,10 @@ namespace Ryujinx.Graphics.Shader.Translation { usageFlags |= TextureUsageFlags.NeedsScaleValue; - var canScale = (dimensions == 2 && !isArray) || (dimensions == 3 && isArray); + var canScale = (Stage == ShaderStage.Fragment || Stage == ShaderStage.Compute) && !isIndexed && !write && + ((dimensions == 2 && !isArray) || + (dimensions == 3 && isArray)); + if (!canScale) { // Resolution scaling cannot be applied to this texture right now. @@ -293,34 +299,59 @@ namespace Ryujinx.Graphics.Shader.Translation if (UsedFeatures.HasFlag(FeatureFlags.CbIndexing)) { - usedMask = FillMask(usedMask); + usedMask |= (int)GpuAccessor.QueryConstantBufferUse(); } - return _cachedConstantBufferDescriptors = GetBufferDescriptors(usedMask, 0, _counts.IncrementUniformBuffersCount); + FirstConstantBufferBinding = _counts.UniformBuffersCount; + + return _cachedConstantBufferDescriptors = GetBufferDescriptors( + usedMask, + 0, + UsedFeatures.HasFlag(FeatureFlags.CbIndexing), + _counts.IncrementUniformBuffersCount); } public BufferDescriptor[] GetStorageBufferDescriptors() { - return _cachedStorageBufferDescriptors ??= GetBufferDescriptors(FillMask(_usedStorageBuffers), _usedStorageBuffersWrite, _counts.IncrementStorageBuffersCount); + if (_cachedStorageBufferDescriptors != null) + { + return _cachedStorageBufferDescriptors; + } + + FirstStorageBufferBinding = _counts.StorageBuffersCount; + + return _cachedStorageBufferDescriptors = GetBufferDescriptors( + _usedStorageBuffers, + _usedStorageBuffersWrite, + true, + _counts.IncrementStorageBuffersCount); } - private static int FillMask(int mask) - { - // When the storage or uniform buffers are used as array, we must allocate a binding - // even for the "gaps" that are not used on the shader. - // For this reason, fill up the gaps so that all slots up to the highest one are - // marked as "used". - return mask != 0 ? (int)(uint.MaxValue >> BitOperations.LeadingZeroCount((uint)mask)) : 0; - } - - private static BufferDescriptor[] GetBufferDescriptors(int usedMask, int writtenMask, Func getBindingCallback) + private static BufferDescriptor[] GetBufferDescriptors( + int usedMask, + int writtenMask, + bool isArray, + Func getBindingCallback) { var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)]; + int lastSlot = -1; + for (int i = 0; i < descriptors.Length; i++) { int slot = BitOperations.TrailingZeroCount(usedMask); + if (isArray) + { + // The next array entries also consumes bindings, even if they are unused. + for (int j = lastSlot + 1; j < slot; j++) + { + getBindingCallback(); + } + } + + lastSlot = slot; + descriptors[i] = new BufferDescriptor(getBindingCallback(), slot); if ((writtenMask & (1 << slot)) != 0)