aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Shader
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2024-04-07 18:25:55 -0300
committerGitHub <noreply@github.com>2024-04-07 18:25:55 -0300
commit3e6e0e4afaa3c3ffb118cb17b61feb16966a7eeb (patch)
treea4652499c089b0853e39c382cad82a9db4d6ad08 /src/Ryujinx.Graphics.Shader
parent808803d97a0c06809bf000687c252f960048fcf0 (diff)
Add support for large sampler arrays on Vulkan (#6489)
* Add support for large sampler arrays on Vulkan * Shader cache version bump * Format whitespace * Move DescriptorSetManager to PipelineLayoutCacheEntry to allow different pool sizes per layout * Handle array textures with different types on the same buffer * Somewhat better caching system * Avoid useless buffer data modification checks * Move redundant bindings update checking to the backend * Fix an issue where texture arrays would get the same bindings across stages on Vulkan * Backport some fixes from part 2 * Fix typo * PR feedback * Format whitespace * Add some missing XML docs
Diffstat (limited to 'src/Ryujinx.Graphics.Shader')
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs46
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs10
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs2
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs2
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs2
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs173
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs12
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenShuffle.cs4
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs4
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs4
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs4
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs39
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ImageDeclaration.cs20
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs220
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SamplerDeclaration.cs27
-rw-r--r--src/Ryujinx.Graphics.Shader/IGpuAccessor.cs43
-rw-r--r--src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs12
-rw-r--r--src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs10
-rw-r--r--src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs3
-rw-r--r--src/Ryujinx.Graphics.Shader/SamplerType.cs35
-rw-r--r--src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs6
-rw-r--r--src/Ryujinx.Graphics.Shader/TextureDescriptor.cs11
-rw-r--r--src/Ryujinx.Graphics.Shader/TextureHandle.cs4
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs308
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs236
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs118
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs10
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs198
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/TransformContext.cs3
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs43
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs7
31 files changed, 857 insertions, 759 deletions
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index 500de71f..763487da 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -339,24 +339,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static void DeclareSamplers(CodeGenContext context, IEnumerable<TextureDefinition> definitions)
{
- int arraySize = 0;
-
foreach (var definition in definitions)
{
- string indexExpr = string.Empty;
+ string arrayDecl = string.Empty;
- if (definition.Type.HasFlag(SamplerType.Indexed))
+ if (definition.ArrayLength > 1)
{
- if (arraySize == 0)
- {
- arraySize = ResourceManager.SamplerArraySize;
- }
- else if (--arraySize != 0)
- {
- continue;
- }
-
- indexExpr = $"[{NumberFormatter.FormatInt(arraySize)}]";
+ arrayDecl = $"[{NumberFormatter.FormatInt(definition.ArrayLength)}]";
+ }
+ else if (definition.ArrayLength == 0)
+ {
+ arrayDecl = "[]";
}
string samplerTypeName = definition.Type.ToGlslSamplerType();
@@ -368,30 +361,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
layout = $", set = {definition.Set}";
}
- context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {samplerTypeName} {definition.Name}{indexExpr};");
+ context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {samplerTypeName} {definition.Name}{arrayDecl};");
}
}
private static void DeclareImages(CodeGenContext context, IEnumerable<TextureDefinition> definitions)
{
- int arraySize = 0;
-
foreach (var definition in definitions)
{
- string indexExpr = string.Empty;
+ string arrayDecl = string.Empty;
- if (definition.Type.HasFlag(SamplerType.Indexed))
+ if (definition.ArrayLength > 1)
{
- if (arraySize == 0)
- {
- arraySize = ResourceManager.SamplerArraySize;
- }
- else if (--arraySize != 0)
- {
- continue;
- }
-
- indexExpr = $"[{NumberFormatter.FormatInt(arraySize)}]";
+ arrayDecl = $"[{NumberFormatter.FormatInt(definition.ArrayLength)}]";
+ }
+ else if (definition.ArrayLength == 0)
+ {
+ arrayDecl = "[]";
}
string imageTypeName = definition.Type.ToGlslImageType(definition.Format.GetComponentType());
@@ -413,7 +399,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
layout = $", set = {definition.Set}{layout}";
}
- context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {imageTypeName} {definition.Name}{indexExpr};");
+ context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {imageTypeName} {definition.Name}{arrayDecl};");
}
}
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
index eb0cb92d..9e7f64b0 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
@@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
AggregateType type = GetSrcVarType(operation.Inst, 0);
- string srcExpr = GetSoureExpr(context, src, type);
+ string srcExpr = GetSourceExpr(context, src, type);
string zero;
if (type == AggregateType.FP64)
@@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++)
{
- builder.Append($", {GetSoureExpr(context, operation.GetSource(argIndex), dstType)}");
+ builder.Append($", {GetSourceExpr(context, operation.GetSource(argIndex), dstType)}");
}
}
else
@@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
AggregateType dstType = GetSrcVarType(inst, argIndex);
- builder.Append(GetSoureExpr(context, operation.GetSource(argIndex), dstType));
+ builder.Append(GetSourceExpr(context, operation.GetSource(argIndex), dstType));
}
}
@@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
// Return may optionally have a return value (and in this case it is unary).
if (inst == Instruction.Return && operation.SourcesCount != 0)
{
- return $"{op} {GetSoureExpr(context, operation.GetSource(0), context.CurrentFunction.ReturnType)}";
+ return $"{op} {GetSourceExpr(context, operation.GetSource(0), context.CurrentFunction.ReturnType)}";
}
int arity = (int)(info.Type & InstType.ArityMask);
@@ -118,7 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
IAstNode src = operation.GetSource(index);
- string srcExpr = GetSoureExpr(context, src, GetSrcVarType(inst, index));
+ string srcExpr = GetSourceExpr(context, src, GetSrcVarType(inst, index));
bool isLhs = arity == 2 && index == 0;
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs
index 6cc7048b..000d7f79 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenBallot.cs
@@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
AggregateType dstType = GetSrcVarType(operation.Inst, 0);
- string arg = GetSoureExpr(context, operation.GetSource(0), dstType);
+ string arg = GetSourceExpr(context, operation.GetSource(0), dstType);
char component = "xyzw"[operation.Index];
if (context.HostCapabilities.SupportsShaderBallot)
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs
index 0618ba8a..d5448856 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenCall.cs
@@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
for (int i = 0; i < args.Length; i++)
{
- args[i] = GetSoureExpr(context, operation.GetSource(i + 1), function.GetArgumentType(i));
+ args[i] = GetSourceExpr(context, operation.GetSource(i + 1), function.GetArgumentType(i));
}
return $"{function.Name}({string.Join(", ", args)})";
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
index 5c2d16f4..4b28f387 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
@@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return _infoTable[(int)(inst & Instruction.Mask)];
}
- public static string GetSoureExpr(CodeGenContext context, IAstNode node, AggregateType dstType)
+ public static string GetSourceExpr(CodeGenContext context, IAstNode node, AggregateType dstType)
{
return ReinterpretCast(context, node, OperandManager.GetNodeDestType(context, node), dstType);
}
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
index 2e90bd16..b4773b81 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
@@ -14,35 +14,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
-
- // TODO: Bindless texture support. For now we just return 0/do nothing.
- if (isBindless)
- {
- switch (texOp.Inst)
- {
- case Instruction.ImageStore:
- return "// imageStore(bindless)";
- case Instruction.ImageLoad:
- AggregateType componentType = texOp.Format.GetComponentType();
-
- NumberFormatter.TryFormat(0, componentType, out string imageConst);
-
- AggregateType outputType = texOp.GetVectorType(componentType);
-
- if ((outputType & AggregateType.ElementCountMask) != 0)
- {
- return $"{Declarations.GetVarTypeName(context, outputType, precise: false)}({imageConst})";
- }
-
- return imageConst;
- default:
- return NumberFormatter.FormatInt(0);
- }
- }
-
bool isArray = (texOp.Type & SamplerType.Array) != 0;
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
var texCallBuilder = new StringBuilder();
@@ -70,21 +42,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
texCallBuilder.Append(texOp.Inst == Instruction.ImageLoad ? "imageLoad" : "imageStore");
}
- int srcIndex = isBindless ? 1 : 0;
+ int srcIndex = 0;
string Src(AggregateType type)
{
- return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
- }
-
- string indexExpr = null;
-
- if (isIndexed)
- {
- indexExpr = Src(AggregateType.S32);
+ return GetSourceExpr(context, texOp.GetSource(srcIndex++), type);
}
- string imageName = GetImageName(context.Properties, texOp, indexExpr);
+ string imageName = GetImageName(context, texOp, ref srcIndex);
texCallBuilder.Append('(');
texCallBuilder.Append(imageName);
@@ -198,27 +163,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
AstTextureOperation texOp = (AstTextureOperation)operation;
int coordsCount = texOp.Type.GetDimensions();
+ int coordsIndex = 0;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
-
- // TODO: Bindless texture support. For now we just return 0.
- if (isBindless)
- {
- return NumberFormatter.FormatFloat(0);
- }
-
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
-
- string indexExpr = null;
-
- if (isIndexed)
- {
- indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
- }
-
- string samplerName = GetSamplerName(context.Properties, texOp, indexExpr);
-
- int coordsIndex = isBindless || isIndexed ? 1 : 0;
+ string samplerName = GetSamplerName(context, texOp, ref coordsIndex);
string coordsExpr;
@@ -228,14 +175,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
for (int index = 0; index < coordsCount; index++)
{
- elems[index] = GetSoureExpr(context, texOp.GetSource(coordsIndex + index), AggregateType.FP32);
+ elems[index] = GetSourceExpr(context, texOp.GetSource(coordsIndex + index), AggregateType.FP32);
}
coordsExpr = "vec" + coordsCount + "(" + string.Join(", ", elems) + ")";
}
else
{
- coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32);
+ coordsExpr = GetSourceExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32);
}
return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}";
@@ -250,7 +197,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
bool hasDerivatives = (texOp.Flags & TextureFlags.Derivatives) != 0;
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
@@ -260,12 +206,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
bool isArray = (texOp.Type & SamplerType.Array) != 0;
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
- bool colorIsVector = isGather || !isShadow;
-
SamplerType type = texOp.Type & SamplerType.Mask;
bool is2D = type == SamplerType.Texture2D;
@@ -286,24 +229,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
hasLodLevel = false;
}
- // TODO: Bindless texture support. For now we just return 0.
- if (isBindless)
- {
- string scalarValue = NumberFormatter.FormatFloat(0);
-
- if (colorIsVector)
- {
- AggregateType outputType = texOp.GetVectorType(AggregateType.FP32);
-
- if ((outputType & AggregateType.ElementCountMask) != 0)
- {
- return $"{Declarations.GetVarTypeName(context, outputType, precise: false)}({scalarValue})";
- }
- }
-
- return scalarValue;
- }
-
string texCall = intCoords ? "texelFetch" : "texture";
if (isGather)
@@ -328,21 +253,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
texCall += "Offsets";
}
- int srcIndex = isBindless ? 1 : 0;
+ int srcIndex = 0;
string Src(AggregateType type)
{
- return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
+ return GetSourceExpr(context, texOp.GetSource(srcIndex++), type);
}
- string indexExpr = null;
-
- if (isIndexed)
- {
- indexExpr = Src(AggregateType.S32);
- }
-
- string samplerName = GetSamplerName(context.Properties, texOp, indexExpr);
+ string samplerName = GetSamplerName(context, texOp, ref srcIndex);
texCall += "(" + samplerName;
@@ -512,6 +430,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Append(Src(AggregateType.S32));
}
+ bool colorIsVector = isGather || !isShadow;
+
texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : "");
return texCall;
@@ -521,24 +441,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
-
- // TODO: Bindless texture support. For now we just return 0.
- if (isBindless)
- {
- return NumberFormatter.FormatInt(0);
- }
-
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
-
- string indexExpr = null;
-
- if (isIndexed)
- {
- indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
- }
+ int srcIndex = 0;
- string samplerName = GetSamplerName(context.Properties, texOp, indexExpr);
+ string samplerName = GetSamplerName(context, texOp, ref srcIndex);
return $"textureSamples({samplerName})";
}
@@ -547,24 +452,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
-
- // TODO: Bindless texture support. For now we just return 0.
- if (isBindless)
- {
- return NumberFormatter.FormatInt(0);
- }
-
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
-
- string indexExpr = null;
-
- if (isIndexed)
- {
- indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
- }
+ int srcIndex = 0;
- string samplerName = GetSamplerName(context.Properties, texOp, indexExpr);
+ string samplerName = GetSamplerName(context, texOp, ref srcIndex);
if (texOp.Index == 3)
{
@@ -578,9 +468,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (hasLod)
{
- int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
- IAstNode lod = operation.GetSource(lodSrcIndex);
- string lodExpr = GetSoureExpr(context, lod, GetSrcVarType(operation.Inst, lodSrcIndex));
+ IAstNode lod = operation.GetSource(srcIndex);
+ string lodExpr = GetSourceExpr(context, lod, GetSrcVarType(operation.Inst, srcIndex));
texCall = $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}";
}
@@ -697,12 +586,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (storageKind == StorageKind.Input)
{
- string expr = GetSoureExpr(context, operation.GetSource(srcIndex++), AggregateType.S32);
+ string expr = GetSourceExpr(context, operation.GetSource(srcIndex++), AggregateType.S32);
varName = $"gl_in[{expr}].{varName}";
}
else if (storageKind == StorageKind.Output)
{
- string expr = GetSoureExpr(context, operation.GetSource(srcIndex++), AggregateType.S32);
+ string expr = GetSourceExpr(context, operation.GetSource(srcIndex++), AggregateType.S32);
varName = $"gl_out[{expr}].{varName}";
}
}
@@ -735,38 +624,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
}
else
{
- varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]";
+ varName += $"[{GetSourceExpr(context, src, AggregateType.S32)}]";
}
}
if (isStore)
{
varType &= AggregateType.ElementTypeMask;
- varName = $"{varName} = {GetSoureExpr(context, operation.GetSource(srcIndex), varType)}";
+ varName = $"{varName} = {GetSourceExpr(context, operation.GetSource(srcIndex), varType)}";
}
return varName;
}
- private static string GetSamplerName(ShaderProperties resourceDefinitions, AstTextureOperation texOp, string indexExpr)
+ private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex)
{
- string name = resourceDefinitions.Textures[texOp.Binding].Name;
+ TextureDefinition definition = context.Properties.Textures[texOp.Binding];
+ string name = definition.Name;
- if (texOp.Type.HasFlag(SamplerType.Indexed))
+ if (definition.ArrayLength != 1)
{
- name = $"{name}[{indexExpr}]";
+ name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
}
return name;
}
- private static string GetImageName(ShaderProperties resourceDefinitions, AstTextureOperation texOp, string indexExpr)
+ private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex)
{
- string name = resourceDefinitions.Images[texOp.Binding].Name;
+ TextureDefinition definition = context.Properties.Images[texOp.Binding];
+ string name = definition.Name;
- if (texOp.Type.HasFlag(SamplerType.Indexed))
+ if (definition.ArrayLength != 1)
{
- name = $"{name}[{indexExpr}]";
+ name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
}
return name;
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs
index ad84c485..4469785d 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs
@@ -13,8 +13,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
IAstNode src0 = operation.GetSource(0);
IAstNode src1 = operation.GetSource(1);
- string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0));
- string src1Expr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1));
+ string src0Expr = GetSourceExpr(context, src0, GetSrcVarType(operation.Inst, 0));
+ string src1Expr = GetSourceExpr(context, src1, GetSrcVarType(operation.Inst, 1));
return $"packDouble2x32(uvec2({src0Expr}, {src1Expr}))";
}
@@ -24,8 +24,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
IAstNode src0 = operation.GetSource(0);
IAstNode src1 = operation.GetSource(1);
- string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0));
- string src1Expr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1));
+ string src0Expr = GetSourceExpr(context, src0, GetSrcVarType(operation.Inst, 0));
+ string src1Expr = GetSourceExpr(context, src1, GetSrcVarType(operation.Inst, 1));
return $"packHalf2x16(vec2({src0Expr}, {src1Expr}))";
}
@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
IAstNode src = operation.GetSource(0);
- string srcExpr = GetSoureExpr(context, src, GetSrcVarType(operation.Inst, 0));
+ string srcExpr = GetSourceExpr(context, src, GetSrcVarType(operation.Inst, 0));
return $"unpackDouble2x32({srcExpr}){GetMask(operation.Index)}";
}
@@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
IAstNode src = operation.GetSource(0);
- string srcExpr = GetSoureExpr(context, src, GetSrcVarType(operation.Inst, 0));
+ string srcExpr = GetSourceExpr(context, src, GetSrcVarType(operation.Inst, 0));
return $"unpackHalf2x16({srcExpr}){GetMask(operation.Index)}";
}
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenShuffle.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenShuffle.cs
index 6d3859ef..b72b94d9 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenShuffle.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenShuffle.cs
@@ -9,8 +9,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
public static string Shuffle(CodeGenContext context, AstOperation operation)
{
- string value = GetSoureExpr(context, operation.GetSource(0), AggregateType.FP32);
- string index = GetSoureExpr(context, operation.GetSource(1), AggregateType.U32);
+ string value = GetSourceExpr(context, operation.GetSource(0), AggregateType.FP32);
+ string index = GetSourceExpr(context, operation.GetSource(1), AggregateType.U32);
if (context.HostCapabilities.SupportsShaderBallot)
{
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs
index 70174a5b..a300c775 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenVector.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
IAstNode vector = operation.GetSource(0);
IAstNode index = operation.GetSource(1);
- string vectorExpr = GetSoureExpr(context, vector, OperandManager.GetNodeDestType(context, vector));
+ string vectorExpr = GetSourceExpr(context, vector, OperandManager.GetNodeDestType(context, vector));
if (index is AstOperand indexOperand && indexOperand.Type == OperandType.Constant)
{
@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
}
else
{
- string indexExpr = GetSoureExpr(context, index, GetSrcVarType(operation.Inst, 1));
+ string indexExpr = GetSourceExpr(context, index, GetSrcVarType(operation.Inst, 1));
return $"{vectorExpr}[{indexExpr}]";
}
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
index 53ecc453..a350b089 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
@@ -146,9 +146,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
else if (operation is AstTextureOperation texOp)
{
- if (texOp.Inst == Instruction.ImageLoad ||
- texOp.Inst == Instruction.ImageStore ||
- texOp.Inst == Instruction.ImageAtomic)
+ if (texOp.Inst.IsImage())
{
return texOp.GetVectorType(texOp.Format.GetComponentType());
}
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
index 17c3eefe..2b1fdf44 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
@@ -34,8 +34,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Dictionary<int, Instruction> SharedMemories { get; } = new();
public Dictionary<int, SamplerType> SamplersTypes { get; } = new();
- public Dictionary<int, (Instruction, Instruction, Instruction)> Samplers { get; } = new();
- public Dictionary<int, (Instruction, Instruction)> Images { get; } = new();
+ public Dictionary<int, SamplerDeclaration> Samplers { get; } = new();
+ public Dictionary<int, ImageDeclaration> Images { get; } = new();
public Dictionary<IoDefinition, Instruction> Inputs { get; } = new();
public Dictionary<IoDefinition, Instruction> Outputs { get; } = new();
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index b7482425..9633c522 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -181,9 +181,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var sampledImageType = context.TypeSampledImage(imageType);
var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType);
- var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant);
+ var sampledImageArrayPointerType = sampledImagePointerType;
- context.Samplers.Add(sampler.Binding, (imageType, sampledImageType, sampledImageVariable));
+ if (sampler.ArrayLength == 0)
+ {
+ var sampledImageArrayType = context.TypeRuntimeArray(sampledImageType);
+ sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
+ }
+ else if (sampler.ArrayLength != 1)
+ {
+ var sampledImageArrayType = context.TypeArray(sampledImageType, context.Constant(context.TypeU32(), sampler.ArrayLength));
+ sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
+ }
+
+ var sampledImageVariable = context.Variable(sampledImageArrayPointerType, StorageClass.UniformConstant);
+
+ context.Samplers.Add(sampler.Binding, new SamplerDeclaration(
+ imageType,
+ sampledImageType,
+ sampledImagePointerType,
+ sampledImageVariable,
+ sampler.ArrayLength != 1));
context.SamplersTypes.Add(sampler.Binding, sampler.Type);
context.Name(sampledImageVariable, sampler.Name);
@@ -211,9 +229,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
GetImageFormat(image.Format));
var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType);
- var imageVariable = context.Variable(imagePointerType, StorageClass.UniformConstant);
+ var imageArrayPointerType = imagePointerType;
+
+ if (image.ArrayLength == 0)
+ {
+ var imageArrayType = context.TypeRuntimeArray(imageType);
+ imageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, imageArrayType);
+ }
+ else if (image.ArrayLength != 1)
+ {
+ var imageArrayType = context.TypeArray(imageType, context.Constant(context.TypeU32(), image.ArrayLength));
+ imageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, imageArrayType);
+ }
+
+ var imageVariable = context.Variable(imageArrayPointerType, StorageClass.UniformConstant);
- context.Images.Add(image.Binding, (imageType, imageVariable));
+ context.Images.Add(image.Binding, new ImageDeclaration(imageType, imagePointerType, imageVariable, image.ArrayLength != 1));
context.Name(imageVariable, image.Name);
context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ImageDeclaration.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ImageDeclaration.cs
new file mode 100644
index 00000000..1e0aee73
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ImageDeclaration.cs
@@ -0,0 +1,20 @@
+using Spv.Generator;
+
+namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
+{
+ readonly struct ImageDeclaration
+ {
+ public readonly Instruction ImageType;
+ public readonly Instruction ImagePointerType;
+ public readonly Instruction Image;
+ public readonly bool IsIndexed;
+
+ public ImageDeclaration(Instruction imageType, Instruction imagePointerType, Instruction image, bool isIndexed)
+ {
+ ImageType = imageType;
+ ImagePointerType = imagePointerType;
+ Image = image;
+ IsIndexed = isIndexed;
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
index 601753cb..409e466c 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
@@ -591,34 +591,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
-
var componentType = texOp.Format.GetComponentType();
- // TODO: Bindless texture support. For now we just return 0/do nothing.
- if (isBindless)
- {
- return new OperationResult(componentType, componentType switch
- {
- AggregateType.S32 => context.Constant(context.TypeS32(), 0),
- AggregateType.U32 => context.Constant(context.TypeU32(), 0u),
- _ => context.Constant(context.TypeFP32(), 0f),
- });
- }
-
bool isArray = (texOp.Type & SamplerType.Array) != 0;
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
- int srcIndex = isBindless ? 1 : 0;
+ int srcIndex = 0;
SpvInstruction Src(AggregateType type)
{
return context.Get(type, texOp.GetSource(srcIndex++));
}
- if (isIndexed)
+ ImageDeclaration declaration = context.Images[texOp.Binding];
+ SpvInstruction image = declaration.Image;
+
+ SpvInstruction resultType = context.GetType(componentType);
+ SpvInstruction imagePointerType = context.TypePointer(StorageClass.Image, resultType);
+
+ if (declaration.IsIndexed)
{
- Src(AggregateType.S32);
+ SpvInstruction textureIndex = Src(AggregateType.S32);
+
+ image = context.AccessChain(imagePointerType, image, textureIndex);
}
int coordsCount = texOp.Type.GetDimensions();
@@ -646,14 +640,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction value = Src(componentType);
- (var imageType, var imageVariable) = context.Images[texOp.Binding];
-
- context.Load(imageType, imageVariable);
-
- SpvInstruction resultType = context.GetType(componentType);
- SpvInstruction imagePointerType = context.TypePointer(StorageClass.Image, resultType);
-
- var pointer = context.ImageTexelPointer(imagePointerType, imageVariable, pCoords, context.Constant(context.TypeU32(), 0));
+ var pointer = context.ImageTexelPointer(imagePointerType, image, pCoords, context.Constant(context.TypeU32(), 0));
var one = context.Constant(context.TypeU32(), 1);
var zero = context.Constant(context.TypeU32(), 0);
@@ -683,31 +670,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
-
var componentType = texOp.Format.GetComponentType();
- // TODO: Bindless texture support. For now we just return 0/do nothing.
- if (isBindless)
- {
- return GetZeroOperationResult(context, texOp, componentType, isVector: true);
- }
-
bool isArray = (texOp.Type & SamplerType.Array) != 0;
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
- int srcIndex = isBindless ? 1 : 0;
+ int srcIndex = 0;
SpvInstruction Src(AggregateType type)
{
return context.Get(type, texOp.GetSource(srcIndex++));
}
- if (isIndexed)
+ ImageDeclaration declaration = context.Images[texOp.Binding];
+ SpvInstruction image = declaration.Image;
+
+ if (declaration.IsIndexed)
{
- Src(AggregateType.S32);
+ SpvInstruction textureIndex = Src(AggregateType.S32);
+
+ image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
}
+ image = context.Load(declaration.ImageType, image);
+
int coordsCount = texOp.Type.GetDimensions();
int pCount = coordsCount + (isArray ? 1 : 0);
@@ -731,9 +716,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
pCoords = Src(AggregateType.S32);
}
- (var imageType, var imageVariable) = context.Images[texOp.Binding];
-
- var image = context.Load(imageType, imageVariable);
var imageComponentType = context.GetType(componentType);
var swizzledResultType = texOp.GetVectorType(componentType);
@@ -747,29 +729,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
-
- // TODO: Bindless texture support. For now we just return 0/do nothing.
- if (isBindless)
- {
- return OperationResult.Invalid;
- }
-
bool isArray = (texOp.Type & SamplerType.Array) != 0;
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
- int srcIndex = isBindless ? 1 : 0;
+ int srcIndex = 0;
SpvInstruction Src(AggregateType type)
{
return context.Get(type, texOp.GetSource(srcIndex++));
}
- if (isIndexed)
+ ImageDeclaration declaration = context.Images[texOp.Binding];
+ SpvInstruction image = declaration.Image;
+
+ if (declaration.IsIndexed)
{
- Src(AggregateType.S32);
+ SpvInstruction textureIndex = Src(AggregateType.S32);
+
+ image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
}
+ image = context.Load(declaration.ImageType, image);
+
int coordsCount = texOp.Type.GetDimensions();
int pCount = coordsCount + (isArray ? 1 : 0);
@@ -818,10 +798,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var texel = context.CompositeConstruct(context.TypeVector(context.GetType(componentType), ComponentsCount), cElems);
- (var imageType, var imageVariable) = context.Images[texOp.Binding];
-
- var image = context.Load(imageType, imageVariable);
-
context.ImageWrite(image, pCoords, texel, ImageOperandsMask.MaskNone);
return OperationResult.Invalid;
@@ -854,16 +830,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
-
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
-
- // TODO: Bindless texture support. For now we just return 0.
- if (isBindless)
- {
- return new OperationResult(AggregateType.S32, context.Constant(context.TypeS32(), 0));
- }
-
int srcIndex = 0;
SpvInstruction Src(AggregateType type)
@@ -871,11 +837,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return context.Get(type, texOp.GetSource(srcIndex++));
}
- if (isIndexed)
+ SamplerDeclaration declaration = context.Samplers[texOp.Binding];
+ SpvInstruction image = declaration.Image;
+
+ if (declaration.IsIndexed)
{
- Src(AggregateType.S32);
+ SpvInstruction textureIndex = Src(AggregateType.S32);
+
+ image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
}
+ image = context.Load(declaration.SampledImageType, image);
+
int pCount = texOp.Type.GetDimensions();
SpvInstruction pCoords;
@@ -897,10 +870,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
pCoords = Src(AggregateType.FP32);
}
- (_, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
-
- var image = context.Load(sampledImageType, sampledImageVariable);
-
var resultType = context.TypeVector(context.TypeFP32(), 2);
var packed = context.ImageQueryLod(resultType, image, pCoords);
var result = context.CompositeExtract(context.TypeFP32(), packed, (SpvLiteralInteger)texOp.Index);
@@ -1182,7 +1151,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
bool hasDerivatives = (texOp.Flags & TextureFlags.Derivatives) != 0;
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
@@ -1192,30 +1160,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
bool isArray = (texOp.Type & SamplerType.Array) != 0;
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
- bool colorIsVector = isGather || !isShadow;
-
- // TODO: Bindless texture support. For now we just return 0.
- if (isBindless)
- {
- return GetZeroOperationResult(context, texOp, AggregateType.FP32, colorIsVector);
- }
-
- int srcIndex = isBindless ? 1 : 0;
+ int srcIndex = 0;
SpvInstruction Src(AggregateType type)
{
return context.Get(type, texOp.GetSource(srcIndex++));
}
- if (isIndexed)
+ SamplerDeclaration declaration = context.Samplers[texOp.Binding];
+ SpvInstruction image = declaration.Image;
+
+ if (declaration.IsIndexed)
{
- Src(AggregateType.S32);
+ SpvInstruction textureIndex = Src(AggregateType.S32);
+
+ image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
}
+ image = context.Load(declaration.SampledImageType, image);
+
int coordsCount = texOp.Type.GetDimensions();
int pCount = coordsCount;
@@ -1419,15 +1385,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
operandsList.Add(sample);
}
- var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32();
-
- (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
+ bool colorIsVector = isGather || !isShadow;
- var image = context.Load(sampledImageType, sampledImageVariable);
+ var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32();
if (intCoords)
{
- image = context.Image(imageType, image);
+ image = context.Image(declaration.ImageType, image);
}
var operands = operandsList.ToArray();
@@ -1485,25 +1449,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
+ SamplerDeclaration declaration = context.Samplers[texOp.Binding];
+ SpvInstruction image = declaration.Image;
- // TODO: Bindless texture support. For now we just return 0.
- if (isBindless)
+ if (declaration.IsIndexed)
{
- return new OperationResult(AggregateType.S32, context.Constant(context.TypeS32(), 0));
- }
-
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
+ SpvInstruction textureIndex = context.GetS32(texOp.GetSource(0));
- if (isIndexed)
- {
- context.GetS32(texOp.GetSource(0));
+ image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
}
- (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
-
- var image = context.Load(sampledImageType, sampledImageVariable);
- image = context.Image(imageType, image);
+ image = context.Load(declaration.SampledImageType, image);
+ image = context.Image(declaration.ImageType, image);
SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image);
@@ -1514,25 +1471,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
AstTextureOperation texOp = (AstTextureOperation)operation;
- bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
+ SamplerDeclaration declaration = context.Samplers[texOp.Binding];
+ SpvInstruction image = declaration.Image;
- // TODO: Bindless texture support. For now we just return 0.
- if (isBindless)
+ if (declaration.IsIndexed)
{
- return new OperationResult(AggregateType.S32, context.Constant(context.TypeS32(), 0));
- }
-
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
+ SpvInstruction textureIndex = context.GetS32(texOp.GetSource(0));
- if (isIndexed)
- {
- context.GetS32(texOp.GetSource(0));
+ image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
}
- (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
-
- var image = context.Load(sampledImageType, sampledImageVariable);
- image = context.Image(imageType, image);
+ image = context.Load(declaration.SampledImageType, image);
+ image = context.Image(declaration.ImageType, image);
if (texOp.Index == 3)
{
@@ -1556,7 +1506,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (hasLod)
{
- int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
+ int lodSrcIndex = declaration.IsIndexed ? 1 : 0;
var lod = context.GetS32(operation.GetSource(lodSrcIndex));
result = context.ImageQuerySizeLod(resultType, image, lod);
}
@@ -1929,38 +1879,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return context.Load(context.GetType(varType), context.Inputs[ioDefinition]);
}
- private static OperationResult GetZeroOperationResult(
- CodeGenContext context,
- AstTextureOperation texOp,
- AggregateType scalarType,
- bool isVector)
- {
- var zero = scalarType switch
- {
- AggregateType.S32 => context.Constant(context.TypeS32(), 0),
- AggregateType.U32 => context.Constant(context.TypeU32(), 0u),
- _ => context.Constant(context.TypeFP32(), 0f),
- };
-
- if (isVector)
- {
- AggregateType outputType = texOp.GetVectorType(scalarType);
-
- if ((outputType & AggregateType.ElementCountMask) != 0)
- {
- int componentsCount = BitOperations.PopCount((uint)texOp.Index);
-
- SpvInstruction[] values = new SpvInstruction[componentsCount];
-
- values.AsSpan().Fill(zero);
-
- return new OperationResult(outputType, context.ConstantComposite(context.GetType(outputType), values));
- }
- }
-
- return new OperationResult(scalarType, zero);
- }
-
private static SpvInstruction GetSwizzledResult(CodeGenContext context, SpvInstruction vector, AggregateType swizzledResultType, int mask)
{
if ((swizzledResultType & AggregateType.ElementCountMask) != 0)
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SamplerDeclaration.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SamplerDeclaration.cs
new file mode 100644
index 00000000..9e0ecd79
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SamplerDeclaration.cs
@@ -0,0 +1,27 @@
+using Spv.Generator;
+
+namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
+{
+ readonly struct SamplerDeclaration
+ {
+ public readonly Instruction ImageType;
+ public readonly Instruction SampledImageType;
+ public readonly Instruction SampledImagePointerType;
+ public readonly Instruction Image;
+ public readonly bool IsIndexed;
+
+ public SamplerDeclaration(
+ Instruction imageType,
+ Instruction sampledImageType,
+ Instruction sampledImagePointerType,
+ Instruction image,
+ bool isIndexed)
+ {
+ ImageType = imageType;
+ SampledImageType = sampledImageType;
+ SampledImagePointerType = sampledImagePointerType;
+ Image = image;
+ IsIndexed = isIndexed;
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index df6d29dc..99366ad6 100644
--- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -27,46 +27,41 @@ namespace Ryujinx.Graphics.Shader
ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize);
/// <summary>
+ /// Gets the size in bytes of a bound constant buffer for the current shader stage.
+ /// </summary>
+ /// <param name="slot">The number of the constant buffer to get the size from</param>
+ /// <returns>Size in bytes</returns>
+ int QueryTextureArrayLengthFromBuffer(int slot);
+
+ /// <summary>
/// Queries the binding number of a constant buffer.
/// </summary>
/// <param name="index">Constant buffer index</param>
/// <returns>Binding number</returns>
- int QueryBindingConstantBuffer(int index)
- {
- return index + 1;
- }
+ int CreateConstantBufferBinding(int index);
/// <summary>
- /// Queries the binding number of a storage buffer.
+ /// Queries the binding number of an image.
/// </summary>
- /// <param name="index">Storage buffer index</param>
+ /// <param name="count">For array of images, the number of elements of the array, otherwise it should be 1</param>
+ /// <param name="isBuffer">Indicates if the image is a buffer image</param>
/// <returns>Binding number</returns>
- int QueryBindingStorageBuffer(int index)
- {
- return index;
- }
+ int CreateImageBinding(int count, bool isBuffer);
/// <summary>
- /// Queries the binding number of a texture.
+ /// Queries the binding number of a storage buffer.
/// </summary>
- /// <param name="index">Texture index</param>
- /// <param name="isBuffer">Indicates if the texture is a buffer texture</param>
+ /// <param name="index">Storage buffer index</param>
/// <returns>Binding number</returns>
- int QueryBindingTexture(int index, bool isBuffer)
- {
- return index;
- }
+ int CreateStorageBufferBinding(int index);
/// <summary>
- /// Queries the binding number of an image.
+ /// Queries the binding number of a texture.
/// </summary>
- /// <param name="index">Image index</param>
- /// <param name="isBuffer">Indicates if the image is a buffer image</param>
+ /// <param name="count">For array of textures, the number of elements of the array, otherwise it should be 1</param>
+ /// <param name="isBuffer">Indicates if the texture is a buffer texture</param>
/// <returns>Binding number</returns>
- int QueryBindingImage(int index, bool isBuffer)
- {
- return index;
- }
+ int CreateTextureBinding(int count, bool isBuffer);
/// <summary>
/// Queries Local Size X for compute shaders.
diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
index e5695ebc..8703e660 100644
--- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
+++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
@@ -161,5 +161,17 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
inst &= Instruction.Mask;
return inst == Instruction.Lod || inst == Instruction.TextureQuerySamples || inst == Instruction.TextureQuerySize;
}
+
+ public static bool IsImage(this Instruction inst)
+ {
+ inst &= Instruction.Mask;
+ return inst == Instruction.ImageAtomic || inst == Instruction.ImageLoad || inst == Instruction.ImageStore;
+ }
+
+ public static bool IsImageStore(this Instruction inst)
+ {
+ inst &= Instruction.Mask;
+ return inst == Instruction.ImageAtomic || inst == Instruction.ImageStore;
+ }
}
}
diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
index f5396a88..0c1b2a3f 100644
--- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
+++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
@@ -20,13 +20,13 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
}
set
{
- if (value != null && value.Type == OperandType.LocalVariable)
- {
- value.AsgOp = this;
- }
-
if (value != null)
{
+ if (value.Type == OperandType.LocalVariable)
+ {
+ value.AsgOp = this;
+ }
+
_dests = new[] { value };
}
else
diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs
index fa5550a6..1b82e294 100644
--- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs
+++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs
@@ -26,9 +26,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Binding = binding;
}
- public void TurnIntoIndexed(int binding)
+ public void TurnIntoArray(int binding)
{
- Type |= SamplerType.Indexed;
Flags &= ~TextureFlags.Bindless;
Binding = binding;
}
diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs
index 85e97368..66c748bf 100644
--- a/src/Ryujinx.Graphics.Shader/SamplerType.cs
+++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs
@@ -16,9 +16,8 @@ namespace Ryujinx.Graphics.Shader
Mask = 0xff,
Array = 1 << 8,
- Indexed = 1 << 9,
- Multisample = 1 << 10,
- Shadow = 1 << 11,
+ Multisample = 1 << 9,
+ Shadow = 1 << 10,
}
static class SamplerTypeExtensions
@@ -36,6 +35,36 @@ namespace Ryujinx.Graphics.Shader
};
}
+ public static string ToShortSamplerType(this SamplerType type)
+ {
+ string typeName = (type & SamplerType.Mask) switch
+ {
+ SamplerType.Texture1D => "1d",
+ SamplerType.TextureBuffer => "b",
+ SamplerType.Texture2D => "2d",
+ SamplerType.Texture3D => "3d",
+ SamplerType.TextureCube => "cube",
+ _ => throw new ArgumentException($"Invalid sampler type \"{type}\"."),
+ };
+
+ if ((type & SamplerType.Multisample) != 0)
+ {
+ typeName += "ms";
+ }
+
+ if ((type & SamplerType.Array) != 0)
+ {
+ typeName += "a";
+ }
+
+ if ((type & SamplerType.Shadow) != 0)
+ {
+ typeName += "s";
+ }
+
+ return typeName;
+ }
+
public static string ToGlslSamplerType(this SamplerType type)
{
string typeName = (type & SamplerType.Mask) switch
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs
index e45c8285..bdd3a2ed 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs
@@ -4,15 +4,17 @@ namespace Ryujinx.Graphics.Shader
{
public int Set { get; }
public int Binding { get; }
+ public int ArrayLength { get; }
public string Name { get; }
public SamplerType Type { get; }
public TextureFormat Format { get; }
public TextureUsageFlags Flags { get; }
- public TextureDefinition(int set, int binding, string name, SamplerType type, TextureFormat format, TextureUsageFlags flags)
+ public TextureDefinition(int set, int binding, int arrayLength, string name, SamplerType type, TextureFormat format, TextureUsageFlags flags)
{
Set = set;
Binding = binding;
+ ArrayLength = arrayLength;
Name = name;
Type = type;
Format = format;
@@ -21,7 +23,7 @@ namespace Ryujinx.Graphics.Shader
public TextureDefinition SetFlag(TextureUsageFlags flag)
{
- return new TextureDefinition(Set, Binding, Name, Type, Format, Flags | flag);
+ return new TextureDefinition(Set, Binding, ArrayLength, Name, Type, Format, Flags | flag);
}
}
}
diff --git a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs
index 1130b63b..38834da7 100644
--- a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs
+++ b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs
@@ -11,16 +11,25 @@ namespace Ryujinx.Graphics.Shader
public readonly int CbufSlot;
public readonly int HandleIndex;
+ public readonly int ArrayLength;
public readonly TextureUsageFlags Flags;
- public TextureDescriptor(int binding, SamplerType type, TextureFormat format, int cbufSlot, int handleIndex, TextureUsageFlags flags)
+ public TextureDescriptor(
+ int binding,
+ SamplerType type,
+ TextureFormat format,
+ int cbufSlot,
+ int handleIndex,
+ int arrayLength,
+ TextureUsageFlags flags)
{
Binding = binding;
Type = type;
Format = format;
CbufSlot = cbufSlot;
HandleIndex = handleIndex;
+ ArrayLength = arrayLength;
Flags = flags;
}
}
diff --git a/src/Ryujinx.Graphics.Shader/TextureHandle.cs b/src/Ryujinx.Graphics.Shader/TextureHandle.cs
index fc9ab2d6..7df9c8e4 100644
--- a/src/Ryujinx.Graphics.Shader/TextureHandle.cs
+++ b/src/Ryujinx.Graphics.Shader/TextureHandle.cs
@@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader
{
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = UnpackOffsets(wordOffset);
- int handle = cachedTextureBuffer.Length != 0 ? cachedTextureBuffer[textureWordOffset] : 0;
+ int handle = textureWordOffset < cachedTextureBuffer.Length ? cachedTextureBuffer[textureWordOffset] : 0;
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses
@@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.Shader
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
{
- samplerHandle = cachedSamplerBuffer.Length != 0 ? cachedSamplerBuffer[samplerWordOffset] : 0;
+ samplerHandle = samplerWordOffset < cachedSamplerBuffer.Length ? cachedSamplerBuffer[samplerWordOffset] : 0;
}
else
{
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
index a8890327..ad955278 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
@@ -15,8 +15,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
// - The handle is a constant buffer value.
// - The handle is the result of a bitwise OR logical operation.
// - Both sources of the OR operation comes from a constant buffer.
- for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
+ LinkedListNode<INode> nextNode;
+
+ for (LinkedListNode<INode> node = block.Operations.First; node != null; node = nextNode)
{
+ nextNode = node.Next;
+
if (node.Value is not TextureOperation texOp)
{
continue;
@@ -27,185 +31,207 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
continue;
}
- if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
+ if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp))
{
- Operand bindlessHandle = texOp.GetSource(0);
+ // If we can't do bindless elimination, remove the texture operation.
+ // Set any destination variables to zero.
- // In some cases the compiler uses a shuffle operation to get the handle,
- // for some textureGrad implementations. In those cases, we can skip the shuffle.
- if (bindlessHandle.AsgOp is Operation shuffleOp && shuffleOp.Inst == Instruction.Shuffle)
+ for (int destIndex = 0; destIndex < texOp.DestsCount; destIndex++)
{
- bindlessHandle = shuffleOp.GetSource(0);
+ block.Operations.AddBefore(node, new Operation(Instruction.Copy, texOp.GetDest(destIndex), OperandHelper.Const(0)));
}
- bindlessHandle = Utils.FindLastOperation(bindlessHandle, block);
+ Utils.DeleteNode(node, texOp);
+ }
+ }
+ }
- // Some instructions do not encode an accurate sampler type:
- // - Most instructions uses the same type for 1D and Buffer.
- // - Query instructions may not have any type.
- // For those cases, we need to try getting the type from current GPU state,
- // as long bindless elimination is successful and we know where the texture descriptor is located.
- bool rewriteSamplerType =
- texOp.Type == SamplerType.TextureBuffer ||
- texOp.Inst == Instruction.TextureQuerySamples ||
- texOp.Inst == Instruction.TextureQuerySize;
+ private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp)
+ {
+ if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
+ {
+ Operand bindlessHandle = texOp.GetSource(0);
- if (bindlessHandle.Type == OperandType.ConstantBuffer)
- {
- SetHandle(
- resourceManager,
- gpuAccessor,
- texOp,
- bindlessHandle.GetCbufOffset(),
- bindlessHandle.GetCbufSlot(),
- rewriteSamplerType,
- isImage: false);
-
- continue;
- }
+ // In some cases the compiler uses a shuffle operation to get the handle,
+ // for some textureGrad implementations. In those cases, we can skip the shuffle.
+ if (bindlessHandle.AsgOp is Operation shuffleOp && shuffleOp.Inst == Instruction.Shuffle)
+ {
+ bindlessHandle = shuffleOp.GetSource(0);
+ }
- if (!TryGetOperation(bindlessHandle.AsgOp, out Operation handleCombineOp))
- {
- continue;
- }
+ bindlessHandle = Utils.FindLastOperation(bindlessHandle, block);
- if (handleCombineOp.Inst != Instruction.BitwiseOr)
- {
- continue;
- }
+ // Some instructions do not encode an accurate sampler type:
+ // - Most instructions uses the same type for 1D and Buffer.
+ // - Query instructions may not have any type.
+ // For those cases, we need to try getting the type from current GPU state,
+ // as long bindless elimination is successful and we know where the texture descriptor is located.
+ bool rewriteSamplerType =
+ texOp.Type == SamplerType.TextureBuffer ||
+ texOp.Inst == Instruction.TextureQuerySamples ||
+ texOp.Inst == Instruction.TextureQuerySize;
- Operand src0 = Utils.FindLastOperation(handleCombineOp.GetSource(0), block);
- Operand src1 = Utils.FindLastOperation(handleCombineOp.GetSource(1), block);
+ if (bindlessHandle.Type == OperandType.ConstantBuffer)
+ {
+ SetHandle(
+ resourceManager,
+ gpuAccessor,
+ texOp,
+ bindlessHandle.GetCbufOffset(),
+ bindlessHandle.GetCbufSlot(),
+ rewriteSamplerType,
+ isImage: false);
+
+ return true;
+ }
- // For cases where we have a constant, ensure that the constant is always
- // the second operand.
- // Since this is a commutative operation, both are fine,
- // and having a "canonical" representation simplifies some checks below.
- if (src0.Type == OperandType.Constant && src1.Type != OperandType.Constant)
- {
- (src0, src1) = (src1, src0);
- }
+ if (!TryGetOperation(bindlessHandle.AsgOp, out Operation handleCombineOp))
+ {
+ return false;
+ }
- TextureHandleType handleType = TextureHandleType.SeparateSamplerHandle;
-
- // Try to match the following patterns:
- // Masked pattern:
- // - samplerHandle = samplerHandle & 0xFFF00000;
- // - textureHandle = textureHandle & 0xFFFFF;
- // - combinedHandle = samplerHandle | textureHandle;
- // Where samplerHandle and textureHandle comes from a constant buffer.
- // Shifted pattern:
- // - samplerHandle = samplerId << 20;
- // - combinedHandle = samplerHandle | textureHandle;
- // Where samplerId and textureHandle comes from a constant buffer.
- // Constant pattern:
- // - combinedHandle = samplerHandleConstant | textureHandle;
- // Where samplerHandleConstant is a constant value, and textureHandle comes from a constant buffer.
- if (src0.AsgOp is Operation src0AsgOp)
+ if (handleCombineOp.Inst != Instruction.BitwiseOr)
+ {
+ return false;
+ }
+
+ Operand src0 = Utils.FindLastOperation(handleCombineOp.GetSource(0), block);
+ Operand src1 = Utils.FindLastOperation(handleCombineOp.GetSource(1), block);
+
+ // For cases where we have a constant, ensure that the constant is always
+ // the second operand.
+ // Since this is a commutative operation, both are fine,
+ // and having a "canonical" representation simplifies some checks below.
+ if (src0.Type == OperandType.Constant && src1.Type != OperandType.Constant)
+ {
+ (src0, src1) = (src1, src0);
+ }
+
+ TextureHandleType handleType = TextureHandleType.SeparateSamplerHandle;
+
+ // Try to match the following patterns:
+ // Masked pattern:
+ // - samplerHandle = samplerHandle & 0xFFF00000;
+ // - textureHandle = textureHandle & 0xFFFFF;
+ // - combinedHandle = samplerHandle | textureHandle;
+ // Where samplerHandle and textureHandle comes from a constant buffer.
+ // Shifted pattern:
+ // - samplerHandle = samplerId << 20;
+ // - combinedHandle = samplerHandle | textureHandle;
+ // Where samplerId and textureHandle comes from a constant buffer.
+ // Constant pattern:
+ // - combinedHandle = samplerHandleConstant | textureHandle;
+ // Where samplerHandleConstant is a constant value, and textureHandle comes from a constant buffer.
+ if (src0.AsgOp is Operation src0AsgOp)
+ {
+ if (src1.AsgOp is Operation src1AsgOp &&
+ src0AsgOp.Inst == Instruction.BitwiseAnd &&
+ src1AsgOp.Inst == Instruction.BitwiseAnd)
{
- if (src1.AsgOp is Operation src1AsgOp &&
- src0AsgOp.Inst == Instruction.BitwiseAnd &&
- src1AsgOp.Inst == Instruction.BitwiseAnd)
+ src0 = GetSourceForMaskedHandle(src0AsgOp, 0xFFFFF);
+ src1 = GetSourceForMaskedHandle(src1AsgOp, 0xFFF00000);
+
+ // The OR operation is commutative, so we can also try to swap the operands to get a match.
+ if (src0 == null || src1 == null)
{
- src0 = GetSourceForMaskedHandle(src0AsgOp, 0xFFFFF);
- src1 = GetSourceForMaskedHandle(src1AsgOp, 0xFFF00000);
-
- // The OR operation is commutative, so we can also try to swap the operands to get a match.
- if (src0 == null || src1 == null)
- {
- src0 = GetSourceForMaskedHandle(src1AsgOp, 0xFFFFF);
- src1 = GetSourceForMaskedHandle(src0AsgOp, 0xFFF00000);
- }
-
- if (src0 == null || src1 == null)
- {
- continue;
- }
+ src0 = GetSourceForMaskedHandle(src1AsgOp, 0xFFFFF);
+ src1 = GetSourceForMaskedHandle(src0AsgOp, 0xFFF00000);
}
- else if (src0AsgOp.Inst == Instruction.ShiftLeft)
+
+ if (src0 == null || src1 == null)
{
- Operand shift = src0AsgOp.GetSource(1);
-
- if (shift.Type == OperandType.Constant && shift.Value == 20)
- {
- src0 = src1;
- src1 = src0AsgOp.GetSource(0);
- handleType = TextureHandleType.SeparateSamplerId;
- }
+ return false;
}
}
- else if (src1.AsgOp is Operation src1AsgOp && src1AsgOp.Inst == Instruction.ShiftLeft)
+ else if (src0AsgOp.Inst == Instruction.ShiftLeft)
{
- Operand shift = src1AsgOp.GetSource(1);
+ Operand shift = src0AsgOp.GetSource(1);
if (shift.Type == OperandType.Constant && shift.Value == 20)
{
- src1 = src1AsgOp.GetSource(0);
+ src0 = src1;
+ src1 = src0AsgOp.GetSource(0);
handleType = TextureHandleType.SeparateSamplerId;
}
}
- else if (src1.Type == OperandType.Constant && (src1.Value & 0xfffff) == 0)
- {
- handleType = TextureHandleType.SeparateConstantSamplerHandle;
- }
+ }
+ else if (src1.AsgOp is Operation src1AsgOp && src1AsgOp.Inst == Instruction.ShiftLeft)
+ {
+ Operand shift = src1AsgOp.GetSource(1);
- if (src0.Type != OperandType.ConstantBuffer)
+ if (shift.Type == OperandType.Constant && shift.Value == 20)
{
- continue;
+ src1 = src1AsgOp.GetSource(0);
+ handleType = TextureHandleType.SeparateSamplerId;
}
+ }
+ else if (src1.Type == OperandType.Constant && (src1.Value & 0xfffff) == 0)
+ {
+ handleType = TextureHandleType.SeparateConstantSamplerHandle;
+ }
- if (handleType == TextureHandleType.SeparateConstantSamplerHandle)
- {
- SetHandle(
- resourceManager,
- gpuAccessor,
- texOp,
- TextureHandle.PackOffsets(src0.GetCbufOffset(), ((src1.Value >> 20) & 0xfff), handleType),
- TextureHandle.PackSlots(src0.GetCbufSlot(), 0),
- rewriteSamplerType,
- isImage: false);
- }
- else if (src1.Type == OperandType.ConstantBuffer)
- {
- SetHandle(
- resourceManager,
- gpuAccessor,
- texOp,
- TextureHandle.PackOffsets(src0.GetCbufOffset(), src1.GetCbufOffset(), handleType),
- TextureHandle.PackSlots(src0.GetCbufSlot(), src1.GetCbufSlot()),
- rewriteSamplerType,
- isImage: false);
- }
+ if (src0.Type != OperandType.ConstantBuffer)
+ {
+ return false;
}
- else if (texOp.Inst == Instruction.ImageLoad ||
- texOp.Inst == Instruction.ImageStore ||
- texOp.Inst == Instruction.ImageAtomic)
+
+ if (handleType == TextureHandleType.SeparateConstantSamplerHandle)
{
- Operand src0 = Utils.FindLastOperation(texOp.GetSource(0), block);
+ SetHandle(
+ resourceManager,
+ gpuAccessor,
+ texOp,
+ TextureHandle.PackOffsets(src0.GetCbufOffset(), ((src1.Value >> 20) & 0xfff), handleType),
+ TextureHandle.PackSlots(src0.GetCbufSlot(), 0),
+ rewriteSamplerType,
+ isImage: false);
+
+ return true;
+ }
+ else if (src1.Type == OperandType.ConstantBuffer)
+ {
+ SetHandle(
+ resourceManager,
+ gpuAccessor,
+ texOp,
+ TextureHandle.PackOffsets(src0.GetCbufOffset(), src1.GetCbufOffset(), handleType),
+ TextureHandle.PackSlots(src0.GetCbufSlot(), src1.GetCbufSlot()),
+ rewriteSamplerType,
+ isImage: false);
+
+ return true;
+ }
+ }
+ else if (texOp.Inst.IsImage())
+ {
+ Operand src0 = Utils.FindLastOperation(texOp.GetSource(0), block);
- if (src0.Type == OperandType.ConstantBuffer)
- {
- int cbufOffset = src0.GetCbufOffset();
- int cbufSlot = src0.GetCbufSlot();
+ if (src0.Type == OperandType.ConstantBuffer)
+ {
+ int cbufOffset = src0.GetCbufOffset();
+ int cbufSlot = src0.GetCbufSlot();
- if (texOp.Format == TextureFormat.Unknown)
+ if (texOp.Format == TextureFormat.Unknown)
+ {
+ if (texOp.Inst == Instruction.ImageAtomic)
{
- if (texOp.Inst == Instruction.ImageAtomic)
- {
- texOp.Format = ShaderProperties.GetTextureFormatAtomic(gpuAccessor, cbufOffset, cbufSlot);
- }
- else
- {
- texOp.Format = ShaderProperties.GetTextureFormat(gpuAccessor, cbufOffset, cbufSlot);
- }
+ texOp.Format = ShaderProperties.GetTextureFormatAtomic(gpuAccessor, cbufOffset, cbufSlot);
}
+ else
+ {
+ texOp.Format = ShaderProperties.GetTextureFormat(gpuAccessor, cbufOffset, cbufSlot);
+ }
+ }
- bool rewriteSamplerType = texOp.Type == SamplerType.TextureBuffer;
+ bool rewriteSamplerType = texOp.Type == SamplerType.TextureBuffer;
- SetHandle(resourceManager, gpuAccessor, texOp, cbufOffset, cbufSlot, rewriteSamplerType, isImage: true);
- }
+ SetHandle(resourceManager, gpuAccessor, texOp, cbufOffset, cbufSlot, rewriteSamplerType, isImage: true);
+
+ return true;
}
}
+
+ return false;
}
private static bool TryGetOperation(INode asgOp, out Operation outOperation)
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs
new file mode 100644
index 00000000..7543d1c2
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs
@@ -0,0 +1,236 @@
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using System;
+using System.Collections.Generic;
+using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
+
+namespace Ryujinx.Graphics.Shader.Translation.Optimizations
+{
+ static class BindlessToArray
+ {
+ private const int NvnTextureBufferIndex = 2;
+ private const int HardcodedArrayLengthOgl = 4;
+
+ // 1 and 0 elements are not considered arrays anymore.
+ private const int MinimumArrayLength = 2;
+
+ public static void RunPassOgl(BasicBlock block, ResourceManager resourceManager)
+ {
+ // We can turn a bindless texture access into a indexed access,
+ // as long the following conditions are true:
+ // - The handle is loaded using a LDC instruction.
+ // - The handle is loaded from the constant buffer with the handles (CB2 for NVN).
+ // - The load has a constant offset.
+ // The base offset of the array of handles on the constant buffer is the constant offset.
+ for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
+ {
+ if (node.Value is not TextureOperation texOp)
+ {
+ continue;
+ }
+
+ if ((texOp.Flags & TextureFlags.Bindless) == 0)
+ {
+ continue;
+ }
+
+ if (texOp.GetSource(0).AsgOp is not Operation handleAsgOp)
+ {
+ continue;
+ }
+
+ if (handleAsgOp.Inst != Instruction.Load ||
+ handleAsgOp.StorageKind != StorageKind.ConstantBuffer ||
+ handleAsgOp.SourcesCount != 4)
+ {
+ continue;
+ }
+
+ Operand ldcSrc0 = handleAsgOp.GetSource(0);
+
+ if (ldcSrc0.Type != OperandType.Constant ||
+ !resourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) ||
+ src0CbufSlot != NvnTextureBufferIndex)
+ {
+ continue;
+ }
+
+ Operand ldcSrc1 = handleAsgOp.GetSource(1);
+
+ // We expect field index 0 to be accessed.
+ if (ldcSrc1.Type != OperandType.Constant || ldcSrc1.Value != 0)
+ {
+ continue;
+ }
+
+ Operand ldcSrc2 = handleAsgOp.GetSource(2);
+
+ // FIXME: This is missing some checks, for example, a check to ensure that the shift value is 2.
+ // Might be not worth fixing since if that doesn't kick in, the result will be no texture
+ // to access anyway which is also wrong.
+ // Plus this whole transform is fundamentally flawed as-is since we have no way to know the array size.
+ // Eventually, this should be entirely removed in favor of a implementation that supports true bindless
+ // texture access.
+ if (ldcSrc2.AsgOp is not Operation shrOp || shrOp.Inst != Instruction.ShiftRightU32)
+ {
+ continue;
+ }
+
+ if (shrOp.GetSource(0).AsgOp is not Operation shrOp2 || shrOp2.Inst != Instruction.ShiftRightU32)
+ {
+ continue;
+ }
+
+ if (shrOp2.GetSource(0).AsgOp is not Operation addOp || addOp.Inst != Instruction.Add)
+ {
+ continue;
+ }
+
+ Operand addSrc1 = addOp.GetSource(1);
+
+ if (addSrc1.Type != OperandType.Constant)
+ {
+ continue;
+ }
+
+ TurnIntoArray(resourceManager, texOp, NvnTextureBufferIndex, addSrc1.Value / 4, HardcodedArrayLengthOgl);
+
+ Operand index = Local();
+
+ Operand source = addOp.GetSource(0);
+
+ Operation shrBy3 = new(Instruction.ShiftRightU32, index, source, Const(3));
+
+ block.Operations.AddBefore(node, shrBy3);
+
+ texOp.SetSource(0, index);
+ }
+ }
+
+ public static void RunPass(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor)
+ {
+ // We can turn a bindless texture access into a indexed access,
+ // as long the following conditions are true:
+ // - The handle is loaded using a LDC instruction.
+ // - The handle is loaded from the constant buffer with the handles (CB2 for NVN).
+ // - The load has a constant offset.
+ // The base offset of the array of handles on the constant buffer is the constant offset.
+ for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
+ {
+ if (node.Value is not TextureOperation texOp)
+ {
+ continue;
+ }
+
+ if ((texOp.Flags & TextureFlags.Bindless) == 0)
+ {
+ continue;
+ }
+
+ if (texOp.GetSource(0).AsgOp is not Operation handleAsgOp)
+ {
+ continue;
+ }
+
+ int secondaryCbufSlot = 0;
+ int secondaryCbufOffset = 0;
+ bool hasSecondaryHandle = false;
+
+ if (handleAsgOp.Inst == Instruction.BitwiseOr)
+ {
+ Operand src0 = handleAsgOp.GetSource(0);
+ Operand src1 = handleAsgOp.GetSource(1);
+
+ if (src0.Type == OperandType.ConstantBuffer && src1.AsgOp is Operation)
+ {
+ handleAsgOp = src1.AsgOp as Operation;
+ secondaryCbufSlot = src0.GetCbufSlot();
+ secondaryCbufOffset = src0.GetCbufOffset();
+ hasSecondaryHandle = true;
+ }
+ else if (src0.AsgOp is Operation && src1.Type == OperandType.ConstantBuffer)
+ {
+ handleAsgOp = src0.AsgOp as Operation;
+ secondaryCbufSlot = src1.GetCbufSlot();
+ secondaryCbufOffset = src1.GetCbufOffset();
+ hasSecondaryHandle = true;
+ }
+ }
+
+ if (handleAsgOp.Inst != Instruction.Load ||
+ handleAsgOp.StorageKind != StorageKind.ConstantBuffer ||
+ handleAsgOp.SourcesCount != 4)
+ {
+ continue;
+ }
+
+ Operand ldcSrc0 = handleAsgOp.GetSource(0);
+
+ if (ldcSrc0.Type != OperandType.Constant ||
+ !resourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot))
+ {
+ continue;
+ }
+
+ Operand ldcSrc1 = handleAsgOp.GetSource(1);
+
+ // We expect field index 0 to be accessed.
+ if (ldcSrc1.Type != OperandType.Constant || ldcSrc1.Value != 0)
+ {
+ continue;
+ }
+
+ Operand ldcVecIndex = handleAsgOp.GetSource(2);
+ Operand ldcElemIndex = handleAsgOp.GetSource(3);
+
+ if (ldcVecIndex.Type != OperandType.LocalVariable || ldcElemIndex.Type != OperandType.LocalVariable)
+ {
+ continue;
+ }
+
+ int cbufSlot;
+ int handleIndex;
+
+ if (hasSecondaryHandle)
+ {
+ cbufSlot = TextureHandle.PackSlots(src0CbufSlot, secondaryCbufSlot);
+ handleIndex = TextureHandle.PackOffsets(0, secondaryCbufOffset, TextureHandleType.SeparateSamplerHandle);
+ }
+ else
+ {
+ cbufSlot = src0CbufSlot;
+ handleIndex = 0;
+ }
+
+ int length = Math.Max(MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromBuffer(src0CbufSlot));
+
+ TurnIntoArray(resourceManager, texOp, cbufSlot, handleIndex, length);
+
+ Operand vecIndex = Local();
+ Operand elemIndex = Local();
+ Operand index = Local();
+ Operand indexMin = Local();
+
+ block.Operations.AddBefore(node, new Operation(Instruction.ShiftLeft, vecIndex, ldcVecIndex, Const(1)));
+ block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, elemIndex, ldcElemIndex, Const(1)));
+ block.Operations.AddBefore(node, new Operation(Instruction.Add, index, vecIndex, elemIndex));
+ block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, indexMin, index, Const(length - 1)));
+
+ texOp.SetSource(0, indexMin);
+ }
+ }
+
+ private static void TurnIntoArray(ResourceManager resourceManager, TextureOperation texOp, int cbufSlot, int handleIndex, int length)
+ {
+ int binding = resourceManager.GetTextureOrImageBinding(
+ texOp.Inst,
+ texOp.Type,
+ texOp.Format,
+ texOp.Flags & ~TextureFlags.Bindless,
+ cbufSlot,
+ handleIndex,
+ length);
+
+ texOp.TurnIntoArray(binding);
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs
deleted file mode 100644
index 2bd31fe1..00000000
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using Ryujinx.Graphics.Shader.IntermediateRepresentation;
-using System.Collections.Generic;
-
-using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
-
-namespace Ryujinx.Graphics.Shader.Translation.Optimizations
-{
- static class BindlessToIndexed
- {
- private const int NvnTextureBufferIndex = 2;
-
- public static void RunPass(BasicBlock block, ResourceManager resourceManager)
- {
- // We can turn a bindless texture access into a indexed access,
- // as long the following conditions are true:
- // - The handle is loaded using a LDC instruction.
- // - The handle is loaded from the constant buffer with the handles (CB2 for NVN).
- // - The load has a constant offset.
- // The base offset of the array of handles on the constant buffer is the constant offset.
- for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
- {
- if (node.Value is not TextureOperation texOp)
- {
- continue;
- }
-
- if ((texOp.Flags & TextureFlags.Bindless) == 0)
- {
- continue;
- }
-
- if (texOp.GetSource(0).AsgOp is not Operation handleAsgOp)
- {
- continue;
- }
-
- if (handleAsgOp.Inst != Instruction.Load ||
- handleAsgOp.StorageKind != StorageKind.ConstantBuffer ||
- handleAsgOp.SourcesCount != 4)
- {
- continue;
- }
-
- Operand ldcSrc0 = handleAsgOp.GetSource(0);
-
- if (ldcSrc0.Type != OperandType.Constant ||
- !resourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) ||
- src0CbufSlot != NvnTextureBufferIndex)
- {
- continue;
- }
-
- Operand ldcSrc1 = handleAsgOp.GetSource(1);
-
- // We expect field index 0 to be accessed.
- if (ldcSrc1.Type != OperandType.Constant || ldcSrc1.Value != 0)
- {
- continue;
- }
-
- Operand ldcSrc2 = handleAsgOp.GetSource(2);
-
- // FIXME: This is missing some checks, for example, a check to ensure that the shift value is 2.
- // Might be not worth fixing since if that doesn't kick in, the result will be no texture
- // to access anyway which is also wrong.
- // Plus this whole transform is fundamentally flawed as-is since we have no way to know the array size.
- // Eventually, this should be entirely removed in favor of a implementation that supports true bindless
- // texture access.
- if (ldcSrc2.AsgOp is not Operation shrOp || shrOp.Inst != Instruction.ShiftRightU32)
- {
- continue;
- }
-
- if (shrOp.GetSource(0).AsgOp is not Operation shrOp2 || shrOp2.Inst != Instruction.ShiftRightU32)
- {
- continue;
- }
-
- if (shrOp2.GetSource(0).AsgOp is not Operation addOp || addOp.Inst != Instruction.Add)
- {
- continue;
- }
-
- Operand addSrc1 = addOp.GetSource(1);
-
- if (addSrc1.Type != OperandType.Constant)
- {
- continue;
- }
-
- TurnIntoIndexed(resourceManager, texOp, addSrc1.Value / 4);
-
- Operand index = Local();
-
- Operand source = addOp.GetSource(0);
-
- Operation shrBy3 = new(Instruction.ShiftRightU32, index, source, Const(3));
-
- block.Operations.AddBefore(node, shrBy3);
-
- texOp.SetSource(0, index);
- }
- }
-
- private static void TurnIntoIndexed(ResourceManager resourceManager, TextureOperation texOp, int handle)
- {
- int binding = resourceManager.GetTextureOrImageBinding(
- texOp.Inst,
- texOp.Type | SamplerType.Indexed,
- texOp.Format,
- texOp.Flags & ~TextureFlags.Bindless,
- NvnTextureBufferIndex,
- handle);
-
- texOp.TurnIntoIndexed(binding);
- }
- }
-}
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
index ea06691b..49eb3a89 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
@@ -20,7 +20,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
// Those passes are looking for specific patterns and only needs to run once.
for (int blkIndex = 0; blkIndex < context.Blocks.Length; blkIndex++)
{
- BindlessToIndexed.RunPass(context.Blocks[blkIndex], context.ResourceManager);
+ if (context.TargetApi == TargetApi.OpenGL)
+ {
+ BindlessToArray.RunPassOgl(context.Blocks[blkIndex], context.ResourceManager);
+ }
+ else
+ {
+ BindlessToArray.RunPass(context.Blocks[blkIndex], context.ResourceManager, context.GpuAccessor);
+ }
+
BindlessElimination.RunPass(context.Blocks[blkIndex], context.ResourceManager, context.GpuAccessor);
// FragmentCoord only exists on fragment shaders, so we don't need to check other stages.
diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
index 83332711..e9fe0b1e 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
@@ -14,9 +14,6 @@ namespace Ryujinx.Graphics.Shader.Translation
private const int DefaultLocalMemorySize = 128;
private const int DefaultSharedMemorySize = 4096;
- // TODO: Non-hardcoded array size.
- public const int SamplerArraySize = 4;
-
private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
private readonly IGpuAccessor _gpuAccessor;
@@ -32,7 +29,7 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly HashSet<int> _usedConstantBufferBindings;
- private readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format);
+ private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, SamplerType Type, TextureFormat Format);
private struct TextureMeta
{
@@ -152,7 +149,7 @@ namespace Ryujinx.Graphics.Shader.Translation
int binding = _cbSlotToBindingMap[slot];
if (binding < 0)
{
- binding = _gpuAccessor.QueryBindingConstantBuffer(slot);
+ binding = _gpuAccessor.CreateConstantBufferBinding(slot);
_cbSlotToBindingMap[slot] = binding;
string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}");
@@ -173,7 +170,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (binding < 0)
{
- binding = _gpuAccessor.QueryBindingStorageBuffer(slot);
+ binding = _gpuAccessor.CreateStorageBufferBinding(slot);
_sbSlotToBindingMap[slot] = binding;
string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}");
@@ -227,11 +224,12 @@ namespace Ryujinx.Graphics.Shader.Translation
TextureFormat format,
TextureFlags flags,
int cbufSlot,
- int handle)
+ int handle,
+ int arrayLength = 1)
{
inst &= Instruction.Mask;
- bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
- bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
+ bool isImage = inst.IsImage();
+ bool isWrite = inst.IsImageStore();
bool accurateType = !inst.IsTextureQuery();
bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureQuerySize;
bool coherent = flags.HasFlag(TextureFlags.Coherent);
@@ -241,7 +239,7 @@ namespace Ryujinx.Graphics.Shader.Translation
format = TextureFormat.Unknown;
}
- int binding = GetTextureOrImageBinding(cbufSlot, handle, type, format, isImage, intCoords, isWrite, accurateType, coherent);
+ int binding = GetTextureOrImageBinding(cbufSlot, handle, arrayLength, type, format, isImage, intCoords, isWrite, accurateType, coherent);
_gpuAccessor.RegisterTexture(handle, cbufSlot);
@@ -251,6 +249,7 @@ namespace Ryujinx.Graphics.Shader.Translation
private int GetTextureOrImageBinding(
int cbufSlot,
int handle,
+ int arrayLength,
SamplerType type,
TextureFormat format,
bool isImage,
@@ -260,7 +259,6 @@ namespace Ryujinx.Graphics.Shader.Translation
bool coherent)
{
var dimensions = type.GetDimensions();
- var isIndexed = type.HasFlag(SamplerType.Indexed);
var dict = isImage ? _usedImages : _usedTextures;
var usageFlags = TextureUsageFlags.None;
@@ -269,7 +267,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
usageFlags |= TextureUsageFlags.NeedsScaleValue;
- var canScale = _stage.SupportsRenderScale() && !isIndexed && !write && dimensions == 2;
+ var canScale = _stage.SupportsRenderScale() && arrayLength == 1 && !write && dimensions == 2;
if (!canScale)
{
@@ -289,76 +287,75 @@ namespace Ryujinx.Graphics.Shader.Translation
usageFlags |= TextureUsageFlags.ImageCoherent;
}
- int arraySize = isIndexed ? SamplerArraySize : 1;
- int firstBinding = -1;
-
- for (int layer = 0; layer < arraySize; layer++)
+ // For array textures, we also want to use type as key,
+ // since we may have texture handles stores in the same buffer, but for textures with different types.
+ var keyType = arrayLength > 1 ? type : SamplerType.None;
+ var info = new TextureInfo(cbufSlot, handle, arrayLength, keyType, format);
+ var meta = new TextureMeta()
{
- var info = new TextureInfo(cbufSlot, handle + layer * 2, isIndexed, format);
- var meta = new TextureMeta()
- {
- AccurateType = accurateType,
- Type = type,
- UsageFlags = usageFlags,
- };
+ AccurateType = accurateType,
+ Type = type,
+ UsageFlags = usageFlags,
+ };
- int binding;
+ int binding;
- if (dict.TryGetValue(info, out var existingMeta))
- {
- dict[info] = MergeTextureMeta(meta, existingMeta);
- binding = existingMeta.Binding;
- }
- else
- {
- bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer;
+ if (dict.TryGetValue(info, out var existingMeta))
+ {
+ dict[info] = MergeTextureMeta(meta, existingMeta);
+ binding = existingMeta.Binding;
+ }
+ else
+ {
+ bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer;
- binding = isImage
- ? _gpuAccessor.QueryBindingImage(dict.Count, isBuffer)
- : _gpuAccessor.QueryBindingTexture(dict.Count, isBuffer);
+ binding = isImage
+ ? _gpuAccessor.CreateImageBinding(arrayLength, isBuffer)
+ : _gpuAccessor.CreateTextureBinding(arrayLength, isBuffer);
- meta.Binding = binding;
+ meta.Binding = binding;
- dict.Add(info, meta);
- }
+ dict.Add(info, meta);
+ }
- string nameSuffix;
+ string nameSuffix;
+ string prefix = isImage ? "i" : "t";
- if (isImage)
- {
- nameSuffix = cbufSlot < 0
- ? $"i_tcb_{handle:X}_{format.ToGlslFormat()}"
- : $"i_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
- }
- else
- {
- nameSuffix = cbufSlot < 0 ? $"t_tcb_{handle:X}" : $"t_cb{cbufSlot}_{handle:X}";
- }
+ if (arrayLength != 1 && type != SamplerType.None)
+ {
+ prefix += type.ToShortSamplerType();
+ }
- var definition = new TextureDefinition(
- isImage ? 3 : 2,
- binding,
- $"{_stagePrefix}_{nameSuffix}",
- meta.Type,
- info.Format,
- meta.UsageFlags);
+ if (isImage)
+ {
+ nameSuffix = cbufSlot < 0
+ ? $"{prefix}_tcb_{handle:X}_{format.ToGlslFormat()}"
+ : $"{prefix}_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
+ }
+ else
+ {
+ nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}";
+ }
- if (isImage)
- {
- Properties.AddOrUpdateImage(definition);
- }
- else
- {
- Properties.AddOrUpdateTexture(definition);
- }
+ var definition = new TextureDefinition(
+ isImage ? 3 : 2,
+ binding,
+ arrayLength,
+ $"{_stagePrefix}_{nameSuffix}",
+ meta.Type,
+ info.Format,
+ meta.UsageFlags);
- if (layer == 0)
- {
- firstBinding = binding;
- }
+ if (isImage)
+ {
+ Properties.AddOrUpdateImage(definition);
+ }
+ else
+ {
+ Properties.AddOrUpdateTexture(definition);
}
- return firstBinding;
+ return binding;
}
private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta)
@@ -399,8 +396,7 @@ namespace Ryujinx.Graphics.Shader.Translation
selectedMeta.UsageFlags |= TextureUsageFlags.NeedsScaleValue;
var dimensions = type.GetDimensions();
- var isIndexed = type.HasFlag(SamplerType.Indexed);
- var canScale = _stage.SupportsRenderScale() && !isIndexed && dimensions == 2;
+ var canScale = _stage.SupportsRenderScale() && selectedInfo.ArrayLength == 1 && dimensions == 2;
if (!canScale)
{
@@ -468,34 +464,61 @@ namespace Ryujinx.Graphics.Shader.Translation
return descriptors;
}
- public TextureDescriptor[] GetTextureDescriptors()
+ public TextureDescriptor[] GetTextureDescriptors(bool includeArrays = true)
{
- return GetDescriptors(_usedTextures, _usedTextures.Count);
+ return GetDescriptors(_usedTextures, includeArrays);
}
- public TextureDescriptor[] GetImageDescriptors()
+ public TextureDescriptor[] GetImageDescriptors(bool includeArrays = true)
{
- return GetDescriptors(_usedImages, _usedImages.Count);
+ return GetDescriptors(_usedImages, includeArrays);
}
- private static TextureDescriptor[] GetDescriptors(IReadOnlyDictionary<TextureInfo, TextureMeta> usedResources, int count)
+ private static TextureDescriptor[] GetDescriptors(IReadOnlyDictionary<TextureInfo, TextureMeta> usedResources, bool includeArrays)
{
- TextureDescriptor[] descriptors = new TextureDescriptor[count];
+ List<TextureDescriptor> descriptors = new();
- int descriptorIndex = 0;
+ bool hasAnyArray = false;
foreach ((TextureInfo info, TextureMeta meta) in usedResources)
{
- descriptors[descriptorIndex++] = new TextureDescriptor(
+ if (info.ArrayLength > 1)
+ {
+ hasAnyArray = true;
+ continue;
+ }
+
+ descriptors.Add(new TextureDescriptor(
meta.Binding,
meta.Type,
info.Format,
info.CbufSlot,
info.Handle,
- meta.UsageFlags);
+ info.ArrayLength,
+ meta.UsageFlags));
}
- return descriptors;
+ if (hasAnyArray && includeArrays)
+ {
+ foreach ((TextureInfo info, TextureMeta meta) in usedResources)
+ {
+ if (info.ArrayLength <= 1)
+ {
+ continue;
+ }
+
+ descriptors.Add(new TextureDescriptor(
+ meta.Binding,
+ meta.Type,
+ info.Format,
+ info.CbufSlot,
+ info.Handle,
+ info.ArrayLength,
+ meta.UsageFlags));
+ }
+ }
+
+ return descriptors.ToArray();
}
public bool TryGetCbufSlotAndHandleForTexture(int binding, out int cbufSlot, out int handle)
@@ -531,6 +554,19 @@ namespace Ryujinx.Graphics.Shader.Translation
return FindDescriptorIndex(GetImageDescriptors(), binding);
}
+ public bool IsArrayOfTexturesOrImages(int binding, bool isImage)
+ {
+ foreach ((TextureInfo info, TextureMeta meta) in isImage ? _usedImages : _usedTextures)
+ {
+ if (meta.Binding == binding)
+ {
+ return info.ArrayLength != 1;
+ }
+ }
+
+ return false;
+ }
+
private void AddNewConstantBuffer(int binding, string name)
{
StructureType type = new(new[]
diff --git a/src/Ryujinx.Graphics.Shader/Translation/TransformContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TransformContext.cs
index 87ebb8e7..1e87585f 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/TransformContext.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/TransformContext.cs
@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public readonly ShaderDefinitions Definitions;
public readonly ResourceManager ResourceManager;
public readonly IGpuAccessor GpuAccessor;
+ public readonly TargetApi TargetApi;
public readonly TargetLanguage TargetLanguage;
public readonly ShaderStage Stage;
public readonly ref FeatureFlags UsedFeatures;
@@ -19,6 +20,7 @@ namespace Ryujinx.Graphics.Shader.Translation
ShaderDefinitions definitions,
ResourceManager resourceManager,
IGpuAccessor gpuAccessor,
+ TargetApi targetApi,
TargetLanguage targetLanguage,
ShaderStage stage,
ref FeatureFlags usedFeatures)
@@ -28,6 +30,7 @@ namespace Ryujinx.Graphics.Shader.Translation
Definitions = definitions;
ResourceManager = resourceManager;
GpuAccessor = gpuAccessor;
+ TargetApi = targetApi;
TargetLanguage = targetLanguage;
Stage = stage;
UsedFeatures = ref usedFeatures;
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs
index 495ea8a9..072b4569 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs
@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
{
node = InsertCoordNormalization(context.Hfm, node, context.ResourceManager, context.GpuAccessor, context.Stage);
node = InsertCoordGatherBias(node, context.ResourceManager, context.GpuAccessor);
- node = InsertConstOffsets(node, context.GpuAccessor, context.Stage);
+ node = InsertConstOffsets(node, context.ResourceManager, context.GpuAccessor, context.Stage);
if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat())
{
@@ -45,13 +45,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
-
- int coordsCount = texOp.Type.GetDimensions();
-
- int coordsIndex = isBindless || isIndexed ? 1 : 0;
bool isImage = IsImageInstructionWithScale(texOp.Inst);
+ bool isIndexed = resourceManager.IsArrayOfTexturesOrImages(texOp.Binding, isImage);
if ((texOp.Inst == Instruction.TextureSample || isImage) &&
(intCoords || isImage) &&
@@ -62,9 +58,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
{
int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale);
int samplerIndex = isImage
- ? resourceManager.GetTextureDescriptors().Length + resourceManager.FindImageDescriptorIndex(texOp.Binding)
+ ? resourceManager.GetTextureDescriptors(includeArrays: false).Length + resourceManager.FindImageDescriptorIndex(texOp.Binding)
: resourceManager.FindTextureDescriptorIndex(texOp.Binding);
+ int coordsCount = texOp.Type.GetDimensions();
+ int coordsIndex = isBindless ? 1 : 0;
+
for (int index = 0; index < coordsCount; index++)
{
Operand scaledCoord = Local();
@@ -97,7 +96,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
TextureOperation texOp = (TextureOperation)node.Value;
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
+ bool isIndexed = resourceManager.IsArrayOfTexturesOrImages(texOp.Binding, isImage: false);
if (texOp.Inst == Instruction.TextureQuerySize &&
texOp.Index < 2 &&
@@ -152,8 +151,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
TextureOperation texOp = (TextureOperation)node.Value;
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
+ bool isIndexed = resourceManager.IsArrayOfTexturesOrImages(texOp.Binding, isImage: false);
- if (isBindless || !resourceManager.TryGetCbufSlotAndHandleForTexture(texOp.Binding, out int cbufSlot, out int handle))
+ if (isBindless || isIndexed || !resourceManager.TryGetCbufSlotAndHandleForTexture(texOp.Binding, out int cbufSlot, out int handle))
{
return node;
}
@@ -167,10 +167,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
return node;
}
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
-
int coordsCount = texOp.Type.GetDimensions();
- int coordsIndex = isBindless || isIndexed ? 1 : 0;
int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount;
@@ -178,16 +175,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
{
Operand coordSize = Local();
- Operand[] texSizeSources;
-
- if (isBindless || isIndexed)
- {
- texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) };
- }
- else
- {
- texSizeSources = new Operand[] { Const(0) };
- }
+ Operand[] texSizeSources = new Operand[] { Const(0) };
LinkedListNode<INode> textureSizeNode = node.List.AddBefore(node, new TextureOperation(
Instruction.TextureQuerySize,
@@ -201,13 +189,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
resourceManager.SetUsageFlagsForTextureQuery(texOp.Binding, texOp.Type);
- Operand source = texOp.GetSource(coordsIndex + index);
+ Operand source = texOp.GetSource(index);
Operand coordNormalized = Local();
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize)));
- texOp.SetSource(coordsIndex + index, coordNormalized);
+ texOp.SetSource(index, coordNormalized);
InsertTextureSizeUnscale(hfm, textureSizeNode, resourceManager, stage);
}
@@ -234,7 +222,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
return node;
}
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
+ bool isIndexed = resourceManager.IsArrayOfTexturesOrImages(texOp.Binding, isImage: false);
int coordsCount = texOp.Type.GetDimensions();
int coordsIndex = isBindless || isIndexed ? 1 : 0;
@@ -287,7 +275,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
return node;
}
- private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, IGpuAccessor gpuAccessor, ShaderStage stage)
+ private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor, ShaderStage stage)
{
// Non-constant texture offsets are not allowed (according to the spec),
// however some GPUs does support that.
@@ -321,7 +309,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0;
bool isArray = (texOp.Type & SamplerType.Array) != 0;
- bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
@@ -342,6 +329,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
offsetsCount = 0;
}
+ bool isIndexed = resourceManager.IsArrayOfTexturesOrImages(texOp.Binding, isImage: false);
+
Operand[] offsets = new Operand[offsetsCount];
Operand[] sources = new Operand[texOp.SourcesCount - offsetsCount];
diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
index a193ab3c..581f4372 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
@@ -294,6 +294,7 @@ namespace Ryujinx.Graphics.Shader.Translation
Definitions,
resourceManager,
GpuAccessor,
+ Options.TargetApi,
Options.TargetLanguage,
Definitions.Stage,
ref usedFeatures);
@@ -412,7 +413,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Stage == ShaderStage.Vertex)
{
int ibBinding = resourceManager.Reservations.IndexBufferTextureBinding;
- TextureDefinition indexBuffer = new(2, ibBinding, "ib_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None);
+ TextureDefinition indexBuffer = new(2, ibBinding, 1, "ib_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None);
resourceManager.Properties.AddOrUpdateTexture(indexBuffer);
int inputMap = _program.AttributeUsage.UsedInputAttributes;
@@ -421,7 +422,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
int location = BitOperations.TrailingZeroCount(inputMap);
int binding = resourceManager.Reservations.GetVertexBufferTextureBinding(location);
- TextureDefinition vaBuffer = new(2, binding, $"vb_data{location}", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None);
+ TextureDefinition vaBuffer = new(2, binding, 1, $"vb_data{location}", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None);
resourceManager.Properties.AddOrUpdateTexture(vaBuffer);
inputMap &= ~(1 << location);
@@ -430,7 +431,7 @@ namespace Ryujinx.Graphics.Shader.Translation
else if (Stage == ShaderStage.Geometry)
{
int trbBinding = resourceManager.Reservations.TopologyRemapBufferTextureBinding;
- TextureDefinition remapBuffer = new(2, trbBinding, "trb_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None);
+ TextureDefinition remapBuffer = new(2, trbBinding, 1, "trb_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None);
resourceManager.Properties.AddOrUpdateTexture(remapBuffer);
int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding;