forked from Mirror/Ryujinx
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:
parent
81c9052847
commit
21c9ac6240
42 changed files with 1468 additions and 1259 deletions
|
@ -80,11 +80,6 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int GobAlignment = 64;
|
public const int GobAlignment = 64;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Expected byte alignment for storage buffers
|
|
||||||
/// </summary>
|
|
||||||
public const int StorageAlignment = 16;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of the uniform buffer reserved by the driver to store the storage buffer base addresses.
|
/// Number of the uniform buffer reserved by the driver to store the storage buffer base addresses.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -187,30 +187,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||||
info = cs.Shaders[0].Info;
|
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.BufferManager.SetComputeBufferBindings(cs.Bindings);
|
||||||
|
|
||||||
_channel.TextureManager.SetComputeBindings(cs.Bindings);
|
_channel.TextureManager.SetComputeBindings(cs.Bindings);
|
||||||
|
|
|
@ -222,7 +222,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||||
private void RecordStorageAlignment(BuffersPerStage buffers, int index, ulong gpuVa)
|
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)
|
if (unaligned || HasUnalignedStorageBuffers)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
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 SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
int imageBinding = stageIndex * imagesPerStage * 2;
|
int imageBinding = stageIndex * imagesPerStage * 2;
|
||||||
|
|
||||||
AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage);
|
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.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
|
||||||
AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
|
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);
|
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>
|
/// <summary>
|
||||||
/// Adds buffer usage information to the list of usages.
|
/// Adds buffer usage information to the list of usages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -104,14 +104,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||||
|
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
|
||||||
var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
|
|
||||||
if (sBufferDescriptors.Length != 0)
|
|
||||||
{
|
|
||||||
DeclareStorages(context, sBufferDescriptors);
|
|
||||||
|
|
||||||
context.AppendLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
var textureDescriptors = context.Config.GetTextureDescriptors();
|
var textureDescriptors = context.Config.GetTextureDescriptors();
|
||||||
if (textureDescriptors.Length != 0)
|
if (textureDescriptors.Length != 0)
|
||||||
|
@ -250,11 +243,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.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)
|
if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)
|
||||||
{
|
{
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl");
|
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");
|
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)
|
if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0)
|
||||||
{
|
{
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl");
|
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)
|
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)
|
foreach (BufferDefinition buffer in buffers)
|
||||||
{
|
{
|
||||||
|
@ -365,7 +358,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
_ => "std430"
|
_ => "std430"
|
||||||
};
|
};
|
||||||
|
|
||||||
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) uniform _{buffer.Name}");
|
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}");
|
||||||
context.EnterScope();
|
context.EnterScope();
|
||||||
|
|
||||||
foreach (StructureField field in buffer.Type.Fields)
|
foreach (StructureField field in buffer.Type.Fields)
|
||||||
|
@ -373,11 +366,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
if (field.Type.HasFlag(AggregateType.Array))
|
if (field.Type.HasFlag(AggregateType.Array))
|
||||||
{
|
{
|
||||||
string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array);
|
string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array);
|
||||||
|
|
||||||
|
if (field.ArrayLength > 0)
|
||||||
|
{
|
||||||
string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture);
|
string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
context.AppendLine($"{typeName} {field.Name}[{arraySize}];");
|
context.AppendLine($"{typeName} {field.Name}[{arraySize}];");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
context.AppendLine($"{typeName} {field.Name}[];");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
string typeName = GetVarTypeName(context, field.Type);
|
string typeName = GetVarTypeName(context, field.Type);
|
||||||
|
|
||||||
|
@ -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)
|
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||||
{
|
{
|
||||||
int arraySize = 0;
|
int arraySize = 0;
|
||||||
|
@ -733,7 +718,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
code = code.Replace("\t", CodeGenContext.Tab);
|
code = code.Replace("\t", CodeGenContext.Tab);
|
||||||
code = code.Replace("$SHARED_MEM$", DefaultNames.SharedMemoryName);
|
code = code.Replace("$SHARED_MEM$", DefaultNames.SharedMemoryName);
|
||||||
code = code.Replace("$STORAGE_MEM$", OperandManager.GetShaderStagePrefix(context.Config.Stage) + "_" + DefaultNames.StorageNamePrefix);
|
|
||||||
|
|
||||||
if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot())
|
if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot())
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,12 +11,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
public const string IAttributePrefix = "in_attr";
|
public const string IAttributePrefix = "in_attr";
|
||||||
public const string OAttributePrefix = "out_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 LocalMemoryName = "local_mem";
|
||||||
public const string SharedMemoryName = "shared_mem";
|
public const string SharedMemoryName = "shared_mem";
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -68,33 +68,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
|
|
||||||
string args = string.Empty;
|
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.
|
// 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.
|
// 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.
|
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (int argIndex = 2; argIndex < arity; argIndex++)
|
||||||
|
{
|
||||||
|
args += ", ";
|
||||||
|
|
||||||
|
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
||||||
|
|
||||||
|
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int argIndex = 0; argIndex < arity; argIndex++)
|
||||||
|
{
|
||||||
if (argIndex != 0)
|
if (argIndex != 0)
|
||||||
{
|
{
|
||||||
args += ", ";
|
args += ", ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argIndex == 0 && atomic)
|
|
||||||
{
|
|
||||||
switch (operation.StorageKind)
|
|
||||||
{
|
|
||||||
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}\".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
||||||
|
|
||||||
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||||
|
@ -173,9 +185,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
case Instruction.LoadShared:
|
case Instruction.LoadShared:
|
||||||
return LoadShared(context, operation);
|
return LoadShared(context, operation);
|
||||||
|
|
||||||
case Instruction.LoadStorage:
|
|
||||||
return LoadStorage(context, operation);
|
|
||||||
|
|
||||||
case Instruction.Lod:
|
case Instruction.Lod:
|
||||||
return Lod(context, operation);
|
return Lod(context, operation);
|
||||||
|
|
||||||
|
@ -203,15 +212,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
case Instruction.StoreShared8:
|
case Instruction.StoreShared8:
|
||||||
return StoreShared8(context, operation);
|
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:
|
case Instruction.TextureSample:
|
||||||
return TextureSample(context, operation);
|
return TextureSample(context, operation);
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
Add(Instruction.Load, InstType.Special);
|
Add(Instruction.Load, InstType.Special);
|
||||||
Add(Instruction.LoadLocal, InstType.Special);
|
Add(Instruction.LoadLocal, InstType.Special);
|
||||||
Add(Instruction.LoadShared, InstType.Special);
|
Add(Instruction.LoadShared, InstType.Special);
|
||||||
Add(Instruction.LoadStorage, InstType.Special);
|
|
||||||
Add(Instruction.Lod, InstType.Special);
|
Add(Instruction.Lod, InstType.Special);
|
||||||
Add(Instruction.LogarithmB2, InstType.CallUnary, "log2");
|
Add(Instruction.LogarithmB2, InstType.CallUnary, "log2");
|
||||||
Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9);
|
Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9);
|
||||||
|
@ -123,9 +122,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
Add(Instruction.StoreShared, InstType.Special);
|
Add(Instruction.StoreShared, InstType.Special);
|
||||||
Add(Instruction.StoreShared16, InstType.Special);
|
Add(Instruction.StoreShared16, InstType.Special);
|
||||||
Add(Instruction.StoreShared8, 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.Subtract, InstType.OpBinary, "-", 2);
|
||||||
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
||||||
Add(Instruction.TextureSample, InstType.Special);
|
Add(Instruction.TextureSample, InstType.Special);
|
||||||
|
|
|
@ -210,17 +210,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
return $"{arrayName}[{offsetExpr}]";
|
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)
|
public static string Lod(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
@ -326,60 +315,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
return $"{HelperFunctionNames.StoreShared8}({offsetExpr}, {src})";
|
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)
|
public static string TextureSample(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)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;
|
StorageKind storageKind = operation.StorageKind;
|
||||||
|
|
||||||
string varName;
|
string varName;
|
||||||
AggregateType varType;
|
AggregateType varType;
|
||||||
int srcIndex = 0;
|
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)
|
switch (storageKind)
|
||||||
{
|
{
|
||||||
case StorageKind.ConstantBuffer:
|
case StorageKind.ConstantBuffer:
|
||||||
|
case StorageKind.StorageBuffer:
|
||||||
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
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.");
|
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int binding = bindingIndex.Value;
|
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)
|
if (!(operation.GetSource(srcIndex++) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
|
||||||
{
|
{
|
||||||
|
@ -825,15 +769,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
return varName;
|
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)
|
private static string GetMask(int index)
|
||||||
{
|
{
|
||||||
return $".{"rgba".AsSpan(index, 1)}";
|
return $".{"rgba".AsSpan(index, 1)}";
|
||||||
|
|
|
@ -118,6 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
switch (operation.StorageKind)
|
switch (operation.StorageKind)
|
||||||
{
|
{
|
||||||
case StorageKind.ConstantBuffer:
|
case StorageKind.ConstantBuffer:
|
||||||
|
case StorageKind.StorageBuffer:
|
||||||
if (!(operation.GetSource(0) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
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.");
|
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.");
|
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];
|
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||||
|
|
||||||
return field.Type & AggregateType.ElementTypeMask;
|
return field.Type & AggregateType.ElementTypeMask;
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
public int InputVertices { get; }
|
public int InputVertices { get; }
|
||||||
|
|
||||||
public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
|
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 LocalMemory { get; set; }
|
||||||
public Instruction SharedMemory { get; set; }
|
public Instruction SharedMemory { get; set; }
|
||||||
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
|
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
|
||||||
|
@ -307,9 +307,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
public Instruction GetType(AggregateType type, int length = 1)
|
public Instruction GetType(AggregateType type, int length = 1)
|
||||||
{
|
{
|
||||||
if ((type & AggregateType.Array) != 0)
|
if ((type & AggregateType.Array) != 0)
|
||||||
|
{
|
||||||
|
if (length > 0)
|
||||||
{
|
{
|
||||||
return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length));
|
return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TypeRuntimeArray(GetType(type & ~AggregateType.Array));
|
||||||
|
}
|
||||||
|
}
|
||||||
else if ((type & AggregateType.ElementCountMask) != 0)
|
else if ((type & AggregateType.ElementCountMask) != 0)
|
||||||
{
|
{
|
||||||
int vectorLength = (type & AggregateType.ElementCountMask) switch
|
int vectorLength = (type & AggregateType.ElementCountMask) switch
|
||||||
|
|
|
@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.Translation;
|
||||||
using Spv.Generator;
|
using Spv.Generator;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
|
@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||||
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
|
||||||
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
||||||
DeclareImages(context, context.Config.GetImageDescriptors());
|
DeclareImages(context, context.Config.GetImageDescriptors());
|
||||||
DeclareInputsAndOutputs(context, info);
|
DeclareInputsAndOutputs(context, info);
|
||||||
|
@ -127,6 +128,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
|
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>();
|
HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>();
|
||||||
|
|
||||||
|
@ -155,6 +166,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize);
|
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;
|
offset += fieldSize * field.ArrayLength;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -163,54 +180,35 @@ 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++)
|
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 pointerType = context.TypePointer(StorageClass.Uniform, structType);
|
||||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
var variable = context.Variable(pointerType, StorageClass.Uniform);
|
||||||
|
|
||||||
context.Name(ubVariable, buffer.Name);
|
context.Name(variable, buffer.Name);
|
||||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
|
context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
|
||||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding);
|
context.Decorate(variable, Decoration.Binding, (LiteralInteger)buffer.Binding);
|
||||||
context.AddGlobalVariable(ubVariable);
|
context.AddGlobalVariable(variable);
|
||||||
context.ConstantBuffers.Add(buffer.Binding, ubVariable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
|
if (isBuffer)
|
||||||
{
|
{
|
||||||
if (descriptors.Length == 0)
|
context.StorageBuffers.Add(buffer.Binding, variable);
|
||||||
{
|
}
|
||||||
return;
|
else
|
||||||
|
{
|
||||||
|
context.ConstantBuffers.Add(buffer.Binding, variable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||||
|
|
|
@ -99,7 +99,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
Add(Instruction.Load, GenerateLoad);
|
Add(Instruction.Load, GenerateLoad);
|
||||||
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
||||||
Add(Instruction.LoadShared, GenerateLoadShared);
|
Add(Instruction.LoadShared, GenerateLoadShared);
|
||||||
Add(Instruction.LoadStorage, GenerateLoadStorage);
|
|
||||||
Add(Instruction.Lod, GenerateLod);
|
Add(Instruction.Lod, GenerateLod);
|
||||||
Add(Instruction.LogarithmB2, GenerateLogarithmB2);
|
Add(Instruction.LogarithmB2, GenerateLogarithmB2);
|
||||||
Add(Instruction.LogicalAnd, GenerateLogicalAnd);
|
Add(Instruction.LogicalAnd, GenerateLogicalAnd);
|
||||||
|
@ -137,9 +136,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
Add(Instruction.StoreShared, GenerateStoreShared);
|
Add(Instruction.StoreShared, GenerateStoreShared);
|
||||||
Add(Instruction.StoreShared16, GenerateStoreShared16);
|
Add(Instruction.StoreShared16, GenerateStoreShared16);
|
||||||
Add(Instruction.StoreShared8, GenerateStoreShared8);
|
Add(Instruction.StoreShared8, GenerateStoreShared8);
|
||||||
Add(Instruction.StoreStorage, GenerateStoreStorage);
|
|
||||||
Add(Instruction.StoreStorage16, GenerateStoreStorage16);
|
|
||||||
Add(Instruction.StoreStorage8, GenerateStoreStorage8);
|
|
||||||
Add(Instruction.Subtract, GenerateSubtract);
|
Add(Instruction.Subtract, GenerateSubtract);
|
||||||
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
||||||
Add(Instruction.TextureSample, GenerateTextureSample);
|
Add(Instruction.TextureSample, GenerateTextureSample);
|
||||||
|
@ -889,14 +885,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
return new OperationResult(AggregateType.U32, value);
|
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)
|
private static OperationResult GenerateLod(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
@ -1307,28 +1295,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
return OperationResult.Invalid;
|
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)
|
private static OperationResult GenerateSubtract(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
return GenerateBinary(context, operation, context.Delegates.FSub, context.Delegates.ISub);
|
return GenerateBinary(context, operation, context.Delegates.FSub, context.Delegates.ISub);
|
||||||
|
@ -1849,13 +1815,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
AstOperation operation,
|
AstOperation operation,
|
||||||
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitU)
|
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;
|
SpvInstruction elemPointer;
|
||||||
|
|
||||||
if (operation.StorageKind == StorageKind.StorageBuffer)
|
if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||||
{
|
{
|
||||||
elemPointer = GetStorageElemPointer(context, operation);
|
elemPointer = GetStoragePointer(context, operation, out _);
|
||||||
}
|
}
|
||||||
else if (operation.StorageKind == StorageKind.SharedMemory)
|
else if (operation.StorageKind == StorageKind.SharedMemory)
|
||||||
{
|
{
|
||||||
|
@ -1875,14 +1841,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
|
|
||||||
private static OperationResult GenerateAtomicMemoryCas(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateAtomicMemoryCas(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var value0 = context.GetU32(operation.GetSource(2));
|
var value0 = context.GetU32(operation.GetSource(operation.SourcesCount - 2));
|
||||||
var value1 = context.GetU32(operation.GetSource(3));
|
var value1 = context.GetU32(operation.GetSource(operation.SourcesCount - 1));
|
||||||
|
|
||||||
SpvInstruction elemPointer;
|
SpvInstruction elemPointer;
|
||||||
|
|
||||||
if (operation.StorageKind == StorageKind.StorageBuffer)
|
if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||||
{
|
{
|
||||||
elemPointer = GetStorageElemPointer(context, operation);
|
elemPointer = GetStoragePointer(context, operation, out _);
|
||||||
}
|
}
|
||||||
else if (operation.StorageKind == StorageKind.SharedMemory)
|
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)
|
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;
|
StorageKind storageKind = operation.StorageKind;
|
||||||
|
|
||||||
StorageClass storageClass;
|
StorageClass storageClass;
|
||||||
SpvInstruction baseObj;
|
SpvInstruction baseObj;
|
||||||
AggregateType varType;
|
|
||||||
int srcIndex = 0;
|
int srcIndex = 0;
|
||||||
|
|
||||||
switch (storageKind)
|
switch (storageKind)
|
||||||
{
|
{
|
||||||
case StorageKind.ConstantBuffer:
|
case StorageKind.ConstantBuffer:
|
||||||
|
case StorageKind.StorageBuffer:
|
||||||
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
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.");
|
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.");
|
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];
|
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||||
|
|
||||||
storageClass = StorageClass.Uniform;
|
storageClass = StorageClass.Uniform;
|
||||||
varType = field.Type & AggregateType.ElementTypeMask;
|
varType = field.Type & AggregateType.ElementTypeMask;
|
||||||
baseObj = context.ConstantBuffers[bindingIndex.Value];
|
baseObj = storageKind == StorageKind.ConstantBuffer
|
||||||
|
? context.ConstantBuffers[bindingIndex.Value]
|
||||||
|
: context.StorageBuffers[bindingIndex.Value];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StorageKind.Input:
|
case StorageKind.Input:
|
||||||
|
@ -1993,7 +1979,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
|
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 e0, e1, e2;
|
||||||
SpvInstruction pointer;
|
SpvInstruction pointer;
|
||||||
|
|
||||||
|
@ -2030,16 +2023,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isStore)
|
return pointer;
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable)
|
private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable)
|
||||||
|
@ -2068,25 +2052,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize);
|
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(
|
private static void GenerateStoreSmallInt(
|
||||||
CodeGenContext context,
|
CodeGenContext context,
|
||||||
SpvInstruction elemPointer,
|
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(
|
private static OperationResult GenerateUnary(
|
||||||
CodeGenContext context,
|
CodeGenContext context,
|
||||||
AstOperation operation,
|
AstOperation operation,
|
||||||
|
|
|
@ -10,7 +10,5 @@ namespace Ryujinx.Graphics.Shader
|
||||||
public const int NvnBaseVertexByteOffset = 0x640;
|
public const int NvnBaseVertexByteOffset = 0x640;
|
||||||
public const int NvnBaseInstanceByteOffset = 0x644;
|
public const int NvnBaseInstanceByteOffset = 0x644;
|
||||||
public const int NvnDrawIndexByteOffset = 0x648;
|
public const int NvnDrawIndexByteOffset = 0x648;
|
||||||
|
|
||||||
public const int StorageAlignment = 16;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Shader
|
||||||
/// <returns>Binding number</returns>
|
/// <returns>Binding number</returns>
|
||||||
int QueryBindingConstantBuffer(int index)
|
int QueryBindingConstantBuffer(int index)
|
||||||
{
|
{
|
||||||
return index;
|
return index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -164,6 +164,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (op.Ccc == Ccc.T)
|
if (op.Ccc == Ccc.T)
|
||||||
{
|
{
|
||||||
|
context.PrepareForReturn();
|
||||||
context.Return();
|
context.Return();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -175,6 +176,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
{
|
{
|
||||||
Operand lblSkip = Label();
|
Operand lblSkip = Label();
|
||||||
context.BranchIfFalse(lblSkip, cond);
|
context.BranchIfFalse(lblSkip, cond);
|
||||||
|
context.PrepareForReturn();
|
||||||
context.Return();
|
context.Return();
|
||||||
context.MarkLabel(lblSkip);
|
context.MarkLabel(lblSkip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,13 +336,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
int offset,
|
int offset,
|
||||||
bool extended)
|
bool extended)
|
||||||
{
|
{
|
||||||
bool isSmallInt = size < LsSize.B32;
|
|
||||||
|
|
||||||
int count = GetVectorCount(size);
|
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++)
|
for (int index = 0; index < count; index++)
|
||||||
{
|
{
|
||||||
|
@ -353,12 +352,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand value = context.LoadGlobal(context.IAdd(addrLow, Const(index * 4)), addrHigh);
|
Operand value = context.Load(storageKind, context.IAdd(srcA, Const(offset + index * 4)), addrHigh);
|
||||||
|
|
||||||
if (isSmallInt)
|
|
||||||
{
|
|
||||||
value = ExtractSmallInt(context, size, bitOffset, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(Register(dest), value);
|
context.Copy(Register(dest), value);
|
||||||
}
|
}
|
||||||
|
@ -445,10 +439,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = GetVectorCount((LsSize)size);
|
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++)
|
for (int index = 0; index < count; index++)
|
||||||
{
|
{
|
||||||
|
@ -456,21 +451,22 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
Operand value = Register(isRz ? rd : rd + index, RegisterType.Gpr);
|
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.Store(storageKind, addrLowOffset, addrHigh, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StorageKind GetStorageKind(LsSize size)
|
||||||
{
|
{
|
||||||
context.StoreGlobal8(addrLowOffset, addrHigh, value);
|
return size switch
|
||||||
}
|
|
||||||
else if (size == LsSize2.U16 || size == LsSize2.S16)
|
|
||||||
{
|
{
|
||||||
context.StoreGlobal16(addrLowOffset, addrHigh, value);
|
LsSize.U8 => StorageKind.GlobalMemoryU8,
|
||||||
}
|
LsSize.S8 => StorageKind.GlobalMemoryS8,
|
||||||
else
|
LsSize.U16 => StorageKind.GlobalMemoryU16,
|
||||||
{
|
LsSize.S16 => StorageKind.GlobalMemoryS16,
|
||||||
context.StoreGlobal(addrLowOffset, addrHigh, value);
|
_ => StorageKind.GlobalMemory
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetVectorCount(LsSize size)
|
private static int GetVectorCount(LsSize size)
|
||||||
|
|
|
@ -79,10 +79,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
ImageAtomic,
|
ImageAtomic,
|
||||||
IsNan,
|
IsNan,
|
||||||
Load,
|
Load,
|
||||||
LoadGlobal,
|
|
||||||
LoadLocal,
|
LoadLocal,
|
||||||
LoadShared,
|
LoadShared,
|
||||||
LoadStorage,
|
|
||||||
Lod,
|
Lod,
|
||||||
LogarithmB2,
|
LogarithmB2,
|
||||||
LogicalAnd,
|
LogicalAnd,
|
||||||
|
@ -117,16 +115,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
Sine,
|
Sine,
|
||||||
SquareRoot,
|
SquareRoot,
|
||||||
Store,
|
Store,
|
||||||
StoreGlobal,
|
|
||||||
StoreGlobal16,
|
|
||||||
StoreGlobal8,
|
|
||||||
StoreLocal,
|
StoreLocal,
|
||||||
StoreShared,
|
StoreShared,
|
||||||
StoreShared16,
|
StoreShared16,
|
||||||
StoreShared8,
|
StoreShared8,
|
||||||
StoreStorage,
|
|
||||||
StoreStorage16,
|
|
||||||
StoreStorage8,
|
|
||||||
Subtract,
|
Subtract,
|
||||||
SwizzleAdd,
|
SwizzleAdd,
|
||||||
TextureSample,
|
TextureSample,
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
StorageBuffer,
|
StorageBuffer,
|
||||||
LocalMemory,
|
LocalMemory,
|
||||||
SharedMemory,
|
SharedMemory,
|
||||||
GlobalMemory
|
GlobalMemory,
|
||||||
|
// TODO: Remove those and store type as a field on the Operation class itself.
|
||||||
|
GlobalMemoryS8,
|
||||||
|
GlobalMemoryS16,
|
||||||
|
GlobalMemoryU8,
|
||||||
|
GlobalMemoryU16
|
||||||
}
|
}
|
||||||
|
|
||||||
static class StorageKindExtensions
|
static class StorageKindExtensions
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Shared.glsl" />
|
<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\MultiplyHighS32.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
|
||||||
|
@ -19,7 +18,6 @@
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
enum HelperFunctionsMask
|
enum HelperFunctionsMask
|
||||||
{
|
{
|
||||||
AtomicMinMaxS32Shared = 1 << 0,
|
AtomicMinMaxS32Shared = 1 << 0,
|
||||||
AtomicMinMaxS32Storage = 1 << 1,
|
|
||||||
MultiplyHighS32 = 1 << 2,
|
MultiplyHighS32 = 1 << 2,
|
||||||
MultiplyHighU32 = 1 << 3,
|
MultiplyHighU32 = 1 << 3,
|
||||||
Shuffle = 1 << 4,
|
Shuffle = 1 << 4,
|
||||||
|
@ -14,7 +13,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
ShuffleUp = 1 << 6,
|
ShuffleUp = 1 << 6,
|
||||||
ShuffleXor = 1 << 7,
|
ShuffleXor = 1 << 7,
|
||||||
StoreSharedSmallInt = 1 << 8,
|
StoreSharedSmallInt = 1 << 8,
|
||||||
StoreStorageSmallInt = 1 << 9,
|
|
||||||
SwizzleAdd = 1 << 10,
|
SwizzleAdd = 1 << 10,
|
||||||
FSI = 1 << 11
|
FSI = 1 << 11
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,10 +90,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
Add(Instruction.ImageAtomic, AggregateType.S32);
|
Add(Instruction.ImageAtomic, AggregateType.S32);
|
||||||
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
|
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
|
||||||
Add(Instruction.Load, AggregateType.FP32);
|
Add(Instruction.Load, AggregateType.FP32);
|
||||||
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
|
|
||||||
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
|
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
|
||||||
Add(Instruction.LoadShared, 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.Lod, AggregateType.FP32);
|
||||||
Add(Instruction.LogarithmB2, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.LogarithmB2, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.LogicalAnd, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool);
|
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.Sine, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.Store, AggregateType.Void);
|
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.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
||||||
Add(Instruction.StoreShared, 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.StoreShared16, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
||||||
Add(Instruction.StoreShared8, 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.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
|
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
|
||||||
Add(Instruction.TextureSample, AggregateType.FP32);
|
Add(Instruction.TextureSample, AggregateType.FP32);
|
||||||
|
@ -166,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
{
|
{
|
||||||
return AggregateType.FP32;
|
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;
|
return AggregateType.S32;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,25 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
class ShaderProperties
|
class ShaderProperties
|
||||||
{
|
{
|
||||||
private readonly Dictionary<int, BufferDefinition> _constantBuffers;
|
private readonly Dictionary<int, BufferDefinition> _constantBuffers;
|
||||||
|
private readonly Dictionary<int, BufferDefinition> _storageBuffers;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
|
public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
|
||||||
|
public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers;
|
||||||
|
|
||||||
public ShaderProperties()
|
public ShaderProperties()
|
||||||
{
|
{
|
||||||
_constantBuffers = new Dictionary<int, BufferDefinition>();
|
_constantBuffers = new Dictionary<int, BufferDefinition>();
|
||||||
|
_storageBuffers = new Dictionary<int, BufferDefinition>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddConstantBuffer(int binding, BufferDefinition definition)
|
public void AddConstantBuffer(int binding, BufferDefinition definition)
|
||||||
{
|
{
|
||||||
_constantBuffers[binding] = definition;
|
_constantBuffers[binding] = definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddStorageBuffer(int binding, BufferDefinition definition)
|
||||||
|
{
|
||||||
|
_storageBuffers[binding] = definition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -280,10 +280,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
{
|
{
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
|
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
|
||||||
}
|
}
|
||||||
else if (operation.StorageKind == StorageKind.StorageBuffer)
|
|
||||||
{
|
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case Instruction.MultiplyHighS32:
|
case Instruction.MultiplyHighS32:
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
|
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
|
||||||
|
@ -307,10 +303,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
case Instruction.StoreShared8:
|
case Instruction.StoreShared8:
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreSharedSmallInt;
|
context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreSharedSmallInt;
|
||||||
break;
|
break;
|
||||||
case Instruction.StoreStorage16:
|
|
||||||
case Instruction.StoreStorage8:
|
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreStorageSmallInt;
|
|
||||||
break;
|
|
||||||
case Instruction.SwizzleAdd:
|
case Instruction.SwizzleAdd:
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd;
|
context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -57,6 +57,56 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return context.Add(Instruction.AtomicXor, storageKind, Local(), a, b, c);
|
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)
|
public static Operand Ballot(this EmitterContext context, Operand a)
|
||||||
{
|
{
|
||||||
return context.Add(Instruction.Ballot, Local(), 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);
|
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)
|
public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding)
|
||||||
{
|
{
|
||||||
return context.Add(Instruction.Load, storageKind, Local(), Const(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);
|
: 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)
|
public static Operand LoadLocal(this EmitterContext context, Operand a)
|
||||||
{
|
{
|
||||||
return context.Add(Instruction.LoadLocal, Local(), a);
|
return context.Add(Instruction.LoadLocal, Local(), a);
|
||||||
|
@ -655,7 +705,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
public static void Return(this EmitterContext context)
|
public static void Return(this EmitterContext context)
|
||||||
{
|
{
|
||||||
context.PrepareForReturn();
|
|
||||||
context.Add(Instruction.Return);
|
context.Add(Instruction.Return);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -699,6 +748,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c);
|
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(
|
public static Operand Store(
|
||||||
this EmitterContext context,
|
this EmitterContext context,
|
||||||
StorageKind storageKind,
|
StorageKind storageKind,
|
||||||
|
@ -738,21 +797,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
: context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value);
|
: 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)
|
public static Operand StoreLocal(this EmitterContext context, Operand a, Operand b)
|
||||||
{
|
{
|
||||||
return context.Add(Instruction.StoreLocal, null, a, b);
|
return context.Add(Instruction.StoreLocal, null, a, b);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,6 +19,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
_stage = stage;
|
_stage = stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int AddFunction(Function function)
|
||||||
|
{
|
||||||
|
int functionId = _functionList.Count;
|
||||||
|
_functionList.Add(function);
|
||||||
|
|
||||||
|
return functionId;
|
||||||
|
}
|
||||||
|
|
||||||
public int GetOrCreateFunctionId(HelperFunctionName functionName)
|
public int GetOrCreateFunctionId(HelperFunctionName functionName)
|
||||||
{
|
{
|
||||||
if (_functionIds.TryGetValue(functionName, out int functionId))
|
if (_functionIds.TryGetValue(functionName, out int functionId))
|
||||||
|
@ -27,8 +35,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
}
|
}
|
||||||
|
|
||||||
Function function = GenerateFunction(functionName);
|
Function function = GenerateFunction(functionName);
|
||||||
functionId = _functionList.Count;
|
functionId = AddFunction(function);
|
||||||
_functionList.Add(function);
|
|
||||||
_functionIds.Add(functionName, functionId);
|
_functionIds.Add(functionName, functionId);
|
||||||
|
|
||||||
return functionId;
|
return functionId;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,17 +7,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
{
|
{
|
||||||
static class Optimizer
|
static class Optimizer
|
||||||
{
|
{
|
||||||
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
|
public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config)
|
||||||
{
|
{
|
||||||
RunOptimizationPasses(blocks, config);
|
RunOptimizationPasses(blocks, config);
|
||||||
|
|
||||||
int sbUseMask = 0;
|
GlobalToStorage.RunPass(hfm, blocks, config);
|
||||||
int ubeUseMask = 0;
|
|
||||||
|
|
||||||
// Those passes are looking for specific patterns and only needs to run once.
|
// Those passes are looking for specific patterns and only needs to run once.
|
||||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||||
{
|
{
|
||||||
GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask);
|
|
||||||
BindlessToIndexed.RunPass(blocks[blkIndex], config);
|
BindlessToIndexed.RunPass(blocks[blkIndex], config);
|
||||||
BindlessElimination.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.
|
// Run optimizations one last time to remove any code that is now optimizable after above passes.
|
||||||
RunOptimizationPasses(blocks, config);
|
RunOptimizationPasses(blocks, config);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
switch (operation.Inst)
|
switch (operation.Inst)
|
||||||
{
|
{
|
||||||
case Instruction.Add:
|
case Instruction.Add:
|
||||||
case Instruction.BitwiseExclusiveOr:
|
|
||||||
TryEliminateBinaryOpCommutative(operation, 0);
|
TryEliminateBinaryOpCommutative(operation, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -21,6 +20,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
TryEliminateBitwiseAnd(operation);
|
TryEliminateBitwiseAnd(operation);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Instruction.BitwiseExclusiveOr:
|
||||||
|
if (!TryEliminateXorSwap(operation))
|
||||||
|
{
|
||||||
|
TryEliminateBinaryOpCommutative(operation, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Instruction.BitwiseOr:
|
case Instruction.BitwiseOr:
|
||||||
TryEliminateBitwiseOr(operation);
|
TryEliminateBitwiseOr(operation);
|
||||||
break;
|
break;
|
||||||
|
@ -51,6 +57,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
// Try to recognize and optimize those 3 patterns (in order):
|
// Try to recognize and optimize those 3 patterns (in order):
|
||||||
// x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
|
// x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
|
||||||
// x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
|
// x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
|
||||||
|
|
||||||
Operand x = operation.GetSource(0);
|
Operand x = operation.GetSource(0);
|
||||||
Operand y = operation.GetSource(1);
|
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)
|
private static void TryEliminateBitwiseOr(Operation operation)
|
||||||
{
|
{
|
||||||
// Try to recognize and optimize those 3 patterns (in order):
|
// Try to recognize and optimize those 3 patterns (in order):
|
||||||
// x | 0x00000000 == x, 0x00000000 | y == y,
|
// x | 0x00000000 == x, 0x00000000 | y == y,
|
||||||
// x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
|
// x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
|
||||||
|
|
||||||
Operand x = operation.GetSource(0);
|
Operand x = operation.GetSource(0);
|
||||||
Operand y = operation.GetSource(1);
|
Operand y = operation.GetSource(1);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
{
|
{
|
||||||
|
@ -93,5 +94,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
return source;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
private readonly string _stagePrefix;
|
private readonly string _stagePrefix;
|
||||||
|
|
||||||
private readonly int[] _cbSlotToBindingMap;
|
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;
|
private readonly HashSet<int> _usedConstantBufferBindings;
|
||||||
|
|
||||||
|
@ -26,7 +31,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
_stagePrefix = GetShaderStagePrefix(stage);
|
_stagePrefix = GetShaderStagePrefix(stage);
|
||||||
|
|
||||||
_cbSlotToBindingMap = new int[18];
|
_cbSlotToBindingMap = new int[18];
|
||||||
|
_sbSlotToBindingMap = new int[16];
|
||||||
_cbSlotToBindingMap.AsSpan().Fill(-1);
|
_cbSlotToBindingMap.AsSpan().Fill(-1);
|
||||||
|
_sbSlotToBindingMap.AsSpan().Fill(-1);
|
||||||
|
|
||||||
|
_sbSlots = new Dictionary<int, int>();
|
||||||
|
_sbSlotsReverse = new Dictionary<int, int>();
|
||||||
|
|
||||||
_usedConstantBufferBindings = new HashSet<int>();
|
_usedConstantBufferBindings = new HashSet<int>();
|
||||||
|
|
||||||
|
@ -47,6 +57,52 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return binding;
|
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)
|
public bool TryGetConstantBufferSlot(int binding, out int slot)
|
||||||
{
|
{
|
||||||
for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
|
for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
|
||||||
|
@ -90,6 +146,34 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return descriptors;
|
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)
|
private void AddNewConstantBuffer(int binding, string name)
|
||||||
{
|
{
|
||||||
StructureType type = new StructureType(new[]
|
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));
|
_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)
|
public static string GetShaderStagePrefix(ShaderStage stage)
|
||||||
{
|
{
|
||||||
uint index = (uint)stage;
|
uint index = (uint)stage;
|
||||||
|
@ -111,5 +205,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
return _stagePrefixes[index];
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,10 +2,8 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
using static Ryujinx.Graphics.Shader.Translation.GlobalMemory;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
|
@ -23,11 +21,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
BasicBlock block = blocks[blkIndex];
|
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)
|
if (node.Value is not Operation operation)
|
||||||
{
|
{
|
||||||
node = node.Next;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +53,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
InsertVectorComponentSelect(node, config);
|
InsertVectorComponentSelect(node, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkedListNode<INode> nextNode = node.Next;
|
|
||||||
|
|
||||||
if (operation is TextureOperation texOp)
|
if (operation is TextureOperation texOp)
|
||||||
{
|
{
|
||||||
node = InsertTexelFetchScale(hfm, node, config);
|
node = InsertTexelFetchScale(hfm, node, config);
|
||||||
|
@ -74,15 +69,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
node = InsertSnormNormalization(node, config);
|
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);
|
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)
|
private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config)
|
||||||
{
|
{
|
||||||
TextureOperation texOp = (TextureOperation)node.Value;
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
|
@ -110,12 +110,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
public UInt128 NextInputAttributesComponents { get; private set; }
|
public UInt128 NextInputAttributesComponents { get; private set; }
|
||||||
public UInt128 ThisInputAttributesComponents { 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 readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format);
|
||||||
|
|
||||||
private struct TextureMeta
|
private struct TextureMeta
|
||||||
|
@ -127,18 +121,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
|
private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
|
||||||
private readonly Dictionary<TextureInfo, TextureMeta> _usedImages;
|
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[] _cachedTextureDescriptors;
|
||||||
private TextureDescriptor[] _cachedImageDescriptors;
|
private TextureDescriptor[] _cachedImageDescriptors;
|
||||||
|
|
||||||
private int _firstStorageBufferBinding;
|
|
||||||
|
|
||||||
public int FirstStorageBufferBinding => _firstStorageBufferBinding;
|
|
||||||
|
|
||||||
public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options)
|
public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
|
@ -147,18 +132,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
_transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>();
|
_transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>();
|
||||||
|
|
||||||
AccessibleStorageBuffersMask = (1 << GlobalMemory.StorageMaxCount) - 1;
|
|
||||||
AccessibleConstantBuffersMask = (1 << GlobalMemory.UbeMaxCount) - 1;
|
|
||||||
|
|
||||||
UsedInputAttributesPerPatch = new HashSet<int>();
|
UsedInputAttributesPerPatch = new HashSet<int>();
|
||||||
UsedOutputAttributesPerPatch = new HashSet<int>();
|
UsedOutputAttributesPerPatch = new HashSet<int>();
|
||||||
|
|
||||||
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
|
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
|
||||||
_usedImages = 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());
|
ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,11 +152,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
OutputTopology = outputTopology;
|
OutputTopology = outputTopology;
|
||||||
MaxOutputVertices = maxOutputVertices;
|
MaxOutputVertices = maxOutputVertices;
|
||||||
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
|
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
|
||||||
|
|
||||||
if (Stage != ShaderStage.Compute)
|
|
||||||
{
|
|
||||||
AccessibleConstantBuffersMask = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options)
|
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;
|
UsedInputAttributes |= other.UsedInputAttributes;
|
||||||
UsedOutputAttributes |= other.UsedOutputAttributes;
|
UsedOutputAttributes |= other.UsedOutputAttributes;
|
||||||
_usedStorageBuffers |= other._usedStorageBuffers;
|
|
||||||
_usedStorageBuffersWrite |= other._usedStorageBuffersWrite;
|
|
||||||
|
|
||||||
foreach (var kv in other._usedTextures)
|
foreach (var kv in other._usedTextures)
|
||||||
{
|
{
|
||||||
|
@ -634,23 +606,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
UsedFeatures |= flags;
|
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(
|
public void SetUsedTexture(
|
||||||
Instruction inst,
|
Instruction inst,
|
||||||
SamplerType type,
|
SamplerType type,
|
||||||
|
@ -756,76 +711,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return meta;
|
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()
|
public TextureDescriptor[] GetTextureDescriptors()
|
||||||
{
|
{
|
||||||
return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, GpuAccessor.QueryBindingTexture);
|
return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, GpuAccessor.QueryBindingTexture);
|
||||||
|
@ -922,45 +807,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
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)
|
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
|
||||||
{
|
{
|
||||||
return new ShaderProgramInfo(
|
return new ShaderProgramInfo(
|
||||||
ResourceManager.GetConstantBufferDescriptors(),
|
ResourceManager.GetConstantBufferDescriptors(),
|
||||||
GetStorageBufferDescriptors(),
|
ResourceManager.GetStorageBufferDescriptors(),
|
||||||
GetTextureDescriptors(),
|
GetTextureDescriptors(),
|
||||||
GetImageDescriptors(),
|
GetImageDescriptors(),
|
||||||
identification,
|
identification,
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsResourceWrite(operation.Inst))
|
if (IsResourceWrite(operation.Inst, operation.StorageKind))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return totalVerticesCount + verticesCount == 3 && writesLayer;
|
return totalVerticesCount + verticesCount == 3 && writesLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsResourceWrite(Instruction inst)
|
private static bool IsResourceWrite(Instruction inst, StorageKind storageKind)
|
||||||
{
|
{
|
||||||
switch (inst)
|
switch (inst)
|
||||||
{
|
{
|
||||||
|
@ -170,13 +170,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
case Instruction.AtomicXor:
|
case Instruction.AtomicXor:
|
||||||
case Instruction.ImageAtomic:
|
case Instruction.ImageAtomic:
|
||||||
case Instruction.ImageStore:
|
case Instruction.ImageStore:
|
||||||
case Instruction.StoreGlobal:
|
|
||||||
case Instruction.StoreGlobal16:
|
|
||||||
case Instruction.StoreGlobal8:
|
|
||||||
case Instruction.StoreStorage:
|
|
||||||
case Instruction.StoreStorage16:
|
|
||||||
case Instruction.StoreStorage8:
|
|
||||||
return true;
|
return true;
|
||||||
|
case Instruction.Store:
|
||||||
|
return storageKind == StorageKind.StorageBuffer ||
|
||||||
|
storageKind == StorageKind.SharedMemory ||
|
||||||
|
storageKind == StorageKind.LocalMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
Ssa.Rename(cfg.Blocks);
|
Ssa.Rename(cfg.Blocks);
|
||||||
|
|
||||||
Optimizer.RunPass(cfg.Blocks, config);
|
Optimizer.RunPass(hfm, cfg.Blocks, config);
|
||||||
Rewriter.RunPass(hfm, cfg.Blocks, config);
|
Rewriter.RunPass(hfm, cfg.Blocks, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,8 +115,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
|
holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
|
||||||
if (holder == null)
|
if (holder == null)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X} and type \"{baseType}\".");
|
|
||||||
|
|
||||||
return BufferHandle.Null;
|
return BufferHandle.Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,6 +262,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return holder;
|
return holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Error?.Print(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X} and type \"{baseType}\".");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
public unsafe void UpdateImage(int setIndex, int bindingIndex, DescriptorImageInfo imageInfo, DescriptorType type)
|
||||||
{
|
{
|
||||||
if (imageInfo.ImageView.Handle != 0UL)
|
if (imageInfo.ImageView.Handle != 0UL)
|
||||||
|
|
|
@ -448,15 +448,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
||||||
if (program.HasMinimalLayout)
|
|
||||||
{
|
|
||||||
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (setIndex == PipelineBase.TextureSetIndex)
|
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||||
{
|
{
|
||||||
if (segment.Type != ResourceType.BufferTexture)
|
if (segment.Type != ResourceType.BufferTexture)
|
||||||
|
|
Loading…
Reference in a new issue