Refactoring OGLRenderer rasterizer, some cleanup on the glsl decompiler

This commit is contained in:
gdkchan 2018-03-31 23:58:26 -03:00
commit 5222708e37
13 changed files with 666 additions and 581 deletions

View file

@ -0,0 +1,15 @@
using System;
namespace Ryujinx.Graphics.Gal
{
[Flags]
public enum GalClearBufferFlags
{
Depth = 1 << 0,
Stencil = 1 << 1,
ColorRed = 1 << 2,
ColorGreen = 1 << 3,
ColorBlue = 1 << 4,
ColorAlpha = 1 << 5
}
}

View file

@ -2,8 +2,6 @@ namespace Ryujinx.Graphics.Gal
{ {
public struct GalVertexAttrib public struct GalVertexAttrib
{ {
public int Index { get; private set; }
public int Buffer { get; private set; }
public bool IsConst { get; private set; } public bool IsConst { get; private set; }
public int Offset { get; private set; } public int Offset { get; private set; }
@ -13,16 +11,12 @@ namespace Ryujinx.Graphics.Gal
public bool IsBgra { get; private set; } public bool IsBgra { get; private set; }
public GalVertexAttrib( public GalVertexAttrib(
int Index,
int Buffer,
bool IsConst, bool IsConst,
int Offset, int Offset,
GalVertexAttribSize Size, GalVertexAttribSize Size,
GalVertexAttribType Type, GalVertexAttribType Type,
bool IsBgra) bool IsBgra)
{ {
this.Index = Index;
this.Buffer = Buffer;
this.IsConst = IsConst; this.IsConst = IsConst;
this.Offset = Offset; this.Offset = Offset;
this.Size = Size; this.Size = Size;

View file

@ -21,12 +21,17 @@ namespace Ryujinx.Graphics.Gal
float OffsY, float OffsY,
float Rotate); float Rotate);
//Rasterizer
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs);
void RenderVertexArray(int VbIndex);
//Shader //Shader
void CreateShader(long Tag, byte[] Data, GalShaderType Type); void CreateShader(long Tag, GalShaderType Type, byte[] Data);
void SetShaderCb(long Tag, int Cbuf, byte[] Data); void SetShaderCb(long Tag, int Cbuf, byte[] Data);
void BindShader(long Tag); void BindShader(long Tag);
void BindProgram();
void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs);
void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height); void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);

View file

@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
PixelFormat.Rgba, PixelFormat.Rgba,
PixelType.UnsignedByte, PixelType.UnsignedByte,
Pixels); Pixels);
GL.ActiveTexture(TextureUnit.Texture0); GL.ActiveTexture(TextureUnit.Texture0);
GL.BindVertexArray(VaoHandle); GL.BindVertexArray(VaoHandle);

View file

@ -1,7 +1,178 @@
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
class OGLRasterizer class OGLRasterizer
{ {
private static Dictionary<GalVertexAttribSize, int> AttribElements =
new Dictionary<GalVertexAttribSize, int>()
{
{ GalVertexAttribSize._32_32_32_32, 4 },
{ GalVertexAttribSize._32_32_32, 3 },
{ GalVertexAttribSize._16_16_16_16, 4 },
{ GalVertexAttribSize._32_32, 2 },
{ GalVertexAttribSize._16_16_16, 3 },
{ GalVertexAttribSize._8_8_8_8, 4 },
{ GalVertexAttribSize._16_16, 2 },
{ GalVertexAttribSize._32, 1 },
{ GalVertexAttribSize._8_8_8, 3 },
{ GalVertexAttribSize._8_8, 2 },
{ GalVertexAttribSize._16, 1 },
{ GalVertexAttribSize._8, 1 },
{ GalVertexAttribSize._10_10_10_2, 4 },
{ GalVertexAttribSize._11_11_10, 3 }
};
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> AttribTypes =
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
{
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._32_32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._16_16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._8_8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int }, //?
{ GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //?
};
private struct VertexBuffer
{
public int VaoHandle;
public int VboHandle;
public int PrimCount;
}
private VertexBuffer[] VertexBuffers;
public OGLRasterizer()
{
VertexBuffers = new VertexBuffer[32];
}
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
{
ClearBufferMask Mask = 0;
//OpenGL doesn't support clearing just a single color channel,
//so we can't just clear all channels...
if (Flags.HasFlag(GalClearBufferFlags.ColorRed) &&
Flags.HasFlag(GalClearBufferFlags.ColorGreen) &&
Flags.HasFlag(GalClearBufferFlags.ColorBlue) &&
Flags.HasFlag(GalClearBufferFlags.ColorAlpha))
{
Mask = ClearBufferMask.ColorBufferBit;
}
if (Flags.HasFlag(GalClearBufferFlags.Depth))
{
Mask |= ClearBufferMask.DepthBufferBit;
}
if (Flags.HasFlag(GalClearBufferFlags.Stencil))
{
Mask |= ClearBufferMask.StencilBufferBit;
}
GL.Clear(Mask);
}
public void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs)
{
EnsureVbInitialized(VbIndex);
VertexBuffers[VbIndex].PrimCount = Buffer.Length / Stride;
VertexBuffer Vb = VertexBuffers[VbIndex];
IntPtr Length = new IntPtr(Buffer.Length);
GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindVertexArray(Vb.VaoHandle);
for (int Attr = 0; Attr < 16; Attr++)
{
GL.DisableVertexAttribArray(Attr);
}
for (int Index = 0; Index < Attribs.Length; Index++)
{
GalVertexAttrib Attrib = Attribs[Index];
GL.EnableVertexAttribArray(Index);
GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
bool Unsigned =
Attrib.Type == GalVertexAttribType.Unorm ||
Attrib.Type == GalVertexAttribType.Uint ||
Attrib.Type == GalVertexAttribType.Uscaled;
bool Normalize =
Attrib.Type == GalVertexAttribType.Snorm ||
Attrib.Type == GalVertexAttribType.Unorm;
VertexAttribPointerType Type = 0;
if (Attrib.Type == GalVertexAttribType.Float)
{
Type = VertexAttribPointerType.Float;
}
else
{
Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0);
}
int Size = AttribElements[Attrib.Size];
int Offset = Attrib.Offset;
GL.VertexAttribPointer(Index, Size, Type, Normalize, Stride, Offset);
}
GL.BindVertexArray(0);
}
public void RenderVertexArray(int VbIndex)
{
VertexBuffer Vb = VertexBuffers[VbIndex];
if (Vb.PrimCount == 0)
{
return;
}
GL.BindVertexArray(Vb.VaoHandle);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount);
}
private void EnsureVbInitialized(int VbIndex)
{
VertexBuffer Vb = VertexBuffers[VbIndex];
if (Vb.VaoHandle == 0)
{
Vb.VaoHandle = GL.GenVertexArray();
}
if (Vb.VboHandle == 0)
{
Vb.VboHandle = GL.GenBuffer();
}
VertexBuffers[VbIndex] = Vb;
}
} }
} }

View file

@ -15,13 +15,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public GalShaderType Type { get; private set; } public GalShaderType Type { get; private set; }
public ICollection<ShaderDeclInfo> TextureUsage { get; private set; } public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
public ICollection<ShaderDeclInfo> UniformUsage { get; private set; } public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
public ShaderStage( public ShaderStage(
GalShaderType Type, GalShaderType Type,
ICollection<ShaderDeclInfo> TextureUsage, IEnumerable<ShaderDeclInfo> TextureUsage,
ICollection<ShaderDeclInfo> UniformUsage) IEnumerable<ShaderDeclInfo> UniformUsage)
{ {
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
@ -61,6 +61,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private Dictionary<ShaderProgram, int> Programs; private Dictionary<ShaderProgram, int> Programs;
public int CurrentProgramHandle { get; private set; }
public OGLShader() public OGLShader()
{ {
Stages = new Dictionary<long, ShaderStage>(); Stages = new Dictionary<long, ShaderStage>();
@ -68,14 +70,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Programs = new Dictionary<ShaderProgram, int>(); Programs = new Dictionary<ShaderProgram, int>();
} }
public void Create(long Tag, byte[] Data, GalShaderType Type) public void Create(long Tag, GalShaderType Type, byte[] Data)
{ {
if (!Stages.ContainsKey(Tag)) if (!Stages.ContainsKey(Tag))
{ {
GlslProgram Program = GetGlslProgram(Data, Type); GlslProgram Program = GetGlslProgram(Data, Type);
System.Console.WriteLine(Program.Code);
ShaderStage Stage = new ShaderStage( ShaderStage Stage = new ShaderStage(
Type, Type,
Program.Textures, Program.Textures,
@ -94,6 +94,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf)) foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
{ {
float Value = BitConverter.ToSingle(Data, DeclInfo.Index * 4); float Value = BitConverter.ToSingle(Data, DeclInfo.Index * 4);
int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name);
GL.Uniform1(Location, Value);
} }
} }
} }
@ -113,21 +117,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
public bool BindCurrentProgram() public void BindProgram()
{ {
if (Current.Vertex == null || if (Current.Vertex == null ||
Current.Fragment == null) Current.Fragment == null)
{ {
return false; return;
} }
GL.UseProgram(GetCurrentProgramHandle());
return true;
}
private int GetCurrentProgramHandle()
{
if (!Programs.TryGetValue(Current, out int Handle)) if (!Programs.TryGetValue(Current, out int Handle))
{ {
Handle = GL.CreateProgram(); Handle = GL.CreateProgram();
@ -145,7 +142,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Programs.Add(Current, Handle); Programs.Add(Current, Handle);
} }
return Handle; GL.UseProgram(Handle);
CurrentProgramHandle = Handle;
} }
private void AttachIfNotNull(int ProgramHandle, ShaderStage Stage) private void AttachIfNotNull(int ProgramHandle, ShaderStage Stage)

View file

@ -2,29 +2,20 @@ using OpenTK;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
public class OpenGLRenderer : IGalRenderer public class OpenGLRenderer : IGalRenderer
{ {
private struct VertexBuffer
{
public int VaoHandle;
public int VboHandle;
public int PrimCount;
}
private struct Texture private struct Texture
{ {
public int Handle; public int Handle;
} }
private List<VertexBuffer> VertexBuffers;
private Texture[] Textures; private Texture[] Textures;
private OGLRasterizer Rasterizer;
private OGLShader Shader; private OGLShader Shader;
private ConcurrentQueue<Action> ActionsQueue; private ConcurrentQueue<Action> ActionsQueue;
@ -33,10 +24,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public OpenGLRenderer() public OpenGLRenderer()
{ {
VertexBuffers = new List<VertexBuffer>();
Textures = new Texture[8]; Textures = new Texture[8];
Rasterizer = new OGLRasterizer();
Shader = new OGLShader(); Shader = new OGLShader();
ActionsQueue = new ConcurrentQueue<Action>(); ActionsQueue = new ConcurrentQueue<Action>();
@ -70,18 +61,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Render() public void Render()
{ {
FbRenderer.Render(); FbRenderer.Render();
for (int Index = 0; Index < VertexBuffers.Count; Index++)
{
VertexBuffer Vb = VertexBuffers[Index];
if (Vb.VaoHandle != 0 &&
Vb.PrimCount != 0)
{
GL.BindVertexArray(Vb.VaoHandle);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount);
}
}
} }
public void SetWindowSize(int Width, int Height) public void SetWindowSize(int Width, int Height)
@ -110,157 +89,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL
FbRenderer.Set(Fb, Width, Height, Transform, Offs); FbRenderer.Set(Fb, Width, Height, Transform, Offs);
} }
public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs) public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
{ {
if (Index < 0) ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
}
public void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs)
{
if ((uint)VbIndex > 31)
{ {
throw new ArgumentOutOfRangeException(nameof(Index)); throw new ArgumentOutOfRangeException(nameof(VbIndex));
} }
if (Buffer.Length == 0 || Stride == 0) ActionsQueue.Enqueue(() => Rasterizer.SetVertexArray(VbIndex, Stride,
Buffer ?? throw new ArgumentNullException(nameof(Buffer)),
Attribs ?? throw new ArgumentNullException(nameof(Attribs))));
}
public void RenderVertexArray(int VbIndex)
{
if ((uint)VbIndex > 31)
{ {
return; throw new ArgumentOutOfRangeException(nameof(VbIndex));
} }
EnsureVbInitialized(Index); ActionsQueue.Enqueue(() => Rasterizer.RenderVertexArray(VbIndex));
VertexBuffer Vb = VertexBuffers[Index];
Vb.PrimCount = Buffer.Length / Stride;
VertexBuffers[Index] = Vb;
IntPtr Length = new IntPtr(Buffer.Length);
GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindVertexArray(Vb.VaoHandle);
for (int Attr = 0; Attr < 16; Attr++)
{
GL.DisableVertexAttribArray(Attr);
}
foreach (GalVertexAttrib Attrib in Attribs)
{
if (Attrib.Index >= 3) break;
GL.EnableVertexAttribArray(Attrib.Index);
GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
int Size = 0;
switch (Attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._16:
case GalVertexAttribSize._32:
Size = 1;
break;
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._32_32:
Size = 2;
break;
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._11_11_10:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._32_32_32:
Size = 3;
break;
case GalVertexAttribSize._8_8_8_8:
case GalVertexAttribSize._10_10_10_2:
case GalVertexAttribSize._16_16_16_16:
case GalVertexAttribSize._32_32_32_32:
Size = 4;
break;
}
bool Signed =
Attrib.Type == GalVertexAttribType.Snorm ||
Attrib.Type == GalVertexAttribType.Sint ||
Attrib.Type == GalVertexAttribType.Sscaled;
bool Normalize =
Attrib.Type == GalVertexAttribType.Snorm ||
Attrib.Type == GalVertexAttribType.Unorm;
VertexAttribPointerType Type = 0;
switch (Attrib.Type)
{
case GalVertexAttribType.Snorm:
case GalVertexAttribType.Unorm:
case GalVertexAttribType.Sint:
case GalVertexAttribType.Uint:
case GalVertexAttribType.Uscaled:
case GalVertexAttribType.Sscaled:
{
switch (Attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._8_8_8_8:
{
Type = Signed
? VertexAttribPointerType.Byte
: VertexAttribPointerType.UnsignedByte;
break;
}
case GalVertexAttribSize._16:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._16_16_16_16:
{
Type = Signed
? VertexAttribPointerType.Short
: VertexAttribPointerType.UnsignedShort;
break;
}
case GalVertexAttribSize._10_10_10_2:
case GalVertexAttribSize._11_11_10:
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
{
Type = Signed
? VertexAttribPointerType.Int
: VertexAttribPointerType.UnsignedInt;
break;
}
}
break;
}
case GalVertexAttribType.Float:
{
Type = VertexAttribPointerType.Float;
break;
}
}
GL.VertexAttribPointer(
Attrib.Index,
Size,
Type,
Normalize,
Stride,
Attrib.Offset);
}
GL.BindVertexArray(0);
} }
public void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height) public void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height)
@ -290,14 +143,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle); GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle);
} }
public void CreateShader(long Tag, byte[] Data, GalShaderType Type) public void CreateShader(long Tag, GalShaderType Type, byte[] Data)
{ {
if (Data == null) if (Data == null)
{ {
throw new ArgumentNullException(nameof(Data)); throw new ArgumentNullException(nameof(Data));
} }
ActionsQueue.Enqueue(() => Shader.Create(Tag, Data, Type)); ActionsQueue.Enqueue(() => Shader.Create(Tag, Type, Data));
} }
public void SetShaderCb(long Tag, int Cbuf, byte[] Data) public void SetShaderCb(long Tag, int Cbuf, byte[] Data)
@ -315,26 +168,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ActionsQueue.Enqueue(() => Shader.Bind(Tag)); ActionsQueue.Enqueue(() => Shader.Bind(Tag));
} }
private void EnsureVbInitialized(int VbIndex) public void BindProgram()
{ {
while (VbIndex >= VertexBuffers.Count) ActionsQueue.Enqueue(() => Shader.BindProgram());
{
VertexBuffers.Add(new VertexBuffer());
}
VertexBuffer Vb = VertexBuffers[VbIndex];
if (Vb.VaoHandle == 0)
{
Vb.VaoHandle = GL.GenVertexArray();
}
if (Vb.VboHandle == 0)
{
Vb.VboHandle = GL.GenBuffer();
}
VertexBuffers[VbIndex] = Vb;
} }
private void EnsureTexInitialized(int TexIndex) private void EnsureTexInitialized(int TexIndex)

View file

@ -0,0 +1,203 @@
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.Shader
{
class GlslDecl
{
private const int AttrStartIndex = 8;
private const int TexStartIndex = 8;
private const string InAttrName = "in_attr";
private const string OutAttrName = "out_attr";
private const string UniformName = "c";
private const string GprName = "gpr";
private const string PredName = "pred";
private const string TextureName = "tex";
public const string FragmentOutputName = "FragColor";
private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" };
private string StagePrefix;
private Dictionary<int, ShaderDeclInfo> m_Textures;
private Dictionary<(int, int), ShaderDeclInfo> m_Uniforms;
private Dictionary<int, ShaderDeclInfo> m_InAttributes;
private Dictionary<int, ShaderDeclInfo> m_OutAttributes;
private Dictionary<int, ShaderDeclInfo> m_Gprs;
private Dictionary<int, ShaderDeclInfo> m_Preds;
public IReadOnlyDictionary<int, ShaderDeclInfo> Textures => m_Textures;
public IReadOnlyDictionary<(int, int), ShaderDeclInfo> Uniforms => m_Uniforms;
public IReadOnlyDictionary<int, ShaderDeclInfo> InAttributes => m_InAttributes;
public IReadOnlyDictionary<int, ShaderDeclInfo> OutAttributes => m_OutAttributes;
public IReadOnlyDictionary<int, ShaderDeclInfo> Gprs => m_Gprs;
public IReadOnlyDictionary<int, ShaderDeclInfo> Preds => m_Preds;
public GalShaderType ShaderType { get; private set; }
public GlslDecl(ShaderIrNode[] Nodes, GalShaderType ShaderType)
{
this.ShaderType = ShaderType;
StagePrefix = StagePrefixes[(int)ShaderType] + "_";
m_Uniforms = new Dictionary<(int, int), ShaderDeclInfo>();
m_Textures = new Dictionary<int, ShaderDeclInfo>();
m_InAttributes = new Dictionary<int, ShaderDeclInfo>();
m_OutAttributes = new Dictionary<int, ShaderDeclInfo>();
m_Gprs = new Dictionary<int, ShaderDeclInfo>();
m_Preds = new Dictionary<int, ShaderDeclInfo>();
//FIXME: Only valid for vertex shaders.
if (ShaderType == GalShaderType.Fragment)
{
m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, 0, 4));
}
else
{
m_OutAttributes.Add(7, new ShaderDeclInfo("gl_Position", -1, 0, 4));
}
foreach (ShaderIrNode Node in Nodes)
{
Traverse(null, Node);
}
}
private void Traverse(ShaderIrNode Parent, ShaderIrNode Node)
{
switch (Node)
{
case ShaderIrAsg Asg:
{
Traverse(Asg, Asg.Dst);
Traverse(Asg, Asg.Src);
break;
}
case ShaderIrCond Cond:
{
Traverse(Cond, Cond.Pred);
Traverse(Cond, Cond.Child);
break;
}
case ShaderIrOp Op:
{
Traverse(Op, Op.OperandA);
Traverse(Op, Op.OperandB);
Traverse(Op, Op.OperandC);
if (Op.Inst == ShaderIrInst.Texr ||
Op.Inst == ShaderIrInst.Texg ||
Op.Inst == ShaderIrInst.Texb ||
Op.Inst == ShaderIrInst.Texa)
{
int Handle = ((ShaderIrOperImm)Op.OperandC).Imm;
int Index = Handle - TexStartIndex;
string Name = StagePrefix + TextureName + Index;
m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Index));
}
break;
}
case ShaderIrOperCbuf Cbuf:
{
string Name = StagePrefix + UniformName + Cbuf.Index + "_" + Cbuf.Offs;
ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Offs, Cbuf.Index);
m_Uniforms.TryAdd((Cbuf.Index, Cbuf.Offs), DeclInfo);
break;
}
case ShaderIrOperAbuf Abuf:
{
int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3;
int GlslIndex = Index - AttrStartIndex;
ShaderDeclInfo DeclInfo;
if (Parent is ShaderIrAsg Asg && Asg.Dst == Node)
{
if (!m_OutAttributes.TryGetValue(Index, out DeclInfo))
{
DeclInfo = new ShaderDeclInfo(OutAttrName + GlslIndex, GlslIndex);
m_OutAttributes.Add(Index, DeclInfo);
}
}
else
{
if (!m_InAttributes.TryGetValue(Index, out DeclInfo))
{
DeclInfo = new ShaderDeclInfo(InAttrName + GlslIndex, GlslIndex);
m_InAttributes.Add(Index, DeclInfo);
}
}
DeclInfo.Enlarge(Elem + 1);
break;
}
case ShaderIrOperGpr Gpr:
{
if (!Gpr.IsConst && !HasName(m_Gprs, Gpr.Index))
{
string Name = GprName + Gpr.Index;
m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index));
}
break;
}
case ShaderIrOperPred Pred:
{
if (!Pred.IsConst && !HasName(m_Preds, Pred.Index))
{
string Name = PredName + Pred.Index;
m_Preds.TryAdd(Pred.Index, new ShaderDeclInfo(Name, Pred.Index));
}
break;
}
}
}
private bool HasName(Dictionary<int, ShaderDeclInfo> Decls, int Index)
{
int VecIndex = Index >> 2;
if (Decls.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo))
{
if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size)
{
return true;
}
}
return Decls.ContainsKey(Index);
}
}
}

View file

@ -16,26 +16,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" }; private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" };
private Dictionary<int, ShaderDeclInfo> Textures; private GlslDecl Decl;
private Dictionary<(int, int), ShaderDeclInfo> Uniforms;
private Dictionary<int, ShaderDeclInfo> InAttributes;
private Dictionary<int, ShaderDeclInfo> OutAttributes;
private Dictionary<int, ShaderDeclInfo> Gprs;
private Dictionary<int, ShaderDeclInfo> Preds;
private const int AttrStartIndex = 8;
private const int TexStartIndex = 8;
private const string InputAttrName = "in_attr";
private const string OutputName = "out_attr";
private const string UniformName = "c";
private const string GprName = "gpr";
private const string PredName = "pred";
private const string TextureName = "tex";
private StringBuilder SB; private StringBuilder SB;
@ -72,38 +53,15 @@ namespace Ryujinx.Graphics.Gal.Shader
}; };
} }
public GlslProgram Decompile(int[] Code, GalShaderType Type) public GlslProgram Decompile(int[] Code, GalShaderType ShaderType)
{ {
Uniforms = new Dictionary<(int, int), ShaderDeclInfo>(); ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, ShaderType);
Textures = new Dictionary<int, ShaderDeclInfo>();
InAttributes = new Dictionary<int, ShaderDeclInfo>();
OutAttributes = new Dictionary<int, ShaderDeclInfo>();
Gprs = new Dictionary<int, ShaderDeclInfo>();
Preds = new Dictionary<int, ShaderDeclInfo>();
SB = new StringBuilder();
//FIXME: Only valid for vertex shaders.
if (Type == GalShaderType.Fragment)
{
Gprs.Add(0, new ShaderDeclInfo("FragColor", 0, 0, 4));
}
else
{
OutAttributes.Add(7, new ShaderDeclInfo("gl_Position", -1, 0, 4));
}
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, Type);
ShaderIrNode[] Nodes = Block.GetNodes(); ShaderIrNode[] Nodes = Block.GetNodes();
foreach (ShaderIrNode Node in Nodes) Decl = new GlslDecl(Nodes, ShaderType);
{
Traverse(null, Node); SB = new StringBuilder();
}
SB.AppendLine("#version 430"); SB.AppendLine("#version 430");
@ -120,133 +78,23 @@ namespace Ryujinx.Graphics.Gal.Shader
return new GlslProgram( return new GlslProgram(
GlslCode, GlslCode,
Textures.Values, Decl.Textures.Values,
Uniforms.Values); Decl.Uniforms.Values);
}
private void Traverse(ShaderIrNode Parent, ShaderIrNode Node)
{
switch (Node)
{
case ShaderIrAsg Asg:
{
Traverse(Asg, Asg.Dst);
Traverse(Asg, Asg.Src);
break;
}
case ShaderIrCond Cond:
{
Traverse(Cond, Cond.Pred);
Traverse(Cond, Cond.Child);
break;
}
case ShaderIrOp Op:
{
Traverse(Op, Op.OperandA);
Traverse(Op, Op.OperandB);
Traverse(Op, Op.OperandC);
if (Op.Inst == ShaderIrInst.Texr ||
Op.Inst == ShaderIrInst.Texg ||
Op.Inst == ShaderIrInst.Texb ||
Op.Inst == ShaderIrInst.Texa)
{
int Handle = ((ShaderIrOperImm)Op.OperandC).Imm;
int Index = Handle - TexStartIndex;
string Name = $"{TextureName}{Index}";
Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Index));
}
break;
}
case ShaderIrOperCbuf Cbuf:
{
string Name = $"{UniformName}{Cbuf.Index}_{Cbuf.Offs}";
ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Offs, Cbuf.Index);
Uniforms.TryAdd((Cbuf.Index, Cbuf.Offs), DeclInfo);
break;
}
case ShaderIrOperAbuf Abuf:
{
int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3;
int GlslIndex = Index - AttrStartIndex;
ShaderDeclInfo DeclInfo;
if (Parent is ShaderIrAsg Asg && Asg.Dst == Node)
{
if (!OutAttributes.TryGetValue(Index, out DeclInfo))
{
DeclInfo = new ShaderDeclInfo(OutputName + GlslIndex, GlslIndex);
OutAttributes.Add(Index, DeclInfo);
}
}
else
{
if (!InAttributes.TryGetValue(Index, out DeclInfo))
{
DeclInfo = new ShaderDeclInfo(InputAttrName + GlslIndex, GlslIndex);
InAttributes.Add(Index, DeclInfo);
}
}
DeclInfo.Enlarge(Elem + 1);
break;
}
case ShaderIrOperGpr Gpr:
{
if (!Gpr.IsConst && GetNameWithSwizzle(Gprs, Gpr.Index) == null)
{
string Name = $"{GprName}{Gpr.Index}";
Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index));
}
break;
}
case ShaderIrOperPred Pred:
{
if (!Pred.IsConst && GetNameWithSwizzle(Preds, Pred.Index) == null)
{
string Name = $"{PredName}{Pred.Index}";
Preds.TryAdd(Pred.Index, new ShaderDeclInfo(Name, Pred.Index));
}
break;
}
}
} }
private void PrintDeclTextures() private void PrintDeclTextures()
{ {
PrintDecls(Textures.Values, "uniform sampler2D"); PrintDecls(Decl.Textures, "uniform sampler2D");
} }
private void PrintDeclUniforms() private void PrintDeclUniforms()
{ {
foreach (ShaderDeclInfo DeclInfo in Uniforms.Values.OrderBy(DeclKeySelector)) foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))
{ {
SB.AppendLine($"uniform {GetDecl(DeclInfo)};"); SB.AppendLine($"uniform {GetDecl(DeclInfo)};");
} }
if (Uniforms.Values.Count > 0) if (Decl.Uniforms.Count > 0)
{ {
SB.AppendLine(); SB.AppendLine();
} }
@ -254,17 +102,17 @@ namespace Ryujinx.Graphics.Gal.Shader
private void PrintDeclInAttributes() private void PrintDeclInAttributes()
{ {
PrintDeclAttributes(InAttributes.Values, "in"); PrintDeclAttributes(Decl.InAttributes.Values, "in");
} }
private void PrintDeclOutAttributes() private void PrintDeclOutAttributes()
{ {
PrintDeclAttributes(OutAttributes.Values, "out"); PrintDeclAttributes(Decl.OutAttributes.Values, "out");
} }
private void PrintDeclAttributes(ICollection<ShaderDeclInfo> Decls, string InOut) private void PrintDeclAttributes(IEnumerable<ShaderDeclInfo> Decls, string InOut)
{ {
bool PrintNl = false; int Count = 0;
foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector)) foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector))
{ {
@ -272,11 +120,11 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
SB.AppendLine($"layout (location = {DeclInfo.Index}) {InOut} {GetDecl(DeclInfo)};"); SB.AppendLine($"layout (location = {DeclInfo.Index}) {InOut} {GetDecl(DeclInfo)};");
PrintNl = true; Count++;
} }
} }
if (PrintNl) if (Count > 0)
{ {
SB.AppendLine(); SB.AppendLine();
} }
@ -284,33 +132,37 @@ namespace Ryujinx.Graphics.Gal.Shader
private void PrintDeclGprs() private void PrintDeclGprs()
{ {
PrintDecls(Gprs.Values); PrintDecls(Decl.Gprs);
} }
private void PrintDeclPreds() private void PrintDeclPreds()
{ {
PrintDecls(Preds.Values, "bool"); PrintDecls(Decl.Preds, "bool");
} }
private void PrintDecls(ICollection<ShaderDeclInfo> Decls, string CustomType = null) private void PrintDecls(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, string CustomType = null)
{ {
foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector)) foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector))
{ {
string Name; string Name;
if (CustomType != null) if (CustomType != null)
{ {
Name = $"{CustomType} {DeclInfo.Name};"; Name = CustomType + " " + DeclInfo.Name + ";";
}
else if (DeclInfo.Name == GlslDecl.FragmentOutputName)
{
Name = "out " + GetDecl(DeclInfo) + ";";
} }
else else
{ {
Name = $"{GetDecl(DeclInfo)};"; Name = GetDecl(DeclInfo) + ";";
} }
SB.AppendLine(Name); SB.AppendLine(Name);
} }
if (Decls.Count > 0) if (Dict.Count > 0)
{ {
SB.AppendLine(); SB.AppendLine();
} }
@ -323,7 +175,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetDecl(ShaderDeclInfo DeclInfo) private string GetDecl(ShaderDeclInfo DeclInfo)
{ {
return $"{ElemTypes[DeclInfo.Size - 1]} {DeclInfo.Name}"; return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name;
} }
private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes) private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes)
@ -355,17 +207,17 @@ namespace Ryujinx.Graphics.Gal.Shader
if (Node is ShaderIrCond Cond) if (Node is ShaderIrCond Cond)
{ {
string SubScopeName = $"if ({GetInOperName(Cond.Pred, true)})"; string SubScopeName = "if (" + GetInOperName(Cond.Pred, true) + ")";
PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child); PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child);
} }
else if (Node is ShaderIrAsg Asg && IsValidOutOper(Asg.Dst)) else if (Node is ShaderIrAsg Asg && IsValidOutOper(Asg.Dst))
{ {
SB.AppendLine($"{Identation}{GetOutOperName(Asg.Dst)} = {GetInOperName(Asg.Src, true)};"); SB.AppendLine(Identation + GetOutOperName(Asg.Dst) + " = " + GetInOperName(Asg.Src, true) + ";");
} }
else if (Node is ShaderIrOp Op) else if (Node is ShaderIrOp Op)
{ {
SB.AppendLine($"{Identation}{GetInOperName(Op, true)};"); SB.AppendLine(Identation + GetInOperName(Op, true) + ";");
} }
else else
{ {
@ -432,7 +284,7 @@ namespace Ryujinx.Graphics.Gal.Shader
if (!(Entry || IsUnary(Op.Inst))) if (!(Entry || IsUnary(Op.Inst)))
{ {
Expr = $"({Expr})"; Expr = "(" + Expr + ")";
} }
return Expr; return Expr;
@ -461,7 +313,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetName(ShaderIrOperCbuf Cbuf) private string GetName(ShaderIrOperCbuf Cbuf)
{ {
if (!Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out ShaderDeclInfo DeclInfo)) if (!Decl.Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out ShaderDeclInfo DeclInfo))
{ {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
@ -471,15 +323,15 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetOutAbufName(ShaderIrOperAbuf Abuf) private string GetOutAbufName(ShaderIrOperAbuf Abuf)
{ {
return GetName(OutAttributes, Abuf, Swizzle: true); return GetName(Decl.OutAttributes, Abuf);
} }
private string GetName(ShaderIrOperAbuf Abuf, bool Swizzle = true) private string GetName(ShaderIrOperAbuf Abuf)
{ {
return GetName(InAttributes, Abuf, Swizzle); return GetName(Decl.InAttributes, Abuf);
} }
private string GetName(Dictionary<int, ShaderDeclInfo> Decls, ShaderIrOperAbuf Abuf, bool Swizzle) private string GetName(IReadOnlyDictionary<int, ShaderDeclInfo> Decls, ShaderIrOperAbuf Abuf)
{ {
int Index = Abuf.Offs >> 4; int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3; int Elem = (Abuf.Offs >> 2) & 3;
@ -489,14 +341,12 @@ namespace Ryujinx.Graphics.Gal.Shader
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
Swizzle &= DeclInfo.Size > 1; return DeclInfo.Size > 1 ? DeclInfo.Name + "." + GetAttrSwizzle(Elem) : DeclInfo.Name;
return Swizzle ? $"{DeclInfo.Name}.{GetAttrSwizzle(Elem)}" : DeclInfo.Name;
} }
private string GetName(ShaderIrOperGpr Gpr) private string GetName(ShaderIrOperGpr Gpr)
{ {
return Gpr.IsConst ? "0" : GetNameWithSwizzle(Gprs, Gpr.Index); return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index);
} }
private string GetName(ShaderIrOperImm Imm) private string GetName(ShaderIrOperImm Imm)
@ -506,10 +356,10 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetName(ShaderIrOperPred Pred) private string GetName(ShaderIrOperPred Pred)
{ {
return Pred.IsConst ? "true" : GetNameWithSwizzle(Preds, Pred.Index); return Pred.IsConst ? "true" : GetNameWithSwizzle(Decl.Preds, Pred.Index);
} }
private string GetNameWithSwizzle(Dictionary<int, ShaderDeclInfo> Decls, int Index) private string GetNameWithSwizzle(IReadOnlyDictionary<int, ShaderDeclInfo> Decls, int Index)
{ {
int VecIndex = Index >> 2; int VecIndex = Index >> 2;
@ -517,137 +367,82 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size) if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size)
{ {
return $"{DeclInfo.Name}.{GetAttrSwizzle(Index & 3)}"; return DeclInfo.Name + "." + GetAttrSwizzle(Index & 3);
} }
} }
if (!Decls.TryGetValue(Index, out DeclInfo)) if (!Decls.TryGetValue(Index, out DeclInfo))
{ {
return null; throw new InvalidOperationException();
} }
return DeclInfo.Name; return DeclInfo.Name;
} }
private string GetBandExpr(ShaderIrOp Op) private string GetAttrSwizzle(int Elem)
{ {
return $"{GetInOperName(Op.OperandA)} && " + return "xyzw".Substring(Elem, 1);
$"{GetInOperName(Op.OperandB)}";
} }
private string GetBnotExpr(ShaderIrOp Op) private string GetBandExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&&");
private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!");
private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<");
private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "==");
private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<=");
private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">");
private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!=");
private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">=");
private string GetExitExpr(ShaderIrOp Op) => "return";
private string GetFabsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs");
private string GetFaddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+");
private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos");
private string GetFex2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "exp2");
private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+");
private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2");
private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*");
private string GetFnegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-");
private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / ");
private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt");
private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin");
private string GetIpaExpr(ShaderIrOp Op) => GetInOperName(Op.OperandA);
private string GetKilExpr(ShaderIrOp Op) => "discard";
private string GetUnaryCall(ShaderIrOp Op, string FuncName)
{ {
return $"!{GetInOperName(Op.OperandA)}"; return FuncName + "(" + GetInOperName(Op.OperandA) + ")";
} }
private string GetCltExpr(ShaderIrOp Op) private string GetUnaryExpr(ShaderIrOp Op, string Opr)
{ {
return $"{GetInOperName(Op.OperandA)} < " + return Opr + GetInOperName(Op.OperandA);
$"{GetInOperName(Op.OperandB)}";
} }
private string GetCeqExpr(ShaderIrOp Op) private string GetBinaryExpr(ShaderIrOp Op, string Opr)
{ {
return $"{GetInOperName(Op.OperandA)} == " + return GetInOperName(Op.OperandA) + " " + Opr + " " +
$"{GetInOperName(Op.OperandB)}"; GetInOperName(Op.OperandB);
} }
private string GetCleExpr(ShaderIrOp Op) private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2)
{ {
return $"{GetInOperName(Op.OperandA)} <= " + return GetInOperName(Op.OperandA) + " " + Opr1 + " " +
$"{GetInOperName(Op.OperandB)}"; GetInOperName(Op.OperandB) + " " + Opr2 + " " +
} GetInOperName(Op.OperandC);
private string GetCgtExpr(ShaderIrOp Op)
{
return $"{GetInOperName(Op.OperandA)} > " +
$"{GetInOperName(Op.OperandB)}";
}
private string GetCneExpr(ShaderIrOp Op)
{
return $"{GetInOperName(Op.OperandA)} != " +
$"{GetInOperName(Op.OperandB)}";
}
private string GetCgeExpr(ShaderIrOp Op)
{
return $"{GetInOperName(Op.OperandA)} >= " +
$"{GetInOperName(Op.OperandB)}";
}
private string GetExitExpr(ShaderIrOp Op)
{
return "return";
}
private string GetFabsExpr(ShaderIrOp Op)
{
return $"abs({GetInOperName(Op.OperandA)})";
}
private string GetFaddExpr(ShaderIrOp Op)
{
return $"{GetInOperName(Op.OperandA)} + " +
$"{GetInOperName(Op.OperandB)}";
}
private string GetFcosExpr(ShaderIrOp Op)
{
return $"cos({GetInOperName(Op.OperandA)})";
}
private string GetFex2Expr(ShaderIrOp Op)
{
return $"exp2({GetInOperName(Op.OperandA)})";
}
private string GetFfmaExpr(ShaderIrOp Op)
{
return $"{GetInOperName(Op.OperandA)} * " +
$"{GetInOperName(Op.OperandB)} + " +
$"{GetInOperName(Op.OperandC)}";
}
private string GetFlg2Expr(ShaderIrOp Op)
{
return $"log2({GetInOperName(Op.OperandA)})";
}
private string GetFmulExpr(ShaderIrOp Op)
{
return $"{GetInOperName(Op.OperandA)} * " +
$"{GetInOperName(Op.OperandB)}";
}
private string GetFnegExpr(ShaderIrOp Op)
{
return $"-{GetInOperName(Op.OperandA)}";
}
private string GetFrcpExpr(ShaderIrOp Op)
{
return $"1 / {GetInOperName(Op.OperandA)}";
}
private string GetFrsqExpr(ShaderIrOp Op)
{
return $"inversesqrt({GetInOperName(Op.OperandA)})";
}
private string GetFsinExpr(ShaderIrOp Op)
{
return $"sin({GetInOperName(Op.OperandA)})";
}
private string GetIpaExpr(ShaderIrOp Op)
{
return GetInOperName(Op.OperandA);
}
private string GetKilExpr(ShaderIrOp Op)
{
return "discard";
} }
private string GetTexrExpr(ShaderIrOp Op) => GetTexExpr(Op, 'r'); private string GetTexrExpr(ShaderIrOp Op) => GetTexExpr(Op, 'r');
@ -666,7 +461,7 @@ namespace Ryujinx.Graphics.Gal.Shader
int Handle = ((ShaderIrOperImm)Op.OperandC).Imm; int Handle = ((ShaderIrOperImm)Op.OperandC).Imm;
if (!Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo)) if (!Decl.Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo))
{ {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
@ -676,40 +471,9 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetTexSamplerCoords(ShaderIrOp Op) private string GetTexSamplerCoords(ShaderIrOp Op)
{ {
if (GetInnerNode(Op.OperandA) is ShaderIrOperAbuf AAbuf &&
GetInnerNode(Op.OperandB) is ShaderIrOperAbuf BAbuf)
{
if (AAbuf.GprIndex == ShaderIrOperGpr.ZRIndex &&
BAbuf.GprIndex == ShaderIrOperGpr.ZRIndex &&
(AAbuf.Offs >> 4) == (BAbuf.Offs >> 4))
{
//Needs to call this to ensure it registers all elements used.
GetName(BAbuf);
return $"{GetName(AAbuf, Swizzle: false)}." +
$"{GetAttrSwizzle((AAbuf.Offs >> 2) & 3)}" +
$"{GetAttrSwizzle((BAbuf.Offs >> 2) & 3)}";
}
}
return "vec2(" + return "vec2(" +
$"{GetInOperName(Op.OperandA)}, " + GetInOperName(Op.OperandA) + ", " +
$"{GetInOperName(Op.OperandB)})"; GetInOperName(Op.OperandB) + ")";
}
private ShaderIrNode GetInnerNode(ShaderIrNode Node)
{
if (Node is ShaderIrOp Op && Op.Inst == ShaderIrInst.Ipa)
{
return Op.OperandA;
}
return Node;
}
private string GetAttrSwizzle(int Elem)
{
return "xyzw".Substring(Elem, 1);
} }
} }
} }

View file

@ -6,13 +6,13 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
public string Code { get; private set; } public string Code { get; private set; }
public ICollection<ShaderDeclInfo> Textures { get; private set; } public IEnumerable<ShaderDeclInfo> Textures { get; private set; }
public ICollection<ShaderDeclInfo> Uniforms { get; private set; } public IEnumerable<ShaderDeclInfo> Uniforms { get; private set; }
public GlslProgram( public GlslProgram(
string Code, string Code,
ICollection<ShaderDeclInfo> Textures, IEnumerable<ShaderDeclInfo> Textures,
ICollection<ShaderDeclInfo> Uniforms) IEnumerable<ShaderDeclInfo> Uniforms)
{ {
this.Code = Code; this.Code = Code;
this.Textures = Textures; this.Textures = Textures;

View file

@ -41,6 +41,7 @@ namespace Ryujinx.Graphics.Gpu
} }
AddMethod(0x585, 1, 1, VertexEndGl); AddMethod(0x585, 1, 1, VertexEndGl);
AddMethod(0x674, 1, 1, ClearBuffers);
AddMethod(0x6c3, 1, 1, QueryControl); AddMethod(0x6c3, 1, 1, QueryControl);
AddMethod(0x8e4, 16, 1, CbData); AddMethod(0x8e4, 16, 1, CbData);
AddMethod(0x904, 1, 1, CbBind); AddMethod(0x904, 1, 1, CbBind);
@ -66,18 +67,42 @@ namespace Ryujinx.Graphics.Gpu
int TexHandle = ReadCb(Memory, TexCbuf, 0x20); int TexHandle = ReadCb(Memory, TexCbuf, 0x20);
UploadShaders(Memory);
Gpu.Renderer.BindProgram();
UploadUniforms(Memory);
UploadVertexArrays(Memory);
}
private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry)
{
int Arg0 = PBEntry.Arguments[0];
int Rt = (Arg0 >> 6) & 0xf;
GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f);
Gpu.Renderer.ClearBuffers(Rt, Flags);
}
private void UploadShaders(AMemory Memory)
{
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
for (int Index = 0; Index < 6; Index++) for (int Index = 0; Index < 6; Index++)
{ {
int Offset = ReadRegister(NvGpuEngine3dReg.ShaderOffset + Index * 0x10); int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + Index * 0x10);
int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + Index * 0x10);
if (Offset == 0) if (Offset == 0 || (Index != 1 && Index != 5))
{ {
continue; continue;
} }
long Position = Gpu.GetCpuAddr(BasePosition + (uint)Offset); long Tag = BasePosition + (uint)Offset;
long Position = Gpu.GetCpuAddr(Tag);
if (Position == -1) if (Position == -1)
{ {
@ -91,7 +116,25 @@ namespace Ryujinx.Graphics.Gpu
GalShaderType ShaderType = GetTypeFromProgram(Index); GalShaderType ShaderType = GetTypeFromProgram(Index);
Gpu.Renderer.CreateShader(Position, Code, ShaderType); Gpu.Renderer.CreateShader(Tag, ShaderType, Code);
Gpu.Renderer.BindShader(Tag);
}
}
private void UploadUniforms(AMemory Memory)
{
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
for (int Index = 0; Index < 5; Index++)
{
int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + (Index + 1) * 0x10);
long Tag = BasePosition + (uint)Offset;
if (Offset == 0 || (Index != 0 && Index != 4))
{
continue;
}
for (int Cbuf = 0; Cbuf < Cbs.Length; Cbuf++) for (int Cbuf = 0; Cbuf < Cbs.Length; Cbuf++)
{ {
@ -99,16 +142,68 @@ namespace Ryujinx.Graphics.Gpu
if (Cb.Enabled) if (Cb.Enabled)
{ {
long CbPosition = Cb.Position + (int)ShaderType * Cb.Size; long CbPosition = Cb.Position + Index * Cb.Size;
byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size); byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size);
Gpu.Renderer.SetShaderCb(Position, Cbuf, Data); Gpu.Renderer.SetShaderCb(Tag, Cbuf, Data);
} }
} }
} }
} }
private void UploadVertexArrays(AMemory Memory)
{
List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
for (int Attr = 0; Attr < 16; Attr++)
{
int Packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + Attr);
int ArrayIndex = Packed & 0x1f;
if (Attribs[ArrayIndex] == null)
{
Attribs[ArrayIndex] = new List<GalVertexAttrib>();
}
Attribs[ArrayIndex].Add(new GalVertexAttrib(
((Packed >> 6) & 0x1) != 0,
(Packed >> 7) & 0x3fff,
(GalVertexAttribSize)((Packed >> 21) & 0x3f),
(GalVertexAttribType)((Packed >> 27) & 0x7),
((Packed >> 31) & 0x1) != 0));
}
for (int Index = 0; Index < 32; Index++)
{
int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4);
bool Enable = (Control & 0x1000) != 0;
if (!Enable)
{
continue;
}
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
long EndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 4);
long Size = (EndPos - Position) + 1;
int Stride = Control & 0xfff;
Position = Gpu.GetCpuAddr(Position);
byte[] Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0];
Gpu.Renderer.SetVertexArray(Index, Stride, Data, AttribArray);
Gpu.Renderer.RenderVertexArray(Index);
}
}
private static GalShaderType GetTypeFromProgram(int Program) private static GalShaderType GetTypeFromProgram(int Program)
{ {
switch (Program) switch (Program)
@ -145,9 +240,9 @@ namespace Ryujinx.Graphics.Gpu
private void CbData(AMemory Memory, NsGpuPBEntry PBEntry) private void CbData(AMemory Memory, NsGpuPBEntry PBEntry)
{ {
if (TryGetCpuAddr(NvGpuEngine3dReg.CbAddress, out long Position)) if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
{ {
int Offset = ReadRegister(NvGpuEngine3dReg.CbOffset); int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferNOffset);
foreach (int Arg in PBEntry.Arguments) foreach (int Arg in PBEntry.Arguments)
{ {
@ -156,7 +251,7 @@ namespace Ryujinx.Graphics.Gpu
Offset += 4; Offset += 4;
} }
WriteRegister(NvGpuEngine3dReg.CbOffset, Offset); WriteRegister(NvGpuEngine3dReg.ConstBufferNOffset, Offset);
} }
} }
@ -168,11 +263,11 @@ namespace Ryujinx.Graphics.Gpu
Index = (Index >> 4) & 0x1f; Index = (Index >> 4) & 0x1f;
if (TryGetCpuAddr(NvGpuEngine3dReg.CbAddress, out long Position)) if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
{ {
Cbs[Index].Position = Position; Cbs[Index].Position = Position;
Cbs[Index].Enabled = Enabled; Cbs[Index].Enabled = Enabled;
Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.CbSize); Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize);
} }
} }

View file

@ -2,17 +2,22 @@ namespace Ryujinx.Graphics.Gpu
{ {
enum NvGpuEngine3dReg enum NvGpuEngine3dReg
{ {
ShaderAddress = 0x582, VertexAttribNFormat = 0x458,
QueryAddress = 0x6c0, ShaderAddress = 0x582,
QuerySequence = 0x6c2, QueryAddress = 0x6c0,
QueryControl = 0x6c3, QuerySequence = 0x6c2,
ShaderControl = 0x800, QueryControl = 0x6c3,
ShaderOffset = 0x801, VertexArrayNControl = 0x700,
ShaderMaxGprs = 0x803, VertexArrayNAddress = 0x701,
ShaderType = 0x804, VertexArrayNDivisor = 0x703,
CbSize = 0x8e0, VertexArrayNEndAddr = 0x7c0,
CbAddress = 0x8e1, ShaderNControl = 0x800,
CbOffset = 0x8e3, ShaderNOffset = 0x801,
TextureCbIndex = 0x982 ShaderNMaxGprs = 0x803,
ShaderNType = 0x804,
ConstBufferNSize = 0x8e0,
ConstBufferNAddress = 0x8e1,
ConstBufferNOffset = 0x8e3,
TextureCbIndex = 0x982
} }
} }

View file

@ -173,8 +173,6 @@ namespace Ryujinx
Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " + Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " +
$"{Ns.Statistics.GameFrameRate:0})"; $"{Ns.Statistics.GameFrameRate:0})";
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
Renderer.RunActions(); Renderer.RunActions();
Renderer.Render(); Renderer.Render();