mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-06-30 12:26:25 +02:00
Merge Latest Ryujinx (Unstable)
This commit is contained in:
parent
aaefc0a9e5
commit
12ab8bc3e2
1237 changed files with 48656 additions and 21399 deletions
|
@ -4,14 +4,16 @@ namespace Ryujinx.Graphics.Shader
|
|||
{
|
||||
// New fields should be added to the end of the struct to keep disk shader cache compatibility.
|
||||
|
||||
public readonly int Set;
|
||||
public readonly int Binding;
|
||||
public readonly byte Slot;
|
||||
public readonly byte SbCbSlot;
|
||||
public readonly ushort SbCbOffset;
|
||||
public readonly BufferUsageFlags Flags;
|
||||
|
||||
public BufferDescriptor(int binding, int slot)
|
||||
public BufferDescriptor(int set, int binding, int slot)
|
||||
{
|
||||
Set = set;
|
||||
Binding = binding;
|
||||
Slot = (byte)slot;
|
||||
SbCbSlot = 0;
|
||||
|
@ -19,8 +21,9 @@ namespace Ryujinx.Graphics.Shader
|
|||
Flags = BufferUsageFlags.None;
|
||||
}
|
||||
|
||||
public BufferDescriptor(int binding, int slot, int sbCbSlot, int sbCbOffset, BufferUsageFlags flags)
|
||||
public BufferDescriptor(int set, int binding, int slot, int sbCbSlot, int sbCbOffset, BufferUsageFlags flags)
|
||||
{
|
||||
Set = set;
|
||||
Binding = binding;
|
||||
Slot = (byte)slot;
|
||||
SbCbSlot = (byte)sbCbSlot;
|
||||
|
|
|
@ -6,7 +6,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
{
|
||||
|
@ -339,27 +338,20 @@ 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();
|
||||
string samplerTypeName = definition.Separate ? definition.Type.ToGlslTextureType() : definition.Type.ToGlslSamplerType();
|
||||
|
||||
string layout = string.Empty;
|
||||
|
||||
|
@ -368,30 +360,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 +398,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};");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)})";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
return GetSourceExpr(context, texOp.GetSource(srcIndex++), type);
|
||||
}
|
||||
|
||||
string indexExpr = null;
|
||||
|
||||
if (isIndexed)
|
||||
{
|
||||
indexExpr = Src(AggregateType.S32);
|
||||
}
|
||||
|
||||
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;
|
||||
int srcIndex = 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);
|
||||
}
|
||||
|
||||
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;
|
||||
int srcIndex = 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);
|
||||
}
|
||||
|
||||
string samplerName = GetSamplerName(context.Properties, texOp, indexExpr);
|
||||
string samplerName = GetSamplerName(context, texOp, ref srcIndex);
|
||||
|
||||
if (texOp.Index == 3)
|
||||
{
|
||||
|
@ -572,15 +462,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
}
|
||||
else
|
||||
{
|
||||
context.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition);
|
||||
context.Properties.Textures.TryGetValue(texOp.GetTextureSetAndBinding(), out TextureDefinition definition);
|
||||
bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer;
|
||||
string texCall;
|
||||
|
||||
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,53 @@ 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 textureDefinition = context.Properties.Textures[texOp.GetTextureSetAndBinding()];
|
||||
string name = textureDefinition.Name;
|
||||
|
||||
if (texOp.Type.HasFlag(SamplerType.Indexed))
|
||||
if (textureDefinition.ArrayLength != 1)
|
||||
{
|
||||
name = $"{name}[{indexExpr}]";
|
||||
name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
|
||||
}
|
||||
|
||||
if (texOp.IsSeparate)
|
||||
{
|
||||
TextureDefinition samplerDefinition = context.Properties.Textures[texOp.GetSamplerSetAndBinding()];
|
||||
string samplerName = samplerDefinition.Name;
|
||||
|
||||
if (samplerDefinition.ArrayLength != 1)
|
||||
{
|
||||
samplerName = $"{samplerName}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
|
||||
}
|
||||
|
||||
name = $"{texOp.Type.ToGlslSamplerType()}({name}, {samplerName})";
|
||||
}
|
||||
|
||||
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.GetTextureSetAndBinding()];
|
||||
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;
|
||||
|
|
|
@ -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)}";
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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}]";
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -33,9 +33,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
public Dictionary<int, Instruction> LocalMemories { get; } = new();
|
||||
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<SetBindingPair, SamplerType> SamplersTypes { get; } = new();
|
||||
public Dictionary<SetBindingPair, SamplerDeclaration> Samplers { get; } = new();
|
||||
public Dictionary<SetBindingPair, ImageDeclaration> Images { get; } = new();
|
||||
|
||||
public Dictionary<IoDefinition, Instruction> Inputs { get; } = new();
|
||||
public Dictionary<IoDefinition, Instruction> Outputs { get; } = new();
|
||||
|
@ -98,11 +98,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
Logger = parameters.Logger;
|
||||
TargetApi = parameters.TargetApi;
|
||||
|
||||
AddCapability(Capability.Shader);
|
||||
AddCapability(Capability.Float64);
|
||||
|
||||
SetMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450);
|
||||
|
||||
Delegates = new SpirvDelegates(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -160,31 +160,61 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
{
|
||||
int setIndex = context.TargetApi == TargetApi.Vulkan ? sampler.Set : 0;
|
||||
|
||||
var dim = (sampler.Type & SamplerType.Mask) switch
|
||||
SpvInstruction imageType;
|
||||
SpvInstruction sampledImageType;
|
||||
|
||||
if (sampler.Type != SamplerType.None)
|
||||
{
|
||||
SamplerType.Texture1D => Dim.Dim1D,
|
||||
SamplerType.Texture2D => Dim.Dim2D,
|
||||
SamplerType.Texture3D => Dim.Dim3D,
|
||||
SamplerType.TextureCube => Dim.Cube,
|
||||
SamplerType.TextureBuffer => Dim.Buffer,
|
||||
_ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\"."),
|
||||
};
|
||||
var dim = (sampler.Type & SamplerType.Mask) switch
|
||||
{
|
||||
SamplerType.Texture1D => Dim.Dim1D,
|
||||
SamplerType.Texture2D => Dim.Dim2D,
|
||||
SamplerType.Texture3D => Dim.Dim3D,
|
||||
SamplerType.TextureCube => Dim.Cube,
|
||||
SamplerType.TextureBuffer => Dim.Buffer,
|
||||
_ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\"."),
|
||||
};
|
||||
|
||||
var imageType = context.TypeImage(
|
||||
context.TypeFP32(),
|
||||
dim,
|
||||
sampler.Type.HasFlag(SamplerType.Shadow),
|
||||
sampler.Type.HasFlag(SamplerType.Array),
|
||||
sampler.Type.HasFlag(SamplerType.Multisample),
|
||||
1,
|
||||
ImageFormat.Unknown);
|
||||
imageType = context.TypeImage(
|
||||
context.TypeFP32(),
|
||||
dim,
|
||||
sampler.Type.HasFlag(SamplerType.Shadow),
|
||||
sampler.Type.HasFlag(SamplerType.Array),
|
||||
sampler.Type.HasFlag(SamplerType.Multisample),
|
||||
1,
|
||||
ImageFormat.Unknown);
|
||||
|
||||
var sampledImageType = context.TypeSampledImage(imageType);
|
||||
var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType);
|
||||
var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant);
|
||||
sampledImageType = context.TypeSampledImage(imageType);
|
||||
}
|
||||
else
|
||||
{
|
||||
imageType = sampledImageType = context.TypeSampler();
|
||||
}
|
||||
|
||||
context.Samplers.Add(sampler.Binding, (imageType, sampledImageType, sampledImageVariable));
|
||||
context.SamplersTypes.Add(sampler.Binding, sampler.Type);
|
||||
var sampledOrSeparateImageType = sampler.Separate ? imageType : sampledImageType;
|
||||
var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledOrSeparateImageType);
|
||||
var sampledImageArrayPointerType = sampledImagePointerType;
|
||||
|
||||
if (sampler.ArrayLength == 0)
|
||||
{
|
||||
var sampledImageArrayType = context.TypeRuntimeArray(sampledOrSeparateImageType);
|
||||
sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
|
||||
}
|
||||
else if (sampler.ArrayLength != 1)
|
||||
{
|
||||
var sampledImageArrayType = context.TypeArray(sampledOrSeparateImageType, context.Constant(context.TypeU32(), sampler.ArrayLength));
|
||||
sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
|
||||
}
|
||||
|
||||
var sampledImageVariable = context.Variable(sampledImageArrayPointerType, StorageClass.UniformConstant);
|
||||
|
||||
context.Samplers.Add(new(sampler.Set, sampler.Binding), new SamplerDeclaration(
|
||||
imageType,
|
||||
sampledImageType,
|
||||
sampledImagePointerType,
|
||||
sampledImageVariable,
|
||||
sampler.ArrayLength != 1));
|
||||
context.SamplersTypes.Add(new(sampler.Set, sampler.Binding), sampler.Type);
|
||||
|
||||
context.Name(sampledImageVariable, sampler.Name);
|
||||
context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
||||
|
@ -211,9 +241,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;
|
||||
|
||||
context.Images.Add(image.Binding, (imageType, imageVariable));
|
||||
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(new(image.Set, image.Binding), new ImageDeclaration(imageType, imagePointerType, imageVariable, image.ArrayLength != 1));
|
||||
|
||||
context.Name(imageVariable, image.Name);
|
||||
context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
||||
|
@ -356,6 +399,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
|
||||
context.AddGlobalVariable(perVertexInputVariable);
|
||||
context.Inputs.Add(new IoDefinition(StorageKind.Input, IoVariable.Position), perVertexInputVariable);
|
||||
|
||||
if (context.Definitions.Stage == ShaderStage.Geometry &&
|
||||
context.Definitions.GpPassthrough &&
|
||||
context.HostCapabilities.SupportsGeometryShaderPassthrough)
|
||||
{
|
||||
context.MemberDecorate(perVertexInputStructType, 0, Decoration.PassthroughNV);
|
||||
context.MemberDecorate(perVertexInputStructType, 1, Decoration.PassthroughNV);
|
||||
context.MemberDecorate(perVertexInputStructType, 2, Decoration.PassthroughNV);
|
||||
context.MemberDecorate(perVertexInputStructType, 3, Decoration.PassthroughNV);
|
||||
}
|
||||
}
|
||||
|
||||
var perVertexOutputStructType = CreatePerVertexStructType(context);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.GetTextureSetAndBinding()];
|
||||
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.GetTextureSetAndBinding()];
|
||||
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.GetTextureSetAndBinding()];
|
||||
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,10 +837,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
return context.Get(type, texOp.GetSource(srcIndex++));
|
||||
}
|
||||
|
||||
if (isIndexed)
|
||||
{
|
||||
Src(AggregateType.S32);
|
||||
}
|
||||
SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()];
|
||||
SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
|
||||
|
||||
int pCount = texOp.Type.GetDimensions();
|
||||
|
||||
|
@ -897,10 +861,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 +1142,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,29 +1151,18 @@ 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)
|
||||
{
|
||||
Src(AggregateType.S32);
|
||||
}
|
||||
SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()];
|
||||
SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
|
||||
|
||||
int coordsCount = texOp.Type.GetDimensions();
|
||||
|
||||
|
@ -1419,15 +1367,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
operandsList.Add(sample);
|
||||
}
|
||||
|
||||
bool colorIsVector = isGather || !isShadow;
|
||||
|
||||
var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32();
|
||||
|
||||
(var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
|
||||
|
||||
var image = context.Load(sampledImageType, sampledImageVariable);
|
||||
|
||||
if (intCoords)
|
||||
{
|
||||
image = context.Image(imageType, image);
|
||||
image = context.Image(declaration.ImageType, image);
|
||||
}
|
||||
|
||||
var operands = operandsList.ToArray();
|
||||
|
@ -1485,25 +1431,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
int srcIndex = 0;
|
||||
|
||||
// TODO: Bindless texture support. For now we just return 0.
|
||||
if (isBindless)
|
||||
{
|
||||
return new OperationResult(AggregateType.S32, context.Constant(context.TypeS32(), 0));
|
||||
}
|
||||
SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()];
|
||||
SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
|
||||
|
||||
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||
|
||||
if (isIndexed)
|
||||
{
|
||||
context.GetS32(texOp.GetSource(0));
|
||||
}
|
||||
|
||||
(var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
|
||||
|
||||
var image = context.Load(sampledImageType, sampledImageVariable);
|
||||
image = context.Image(imageType, image);
|
||||
image = context.Image(declaration.ImageType, image);
|
||||
|
||||
SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image);
|
||||
|
||||
|
@ -1514,25 +1447,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
int srcIndex = 0;
|
||||
|
||||
// TODO: Bindless texture support. For now we just return 0.
|
||||
if (isBindless)
|
||||
{
|
||||
return new OperationResult(AggregateType.S32, context.Constant(context.TypeS32(), 0));
|
||||
}
|
||||
SamplerDeclaration declaration = context.Samplers[texOp.GetTextureSetAndBinding()];
|
||||
SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
|
||||
|
||||
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||
|
||||
if (isIndexed)
|
||||
{
|
||||
context.GetS32(texOp.GetSource(0));
|
||||
}
|
||||
|
||||
(var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
|
||||
|
||||
var image = context.Load(sampledImageType, sampledImageVariable);
|
||||
image = context.Image(imageType, image);
|
||||
image = context.Image(declaration.ImageType, image);
|
||||
|
||||
if (texOp.Index == 3)
|
||||
{
|
||||
|
@ -1540,7 +1460,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
}
|
||||
else
|
||||
{
|
||||
var type = context.SamplersTypes[texOp.Binding];
|
||||
var type = context.SamplersTypes[texOp.GetTextureSetAndBinding()];
|
||||
bool hasLod = !type.HasFlag(SamplerType.Multisample) && type != SamplerType.TextureBuffer;
|
||||
|
||||
int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.GetDimensions();
|
||||
|
@ -1556,8 +1476,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
|
||||
if (hasLod)
|
||||
{
|
||||
int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
|
||||
var lod = context.GetS32(operation.GetSource(lodSrcIndex));
|
||||
var lod = context.GetS32(operation.GetSource(srcIndex));
|
||||
result = context.ImageQuerySizeLod(resultType, image, lod);
|
||||
}
|
||||
else
|
||||
|
@ -1929,38 +1848,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)
|
||||
|
@ -1987,6 +1874,43 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
}
|
||||
}
|
||||
|
||||
private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex)
|
||||
{
|
||||
SpvInstruction image = declaration.Image;
|
||||
|
||||
if (declaration.IsIndexed)
|
||||
{
|
||||
SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
|
||||
|
||||
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
|
||||
}
|
||||
|
||||
if (texOp.IsSeparate)
|
||||
{
|
||||
image = context.Load(declaration.ImageType, image);
|
||||
|
||||
SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()];
|
||||
|
||||
SpvInstruction sampler = samplerDeclaration.Image;
|
||||
|
||||
if (samplerDeclaration.IsIndexed)
|
||||
{
|
||||
SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
|
||||
|
||||
sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex);
|
||||
}
|
||||
|
||||
sampler = context.Load(samplerDeclaration.ImageType, sampler);
|
||||
image = context.SampledImage(declaration.SampledImageType, image, sampler);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = context.Load(declaration.SampledImageType, image);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static OperationResult GenerateUnary(
|
||||
CodeGenContext context,
|
||||
AstOperation operation,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,6 +43,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
|
||||
CodeGenContext context = new(info, parameters, instPool, integerPool);
|
||||
|
||||
context.AddCapability(Capability.Shader);
|
||||
|
||||
context.SetMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450);
|
||||
|
||||
context.AddCapability(Capability.GroupNonUniformBallot);
|
||||
context.AddCapability(Capability.GroupNonUniformShuffle);
|
||||
context.AddCapability(Capability.GroupNonUniformVote);
|
||||
|
@ -51,6 +55,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
context.AddCapability(Capability.ImageQuery);
|
||||
context.AddCapability(Capability.SampledBuffer);
|
||||
|
||||
if (parameters.HostCapabilities.SupportsShaderFloat64)
|
||||
{
|
||||
context.AddCapability(Capability.Float64);
|
||||
}
|
||||
|
||||
if (parameters.Definitions.TransformFeedbackEnabled && parameters.Definitions.LastInVertexPipeline)
|
||||
{
|
||||
context.AddCapability(Capability.TransformFeedback);
|
||||
|
@ -58,7 +67,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
|
||||
if (parameters.Definitions.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer)))
|
||||
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer)) ||
|
||||
context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.PrimitiveId)))
|
||||
{
|
||||
context.AddCapability(Capability.Geometry);
|
||||
}
|
||||
|
|
|
@ -102,6 +102,11 @@ namespace Ryujinx.Graphics.Shader
|
|||
/// </summary>
|
||||
public readonly bool OriginUpperLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion.
|
||||
/// </summary>
|
||||
public readonly bool HalvePrimitiveId;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU graphics state.
|
||||
/// </summary>
|
||||
|
@ -124,6 +129,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
/// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param>
|
||||
/// <param name="yNegateEnabled">Indicates if negation of the viewport Y axis is enabled</param>
|
||||
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
|
||||
/// <param name="halvePrimitiveId">Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion</param>
|
||||
public GpuGraphicsState(
|
||||
bool earlyZForce,
|
||||
InputTopology topology,
|
||||
|
@ -143,7 +149,8 @@ namespace Ryujinx.Graphics.Shader
|
|||
in Array8<AttributeType> fragmentOutputTypes,
|
||||
bool dualSourceBlendEnable,
|
||||
bool yNegateEnabled,
|
||||
bool originUpperLeft)
|
||||
bool originUpperLeft,
|
||||
bool halvePrimitiveId)
|
||||
{
|
||||
EarlyZForce = earlyZForce;
|
||||
Topology = topology;
|
||||
|
@ -164,6 +171,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
DualSourceBlendEnable = dualSourceBlendEnable;
|
||||
YNegateEnabled = yNegateEnabled;
|
||||
OriginUpperLeft = originUpperLeft;
|
||||
HalvePrimitiveId = halvePrimitiveId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,45 +27,42 @@ namespace Ryujinx.Graphics.Shader
|
|||
ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize);
|
||||
|
||||
/// <summary>
|
||||
/// Queries the binding number of a constant buffer.
|
||||
/// Gets 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;
|
||||
}
|
||||
SetBindingPair CreateConstantBufferBinding(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Queries the binding number of a storage buffer.
|
||||
/// Gets the binding number of an image.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
SetBindingPair CreateImageBinding(int count, bool isBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the binding number of a storage buffer.
|
||||
/// </summary>
|
||||
/// <param name="index">Storage buffer index</param>
|
||||
/// <returns>Binding number</returns>
|
||||
int QueryBindingStorageBuffer(int index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
SetBindingPair CreateStorageBufferBinding(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Queries the binding number of a texture.
|
||||
/// Gets the binding number of a texture.
|
||||
/// </summary>
|
||||
/// <param name="index">Texture index</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 QueryBindingTexture(int index, bool isBuffer)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
SetBindingPair CreateTextureBinding(int count, bool isBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Queries the binding number of an image.
|
||||
/// Gets the set index for an additional set, or -1 if there's no extra set available.
|
||||
/// </summary>
|
||||
/// <param name="index">Image index</param>
|
||||
/// <param name="isBuffer">Indicates if the image is a buffer image</param>
|
||||
/// <returns>Binding number</returns>
|
||||
int QueryBindingImage(int index, bool isBuffer)
|
||||
/// <returns>Extra set index, or -1 if not available</returns>
|
||||
int CreateExtraSet()
|
||||
{
|
||||
return index;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -147,6 +144,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
default,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
|
@ -303,6 +301,15 @@ namespace Ryujinx.Graphics.Shader
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host API support for separate textures and samplers.
|
||||
/// </summary>
|
||||
/// <returns>True if the API supports samplers and textures to be combined on the shader, false otherwise</returns>
|
||||
bool QueryHostSupportsSeparateSampler()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host GPU shader ballot support.
|
||||
/// </summary>
|
||||
|
@ -393,6 +400,12 @@ namespace Ryujinx.Graphics.Shader
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number of samplers that the bound texture pool may have.
|
||||
/// </summary>
|
||||
/// <returns>Maximum amount of samplers that the pool may have</returns>
|
||||
int QuerySamplerArrayLengthFromPool();
|
||||
|
||||
/// <summary>
|
||||
/// Queries sampler type information.
|
||||
/// </summary>
|
||||
|
@ -404,6 +417,19 @@ namespace Ryujinx.Graphics.Shader
|
|||
return SamplerType.Texture2D;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Gets the maximum number of textures that the bound texture pool may have.
|
||||
/// </summary>
|
||||
/// <returns>Maximum amount of textures that the pool may have</returns>
|
||||
int QueryTextureArrayLengthFromPool();
|
||||
|
||||
/// <summary>
|
||||
/// Queries texture coordinate normalization information.
|
||||
/// </summary>
|
||||
|
|
|
@ -84,6 +84,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
value = context.IConvertU32ToFP32(value);
|
||||
}
|
||||
}
|
||||
else if (offset == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
|
||||
{
|
||||
value = context.ShiftRightS32(value, Const(1));
|
||||
}
|
||||
|
||||
context.Copy(Register(rd), value);
|
||||
}
|
||||
|
@ -187,6 +191,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (op.Imm10 == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
|
||||
{
|
||||
// If quads are used, but the host does not support them, they need to be converted to triangles.
|
||||
// Since each quad becomes 2 triangles, we need to compensate here and divide primitive ID by 2.
|
||||
res = context.ShiftRightS32(res, Const(1));
|
||||
}
|
||||
else if (op.Imm10 == AttributeConsts.FrontFacing && context.TranslatorContext.GpuAccessor.QueryHostHasFrontFacingBug())
|
||||
{
|
||||
// gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs.
|
||||
|
|
|
@ -222,30 +222,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||
}
|
||||
break;
|
||||
case AtomOp.And:
|
||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||
case AtomOp.Min:
|
||||
if (type == AtomSize.S32)
|
||||
{
|
||||
res = context.AtomicAnd(storageKind, e0, e1, value);
|
||||
res = context.AtomicMinS32(storageKind, e0, e1, value);
|
||||
}
|
||||
else
|
||||
else if (type == AtomSize.U32)
|
||||
{
|
||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||
}
|
||||
break;
|
||||
case AtomOp.Xor:
|
||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||
{
|
||||
res = context.AtomicXor(storageKind, e0, e1, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||
}
|
||||
break;
|
||||
case AtomOp.Or:
|
||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||
{
|
||||
res = context.AtomicOr(storageKind, e0, e1, value);
|
||||
res = context.AtomicMinU32(storageKind, e0, e1, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -266,20 +250,49 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||
}
|
||||
break;
|
||||
case AtomOp.Min:
|
||||
if (type == AtomSize.S32)
|
||||
case AtomOp.And:
|
||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||
{
|
||||
res = context.AtomicMinS32(storageKind, e0, e1, value);
|
||||
}
|
||||
else if (type == AtomSize.U32)
|
||||
{
|
||||
res = context.AtomicMinU32(storageKind, e0, e1, value);
|
||||
res = context.AtomicAnd(storageKind, e0, e1, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||
}
|
||||
break;
|
||||
case AtomOp.Or:
|
||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||
{
|
||||
res = context.AtomicOr(storageKind, e0, e1, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||
}
|
||||
break;
|
||||
case AtomOp.Xor:
|
||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||
{
|
||||
res = context.AtomicXor(storageKind, e0, e1, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||
}
|
||||
break;
|
||||
case AtomOp.Exch:
|
||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||
{
|
||||
res = context.AtomicSwap(storageKind, e0, e1, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
context.TranslatorContext.GpuAccessor.Log($"Invalid atomic operation: {op}.");
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
if (op.BVal)
|
||||
{
|
||||
context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
|
||||
context.Copy(dest, context.ConditionalSelect(res, ConstF(1), ConstF(0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -278,7 +278,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
flags |= TextureFlags.Bindless;
|
||||
}
|
||||
|
||||
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.ImageAtomic,
|
||||
type,
|
||||
format,
|
||||
|
@ -286,7 +286,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
TextureOperation.DefaultCbufSlot,
|
||||
imm);
|
||||
|
||||
Operand res = context.ImageAtomic(type, format, flags, binding, sources);
|
||||
Operand res = context.ImageAtomic(type, format, flags, setAndBinding, sources);
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
@ -389,7 +389,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
TextureFormat format = isBindless ? TextureFormat.Unknown : ShaderProperties.GetTextureFormat(context.TranslatorContext.GpuAccessor, handle);
|
||||
|
||||
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.ImageLoad,
|
||||
type,
|
||||
format,
|
||||
|
@ -397,7 +397,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
TextureOperation.DefaultCbufSlot,
|
||||
handle);
|
||||
|
||||
context.ImageLoad(type, format, flags, binding, (int)componentMask, dests, sources);
|
||||
context.ImageLoad(type, format, flags, setAndBinding, (int)componentMask, dests, sources);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -432,7 +432,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
TextureFormat format = GetTextureFormat(size);
|
||||
|
||||
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.ImageLoad,
|
||||
type,
|
||||
format,
|
||||
|
@ -440,7 +440,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
TextureOperation.DefaultCbufSlot,
|
||||
handle);
|
||||
|
||||
context.ImageLoad(type, format, flags, binding, compMask, dests, sources);
|
||||
context.ImageLoad(type, format, flags, setAndBinding, compMask, dests, sources);
|
||||
|
||||
switch (size)
|
||||
{
|
||||
|
@ -552,7 +552,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
flags |= TextureFlags.Bindless;
|
||||
}
|
||||
|
||||
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.ImageAtomic,
|
||||
type,
|
||||
format,
|
||||
|
@ -560,7 +560,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
TextureOperation.DefaultCbufSlot,
|
||||
imm);
|
||||
|
||||
context.ImageAtomic(type, format, flags, binding, sources);
|
||||
context.ImageAtomic(type, format, flags, setAndBinding, sources);
|
||||
}
|
||||
|
||||
private static void EmitSust(
|
||||
|
@ -679,7 +679,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
flags |= TextureFlags.Coherent;
|
||||
}
|
||||
|
||||
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.ImageStore,
|
||||
type,
|
||||
format,
|
||||
|
@ -687,7 +687,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
TextureOperation.DefaultCbufSlot,
|
||||
handle);
|
||||
|
||||
context.ImageStore(type, format, flags, binding, sources);
|
||||
context.ImageStore(type, format, flags, setAndBinding, sources);
|
||||
}
|
||||
|
||||
private static int GetComponentSizeInBytesLog2(SuatomSize size)
|
||||
|
|
|
@ -578,12 +578,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
type = SamplerType.Texture2D;
|
||||
flags = TextureFlags.Gather;
|
||||
|
||||
if (tld4sOp.Dc)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
|
||||
type |= SamplerType.Shadow;
|
||||
}
|
||||
int depthCompareIndex = sourcesList.Count;
|
||||
|
||||
if (tld4sOp.Aoffi)
|
||||
{
|
||||
|
@ -592,7 +587,16 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
flags |= TextureFlags.Offset;
|
||||
}
|
||||
|
||||
sourcesList.Add(Const((int)tld4sOp.TexComp));
|
||||
if (tld4sOp.Dc)
|
||||
{
|
||||
sourcesList.Insert(depthCompareIndex, Rb());
|
||||
|
||||
type |= SamplerType.Shadow;
|
||||
}
|
||||
else
|
||||
{
|
||||
sourcesList.Add(Const((int)tld4sOp.TexComp));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -881,7 +885,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
return Register(dest++, RegisterType.Gpr);
|
||||
}
|
||||
|
||||
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
SetBindingPair setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.Lod,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
|
@ -909,7 +913,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
else
|
||||
{
|
||||
// The instruction component order is the inverse of GLSL's.
|
||||
Operand res = context.Lod(type, flags, binding, compIndex ^ 1, sources);
|
||||
Operand res = context.Lod(type, flags, setAndBinding, compIndex ^ 1, sources);
|
||||
|
||||
res = context.FPMultiply(res, ConstF(256.0f));
|
||||
|
||||
|
@ -1112,12 +1116,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
}
|
||||
|
||||
TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
|
||||
int binding;
|
||||
SetBindingPair setAndBinding;
|
||||
|
||||
switch (query)
|
||||
{
|
||||
case TexQuery.TexHeaderDimension:
|
||||
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.TextureQuerySize,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
|
@ -1136,13 +1140,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
break;
|
||||
}
|
||||
|
||||
context.Copy(d, context.TextureQuerySize(type, flags, binding, compIndex, sources));
|
||||
context.Copy(d, context.TextureQuerySize(type, flags, setAndBinding, compIndex, sources));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TexQuery.TexHeaderTextureType:
|
||||
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
setAndBinding = isBindless ? default : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.TextureQuerySamples,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
|
@ -1167,7 +1171,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
if (d != null)
|
||||
{
|
||||
context.Copy(d, context.TextureQuerySamples(type, flags, binding, sources));
|
||||
context.Copy(d, context.TextureQuerySamples(type, flags, setAndBinding, sources));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1187,7 +1191,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
Operand[] dests,
|
||||
Operand[] sources)
|
||||
{
|
||||
int binding = flags.HasFlag(TextureFlags.Bindless) ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||
SetBindingPair setAndBinding = flags.HasFlag(TextureFlags.Bindless) ? default : context.ResourceManager.GetTextureOrImageBinding(
|
||||
Instruction.TextureSample,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
|
@ -1195,7 +1199,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
TextureOperation.DefaultCbufSlot,
|
||||
handle);
|
||||
|
||||
context.TextureSample(type, flags, binding, componentMask, dests, sources);
|
||||
context.TextureSample(type, flags, setAndBinding, componentMask, dests, sources);
|
||||
}
|
||||
|
||||
private static SamplerType ConvertSamplerType(TexDim dimensions)
|
||||
|
|
|
@ -156,10 +156,42 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
return false;
|
||||
}
|
||||
|
||||
public static bool IsComparison(this Instruction inst)
|
||||
{
|
||||
switch (inst & Instruction.Mask)
|
||||
{
|
||||
case Instruction.CompareEqual:
|
||||
case Instruction.CompareGreater:
|
||||
case Instruction.CompareGreaterOrEqual:
|
||||
case Instruction.CompareGreaterOrEqualU32:
|
||||
case Instruction.CompareGreaterU32:
|
||||
case Instruction.CompareLess:
|
||||
case Instruction.CompareLessOrEqual:
|
||||
case Instruction.CompareLessOrEqualU32:
|
||||
case Instruction.CompareLessU32:
|
||||
case Instruction.CompareNotEqual:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsTextureQuery(this Instruction inst)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -216,6 +216,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
|
||||
newSources[index] = source;
|
||||
|
||||
if (source != null && source.Type == OperandType.LocalVariable)
|
||||
{
|
||||
source.UseOps.Add(this);
|
||||
}
|
||||
|
||||
_sources = newSources;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,17 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
public TextureFormat Format { get; set; }
|
||||
public TextureFlags Flags { get; private set; }
|
||||
|
||||
public int Set { get; private set; }
|
||||
public int Binding { get; private set; }
|
||||
public int SamplerSet { get; private set; }
|
||||
public int SamplerBinding { get; private set; }
|
||||
|
||||
public TextureOperation(
|
||||
Instruction inst,
|
||||
SamplerType type,
|
||||
TextureFormat format,
|
||||
TextureFlags flags,
|
||||
int set,
|
||||
int binding,
|
||||
int compIndex,
|
||||
Operand[] dests,
|
||||
|
@ -23,17 +27,28 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
Type = type;
|
||||
Format = format;
|
||||
Flags = flags;
|
||||
Set = set;
|
||||
Binding = binding;
|
||||
SamplerSet = -1;
|
||||
SamplerBinding = -1;
|
||||
}
|
||||
|
||||
public void TurnIntoIndexed(int binding)
|
||||
public void TurnIntoArray(SetBindingPair setAndBinding)
|
||||
{
|
||||
Type |= SamplerType.Indexed;
|
||||
Flags &= ~TextureFlags.Bindless;
|
||||
Binding = binding;
|
||||
Set = setAndBinding.SetIndex;
|
||||
Binding = setAndBinding.Binding;
|
||||
}
|
||||
|
||||
public void SetBinding(int binding)
|
||||
public void TurnIntoArray(SetBindingPair textureSetAndBinding, SetBindingPair samplerSetAndBinding)
|
||||
{
|
||||
TurnIntoArray(textureSetAndBinding);
|
||||
|
||||
SamplerSet = samplerSetAndBinding.SetIndex;
|
||||
SamplerBinding = samplerSetAndBinding.Binding;
|
||||
}
|
||||
|
||||
public void SetBinding(SetBindingPair setAndBinding)
|
||||
{
|
||||
if ((Flags & TextureFlags.Bindless) != 0)
|
||||
{
|
||||
|
@ -42,7 +57,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
RemoveSource(0);
|
||||
}
|
||||
|
||||
Binding = binding;
|
||||
Set = setAndBinding.SetIndex;
|
||||
Binding = setAndBinding.Binding;
|
||||
}
|
||||
|
||||
public void SetLodLevelFlag()
|
||||
|
|
|
@ -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,10 +35,41 @@ 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
|
||||
{
|
||||
SamplerType.None => "sampler",
|
||||
SamplerType.Texture1D => "sampler1D",
|
||||
SamplerType.TextureBuffer => "samplerBuffer",
|
||||
SamplerType.Texture2D => "sampler2D",
|
||||
|
@ -66,6 +96,31 @@ namespace Ryujinx.Graphics.Shader
|
|||
return typeName;
|
||||
}
|
||||
|
||||
public static string ToGlslTextureType(this SamplerType type)
|
||||
{
|
||||
string typeName = (type & SamplerType.Mask) switch
|
||||
{
|
||||
SamplerType.Texture1D => "texture1D",
|
||||
SamplerType.TextureBuffer => "textureBuffer",
|
||||
SamplerType.Texture2D => "texture2D",
|
||||
SamplerType.Texture3D => "texture3D",
|
||||
SamplerType.TextureCube => "textureCube",
|
||||
_ => throw new ArgumentException($"Invalid texture type \"{type}\"."),
|
||||
};
|
||||
|
||||
if ((type & SamplerType.Multisample) != 0)
|
||||
{
|
||||
typeName += "MS";
|
||||
}
|
||||
|
||||
if ((type & SamplerType.Array) != 0)
|
||||
{
|
||||
typeName += "Array";
|
||||
}
|
||||
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public static string ToGlslImageType(this SamplerType type, AggregateType componentType)
|
||||
{
|
||||
string typeName = (type & SamplerType.Mask) switch
|
||||
|
|
4
src/Ryujinx.Graphics.Shader/SetBindingPair.cs
Normal file
4
src/Ryujinx.Graphics.Shader/SetBindingPair.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
public readonly record struct SetBindingPair(int SetIndex, int Binding);
|
||||
}
|
|
@ -8,21 +8,42 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
public TextureFormat Format { get; }
|
||||
public TextureFlags Flags { get; }
|
||||
|
||||
public int Set { get; }
|
||||
public int Binding { get; }
|
||||
public int SamplerSet { get; }
|
||||
public int SamplerBinding { get; }
|
||||
|
||||
public bool IsSeparate => SamplerBinding >= 0;
|
||||
|
||||
public AstTextureOperation(
|
||||
Instruction inst,
|
||||
SamplerType type,
|
||||
TextureFormat format,
|
||||
TextureFlags flags,
|
||||
int set,
|
||||
int binding,
|
||||
int samplerSet,
|
||||
int samplerBinding,
|
||||
int index,
|
||||
params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length)
|
||||
{
|
||||
Type = type;
|
||||
Format = format;
|
||||
Flags = flags;
|
||||
Set = set;
|
||||
Binding = binding;
|
||||
SamplerSet = samplerSet;
|
||||
SamplerBinding = samplerBinding;
|
||||
}
|
||||
|
||||
public SetBindingPair GetTextureSetAndBinding()
|
||||
{
|
||||
return new SetBindingPair(Set, Binding);
|
||||
}
|
||||
|
||||
public SetBindingPair GetSamplerSetAndBinding()
|
||||
{
|
||||
return new SetBindingPair(SamplerSet, SamplerBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,17 +24,21 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
continue;
|
||||
}
|
||||
|
||||
Operand temp = OperandHelper.Local();
|
||||
|
||||
for (int index = 0; index < phi.SourcesCount; index++)
|
||||
{
|
||||
Operand src = phi.GetSource(index);
|
||||
|
||||
BasicBlock srcBlock = phi.GetBlock(index);
|
||||
|
||||
Operation copyOp = new(Instruction.Copy, phi.Dest, src);
|
||||
Operation copyOp = new(Instruction.Copy, temp, src);
|
||||
|
||||
srcBlock.Append(copyOp);
|
||||
}
|
||||
|
||||
Operation copyOp2 = new(Instruction.Copy, phi.Dest, temp);
|
||||
|
||||
nextNode = block.Operations.AddAfter(node, copyOp2).Next;
|
||||
block.Operations.Remove(node);
|
||||
|
||||
node = nextNode;
|
||||
|
|
|
@ -6,15 +6,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
{
|
||||
private readonly Dictionary<int, BufferDefinition> _constantBuffers;
|
||||
private readonly Dictionary<int, BufferDefinition> _storageBuffers;
|
||||
private readonly Dictionary<int, TextureDefinition> _textures;
|
||||
private readonly Dictionary<int, TextureDefinition> _images;
|
||||
private readonly Dictionary<SetBindingPair, TextureDefinition> _textures;
|
||||
private readonly Dictionary<SetBindingPair, TextureDefinition> _images;
|
||||
private readonly Dictionary<int, MemoryDefinition> _localMemories;
|
||||
private readonly Dictionary<int, MemoryDefinition> _sharedMemories;
|
||||
|
||||
public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
|
||||
public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers;
|
||||
public IReadOnlyDictionary<int, TextureDefinition> Textures => _textures;
|
||||
public IReadOnlyDictionary<int, TextureDefinition> Images => _images;
|
||||
public IReadOnlyDictionary<SetBindingPair, TextureDefinition> Textures => _textures;
|
||||
public IReadOnlyDictionary<SetBindingPair, TextureDefinition> Images => _images;
|
||||
public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories;
|
||||
public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories;
|
||||
|
||||
|
@ -22,8 +22,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
{
|
||||
_constantBuffers = new Dictionary<int, BufferDefinition>();
|
||||
_storageBuffers = new Dictionary<int, BufferDefinition>();
|
||||
_textures = new Dictionary<int, TextureDefinition>();
|
||||
_images = new Dictionary<int, TextureDefinition>();
|
||||
_textures = new Dictionary<SetBindingPair, TextureDefinition>();
|
||||
_images = new Dictionary<SetBindingPair, TextureDefinition>();
|
||||
_localMemories = new Dictionary<int, MemoryDefinition>();
|
||||
_sharedMemories = new Dictionary<int, MemoryDefinition>();
|
||||
}
|
||||
|
@ -40,12 +40,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
public void AddOrUpdateTexture(TextureDefinition definition)
|
||||
{
|
||||
_textures[definition.Binding] = definition;
|
||||
_textures[new(definition.Set, definition.Binding)] = definition;
|
||||
}
|
||||
|
||||
public void AddOrUpdateImage(TextureDefinition definition)
|
||||
{
|
||||
_images[definition.Binding] = definition;
|
||||
_images[new(definition.Set, definition.Binding)] = definition;
|
||||
}
|
||||
|
||||
public int AddLocalMemory(MemoryDefinition definition)
|
||||
|
|
|
@ -169,7 +169,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
AstTextureOperation GetAstTextureOperation(TextureOperation texOp)
|
||||
{
|
||||
return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.Index, sources);
|
||||
return new AstTextureOperation(
|
||||
inst,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Set,
|
||||
texOp.Binding,
|
||||
texOp.SamplerSet,
|
||||
texOp.SamplerBinding,
|
||||
texOp.Index,
|
||||
sources);
|
||||
}
|
||||
|
||||
int componentsCount = BitOperations.PopCount((uint)operation.Index);
|
||||
|
|
|
@ -4,24 +4,40 @@ namespace Ryujinx.Graphics.Shader
|
|||
{
|
||||
public int Set { get; }
|
||||
public int Binding { get; }
|
||||
public int ArrayLength { get; }
|
||||
public bool Separate { 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,
|
||||
bool separate,
|
||||
string name,
|
||||
SamplerType type,
|
||||
TextureFormat format,
|
||||
TextureUsageFlags flags)
|
||||
{
|
||||
Set = set;
|
||||
Binding = binding;
|
||||
ArrayLength = arrayLength;
|
||||
Separate = separate;
|
||||
Name = name;
|
||||
Type = type;
|
||||
Format = format;
|
||||
Flags = flags;
|
||||
}
|
||||
|
||||
public TextureDefinition(int set, int binding, string name, SamplerType type) : this(set, binding, 1, false, name, type, TextureFormat.Unknown, TextureUsageFlags.None)
|
||||
{
|
||||
}
|
||||
|
||||
public TextureDefinition SetFlag(TextureUsageFlags flag)
|
||||
{
|
||||
return new TextureDefinition(Set, Binding, Name, Type, Format, Flags | flag);
|
||||
return new TextureDefinition(Set, Binding, ArrayLength, Separate, Name, Type, Format, Flags | flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
{
|
||||
// New fields should be added to the end of the struct to keep disk shader cache compatibility.
|
||||
|
||||
public readonly int Set;
|
||||
public readonly int Binding;
|
||||
|
||||
public readonly SamplerType Type;
|
||||
|
@ -11,16 +12,31 @@ namespace Ryujinx.Graphics.Shader
|
|||
|
||||
public readonly int CbufSlot;
|
||||
public readonly int HandleIndex;
|
||||
public readonly int ArrayLength;
|
||||
|
||||
public readonly bool Separate;
|
||||
|
||||
public readonly TextureUsageFlags Flags;
|
||||
|
||||
public TextureDescriptor(int binding, SamplerType type, TextureFormat format, int cbufSlot, int handleIndex, TextureUsageFlags flags)
|
||||
public TextureDescriptor(
|
||||
int set,
|
||||
int binding,
|
||||
SamplerType type,
|
||||
TextureFormat format,
|
||||
int cbufSlot,
|
||||
int handleIndex,
|
||||
int arrayLength,
|
||||
bool separate,
|
||||
TextureUsageFlags flags)
|
||||
{
|
||||
Set = set;
|
||||
Binding = binding;
|
||||
Type = type;
|
||||
Format = format;
|
||||
CbufSlot = cbufSlot;
|
||||
HandleIndex = handleIndex;
|
||||
ArrayLength = arrayLength;
|
||||
Separate = separate;
|
||||
Flags = flags;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
SeparateSamplerHandle = 1,
|
||||
SeparateSamplerId = 2,
|
||||
SeparateConstantSamplerHandle = 3,
|
||||
Direct = 4,
|
||||
}
|
||||
|
||||
public static class TextureHandle
|
||||
|
@ -88,7 +89,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 +103,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
|
||||
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
|
||||
{
|
||||
samplerHandle = cachedSamplerBuffer.Length != 0 ? cachedSamplerBuffer[samplerWordOffset] : 0;
|
||||
samplerHandle = samplerWordOffset < cachedSamplerBuffer.Length ? cachedSamplerBuffer[samplerWordOffset] : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -80,9 +80,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return;
|
||||
}
|
||||
|
||||
if (TranslatorContext.Definitions.Stage == ShaderStage.Vertex && TranslatorContext.Options.TargetApi == TargetApi.Vulkan)
|
||||
// Vulkan requires the point size to be always written on the shader if the primitive topology is points.
|
||||
// OpenGL requires the point size to be always written on the shader if PROGRAM_POINT_SIZE is set.
|
||||
if (TranslatorContext.Definitions.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
// Vulkan requires the point size to be always written on the shader if the primitive topology is points.
|
||||
this.Store(StorageKind.Output, IoVariable.PointSize, null, ConstF(TranslatorContext.Definitions.PointSize));
|
||||
}
|
||||
|
||||
|
@ -123,7 +124,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
this.TextureSample(
|
||||
SamplerType.TextureBuffer,
|
||||
TextureFlags.IntCoords,
|
||||
ResourceManager.Reservations.IndexBufferTextureBinding,
|
||||
ResourceManager.Reservations.GetIndexBufferTextureSetAndBinding(),
|
||||
1,
|
||||
new[] { vertexIndexVr },
|
||||
new[] { this.IAdd(ibBaseOffset, outputVertexOffset) });
|
||||
|
@ -144,7 +145,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
this.TextureSample(
|
||||
SamplerType.TextureBuffer,
|
||||
TextureFlags.IntCoords,
|
||||
ResourceManager.Reservations.TopologyRemapBufferTextureBinding,
|
||||
ResourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(),
|
||||
1,
|
||||
new[] { vertexIndex },
|
||||
new[] { this.IAdd(baseVertex, Const(index)) });
|
||||
|
|
|
@ -618,12 +618,21 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
SamplerType type,
|
||||
TextureFormat format,
|
||||
TextureFlags flags,
|
||||
int binding,
|
||||
SetBindingPair setAndBinding,
|
||||
Operand[] sources)
|
||||
{
|
||||
Operand dest = Local();
|
||||
|
||||
context.Add(new TextureOperation(Instruction.ImageAtomic, type, format, flags, binding, 0, new[] { dest }, sources));
|
||||
context.Add(new TextureOperation(
|
||||
Instruction.ImageAtomic,
|
||||
type,
|
||||
format,
|
||||
flags,
|
||||
setAndBinding.SetIndex,
|
||||
setAndBinding.Binding,
|
||||
0,
|
||||
new[] { dest },
|
||||
sources));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
@ -633,12 +642,21 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
SamplerType type,
|
||||
TextureFormat format,
|
||||
TextureFlags flags,
|
||||
int binding,
|
||||
SetBindingPair setAndBinding,
|
||||
int compMask,
|
||||
Operand[] dests,
|
||||
Operand[] sources)
|
||||
{
|
||||
context.Add(new TextureOperation(Instruction.ImageLoad, type, format, flags, binding, compMask, dests, sources));
|
||||
context.Add(new TextureOperation(
|
||||
Instruction.ImageLoad,
|
||||
type,
|
||||
format,
|
||||
flags,
|
||||
setAndBinding.SetIndex,
|
||||
setAndBinding.Binding,
|
||||
compMask,
|
||||
dests,
|
||||
sources));
|
||||
}
|
||||
|
||||
public static void ImageStore(
|
||||
|
@ -646,10 +664,19 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
SamplerType type,
|
||||
TextureFormat format,
|
||||
TextureFlags flags,
|
||||
int binding,
|
||||
SetBindingPair setAndBinding,
|
||||
Operand[] sources)
|
||||
{
|
||||
context.Add(new TextureOperation(Instruction.ImageStore, type, format, flags, binding, 0, null, sources));
|
||||
context.Add(new TextureOperation(
|
||||
Instruction.ImageStore,
|
||||
type,
|
||||
format,
|
||||
flags,
|
||||
setAndBinding.SetIndex,
|
||||
setAndBinding.Binding,
|
||||
0,
|
||||
null,
|
||||
sources));
|
||||
}
|
||||
|
||||
public static Operand IsNan(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32)
|
||||
|
@ -718,13 +745,22 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
this EmitterContext context,
|
||||
SamplerType type,
|
||||
TextureFlags flags,
|
||||
int binding,
|
||||
SetBindingPair setAndBinding,
|
||||
int compIndex,
|
||||
Operand[] sources)
|
||||
{
|
||||
Operand dest = Local();
|
||||
|
||||
context.Add(new TextureOperation(Instruction.Lod, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
|
||||
context.Add(new TextureOperation(
|
||||
Instruction.Lod,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
flags,
|
||||
setAndBinding.SetIndex,
|
||||
setAndBinding.Binding,
|
||||
compIndex,
|
||||
new[] { dest },
|
||||
sources));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
@ -889,24 +925,42 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
this EmitterContext context,
|
||||
SamplerType type,
|
||||
TextureFlags flags,
|
||||
int binding,
|
||||
SetBindingPair setAndBinding,
|
||||
int compMask,
|
||||
Operand[] dests,
|
||||
Operand[] sources)
|
||||
{
|
||||
context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources));
|
||||
context.Add(new TextureOperation(
|
||||
Instruction.TextureSample,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
flags,
|
||||
setAndBinding.SetIndex,
|
||||
setAndBinding.Binding,
|
||||
compMask,
|
||||
dests,
|
||||
sources));
|
||||
}
|
||||
|
||||
public static Operand TextureQuerySamples(
|
||||
this EmitterContext context,
|
||||
SamplerType type,
|
||||
TextureFlags flags,
|
||||
int binding,
|
||||
SetBindingPair setAndBinding,
|
||||
Operand[] sources)
|
||||
{
|
||||
Operand dest = Local();
|
||||
|
||||
context.Add(new TextureOperation(Instruction.TextureQuerySamples, type, TextureFormat.Unknown, flags, binding, 0, new[] { dest }, sources));
|
||||
context.Add(new TextureOperation(
|
||||
Instruction.TextureQuerySamples,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
flags,
|
||||
setAndBinding.SetIndex,
|
||||
setAndBinding.Binding,
|
||||
0,
|
||||
new[] { dest },
|
||||
sources));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
@ -915,13 +969,22 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
this EmitterContext context,
|
||||
SamplerType type,
|
||||
TextureFlags flags,
|
||||
int binding,
|
||||
SetBindingPair setAndBinding,
|
||||
int compIndex,
|
||||
Operand[] sources)
|
||||
{
|
||||
Operand dest = Local();
|
||||
|
||||
context.Add(new TextureOperation(Instruction.TextureQuerySize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
|
||||
context.Add(new TextureOperation(
|
||||
Instruction.TextureQuerySize,
|
||||
type,
|
||||
TextureFormat.Unknown,
|
||||
flags,
|
||||
setAndBinding.SetIndex,
|
||||
setAndBinding.Binding,
|
||||
compIndex,
|
||||
new[] { dest },
|
||||
sources));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
public readonly bool SupportsGeometryShaderPassthrough;
|
||||
public readonly bool SupportsShaderBallot;
|
||||
public readonly bool SupportsShaderBarrierDivergence;
|
||||
public readonly bool SupportsShaderFloat64;
|
||||
public readonly bool SupportsTextureShadowLod;
|
||||
public readonly bool SupportsViewportMask;
|
||||
|
||||
|
@ -18,6 +19,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
bool supportsGeometryShaderPassthrough,
|
||||
bool supportsShaderBallot,
|
||||
bool supportsShaderBarrierDivergence,
|
||||
bool supportsShaderFloat64,
|
||||
bool supportsTextureShadowLod,
|
||||
bool supportsViewportMask)
|
||||
{
|
||||
|
@ -27,6 +29,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
||||
SupportsShaderBallot = supportsShaderBallot;
|
||||
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
||||
SupportsShaderFloat64 = supportsShaderFloat64;
|
||||
SupportsTextureShadowLod = supportsTextureShadowLod;
|
||||
SupportsViewportMask = supportsViewportMask;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Ryujinx.Graphics.Shader.Instructions;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
|
@ -15,8 +16,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 +32,325 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
continue;
|
||||
}
|
||||
|
||||
if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
|
||||
if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp) &&
|
||||
!GenerateBindlessAccess(block, resourceManager, gpuAccessor, texOp, node))
|
||||
{
|
||||
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)
|
||||
string typeName = texOp.Inst.IsImage()
|
||||
? texOp.Type.ToGlslImageType(texOp.Format.GetComponentType())
|
||||
: texOp.Type.ToGlslTextureType();
|
||||
|
||||
gpuAccessor.Log($"Failed to find handle source for bindless access of type \"{typeName}\".");
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
SetHandle(
|
||||
resourceManager,
|
||||
gpuAccessor,
|
||||
texOp,
|
||||
bindlessHandle.GetCbufOffset(),
|
||||
bindlessHandle.GetCbufSlot(),
|
||||
rewriteSamplerType,
|
||||
isImage: false);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryGetOperation(bindlessHandle.AsgOp, out Operation handleCombineOp))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (handleCombineOp.Inst != Instruction.BitwiseOr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (src0AsgOp.Inst == Instruction.ShiftLeft)
|
||||
{
|
||||
Operand shift = src0AsgOp.GetSource(1);
|
||||
|
||||
if (shift.Type == OperandType.Constant && shift.Value == 20)
|
||||
{
|
||||
src0 = src1;
|
||||
src1 = src0AsgOp.GetSource(0);
|
||||
handleType = TextureHandleType.SeparateSamplerId;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (src1.AsgOp is Operation src1AsgOp && src1AsgOp.Inst == Instruction.ShiftLeft)
|
||||
{
|
||||
Operand shift = src1AsgOp.GetSource(1);
|
||||
|
||||
if (shift.Type == OperandType.Constant && shift.Value == 20)
|
||||
{
|
||||
src1 = src1AsgOp.GetSource(0);
|
||||
handleType = TextureHandleType.SeparateSamplerId;
|
||||
}
|
||||
}
|
||||
else if (src1.Type == OperandType.Constant && (src1.Value & 0xfffff) == 0)
|
||||
{
|
||||
handleType = TextureHandleType.SeparateConstantSamplerHandle;
|
||||
}
|
||||
|
||||
if (src0.Type != OperandType.ConstantBuffer)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
Utils.DeleteNode(node, texOp);
|
||||
}
|
||||
else if (texOp.Inst == Instruction.ImageLoad ||
|
||||
texOp.Inst == Instruction.ImageStore ||
|
||||
texOp.Inst == Instruction.ImageAtomic)
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GenerateBindlessAccess(
|
||||
BasicBlock block,
|
||||
ResourceManager resourceManager,
|
||||
IGpuAccessor gpuAccessor,
|
||||
TextureOperation texOp,
|
||||
LinkedListNode<INode> node)
|
||||
{
|
||||
if (!gpuAccessor.QueryHostSupportsSeparateSampler())
|
||||
{
|
||||
// We depend on combining samplers and textures in the shader being supported for this.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Operand bindlessHandle = texOp.GetSource(0);
|
||||
|
||||
if (bindlessHandle.AsgOp is PhiNode phi)
|
||||
{
|
||||
for (int srcIndex = 0; srcIndex < phi.SourcesCount; srcIndex++)
|
||||
{
|
||||
Operand src0 = Utils.FindLastOperation(texOp.GetSource(0), block);
|
||||
Operand phiSource = phi.GetSource(srcIndex);
|
||||
|
||||
if (src0.Type == OperandType.ConstantBuffer)
|
||||
if (phiSource.AsgOp is not PhiNode && !IsBindlessAccessAllowed(phiSource))
|
||||
{
|
||||
int cbufOffset = src0.GetCbufOffset();
|
||||
int cbufSlot = src0.GetCbufSlot();
|
||||
|
||||
if (texOp.Format == TextureFormat.Unknown)
|
||||
{
|
||||
if (texOp.Inst == Instruction.ImageAtomic)
|
||||
{
|
||||
texOp.Format = ShaderProperties.GetTextureFormatAtomic(gpuAccessor, cbufOffset, cbufSlot);
|
||||
}
|
||||
else
|
||||
{
|
||||
texOp.Format = ShaderProperties.GetTextureFormat(gpuAccessor, cbufOffset, cbufSlot);
|
||||
}
|
||||
}
|
||||
|
||||
bool rewriteSamplerType = texOp.Type == SamplerType.TextureBuffer;
|
||||
|
||||
SetHandle(resourceManager, gpuAccessor, texOp, cbufOffset, cbufSlot, rewriteSamplerType, isImage: true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!IsBindlessAccessAllowed(bindlessHandle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Operand textureHandle = OperandHelper.Local();
|
||||
Operand samplerHandle = OperandHelper.Local();
|
||||
Operand textureIndex = OperandHelper.Local();
|
||||
|
||||
block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, bindlessHandle, OperandHelper.Const(0xfffff)));
|
||||
block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, bindlessHandle, OperandHelper.Const(20)));
|
||||
|
||||
int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool());
|
||||
|
||||
block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, textureIndex, textureHandle, OperandHelper.Const(texturePoolLength - 1)));
|
||||
|
||||
texOp.SetSource(0, textureIndex);
|
||||
|
||||
bool hasSampler = !texOp.Inst.IsImage();
|
||||
|
||||
SetBindingPair textureSetAndBinding = resourceManager.GetTextureOrImageBinding(
|
||||
texOp.Inst,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags & ~TextureFlags.Bindless,
|
||||
0,
|
||||
TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct),
|
||||
texturePoolLength,
|
||||
hasSampler);
|
||||
|
||||
if (hasSampler)
|
||||
{
|
||||
Operand samplerIndex = OperandHelper.Local();
|
||||
|
||||
int samplerPoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QuerySamplerArrayLengthFromPool());
|
||||
|
||||
block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, samplerIndex, samplerHandle, OperandHelper.Const(samplerPoolLength - 1)));
|
||||
|
||||
texOp.InsertSource(1, samplerIndex);
|
||||
|
||||
SetBindingPair samplerSetAndBinding = resourceManager.GetTextureOrImageBinding(
|
||||
texOp.Inst,
|
||||
SamplerType.None,
|
||||
texOp.Format,
|
||||
TextureFlags.None,
|
||||
0,
|
||||
TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct),
|
||||
samplerPoolLength);
|
||||
|
||||
texOp.TurnIntoArray(textureSetAndBinding, samplerSetAndBinding);
|
||||
}
|
||||
else
|
||||
{
|
||||
texOp.TurnIntoArray(textureSetAndBinding);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsBindlessAccessAllowed(Operand bindlessHandle)
|
||||
{
|
||||
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
// Bindless access with handles from constant buffer is allowed.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bindlessHandle.AsgOp is not Operation handleOp ||
|
||||
handleOp.Inst != Instruction.Load ||
|
||||
(handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
|
||||
{
|
||||
// Right now, we only allow bindless access when the handle comes from a shader input or storage buffer.
|
||||
// This is an artificial limitation to prevent it from being used in cases where it
|
||||
// would have a large performance impact of loading all textures in the pool.
|
||||
// It might be removed in the future, if we can mitigate the performance impact.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
bindlessHandle = Utils.FindLastOperation(bindlessHandle, block);
|
||||
|
||||
// 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;
|
||||
|
||||
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
SetHandle(
|
||||
resourceManager,
|
||||
gpuAccessor,
|
||||
texOp,
|
||||
bindlessHandle.GetCbufOffset(),
|
||||
bindlessHandle.GetCbufSlot(),
|
||||
rewriteSamplerType,
|
||||
isImage: false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryGetOperation(bindlessHandle.AsgOp, out Operation handleCombineOp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (src0AsgOp.Inst == Instruction.ShiftLeft)
|
||||
{
|
||||
Operand shift = src0AsgOp.GetSource(1);
|
||||
|
||||
if (shift.Type == OperandType.Constant && shift.Value == 20)
|
||||
{
|
||||
src0 = src1;
|
||||
src1 = src0AsgOp.GetSource(0);
|
||||
handleType = TextureHandleType.SeparateSamplerId;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (src1.AsgOp is Operation src1AsgOp && src1AsgOp.Inst == Instruction.ShiftLeft)
|
||||
{
|
||||
Operand shift = src1AsgOp.GetSource(1);
|
||||
|
||||
if (shift.Type == OperandType.Constant && shift.Value == 20)
|
||||
{
|
||||
src1 = src1AsgOp.GetSource(0);
|
||||
handleType = TextureHandleType.SeparateSamplerId;
|
||||
}
|
||||
}
|
||||
else if (src1.Type == OperandType.Constant && (src1.Value & 0xfffff) == 0)
|
||||
{
|
||||
handleType = TextureHandleType.SeparateConstantSamplerHandle;
|
||||
}
|
||||
|
||||
if (src0.Type != OperandType.ConstantBuffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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 (texOp.Format == TextureFormat.Unknown)
|
||||
{
|
||||
if (texOp.Inst == Instruction.ImageAtomic)
|
||||
{
|
||||
texOp.Format = ShaderProperties.GetTextureFormatAtomic(gpuAccessor, cbufOffset, cbufSlot);
|
||||
}
|
||||
else
|
||||
{
|
||||
texOp.Format = ShaderProperties.GetTextureFormat(gpuAccessor, cbufOffset, cbufSlot);
|
||||
}
|
||||
}
|
||||
|
||||
bool rewriteSamplerType = texOp.Type == SamplerType.TextureBuffer;
|
||||
|
||||
SetHandle(resourceManager, gpuAccessor, texOp, cbufOffset, cbufSlot, rewriteSamplerType, isImage: true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGetOperation(INode asgOp, out Operation outOperation)
|
||||
|
@ -335,7 +480,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
}
|
||||
}
|
||||
|
||||
int binding = resourceManager.GetTextureOrImageBinding(
|
||||
SetBindingPair setAndBinding = resourceManager.GetTextureOrImageBinding(
|
||||
texOp.Inst,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
|
@ -343,7 +488,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
cbufSlot,
|
||||
cbufOffset);
|
||||
|
||||
texOp.SetBinding(binding);
|
||||
texOp.SetBinding(setAndBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
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.
|
||||
public 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;
|
||||
}
|
||||
|
||||
Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block);
|
||||
|
||||
if (bindlessHandle.AsgOp is not Operation handleAsgOp)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int secondaryCbufSlot = 0;
|
||||
int secondaryCbufOffset = 0;
|
||||
bool hasSecondaryHandle = false;
|
||||
|
||||
if (handleAsgOp.Inst == Instruction.BitwiseOr)
|
||||
{
|
||||
Operand src0 = Utils.FindLastOperation(handleAsgOp.GetSource(0), block);
|
||||
Operand src1 = Utils.FindLastOperation(handleAsgOp.GetSource(1), block);
|
||||
|
||||
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)
|
||||
{
|
||||
SetBindingPair setAndBinding = resourceManager.GetTextureOrImageBinding(
|
||||
texOp.Inst,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags & ~TextureFlags.Bindless,
|
||||
cbufSlot,
|
||||
handleIndex,
|
||||
length);
|
||||
|
||||
texOp.TurnIntoArray(setAndBinding);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
@ -144,18 +152,14 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
{
|
||||
// If all phi sources are the same, we can propagate it and remove the phi.
|
||||
|
||||
Operand firstSrc = phi.GetSource(0);
|
||||
|
||||
for (int index = 1; index < phi.SourcesCount; index++)
|
||||
if (!Utils.AreAllSourcesTheSameOperand(phi))
|
||||
{
|
||||
if (!IsSameOperand(firstSrc, phi.GetSource(index)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// All sources are equal, we can propagate the value.
|
||||
|
||||
Operand firstSrc = phi.GetSource(0);
|
||||
Operand dest = phi.Dest;
|
||||
|
||||
INode[] uses = dest.UseOps.ToArray();
|
||||
|
@ -174,17 +178,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool IsSameOperand(Operand x, Operand y)
|
||||
{
|
||||
if (x.Type != y.Type || x.Value != y.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Handle Load operations with the same storage and the same constant parameters.
|
||||
return x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer;
|
||||
}
|
||||
|
||||
private static bool PropagatePack(Operation packOp)
|
||||
{
|
||||
// Propagate pack source operands to uses by unpack
|
||||
|
@ -322,7 +315,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
Operand lhs = operation.GetSource(0);
|
||||
Operand rhs = operation.GetSource(1);
|
||||
|
||||
// Check LHS of the the main multiplication operation. We expect an input being multiplied by gl_FragCoord.w.
|
||||
// Check LHS of the main multiplication operation. We expect an input being multiplied by gl_FragCoord.w.
|
||||
if (lhs.AsgOp is not Operation attrMulOp || attrMulOp.Inst != (Instruction.FP32 | Instruction.Multiply))
|
||||
{
|
||||
return;
|
||||
|
|
|
@ -31,6 +31,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
TryEliminateBitwiseOr(operation);
|
||||
break;
|
||||
|
||||
case Instruction.CompareNotEqual:
|
||||
TryEliminateCompareNotEqual(operation);
|
||||
break;
|
||||
|
||||
case Instruction.ConditionalSelect:
|
||||
TryEliminateConditionalSelect(operation);
|
||||
break;
|
||||
|
@ -174,6 +178,32 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateCompareNotEqual(Operation operation)
|
||||
{
|
||||
// Comparison instruction returns 0 if the result is false, and -1 if true.
|
||||
// Doing a not equal zero comparison on the result is redundant, so we can just copy the first result in this case.
|
||||
|
||||
Operand lhs = operation.GetSource(0);
|
||||
Operand rhs = operation.GetSource(1);
|
||||
|
||||
if (lhs.Type == OperandType.Constant)
|
||||
{
|
||||
(lhs, rhs) = (rhs, lhs);
|
||||
}
|
||||
|
||||
if (rhs.Type != OperandType.Constant || rhs.Value != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (lhs.AsgOp is not Operation compareOp || !compareOp.Inst.IsComparison())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
operation.TurnIntoCopy(lhs);
|
||||
}
|
||||
|
||||
private static void TryEliminateConditionalSelect(Operation operation)
|
||||
{
|
||||
Operand cond = operation.GetSource(0);
|
||||
|
|
|
@ -34,6 +34,50 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
return elemIndexSrc.Type == OperandType.Constant && elemIndexSrc.Value == elemIndex;
|
||||
}
|
||||
|
||||
private static bool IsSameOperand(Operand x, Operand y)
|
||||
{
|
||||
if (x.Type != y.Type || x.Value != y.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Handle Load operations with the same storage and the same constant parameters.
|
||||
return x == y || x.Type == OperandType.Constant || x.Type == OperandType.ConstantBuffer;
|
||||
}
|
||||
|
||||
private static bool AreAllSourcesEqual(INode node, INode otherNode)
|
||||
{
|
||||
if (node.SourcesCount != otherNode.SourcesCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int index = 0; index < node.SourcesCount; index++)
|
||||
{
|
||||
if (!IsSameOperand(node.GetSource(index), otherNode.GetSource(index)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool AreAllSourcesTheSameOperand(INode node)
|
||||
{
|
||||
Operand firstSrc = node.GetSource(0);
|
||||
|
||||
for (int index = 1; index < node.SourcesCount; index++)
|
||||
{
|
||||
if (!IsSameOperand(firstSrc, node.GetSource(index)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Operation FindBranchSource(BasicBlock block)
|
||||
{
|
||||
foreach (BasicBlock sourceBlock in block.Predecessors)
|
||||
|
@ -55,6 +99,19 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
return inst == Instruction.BranchIfFalse || inst == Instruction.BranchIfTrue;
|
||||
}
|
||||
|
||||
private static bool IsSameCondition(Operand currentCondition, Operand queryCondition)
|
||||
{
|
||||
if (currentCondition == queryCondition)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return currentCondition.AsgOp is Operation currentOperation &&
|
||||
queryCondition.AsgOp is Operation queryOperation &&
|
||||
currentOperation.Inst == queryOperation.Inst &&
|
||||
AreAllSourcesEqual(currentOperation, queryOperation);
|
||||
}
|
||||
|
||||
private static bool BlockConditionsMatch(BasicBlock currentBlock, BasicBlock queryBlock)
|
||||
{
|
||||
// Check if all the conditions for the query block are satisfied by the current block.
|
||||
|
@ -70,10 +127,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
|
||||
return currentBranch != null && queryBranch != null &&
|
||||
currentBranch.Inst == queryBranch.Inst &&
|
||||
currentCondition == queryCondition;
|
||||
IsSameCondition(currentCondition, queryCondition);
|
||||
}
|
||||
|
||||
public static Operand FindLastOperation(Operand source, BasicBlock block)
|
||||
public static Operand FindLastOperation(Operand source, BasicBlock block, bool recurse = true)
|
||||
{
|
||||
if (source.AsgOp is PhiNode phiNode)
|
||||
{
|
||||
|
@ -81,13 +138,48 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||
// Ensure that conditions met for that branch are also met for the current one.
|
||||
// Prefer the latest sources for the phi node.
|
||||
|
||||
int undefCount = 0;
|
||||
|
||||
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
||||
{
|
||||
BasicBlock phiBlock = phiNode.GetBlock(i);
|
||||
Operand phiSource = phiNode.GetSource(i);
|
||||
|
||||
if (BlockConditionsMatch(block, phiBlock))
|
||||
{
|
||||
return phiNode.GetSource(i);
|
||||
return phiSource;
|
||||
}
|
||||
else if (recurse && phiSource.AsgOp is PhiNode)
|
||||
{
|
||||
// Phi source is another phi.
|
||||
// Let's check if that phi has a block that matches our condition.
|
||||
|
||||
Operand match = FindLastOperation(phiSource, block, false);
|
||||
|
||||
if (match != phiSource)
|
||||
{
|
||||
return match;
|
||||
}
|
||||
}
|
||||
else if (phiSource.Type == OperandType.Undefined)
|
||||
{
|
||||
undefCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// If all sources but one are undefined, we can assume that the one
|
||||
// that is not undefined is the right one.
|
||||
|
||||
if (undefCount == phiNode.SourcesCount - 1)
|
||||
{
|
||||
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
||||
{
|
||||
Operand phiSource = phiNode.GetSource(i);
|
||||
|
||||
if (phiSource.Type != OperandType.Undefined)
|
||||
{
|
||||
return phiSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,9 +155,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index];
|
||||
}
|
||||
|
||||
if (operation.Dest != null && operation.Dest.Type == OperandType.Register)
|
||||
for (int dstIndex = 0; dstIndex < operation.DestsCount; dstIndex++)
|
||||
{
|
||||
localOutputs[block.Index] |= GetMask(operation.Dest.GetRegister());
|
||||
Operand dest = operation.GetDest(dstIndex);
|
||||
|
||||
if (dest != null && dest.Type == OperandType.Register)
|
||||
{
|
||||
localOutputs[block.Index] |= GetMask(dest.GetRegister());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,17 +14,14 @@ 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;
|
||||
private readonly ShaderStage _stage;
|
||||
private readonly string _stagePrefix;
|
||||
|
||||
private readonly int[] _cbSlotToBindingMap;
|
||||
private readonly int[] _sbSlotToBindingMap;
|
||||
private readonly SetBindingPair[] _cbSlotToBindingMap;
|
||||
private readonly SetBindingPair[] _sbSlotToBindingMap;
|
||||
private uint _sbSlotWritten;
|
||||
|
||||
private readonly Dictionary<int, int> _sbSlots;
|
||||
|
@ -32,10 +29,11 @@ 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, bool Separate, SamplerType Type, TextureFormat Format);
|
||||
|
||||
private struct TextureMeta
|
||||
{
|
||||
public int Set;
|
||||
public int Binding;
|
||||
public bool AccurateType;
|
||||
public SamplerType Type;
|
||||
|
@ -67,10 +65,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
_stage = stage;
|
||||
_stagePrefix = GetShaderStagePrefix(stage);
|
||||
|
||||
_cbSlotToBindingMap = new int[18];
|
||||
_sbSlotToBindingMap = new int[16];
|
||||
_cbSlotToBindingMap.AsSpan().Fill(-1);
|
||||
_sbSlotToBindingMap.AsSpan().Fill(-1);
|
||||
_cbSlotToBindingMap = new SetBindingPair[18];
|
||||
_sbSlotToBindingMap = new SetBindingPair[16];
|
||||
_cbSlotToBindingMap.AsSpan().Fill(new(-1, -1));
|
||||
_sbSlotToBindingMap.AsSpan().Fill(new(-1, -1));
|
||||
|
||||
_sbSlots = new();
|
||||
_sbSlotsReverse = new();
|
||||
|
@ -149,16 +147,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public int GetConstantBufferBinding(int slot)
|
||||
{
|
||||
int binding = _cbSlotToBindingMap[slot];
|
||||
if (binding < 0)
|
||||
SetBindingPair setAndBinding = _cbSlotToBindingMap[slot];
|
||||
if (setAndBinding.Binding < 0)
|
||||
{
|
||||
binding = _gpuAccessor.QueryBindingConstantBuffer(slot);
|
||||
_cbSlotToBindingMap[slot] = binding;
|
||||
setAndBinding = _gpuAccessor.CreateConstantBufferBinding(slot);
|
||||
_cbSlotToBindingMap[slot] = setAndBinding;
|
||||
string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
|
||||
AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}");
|
||||
AddNewConstantBuffer(setAndBinding.SetIndex, setAndBinding.Binding, $"{_stagePrefix}_c{slotNumber}");
|
||||
}
|
||||
|
||||
return binding;
|
||||
return setAndBinding.Binding;
|
||||
}
|
||||
|
||||
public bool TryGetStorageBufferBinding(int sbCbSlot, int sbCbOffset, bool write, out int binding)
|
||||
|
@ -169,14 +167,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return false;
|
||||
}
|
||||
|
||||
binding = _sbSlotToBindingMap[slot];
|
||||
SetBindingPair setAndBinding = _sbSlotToBindingMap[slot];
|
||||
|
||||
if (binding < 0)
|
||||
if (setAndBinding.Binding < 0)
|
||||
{
|
||||
binding = _gpuAccessor.QueryBindingStorageBuffer(slot);
|
||||
_sbSlotToBindingMap[slot] = binding;
|
||||
setAndBinding = _gpuAccessor.CreateStorageBufferBinding(slot);
|
||||
_sbSlotToBindingMap[slot] = setAndBinding;
|
||||
string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
|
||||
AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}");
|
||||
AddNewStorageBuffer(setAndBinding.SetIndex, setAndBinding.Binding, $"{_stagePrefix}_s{slotNumber}");
|
||||
}
|
||||
|
||||
if (write)
|
||||
|
@ -184,6 +182,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
_sbSlotWritten |= 1u << slot;
|
||||
}
|
||||
|
||||
binding = setAndBinding.Binding;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -211,7 +210,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
|
||||
{
|
||||
if (_cbSlotToBindingMap[slot] == binding)
|
||||
if (_cbSlotToBindingMap[slot].Binding == binding)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -221,17 +220,19 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return false;
|
||||
}
|
||||
|
||||
public int GetTextureOrImageBinding(
|
||||
public SetBindingPair GetTextureOrImageBinding(
|
||||
Instruction inst,
|
||||
SamplerType type,
|
||||
TextureFormat format,
|
||||
TextureFlags flags,
|
||||
int cbufSlot,
|
||||
int handle)
|
||||
int handle,
|
||||
int arrayLength = 1,
|
||||
bool separate = false)
|
||||
{
|
||||
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,26 +242,38 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
format = TextureFormat.Unknown;
|
||||
}
|
||||
|
||||
int binding = GetTextureOrImageBinding(cbufSlot, handle, type, format, isImage, intCoords, isWrite, accurateType, coherent);
|
||||
SetBindingPair setAndBinding = GetTextureOrImageBinding(
|
||||
cbufSlot,
|
||||
handle,
|
||||
arrayLength,
|
||||
type,
|
||||
format,
|
||||
isImage,
|
||||
intCoords,
|
||||
isWrite,
|
||||
accurateType,
|
||||
coherent,
|
||||
separate);
|
||||
|
||||
_gpuAccessor.RegisterTexture(handle, cbufSlot);
|
||||
|
||||
return binding;
|
||||
return setAndBinding;
|
||||
}
|
||||
|
||||
private int GetTextureOrImageBinding(
|
||||
private SetBindingPair GetTextureOrImageBinding(
|
||||
int cbufSlot,
|
||||
int handle,
|
||||
int arrayLength,
|
||||
SamplerType type,
|
||||
TextureFormat format,
|
||||
bool isImage,
|
||||
bool intCoords,
|
||||
bool write,
|
||||
bool accurateType,
|
||||
bool coherent)
|
||||
bool coherent,
|
||||
bool separate)
|
||||
{
|
||||
var dimensions = type.GetDimensions();
|
||||
var isIndexed = type.HasFlag(SamplerType.Indexed);
|
||||
var dimensions = type == SamplerType.None ? 0 : type.GetDimensions();
|
||||
var dict = isImage ? _usedImages : _usedTextures;
|
||||
|
||||
var usageFlags = TextureUsageFlags.None;
|
||||
|
@ -269,7 +282,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,80 +302,102 @@ 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, separate, 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 setIndex;
|
||||
int binding;
|
||||
|
||||
if (dict.TryGetValue(info, out var existingMeta))
|
||||
if (dict.TryGetValue(info, out var existingMeta))
|
||||
{
|
||||
dict[info] = MergeTextureMeta(meta, existingMeta);
|
||||
setIndex = existingMeta.Set;
|
||||
binding = existingMeta.Binding;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (arrayLength > 1 && (setIndex = _gpuAccessor.CreateExtraSet()) >= 0)
|
||||
{
|
||||
dict[info] = MergeTextureMeta(meta, existingMeta);
|
||||
binding = existingMeta.Binding;
|
||||
// We reserved an "extra set" for the array.
|
||||
// In this case the binding is always the first one (0).
|
||||
// Using separate sets for array is better as we need to do less descriptor set updates.
|
||||
|
||||
binding = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer;
|
||||
|
||||
binding = isImage
|
||||
? _gpuAccessor.QueryBindingImage(dict.Count, isBuffer)
|
||||
: _gpuAccessor.QueryBindingTexture(dict.Count, isBuffer);
|
||||
SetBindingPair setAndBinding = isImage
|
||||
? _gpuAccessor.CreateImageBinding(arrayLength, isBuffer)
|
||||
: _gpuAccessor.CreateTextureBinding(arrayLength, isBuffer);
|
||||
|
||||
meta.Binding = binding;
|
||||
|
||||
dict.Add(info, meta);
|
||||
setIndex = setAndBinding.SetIndex;
|
||||
binding = setAndBinding.Binding;
|
||||
}
|
||||
|
||||
string nameSuffix;
|
||||
meta.Set = setIndex;
|
||||
meta.Binding = binding;
|
||||
|
||||
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}";
|
||||
}
|
||||
|
||||
var definition = new TextureDefinition(
|
||||
isImage ? 3 : 2,
|
||||
binding,
|
||||
$"{_stagePrefix}_{nameSuffix}",
|
||||
meta.Type,
|
||||
info.Format,
|
||||
meta.UsageFlags);
|
||||
|
||||
if (isImage)
|
||||
{
|
||||
Properties.AddOrUpdateImage(definition);
|
||||
}
|
||||
else
|
||||
{
|
||||
Properties.AddOrUpdateTexture(definition);
|
||||
}
|
||||
|
||||
if (layer == 0)
|
||||
{
|
||||
firstBinding = binding;
|
||||
}
|
||||
dict.Add(info, meta);
|
||||
}
|
||||
|
||||
return firstBinding;
|
||||
string nameSuffix;
|
||||
string prefix = isImage ? "i" : "t";
|
||||
|
||||
if (arrayLength != 1 && type != SamplerType.None)
|
||||
{
|
||||
prefix += type.ToShortSamplerType();
|
||||
}
|
||||
|
||||
if (isImage)
|
||||
{
|
||||
nameSuffix = cbufSlot < 0
|
||||
? $"{prefix}_tcb_{handle:X}_{format.ToGlslFormat()}"
|
||||
: $"{prefix}_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
|
||||
}
|
||||
else if (type == SamplerType.None)
|
||||
{
|
||||
nameSuffix = cbufSlot < 0 ? $"s_tcb_{handle:X}" : $"s_cb{cbufSlot}_{handle:X}";
|
||||
}
|
||||
else
|
||||
{
|
||||
nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}";
|
||||
}
|
||||
|
||||
var definition = new TextureDefinition(
|
||||
setIndex,
|
||||
binding,
|
||||
arrayLength,
|
||||
separate,
|
||||
$"{_stagePrefix}_{nameSuffix}",
|
||||
meta.Type,
|
||||
info.Format,
|
||||
meta.UsageFlags);
|
||||
|
||||
if (isImage)
|
||||
{
|
||||
Properties.AddOrUpdateImage(definition);
|
||||
}
|
||||
else
|
||||
{
|
||||
Properties.AddOrUpdateTexture(definition);
|
||||
}
|
||||
|
||||
return new SetBindingPair(setIndex, binding);
|
||||
}
|
||||
|
||||
private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta)
|
||||
{
|
||||
meta.Set = existingMeta.Set;
|
||||
meta.Binding = existingMeta.Binding;
|
||||
meta.UsageFlags |= existingMeta.UsageFlags;
|
||||
|
||||
|
@ -399,8 +434,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)
|
||||
{
|
||||
|
@ -426,11 +460,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
for (int slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
|
||||
{
|
||||
int binding = _cbSlotToBindingMap[slot];
|
||||
SetBindingPair setAndBinding = _cbSlotToBindingMap[slot];
|
||||
|
||||
if (binding >= 0 && _usedConstantBufferBindings.Contains(binding))
|
||||
if (setAndBinding.Binding >= 0 && _usedConstantBufferBindings.Contains(setAndBinding.Binding))
|
||||
{
|
||||
descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot);
|
||||
descriptors[descriptorIndex++] = new BufferDescriptor(setAndBinding.SetIndex, setAndBinding.Binding, slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -450,13 +484,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
foreach ((int key, int slot) in _sbSlots)
|
||||
{
|
||||
int binding = _sbSlotToBindingMap[slot];
|
||||
SetBindingPair setAndBinding = _sbSlotToBindingMap[slot];
|
||||
|
||||
if (binding >= 0)
|
||||
if (setAndBinding.Binding >= 0)
|
||||
{
|
||||
(int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key);
|
||||
BufferUsageFlags flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None;
|
||||
descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset, flags);
|
||||
descriptors[descriptorIndex++] = new BufferDescriptor(setAndBinding.SetIndex, setAndBinding.Binding, slot, sbCbSlot, sbCbOffset, flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -468,34 +502,65 @@ 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.Set,
|
||||
meta.Binding,
|
||||
meta.Type,
|
||||
info.Format,
|
||||
info.CbufSlot,
|
||||
info.Handle,
|
||||
meta.UsageFlags);
|
||||
info.ArrayLength,
|
||||
info.Separate,
|
||||
meta.UsageFlags));
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
if (hasAnyArray && includeArrays)
|
||||
{
|
||||
foreach ((TextureInfo info, TextureMeta meta) in usedResources)
|
||||
{
|
||||
if (info.ArrayLength <= 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
descriptors.Add(new TextureDescriptor(
|
||||
meta.Set,
|
||||
meta.Binding,
|
||||
meta.Type,
|
||||
info.Format,
|
||||
info.CbufSlot,
|
||||
info.Handle,
|
||||
info.ArrayLength,
|
||||
info.Separate,
|
||||
meta.UsageFlags));
|
||||
}
|
||||
}
|
||||
|
||||
return descriptors.ToArray();
|
||||
}
|
||||
|
||||
public bool TryGetCbufSlotAndHandleForTexture(int binding, out int cbufSlot, out int handle)
|
||||
|
@ -531,24 +596,37 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return FindDescriptorIndex(GetImageDescriptors(), binding);
|
||||
}
|
||||
|
||||
private void AddNewConstantBuffer(int binding, string name)
|
||||
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 setIndex, int binding, string name)
|
||||
{
|
||||
StructureType type = new(new[]
|
||||
{
|
||||
new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16),
|
||||
});
|
||||
|
||||
Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, binding, name, type));
|
||||
Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, setIndex, binding, name, type));
|
||||
}
|
||||
|
||||
private void AddNewStorageBuffer(int binding, string name)
|
||||
private void AddNewStorageBuffer(int setIndex, int binding, string name)
|
||||
{
|
||||
StructureType type = new(new[]
|
||||
{
|
||||
new StructureField(AggregateType.Array | AggregateType.U32, "data", 0),
|
||||
});
|
||||
|
||||
Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, 1, binding, name, type));
|
||||
Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, setIndex, binding, name, type));
|
||||
}
|
||||
|
||||
public static string GetShaderStagePrefix(ShaderStage stage)
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public const int MaxVertexBufferTextures = 32;
|
||||
|
||||
private const int TextureSetIndex = 2; // TODO: Get from GPU accessor.
|
||||
|
||||
public int VertexInfoConstantBufferBinding { get; }
|
||||
public int VertexOutputStorageBufferBinding { get; }
|
||||
public int GeometryVertexOutputStorageBufferBinding { get; }
|
||||
|
@ -163,6 +165,21 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return _vertexBufferTextureBaseBinding + vaLocation;
|
||||
}
|
||||
|
||||
public SetBindingPair GetVertexBufferTextureSetAndBinding(int vaLocation)
|
||||
{
|
||||
return new SetBindingPair(TextureSetIndex, GetVertexBufferTextureBinding(vaLocation));
|
||||
}
|
||||
|
||||
public SetBindingPair GetIndexBufferTextureSetAndBinding()
|
||||
{
|
||||
return new SetBindingPair(TextureSetIndex, IndexBufferTextureBinding);
|
||||
}
|
||||
|
||||
public SetBindingPair GetTopologyRemapBufferTextureSetAndBinding()
|
||||
{
|
||||
return new SetBindingPair(TextureSetIndex, TopologyRemapBufferTextureBinding);
|
||||
}
|
||||
|
||||
internal bool TryGetOffset(StorageKind storageKind, int location, int component, out int offset)
|
||||
{
|
||||
return _offsets.TryGetValue(new IoDefinition(storageKind, IoVariable.UserDefined, location, component), out offset);
|
||||
|
|
|
@ -45,6 +45,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
public bool YNegateEnabled => _graphicsState.YNegateEnabled;
|
||||
public bool OriginUpperLeft => _graphicsState.OriginUpperLeft;
|
||||
|
||||
public bool HalvePrimitiveId => _graphicsState.HalvePrimitiveId;
|
||||
|
||||
public ImapPixelType[] ImapTypes { get; }
|
||||
public bool IaIndexing { get; private set; }
|
||||
public bool OaIndexing { get; private set; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,22 +175,14 @@ 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,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Set,
|
||||
texOp.Binding,
|
||||
index,
|
||||
new[] { coordSize },
|
||||
|
@ -201,13 +190,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 +223,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;
|
||||
|
@ -263,6 +252,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Set,
|
||||
texOp.Binding,
|
||||
index,
|
||||
new[] { coordSize },
|
||||
|
@ -287,7 +277,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 +311,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 +331,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];
|
||||
|
||||
|
@ -482,6 +473,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
|
||||
texOp.Set,
|
||||
texOp.Binding,
|
||||
1 << 3, // W component: i=0, j=0
|
||||
new[] { dests[destIndex++] },
|
||||
|
@ -538,6 +530,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
|
||||
texOp.Set,
|
||||
texOp.Binding,
|
||||
componentIndex,
|
||||
dests,
|
||||
|
@ -584,6 +577,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Set,
|
||||
texOp.Binding,
|
||||
index,
|
||||
new[] { texSizes[index] },
|
||||
|
@ -614,6 +608,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Set,
|
||||
texOp.Binding,
|
||||
0,
|
||||
new[] { lod },
|
||||
|
@ -644,6 +639,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Set,
|
||||
texOp.Binding,
|
||||
index,
|
||||
new[] { texSizes[index] },
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
{
|
||||
bool needsSextNorm = context.Definitions.IsAttributePackedRgb10A2Signed(location);
|
||||
|
||||
SetBindingPair setAndBinding = context.ResourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location);
|
||||
Operand temp = needsSextNorm ? Local() : dest;
|
||||
Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, 0);
|
||||
|
||||
|
@ -62,7 +63,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
SamplerType.TextureBuffer,
|
||||
TextureFormat.Unknown,
|
||||
TextureFlags.IntCoords,
|
||||
context.ResourceManager.Reservations.GetVertexBufferTextureBinding(location),
|
||||
setAndBinding.SetIndex,
|
||||
setAndBinding.Binding,
|
||||
1 << component,
|
||||
new[] { temp },
|
||||
new[] { vertexElemOffset }));
|
||||
|
@ -75,6 +77,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
}
|
||||
else
|
||||
{
|
||||
SetBindingPair setAndBinding = context.ResourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location);
|
||||
Operand temp = component > 0 ? Local() : dest;
|
||||
Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, component);
|
||||
|
||||
|
@ -83,7 +86,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
SamplerType.TextureBuffer,
|
||||
TextureFormat.Unknown,
|
||||
TextureFlags.IntCoords,
|
||||
context.ResourceManager.Reservations.GetVertexBufferTextureBinding(location),
|
||||
setAndBinding.SetIndex,
|
||||
setAndBinding.Binding,
|
||||
1,
|
||||
new[] { temp },
|
||||
new[] { vertexElemOffset }));
|
||||
|
|
|
@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
if (stage == ShaderStage.Vertex)
|
||||
{
|
||||
InitializePositionOutput(context);
|
||||
InitializeVertexOutputs(context);
|
||||
}
|
||||
|
||||
UInt128 usedAttributes = context.TranslatorContext.AttributeUsage.NextInputAttributesComponents;
|
||||
|
@ -236,12 +236,20 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
}
|
||||
}
|
||||
|
||||
private static void InitializePositionOutput(EmitterContext context)
|
||||
private static void InitializeVertexOutputs(EmitterContext context)
|
||||
{
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f));
|
||||
}
|
||||
|
||||
if (context.Program.ClipDistancesWritten != 0)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
context.Store(StorageKind.Output, IoVariable.ClipDistance, null, Const(i), ConstF(0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitializeOutput(EmitterContext context, int location, bool perPatch)
|
||||
|
|
|
@ -294,6 +294,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
Definitions,
|
||||
resourceManager,
|
||||
GpuAccessor,
|
||||
Options.TargetApi,
|
||||
Options.TargetLanguage,
|
||||
Definitions.Stage,
|
||||
ref usedFeatures);
|
||||
|
@ -362,6 +363,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
GpuAccessor.QueryHostSupportsGeometryShaderPassthrough(),
|
||||
GpuAccessor.QueryHostSupportsShaderBallot(),
|
||||
GpuAccessor.QueryHostSupportsShaderBarrierDivergence(),
|
||||
GpuAccessor.QueryHostSupportsShaderFloat64(),
|
||||
GpuAccessor.QueryHostSupportsTextureShadowLod(),
|
||||
GpuAccessor.QueryHostSupportsViewportMask());
|
||||
|
||||
|
@ -411,8 +413,8 @@ 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);
|
||||
SetBindingPair ibSetAndBinding = resourceManager.Reservations.GetIndexBufferTextureSetAndBinding();
|
||||
TextureDefinition indexBuffer = new(ibSetAndBinding.SetIndex, ibSetAndBinding.Binding, "ib_data", SamplerType.TextureBuffer);
|
||||
resourceManager.Properties.AddOrUpdateTexture(indexBuffer);
|
||||
|
||||
int inputMap = _program.AttributeUsage.UsedInputAttributes;
|
||||
|
@ -420,8 +422,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
while (inputMap != 0)
|
||||
{
|
||||
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);
|
||||
SetBindingPair setAndBinding = resourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location);
|
||||
TextureDefinition vaBuffer = new(setAndBinding.SetIndex, setAndBinding.Binding, $"vb_data{location}", SamplerType.TextureBuffer);
|
||||
resourceManager.Properties.AddOrUpdateTexture(vaBuffer);
|
||||
|
||||
inputMap &= ~(1 << location);
|
||||
|
@ -429,8 +431,8 @@ 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);
|
||||
SetBindingPair trbSetAndBinding = resourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding();
|
||||
TextureDefinition remapBuffer = new(trbSetAndBinding.SetIndex, trbSetAndBinding.Binding, "trb_data", SamplerType.TextureBuffer);
|
||||
resourceManager.Properties.AddOrUpdateTexture(remapBuffer);
|
||||
|
||||
int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue