diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs')
| -rw-r--r-- | src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs new file mode 100644 index 00000000..53f1e847 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs @@ -0,0 +1,185 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation +{ + static class ShaderIdentifier + { + public static ShaderIdentification Identify(Function[] functions, ShaderConfig config) + { + if (config.Stage == ShaderStage.Geometry && + config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles && + !config.GpuAccessor.QueryHostSupportsGeometryShader() && + IsLayerPassthroughGeometryShader(functions, out int layerInputAttr)) + { + config.SetGeometryShaderLayerInputAttribute(layerInputAttr); + + return ShaderIdentification.GeometryLayerPassthrough; + } + + return ShaderIdentification.None; + } + + private static bool IsLayerPassthroughGeometryShader(Function[] functions, out int layerInputAttr) + { + bool writesLayer = false; + layerInputAttr = 0; + + if (functions.Length != 1) + { + return false; + } + + int verticesCount = 0; + int totalVerticesCount = 0; + + foreach (BasicBlock block in functions[0].Blocks) + { + // We are not expecting loops or any complex control flow here, so fail in those cases. + if (block.Branch != null && block.Branch.Index <= block.Index) + { + return false; + } + + foreach (INode node in block.Operations) + { + if (!(node is Operation operation)) + { + continue; + } + + if (IsResourceWrite(operation.Inst)) + { + return false; + } + + if (operation.Inst == Instruction.Store && operation.StorageKind == StorageKind.Output) + { + Operand src = operation.GetSource(operation.SourcesCount - 1); + Operation srcAttributeAsgOp = null; + + if (src.Type == OperandType.LocalVariable && + src.AsgOp is Operation asgOp && + asgOp.Inst == Instruction.Load && + asgOp.StorageKind.IsInputOrOutput()) + { + if (asgOp.StorageKind != StorageKind.Input) + { + return false; + } + + srcAttributeAsgOp = asgOp; + } + + if (srcAttributeAsgOp != null) + { + IoVariable dstAttribute = (IoVariable)operation.GetSource(0).Value; + IoVariable srcAttribute = (IoVariable)srcAttributeAsgOp.GetSource(0).Value; + + if (dstAttribute == IoVariable.Layer && srcAttribute == IoVariable.UserDefined) + { + if (srcAttributeAsgOp.SourcesCount != 4) + { + return false; + } + + writesLayer = true; + layerInputAttr = srcAttributeAsgOp.GetSource(1).Value * 4 + srcAttributeAsgOp.GetSource(3).Value;; + } + else + { + if (dstAttribute != srcAttribute) + { + return false; + } + + int inputsCount = operation.SourcesCount - 2; + + if (dstAttribute == IoVariable.UserDefined) + { + if (operation.GetSource(1).Value != srcAttributeAsgOp.GetSource(1).Value) + { + return false; + } + + inputsCount--; + } + + for (int i = 0; i < inputsCount; i++) + { + int dstIndex = operation.SourcesCount - 2 - i; + int srcIndex = srcAttributeAsgOp.SourcesCount - 1 - i; + + if ((dstIndex | srcIndex) < 0) + { + return false; + } + + if (operation.GetSource(dstIndex).Type != OperandType.Constant || + srcAttributeAsgOp.GetSource(srcIndex).Type != OperandType.Constant || + operation.GetSource(dstIndex).Value != srcAttributeAsgOp.GetSource(srcIndex).Value) + { + return false; + } + } + } + } + else if (src.Type == OperandType.Constant) + { + int dstComponent = operation.GetSource(operation.SourcesCount - 2).Value; + float expectedValue = dstComponent == 3 ? 1f : 0f; + + if (src.AsFloat() != expectedValue) + { + return false; + } + } + else + { + return false; + } + } + else if (operation.Inst == Instruction.EmitVertex) + { + verticesCount++; + } + else if (operation.Inst == Instruction.EndPrimitive) + { + totalVerticesCount += verticesCount; + verticesCount = 0; + } + } + } + + return totalVerticesCount + verticesCount == 3 && writesLayer; + } + + private static bool IsResourceWrite(Instruction inst) + { + switch (inst) + { + case Instruction.AtomicAdd: + case Instruction.AtomicAnd: + case Instruction.AtomicCompareAndSwap: + case Instruction.AtomicMaxS32: + case Instruction.AtomicMaxU32: + case Instruction.AtomicMinS32: + case Instruction.AtomicMinU32: + case Instruction.AtomicOr: + case Instruction.AtomicSwap: + case Instruction.AtomicXor: + case Instruction.ImageAtomic: + case Instruction.ImageStore: + case Instruction.StoreGlobal: + case Instruction.StoreGlobal16: + case Instruction.StoreGlobal8: + case Instruction.StoreStorage: + case Instruction.StoreStorage16: + case Instruction.StoreStorage8: + return true; + } + + return false; + } + } +} |
