Fix for current framebuffer issues (#78)
[GPU] Fix some of the current framebuffer issues
This commit is contained in:
parent
262b5b8054
commit
c8c86a3854
23 changed files with 482 additions and 891 deletions
|
@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
public GlslProgram Decompile(int[] Code, GalShaderType ShaderType)
|
||||
{
|
||||
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, ShaderType);
|
||||
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0);
|
||||
|
||||
ShaderIrNode[] Nodes = Block.GetNodes();
|
||||
|
||||
|
|
|
@ -74,11 +74,9 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
for (int Ch = 0; Ch < 4; Ch++)
|
||||
{
|
||||
ShaderIrOperGpr Dst = (Ch >> 1) != 0
|
||||
? GetOperGpr28(OpCode)
|
||||
: GetOperGpr0 (OpCode);
|
||||
|
||||
Dst.Index += Ch & 1;
|
||||
//Assign it to a temp because the destination registers
|
||||
//may be used as texture coord input aswell.
|
||||
ShaderIrOperGpr Dst = new ShaderIrOperGpr(0x100 + Ch);
|
||||
|
||||
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
|
||||
|
||||
|
@ -86,6 +84,19 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
|
||||
}
|
||||
|
||||
for (int Ch = 0; Ch < 4; Ch++)
|
||||
{
|
||||
ShaderIrOperGpr Src = new ShaderIrOperGpr(0x100 + Ch);
|
||||
|
||||
ShaderIrOperGpr Dst = (Ch >> 1) != 0
|
||||
? GetOperGpr28(OpCode)
|
||||
: GetOperGpr0 (OpCode);
|
||||
|
||||
Dst.Index += Ch & 1;
|
||||
|
||||
Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{
|
||||
static class ShaderDecoder
|
||||
{
|
||||
public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, GalShaderType ShaderType)
|
||||
public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset)
|
||||
{
|
||||
ShaderIrBlock Block = new ShaderIrBlock();
|
||||
|
||||
|
@ -37,8 +37,6 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
}
|
||||
|
||||
Block.RunOptimizationPasses(ShaderType);
|
||||
|
||||
return Block;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,6 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Nodes.Add(Node);
|
||||
}
|
||||
|
||||
public void RunOptimizationPasses(GalShaderType ShaderType)
|
||||
{
|
||||
ShaderOptExprProp.Optimize(Nodes, ShaderType);
|
||||
}
|
||||
|
||||
public ShaderIrNode[] GetNodes()
|
||||
{
|
||||
return Nodes.ToArray();
|
||||
|
|
|
@ -1,366 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
static class ShaderOptExprProp
|
||||
{
|
||||
private struct UseSite
|
||||
{
|
||||
public ShaderIrNode Parent { get; private set; }
|
||||
public ShaderIrCond Cond { get; private set; }
|
||||
|
||||
public int UseIndex { get; private set; }
|
||||
|
||||
public int OperIndex { get; private set; }
|
||||
|
||||
public UseSite(
|
||||
ShaderIrNode Parent,
|
||||
ShaderIrCond Cond,
|
||||
int UseIndex,
|
||||
int OperIndex)
|
||||
{
|
||||
this.Parent = Parent;
|
||||
this.Cond = Cond;
|
||||
this.UseIndex = UseIndex;
|
||||
this.OperIndex = OperIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private class RegUse
|
||||
{
|
||||
public ShaderIrAsg Asg { get; private set; }
|
||||
|
||||
public int AsgIndex { get; private set; }
|
||||
|
||||
public int LastSiteIndex { get; private set; }
|
||||
|
||||
public ShaderIrCond Cond { get; private set; }
|
||||
|
||||
private List<UseSite> Sites;
|
||||
|
||||
public RegUse()
|
||||
{
|
||||
Sites = new List<UseSite>();
|
||||
}
|
||||
|
||||
public void AddUseSite(UseSite Site)
|
||||
{
|
||||
if (LastSiteIndex < Site.UseIndex)
|
||||
{
|
||||
LastSiteIndex = Site.UseIndex;
|
||||
}
|
||||
|
||||
Sites.Add(Site);
|
||||
}
|
||||
|
||||
public bool TryPropagate()
|
||||
{
|
||||
//This happens when a untiliazied register is used,
|
||||
//this usually indicates a decoding error, but may also
|
||||
//be caused by bogus programs (?). In any case, we just
|
||||
//keep the unitialized access and avoid trying to propagate
|
||||
//the expression (since we can't propagate what doesn't yet exist).
|
||||
if (Asg == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Cond != null)
|
||||
{
|
||||
//If the assignment is conditional, we can only propagate
|
||||
//to the use sites that shares the same condition of the assignment.
|
||||
foreach (UseSite Site in Sites)
|
||||
{
|
||||
if (!IsSameCondition(Cond, Site.Cond))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Sites.Count > 0)
|
||||
{
|
||||
foreach (UseSite Site in Sites)
|
||||
{
|
||||
if (Site.Parent is ShaderIrCond Cond)
|
||||
{
|
||||
switch (Site.OperIndex)
|
||||
{
|
||||
case 0: Cond.Pred = Asg.Src; break;
|
||||
case 1: Cond.Child = Asg.Src; break;
|
||||
|
||||
default: throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
else if (Site.Parent is ShaderIrOp Op)
|
||||
{
|
||||
switch (Site.OperIndex)
|
||||
{
|
||||
case 0: Op.OperandA = Asg.Src; break;
|
||||
case 1: Op.OperandB = Asg.Src; break;
|
||||
case 2: Op.OperandC = Asg.Src; break;
|
||||
|
||||
default: throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
else if (Site.Parent is ShaderIrAsg SiteAsg)
|
||||
{
|
||||
SiteAsg.Src = Asg.Src;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, ShaderIrCond Cond)
|
||||
{
|
||||
this.Asg = Asg;
|
||||
this.AsgIndex = AsgIndex;
|
||||
this.Cond = Cond;
|
||||
|
||||
LastSiteIndex = 0;
|
||||
|
||||
Sites.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Optimize(List<ShaderIrNode> Nodes, GalShaderType ShaderType)
|
||||
{
|
||||
Dictionary<int, RegUse> Uses = new Dictionary<int, RegUse>();
|
||||
|
||||
RegUse GetUse(int Key)
|
||||
{
|
||||
RegUse Use;
|
||||
|
||||
if (!Uses.TryGetValue(Key, out Use))
|
||||
{
|
||||
Use = new RegUse();
|
||||
|
||||
Uses.Add(Key, Use);
|
||||
}
|
||||
|
||||
return Use;
|
||||
}
|
||||
|
||||
int GetGprKey(int GprIndex)
|
||||
{
|
||||
return GprIndex;
|
||||
}
|
||||
|
||||
int GetPredKey(int PredIndex)
|
||||
{
|
||||
return PredIndex | 0x10000000;
|
||||
}
|
||||
|
||||
RegUse GetGprUse(int GprIndex)
|
||||
{
|
||||
return GetUse(GetGprKey(GprIndex));
|
||||
}
|
||||
|
||||
RegUse GetPredUse(int PredIndex)
|
||||
{
|
||||
return GetUse(GetPredKey(PredIndex));
|
||||
}
|
||||
|
||||
void RemoveUse(RegUse Use)
|
||||
{
|
||||
if (!Nodes.Remove((ShaderIrNode)Use.Cond ?? Use.Asg))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
void FindRegUses(
|
||||
List<(int, UseSite)> UseList,
|
||||
ShaderIrNode Parent,
|
||||
ShaderIrNode Node,
|
||||
ShaderIrCond CondNode,
|
||||
int UseIndex,
|
||||
int OperIndex = 0)
|
||||
{
|
||||
if (Node is ShaderIrAsg Asg)
|
||||
{
|
||||
FindRegUses(UseList, Asg, Asg.Src, CondNode, UseIndex);
|
||||
}
|
||||
else if (Node is ShaderIrCond Cond)
|
||||
{
|
||||
FindRegUses(UseList, Cond, Cond.Pred, CondNode, UseIndex, 0);
|
||||
FindRegUses(UseList, Cond, Cond.Child, CondNode, UseIndex, 1);
|
||||
}
|
||||
else if (Node is ShaderIrOp Op)
|
||||
{
|
||||
FindRegUses(UseList, Op, Op.OperandA, CondNode, UseIndex, 0);
|
||||
FindRegUses(UseList, Op, Op.OperandB, CondNode, UseIndex, 1);
|
||||
FindRegUses(UseList, Op, Op.OperandC, CondNode, UseIndex, 2);
|
||||
}
|
||||
else if (Node is ShaderIrOperGpr Gpr && !Gpr.IsConst)
|
||||
{
|
||||
UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
|
||||
}
|
||||
else if (Node is ShaderIrOperPred Pred)
|
||||
{
|
||||
UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
|
||||
}
|
||||
}
|
||||
|
||||
void TryAddRegUseSite(ShaderIrNode Node, ShaderIrCond CondNode, int UseIndex)
|
||||
{
|
||||
List<(int, UseSite)> UseList = new List<(int, UseSite)>();
|
||||
|
||||
FindRegUses(UseList, null, Node, CondNode, UseIndex);
|
||||
|
||||
foreach ((int Key, UseSite Site) in UseList)
|
||||
{
|
||||
GetUse(Key).AddUseSite(Site);
|
||||
}
|
||||
}
|
||||
|
||||
bool TryPropagate(RegUse Use)
|
||||
{
|
||||
//We can only propagate if the registers that the expression depends
|
||||
//on weren't assigned after the original expression assignment
|
||||
//to a register took place. We traverse the expression tree to find
|
||||
//all registers being used, if any of those registers was assigned
|
||||
//after the assignment to be propagated, then we can't propagate.
|
||||
if (Use?.Asg == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<(int, UseSite)> UseList = new List<(int, UseSite)>();
|
||||
|
||||
if (Use.Cond != null)
|
||||
{
|
||||
FindRegUses(UseList, null, Use.Cond, null, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FindRegUses(UseList, Use.Asg, Use.Asg.Src, null, 0);
|
||||
}
|
||||
|
||||
foreach ((int Key, UseSite Site) in UseList)
|
||||
{
|
||||
//TODO: Build an assignment list inside RegUse,
|
||||
//and check if there is an assignment inside the
|
||||
//range of Use.AsgIndex and Use.LastSiteIndex,
|
||||
//and if that's the case, then we should return false.
|
||||
//The current method is too conservative.
|
||||
if (GetUse(Key).AsgIndex >= Use.AsgIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Use.TryPropagate();
|
||||
}
|
||||
|
||||
for (int Index = 0, IterCount = 0; Index < Nodes.Count; Index++, IterCount++)
|
||||
{
|
||||
ShaderIrNode Node = Nodes[Index];
|
||||
|
||||
ShaderIrCond CondNode = null;
|
||||
|
||||
if (Node is ShaderIrCond)
|
||||
{
|
||||
CondNode = (ShaderIrCond)Node;
|
||||
}
|
||||
|
||||
TryAddRegUseSite(Node, CondNode, IterCount);;
|
||||
|
||||
while (Node is ShaderIrCond Cond)
|
||||
{
|
||||
Node = Cond.Child;
|
||||
}
|
||||
|
||||
if (!(Node is ShaderIrAsg Asg))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RegUse Use = null;
|
||||
|
||||
if (Asg.Dst is ShaderIrOperGpr Gpr && !Gpr.IsConst)
|
||||
{
|
||||
Use = GetGprUse(Gpr.Index);
|
||||
}
|
||||
else if (Asg.Dst is ShaderIrOperPred Pred)
|
||||
{
|
||||
Use = GetPredUse(Pred.Index);
|
||||
}
|
||||
|
||||
bool CanRemoveAsg = CondNode == null;
|
||||
|
||||
CanRemoveAsg |= IsSameCondition(CondNode, Use?.Cond);
|
||||
|
||||
if (CanRemoveAsg && TryPropagate(Use))
|
||||
{
|
||||
RemoveUse(Use);
|
||||
|
||||
//Note: Only decrement if the removal was successful.
|
||||
//RemoveUse throws when this is not the case so we should be good.
|
||||
Index--;
|
||||
}
|
||||
|
||||
//All nodes inside conditional nodes can't be propagated,
|
||||
//as we don't even know if they will be executed to begin with.
|
||||
Use?.SetNewAsg(Asg, IterCount, CondNode);
|
||||
}
|
||||
|
||||
foreach (RegUse Use in Uses.Values)
|
||||
{
|
||||
//Gprs 0-3 are the color output on fragment shaders,
|
||||
//so we can't remove the last assignments to those registers.
|
||||
if (ShaderType == GalShaderType.Fragment)
|
||||
{
|
||||
if (Use.Asg?.Dst is ShaderIrOperGpr Gpr && Gpr.Index < 4)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (TryPropagate(Use))
|
||||
{
|
||||
RemoveUse(Use);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsSameCondition(ShaderIrCond CondA, ShaderIrCond CondB)
|
||||
{
|
||||
if (CondA == null || CondB == null)
|
||||
{
|
||||
return CondA == CondB;
|
||||
}
|
||||
|
||||
if (CondA.Not != CondB.Not)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CondA.Pred is ShaderIrOperPred PredA)
|
||||
{
|
||||
if (!(CondB.Pred is ShaderIrOperPred PredB))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PredA.Index != PredB.Index)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (CondA.Pred != CondB.Pred)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue