Implement shader storage buffer operations using new Load/Store instructions (#4993)

* Implement storage buffer operations using new Load/Store instruction

* Extend GenerateMultiTargetStorageOp to also match access with constant offset, and log and comments

* Remove now unused code

* Catch more complex cases of global memory usage

* Shader cache version bump

* Extend global access elimination to work with more shared memory cases

* Change alignment requirement from 16 bytes to 8 bytes, handle cases where we need more than 16 storage buffers

* Tweak preferencing to catch more cases

* Enable CB0 elimination even when host storage buffer alignment is > 16 (for Intel)

* Fix storage buffer bindings

* Simplify some code

* Shader cache version bump

* Fix typo

* Extend global memory elimination to handle shared memory with multiple possible offsets and local memory
This commit is contained in:
gdkchan 2023-06-03 20:12:18 -03:00 committed by GitHub
parent 81c9052847
commit 21c9ac6240
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 1468 additions and 1259 deletions

View file

@ -80,11 +80,6 @@ namespace Ryujinx.Graphics.Gpu
/// </summary>
public const int GobAlignment = 64;
/// <summary>
/// Expected byte alignment for storage buffers
/// </summary>
public const int StorageAlignment = 16;
/// <summary>
/// Number of the uniform buffer reserved by the driver to store the storage buffer base addresses.
/// </summary>

View file

@ -187,30 +187,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
info = cs.Shaders[0].Info;
}
for (int index = 0; index < info.CBuffers.Count; index++)
{
BufferDescriptor cb = info.CBuffers[index];
// NVN uses the "hardware" constant buffer for anything that is less than 8,
// and those are already bound above.
// Anything greater than or equal to 8 uses the emulated constant buffers.
// They are emulated using global memory loads.
if (cb.Slot < 8)
{
continue;
}
ulong cbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0);
int cbDescOffset = 0x260 + (cb.Slot - 8) * 0x10;
cbDescAddress += (ulong)cbDescOffset;
SbDescriptor cbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(cbDescAddress);
_channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
}
_channel.BufferManager.SetComputeBufferBindings(cs.Bindings);
_channel.TextureManager.SetComputeBindings(cs.Bindings);

View file

@ -222,7 +222,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
private void RecordStorageAlignment(BuffersPerStage buffers, int index, ulong gpuVa)
{
bool unaligned = (gpuVa & (Constants.StorageAlignment - 1)) != 0;
bool unaligned = (gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1)) != 0;
if (unaligned || HasUnalignedStorageBuffers)
{

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 5027;
private const uint CodeGenVersion = 4992;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View file

@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
int imageBinding = stageIndex * imagesPerStage * 2;
AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage);
AddArrayDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage);
AddDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage);
AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
@ -133,19 +133,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
AddDescriptor(stages, type2, setIndex, binding + count, count);
}
/// <summary>
/// Adds an array resource to the list of descriptors.
/// </summary>
/// <param name="stages">Shader stages where the resource is used</param>
/// <param name="type">Type of the resource</param>
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
/// <param name="binding">Binding number where the resource will be bound</param>
/// <param name="count">Number of resources bound at the binding location</param>
private void AddArrayDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
{
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, count, type, stages));
}
/// <summary>
/// Adds buffer usage information to the list of usages.
/// </summary>

View file

@ -104,14 +104,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
if (sBufferDescriptors.Length != 0)
{
DeclareStorages(context, sBufferDescriptors);
context.AppendLine();
}
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
var textureDescriptors = context.Config.GetTextureDescriptors();
if (textureDescriptors.Length != 0)
@ -250,11 +243,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl");
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Storage) != 0)
{
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl");
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)
{
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl");
@ -290,11 +278,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl");
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.StoreStorageSmallInt) != 0)
{
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl");
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0)
{
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl");
@ -356,6 +339,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
{
DeclareBuffers(context, buffers, "uniform");
}
private static void DeclareStorageBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
{
DeclareBuffers(context, buffers, "buffer");
}
private static void DeclareBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers, string declType)
{
foreach (BufferDefinition buffer in buffers)
{
@ -365,7 +358,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
_ => "std430"
};
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) uniform _{buffer.Name}");
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}");
context.EnterScope();
foreach (StructureField field in buffer.Type.Fields)
@ -373,9 +366,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (field.Type.HasFlag(AggregateType.Array))
{
string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array);
string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture);
context.AppendLine($"{typeName} {field.Name}[{arraySize}];");
if (field.ArrayLength > 0)
{
string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture);
context.AppendLine($"{typeName} {field.Name}[{arraySize}];");
}
else
{
context.AppendLine($"{typeName} {field.Name}[];");
}
}
else
{
@ -390,22 +391,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
}
private static void DeclareStorages(CodeGenContext context, BufferDescriptor[] descriptors)
{
string sbName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
sbName += "_" + DefaultNames.StorageNamePrefix;
string blockName = $"{sbName}_{DefaultNames.BlockSuffix}";
string layout = context.Config.Options.TargetApi == TargetApi.Vulkan ? ", set = 1" : string.Empty;
context.AppendLine($"layout (binding = {context.Config.FirstStorageBufferBinding}{layout}, std430) buffer {blockName}");
context.EnterScope();
context.AppendLine("uint " + DefaultNames.DataName + "[];");
context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(descriptors.Max(x => x.Slot) + 1)}];");
}
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
{
int arraySize = 0;
@ -733,7 +718,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
code = code.Replace("\t", CodeGenContext.Tab);
code = code.Replace("$SHARED_MEM$", DefaultNames.SharedMemoryName);
code = code.Replace("$STORAGE_MEM$", OperandManager.GetShaderStagePrefix(context.Config.Stage) + "_" + DefaultNames.StorageNamePrefix);
if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot())
{

View file

@ -11,12 +11,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public const string IAttributePrefix = "in_attr";
public const string OAttributePrefix = "out_attr";
public const string StorageNamePrefix = "s";
public const string DataName = "data";
public const string BlockSuffix = "block";
public const string LocalMemoryName = "local_mem";
public const string SharedMemoryName = "shared_mem";

View file

@ -1,21 +0,0 @@
int Helper_AtomicMaxS32(int index, int offset, int value)
{
uint oldValue, newValue;
do
{
oldValue = $STORAGE_MEM$[index].data[offset];
newValue = uint(max(int(oldValue), value));
} while (atomicCompSwap($STORAGE_MEM$[index].data[offset], oldValue, newValue) != oldValue);
return int(oldValue);
}
int Helper_AtomicMinS32(int index, int offset, int value)
{
uint oldValue, newValue;
do
{
oldValue = $STORAGE_MEM$[index].data[offset];
newValue = uint(min(int(oldValue), value));
} while (atomicCompSwap($STORAGE_MEM$[index].data[offset], oldValue, newValue) != oldValue);
return int(oldValue);
}

View file

@ -1,23 +0,0 @@
void Helper_StoreStorage16(int index, int offset, uint value)
{
int wordOffset = offset >> 2;
int bitOffset = (offset & 3) * 8;
uint oldValue, newValue;
do
{
oldValue = $STORAGE_MEM$[index].data[wordOffset];
newValue = bitfieldInsert(oldValue, value, bitOffset, 16);
} while (atomicCompSwap($STORAGE_MEM$[index].data[wordOffset], oldValue, newValue) != oldValue);
}
void Helper_StoreStorage8(int index, int offset, uint value)
{
int wordOffset = offset >> 2;
int bitOffset = (offset & 3) * 8;
uint oldValue, newValue;
do
{
oldValue = $STORAGE_MEM$[index].data[wordOffset];
newValue = bitfieldInsert(oldValue, value, bitOffset, 8);
} while (atomicCompSwap($STORAGE_MEM$[index].data[wordOffset], oldValue, newValue) != oldValue);
}

View file

@ -68,33 +68,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string args = string.Empty;
for (int argIndex = 0; argIndex < arity; argIndex++)
if (atomic && operation.StorageKind == StorageKind.StorageBuffer)
{
args = GenerateLoadOrStore(context, operation, isStore: false);
AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32
? AggregateType.S32
: AggregateType.U32;
for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++)
{
args += ", " + GetSoureExpr(context, operation.GetSource(argIndex), dstType);
}
}
else if (atomic && operation.StorageKind == StorageKind.SharedMemory)
{
args = LoadShared(context, operation);
// For shared memory access, the second argument is unused and should be ignored.
// It is there to make both storage and shared access have the same number of arguments.
// For storage, both inputs are consumed when the argument index is 0, so we should skip it here.
if (argIndex == 1 && (atomic || operation.StorageKind == StorageKind.SharedMemory))
{
continue;
}
if (argIndex != 0)
for (int argIndex = 2; argIndex < arity; argIndex++)
{
args += ", ";
}
if (argIndex == 0 && atomic)
AggregateType dstType = GetSrcVarType(inst, argIndex);
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
}
}
else
{
for (int argIndex = 0; argIndex < arity; argIndex++)
{
switch (operation.StorageKind)
if (argIndex != 0)
{
case StorageKind.SharedMemory: args += LoadShared(context, operation); break;
case StorageKind.StorageBuffer: args += LoadStorage(context, operation); break;
default: throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\".");
args += ", ";
}
}
else
{
AggregateType dstType = GetSrcVarType(inst, argIndex);
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
@ -173,9 +185,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.LoadShared:
return LoadShared(context, operation);
case Instruction.LoadStorage:
return LoadStorage(context, operation);
case Instruction.Lod:
return Lod(context, operation);
@ -203,15 +212,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.StoreShared8:
return StoreShared8(context, operation);
case Instruction.StoreStorage:
return StoreStorage(context, operation);
case Instruction.StoreStorage16:
return StoreStorage16(context, operation);
case Instruction.StoreStorage8:
return StoreStorage8(context, operation);
case Instruction.TextureSample:
return TextureSample(context, operation);

View file

@ -85,7 +85,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.Load, InstType.Special);
Add(Instruction.LoadLocal, InstType.Special);
Add(Instruction.LoadShared, InstType.Special);
Add(Instruction.LoadStorage, InstType.Special);
Add(Instruction.Lod, InstType.Special);
Add(Instruction.LogarithmB2, InstType.CallUnary, "log2");
Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9);
@ -123,9 +122,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.StoreShared, InstType.Special);
Add(Instruction.StoreShared16, InstType.Special);
Add(Instruction.StoreShared8, InstType.Special);
Add(Instruction.StoreStorage, InstType.Special);
Add(Instruction.StoreStorage16, InstType.Special);
Add(Instruction.StoreStorage8, InstType.Special);
Add(Instruction.Subtract, InstType.OpBinary, "-", 2);
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
Add(Instruction.TextureSample, InstType.Special);

View file

@ -210,17 +210,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return $"{arrayName}[{offsetExpr}]";
}
public static string LoadStorage(CodeGenContext context, AstOperation operation)
{
IAstNode src1 = operation.GetSource(0);
IAstNode src2 = operation.GetSource(1);
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
}
public static string Lod(CodeGenContext context, AstOperation operation)
{
AstTextureOperation texOp = (AstTextureOperation)operation;
@ -326,60 +315,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return $"{HelperFunctionNames.StoreShared8}({offsetExpr}, {src})";
}
public static string StoreStorage(CodeGenContext context, AstOperation operation)
{
IAstNode src1 = operation.GetSource(0);
IAstNode src2 = operation.GetSource(1);
IAstNode src3 = operation.GetSource(2);
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
return $"{sb} = {src}";
}
public static string StoreStorage16(CodeGenContext context, AstOperation operation)
{
IAstNode src1 = operation.GetSource(0);
IAstNode src2 = operation.GetSource(1);
IAstNode src3 = operation.GetSource(2);
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
return $"{HelperFunctionNames.StoreStorage16}({indexExpr}, {offsetExpr}, {src})";
}
public static string StoreStorage8(CodeGenContext context, AstOperation operation)
{
IAstNode src1 = operation.GetSource(0);
IAstNode src2 = operation.GetSource(1);
IAstNode src3 = operation.GetSource(2);
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
return $"{HelperFunctionNames.StoreStorage8}({indexExpr}, {offsetExpr}, {src})";
}
public static string TextureSample(CodeGenContext context, AstOperation operation)
{
AstTextureOperation texOp = (AstTextureOperation)operation;
@ -701,25 +636,34 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
}
}
private static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
public static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
{
StorageKind storageKind = operation.StorageKind;
string varName;
AggregateType varType;
int srcIndex = 0;
int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount;
bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic();
int inputsCount = isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount;
if (operation.Inst == Instruction.AtomicCompareAndSwap)
{
inputsCount--;
}
switch (storageKind)
{
case StorageKind.ConstantBuffer:
case StorageKind.StorageBuffer:
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
{
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
}
int binding = bindingIndex.Value;
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[binding];
BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer
? context.Config.Properties.ConstantBuffers[binding]
: context.Config.Properties.StorageBuffers[binding];
if (!(operation.GetSource(srcIndex++) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
{
@ -825,15 +769,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return varName;
}
private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage)
{
string sbName = OperandManager.GetShaderStagePrefix(stage);
sbName += "_" + DefaultNames.StorageNamePrefix;
return $"{sbName}[{slotExpr}].{DefaultNames.DataName}[{offsetExpr}]";
}
private static string GetMask(int index)
{
return $".{"rgba".AsSpan(index, 1)}";

View file

@ -118,6 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
switch (operation.StorageKind)
{
case StorageKind.ConstantBuffer:
case StorageKind.StorageBuffer:
if (!(operation.GetSource(0) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
{
throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
@ -128,7 +129,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
}
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value];
BufferDefinition buffer = operation.StorageKind == StorageKind.ConstantBuffer
? context.Config.Properties.ConstantBuffers[bindingIndex.Value]
: context.Config.Properties.StorageBuffers[bindingIndex.Value];
StructureField field = buffer.Type.Fields[fieldIndex.Value];
return field.Type & AggregateType.ElementTypeMask;

View file

@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public int InputVertices { get; }
public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
public Instruction StorageBuffersArray { get; set; }
public Dictionary<int, Instruction> StorageBuffers { get; } = new Dictionary<int, Instruction>();
public Instruction LocalMemory { get; set; }
public Instruction SharedMemory { get; set; }
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
@ -308,7 +308,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
if ((type & AggregateType.Array) != 0)
{
return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length));
if (length > 0)
{
return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length));
}
else
{
return TypeRuntimeArray(GetType(type & ~AggregateType.Array));
}
}
else if ((type & AggregateType.ElementCountMask) != 0)
{

View file

@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.Translation;
using Spv.Generator;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using static Spv.Specification;
@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
DeclareSamplers(context, context.Config.GetTextureDescriptors());
DeclareImages(context, context.Config.GetImageDescriptors());
DeclareInputsAndOutputs(context, info);
@ -127,6 +128,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
{
DeclareBuffers(context, buffers, isBuffer: false);
}
private static void DeclareStorageBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
{
DeclareBuffers(context, buffers, isBuffer: true);
}
private static void DeclareBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers, bool isBuffer)
{
HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>();
@ -155,6 +166,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize);
}
// Zero lengths are assumed to be a "runtime array" (which does not have a explicit length
// specified on the shader, and instead assumes the bound buffer length).
// It is only valid as the last struct element.
Debug.Assert(field.ArrayLength > 0 || fieldIndex == buffer.Type.Fields.Length - 1);
offset += fieldSize * field.ArrayLength;
}
else
@ -163,56 +180,37 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
}
var ubStructType = context.TypeStruct(false, structFieldTypes);
var structType = context.TypeStruct(false, structFieldTypes);
if (decoratedTypes.Add(ubStructType))
if (decoratedTypes.Add(structType))
{
context.Decorate(ubStructType, Decoration.Block);
context.Decorate(structType, isBuffer ? Decoration.BufferBlock : Decoration.Block);
for (int fieldIndex = 0; fieldIndex < structFieldOffsets.Length; fieldIndex++)
{
context.MemberDecorate(ubStructType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]);
context.MemberDecorate(structType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]);
}
}
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
var pointerType = context.TypePointer(StorageClass.Uniform, structType);
var variable = context.Variable(pointerType, StorageClass.Uniform);
context.Name(ubVariable, buffer.Name);
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding);
context.AddGlobalVariable(ubVariable);
context.ConstantBuffers.Add(buffer.Binding, ubVariable);
context.Name(variable, buffer.Name);
context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
context.Decorate(variable, Decoration.Binding, (LiteralInteger)buffer.Binding);
context.AddGlobalVariable(variable);
if (isBuffer)
{
context.StorageBuffers.Add(buffer.Binding, variable);
}
else
{
context.ConstantBuffers.Add(buffer.Binding, variable);
}
}
}
private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
{
if (descriptors.Length == 0)
{
return;
}
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 1 : 0;
int count = descriptors.Max(x => x.Slot) + 1;
var sbArrayType = context.TypeRuntimeArray(context.TypeU32());
context.Decorate(sbArrayType, Decoration.ArrayStride, (LiteralInteger)4);
var sbStructType = context.TypeStruct(true, sbArrayType);
context.Decorate(sbStructType, Decoration.BufferBlock);
context.MemberDecorate(sbStructType, 0, Decoration.Offset, (LiteralInteger)0);
var sbStructArrayType = context.TypeArray(sbStructType, context.Constant(context.TypeU32(), count));
var sbPointerType = context.TypePointer(StorageClass.Uniform, sbStructArrayType);
var sbVariable = context.Variable(sbPointerType, StorageClass.Uniform);
context.Name(sbVariable, $"{GetStagePrefix(context.Config.Stage)}_s");
context.Decorate(sbVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
context.Decorate(sbVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstStorageBufferBinding);
context.AddGlobalVariable(sbVariable);
context.StorageBuffersArray = sbVariable;
}
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
{
foreach (var descriptor in descriptors)

View file

@ -99,7 +99,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.Load, GenerateLoad);
Add(Instruction.LoadLocal, GenerateLoadLocal);
Add(Instruction.LoadShared, GenerateLoadShared);
Add(Instruction.LoadStorage, GenerateLoadStorage);
Add(Instruction.Lod, GenerateLod);
Add(Instruction.LogarithmB2, GenerateLogarithmB2);
Add(Instruction.LogicalAnd, GenerateLogicalAnd);
@ -137,9 +136,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.StoreShared, GenerateStoreShared);
Add(Instruction.StoreShared16, GenerateStoreShared16);
Add(Instruction.StoreShared8, GenerateStoreShared8);
Add(Instruction.StoreStorage, GenerateStoreStorage);
Add(Instruction.StoreStorage16, GenerateStoreStorage16);
Add(Instruction.StoreStorage8, GenerateStoreStorage8);
Add(Instruction.Subtract, GenerateSubtract);
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
Add(Instruction.TextureSample, GenerateTextureSample);
@ -889,14 +885,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return new OperationResult(AggregateType.U32, value);
}
private static OperationResult GenerateLoadStorage(CodeGenContext context, AstOperation operation)
{
var elemPointer = GetStorageElemPointer(context, operation);
var value = context.Load(context.TypeU32(), elemPointer);
return new OperationResult(AggregateType.U32, value);
}
private static OperationResult GenerateLod(CodeGenContext context, AstOperation operation)
{
AstTextureOperation texOp = (AstTextureOperation)operation;
@ -1307,28 +1295,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return OperationResult.Invalid;
}
private static OperationResult GenerateStoreStorage(CodeGenContext context, AstOperation operation)
{
var elemPointer = GetStorageElemPointer(context, operation);
context.Store(elemPointer, context.Get(AggregateType.U32, operation.GetSource(2)));
return OperationResult.Invalid;
}
private static OperationResult GenerateStoreStorage16(CodeGenContext context, AstOperation operation)
{
GenerateStoreStorageSmallInt(context, operation, 16);
return OperationResult.Invalid;
}
private static OperationResult GenerateStoreStorage8(CodeGenContext context, AstOperation operation)
{
GenerateStoreStorageSmallInt(context, operation, 8);
return OperationResult.Invalid;
}
private static OperationResult GenerateSubtract(CodeGenContext context, AstOperation operation)
{
return GenerateBinary(context, operation, context.Delegates.FSub, context.Delegates.ISub);
@ -1849,13 +1815,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
AstOperation operation,
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitU)
{
var value = context.GetU32(operation.GetSource(2));
var value = context.GetU32(operation.GetSource(operation.SourcesCount - 1));
SpvInstruction elemPointer;
if (operation.StorageKind == StorageKind.StorageBuffer)
{
elemPointer = GetStorageElemPointer(context, operation);
elemPointer = GetStoragePointer(context, operation, out _);
}
else if (operation.StorageKind == StorageKind.SharedMemory)
{
@ -1875,14 +1841,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static OperationResult GenerateAtomicMemoryCas(CodeGenContext context, AstOperation operation)
{
var value0 = context.GetU32(operation.GetSource(2));
var value1 = context.GetU32(operation.GetSource(3));
var value0 = context.GetU32(operation.GetSource(operation.SourcesCount - 2));
var value1 = context.GetU32(operation.GetSource(operation.SourcesCount - 1));
SpvInstruction elemPointer;
if (operation.StorageKind == StorageKind.StorageBuffer)
{
elemPointer = GetStorageElemPointer(context, operation);
elemPointer = GetStoragePointer(context, operation, out _);
}
else if (operation.StorageKind == StorageKind.SharedMemory)
{
@ -1901,17 +1867,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
private static OperationResult GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
{
SpvInstruction pointer = GetStoragePointer(context, operation, out AggregateType varType);
if (isStore)
{
context.Store(pointer, context.Get(varType, operation.GetSource(operation.SourcesCount - 1)));
return OperationResult.Invalid;
}
else
{
var result = context.Load(context.GetType(varType), pointer);
return new OperationResult(varType, result);
}
}
private static SpvInstruction GetStoragePointer(CodeGenContext context, AstOperation operation, out AggregateType varType)
{
StorageKind storageKind = operation.StorageKind;
StorageClass storageClass;
SpvInstruction baseObj;
AggregateType varType;
int srcIndex = 0;
switch (storageKind)
{
case StorageKind.ConstantBuffer:
case StorageKind.StorageBuffer:
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
{
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
@ -1922,12 +1904,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand.");
}
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value];
BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer
? context.Config.Properties.ConstantBuffers[bindingIndex.Value]
: context.Config.Properties.StorageBuffers[bindingIndex.Value];
StructureField field = buffer.Type.Fields[fieldIndex.Value];
storageClass = StorageClass.Uniform;
varType = field.Type & AggregateType.ElementTypeMask;
baseObj = context.ConstantBuffers[bindingIndex.Value];
baseObj = storageKind == StorageKind.ConstantBuffer
? context.ConstantBuffers[bindingIndex.Value]
: context.StorageBuffers[bindingIndex.Value];
break;
case StorageKind.Input:
@ -1993,7 +1979,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
}
int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic();
int inputsCount = (isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
if (operation.Inst == Instruction.AtomicCompareAndSwap)
{
inputsCount--;
}
SpvInstruction e0, e1, e2;
SpvInstruction pointer;
@ -2030,16 +2023,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
break;
}
if (isStore)
{
context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex)));
return OperationResult.Invalid;
}
else
{
var result = context.Load(context.GetType(varType), pointer);
return new OperationResult(varType, result);
}
return pointer;
}
private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable)
@ -2068,25 +2052,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize);
}
private static void GenerateStoreStorageSmallInt(CodeGenContext context, AstOperation operation, int bitSize)
{
var i0 = context.Get(AggregateType.S32, operation.GetSource(0));
var offset = context.Get(AggregateType.U32, operation.GetSource(1));
var value = context.Get(AggregateType.U32, operation.GetSource(2));
var wordOffset = context.ShiftRightLogical(context.TypeU32(), offset, context.Constant(context.TypeU32(), 2));
var bitOffset = context.BitwiseAnd(context.TypeU32(), offset, context.Constant(context.TypeU32(), 3));
bitOffset = context.ShiftLeftLogical(context.TypeU32(), bitOffset, context.Constant(context.TypeU32(), 3));
var sbVariable = context.StorageBuffersArray;
var i1 = context.Constant(context.TypeS32(), 0);
var elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeU32()), sbVariable, i0, i1, wordOffset);
GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize);
}
private static void GenerateStoreSmallInt(
CodeGenContext context,
SpvInstruction elemPointer,
@ -2173,16 +2138,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
}
private static SpvInstruction GetStorageElemPointer(CodeGenContext context, AstOperation operation)
{
var sbVariable = context.StorageBuffersArray;
var i0 = context.Get(AggregateType.S32, operation.GetSource(0));
var i1 = context.Constant(context.TypeS32(), 0);
var i2 = context.Get(AggregateType.S32, operation.GetSource(1));
return context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeU32()), sbVariable, i0, i1, i2);
}
private static OperationResult GenerateUnary(
CodeGenContext context,
AstOperation operation,

View file

@ -10,7 +10,5 @@ namespace Ryujinx.Graphics.Shader
public const int NvnBaseVertexByteOffset = 0x640;
public const int NvnBaseInstanceByteOffset = 0x644;
public const int NvnDrawIndexByteOffset = 0x648;
public const int StorageAlignment = 16;
}
}

View file

@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Shader
/// <returns>Binding number</returns>
int QueryBindingConstantBuffer(int index)
{
return index;
return index + 1;
}
/// <summary>

View file

@ -164,6 +164,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (op.Ccc == Ccc.T)
{
context.PrepareForReturn();
context.Return();
}
else
@ -175,6 +176,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
Operand lblSkip = Label();
context.BranchIfFalse(lblSkip, cond);
context.PrepareForReturn();
context.Return();
context.MarkLabel(lblSkip);
}

View file

@ -336,13 +336,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
int offset,
bool extended)
{
bool isSmallInt = size < LsSize.B32;
int count = GetVectorCount(size);
StorageKind storageKind = GetStorageKind(size);
(Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
(_, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
Operand bitOffset = GetBitOffset(context, addrLow);
Operand srcA = context.Copy(new Operand(new Register(ra, RegisterType.Gpr)));
for (int index = 0; index < count; index++)
{
@ -353,12 +352,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
break;
}
Operand value = context.LoadGlobal(context.IAdd(addrLow, Const(index * 4)), addrHigh);
if (isSmallInt)
{
value = ExtractSmallInt(context, size, bitOffset, value);
}
Operand value = context.Load(storageKind, context.IAdd(srcA, Const(offset + index * 4)), addrHigh);
context.Copy(Register(dest), value);
}
@ -445,10 +439,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
int count = GetVectorCount((LsSize)size);
StorageKind storageKind = GetStorageKind((LsSize)size);
(Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
(_, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
Operand bitOffset = GetBitOffset(context, addrLow);
Operand srcA = context.Copy(new Operand(new Register(ra, RegisterType.Gpr)));
for (int index = 0; index < count; index++)
{
@ -456,23 +451,24 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand value = Register(isRz ? rd : rd + index, RegisterType.Gpr);
Operand addrLowOffset = context.IAdd(addrLow, Const(index * 4));
Operand addrLowOffset = context.IAdd(srcA, Const(offset + index * 4));
if (size == LsSize2.U8 || size == LsSize2.S8)
{
context.StoreGlobal8(addrLowOffset, addrHigh, value);
}
else if (size == LsSize2.U16 || size == LsSize2.S16)
{
context.StoreGlobal16(addrLowOffset, addrHigh, value);
}
else
{
context.StoreGlobal(addrLowOffset, addrHigh, value);
}
context.Store(storageKind, addrLowOffset, addrHigh, value);
}
}
private static StorageKind GetStorageKind(LsSize size)
{
return size switch
{
LsSize.U8 => StorageKind.GlobalMemoryU8,
LsSize.S8 => StorageKind.GlobalMemoryS8,
LsSize.U16 => StorageKind.GlobalMemoryU16,
LsSize.S16 => StorageKind.GlobalMemoryS16,
_ => StorageKind.GlobalMemory
};
}
private static int GetVectorCount(LsSize size)
{
switch (size)

View file

@ -79,10 +79,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
ImageAtomic,
IsNan,
Load,
LoadGlobal,
LoadLocal,
LoadShared,
LoadStorage,
Lod,
LogarithmB2,
LogicalAnd,
@ -117,16 +115,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Sine,
SquareRoot,
Store,
StoreGlobal,
StoreGlobal16,
StoreGlobal8,
StoreLocal,
StoreShared,
StoreShared16,
StoreShared8,
StoreStorage,
StoreStorage16,
StoreStorage8,
Subtract,
SwizzleAdd,
TextureSample,

View file

@ -11,7 +11,12 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
StorageBuffer,
LocalMemory,
SharedMemory,
GlobalMemory
GlobalMemory,
// TODO: Remove those and store type as a field on the Operation class itself.
GlobalMemoryS8,
GlobalMemoryS16,
GlobalMemoryU8,
GlobalMemoryU16
}
static class StorageKindExtensions

View file

@ -11,7 +11,6 @@
<ItemGroup>
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Shared.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Storage.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
@ -19,7 +18,6 @@
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
</ItemGroup>

View file

@ -6,7 +6,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
enum HelperFunctionsMask
{
AtomicMinMaxS32Shared = 1 << 0,
AtomicMinMaxS32Storage = 1 << 1,
MultiplyHighS32 = 1 << 2,
MultiplyHighU32 = 1 << 3,
Shuffle = 1 << 4,
@ -14,7 +13,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
ShuffleUp = 1 << 6,
ShuffleXor = 1 << 7,
StoreSharedSmallInt = 1 << 8,
StoreStorageSmallInt = 1 << 9,
SwizzleAdd = 1 << 10,
FSI = 1 << 11
}

View file

@ -90,10 +90,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.ImageAtomic, AggregateType.S32);
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
Add(Instruction.Load, AggregateType.FP32);
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
Add(Instruction.LoadShared, AggregateType.U32, AggregateType.S32);
Add(Instruction.LoadStorage, AggregateType.U32, AggregateType.S32, AggregateType.S32);
Add(Instruction.Lod, AggregateType.FP32);
Add(Instruction.LogarithmB2, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.LogicalAnd, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool);
@ -123,14 +121,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.Store, AggregateType.Void);
Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreShared16, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreShared8, AggregateType.Void, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreStorage, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreStorage16, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.StoreStorage8, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
Add(Instruction.TextureSample, AggregateType.FP32);
@ -166,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
return AggregateType.FP32;
}
else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store)
else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store || inst.IsAtomic())
{
return AggregateType.S32;
}

View file

@ -5,17 +5,25 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
class ShaderProperties
{
private readonly Dictionary<int, BufferDefinition> _constantBuffers;
private readonly Dictionary<int, BufferDefinition> _storageBuffers;
public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers;
public ShaderProperties()
{
_constantBuffers = new Dictionary<int, BufferDefinition>();
_storageBuffers = new Dictionary<int, BufferDefinition>();
}
public void AddConstantBuffer(int binding, BufferDefinition definition)
{
_constantBuffers[binding] = definition;
}
public void AddStorageBuffer(int binding, BufferDefinition definition)
{
_storageBuffers[binding] = definition;
}
}
}

View file

@ -280,10 +280,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
}
else if (operation.StorageKind == StorageKind.StorageBuffer)
{
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
}
break;
case Instruction.MultiplyHighS32:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
@ -307,10 +303,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
case Instruction.StoreShared8:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreSharedSmallInt;
break;
case Instruction.StoreStorage16:
case Instruction.StoreStorage8:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreStorageSmallInt;
break;
case Instruction.SwizzleAdd:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd;
break;

View file

@ -57,6 +57,56 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.AtomicXor, storageKind, Local(), a, b, c);
}
public static Operand AtomicAdd(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.AtomicAdd, storageKind, Local(), Const(binding), e0, e1, value);
}
public static Operand AtomicAnd(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.AtomicAnd, storageKind, Local(), Const(binding), e0, e1, value);
}
public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand compare, Operand value)
{
return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), Const(binding), e0, e1, compare, value);
}
public static Operand AtomicMaxS32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.AtomicMaxS32, storageKind, Local(), Const(binding), e0, e1, value);
}
public static Operand AtomicMaxU32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.AtomicMaxU32, storageKind, Local(), Const(binding), e0, e1, value);
}
public static Operand AtomicMinS32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.AtomicMinS32, storageKind, Local(), Const(binding), e0, e1, value);
}
public static Operand AtomicMinU32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.AtomicMinU32, storageKind, Local(), Const(binding), e0, e1, value);
}
public static Operand AtomicOr(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.AtomicOr, storageKind, Local(), Const(binding), e0, e1, value);
}
public static Operand AtomicSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.AtomicSwap, storageKind, Local(), Const(binding), e0, e1, value);
}
public static Operand AtomicXor(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.AtomicXor, storageKind, Local(), Const(binding), e0, e1, value);
}
public static Operand Ballot(this EmitterContext context, Operand a)
{
return context.Add(Instruction.Ballot, Local(), a);
@ -554,6 +604,11 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(fpType | Instruction.IsNan, Local(), a);
}
public static Operand Load(this EmitterContext context, StorageKind storageKind, Operand e0, Operand e1)
{
return context.Add(Instruction.Load, storageKind, Local(), e0, e1);
}
public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding)
{
return context.Add(Instruction.Load, storageKind, Local(), Const(binding));
@ -606,11 +661,6 @@ namespace Ryujinx.Graphics.Shader.Translation
: context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex);
}
public static Operand LoadGlobal(this EmitterContext context, Operand a, Operand b)
{
return context.Add(Instruction.LoadGlobal, Local(), a, b);
}
public static Operand LoadLocal(this EmitterContext context, Operand a)
{
return context.Add(Instruction.LoadLocal, Local(), a);
@ -655,7 +705,6 @@ namespace Ryujinx.Graphics.Shader.Translation
public static void Return(this EmitterContext context)
{
context.PrepareForReturn();
context.Add(Instruction.Return);
}
@ -699,6 +748,16 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c);
}
public static Operand Store(this EmitterContext context, StorageKind storageKind, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.Store, storageKind, null, e0, e1, value);
}
public static Operand Store(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
{
return context.Add(Instruction.Store, storageKind, null, Const(binding), e0, e1, value);
}
public static Operand Store(
this EmitterContext context,
StorageKind storageKind,
@ -738,21 +797,6 @@ namespace Ryujinx.Graphics.Shader.Translation
: context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value);
}
public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.StoreGlobal, null, a, b, c);
}
public static Operand StoreGlobal16(this EmitterContext context, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.StoreGlobal16, null, a, b, c);
}
public static Operand StoreGlobal8(this EmitterContext context, Operand a, Operand b, Operand c)
{
return context.Add(Instruction.StoreGlobal8, null, a, b, c);
}
public static Operand StoreLocal(this EmitterContext context, Operand a, Operand b)
{
return context.Add(Instruction.StoreLocal, null, a, b);

View file

@ -1,54 +0,0 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
namespace Ryujinx.Graphics.Shader.Translation
{
static class GlobalMemory
{
private const int StorageDescsBaseOffset = 0x44; // In words.
public const int StorageDescSize = 4; // In words.
public const int StorageMaxCount = 16;
public const int StorageDescsSize = StorageDescSize * StorageMaxCount;
public const int UbeBaseOffset = 0x98; // In words.
public const int UbeMaxCount = 9;
public const int UbeDescsSize = StorageDescSize * UbeMaxCount;
public const int UbeFirstCbuf = 8;
public const int DriverReservedCb = 0;
public static bool UsesGlobalMemory(Instruction inst, StorageKind storageKind)
{
return (inst.IsAtomic() && storageKind == StorageKind.GlobalMemory) ||
inst == Instruction.LoadGlobal ||
inst == Instruction.StoreGlobal ||
inst == Instruction.StoreGlobal16 ||
inst == Instruction.StoreGlobal8;
}
public static int GetStorageCbOffset(ShaderStage stage, int slot)
{
return GetStorageBaseCbOffset(stage) + slot * StorageDescSize;
}
public static int GetStorageBaseCbOffset(ShaderStage stage)
{
return stage switch
{
ShaderStage.Compute => StorageDescsBaseOffset + 2 * StorageDescsSize,
ShaderStage.Vertex => StorageDescsBaseOffset,
ShaderStage.TessellationControl => StorageDescsBaseOffset + 1 * StorageDescsSize,
ShaderStage.TessellationEvaluation => StorageDescsBaseOffset + 2 * StorageDescsSize,
ShaderStage.Geometry => StorageDescsBaseOffset + 3 * StorageDescsSize,
ShaderStage.Fragment => StorageDescsBaseOffset + 4 * StorageDescsSize,
_ => 0
};
}
public static int GetConstantUbeOffset(int slot)
{
return UbeBaseOffset + slot * StorageDescSize;
}
}
}

View file

@ -19,6 +19,14 @@ namespace Ryujinx.Graphics.Shader.Translation
_stage = stage;
}
public int AddFunction(Function function)
{
int functionId = _functionList.Count;
_functionList.Add(function);
return functionId;
}
public int GetOrCreateFunctionId(HelperFunctionName functionName)
{
if (_functionIds.TryGetValue(functionName, out int functionId))
@ -27,8 +35,7 @@ namespace Ryujinx.Graphics.Shader.Translation
}
Function function = GenerateFunction(functionName);
functionId = _functionList.Count;
_functionList.Add(function);
functionId = AddFunction(function);
_functionIds.Add(functionName, functionId);
return functionId;

View file

@ -7,17 +7,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
static class Optimizer
{
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config)
{
RunOptimizationPasses(blocks, config);
int sbUseMask = 0;
int ubeUseMask = 0;
GlobalToStorage.RunPass(hfm, blocks, config);
// Those passes are looking for specific patterns and only needs to run once.
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
{
GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask);
BindlessToIndexed.RunPass(blocks[blkIndex], config);
BindlessElimination.RunPass(blocks[blkIndex], config);
@ -28,8 +26,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
}
}
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
// Run optimizations one last time to remove any code that is now optimizable after above passes.
RunOptimizationPasses(blocks, config);
}

View file

@ -13,7 +13,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
switch (operation.Inst)
{
case Instruction.Add:
case Instruction.BitwiseExclusiveOr:
TryEliminateBinaryOpCommutative(operation, 0);
break;
@ -21,6 +20,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
TryEliminateBitwiseAnd(operation);
break;
case Instruction.BitwiseExclusiveOr:
if (!TryEliminateXorSwap(operation))
{
TryEliminateBinaryOpCommutative(operation, 0);
}
break;
case Instruction.BitwiseOr:
TryEliminateBitwiseOr(operation);
break;
@ -49,8 +55,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
private static void TryEliminateBitwiseAnd(Operation operation)
{
// Try to recognize and optimize those 3 patterns (in order):
// x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
// x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
// x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
// x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
Operand x = operation.GetSource(0);
Operand y = operation.GetSource(1);
@ -68,11 +75,62 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
}
}
private static bool TryEliminateXorSwap(Operation xCopyOp)
{
// Try to recognize XOR swap pattern:
// x = x ^ y
// y = x ^ y
// x = x ^ y
// Or, in SSA:
// x2 = x ^ y
// y2 = x2 ^ y
// x3 = x2 ^ y2
// Transform it into something more sane:
// temp = y
// y = x
// x = temp
// Note that because XOR is commutative, there are actually
// multiple possible combinations of this pattern, for
// simplicity this only catches one of them.
Operand x = xCopyOp.GetSource(0);
Operand y = xCopyOp.GetSource(1);
if (x.AsgOp is not Operation tCopyOp || tCopyOp.Inst != Instruction.BitwiseExclusiveOr ||
y.AsgOp is not Operation yCopyOp || yCopyOp.Inst != Instruction.BitwiseExclusiveOr)
{
return false;
}
if (tCopyOp == yCopyOp)
{
return false;
}
if (yCopyOp.GetSource(0) != x ||
yCopyOp.GetSource(1) != tCopyOp.GetSource(1) ||
x.UseOps.Count != 2)
{
return false;
}
x = tCopyOp.GetSource(0);
y = tCopyOp.GetSource(1);
tCopyOp.TurnIntoCopy(y); // Temp = Y
yCopyOp.TurnIntoCopy(x); // Y = X
xCopyOp.TurnIntoCopy(tCopyOp.Dest); // X = Temp
return true;
}
private static void TryEliminateBitwiseOr(Operation operation)
{
// Try to recognize and optimize those 3 patterns (in order):
// x | 0x00000000 == x, 0x00000000 | y == y,
// x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
// x | 0x00000000 == x, 0x00000000 | y == y,
// x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
Operand x = operation.GetSource(0);
Operand y = operation.GetSource(1);

View file

@ -1,4 +1,5 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
@ -93,5 +94,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
return source;
}
public static void DeleteNode(LinkedListNode<INode> node, Operation operation)
{
node.List.Remove(node);
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
{
operation.SetSource(srcIndex, null);
}
operation.Dest = null;
}
}
}

View file

@ -14,6 +14,11 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly string _stagePrefix;
private readonly int[] _cbSlotToBindingMap;
private readonly int[] _sbSlotToBindingMap;
private uint _sbSlotWritten;
private readonly Dictionary<int, int> _sbSlots;
private readonly Dictionary<int, int> _sbSlotsReverse;
private readonly HashSet<int> _usedConstantBufferBindings;
@ -26,7 +31,12 @@ namespace Ryujinx.Graphics.Shader.Translation
_stagePrefix = GetShaderStagePrefix(stage);
_cbSlotToBindingMap = new int[18];
_sbSlotToBindingMap = new int[16];
_cbSlotToBindingMap.AsSpan().Fill(-1);
_sbSlotToBindingMap.AsSpan().Fill(-1);
_sbSlots = new Dictionary<int, int>();
_sbSlotsReverse = new Dictionary<int, int>();
_usedConstantBufferBindings = new HashSet<int>();
@ -47,6 +57,52 @@ namespace Ryujinx.Graphics.Shader.Translation
return binding;
}
public bool TryGetStorageBufferBinding(int sbCbSlot, int sbCbOffset, bool write, out int binding)
{
if (!TryGetSbSlot((byte)sbCbSlot, (ushort)sbCbOffset, out int slot))
{
binding = 0;
return false;
}
binding = _sbSlotToBindingMap[slot];
if (binding < 0)
{
binding = _gpuAccessor.QueryBindingStorageBuffer(slot);
_sbSlotToBindingMap[slot] = binding;
string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}");
}
if (write)
{
_sbSlotWritten |= 1u << slot;
}
return true;
}
private bool TryGetSbSlot(byte sbCbSlot, ushort sbCbOffset, out int slot)
{
int key = PackSbCbInfo(sbCbSlot, sbCbOffset);
if (!_sbSlots.TryGetValue(key, out slot))
{
slot = _sbSlots.Count;
if (slot >= _sbSlotToBindingMap.Length)
{
return false;
}
_sbSlots.Add(key, slot);
_sbSlotsReverse.Add(slot, key);
}
return true;
}
public bool TryGetConstantBufferSlot(int binding, out int slot)
{
for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
@ -90,6 +146,34 @@ namespace Ryujinx.Graphics.Shader.Translation
return descriptors;
}
public BufferDescriptor[] GetStorageBufferDescriptors()
{
var descriptors = new BufferDescriptor[_sbSlots.Count];
int descriptorIndex = 0;
foreach ((int key, int slot) in _sbSlots)
{
int binding = _sbSlotToBindingMap[slot];
if (binding >= 0)
{
(int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key);
descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset)
{
Flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None
};
}
}
if (descriptors.Length != descriptorIndex)
{
Array.Resize(ref descriptors, descriptorIndex);
}
return descriptors;
}
private void AddNewConstantBuffer(int binding, string name)
{
StructureType type = new StructureType(new[]
@ -100,6 +184,16 @@ namespace Ryujinx.Graphics.Shader.Translation
_properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
}
private void AddNewStorageBuffer(int binding, string name)
{
StructureType type = new StructureType(new[]
{
new StructureField(AggregateType.Array | AggregateType.U32, "data", 0)
});
_properties.AddStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type));
}
public static string GetShaderStagePrefix(ShaderStage stage)
{
uint index = (uint)stage;
@ -111,5 +205,15 @@ namespace Ryujinx.Graphics.Shader.Translation
return _stagePrefixes[index];
}
private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset)
{
return sbCbOffset | ((int)sbCbSlot << 16);
}
private static (int, int) UnpackSbCbInfo(int key)
{
return ((byte)(key >> 16), (ushort)key);
}
}
}

View file

@ -2,10 +2,8 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
using static Ryujinx.Graphics.Shader.Translation.GlobalMemory;
namespace Ryujinx.Graphics.Shader.Translation
{
@ -23,11 +21,10 @@ namespace Ryujinx.Graphics.Shader.Translation
{
BasicBlock block = blocks[blkIndex];
for (LinkedListNode<INode> node = block.Operations.First; node != null;)
for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
{
if (node.Value is not Operation operation)
{
node = node.Next;
continue;
}
@ -56,8 +53,6 @@ namespace Ryujinx.Graphics.Shader.Translation
InsertVectorComponentSelect(node, config);
}
LinkedListNode<INode> nextNode = node.Next;
if (operation is TextureOperation texOp)
{
node = InsertTexelFetchScale(hfm, node, config);
@ -74,15 +69,7 @@ namespace Ryujinx.Graphics.Shader.Translation
node = InsertSnormNormalization(node, config);
}
}
nextNode = node.Next;
}
else if (UsesGlobalMemory(operation.Inst, operation.StorageKind))
{
nextNode = RewriteGlobalAccess(node, config)?.Next ?? nextNode;
}
node = nextNode;
}
}
}
@ -184,196 +171,6 @@ namespace Ryujinx.Graphics.Shader.Translation
operation.TurnIntoCopy(result);
}
private static LinkedListNode<INode> RewriteGlobalAccess(LinkedListNode<INode> node, ShaderConfig config)
{
Operation operation = (Operation)node.Value;
bool isAtomic = operation.Inst.IsAtomic();
bool isStg16Or8 = operation.Inst == Instruction.StoreGlobal16 || operation.Inst == Instruction.StoreGlobal8;
bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal || isStg16Or8;
Operation storageOp = null;
Operand PrependOperation(Instruction inst, params Operand[] sources)
{
Operand local = Local();
node.List.AddBefore(node, new Operation(inst, local, sources));
return local;
}
Operand PrependStorageOperation(Instruction inst, StorageKind storageKind, params Operand[] sources)
{
Operand local = Local();
node.List.AddBefore(node, new Operation(inst, storageKind, local, sources));
return local;
}
Operand PrependExistingOperation(Operation operation)
{
Operand local = Local();
operation.Dest = local;
node.List.AddBefore(node, operation);
return local;
}
Operand addrLow = operation.GetSource(0);
Operand addrHigh = operation.GetSource(1);
Operand sbBaseAddrLow = Const(0);
Operand sbSlot = Const(0);
Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
Operand BindingRangeCheck(int cbOffset, out Operand baseAddrLow)
{
baseAddrLow = Cbuf(DriverReservedCb, cbOffset);
Operand baseAddrHigh = Cbuf(DriverReservedCb, cbOffset + 1);
Operand size = Cbuf(DriverReservedCb, cbOffset + 2);
Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow);
Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow);
Operand inRangeLow = PrependOperation(Instruction.CompareLessU32, offset, size);
Operand addrHighBorrowed = PrependOperation(Instruction.Add, addrHigh, borrow);
Operand inRangeHigh = PrependOperation(Instruction.CompareEqual, addrHighBorrowed, baseAddrHigh);
return PrependOperation(Instruction.BitwiseAnd, inRangeLow, inRangeHigh);
}
int sbUseMask = config.AccessibleStorageBuffersMask;
while (sbUseMask != 0)
{
int slot = BitOperations.TrailingZeroCount(sbUseMask);
sbUseMask &= ~(1 << slot);
int cbOffset = GetStorageCbOffset(config.Stage, slot);
slot = config.GetSbSlot(DriverReservedCb, (ushort)cbOffset);
config.SetUsedStorageBuffer(slot, isWrite);
Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow);
sbBaseAddrLow = PrependOperation(Instruction.ConditionalSelect, inRange, baseAddrLow, sbBaseAddrLow);
sbSlot = PrependOperation(Instruction.ConditionalSelect, inRange, Const(slot), sbSlot);
}
if (config.AccessibleStorageBuffersMask != 0)
{
Operand baseAddrTrunc = PrependOperation(Instruction.BitwiseAnd, sbBaseAddrLow, alignMask);
Operand byteOffset = PrependOperation(Instruction.Subtract, addrLow, baseAddrTrunc);
Operand[] sources = new Operand[operation.SourcesCount];
sources[0] = sbSlot;
if (isStg16Or8)
{
sources[1] = byteOffset;
}
else
{
sources[1] = PrependOperation(Instruction.ShiftRightU32, byteOffset, Const(2));
}
for (int index = 2; index < operation.SourcesCount; index++)
{
sources[index] = operation.GetSource(index);
}
if (isAtomic)
{
storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources);
}
else if (operation.Inst == Instruction.LoadGlobal)
{
storageOp = new Operation(Instruction.LoadStorage, operation.Dest, sources);
}
else
{
Instruction storeInst = operation.Inst switch
{
Instruction.StoreGlobal16 => Instruction.StoreStorage16,
Instruction.StoreGlobal8 => Instruction.StoreStorage8,
_ => Instruction.StoreStorage
};
storageOp = new Operation(storeInst, null, sources);
}
}
else if (operation.Dest != null)
{
storageOp = new Operation(Instruction.Copy, operation.Dest, Const(0));
}
if (operation.Inst == Instruction.LoadGlobal)
{
int cbeUseMask = config.AccessibleConstantBuffersMask;
while (cbeUseMask != 0)
{
int slot = BitOperations.TrailingZeroCount(cbeUseMask);
int cbSlot = UbeFirstCbuf + slot;
cbeUseMask &= ~(1 << slot);
Operand previousResult = PrependExistingOperation(storageOp);
int cbOffset = GetConstantUbeOffset(slot);
Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow);
Operand baseAddrTruncConst = PrependOperation(Instruction.BitwiseAnd, baseAddrLow, alignMask);
Operand byteOffsetConst = PrependOperation(Instruction.Subtract, addrLow, baseAddrTruncConst);
Operand cbIndex = PrependOperation(Instruction.ShiftRightU32, byteOffsetConst, Const(2));
Operand vecIndex = PrependOperation(Instruction.ShiftRightU32, cbIndex, Const(2));
Operand elemIndex = PrependOperation(Instruction.BitwiseAnd, cbIndex, Const(3));
Operand[] sourcesCb = new Operand[4];
sourcesCb[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot));
sourcesCb[1] = Const(0);
sourcesCb[2] = vecIndex;
sourcesCb[3] = elemIndex;
Operand ldcResult = PrependStorageOperation(Instruction.Load, StorageKind.ConstantBuffer, sourcesCb);
storageOp = new Operation(Instruction.ConditionalSelect, operation.Dest, inRange, ldcResult, previousResult);
}
}
for (int index = 0; index < operation.SourcesCount; index++)
{
operation.SetSource(index, null);
}
LinkedListNode<INode> oldNode = node;
LinkedList<INode> oldNodeList = oldNode.List;
if (storageOp != null)
{
node = node.List.AddBefore(node, storageOp);
}
else
{
node = null;
}
oldNodeList.Remove(oldNode);
return node;
}
private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config)
{
TextureOperation texOp = (TextureOperation)node.Value;

View file

@ -110,12 +110,6 @@ namespace Ryujinx.Graphics.Shader.Translation
public UInt128 NextInputAttributesComponents { get; private set; }
public UInt128 ThisInputAttributesComponents { get; private set; }
public int AccessibleStorageBuffersMask { get; private set; }
public int AccessibleConstantBuffersMask { get; private set; }
private int _usedStorageBuffers;
private int _usedStorageBuffersWrite;
private readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format);
private struct TextureMeta
@ -127,18 +121,9 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
private readonly Dictionary<TextureInfo, TextureMeta> _usedImages;
private readonly Dictionary<int, int> _sbSlots;
private readonly Dictionary<int, int> _sbSlotsReverse;
private BufferDescriptor[] _cachedStorageBufferDescriptors;
private TextureDescriptor[] _cachedTextureDescriptors;
private TextureDescriptor[] _cachedImageDescriptors;
private int _firstStorageBufferBinding;
public int FirstStorageBufferBinding => _firstStorageBufferBinding;
public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options)
{
Stage = stage;
@ -147,18 +132,12 @@ namespace Ryujinx.Graphics.Shader.Translation
_transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>();
AccessibleStorageBuffersMask = (1 << GlobalMemory.StorageMaxCount) - 1;
AccessibleConstantBuffersMask = (1 << GlobalMemory.UbeMaxCount) - 1;
UsedInputAttributesPerPatch = new HashSet<int>();
UsedOutputAttributesPerPatch = new HashSet<int>();
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
_sbSlots = new Dictionary<int, int>();
_sbSlotsReverse = new Dictionary<int, int>();
ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties());
}
@ -173,11 +152,6 @@ namespace Ryujinx.Graphics.Shader.Translation
OutputTopology = outputTopology;
MaxOutputVertices = maxOutputVertices;
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
if (Stage != ShaderStage.Compute)
{
AccessibleConstantBuffersMask = 0;
}
}
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options)
@ -433,8 +407,6 @@ namespace Ryujinx.Graphics.Shader.Translation
UsedInputAttributes |= other.UsedInputAttributes;
UsedOutputAttributes |= other.UsedOutputAttributes;
_usedStorageBuffers |= other._usedStorageBuffers;
_usedStorageBuffersWrite |= other._usedStorageBuffersWrite;
foreach (var kv in other._usedTextures)
{
@ -634,23 +606,6 @@ namespace Ryujinx.Graphics.Shader.Translation
UsedFeatures |= flags;
}
public void SetAccessibleBufferMasks(int sbMask, int ubeMask)
{
AccessibleStorageBuffersMask = sbMask;
AccessibleConstantBuffersMask = ubeMask;
}
public void SetUsedStorageBuffer(int slot, bool write)
{
int mask = 1 << slot;
_usedStorageBuffers |= mask;
if (write)
{
_usedStorageBuffersWrite |= mask;
}
}
public void SetUsedTexture(
Instruction inst,
SamplerType type,
@ -756,76 +711,6 @@ namespace Ryujinx.Graphics.Shader.Translation
return meta;
}
public BufferDescriptor[] GetStorageBufferDescriptors()
{
if (_cachedStorageBufferDescriptors != null)
{
return _cachedStorageBufferDescriptors;
}
return _cachedStorageBufferDescriptors = GetStorageBufferDescriptors(
_usedStorageBuffers,
_usedStorageBuffersWrite,
true,
out _firstStorageBufferBinding,
GpuAccessor.QueryBindingStorageBuffer);
}
private BufferDescriptor[] GetStorageBufferDescriptors(
int usedMask,
int writtenMask,
bool isArray,
out int firstBinding,
Func<int, int> getBindingCallback)
{
firstBinding = 0;
bool hasFirstBinding = false;
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++)
{
int binding = getBindingCallback(j);
if (!hasFirstBinding)
{
firstBinding = binding;
hasFirstBinding = true;
}
}
}
lastSlot = slot;
(int sbCbSlot, int sbCbOffset) = GetSbCbInfo(slot);
descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot, sbCbSlot, sbCbOffset);
if (!hasFirstBinding)
{
firstBinding = descriptors[i].Binding;
hasFirstBinding = true;
}
if ((writtenMask & (1 << slot)) != 0)
{
descriptors[i].SetFlag(BufferUsageFlags.Write);
}
usedMask &= ~(1 << slot);
}
return descriptors;
}
public TextureDescriptor[] GetTextureDescriptors()
{
return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, GpuAccessor.QueryBindingTexture);
@ -922,45 +807,11 @@ namespace Ryujinx.Graphics.Shader.Translation
return FindDescriptorIndex(GetImageDescriptors(), texOp);
}
public int GetSbSlot(byte sbCbSlot, ushort sbCbOffset)
{
int key = PackSbCbInfo(sbCbSlot, sbCbOffset);
if (!_sbSlots.TryGetValue(key, out int slot))
{
slot = _sbSlots.Count;
_sbSlots.Add(key, slot);
_sbSlotsReverse.Add(slot, key);
}
return slot;
}
public (int, int) GetSbCbInfo(int slot)
{
if (_sbSlotsReverse.TryGetValue(slot, out int key))
{
return UnpackSbCbInfo(key);
}
throw new ArgumentException($"Invalid slot {slot}.", nameof(slot));
}
private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset)
{
return sbCbOffset | ((int)sbCbSlot << 16);
}
private static (int, int) UnpackSbCbInfo(int key)
{
return ((byte)(key >> 16), (ushort)key);
}
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
{
return new ShaderProgramInfo(
ResourceManager.GetConstantBufferDescriptors(),
GetStorageBufferDescriptors(),
ResourceManager.GetStorageBufferDescriptors(),
GetTextureDescriptors(),
GetImageDescriptors(),
identification,

View file

@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Shader.Translation
continue;
}
if (IsResourceWrite(operation.Inst))
if (IsResourceWrite(operation.Inst, operation.StorageKind))
{
return false;
}
@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return totalVerticesCount + verticesCount == 3 && writesLayer;
}
private static bool IsResourceWrite(Instruction inst)
private static bool IsResourceWrite(Instruction inst, StorageKind storageKind)
{
switch (inst)
{
@ -170,13 +170,11 @@ namespace Ryujinx.Graphics.Shader.Translation
case Instruction.AtomicXor:
case Instruction.ImageAtomic:
case Instruction.ImageStore:
case Instruction.StoreGlobal:
case Instruction.StoreGlobal16:
case Instruction.StoreGlobal8:
case Instruction.StoreStorage:
case Instruction.StoreStorage16:
case Instruction.StoreStorage8:
return true;
case Instruction.Store:
return storageKind == StorageKind.StorageBuffer ||
storageKind == StorageKind.SharedMemory ||
storageKind == StorageKind.LocalMemory;
}
return false;

View file

@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.Translation
Ssa.Rename(cfg.Blocks);
Optimizer.RunPass(cfg.Blocks, config);
Optimizer.RunPass(hfm, cfg.Blocks, config);
Rewriter.RunPass(hfm, cfg.Blocks, config);
}

View file

@ -115,8 +115,6 @@ namespace Ryujinx.Graphics.Vulkan
holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
if (holder == null)
{
Logger.Error?.Print(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X} and type \"{baseType}\".");
return BufferHandle.Null;
}
@ -264,6 +262,8 @@ namespace Ryujinx.Graphics.Vulkan
return holder;
}
Logger.Error?.Print(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X} and type \"{baseType}\".");
return null;
}

View file

@ -70,30 +70,6 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public unsafe void UpdateStorageBuffers(int setIndex, int baseBinding, ReadOnlySpan<DescriptorBufferInfo> bufferInfo)
{
if (bufferInfo.Length == 0)
{
return;
}
fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo)
{
var writeDescriptorSet = new WriteDescriptorSet
{
SType = StructureType.WriteDescriptorSet,
DstSet = _descriptorSets[setIndex],
DstBinding = (uint)(baseBinding & ~(Constants.MaxStorageBuffersPerStage - 1)),
DstArrayElement = (uint)(baseBinding & (Constants.MaxStorageBuffersPerStage - 1)),
DescriptorType = DescriptorType.StorageBuffer,
DescriptorCount = (uint)bufferInfo.Length,
PBufferInfo = pBufferInfo
};
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
}
}
public unsafe void UpdateImage(int setIndex, int bindingIndex, DescriptorImageInfo imageInfo, DescriptorType type)
{
if (imageInfo.ImageView.Handle != 0UL)

View file

@ -448,14 +448,7 @@ namespace Ryujinx.Graphics.Vulkan
}
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
if (program.HasMinimalLayout)
{
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
}
else
{
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
}
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
}
else if (setIndex == PipelineBase.TextureSetIndex)
{