Implement SSY/SYNC shader instructions (#382)

* Use a program counter to control shaders' flow

* Cleanup

* Implement SSY/SYNC

* Address feedback

* Fixup commentary

* Fixup Ssy instruction
This commit is contained in:
ReinUsesLisp 2018-08-31 13:14:04 -03:00 committed by gdkchan
parent 7cb6fd8090
commit 42dc925c3d
11 changed files with 313 additions and 268 deletions

View file

@ -155,18 +155,19 @@ namespace Ryujinx.Graphics.Gal.Shader
PrintDeclOutAttributes();
PrintDeclGprs();
PrintDeclPreds();
PrintDeclSsy();
if (BlocksB != null)
{
PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramAName + "()", IdentationStr);
PrintBlockScope(Blocks, GlslDecl.BasicBlockAName);
SB.AppendLine();
PrintBlockScope(BlocksB[0], null, null, "void " + GlslDecl.ProgramBName + "()", IdentationStr);
PrintBlockScope(BlocksB, GlslDecl.BasicBlockBName);
}
else
{
PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramName + "()", IdentationStr);
PrintBlockScope(Blocks, GlslDecl.BasicBlockName);
}
SB.AppendLine();
@ -357,6 +358,13 @@ namespace Ryujinx.Graphics.Gal.Shader
PrintDecls(Decl.Preds, "bool");
}
private void PrintDeclSsy()
{
SB.AppendLine("uint " + GlslDecl.SsyCursorName + ";");
SB.AppendLine("uint " + GlslDecl.SsyStackName + "[" + GlslDecl.SsyStackSize + "];" + Environment.NewLine);
}
private void PrintDecls(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, string CustomType = null, string Suffix = "")
{
foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector))
@ -432,14 +440,16 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
SB.AppendLine(IdentationStr + "uint pc;");
if (BlocksB != null)
{
SB.AppendLine(IdentationStr + GlslDecl.ProgramAName + "();");
SB.AppendLine(IdentationStr + GlslDecl.ProgramBName + "();");
PrintProgram(Blocks, GlslDecl.BasicBlockAName);
PrintProgram(BlocksB, GlslDecl.BasicBlockBName);
}
else
{
SB.AppendLine(IdentationStr + GlslDecl.ProgramName + "();");
PrintProgram(Blocks, GlslDecl.BasicBlockName);
}
if (Decl.ShaderType != GalShaderType.Geometry)
@ -477,6 +487,32 @@ namespace Ryujinx.Graphics.Gal.Shader
SB.AppendLine("}");
}
private void PrintProgram(ShaderIrBlock[] Blocks, string Name)
{
const string Ident1 = IdentationStr;
const string Ident2 = Ident1 + IdentationStr;
const string Ident3 = Ident2 + IdentationStr;
const string Ident4 = Ident3 + IdentationStr;
SB.AppendLine(Ident1 + "pc = " + GetBlockPosition(Blocks[0]) + ";");
SB.AppendLine(Ident1 + "do {");
SB.AppendLine(Ident2 + "switch (pc) {");
foreach (ShaderIrBlock Block in Blocks)
{
string FunctionName = Block.Position.ToString("x8");
SB.AppendLine(Ident3 + "case 0x" + FunctionName + ": pc = " + Name + "_" + FunctionName + "(); break;");
}
SB.AppendLine(Ident3 + "default:");
SB.AppendLine(Ident4 + "pc = 0;");
SB.AppendLine(Ident4 + "break;");
SB.AppendLine(Ident2 + "}");
SB.AppendLine(Ident1 + "} while (pc != 0);");
}
private void PrintAttrToOutput(string Identation = IdentationStr)
{
foreach (KeyValuePair<int, ShaderDeclInfo> KV in Decl.OutAttributes)
@ -510,193 +546,145 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
private void PrintBlockScope(
ShaderIrBlock Block,
ShaderIrBlock EndBlock,
ShaderIrBlock LoopBlock,
string ScopeName,
string Identation,
bool IsDoWhile = false)
private void PrintBlockScope(ShaderIrBlock[] Blocks, string Name)
{
string UpIdent = Identation.Substring(0, Identation.Length - IdentationStr.Length);
foreach (ShaderIrBlock Block in Blocks)
{
SB.AppendLine("uint " + Name + "_" + Block.Position.ToString("x8") + "() {");
if (IsDoWhile)
{
SB.AppendLine(UpIdent + "do {");
}
else
{
SB.AppendLine(UpIdent + ScopeName + " {");
}
PrintNodes(Block, Block.GetNodes());
while (Block != null && Block != EndBlock)
{
ShaderIrNode[] Nodes = Block.GetNodes();
Block = PrintNodes(Block, EndBlock, LoopBlock, Identation, Nodes);
}
if (IsDoWhile)
{
SB.AppendLine(UpIdent + "} " + ScopeName + ";");
}
else
{
SB.AppendLine(UpIdent + "}");
SB.AppendLine("}" + Environment.NewLine);
}
}
private ShaderIrBlock PrintNodes(
ShaderIrBlock Block,
ShaderIrBlock EndBlock,
ShaderIrBlock LoopBlock,
string Identation,
params ShaderIrNode[] Nodes)
private void PrintNode(ShaderIrBlock Block, ShaderIrNode Node, string Identation)
{
/*
* Notes about control flow and if-else/loop generation:
* The code assumes that the program has sane control flow,
* that is, there's no jumps to a location after another jump or
* jump target (except for the end of an if-else block), and backwards
* jumps to a location before the last loop dominator.
* Such cases needs to be transformed on a step before the GLSL code
* generation to ensure that we have sane graphs to work with.
* TODO: Such transformation is not yet implemented.
*/
string NewIdent = Identation + IdentationStr;
ShaderIrBlock LoopTail = GetLoopTailBlock(Block);
if (LoopTail != null && LoopBlock != Block)
if (Node is ShaderIrCond Cond)
{
//Shoock! kuma shock! We have a loop here!
//The entire sequence needs to be inside a do-while block.
ShaderIrBlock LoopEnd = GetDownBlock(LoopTail);
string IfExpr = GetSrcExpr(Cond.Pred, true);
PrintBlockScope(Block, LoopEnd, Block, "while (false)", NewIdent, IsDoWhile: true);
if (Cond.Not)
{
IfExpr = "!(" + IfExpr + ")";
}
return LoopEnd;
SB.AppendLine(Identation + "if (" + IfExpr + ") {");
if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra)
{
SB.AppendLine(Identation + IdentationStr + "return " + GetBlockPosition(Block.Branch) + ";");
}
else
{
PrintNode(Block, Cond.Child, Identation + IdentationStr);
}
SB.AppendLine(Identation + "}");
}
foreach (ShaderIrNode Node in Nodes)
else if (Node is ShaderIrAsg Asg)
{
if (Node is ShaderIrCond Cond)
if (IsValidOutOper(Asg.Dst))
{
string IfExpr = GetSrcExpr(Cond.Pred, true);
string Expr = GetSrcExpr(Asg.Src, true);
if (Cond.Not)
{
IfExpr = "!(" + IfExpr + ")";
}
Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra)
{
//Branch is a loop branch and would result in infinite recursion.
if (Block.Branch.Position <= Block.Position)
{
SB.AppendLine(Identation + "if (" + IfExpr + ") {");
SB.AppendLine(Identation + IdentationStr + "continue;");
SB.AppendLine(Identation + "}");
continue;
}
string SubScopeName = "if (!" + IfExpr + ")";
PrintBlockScope(Block.Next, Block.Branch, LoopBlock, SubScopeName, NewIdent);
ShaderIrBlock IfElseEnd = GetUpBlock(Block.Branch).Branch;
if (IfElseEnd?.Position > Block.Branch.Position)
{
PrintBlockScope(Block.Branch, IfElseEnd, LoopBlock, "else", NewIdent);
return IfElseEnd;
}
return Block.Branch;
}
else
{
SB.AppendLine(Identation + "if (" + IfExpr + ") {");
PrintNodes(Block, EndBlock, LoopBlock, NewIdent, Cond.Child);
SB.AppendLine(Identation + "}");
}
SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
}
else if (Node is ShaderIrAsg Asg)
}
else if (Node is ShaderIrOp Op)
{
switch (Op.Inst)
{
if (IsValidOutOper(Asg.Dst))
case ShaderIrInst.Bra:
{
string Expr = GetSrcExpr(Asg.Src, true);
SB.AppendLine(Identation + "return " + GetBlockPosition(Block.Branch) + ";");
Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
break;
}
SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
}
}
else if (Node is ShaderIrOp Op)
{
if (Op.Inst == ShaderIrInst.Bra)
{
if (Block.Branch.Position <= Block.Position)
{
SB.AppendLine(Identation + "continue;");
}
}
else if (Op.Inst == ShaderIrInst.Emit)
case ShaderIrInst.Emit:
{
PrintAttrToOutput(Identation);
SB.AppendLine(Identation + "EmitVertex();");
break;
}
else
case ShaderIrInst.Ssy:
{
string StackIndex = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]";
int TargetPosition = (Op.OperandA as ShaderIrOperImm).Value;
string Target = "0x" + TargetPosition.ToString("x8") + "u";
SB.AppendLine(Identation + StackIndex + " = " + Target + ";");
SB.AppendLine(Identation + GlslDecl.SsyCursorName + "++;");
break;
}
case ShaderIrInst.Sync:
{
SB.AppendLine(Identation + GlslDecl.SsyCursorName + "--;");
string Target = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]";
SB.AppendLine(Identation + "return " + Target + ";");
break;
}
default:
SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
}
}
else if (Node is ShaderIrCmnt Cmnt)
{
SB.AppendLine(Identation + "// " + Cmnt.Comment);
}
else
{
throw new InvalidOperationException();
break;
}
}
return Block.Next;
}
private ShaderIrBlock GetUpBlock(ShaderIrBlock Block)
{
return Blocks.FirstOrDefault(x => x.EndPosition == Block.Position);
}
private ShaderIrBlock GetDownBlock(ShaderIrBlock Block)
{
return Blocks.FirstOrDefault(x => x.Position == Block.EndPosition);
}
private ShaderIrBlock GetLoopTailBlock(ShaderIrBlock LoopHead)
{
ShaderIrBlock Tail = null;
foreach (ShaderIrBlock Block in LoopHead.Sources)
else if (Node is ShaderIrCmnt Cmnt)
{
if (Block.Position >= LoopHead.Position)
{
if (Tail == null || Tail.Position < Block.Position)
{
Tail = Block;
}
}
SB.AppendLine(Identation + "// " + Cmnt.Comment);
}
else
{
throw new InvalidOperationException();
}
}
private void PrintNodes(ShaderIrBlock Block, ShaderIrNode[] Nodes)
{
foreach (ShaderIrNode Node in Nodes)
{
PrintNode(Block, Node, IdentationStr);
}
return Tail;
if (Nodes.Length > 0)
{
ShaderIrNode Last = Nodes[Nodes.Length - 1];
bool UnconditionalFlowChange = false;
if (Last is ShaderIrOp Op)
{
switch (Op.Inst)
{
case ShaderIrInst.Bra:
case ShaderIrInst.Exit:
case ShaderIrInst.Kil:
case ShaderIrInst.Sync:
UnconditionalFlowChange = true;
break;
}
}
if (!UnconditionalFlowChange)
{
SB.AppendLine(IdentationStr + "return " + GetBlockPosition(Block.Next) + ";");
}
}
}
private bool IsValidOutOper(ShaderIrNode Node)
@ -1006,7 +994,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetCnumExpr(ShaderIrOp Op) => GetUnaryCall(Op, "!isnan");
private string GetExitExpr(ShaderIrOp Op) => "return";
private string GetExitExpr(ShaderIrOp Op) => "return 0u";
private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos");
@ -1351,5 +1339,17 @@ namespace Ryujinx.Graphics.Gal.Shader
throw new ArgumentException(nameof(Node));
}
private static string GetBlockPosition(ShaderIrBlock Block)
{
if (Block != null)
{
return "0x" + Block.Position.ToString("x8") + "u";
}
else
{
return "0u";
}
}
}
}