Geometry shader emulation for macOS (#5551)
* Implement vertex and geometry shader conversion to compute * Call InitializeReservedCounts for compute too * PR feedback * Set clip distance mask for geometry and tessellation shaders too * Transform feedback emulation only for vertex
This commit is contained in:
parent
93d78f9ac4
commit
f09bba82b9
65 changed files with 3912 additions and 593 deletions
|
@ -0,0 +1,378 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
{
|
||||
class GeometryToCompute : ITransformPass
|
||||
{
|
||||
public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures)
|
||||
{
|
||||
return usedFeatures.HasFlag(FeatureFlags.VtgAsCompute);
|
||||
}
|
||||
|
||||
public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node)
|
||||
{
|
||||
if (context.Definitions.Stage != ShaderStage.Geometry)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
Operation operation = (Operation)node.Value;
|
||||
|
||||
LinkedListNode<INode> newNode = node;
|
||||
|
||||
switch (operation.Inst)
|
||||
{
|
||||
case Instruction.EmitVertex:
|
||||
newNode = GenerateEmitVertex(context.Definitions, context.ResourceManager, node);
|
||||
break;
|
||||
case Instruction.EndPrimitive:
|
||||
newNode = GenerateEndPrimitive(context.Definitions, context.ResourceManager, node);
|
||||
break;
|
||||
case Instruction.Load:
|
||||
if (operation.StorageKind == StorageKind.Input)
|
||||
{
|
||||
IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
|
||||
|
||||
if (TryGetOffset(context.ResourceManager, operation, StorageKind.Input, out int inputOffset))
|
||||
{
|
||||
Operand primVertex = ioVariable == IoVariable.UserDefined
|
||||
? operation.GetSource(2)
|
||||
: operation.GetSource(1);
|
||||
|
||||
Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, inputOffset, primVertex);
|
||||
|
||||
newNode = node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.StorageBuffer,
|
||||
operation.Dest,
|
||||
new[] { Const(context.ResourceManager.Reservations.VertexOutputStorageBufferBinding), Const(0), vertexElemOffset }));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ioVariable)
|
||||
{
|
||||
case IoVariable.InvocationId:
|
||||
newNode = GenerateInvocationId(node, operation.Dest);
|
||||
break;
|
||||
case IoVariable.PrimitiveId:
|
||||
newNode = GeneratePrimitiveId(context.ResourceManager, node, operation.Dest);
|
||||
break;
|
||||
case IoVariable.GlobalId:
|
||||
case IoVariable.SubgroupEqMask:
|
||||
case IoVariable.SubgroupGeMask:
|
||||
case IoVariable.SubgroupGtMask:
|
||||
case IoVariable.SubgroupLaneId:
|
||||
case IoVariable.SubgroupLeMask:
|
||||
case IoVariable.SubgroupLtMask:
|
||||
// Those are valid or expected for geometry shaders.
|
||||
break;
|
||||
default:
|
||||
context.GpuAccessor.Log($"Invalid input \"{ioVariable}\".");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (operation.StorageKind == StorageKind.Output)
|
||||
{
|
||||
if (TryGetOffset(context.ResourceManager, operation, StorageKind.Output, out int outputOffset))
|
||||
{
|
||||
newNode = node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.LocalMemory,
|
||||
operation.Dest,
|
||||
new[] { Const(context.ResourceManager.LocalVertexDataMemoryId), Const(outputOffset) }));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.GpuAccessor.Log($"Invalid output \"{(IoVariable)operation.GetSource(0).Value}\".");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Instruction.Store:
|
||||
if (operation.StorageKind == StorageKind.Output)
|
||||
{
|
||||
if (TryGetOffset(context.ResourceManager, operation, StorageKind.Output, out int outputOffset))
|
||||
{
|
||||
Operand value = operation.GetSource(operation.SourcesCount - 1);
|
||||
|
||||
newNode = node.List.AddBefore(node, new Operation(
|
||||
Instruction.Store,
|
||||
StorageKind.LocalMemory,
|
||||
(Operand)null,
|
||||
new[] { Const(context.ResourceManager.LocalVertexDataMemoryId), Const(outputOffset), value }));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.GpuAccessor.Log($"Invalid output \"{(IoVariable)operation.GetSource(0).Value}\".");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (newNode != node)
|
||||
{
|
||||
Utils.DeleteNode(node, operation);
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateEmitVertex(ShaderDefinitions definitions, ResourceManager resourceManager, LinkedListNode<INode> node)
|
||||
{
|
||||
int vbOutputBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding;
|
||||
int ibOutputBinding = resourceManager.Reservations.GeometryIndexOutputStorageBufferBinding;
|
||||
int stride = resourceManager.Reservations.OutputSizePerInvocation;
|
||||
|
||||
Operand outputPrimVertex = IncrementLocalMemory(node, resourceManager.LocalGeometryOutputVertexCountMemoryId);
|
||||
Operand baseVertexOffset = GenerateBaseOffset(
|
||||
resourceManager,
|
||||
node,
|
||||
definitions.MaxOutputVertices * definitions.ThreadsPerInputPrimitive,
|
||||
definitions.ThreadsPerInputPrimitive);
|
||||
Operand outputBaseVertex = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, outputBaseVertex, new[] { baseVertexOffset, outputPrimVertex }));
|
||||
|
||||
Operand outputPrimIndex = IncrementLocalMemory(node, resourceManager.LocalGeometryOutputIndexCountMemoryId);
|
||||
Operand baseIndexOffset = GenerateBaseOffset(
|
||||
resourceManager,
|
||||
node,
|
||||
definitions.GetGeometryOutputIndexBufferStride(),
|
||||
definitions.ThreadsPerInputPrimitive);
|
||||
Operand outputBaseIndex = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, outputBaseIndex, new[] { baseIndexOffset, outputPrimIndex }));
|
||||
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Store,
|
||||
StorageKind.StorageBuffer,
|
||||
null,
|
||||
new[] { Const(ibOutputBinding), Const(0), outputBaseIndex, outputBaseVertex }));
|
||||
|
||||
Operand baseOffset = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Multiply, baseOffset, new[] { outputBaseVertex, Const(stride) }));
|
||||
|
||||
LinkedListNode<INode> newNode = node;
|
||||
|
||||
for (int offset = 0; offset < stride; offset++)
|
||||
{
|
||||
Operand vertexOffset;
|
||||
|
||||
if (offset > 0)
|
||||
{
|
||||
vertexOffset = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, vertexOffset, new[] { baseOffset, Const(offset) }));
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexOffset = baseOffset;
|
||||
}
|
||||
|
||||
Operand value = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.LocalMemory,
|
||||
value,
|
||||
new[] { Const(resourceManager.LocalVertexDataMemoryId), Const(offset) }));
|
||||
|
||||
newNode = node.List.AddBefore(node, new Operation(
|
||||
Instruction.Store,
|
||||
StorageKind.StorageBuffer,
|
||||
null,
|
||||
new[] { Const(vbOutputBinding), Const(0), vertexOffset, value }));
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateEndPrimitive(ShaderDefinitions definitions, ResourceManager resourceManager, LinkedListNode<INode> node)
|
||||
{
|
||||
int ibOutputBinding = resourceManager.Reservations.GeometryIndexOutputStorageBufferBinding;
|
||||
|
||||
Operand outputPrimIndex = IncrementLocalMemory(node, resourceManager.LocalGeometryOutputIndexCountMemoryId);
|
||||
Operand baseIndexOffset = GenerateBaseOffset(
|
||||
resourceManager,
|
||||
node,
|
||||
definitions.GetGeometryOutputIndexBufferStride(),
|
||||
definitions.ThreadsPerInputPrimitive);
|
||||
Operand outputBaseIndex = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, outputBaseIndex, new[] { baseIndexOffset, outputPrimIndex }));
|
||||
|
||||
return node.List.AddBefore(node, new Operation(
|
||||
Instruction.Store,
|
||||
StorageKind.StorageBuffer,
|
||||
null,
|
||||
new[] { Const(ibOutputBinding), Const(0), outputBaseIndex, Const(-1) }));
|
||||
}
|
||||
|
||||
private static Operand GenerateBaseOffset(ResourceManager resourceManager, LinkedListNode<INode> node, int stride, int threadsPerInputPrimitive)
|
||||
{
|
||||
Operand primitiveId = Local();
|
||||
GeneratePrimitiveId(resourceManager, node, primitiveId);
|
||||
|
||||
Operand baseOffset = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Multiply, baseOffset, new[] { primitiveId, Const(stride) }));
|
||||
|
||||
Operand invocationId = Local();
|
||||
GenerateInvocationId(node, invocationId);
|
||||
|
||||
Operand invocationOffset = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Multiply, invocationOffset, new[] { invocationId, Const(stride / threadsPerInputPrimitive) }));
|
||||
|
||||
Operand combinedOffset = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, combinedOffset, new[] { baseOffset, invocationOffset }));
|
||||
|
||||
return combinedOffset;
|
||||
}
|
||||
|
||||
private static Operand IncrementLocalMemory(LinkedListNode<INode> node, int memoryId)
|
||||
{
|
||||
Operand oldValue = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.LocalMemory,
|
||||
oldValue,
|
||||
new[] { Const(memoryId) }));
|
||||
|
||||
Operand newValue = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, newValue, new[] { oldValue, Const(1) }));
|
||||
|
||||
node.List.AddBefore(node, new Operation(Instruction.Store, StorageKind.LocalMemory, null, new[] { Const(memoryId), newValue }));
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
private static Operand GenerateVertexOffset(
|
||||
ResourceManager resourceManager,
|
||||
LinkedListNode<INode> node,
|
||||
int elementOffset,
|
||||
Operand primVertex)
|
||||
{
|
||||
int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
|
||||
|
||||
Operand vertexCount = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.ConstantBuffer,
|
||||
vertexCount,
|
||||
new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexCounts), Const(0) }));
|
||||
|
||||
Operand primInputVertex = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.LocalMemory,
|
||||
primInputVertex,
|
||||
new[] { Const(resourceManager.LocalTopologyRemapMemoryId), primVertex }));
|
||||
|
||||
Operand instanceIndex = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.Input,
|
||||
instanceIndex,
|
||||
new[] { Const((int)IoVariable.GlobalId), Const(1) }));
|
||||
|
||||
Operand baseVertex = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Multiply, baseVertex, new[] { instanceIndex, vertexCount }));
|
||||
|
||||
Operand vertexIndex = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, vertexIndex, new[] { baseVertex, primInputVertex }));
|
||||
|
||||
Operand vertexBaseOffset = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Multiply,
|
||||
vertexBaseOffset,
|
||||
new[] { vertexIndex, Const(resourceManager.Reservations.InputSizePerInvocation) }));
|
||||
|
||||
Operand vertexElemOffset;
|
||||
|
||||
if (elementOffset != 0)
|
||||
{
|
||||
vertexElemOffset = Local();
|
||||
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, vertexElemOffset, new[] { vertexBaseOffset, Const(elementOffset) }));
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexElemOffset = vertexBaseOffset;
|
||||
}
|
||||
|
||||
return vertexElemOffset;
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GeneratePrimitiveId(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
|
||||
{
|
||||
int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
|
||||
|
||||
Operand vertexCount = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.ConstantBuffer,
|
||||
vertexCount,
|
||||
new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexCounts), Const(0) }));
|
||||
|
||||
Operand vertexIndex = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.Input,
|
||||
vertexIndex,
|
||||
new[] { Const((int)IoVariable.GlobalId), Const(0) }));
|
||||
|
||||
Operand instanceIndex = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.Input,
|
||||
instanceIndex,
|
||||
new[] { Const((int)IoVariable.GlobalId), Const(1) }));
|
||||
|
||||
Operand baseVertex = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Multiply, baseVertex, new[] { instanceIndex, vertexCount }));
|
||||
|
||||
return node.List.AddBefore(node, new Operation(Instruction.Add, dest, new[] { baseVertex, vertexIndex }));
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateInvocationId(LinkedListNode<INode> node, Operand dest)
|
||||
{
|
||||
return node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.Input,
|
||||
dest,
|
||||
new[] { Const((int)IoVariable.GlobalId), Const(2) }));
|
||||
}
|
||||
|
||||
private static bool TryGetOffset(ResourceManager resourceManager, Operation operation, StorageKind storageKind, out int outputOffset)
|
||||
{
|
||||
bool isStore = operation.Inst == Instruction.Store;
|
||||
|
||||
IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
|
||||
|
||||
bool isValidOutput;
|
||||
|
||||
if (ioVariable == IoVariable.UserDefined)
|
||||
{
|
||||
int lastIndex = operation.SourcesCount - (isStore ? 2 : 1);
|
||||
|
||||
int location = operation.GetSource(1).Value;
|
||||
int component = operation.GetSource(lastIndex).Value;
|
||||
|
||||
isValidOutput = resourceManager.Reservations.TryGetOffset(storageKind, location, component, out outputOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ResourceReservations.IsVectorOrArrayVariable(ioVariable))
|
||||
{
|
||||
int component = operation.GetSource(operation.SourcesCount - (isStore ? 2 : 1)).Value;
|
||||
|
||||
isValidOutput = resourceManager.Reservations.TryGetOffset(storageKind, ioVariable, component, out outputOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
isValidOutput = resourceManager.Reservations.TryGetOffset(storageKind, ioVariable, out outputOffset);
|
||||
}
|
||||
}
|
||||
|
||||
return isValidOutput;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -153,15 +153,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
|
||||
if (isBindless)
|
||||
if (isBindless || !resourceManager.TryGetCbufSlotAndHandleForTexture(texOp.Binding, out int cbufSlot, out int handle))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||
|
||||
(int cbufSlot, int handle) = resourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding);
|
||||
|
||||
bool isCoordNormalized = gpuAccessor.QueryTextureCoordNormalized(handle, cbufSlot);
|
||||
|
||||
if (isCoordNormalized || intCoords)
|
||||
|
@ -607,13 +605,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
|
||||
// We can't query the format of a bindless texture,
|
||||
// because the handle is unknown, it can have any format.
|
||||
if (texOp.Flags.HasFlag(TextureFlags.Bindless))
|
||||
if (texOp.Flags.HasFlag(TextureFlags.Bindless) || !resourceManager.TryGetCbufSlotAndHandleForTexture(texOp.Binding, out int cbufSlot, out int handle))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
(int cbufSlot, int handle) = resourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding);
|
||||
|
||||
TextureFormat format = gpuAccessor.QueryTextureFormat(handle, cbufSlot);
|
||||
|
||||
int maxPositive = format switch
|
||||
|
|
|
@ -14,6 +14,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
RunPass<SharedStoreSmallIntCas>(context);
|
||||
RunPass<SharedAtomicSignedCas>(context);
|
||||
RunPass<ShufflePass>(context);
|
||||
RunPass<VertexToCompute>(context);
|
||||
RunPass<GeometryToCompute>(context);
|
||||
}
|
||||
|
||||
private static void RunPass<T>(TransformContext context) where T : ITransformPass
|
||||
|
|
|
@ -0,0 +1,364 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||
{
|
||||
class VertexToCompute : ITransformPass
|
||||
{
|
||||
public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures)
|
||||
{
|
||||
return usedFeatures.HasFlag(FeatureFlags.VtgAsCompute);
|
||||
}
|
||||
|
||||
public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node)
|
||||
{
|
||||
if (context.Definitions.Stage != ShaderStage.Vertex)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
Operation operation = (Operation)node.Value;
|
||||
|
||||
LinkedListNode<INode> newNode = node;
|
||||
|
||||
if (operation.Inst == Instruction.Load && operation.StorageKind == StorageKind.Input)
|
||||
{
|
||||
Operand dest = operation.Dest;
|
||||
|
||||
switch ((IoVariable)operation.GetSource(0).Value)
|
||||
{
|
||||
case IoVariable.BaseInstance:
|
||||
newNode = GenerateBaseInstanceLoad(context.ResourceManager, node, dest);
|
||||
break;
|
||||
case IoVariable.BaseVertex:
|
||||
newNode = GenerateBaseVertexLoad(context.ResourceManager, node, dest);
|
||||
break;
|
||||
case IoVariable.InstanceId:
|
||||
newNode = GenerateInstanceIdLoad(node, dest);
|
||||
break;
|
||||
case IoVariable.InstanceIndex:
|
||||
newNode = GenerateInstanceIndexLoad(context.ResourceManager, node, dest);
|
||||
break;
|
||||
case IoVariable.VertexId:
|
||||
case IoVariable.VertexIndex:
|
||||
newNode = GenerateVertexIndexLoad(context.ResourceManager, node, dest);
|
||||
break;
|
||||
case IoVariable.UserDefined:
|
||||
int location = operation.GetSource(1).Value;
|
||||
int component = operation.GetSource(2).Value;
|
||||
|
||||
if (context.Definitions.IsAttributePacked(location))
|
||||
{
|
||||
bool needsSextNorm = context.Definitions.IsAttributePackedRgb10A2Signed(location);
|
||||
|
||||
Operand temp = needsSextNorm ? Local() : dest;
|
||||
Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, 0);
|
||||
|
||||
newNode = node.List.AddBefore(node, new TextureOperation(
|
||||
Instruction.TextureSample,
|
||||
SamplerType.TextureBuffer,
|
||||
TextureFormat.Unknown,
|
||||
TextureFlags.IntCoords,
|
||||
context.ResourceManager.Reservations.GetVertexBufferTextureBinding(location),
|
||||
1 << component,
|
||||
new[] { temp },
|
||||
new[] { vertexElemOffset }));
|
||||
|
||||
if (needsSextNorm)
|
||||
{
|
||||
bool sint = context.Definitions.IsAttributeSint(location);
|
||||
CopySignExtendedNormalized(node, component == 3 ? 2 : 10, !sint, dest, temp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand temp = component > 0 ? Local() : dest;
|
||||
Operand vertexElemOffset = GenerateVertexOffset(context.ResourceManager, node, location, component);
|
||||
|
||||
newNode = node.List.AddBefore(node, new TextureOperation(
|
||||
Instruction.TextureSample,
|
||||
SamplerType.TextureBuffer,
|
||||
TextureFormat.Unknown,
|
||||
TextureFlags.IntCoords,
|
||||
context.ResourceManager.Reservations.GetVertexBufferTextureBinding(location),
|
||||
1,
|
||||
new[] { temp },
|
||||
new[] { vertexElemOffset }));
|
||||
|
||||
if (component > 0)
|
||||
{
|
||||
newNode = CopyMasked(context.ResourceManager, newNode, location, component, dest, temp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IoVariable.GlobalId:
|
||||
case IoVariable.SubgroupEqMask:
|
||||
case IoVariable.SubgroupGeMask:
|
||||
case IoVariable.SubgroupGtMask:
|
||||
case IoVariable.SubgroupLaneId:
|
||||
case IoVariable.SubgroupLeMask:
|
||||
case IoVariable.SubgroupLtMask:
|
||||
// Those are valid or expected for vertex shaders.
|
||||
break;
|
||||
default:
|
||||
context.GpuAccessor.Log($"Invalid input \"{(IoVariable)operation.GetSource(0).Value}\".");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (operation.Inst == Instruction.Load && operation.StorageKind == StorageKind.Output)
|
||||
{
|
||||
if (TryGetOutputOffset(context.ResourceManager, operation, out int outputOffset))
|
||||
{
|
||||
newNode = node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.LocalMemory,
|
||||
operation.Dest,
|
||||
new[] { Const(context.ResourceManager.LocalVertexDataMemoryId), Const(outputOffset) }));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.GpuAccessor.Log($"Invalid output \"{(IoVariable)operation.GetSource(0).Value}\".");
|
||||
}
|
||||
}
|
||||
else if (operation.Inst == Instruction.Store && operation.StorageKind == StorageKind.Output)
|
||||
{
|
||||
if (TryGetOutputOffset(context.ResourceManager, operation, out int outputOffset))
|
||||
{
|
||||
Operand value = operation.GetSource(operation.SourcesCount - 1);
|
||||
|
||||
newNode = node.List.AddBefore(node, new Operation(
|
||||
Instruction.Store,
|
||||
StorageKind.LocalMemory,
|
||||
(Operand)null,
|
||||
new[] { Const(context.ResourceManager.LocalVertexDataMemoryId), Const(outputOffset), value }));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.GpuAccessor.Log($"Invalid output \"{(IoVariable)operation.GetSource(0).Value}\".");
|
||||
}
|
||||
}
|
||||
|
||||
if (newNode != node)
|
||||
{
|
||||
Utils.DeleteNode(node, operation);
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
private static Operand GenerateVertexOffset(ResourceManager resourceManager, LinkedListNode<INode> node, int location, int component)
|
||||
{
|
||||
int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
|
||||
|
||||
Operand vertexIdVr = Local();
|
||||
GenerateVertexIdVertexRateLoad(resourceManager, node, vertexIdVr);
|
||||
|
||||
Operand vertexIdIr = Local();
|
||||
GenerateVertexIdInstanceRateLoad(resourceManager, node, vertexIdIr);
|
||||
|
||||
Operand attributeOffset = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.ConstantBuffer,
|
||||
attributeOffset,
|
||||
new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexOffsets), Const(location), Const(0) }));
|
||||
|
||||
Operand isInstanceRate = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.ConstantBuffer,
|
||||
isInstanceRate,
|
||||
new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexOffsets), Const(location), Const(1) }));
|
||||
|
||||
Operand vertexId = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.ConditionalSelect,
|
||||
vertexId,
|
||||
new[] { isInstanceRate, vertexIdIr, vertexIdVr }));
|
||||
|
||||
Operand vertexStride = Local();
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.ConstantBuffer,
|
||||
vertexStride,
|
||||
new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexStrides), Const(location), Const(0) }));
|
||||
|
||||
Operand vertexBaseOffset = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Multiply, vertexBaseOffset, new[] { vertexId, vertexStride }));
|
||||
|
||||
Operand vertexOffset = Local();
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, vertexOffset, new[] { attributeOffset, vertexBaseOffset }));
|
||||
|
||||
Operand vertexElemOffset;
|
||||
|
||||
if (component != 0)
|
||||
{
|
||||
vertexElemOffset = Local();
|
||||
|
||||
node.List.AddBefore(node, new Operation(Instruction.Add, vertexElemOffset, new[] { vertexOffset, Const(component) }));
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexElemOffset = vertexOffset;
|
||||
}
|
||||
|
||||
return vertexElemOffset;
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> CopySignExtendedNormalized(LinkedListNode<INode> node, int bits, bool normalize, Operand dest, Operand src)
|
||||
{
|
||||
Operand leftShifted = Local();
|
||||
node = node.List.AddAfter(node, new Operation(
|
||||
Instruction.ShiftLeft,
|
||||
leftShifted,
|
||||
new[] { src, Const(32 - bits) }));
|
||||
|
||||
Operand rightShifted = normalize ? Local() : dest;
|
||||
node = node.List.AddAfter(node, new Operation(
|
||||
Instruction.ShiftRightS32,
|
||||
rightShifted,
|
||||
new[] { leftShifted, Const(32 - bits) }));
|
||||
|
||||
if (normalize)
|
||||
{
|
||||
Operand asFloat = Local();
|
||||
node = node.List.AddAfter(node, new Operation(Instruction.ConvertS32ToFP32, asFloat, new[] { rightShifted }));
|
||||
node = node.List.AddAfter(node, new Operation(
|
||||
Instruction.FP32 | Instruction.Multiply,
|
||||
dest,
|
||||
new[] { asFloat, ConstF(1f / (1 << (bits - 1))) }));
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> CopyMasked(
|
||||
ResourceManager resourceManager,
|
||||
LinkedListNode<INode> node,
|
||||
int location,
|
||||
int component,
|
||||
Operand dest,
|
||||
Operand src)
|
||||
{
|
||||
Operand componentExists = Local();
|
||||
int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
|
||||
node = node.List.AddAfter(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.ConstantBuffer,
|
||||
componentExists,
|
||||
new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexStrides), Const(location), Const(component) }));
|
||||
|
||||
return node.List.AddAfter(node, new Operation(
|
||||
Instruction.ConditionalSelect,
|
||||
dest,
|
||||
new[] { componentExists, src, ConstF(component == 3 ? 1f : 0f) }));
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateBaseVertexLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
|
||||
{
|
||||
int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
|
||||
|
||||
return node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.ConstantBuffer,
|
||||
dest,
|
||||
new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexCounts), Const(2) }));
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateBaseInstanceLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
|
||||
{
|
||||
int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
|
||||
|
||||
return node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.ConstantBuffer,
|
||||
dest,
|
||||
new[] { Const(vertexInfoCbBinding), Const((int)VertexInfoBufferField.VertexCounts), Const(3) }));
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateVertexIndexLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
|
||||
{
|
||||
Operand baseVertex = Local();
|
||||
Operand vertexId = Local();
|
||||
|
||||
GenerateBaseVertexLoad(resourceManager, node, baseVertex);
|
||||
GenerateVertexIdVertexRateLoad(resourceManager, node, vertexId);
|
||||
|
||||
return node.List.AddBefore(node, new Operation(Instruction.Add, dest, new[] { baseVertex, vertexId }));
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateInstanceIndexLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
|
||||
{
|
||||
Operand baseInstance = Local();
|
||||
Operand instanceId = Local();
|
||||
|
||||
GenerateBaseInstanceLoad(resourceManager, node, baseInstance);
|
||||
|
||||
node.List.AddBefore(node, new Operation(
|
||||
Instruction.Load,
|
||||
StorageKind.Input,
|
||||
instanceId,
|
||||
new[] { Const((int)IoVariable.GlobalId), Const(1) }));
|
||||
|
||||
return node.List.AddBefore(node, new Operation(Instruction.Add, dest, new[] { baseInstance, instanceId }));
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateVertexIdVertexRateLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
|
||||
{
|
||||
Operand[] sources = new Operand[] { Const(resourceManager.LocalVertexIndexVertexRateMemoryId) };
|
||||
|
||||
return node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.LocalMemory, dest, sources));
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateVertexIdInstanceRateLoad(ResourceManager resourceManager, LinkedListNode<INode> node, Operand dest)
|
||||
{
|
||||
Operand[] sources = new Operand[] { Const(resourceManager.LocalVertexIndexInstanceRateMemoryId) };
|
||||
|
||||
return node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.LocalMemory, dest, sources));
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> GenerateInstanceIdLoad(LinkedListNode<INode> node, Operand dest)
|
||||
{
|
||||
Operand[] sources = new Operand[] { Const((int)IoVariable.GlobalId), Const(1) };
|
||||
|
||||
return node.List.AddBefore(node, new Operation(Instruction.Load, StorageKind.Input, dest, sources));
|
||||
}
|
||||
|
||||
private static bool TryGetOutputOffset(ResourceManager resourceManager, Operation operation, out int outputOffset)
|
||||
{
|
||||
bool isStore = operation.Inst == Instruction.Store;
|
||||
|
||||
IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
|
||||
|
||||
bool isValidOutput;
|
||||
|
||||
if (ioVariable == IoVariable.UserDefined)
|
||||
{
|
||||
int lastIndex = operation.SourcesCount - (isStore ? 2 : 1);
|
||||
|
||||
int location = operation.GetSource(1).Value;
|
||||
int component = operation.GetSource(lastIndex).Value;
|
||||
|
||||
isValidOutput = resourceManager.Reservations.TryGetOffset(StorageKind.Output, location, component, out outputOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ResourceReservations.IsVectorOrArrayVariable(ioVariable))
|
||||
{
|
||||
int component = operation.GetSource(operation.SourcesCount - (isStore ? 2 : 1)).Value;
|
||||
|
||||
isValidOutput = resourceManager.Reservations.TryGetOffset(StorageKind.Output, ioVariable, component, out outputOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
isValidOutput = resourceManager.Reservations.TryGetOffset(StorageKind.Output, ioVariable, out outputOffset);
|
||||
}
|
||||
}
|
||||
|
||||
return isValidOutput;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue