mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-07-29 01:47:11 +02:00
Move solution and projects to src
This commit is contained in:
parent
cd124bda58
commit
cee7121058
3466 changed files with 55 additions and 55 deletions
409
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
Normal file
409
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
Normal file
|
@ -0,0 +1,409 @@
|
|||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using Spv.Generator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
using IrConsts = IntermediateRepresentation.IrConsts;
|
||||
using IrOperandType = IntermediateRepresentation.OperandType;
|
||||
|
||||
partial class CodeGenContext : Module
|
||||
{
|
||||
private const uint SpirvVersionMajor = 1;
|
||||
private const uint SpirvVersionMinor = 3;
|
||||
private const uint SpirvVersionRevision = 0;
|
||||
private const uint SpirvVersionPacked = (SpirvVersionMajor << 16) | (SpirvVersionMinor << 8) | SpirvVersionRevision;
|
||||
|
||||
public StructuredProgramInfo Info { get; }
|
||||
|
||||
public ShaderConfig Config { get; }
|
||||
|
||||
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; }
|
||||
public Instruction SharedMemory { get; set; }
|
||||
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
|
||||
public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>();
|
||||
public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>();
|
||||
public Dictionary<IoDefinition, Instruction> Inputs { get; } = new Dictionary<IoDefinition, Instruction>();
|
||||
public Dictionary<IoDefinition, Instruction> Outputs { get; } = new Dictionary<IoDefinition, Instruction>();
|
||||
public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();
|
||||
public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, 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>();
|
||||
private readonly Dictionary<int, (StructuredFunction, Instruction)> _functions = new Dictionary<int, (StructuredFunction, Instruction)>();
|
||||
|
||||
private class BlockState
|
||||
{
|
||||
private int _entryCount;
|
||||
private readonly List<Instruction> _labels = new List<Instruction>();
|
||||
|
||||
public Instruction GetNextLabel(CodeGenContext context)
|
||||
{
|
||||
return GetLabel(context, _entryCount);
|
||||
}
|
||||
|
||||
public Instruction GetNextLabelAutoIncrement(CodeGenContext context)
|
||||
{
|
||||
return GetLabel(context, _entryCount++);
|
||||
}
|
||||
|
||||
public Instruction GetLabel(CodeGenContext context, int index)
|
||||
{
|
||||
while (index >= _labels.Count)
|
||||
{
|
||||
_labels.Add(context.Label());
|
||||
}
|
||||
|
||||
return _labels[index];
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<AstBlock, BlockState> _labels = new Dictionary<AstBlock, BlockState>();
|
||||
|
||||
public Dictionary<AstBlock, (Instruction, Instruction)> LoopTargets { get; set; }
|
||||
|
||||
public AstBlock CurrentBlock { get; private set; }
|
||||
|
||||
public SpirvDelegates Delegates { get; }
|
||||
|
||||
public CodeGenContext(
|
||||
StructuredProgramInfo info,
|
||||
ShaderConfig config,
|
||||
GeneratorPool<Instruction> instPool,
|
||||
GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool)
|
||||
{
|
||||
Info = info;
|
||||
Config = config;
|
||||
|
||||
if (config.Stage == ShaderStage.Geometry)
|
||||
{
|
||||
InputTopology inPrimitive = config.GpuAccessor.QueryPrimitiveTopology();
|
||||
|
||||
InputVertices = inPrimitive switch
|
||||
{
|
||||
InputTopology.Points => 1,
|
||||
InputTopology.Lines => 2,
|
||||
InputTopology.LinesAdjacency => 2,
|
||||
InputTopology.Triangles => 3,
|
||||
InputTopology.TrianglesAdjacency => 3,
|
||||
_ => throw new InvalidOperationException($"Invalid input topology \"{inPrimitive}\".")
|
||||
};
|
||||
}
|
||||
|
||||
AddCapability(Capability.Shader);
|
||||
AddCapability(Capability.Float64);
|
||||
|
||||
SetMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450);
|
||||
|
||||
Delegates = new SpirvDelegates(this);
|
||||
}
|
||||
|
||||
public void StartFunction()
|
||||
{
|
||||
_locals.Clear();
|
||||
_localForArgs.Clear();
|
||||
_funcArgs.Clear();
|
||||
}
|
||||
|
||||
public void EnterBlock(AstBlock block)
|
||||
{
|
||||
CurrentBlock = block;
|
||||
AddLabel(GetBlockStateLazy(block).GetNextLabelAutoIncrement(this));
|
||||
}
|
||||
|
||||
public Instruction GetFirstLabel(AstBlock block)
|
||||
{
|
||||
return GetBlockStateLazy(block).GetLabel(this, 0);
|
||||
}
|
||||
|
||||
public Instruction GetNextLabel(AstBlock block)
|
||||
{
|
||||
return GetBlockStateLazy(block).GetNextLabel(this);
|
||||
}
|
||||
|
||||
private BlockState GetBlockStateLazy(AstBlock block)
|
||||
{
|
||||
if (!_labels.TryGetValue(block, out var blockState))
|
||||
{
|
||||
blockState = new BlockState();
|
||||
|
||||
_labels.Add(block, blockState);
|
||||
}
|
||||
|
||||
return blockState;
|
||||
}
|
||||
|
||||
public Instruction NewBlock()
|
||||
{
|
||||
var label = Label();
|
||||
Branch(label);
|
||||
AddLabel(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
public Instruction[] GetMainInterface()
|
||||
{
|
||||
var mainInterface = new List<Instruction>();
|
||||
|
||||
mainInterface.AddRange(Inputs.Values);
|
||||
mainInterface.AddRange(Outputs.Values);
|
||||
mainInterface.AddRange(InputsPerPatch.Values);
|
||||
mainInterface.AddRange(OutputsPerPatch.Values);
|
||||
|
||||
return mainInterface.ToArray();
|
||||
}
|
||||
|
||||
public void DeclareLocal(AstOperand local, Instruction spvLocal)
|
||||
{
|
||||
_locals.Add(local, spvLocal);
|
||||
}
|
||||
|
||||
public void DeclareLocalForArgs(int funcIndex, Instruction[] spvLocals)
|
||||
{
|
||||
_localForArgs.Add(funcIndex, spvLocals);
|
||||
}
|
||||
|
||||
public void DeclareArgument(int argIndex, Instruction spvLocal)
|
||||
{
|
||||
_funcArgs.Add(argIndex, spvLocal);
|
||||
}
|
||||
|
||||
public void DeclareFunction(int funcIndex, StructuredFunction function, Instruction spvFunc)
|
||||
{
|
||||
_functions.Add(funcIndex, (function, spvFunc));
|
||||
}
|
||||
|
||||
public Instruction GetFP32(IAstNode node)
|
||||
{
|
||||
return Get(AggregateType.FP32, node);
|
||||
}
|
||||
|
||||
public Instruction GetFP64(IAstNode node)
|
||||
{
|
||||
return Get(AggregateType.FP64, node);
|
||||
}
|
||||
|
||||
public Instruction GetS32(IAstNode node)
|
||||
{
|
||||
return Get(AggregateType.S32, node);
|
||||
}
|
||||
|
||||
public Instruction GetU32(IAstNode node)
|
||||
{
|
||||
return Get(AggregateType.U32, node);
|
||||
}
|
||||
|
||||
public Instruction Get(AggregateType type, IAstNode node)
|
||||
{
|
||||
if (node is AstOperation operation)
|
||||
{
|
||||
var opResult = Instructions.Generate(this, operation);
|
||||
return BitcastIfNeeded(type, opResult.Type, opResult.Value);
|
||||
}
|
||||
else if (node is AstOperand operand)
|
||||
{
|
||||
return operand.Type switch
|
||||
{
|
||||
IrOperandType.Argument => GetArgument(type, operand),
|
||||
IrOperandType.Constant => GetConstant(type, operand),
|
||||
IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
|
||||
IrOperandType.LocalVariable => GetLocal(type, operand),
|
||||
IrOperandType.Undefined => GetUndefined(type),
|
||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
throw new NotImplementedException(node.GetType().Name);
|
||||
}
|
||||
|
||||
public Instruction GetWithType(IAstNode node, out AggregateType type)
|
||||
{
|
||||
if (node is AstOperation operation)
|
||||
{
|
||||
var opResult = Instructions.Generate(this, operation);
|
||||
type = opResult.Type;
|
||||
return opResult.Value;
|
||||
}
|
||||
else if (node is AstOperand operand)
|
||||
{
|
||||
switch (operand.Type)
|
||||
{
|
||||
case IrOperandType.LocalVariable:
|
||||
type = operand.VarType;
|
||||
return GetLocal(type, operand);
|
||||
default:
|
||||
throw new ArgumentException($"Invalid operand type \"{operand.Type}\".");
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException(node.GetType().Name);
|
||||
}
|
||||
|
||||
private Instruction GetUndefined(AggregateType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
AggregateType.Bool => ConstantFalse(TypeBool()),
|
||||
AggregateType.FP32 => Constant(TypeFP32(), 0f),
|
||||
AggregateType.FP64 => Constant(TypeFP64(), 0d),
|
||||
_ => Constant(GetType(type), 0)
|
||||
};
|
||||
}
|
||||
|
||||
public Instruction GetConstant(AggregateType type, AstOperand operand)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
AggregateType.Bool => operand.Value != 0 ? ConstantTrue(TypeBool()) : ConstantFalse(TypeBool()),
|
||||
AggregateType.FP32 => Constant(TypeFP32(), BitConverter.Int32BitsToSingle(operand.Value)),
|
||||
AggregateType.FP64 => Constant(TypeFP64(), (double)BitConverter.Int32BitsToSingle(operand.Value)),
|
||||
AggregateType.S32 => Constant(TypeS32(), operand.Value),
|
||||
AggregateType.U32 => Constant(TypeU32(), (uint)operand.Value),
|
||||
_ => throw new ArgumentException($"Invalid type \"{type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
public Instruction GetConstantBuffer(AggregateType type, AstOperand operand)
|
||||
{
|
||||
var i1 = Constant(TypeS32(), 0);
|
||||
var i2 = Constant(TypeS32(), operand.CbufOffset >> 2);
|
||||
var i3 = Constant(TypeU32(), operand.CbufOffset & 3);
|
||||
|
||||
Instruction elemPointer;
|
||||
|
||||
if (UniformBuffersArray != null)
|
||||
{
|
||||
var ubVariable = UniformBuffersArray;
|
||||
var i0 = Constant(TypeS32(), operand.CbufSlot);
|
||||
|
||||
elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i0, i1, i2, i3);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ubVariable = UniformBuffers[operand.CbufSlot];
|
||||
|
||||
elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i1, i2, i3);
|
||||
}
|
||||
|
||||
return BitcastIfNeeded(type, AggregateType.FP32, Load(TypeFP32(), elemPointer));
|
||||
}
|
||||
|
||||
public Instruction GetLocalPointer(AstOperand local)
|
||||
{
|
||||
return _locals[local];
|
||||
}
|
||||
|
||||
public Instruction[] GetLocalForArgsPointers(int funcIndex)
|
||||
{
|
||||
return _localForArgs[funcIndex];
|
||||
}
|
||||
|
||||
public Instruction GetArgumentPointer(AstOperand funcArg)
|
||||
{
|
||||
return _funcArgs[funcArg.Value];
|
||||
}
|
||||
|
||||
public Instruction GetLocal(AggregateType dstType, AstOperand local)
|
||||
{
|
||||
var srcType = local.VarType;
|
||||
return BitcastIfNeeded(dstType, srcType, Load(GetType(srcType), GetLocalPointer(local)));
|
||||
}
|
||||
|
||||
public Instruction GetArgument(AggregateType dstType, AstOperand funcArg)
|
||||
{
|
||||
var srcType = funcArg.VarType;
|
||||
return BitcastIfNeeded(dstType, srcType, Load(GetType(srcType), GetArgumentPointer(funcArg)));
|
||||
}
|
||||
|
||||
public (StructuredFunction, Instruction) GetFunction(int funcIndex)
|
||||
{
|
||||
return _functions[funcIndex];
|
||||
}
|
||||
|
||||
public Instruction GetType(AggregateType type, int length = 1)
|
||||
{
|
||||
if ((type & AggregateType.Array) != 0)
|
||||
{
|
||||
return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length));
|
||||
}
|
||||
else if ((type & AggregateType.ElementCountMask) != 0)
|
||||
{
|
||||
int vectorLength = (type & AggregateType.ElementCountMask) switch
|
||||
{
|
||||
AggregateType.Vector2 => 2,
|
||||
AggregateType.Vector3 => 3,
|
||||
AggregateType.Vector4 => 4,
|
||||
_ => 1
|
||||
};
|
||||
|
||||
return TypeVector(GetType(type & ~AggregateType.ElementCountMask), vectorLength);
|
||||
}
|
||||
|
||||
return type switch
|
||||
{
|
||||
AggregateType.Void => TypeVoid(),
|
||||
AggregateType.Bool => TypeBool(),
|
||||
AggregateType.FP32 => TypeFP32(),
|
||||
AggregateType.FP64 => TypeFP64(),
|
||||
AggregateType.S32 => TypeS32(),
|
||||
AggregateType.U32 => TypeU32(),
|
||||
_ => throw new ArgumentException($"Invalid attribute type \"{type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
public Instruction BitcastIfNeeded(AggregateType dstType, AggregateType srcType, Instruction value)
|
||||
{
|
||||
if (dstType == srcType)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (dstType == AggregateType.Bool)
|
||||
{
|
||||
return INotEqual(TypeBool(), BitcastIfNeeded(AggregateType.S32, srcType, value), Constant(TypeS32(), 0));
|
||||
}
|
||||
else if (srcType == AggregateType.Bool)
|
||||
{
|
||||
var intTrue = Constant(TypeS32(), IrConsts.True);
|
||||
var intFalse = Constant(TypeS32(), IrConsts.False);
|
||||
|
||||
return BitcastIfNeeded(dstType, AggregateType.S32, Select(TypeS32(), value, intTrue, intFalse));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Bitcast(GetType(dstType, 1), value);
|
||||
}
|
||||
}
|
||||
|
||||
public Instruction TypeS32()
|
||||
{
|
||||
return TypeInt(32, true);
|
||||
}
|
||||
|
||||
public Instruction TypeU32()
|
||||
{
|
||||
return TypeInt(32, false);
|
||||
}
|
||||
|
||||
public Instruction TypeFP32()
|
||||
{
|
||||
return TypeFloat(32);
|
||||
}
|
||||
|
||||
public Instruction TypeFP64()
|
||||
{
|
||||
return TypeFloat(64);
|
||||
}
|
||||
}
|
||||
}
|
615
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
Normal file
615
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
Normal file
|
@ -0,0 +1,615 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using Spv.Generator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using static Spv.Specification;
|
||||
using SpvInstruction = Spv.Generator.Instruction;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
static class Declarations
|
||||
{
|
||||
private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
||||
|
||||
public static void DeclareParameters(CodeGenContext context, StructuredFunction function)
|
||||
{
|
||||
DeclareParameters(context, function.InArguments, 0);
|
||||
DeclareParameters(context, function.OutArguments, function.InArguments.Length);
|
||||
}
|
||||
|
||||
private static void DeclareParameters(CodeGenContext context, IEnumerable<AggregateType> argTypes, int argIndex)
|
||||
{
|
||||
foreach (var argType in argTypes)
|
||||
{
|
||||
var argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType));
|
||||
var spvArg = context.FunctionParameter(argPointerType);
|
||||
|
||||
context.DeclareArgument(argIndex++, spvArg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeclareLocals(CodeGenContext context, StructuredFunction function)
|
||||
{
|
||||
foreach (AstOperand local in function.Locals)
|
||||
{
|
||||
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType));
|
||||
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
|
||||
|
||||
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)
|
||||
{
|
||||
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
||||
{
|
||||
StructuredFunction function = functions[funcIndex];
|
||||
SpvInstruction[] locals = new SpvInstruction[function.InArguments.Length];
|
||||
|
||||
for (int i = 0; i < function.InArguments.Length; i++)
|
||||
{
|
||||
var type = function.GetArgumentType(i);
|
||||
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
|
||||
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
|
||||
|
||||
context.AddLocalVariable(spvLocal);
|
||||
|
||||
locals[i] = spvLocal;
|
||||
}
|
||||
|
||||
context.DeclareLocalForArgs(funcIndex, locals);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
if (context.Config.Stage == ShaderStage.Compute)
|
||||
{
|
||||
int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4);
|
||||
|
||||
if (localMemorySize != 0)
|
||||
{
|
||||
DeclareLocalMemory(context, localMemorySize);
|
||||
}
|
||||
|
||||
int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4);
|
||||
|
||||
if (sharedMemorySize != 0)
|
||||
{
|
||||
DeclareSharedMemory(context, sharedMemorySize);
|
||||
}
|
||||
}
|
||||
else if (context.Config.LocalMemorySize != 0)
|
||||
{
|
||||
int localMemorySize = BitUtils.DivRoundUp(context.Config.LocalMemorySize, 4);
|
||||
DeclareLocalMemory(context, localMemorySize);
|
||||
}
|
||||
|
||||
DeclareSupportBuffer(context);
|
||||
DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
|
||||
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
||||
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
||||
DeclareImages(context, context.Config.GetImageDescriptors());
|
||||
DeclareInputsAndOutputs(context, info);
|
||||
}
|
||||
|
||||
private static void DeclareLocalMemory(CodeGenContext context, int size)
|
||||
{
|
||||
context.LocalMemory = DeclareMemory(context, StorageClass.Private, size);
|
||||
}
|
||||
|
||||
private static void DeclareSharedMemory(CodeGenContext context, int size)
|
||||
{
|
||||
context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size);
|
||||
}
|
||||
|
||||
private static SpvInstruction DeclareMemory(CodeGenContext context, StorageClass storage, int size)
|
||||
{
|
||||
var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size));
|
||||
var pointerType = context.TypePointer(storage, arrayType);
|
||||
var variable = context.Variable(pointerType, storage);
|
||||
|
||||
context.AddGlobalVariable(variable);
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
private static void DeclareSupportBuffer(CodeGenContext context)
|
||||
{
|
||||
if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount));
|
||||
var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4);
|
||||
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, viewportInverseVectorType, 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.ViewportInverseOffset);
|
||||
context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
|
||||
context.MemberDecorate(supportBufferStructType, 4, 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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint ubSize = Constants.ConstantBufferSize / 16;
|
||||
|
||||
var ubArrayType = context.TypeArray(context.TypeVector(context.TypeFP32(), 4), context.Constant(context.TypeU32(), ubSize), true);
|
||||
context.Decorate(ubArrayType, Decoration.ArrayStride, (LiteralInteger)16);
|
||||
var ubStructType = context.TypeStruct(true, ubArrayType);
|
||||
context.Decorate(ubStructType, Decoration.Block);
|
||||
context.MemberDecorate(ubStructType, 0, Decoration.Offset, (LiteralInteger)0);
|
||||
|
||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
|
||||
{
|
||||
int count = descriptors.Max(x => x.Slot) + 1;
|
||||
|
||||
var ubStructArrayType = context.TypeArray(ubStructType, context.Constant(context.TypeU32(), count));
|
||||
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructArrayType);
|
||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
||||
|
||||
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_u");
|
||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstConstantBufferBinding);
|
||||
context.AddGlobalVariable(ubVariable);
|
||||
|
||||
context.UniformBuffersArray = ubVariable;
|
||||
}
|
||||
else
|
||||
{
|
||||
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
||||
|
||||
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_c{descriptor.Slot}");
|
||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
||||
context.AddGlobalVariable(ubVariable);
|
||||
context.UniformBuffers.Add(descriptor.Slot, ubVariable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
|
||||
{
|
||||
if (descriptors.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 1 : 0;
|
||||
int count = descriptors.Max(x => x.Slot) + 1;
|
||||
|
||||
var sbArrayType = context.TypeRuntimeArray(context.TypeU32());
|
||||
context.Decorate(sbArrayType, Decoration.ArrayStride, (LiteralInteger)4);
|
||||
var sbStructType = context.TypeStruct(true, sbArrayType);
|
||||
context.Decorate(sbStructType, Decoration.BufferBlock);
|
||||
context.MemberDecorate(sbStructType, 0, Decoration.Offset, (LiteralInteger)0);
|
||||
var sbStructArrayType = context.TypeArray(sbStructType, context.Constant(context.TypeU32(), count));
|
||||
var sbPointerType = context.TypePointer(StorageClass.Uniform, sbStructArrayType);
|
||||
var sbVariable = context.Variable(sbPointerType, StorageClass.Uniform);
|
||||
|
||||
context.Name(sbVariable, $"{GetStagePrefix(context.Config.Stage)}_s");
|
||||
context.Decorate(sbVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
||||
context.Decorate(sbVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstStorageBufferBinding);
|
||||
context.AddGlobalVariable(sbVariable);
|
||||
|
||||
context.StorageBuffersArray = sbVariable;
|
||||
}
|
||||
|
||||
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||
{
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format);
|
||||
|
||||
if (context.Samplers.ContainsKey(meta))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 2 : 0;
|
||||
|
||||
var dim = (descriptor.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 \"{descriptor.Type & SamplerType.Mask}\".")
|
||||
};
|
||||
|
||||
var imageType = context.TypeImage(
|
||||
context.TypeFP32(),
|
||||
dim,
|
||||
descriptor.Type.HasFlag(SamplerType.Shadow),
|
||||
descriptor.Type.HasFlag(SamplerType.Array),
|
||||
descriptor.Type.HasFlag(SamplerType.Multisample),
|
||||
1,
|
||||
ImageFormat.Unknown);
|
||||
|
||||
var nameSuffix = meta.CbufSlot < 0 ? $"_tcb_{meta.Handle:X}" : $"_cb{meta.CbufSlot}_{meta.Handle:X}";
|
||||
|
||||
var sampledImageType = context.TypeSampledImage(imageType);
|
||||
var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType);
|
||||
var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant);
|
||||
|
||||
context.Samplers.Add(meta, (imageType, sampledImageType, sampledImageVariable));
|
||||
context.SamplersTypes.Add(meta, descriptor.Type);
|
||||
|
||||
context.Name(sampledImageVariable, $"{GetStagePrefix(context.Config.Stage)}_tex{nameSuffix}");
|
||||
context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
||||
context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
||||
context.AddGlobalVariable(sampledImageVariable);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||
{
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format);
|
||||
|
||||
if (context.Images.ContainsKey(meta))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 3 : 0;
|
||||
|
||||
var dim = GetDim(descriptor.Type);
|
||||
|
||||
var imageType = context.TypeImage(
|
||||
context.GetType(meta.Format.GetComponentType()),
|
||||
dim,
|
||||
descriptor.Type.HasFlag(SamplerType.Shadow),
|
||||
descriptor.Type.HasFlag(SamplerType.Array),
|
||||
descriptor.Type.HasFlag(SamplerType.Multisample),
|
||||
AccessQualifier.ReadWrite,
|
||||
GetImageFormat(meta.Format));
|
||||
|
||||
var nameSuffix = meta.CbufSlot < 0 ?
|
||||
$"_tcb_{meta.Handle:X}_{meta.Format.ToGlslFormat()}" :
|
||||
$"_cb{meta.CbufSlot}_{meta.Handle:X}_{meta.Format.ToGlslFormat()}";
|
||||
|
||||
var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType);
|
||||
var imageVariable = context.Variable(imagePointerType, StorageClass.UniformConstant);
|
||||
|
||||
context.Images.Add(meta, (imageType, imageVariable));
|
||||
|
||||
context.Name(imageVariable, $"{GetStagePrefix(context.Config.Stage)}_img{nameSuffix}");
|
||||
context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
||||
context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
||||
|
||||
if (descriptor.Flags.HasFlag(TextureUsageFlags.ImageCoherent))
|
||||
{
|
||||
context.Decorate(imageVariable, Decoration.Coherent);
|
||||
}
|
||||
|
||||
context.AddGlobalVariable(imageVariable);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dim GetDim(SamplerType type)
|
||||
{
|
||||
return (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 ArgumentException($"Invalid sampler type \"{type & SamplerType.Mask}\".")
|
||||
};
|
||||
}
|
||||
|
||||
private static ImageFormat GetImageFormat(TextureFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
TextureFormat.Unknown => ImageFormat.Unknown,
|
||||
TextureFormat.R8Unorm => ImageFormat.R8,
|
||||
TextureFormat.R8Snorm => ImageFormat.R8Snorm,
|
||||
TextureFormat.R8Uint => ImageFormat.R8ui,
|
||||
TextureFormat.R8Sint => ImageFormat.R8i,
|
||||
TextureFormat.R16Float => ImageFormat.R16f,
|
||||
TextureFormat.R16Unorm => ImageFormat.R16,
|
||||
TextureFormat.R16Snorm => ImageFormat.R16Snorm,
|
||||
TextureFormat.R16Uint => ImageFormat.R16ui,
|
||||
TextureFormat.R16Sint => ImageFormat.R16i,
|
||||
TextureFormat.R32Float => ImageFormat.R32f,
|
||||
TextureFormat.R32Uint => ImageFormat.R32ui,
|
||||
TextureFormat.R32Sint => ImageFormat.R32i,
|
||||
TextureFormat.R8G8Unorm => ImageFormat.Rg8,
|
||||
TextureFormat.R8G8Snorm => ImageFormat.Rg8Snorm,
|
||||
TextureFormat.R8G8Uint => ImageFormat.Rg8ui,
|
||||
TextureFormat.R8G8Sint => ImageFormat.Rg8i,
|
||||
TextureFormat.R16G16Float => ImageFormat.Rg16f,
|
||||
TextureFormat.R16G16Unorm => ImageFormat.Rg16,
|
||||
TextureFormat.R16G16Snorm => ImageFormat.Rg16Snorm,
|
||||
TextureFormat.R16G16Uint => ImageFormat.Rg16ui,
|
||||
TextureFormat.R16G16Sint => ImageFormat.Rg16i,
|
||||
TextureFormat.R32G32Float => ImageFormat.Rg32f,
|
||||
TextureFormat.R32G32Uint => ImageFormat.Rg32ui,
|
||||
TextureFormat.R32G32Sint => ImageFormat.Rg32i,
|
||||
TextureFormat.R8G8B8A8Unorm => ImageFormat.Rgba8,
|
||||
TextureFormat.R8G8B8A8Snorm => ImageFormat.Rgba8Snorm,
|
||||
TextureFormat.R8G8B8A8Uint => ImageFormat.Rgba8ui,
|
||||
TextureFormat.R8G8B8A8Sint => ImageFormat.Rgba8i,
|
||||
TextureFormat.R16G16B16A16Float => ImageFormat.Rgba16f,
|
||||
TextureFormat.R16G16B16A16Unorm => ImageFormat.Rgba16,
|
||||
TextureFormat.R16G16B16A16Snorm => ImageFormat.Rgba16Snorm,
|
||||
TextureFormat.R16G16B16A16Uint => ImageFormat.Rgba16ui,
|
||||
TextureFormat.R16G16B16A16Sint => ImageFormat.Rgba16i,
|
||||
TextureFormat.R32G32B32A32Float => ImageFormat.Rgba32f,
|
||||
TextureFormat.R32G32B32A32Uint => ImageFormat.Rgba32ui,
|
||||
TextureFormat.R32G32B32A32Sint => ImageFormat.Rgba32i,
|
||||
TextureFormat.R10G10B10A2Unorm => ImageFormat.Rgb10A2,
|
||||
TextureFormat.R10G10B10A2Uint => ImageFormat.Rgb10a2ui,
|
||||
TextureFormat.R11G11B10Float => ImageFormat.R11fG11fB10f,
|
||||
_ => throw new ArgumentException($"Invalid texture format \"{format}\".")
|
||||
};
|
||||
}
|
||||
|
||||
private static void DeclareInputsAndOutputs(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
foreach (var ioDefinition in info.IoDefinitions)
|
||||
{
|
||||
var ioVariable = ioDefinition.IoVariable;
|
||||
|
||||
// Those are actually from constant buffer, rather than being actual inputs or outputs,
|
||||
// so we must ignore them here as they are declared as part of the support buffer.
|
||||
// TODO: Delete this after we represent this properly on the IR (as a constant buffer rather than "input").
|
||||
if (ioVariable == IoVariable.FragmentOutputIsBgra ||
|
||||
ioVariable == IoVariable.SupportBlockRenderScale ||
|
||||
ioVariable == IoVariable.SupportBlockViewInverse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isOutput = ioDefinition.StorageKind.IsOutput();
|
||||
bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
|
||||
|
||||
PixelImap iq = PixelImap.Unused;
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
if (ioVariable == IoVariable.UserDefined)
|
||||
{
|
||||
iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType();
|
||||
}
|
||||
else
|
||||
{
|
||||
(_, AggregateType varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
||||
AggregateType elemType = varType & AggregateType.ElementTypeMask;
|
||||
|
||||
if (elemType == AggregateType.S32 || elemType == AggregateType.U32)
|
||||
{
|
||||
iq = PixelImap.Constant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareInputOrOutput(CodeGenContext context, IoDefinition ioDefinition, bool isOutput, bool isPerPatch, PixelImap iq = PixelImap.Unused)
|
||||
{
|
||||
IoVariable ioVariable = ioDefinition.IoVariable;
|
||||
var storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
|
||||
|
||||
bool isBuiltIn;
|
||||
BuiltIn builtIn = default;
|
||||
AggregateType varType;
|
||||
|
||||
if (ioVariable == IoVariable.UserDefined)
|
||||
{
|
||||
varType = context.Config.GetUserDefinedType(ioDefinition.Location, isOutput);
|
||||
isBuiltIn = false;
|
||||
}
|
||||
else if (ioVariable == IoVariable.FragmentOutputColor)
|
||||
{
|
||||
varType = context.Config.GetFragmentOutputColorType(ioDefinition.Location);
|
||||
isBuiltIn = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
(builtIn, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
||||
isBuiltIn = true;
|
||||
|
||||
if (varType == AggregateType.Invalid)
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown variable {ioVariable}.");
|
||||
}
|
||||
}
|
||||
|
||||
bool hasComponent = context.Config.HasPerLocationInputOrOutputComponent(ioVariable, ioDefinition.Location, ioDefinition.Component, isOutput);
|
||||
|
||||
if (hasComponent)
|
||||
{
|
||||
varType &= AggregateType.ElementTypeMask;
|
||||
}
|
||||
else if (ioVariable == IoVariable.UserDefined && context.Config.HasTransformFeedbackOutputs(isOutput))
|
||||
{
|
||||
varType &= AggregateType.ElementTypeMask;
|
||||
varType |= context.Config.GetTransformFeedbackOutputComponents(ioDefinition.Location, ioDefinition.Component) switch
|
||||
{
|
||||
2 => AggregateType.Vector2,
|
||||
3 => AggregateType.Vector3,
|
||||
4 => AggregateType.Vector4,
|
||||
_ => AggregateType.Invalid
|
||||
};
|
||||
}
|
||||
|
||||
var spvType = context.GetType(varType, IoMap.GetSpirvBuiltInArrayLength(ioVariable));
|
||||
bool builtInPassthrough = false;
|
||||
|
||||
if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Config.Stage, isOutput))
|
||||
{
|
||||
int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
|
||||
spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
||||
|
||||
if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||
{
|
||||
builtInPassthrough = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.Config.Stage == ShaderStage.TessellationControl && isOutput && !isPerPatch)
|
||||
{
|
||||
spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
||||
}
|
||||
|
||||
var spvPointerType = context.TypePointer(storageClass, spvType);
|
||||
var spvVar = context.Variable(spvPointerType, storageClass);
|
||||
|
||||
if (builtInPassthrough)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||
}
|
||||
|
||||
if (isBuiltIn)
|
||||
{
|
||||
if (isPerPatch)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Patch);
|
||||
}
|
||||
|
||||
if (context.Config.GpuAccessor.QueryHostReducedPrecision() && ioVariable == IoVariable.Position)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Invariant);
|
||||
}
|
||||
|
||||
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)builtIn);
|
||||
}
|
||||
else if (isPerPatch)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Patch);
|
||||
|
||||
if (ioVariable == IoVariable.UserDefined)
|
||||
{
|
||||
int location = context.Config.GetPerPatchAttributeLocation(ioDefinition.Location);
|
||||
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
}
|
||||
}
|
||||
else if (ioVariable == IoVariable.UserDefined)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)ioDefinition.Location);
|
||||
|
||||
if (hasComponent)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)ioDefinition.Component);
|
||||
}
|
||||
|
||||
if (!isOutput &&
|
||||
!isPerPatch &&
|
||||
(context.Config.PassthroughAttributes & (1 << ioDefinition.Location)) != 0 &&
|
||||
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||
}
|
||||
}
|
||||
else if (ioVariable == IoVariable.FragmentOutputColor)
|
||||
{
|
||||
int location = ioDefinition.Location;
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
|
||||
{
|
||||
int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes);
|
||||
int index = location - firstLocation;
|
||||
int mask = 3 << firstLocation;
|
||||
|
||||
if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation);
|
||||
context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isOutput)
|
||||
{
|
||||
switch (iq)
|
||||
{
|
||||
case PixelImap.Constant:
|
||||
context.Decorate(spvVar, Decoration.Flat);
|
||||
break;
|
||||
case PixelImap.ScreenLinear:
|
||||
context.Decorate(spvVar, Decoration.NoPerspective);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (context.Config.TryGetTransformFeedbackOutput(
|
||||
ioVariable,
|
||||
ioDefinition.Location,
|
||||
ioDefinition.Component,
|
||||
out var transformFeedbackOutput))
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)transformFeedbackOutput.Buffer);
|
||||
context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)transformFeedbackOutput.Stride);
|
||||
context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)transformFeedbackOutput.Offset);
|
||||
}
|
||||
|
||||
context.AddGlobalVariable(spvVar);
|
||||
|
||||
var dict = isPerPatch
|
||||
? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch)
|
||||
: (isOutput ? context.Outputs : context.Inputs);
|
||||
dict.Add(ioDefinition, spvVar);
|
||||
}
|
||||
|
||||
private static string GetStagePrefix(ShaderStage stage)
|
||||
{
|
||||
return StagePrefixes[(int)stage];
|
||||
}
|
||||
}
|
||||
}
|
22
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs
Normal file
22
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
static class EnumConversion
|
||||
{
|
||||
public static ExecutionModel Convert(this ShaderStage stage)
|
||||
{
|
||||
return stage switch
|
||||
{
|
||||
ShaderStage.Compute => ExecutionModel.GLCompute,
|
||||
ShaderStage.Vertex => ExecutionModel.Vertex,
|
||||
ShaderStage.TessellationControl => ExecutionModel.TessellationControl,
|
||||
ShaderStage.TessellationEvaluation => ExecutionModel.TessellationEvaluation,
|
||||
ShaderStage.Geometry => ExecutionModel.Geometry,
|
||||
ShaderStage.Fragment => ExecutionModel.Fragment,
|
||||
_ => throw new ArgumentException($"Invalid shader stage \"{stage}\".")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
2480
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
Normal file
2480
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
Normal file
File diff suppressed because it is too large
Load diff
86
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs
Normal file
86
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/IoMap.cs
Normal file
|
@ -0,0 +1,86 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
static class IoMap
|
||||
{
|
||||
// At least 16 attributes are guaranteed by the spec.
|
||||
private const int MaxAttributes = 16;
|
||||
|
||||
public static (BuiltIn, AggregateType) GetSpirvBuiltIn(IoVariable ioVariable)
|
||||
{
|
||||
return ioVariable switch
|
||||
{
|
||||
IoVariable.BaseInstance => (BuiltIn.BaseInstance, AggregateType.S32),
|
||||
IoVariable.BaseVertex => (BuiltIn.BaseVertex, AggregateType.S32),
|
||||
IoVariable.ClipDistance => (BuiltIn.ClipDistance, AggregateType.Array | AggregateType.FP32),
|
||||
IoVariable.CtaId => (BuiltIn.WorkgroupId, AggregateType.Vector3 | AggregateType.U32),
|
||||
IoVariable.DrawIndex => (BuiltIn.DrawIndex, AggregateType.S32),
|
||||
IoVariable.FragmentCoord => (BuiltIn.FragCoord, AggregateType.Vector4 | AggregateType.FP32),
|
||||
IoVariable.FragmentOutputDepth => (BuiltIn.FragDepth, AggregateType.FP32),
|
||||
IoVariable.FrontFacing => (BuiltIn.FrontFacing, AggregateType.Bool),
|
||||
IoVariable.InstanceId => (BuiltIn.InstanceId, AggregateType.S32),
|
||||
IoVariable.InstanceIndex => (BuiltIn.InstanceIndex, AggregateType.S32),
|
||||
IoVariable.InvocationId => (BuiltIn.InvocationId, AggregateType.S32),
|
||||
IoVariable.Layer => (BuiltIn.Layer, AggregateType.S32),
|
||||
IoVariable.PatchVertices => (BuiltIn.PatchVertices, AggregateType.S32),
|
||||
IoVariable.PointCoord => (BuiltIn.PointCoord, AggregateType.Vector2 | AggregateType.FP32),
|
||||
IoVariable.PointSize => (BuiltIn.PointSize, AggregateType.FP32),
|
||||
IoVariable.Position => (BuiltIn.Position, AggregateType.Vector4 | AggregateType.FP32),
|
||||
IoVariable.PrimitiveId => (BuiltIn.PrimitiveId, AggregateType.S32),
|
||||
IoVariable.SubgroupEqMask => (BuiltIn.SubgroupEqMask, AggregateType.Vector4 | AggregateType.U32),
|
||||
IoVariable.SubgroupGeMask => (BuiltIn.SubgroupGeMask, AggregateType.Vector4 | AggregateType.U32),
|
||||
IoVariable.SubgroupGtMask => (BuiltIn.SubgroupGtMask, AggregateType.Vector4 | AggregateType.U32),
|
||||
IoVariable.SubgroupLaneId => (BuiltIn.SubgroupLocalInvocationId, AggregateType.U32),
|
||||
IoVariable.SubgroupLeMask => (BuiltIn.SubgroupLeMask, AggregateType.Vector4 | AggregateType.U32),
|
||||
IoVariable.SubgroupLtMask => (BuiltIn.SubgroupLtMask, AggregateType.Vector4 | AggregateType.U32),
|
||||
IoVariable.TessellationCoord => (BuiltIn.TessCoord, AggregateType.Vector3 | AggregateType.FP32),
|
||||
IoVariable.TessellationLevelInner => (BuiltIn.TessLevelInner, AggregateType.Array | AggregateType.FP32),
|
||||
IoVariable.TessellationLevelOuter => (BuiltIn.TessLevelOuter, AggregateType.Array | AggregateType.FP32),
|
||||
IoVariable.ThreadId => (BuiltIn.LocalInvocationId, AggregateType.Vector3 | AggregateType.U32),
|
||||
IoVariable.ThreadKill => (BuiltIn.HelperInvocation, AggregateType.Bool),
|
||||
IoVariable.VertexId => (BuiltIn.VertexId, AggregateType.S32),
|
||||
IoVariable.VertexIndex => (BuiltIn.VertexIndex, AggregateType.S32),
|
||||
IoVariable.ViewportIndex => (BuiltIn.ViewportIndex, AggregateType.S32),
|
||||
IoVariable.ViewportMask => (BuiltIn.ViewportMaskNV, AggregateType.Array | AggregateType.S32),
|
||||
_ => (default, AggregateType.Invalid)
|
||||
};
|
||||
}
|
||||
|
||||
public static int GetSpirvBuiltInArrayLength(IoVariable ioVariable)
|
||||
{
|
||||
return ioVariable switch
|
||||
{
|
||||
IoVariable.ClipDistance => 8,
|
||||
IoVariable.TessellationLevelInner => 2,
|
||||
IoVariable.TessellationLevelOuter => 4,
|
||||
IoVariable.ViewportMask => 1,
|
||||
IoVariable.UserDefined => MaxAttributes,
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsPerVertex(IoVariable ioVariable, ShaderStage stage, bool isOutput)
|
||||
{
|
||||
switch (ioVariable)
|
||||
{
|
||||
case IoVariable.Layer:
|
||||
case IoVariable.ViewportIndex:
|
||||
case IoVariable.PointSize:
|
||||
case IoVariable.Position:
|
||||
case IoVariable.UserDefined:
|
||||
case IoVariable.ClipDistance:
|
||||
case IoVariable.PointCoord:
|
||||
case IoVariable.ViewportMask:
|
||||
return !isOutput &&
|
||||
(stage == ShaderStage.TessellationControl ||
|
||||
stage == ShaderStage.TessellationEvaluation ||
|
||||
stage == ShaderStage.Geometry);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
19
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs
Normal file
19
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using Spv.Generator;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
readonly struct OperationResult
|
||||
{
|
||||
public static OperationResult Invalid => new OperationResult(AggregateType.Invalid, null);
|
||||
|
||||
public AggregateType Type { get; }
|
||||
public Instruction Value { get; }
|
||||
|
||||
public OperationResult(AggregateType type, Instruction value)
|
||||
{
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
227
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs
Normal file
227
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs
Normal file
|
@ -0,0 +1,227 @@
|
|||
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(), 4);
|
||||
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 passthroughLabel = context.Label();
|
||||
var finalMergeLabel = context.Label();
|
||||
|
||||
context.SelectionMerge(finalMergeLabel, SelectionControlMask.MaskNone);
|
||||
context.BranchConditional(passthrough, passthroughLabel, finalMergeLabel);
|
||||
|
||||
context.AddLabel(passthroughLabel);
|
||||
|
||||
context.Store(localVector, vector);
|
||||
context.Branch(finalMergeLabel);
|
||||
|
||||
context.AddLabel(finalMergeLabel);
|
||||
|
||||
return 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[new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)];
|
||||
var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer);
|
||||
var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1);
|
||||
|
||||
var scaleMod = context.FMod(vector2Type, fragCoordXY, scaleVector);
|
||||
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(), 4);
|
||||
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.GlslFAbs(context.TypeFP32(), 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(), sizeFloat, scale);
|
||||
var sizeUnscaledInt = context.ConvertFToS(context.TypeS32(), sizeUnscaled);
|
||||
|
||||
return context.Select(context.TypeS32(), passthrough, size, sizeUnscaledInt);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
}
|
226
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs
Normal file
226
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs
Normal file
|
@ -0,0 +1,226 @@
|
|||
using FuncBinaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
|
||||
using FuncQuaternaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
|
||||
using FuncTernaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
|
||||
using FuncUnaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate cache for SPIR-V instruction generators. Avoids delegate allocation when passing generators as arguments.
|
||||
/// </summary>
|
||||
internal readonly struct SpirvDelegates
|
||||
{
|
||||
// Unary
|
||||
public readonly FuncUnaryInstruction GlslFAbs;
|
||||
public readonly FuncUnaryInstruction GlslSAbs;
|
||||
public readonly FuncUnaryInstruction GlslCeil;
|
||||
public readonly FuncUnaryInstruction GlslCos;
|
||||
public readonly FuncUnaryInstruction GlslExp2;
|
||||
public readonly FuncUnaryInstruction GlslFloor;
|
||||
public readonly FuncUnaryInstruction GlslLog2;
|
||||
public readonly FuncUnaryInstruction FNegate;
|
||||
public readonly FuncUnaryInstruction SNegate;
|
||||
public readonly FuncUnaryInstruction GlslInverseSqrt;
|
||||
public readonly FuncUnaryInstruction GlslRoundEven;
|
||||
public readonly FuncUnaryInstruction GlslSin;
|
||||
public readonly FuncUnaryInstruction GlslSqrt;
|
||||
public readonly FuncUnaryInstruction GlslTrunc;
|
||||
|
||||
// UnaryBool
|
||||
public readonly FuncUnaryInstruction LogicalNot;
|
||||
|
||||
// UnaryFP32
|
||||
public readonly FuncUnaryInstruction DPdx;
|
||||
public readonly FuncUnaryInstruction DPdy;
|
||||
|
||||
// UnaryS32
|
||||
public readonly FuncUnaryInstruction BitCount;
|
||||
public readonly FuncUnaryInstruction BitReverse;
|
||||
public readonly FuncUnaryInstruction Not;
|
||||
|
||||
// Compare
|
||||
public readonly FuncBinaryInstruction FOrdEqual;
|
||||
public readonly FuncBinaryInstruction IEqual;
|
||||
public readonly FuncBinaryInstruction FOrdGreaterThan;
|
||||
public readonly FuncBinaryInstruction SGreaterThan;
|
||||
public readonly FuncBinaryInstruction FOrdGreaterThanEqual;
|
||||
public readonly FuncBinaryInstruction SGreaterThanEqual;
|
||||
public readonly FuncBinaryInstruction FOrdLessThan;
|
||||
public readonly FuncBinaryInstruction SLessThan;
|
||||
public readonly FuncBinaryInstruction FOrdLessThanEqual;
|
||||
public readonly FuncBinaryInstruction SLessThanEqual;
|
||||
public readonly FuncBinaryInstruction FOrdNotEqual;
|
||||
public readonly FuncBinaryInstruction INotEqual;
|
||||
|
||||
// CompareU32
|
||||
public readonly FuncBinaryInstruction UGreaterThanEqual;
|
||||
public readonly FuncBinaryInstruction UGreaterThan;
|
||||
public readonly FuncBinaryInstruction ULessThanEqual;
|
||||
public readonly FuncBinaryInstruction ULessThan;
|
||||
|
||||
// Binary
|
||||
public readonly FuncBinaryInstruction FAdd;
|
||||
public readonly FuncBinaryInstruction IAdd;
|
||||
public readonly FuncBinaryInstruction FDiv;
|
||||
public readonly FuncBinaryInstruction SDiv;
|
||||
public readonly FuncBinaryInstruction GlslFMax;
|
||||
public readonly FuncBinaryInstruction GlslSMax;
|
||||
public readonly FuncBinaryInstruction GlslFMin;
|
||||
public readonly FuncBinaryInstruction GlslSMin;
|
||||
public readonly FuncBinaryInstruction FMul;
|
||||
public readonly FuncBinaryInstruction IMul;
|
||||
public readonly FuncBinaryInstruction FSub;
|
||||
public readonly FuncBinaryInstruction ISub;
|
||||
|
||||
// BinaryBool
|
||||
public readonly FuncBinaryInstruction LogicalAnd;
|
||||
public readonly FuncBinaryInstruction LogicalNotEqual;
|
||||
public readonly FuncBinaryInstruction LogicalOr;
|
||||
|
||||
// BinaryS32
|
||||
public readonly FuncBinaryInstruction BitwiseAnd;
|
||||
public readonly FuncBinaryInstruction BitwiseXor;
|
||||
public readonly FuncBinaryInstruction BitwiseOr;
|
||||
public readonly FuncBinaryInstruction ShiftLeftLogical;
|
||||
public readonly FuncBinaryInstruction ShiftRightArithmetic;
|
||||
public readonly FuncBinaryInstruction ShiftRightLogical;
|
||||
|
||||
// BinaryU32
|
||||
public readonly FuncBinaryInstruction GlslUMax;
|
||||
public readonly FuncBinaryInstruction GlslUMin;
|
||||
|
||||
// AtomicMemoryBinary
|
||||
public readonly FuncQuaternaryInstruction AtomicIAdd;
|
||||
public readonly FuncQuaternaryInstruction AtomicAnd;
|
||||
public readonly FuncQuaternaryInstruction AtomicSMin;
|
||||
public readonly FuncQuaternaryInstruction AtomicUMin;
|
||||
public readonly FuncQuaternaryInstruction AtomicSMax;
|
||||
public readonly FuncQuaternaryInstruction AtomicUMax;
|
||||
public readonly FuncQuaternaryInstruction AtomicOr;
|
||||
public readonly FuncQuaternaryInstruction AtomicExchange;
|
||||
public readonly FuncQuaternaryInstruction AtomicXor;
|
||||
|
||||
// Ternary
|
||||
public readonly FuncTernaryInstruction GlslFClamp;
|
||||
public readonly FuncTernaryInstruction GlslSClamp;
|
||||
public readonly FuncTernaryInstruction GlslFma;
|
||||
|
||||
// TernaryS32
|
||||
public readonly FuncTernaryInstruction BitFieldSExtract;
|
||||
public readonly FuncTernaryInstruction BitFieldUExtract;
|
||||
|
||||
// TernaryU32
|
||||
public readonly FuncTernaryInstruction GlslUClamp;
|
||||
|
||||
// QuaternaryS32
|
||||
public readonly FuncQuaternaryInstruction BitFieldInsert;
|
||||
|
||||
public SpirvDelegates(CodeGenContext context)
|
||||
{
|
||||
// Unary
|
||||
GlslFAbs = context.GlslFAbs;
|
||||
GlslSAbs = context.GlslSAbs;
|
||||
GlslCeil = context.GlslCeil;
|
||||
GlslCos = context.GlslCos;
|
||||
GlslExp2 = context.GlslExp2;
|
||||
GlslFloor = context.GlslFloor;
|
||||
GlslLog2 = context.GlslLog2;
|
||||
FNegate = context.FNegate;
|
||||
SNegate = context.SNegate;
|
||||
GlslInverseSqrt = context.GlslInverseSqrt;
|
||||
GlslRoundEven = context.GlslRoundEven;
|
||||
GlslSin = context.GlslSin;
|
||||
GlslSqrt = context.GlslSqrt;
|
||||
GlslTrunc = context.GlslTrunc;
|
||||
|
||||
// UnaryBool
|
||||
LogicalNot = context.LogicalNot;
|
||||
|
||||
// UnaryFP32
|
||||
DPdx = context.DPdx;
|
||||
DPdy = context.DPdy;
|
||||
|
||||
// UnaryS32
|
||||
BitCount = context.BitCount;
|
||||
BitReverse = context.BitReverse;
|
||||
Not = context.Not;
|
||||
|
||||
// Compare
|
||||
FOrdEqual = context.FOrdEqual;
|
||||
IEqual = context.IEqual;
|
||||
FOrdGreaterThan = context.FOrdGreaterThan;
|
||||
SGreaterThan = context.SGreaterThan;
|
||||
FOrdGreaterThanEqual = context.FOrdGreaterThanEqual;
|
||||
SGreaterThanEqual = context.SGreaterThanEqual;
|
||||
FOrdLessThan = context.FOrdLessThan;
|
||||
SLessThan = context.SLessThan;
|
||||
FOrdLessThanEqual = context.FOrdLessThanEqual;
|
||||
SLessThanEqual = context.SLessThanEqual;
|
||||
FOrdNotEqual = context.FOrdNotEqual;
|
||||
INotEqual = context.INotEqual;
|
||||
|
||||
// CompareU32
|
||||
UGreaterThanEqual = context.UGreaterThanEqual;
|
||||
UGreaterThan = context.UGreaterThan;
|
||||
ULessThanEqual = context.ULessThanEqual;
|
||||
ULessThan = context.ULessThan;
|
||||
|
||||
// Binary
|
||||
FAdd = context.FAdd;
|
||||
IAdd = context.IAdd;
|
||||
FDiv = context.FDiv;
|
||||
SDiv = context.SDiv;
|
||||
GlslFMax = context.GlslFMax;
|
||||
GlslSMax = context.GlslSMax;
|
||||
GlslFMin = context.GlslFMin;
|
||||
GlslSMin = context.GlslSMin;
|
||||
FMul = context.FMul;
|
||||
IMul = context.IMul;
|
||||
FSub = context.FSub;
|
||||
ISub = context.ISub;
|
||||
|
||||
// BinaryBool
|
||||
LogicalAnd = context.LogicalAnd;
|
||||
LogicalNotEqual = context.LogicalNotEqual;
|
||||
LogicalOr = context.LogicalOr;
|
||||
|
||||
// BinaryS32
|
||||
BitwiseAnd = context.BitwiseAnd;
|
||||
BitwiseXor = context.BitwiseXor;
|
||||
BitwiseOr = context.BitwiseOr;
|
||||
ShiftLeftLogical = context.ShiftLeftLogical;
|
||||
ShiftRightArithmetic = context.ShiftRightArithmetic;
|
||||
ShiftRightLogical = context.ShiftRightLogical;
|
||||
|
||||
// BinaryU32
|
||||
GlslUMax = context.GlslUMax;
|
||||
GlslUMin = context.GlslUMin;
|
||||
|
||||
// AtomicMemoryBinary
|
||||
AtomicIAdd = context.AtomicIAdd;
|
||||
AtomicAnd = context.AtomicAnd;
|
||||
AtomicSMin = context.AtomicSMin;
|
||||
AtomicUMin = context.AtomicUMin;
|
||||
AtomicSMax = context.AtomicSMax;
|
||||
AtomicUMax = context.AtomicUMax;
|
||||
AtomicOr = context.AtomicOr;
|
||||
AtomicExchange = context.AtomicExchange;
|
||||
AtomicXor = context.AtomicXor;
|
||||
|
||||
// Ternary
|
||||
GlslFClamp = context.GlslFClamp;
|
||||
GlslSClamp = context.GlslSClamp;
|
||||
GlslFma = context.GlslFma;
|
||||
|
||||
// TernaryS32
|
||||
BitFieldSExtract = context.BitFieldSExtract;
|
||||
BitFieldUExtract = context.BitFieldUExtract;
|
||||
|
||||
// TernaryU32
|
||||
GlslUClamp = context.GlslUClamp;
|
||||
|
||||
// QuaternaryS32
|
||||
BitFieldInsert = context.BitFieldInsert;
|
||||
}
|
||||
}
|
||||
}
|
415
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
Normal file
415
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
Normal file
|
@ -0,0 +1,415 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
using SpvInstruction = Spv.Generator.Instruction;
|
||||
using SpvInstructionPool = Spv.Generator.GeneratorPool<Spv.Generator.Instruction>;
|
||||
using SpvLiteralInteger = Spv.Generator.LiteralInteger;
|
||||
using SpvLiteralIntegerPool = Spv.Generator.GeneratorPool<Spv.Generator.LiteralInteger>;
|
||||
|
||||
static class SpirvGenerator
|
||||
{
|
||||
// Resource pools for Spirv generation. Note: Increase count when more threads are being used.
|
||||
private const int GeneratorPoolCount = 1;
|
||||
private static ObjectPool<SpvInstructionPool> InstructionPool;
|
||||
private static ObjectPool<SpvLiteralIntegerPool> IntegerPool;
|
||||
private static object PoolLock;
|
||||
|
||||
static SpirvGenerator()
|
||||
{
|
||||
InstructionPool = new (() => new SpvInstructionPool(), GeneratorPoolCount);
|
||||
IntegerPool = new (() => new SpvLiteralIntegerPool(), GeneratorPoolCount);
|
||||
PoolLock = new object();
|
||||
}
|
||||
|
||||
private const HelperFunctionsMask NeedsInvocationIdMask =
|
||||
HelperFunctionsMask.Shuffle |
|
||||
HelperFunctionsMask.ShuffleDown |
|
||||
HelperFunctionsMask.ShuffleUp |
|
||||
HelperFunctionsMask.ShuffleXor |
|
||||
HelperFunctionsMask.SwizzleAdd;
|
||||
|
||||
public static byte[] Generate(StructuredProgramInfo info, ShaderConfig config)
|
||||
{
|
||||
SpvInstructionPool instPool;
|
||||
SpvLiteralIntegerPool integerPool;
|
||||
|
||||
lock (PoolLock)
|
||||
{
|
||||
instPool = InstructionPool.Allocate();
|
||||
integerPool = IntegerPool.Allocate();
|
||||
}
|
||||
|
||||
CodeGenContext context = new CodeGenContext(info, config, instPool, integerPool);
|
||||
|
||||
context.AddCapability(Capability.GroupNonUniformBallot);
|
||||
context.AddCapability(Capability.GroupNonUniformShuffle);
|
||||
context.AddCapability(Capability.GroupNonUniformVote);
|
||||
context.AddCapability(Capability.ImageBuffer);
|
||||
context.AddCapability(Capability.ImageGatherExtended);
|
||||
context.AddCapability(Capability.ImageQuery);
|
||||
context.AddCapability(Capability.SampledBuffer);
|
||||
|
||||
if (config.TransformFeedbackEnabled && config.LastInVertexPipeline)
|
||||
{
|
||||
context.AddCapability(Capability.TransformFeedback);
|
||||
}
|
||||
|
||||
if (config.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Input, IoVariable.Layer)))
|
||||
{
|
||||
context.AddCapability(Capability.Geometry);
|
||||
}
|
||||
|
||||
if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock())
|
||||
{
|
||||
context.AddCapability(Capability.FragmentShaderPixelInterlockEXT);
|
||||
context.AddExtension("SPV_EXT_fragment_shader_interlock");
|
||||
}
|
||||
}
|
||||
else if (config.Stage == ShaderStage.Geometry)
|
||||
{
|
||||
context.AddCapability(Capability.Geometry);
|
||||
|
||||
if (config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||
{
|
||||
context.AddExtension("SPV_NV_geometry_shader_passthrough");
|
||||
context.AddCapability(Capability.GeometryShaderPassthroughNV);
|
||||
}
|
||||
}
|
||||
else if (config.Stage == ShaderStage.TessellationControl || config.Stage == ShaderStage.TessellationEvaluation)
|
||||
{
|
||||
context.AddCapability(Capability.Tessellation);
|
||||
}
|
||||
else if (config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
context.AddCapability(Capability.DrawParameters);
|
||||
}
|
||||
|
||||
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Output, IoVariable.ViewportMask)))
|
||||
{
|
||||
context.AddExtension("SPV_NV_viewport_array2");
|
||||
context.AddCapability(Capability.ShaderViewportMaskNV);
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0)
|
||||
{
|
||||
info.IoDefinitions.Add(new IoDefinition(StorageKind.Input, IoVariable.SubgroupLaneId));
|
||||
}
|
||||
|
||||
Declarations.DeclareAll(context, info);
|
||||
|
||||
for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++)
|
||||
{
|
||||
var function = info.Functions[funcIndex];
|
||||
var retType = context.GetType(function.ReturnType);
|
||||
|
||||
var funcArgs = new SpvInstruction[function.InArguments.Length + function.OutArguments.Length];
|
||||
|
||||
for (int argIndex = 0; argIndex < funcArgs.Length; argIndex++)
|
||||
{
|
||||
var argType = context.GetType(function.GetArgumentType(argIndex));
|
||||
var argPointerType = context.TypePointer(StorageClass.Function, argType);
|
||||
funcArgs[argIndex] = argPointerType;
|
||||
}
|
||||
|
||||
var funcType = context.TypeFunction(retType, false, funcArgs);
|
||||
var spvFunc = context.Function(retType, FunctionControlMask.MaskNone, funcType);
|
||||
|
||||
context.DeclareFunction(funcIndex, function, spvFunc);
|
||||
}
|
||||
|
||||
for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++)
|
||||
{
|
||||
Generate(context, info, funcIndex);
|
||||
}
|
||||
|
||||
byte[] result = context.Generate();
|
||||
|
||||
lock (PoolLock)
|
||||
{
|
||||
InstructionPool.Release(instPool);
|
||||
IntegerPool.Release(integerPool);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex)
|
||||
{
|
||||
var function = info.Functions[funcIndex];
|
||||
|
||||
(_, var spvFunc) = context.GetFunction(funcIndex);
|
||||
|
||||
context.AddFunction(spvFunc);
|
||||
context.StartFunction();
|
||||
|
||||
Declarations.DeclareParameters(context, function);
|
||||
|
||||
context.EnterBlock(function.MainBlock);
|
||||
|
||||
Declarations.DeclareLocals(context, function);
|
||||
Declarations.DeclareLocalForArgs(context, info.Functions);
|
||||
|
||||
Generate(context, function.MainBlock);
|
||||
|
||||
// Functions must always end with a return.
|
||||
if (!(function.MainBlock.Last is AstOperation operation) ||
|
||||
(operation.Inst != Instruction.Return && operation.Inst != Instruction.Discard))
|
||||
{
|
||||
context.Return();
|
||||
}
|
||||
|
||||
context.FunctionEnd();
|
||||
|
||||
if (funcIndex == 0)
|
||||
{
|
||||
context.AddEntryPoint(context.Config.Stage.Convert(), spvFunc, "main", context.GetMainInterface());
|
||||
|
||||
if (context.Config.Stage == ShaderStage.TessellationControl)
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)context.Config.ThreadsPerInputPrimitive);
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.TessellationEvaluation)
|
||||
{
|
||||
switch (context.Config.GpuAccessor.QueryTessPatchType())
|
||||
{
|
||||
case TessPatchType.Isolines:
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.Isolines);
|
||||
break;
|
||||
case TessPatchType.Triangles:
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.Triangles);
|
||||
break;
|
||||
case TessPatchType.Quads:
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.Quads);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (context.Config.GpuAccessor.QueryTessSpacing())
|
||||
{
|
||||
case TessSpacing.EqualSpacing:
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.SpacingEqual);
|
||||
break;
|
||||
case TessSpacing.FractionalEventSpacing:
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.SpacingFractionalEven);
|
||||
break;
|
||||
case TessSpacing.FractionalOddSpacing:
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.SpacingFractionalOdd);
|
||||
break;
|
||||
}
|
||||
|
||||
bool tessCw = context.Config.GpuAccessor.QueryTessCw();
|
||||
|
||||
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
|
||||
{
|
||||
// We invert the front face on Vulkan backend, so we need to do that here as well.
|
||||
tessCw = !tessCw;
|
||||
}
|
||||
|
||||
if (tessCw)
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCw);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCcw);
|
||||
}
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.Geometry)
|
||||
{
|
||||
InputTopology inputTopology = context.Config.GpuAccessor.QueryPrimitiveTopology();
|
||||
|
||||
context.AddExecutionMode(spvFunc, inputTopology switch
|
||||
{
|
||||
InputTopology.Points => ExecutionMode.InputPoints,
|
||||
InputTopology.Lines => ExecutionMode.InputLines,
|
||||
InputTopology.LinesAdjacency => ExecutionMode.InputLinesAdjacency,
|
||||
InputTopology.Triangles => ExecutionMode.Triangles,
|
||||
InputTopology.TrianglesAdjacency => ExecutionMode.InputTrianglesAdjacency,
|
||||
_ => throw new InvalidOperationException($"Invalid input topology \"{inputTopology}\".")
|
||||
});
|
||||
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.Invocations, (SpvLiteralInteger)context.Config.ThreadsPerInputPrimitive);
|
||||
|
||||
context.AddExecutionMode(spvFunc, context.Config.OutputTopology switch
|
||||
{
|
||||
OutputTopology.PointList => ExecutionMode.OutputPoints,
|
||||
OutputTopology.LineStrip => ExecutionMode.OutputLineStrip,
|
||||
OutputTopology.TriangleStrip => ExecutionMode.OutputTriangleStrip,
|
||||
_ => throw new InvalidOperationException($"Invalid output topology \"{context.Config.OutputTopology}\".")
|
||||
});
|
||||
|
||||
int maxOutputVertices = context.Config.GpPassthrough ? context.InputVertices : context.Config.MaxOutputVertices;
|
||||
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)maxOutputVertices);
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, context.Config.Options.TargetApi == TargetApi.Vulkan
|
||||
? ExecutionMode.OriginUpperLeft
|
||||
: ExecutionMode.OriginLowerLeft);
|
||||
|
||||
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Output, IoVariable.FragmentOutputDepth)))
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.DepthReplacing);
|
||||
}
|
||||
|
||||
if (context.Config.GpuAccessor.QueryEarlyZForce())
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.EarlyFragmentTests);
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.FSI) != 0 &&
|
||||
context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock())
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.PixelInterlockOrderedEXT);
|
||||
}
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.Compute)
|
||||
{
|
||||
var localSizeX = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeX();
|
||||
var localSizeY = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeY();
|
||||
var localSizeZ = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeZ();
|
||||
|
||||
context.AddExecutionMode(
|
||||
spvFunc,
|
||||
ExecutionMode.LocalSize,
|
||||
localSizeX,
|
||||
localSizeY,
|
||||
localSizeZ);
|
||||
}
|
||||
|
||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, ExecutionMode.Xfb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Generate(CodeGenContext context, AstBlock block)
|
||||
{
|
||||
AstBlockVisitor visitor = new AstBlockVisitor(block);
|
||||
|
||||
var loopTargets = new Dictionary<AstBlock, (SpvInstruction, SpvInstruction)>();
|
||||
|
||||
context.LoopTargets = loopTargets;
|
||||
|
||||
visitor.BlockEntered += (sender, e) =>
|
||||
{
|
||||
AstBlock mergeBlock = e.Block.Parent;
|
||||
|
||||
if (e.Block.Type == AstBlockType.If)
|
||||
{
|
||||
AstBlock ifTrueBlock = e.Block;
|
||||
AstBlock ifFalseBlock;
|
||||
|
||||
if (AstHelper.Next(e.Block) is AstBlock nextBlock && nextBlock.Type == AstBlockType.Else)
|
||||
{
|
||||
ifFalseBlock = nextBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
ifFalseBlock = mergeBlock;
|
||||
}
|
||||
|
||||
var condition = context.Get(AggregateType.Bool, e.Block.Condition);
|
||||
|
||||
context.SelectionMerge(context.GetNextLabel(mergeBlock), SelectionControlMask.MaskNone);
|
||||
context.BranchConditional(condition, context.GetNextLabel(ifTrueBlock), context.GetNextLabel(ifFalseBlock));
|
||||
}
|
||||
else if (e.Block.Type == AstBlockType.DoWhile)
|
||||
{
|
||||
var continueTarget = context.Label();
|
||||
|
||||
loopTargets.Add(e.Block, (context.NewBlock(), continueTarget));
|
||||
|
||||
context.LoopMerge(context.GetNextLabel(mergeBlock), continueTarget, LoopControlMask.MaskNone);
|
||||
context.Branch(context.GetFirstLabel(e.Block));
|
||||
}
|
||||
|
||||
context.EnterBlock(e.Block);
|
||||
};
|
||||
|
||||
visitor.BlockLeft += (sender, e) =>
|
||||
{
|
||||
if (e.Block.Parent != null)
|
||||
{
|
||||
if (e.Block.Type == AstBlockType.DoWhile)
|
||||
{
|
||||
// This is a loop, we need to jump back to the loop header
|
||||
// if the condition is true.
|
||||
AstBlock mergeBlock = e.Block.Parent;
|
||||
|
||||
(var loopTarget, var continueTarget) = loopTargets[e.Block];
|
||||
|
||||
context.Branch(continueTarget);
|
||||
context.AddLabel(continueTarget);
|
||||
|
||||
var condition = context.Get(AggregateType.Bool, e.Block.Condition);
|
||||
|
||||
context.BranchConditional(condition, loopTarget, context.GetNextLabel(mergeBlock));
|
||||
}
|
||||
else
|
||||
{
|
||||
// We only need a branch if the last instruction didn't
|
||||
// already cause the program to exit or jump elsewhere.
|
||||
bool lastIsCf = e.Block.Last is AstOperation lastOp &&
|
||||
(lastOp.Inst == Instruction.Discard ||
|
||||
lastOp.Inst == Instruction.LoopBreak ||
|
||||
lastOp.Inst == Instruction.LoopContinue ||
|
||||
lastOp.Inst == Instruction.Return);
|
||||
|
||||
if (!lastIsCf)
|
||||
{
|
||||
context.Branch(context.GetNextLabel(e.Block.Parent));
|
||||
}
|
||||
}
|
||||
|
||||
bool hasElse = AstHelper.Next(e.Block) is AstBlock nextBlock &&
|
||||
(nextBlock.Type == AstBlockType.Else ||
|
||||
nextBlock.Type == AstBlockType.ElseIf);
|
||||
|
||||
// Re-enter the parent block.
|
||||
if (e.Block.Parent != null && !hasElse)
|
||||
{
|
||||
context.EnterBlock(e.Block.Parent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
foreach (IAstNode node in visitor.Visit())
|
||||
{
|
||||
if (node is AstAssignment assignment)
|
||||
{
|
||||
var dest = (AstOperand)assignment.Destination;
|
||||
|
||||
if (dest.Type == OperandType.LocalVariable)
|
||||
{
|
||||
var source = context.Get(dest.VarType, assignment.Source);
|
||||
context.Store(context.GetLocalPointer(dest), source);
|
||||
}
|
||||
else if (dest.Type == OperandType.Argument)
|
||||
{
|
||||
var source = context.Get(dest.VarType, assignment.Source);
|
||||
context.Store(context.GetArgumentPointer(dest), source);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(dest.Type.ToString());
|
||||
}
|
||||
}
|
||||
else if (node is AstOperation operation)
|
||||
{
|
||||
Instructions.Generate(context, operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
4
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs
Normal file
4
src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
readonly record struct TextureMeta(int CbufSlot, int Handle, TextureFormat Format);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue