SPIR-V: Resolution scale support and fix TextureSample multisample with LOD bug

This commit is contained in:
gdk 2022-04-08 16:13:23 -03:00 committed by riperiperi
parent f18e7aa779
commit 0b27b3e470
8 changed files with 344 additions and 58 deletions

View file

@ -70,53 +70,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
AppendLine("}" + suffix);
}
public (TextureDescriptor, int) FindTextureDescriptor(AstTextureOperation texOp)
{
TextureDescriptor[] descriptors = Config.GetTextureDescriptors();
for (int i = 0; i < descriptors.Length; i++)
{
var descriptor = descriptors[i];
if (descriptor.CbufSlot == texOp.CbufSlot &&
descriptor.HandleIndex == texOp.Handle &&
descriptor.Format == texOp.Format)
{
return (descriptor, i);
}
}
return (default, -1);
}
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
{
for (int i = 0; i < array.Length; i++)
{
var descriptor = array[i];
if (descriptor.Type == texOp.Type &&
descriptor.CbufSlot == texOp.CbufSlot &&
descriptor.HandleIndex == texOp.Handle &&
descriptor.Format == texOp.Format)
{
return i;
}
}
return -1;
}
public int FindTextureDescriptorIndex(AstTextureOperation texOp)
{
return FindDescriptorIndex(Config.GetTextureDescriptors(), texOp);
}
public int FindImageDescriptorIndex(AstTextureOperation texOp)
{
return FindDescriptorIndex(Config.GetImageDescriptors(), texOp);
}
public StructuredFunction GetFunction(int id)
{
return _info.Functions[id];

View file

@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
!isIndexed)
{
// Image scales start after texture ones.
int scaleIndex = context.Config.GetTextureDescriptors().Length + context.FindImageDescriptorIndex(texOp);
int scaleIndex = context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp);
if (pCount == 3 && isArray)
{
@ -625,7 +625,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
!isBindless &&
!isIndexed)
{
int index = context.FindTextureDescriptorIndex(texOp);
int index = context.Config.FindTextureDescriptorIndex(texOp);
if (pCount == 3 && isArray)
{
@ -762,7 +762,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
}
else
{
(TextureDescriptor descriptor, int descriptorIndex) = context.FindTextureDescriptor(texOp);
(TextureDescriptor descriptor, int descriptorIndex) = context.Config.FindTextureDescriptor(texOp);
bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer;
string texCall;

View file

@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public int InputVertices { get; }
public Dictionary<int, Instruction> UniformBuffers { get; } = new Dictionary<int, Instruction>();
public Instruction SupportBuffer { get; set; }
public Instruction UniformBuffersArray { get; set; }
public Instruction StorageBuffersArray { get; set; }
public Instruction LocalMemory { get; set; }
@ -34,6 +35,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Dictionary<int, Instruction> InputsPerPatch { get; } = new Dictionary<int, Instruction>();
public Dictionary<int, Instruction> OutputsPerPatch { get; } = new Dictionary<int, Instruction>();
public Instruction CoordTemp { get; set; }
private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
private readonly Dictionary<int, Instruction> _funcArgs = new Dictionary<int, Instruction>();
@ -150,19 +152,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Instruction[] GetMainInterface()
{
var mainInterface = Inputs.Values
.Concat(Outputs.Values)
.Concat(InputsPerPatch.Values)
.Concat(OutputsPerPatch.Values);
var mainInterface = new List<Instruction>();
mainInterface.AddRange(Inputs.Values);
mainInterface.AddRange(Outputs.Values);
mainInterface.AddRange(InputsPerPatch.Values);
mainInterface.AddRange(OutputsPerPatch.Values);
if (InputsArray != null)
{
mainInterface = mainInterface.Append(InputsArray);
mainInterface.Add(InputsArray);
}
if (OutputsArray != null)
{
mainInterface = mainInterface.Append(OutputsArray);
mainInterface.Add(OutputsArray);
}
return mainInterface.ToArray();
@ -320,7 +324,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType);
return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer));
var value = Load(GetType(elemType), elemPointer);
if (Config.Stage == ShaderStage.Fragment && (attr == AttributeConsts.PositionX || attr == AttributeConsts.PositionY))
{
var pointerType = TypePointer(StorageClass.Uniform, TypeFP32());
var fieldIndex = Constant(TypeU32(), 3);
var scaleIndex = Constant(TypeU32(), 0);
var scaleElemPointer = AccessChain(pointerType, SupportBuffer, fieldIndex, scaleIndex);
var scale = Load(TypeFP32(), scaleElemPointer);
value = FDiv(TypeFP32(), value, scale);
}
return BitcastIfNeeded(type, elemType, value);
}
public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType)

View file

@ -46,6 +46,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddLocalVariable(spvLocal);
context.DeclareLocal(local, spvLocal);
}
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
var coordTempPointerType = context.TypePointer(StorageClass.Function, ivector2Type);
var coordTemp = context.Variable(coordTempPointerType, StorageClass.Function);
context.AddLocalVariable(coordTemp);
context.CoordTemp = coordTemp;
}
public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions)
@ -94,6 +101,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
DeclareLocalMemory(context, localMemorySize);
}
DeclareSupportBuffer(context);
DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
DeclareSamplers(context, context.Config.GetTextureDescriptors());
@ -125,6 +133,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return variable;
}
private static void DeclareSupportBuffer(CodeGenContext context)
{
if (!context.Config.Stage.SupportsRenderScale())
{
return;
}
var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount));
var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount));
context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
var supportBufferStructType = context.TypeStruct(false, context.TypeU32(), isBgraArrayType, context.TypeS32(), renderScaleArrayType);
context.MemberDecorate(supportBufferStructType, 0, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentAlphaTestOffset);
context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset);
context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset);
context.Decorate(supportBufferStructType, Decoration.Block);
var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType);
var supportBufferVariable = context.Variable(supportBufferPointerType, StorageClass.Uniform);
context.Decorate(supportBufferVariable, Decoration.DescriptorSet, (LiteralInteger)0);
context.Decorate(supportBufferVariable, Decoration.Binding, (LiteralInteger)0);
context.AddGlobalVariable(supportBufferVariable);
context.SupportBuffer = supportBufferVariable;
}
private static void DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
{
if (descriptors.Length == 0)

View file

@ -739,6 +739,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
pCoords = Src(AggregateType.S32);
}
pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords: true, isBindless, isIndexed, isArray, pCount);
(var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
var image = context.Load(imageType, imageVariable);
@ -1487,6 +1489,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
SpvInstruction pCoords = AssemblePVector(pCount);
pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount);
SpvInstruction AssembleDerivativesVector(int count)
{
@ -1601,7 +1604,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
operandsList.Add(lodBias);
}
if (hasLodLevel)
if (!isMultisample && hasLodLevel)
{
operandsMask |= ImageOperandsMask.Lod;
operandsList.Add(lod);
@ -1751,6 +1754,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
result = context.CompositeExtract(context.TypeS32(), result, (SpvLiteralInteger)texOp.Index);
}
result = ScalingHelpers.ApplyUnscaling(context, texOp, result, isBindless, isIndexed);
return new OperationResult(AggregateType.S32, result);
}
}

View file

@ -0,0 +1,216 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using static Spv.Specification;
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
using SpvInstruction = Spv.Generator.Instruction;
static class ScalingHelpers
{
public static SpvInstruction ApplyScaling(
CodeGenContext context,
AstTextureOperation texOp,
SpvInstruction vector,
bool intCoords,
bool isBindless,
bool isIndexed,
bool isArray,
int pCount)
{
if (intCoords)
{
if ((context.Config.Stage.SupportsRenderScale()) &&
!isBindless &&
!isIndexed)
{
int index = texOp.Inst == Instruction.ImageLoad
? context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp)
: context.Config.FindTextureDescriptorIndex(texOp);
if (pCount == 3 && isArray)
{
return ApplyScaling2DArray(context, vector, index);
}
else if (pCount == 2 && !isArray)
{
return ApplyScaling2D(context, vector, index);
}
}
}
return vector;
}
private static SpvInstruction ApplyScaling2DArray(CodeGenContext context, SpvInstruction vector, int index)
{
// The array index is not scaled, just x and y.
var vectorXY = context.VectorShuffle(context.TypeVector(context.TypeS32(), 2), vector, vector, 0, 1);
var vectorZ = context.CompositeExtract(context.TypeS32(), vector, 2);
var vectorXYScaled = ApplyScaling2D(context, vectorXY, index);
var vectorScaled = context.CompositeConstruct(context.TypeVector(context.TypeS32(), 3), vectorXYScaled, vectorZ);
return vectorScaled;
}
private static SpvInstruction ApplyScaling2D(CodeGenContext context, SpvInstruction vector, int index)
{
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
var fieldIndex = context.Constant(context.TypeU32(), 3);
var scaleIndex = context.Constant(context.TypeU32(), index);
if (context.Config.Stage == ShaderStage.Vertex)
{
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
}
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
var scale = context.Load(context.TypeFP32(), scaleElemPointer);
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
var localVector = context.CoordTemp;
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
var mergeLabel = context.Label();
if (context.Config.Stage == ShaderStage.Fragment)
{
var scaledInterpolatedLabel = context.Label();
var scaledNoInterpolationLabel = context.Label();
var needsInterpolation = context.FOrdLessThan(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 0f));
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
context.BranchConditional(needsInterpolation, scaledInterpolatedLabel, scaledNoInterpolationLabel);
// scale < 0.0
context.AddLabel(scaledInterpolatedLabel);
ApplyScalingInterpolated(context, localVector, vector, scale);
context.Branch(mergeLabel);
// scale >= 0.0
context.AddLabel(scaledNoInterpolationLabel);
ApplyScalingNoInterpolation(context, localVector, vector, scale);
context.Branch(mergeLabel);
context.AddLabel(mergeLabel);
var passthroughVector = context.CompositeConstruct(context.TypeVector(context.TypeBool(), 2), passthrough, passthrough);
return context.Select(ivector2Type, passthroughVector, vector, context.Load(ivector2Type, localVector));
}
else
{
var passthroughLabel = context.Label();
var scaledLabel = context.Label();
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
context.BranchConditional(passthrough, passthroughLabel, scaledLabel);
// scale == 1.0
context.AddLabel(passthroughLabel);
context.Store(localVector, vector);
context.Branch(mergeLabel);
// scale != 1.0
context.AddLabel(scaledLabel);
ApplyScalingNoInterpolation(context, localVector, vector, scale);
context.Branch(mergeLabel);
context.AddLabel(mergeLabel);
return context.Load(ivector2Type, localVector);
}
}
private static void ApplyScalingInterpolated(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
{
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
var scaleNegated = context.FNegate(context.TypeFP32(), scale);
var scaleVector = context.CompositeConstruct(vector2Type, scaleNegated, scaleNegated);
var vectorFloat = context.ConvertSToF(vector2Type, vector);
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated);
var fragCoordPointer = context.Inputs[AttributeConsts.PositionX];
var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer);
var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1);
var scaleMod = context.FMod(vector2Type, scaleVector, fragCoordXY);
var vectorInterpolated = context.FAdd(vector2Type, vectorScaled, scaleMod);
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorInterpolated));
}
private static void ApplyScalingNoInterpolation(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
{
if (context.Config.Stage == ShaderStage.Vertex)
{
scale = context.GlslFAbs(context.TypeFP32(), scale);
}
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
var vectorFloat = context.ConvertSToF(vector2Type, vector);
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scale);
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorScaled));
}
public static SpvInstruction ApplyUnscaling(
CodeGenContext context,
AstTextureOperation texOp,
SpvInstruction size,
bool isBindless,
bool isIndexed)
{
if ((context.Config.Stage.SupportsRenderScale()) &&
!isBindless &&
!isIndexed)
{
int index = context.Config.FindTextureDescriptorIndex(texOp);
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
var fieldIndex = context.Constant(context.TypeU32(), 3);
var scaleIndex = context.Constant(context.TypeU32(), index);
if (context.Config.Stage == ShaderStage.Vertex)
{
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
}
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
var scale = context.Load(context.TypeFP32(), scaleElemPointer);
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
var sizeFloat = context.ConvertSToF(context.TypeFP32(), size);
var sizeUnscaled = context.FDiv(context.TypeFP32(), size, scale);
var sizeUnscaledInt = context.ConvertFToS(context.TypeS32(), sizeUnscaled);
return context.Select(context.TypeS32(), passthrough, size, sizeUnscaledInt);
}
return size;
}
}
}

View file

@ -64,6 +64,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Info.Inputs.Add(AttributeConsts.ClipDistance0 + i * 4);
}
}
else if (config.Stage == ShaderStage.Fragment)
{
// Potentially used for texture coordinate scaling.
Info.Inputs.Add(AttributeConsts.PositionX);
Info.Inputs.Add(AttributeConsts.PositionY);
}
}
public void EnterFunction(

View file

@ -1,4 +1,5 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using System;
using System.Collections.Generic;
using System.Linq;
@ -606,5 +607,52 @@ namespace Ryujinx.Graphics.Shader.Translation
return descriptors;
}
public (TextureDescriptor, int) FindTextureDescriptor(AstTextureOperation texOp)
{
TextureDescriptor[] descriptors = GetTextureDescriptors();
for (int i = 0; i < descriptors.Length; i++)
{
var descriptor = descriptors[i];
if (descriptor.CbufSlot == texOp.CbufSlot &&
descriptor.HandleIndex == texOp.Handle &&
descriptor.Format == texOp.Format)
{
return (descriptor, i);
}
}
return (default, -1);
}
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
{
for (int i = 0; i < array.Length; i++)
{
var descriptor = array[i];
if (descriptor.Type == texOp.Type &&
descriptor.CbufSlot == texOp.CbufSlot &&
descriptor.HandleIndex == texOp.Handle &&
descriptor.Format == texOp.Format)
{
return i;
}
}
return -1;
}
public int FindTextureDescriptorIndex(AstTextureOperation texOp)
{
return FindDescriptorIndex(GetTextureDescriptors(), texOp);
}
public int FindImageDescriptorIndex(AstTextureOperation texOp)
{
return FindDescriptorIndex(GetImageDescriptors(), texOp);
}
}
}