Merge shader branch, adding support for GLSL decompilation, a macro
interpreter, and a rewrite of the GPU code.
This commit is contained in:
parent
7acd0e0122
commit
b9aa3966c0
77 changed files with 5301 additions and 766 deletions
|
@ -538,9 +538,9 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
{
|
{
|
||||||
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size);
|
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size);
|
||||||
|
|
||||||
NsGpuPBEntry[] PushBuffer = NsGpuPBEntry.DecodePushBuffer(Data);
|
NsGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data);
|
||||||
|
|
||||||
Context.Ns.Gpu.ProcessPushBuffer(PushBuffer, Context.Memory);
|
Context.Ns.Gpu.Fifo.PushBuffer(Context.Memory, PushBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,8 +680,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
}
|
}
|
||||||
|
|
||||||
Map.CpuAddress = Addr;
|
Map.CpuAddress = Addr;
|
||||||
Map.Align = Align;
|
Map.Align = Align;
|
||||||
Map.Kind = Kind;
|
Map.Kind = Kind;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,12 +244,16 @@ namespace Ryujinx.Core.OsHle.Services.Android
|
||||||
{
|
{
|
||||||
int Slot = ParcelReader.ReadInt32();
|
int Slot = ParcelReader.ReadInt32();
|
||||||
|
|
||||||
int BufferCount = ParcelReader.ReadInt32();
|
int BufferCount = ParcelReader.ReadInt32();
|
||||||
long BufferSize = ParcelReader.ReadInt64();
|
|
||||||
|
|
||||||
BufferQueue[Slot].State = BufferState.Free;
|
if (BufferCount > 0)
|
||||||
|
{
|
||||||
|
long BufferSize = ParcelReader.ReadInt64();
|
||||||
|
|
||||||
BufferQueue[Slot].Data = new GbpBuffer(ParcelReader);
|
BufferQueue[Slot].State = BufferState.Free;
|
||||||
|
|
||||||
|
BufferQueue[Slot].Data = new GbpBuffer(ParcelReader);
|
||||||
|
}
|
||||||
|
|
||||||
return MakeReplyParcel(Context, 0);
|
return MakeReplyParcel(Context, 0);
|
||||||
}
|
}
|
||||||
|
|
11
Ryujinx.Graphics/Gal/GalBlendEquation.cs
Normal file
11
Ryujinx.Graphics/Gal/GalBlendEquation.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public enum GalBlendEquation
|
||||||
|
{
|
||||||
|
FuncAdd = 0x8006,
|
||||||
|
Min = 0x8007,
|
||||||
|
Max = 0x8008,
|
||||||
|
FuncSubtract = 0x800a,
|
||||||
|
FuncReverseSubtract = 0x800b
|
||||||
|
}
|
||||||
|
}
|
25
Ryujinx.Graphics/Gal/GalBlendFactor.cs
Normal file
25
Ryujinx.Graphics/Gal/GalBlendFactor.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public enum GalBlendFactor
|
||||||
|
{
|
||||||
|
Zero = 0x4000,
|
||||||
|
One = 0x4001,
|
||||||
|
SrcColor = 0x4300,
|
||||||
|
OneMinusSrcColor = 0x4301,
|
||||||
|
SrcAlpha = 0x4302,
|
||||||
|
OneMinusSrcAlpha = 0x4303,
|
||||||
|
DstAlpha = 0x4304,
|
||||||
|
OneMinusDstAlpha = 0x4305,
|
||||||
|
DstColor = 0x4306,
|
||||||
|
OneMinusDstColor = 0x4307,
|
||||||
|
SrcAlphaSaturate = 0x4308,
|
||||||
|
ConstantColor = 0xc001,
|
||||||
|
OneMinusConstantColor = 0xc002,
|
||||||
|
ConstantAlpha = 0xc003,
|
||||||
|
OneMinusConstantAlpha = 0xc004,
|
||||||
|
Src1Color = 0xc900,
|
||||||
|
OneMinusSrc1Color = 0xc901,
|
||||||
|
Src1Alpha = 0xc902,
|
||||||
|
OneMinusSrc1Alpha = 0xc903
|
||||||
|
}
|
||||||
|
}
|
15
Ryujinx.Graphics/Gal/GalClearBufferFlags.cs
Normal file
15
Ryujinx.Graphics/Gal/GalClearBufferFlags.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
22
Ryujinx.Graphics/Gal/GalColorF.cs
Normal file
22
Ryujinx.Graphics/Gal/GalColorF.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public struct GalColorF
|
||||||
|
{
|
||||||
|
public float Red { get; private set; }
|
||||||
|
public float Green { get; private set; }
|
||||||
|
public float Blue { get; private set; }
|
||||||
|
public float Alpha { get; private set; }
|
||||||
|
|
||||||
|
public GalColorF(
|
||||||
|
float Red,
|
||||||
|
float Green,
|
||||||
|
float Blue,
|
||||||
|
float Alpha)
|
||||||
|
{
|
||||||
|
this.Red = Red;
|
||||||
|
this.Green = Green;
|
||||||
|
this.Blue = Blue;
|
||||||
|
this.Alpha = Alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Graphics/Gal/GalIndexFormat.cs
Normal file
9
Ryujinx.Graphics/Gal/GalIndexFormat.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public enum GalIndexFormat
|
||||||
|
{
|
||||||
|
Byte = 0,
|
||||||
|
Int16 = 1,
|
||||||
|
Int32 = 2
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics/Gal/GalShaderType.cs
Normal file
11
Ryujinx.Graphics/Gal/GalShaderType.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public enum GalShaderType
|
||||||
|
{
|
||||||
|
Vertex = 0,
|
||||||
|
TessControl = 1,
|
||||||
|
TessEvaluation = 2,
|
||||||
|
Geometry = 3,
|
||||||
|
Fragment = 4
|
||||||
|
}
|
||||||
|
}
|
20
Ryujinx.Graphics/Gal/GalTexture.cs
Normal file
20
Ryujinx.Graphics/Gal/GalTexture.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public struct GalTexture
|
||||||
|
{
|
||||||
|
public byte[] Data;
|
||||||
|
|
||||||
|
public int Width;
|
||||||
|
public int Height;
|
||||||
|
|
||||||
|
public GalTextureFormat Format;
|
||||||
|
|
||||||
|
public GalTexture(byte[] Data, int Width, int Height, GalTextureFormat Format)
|
||||||
|
{
|
||||||
|
this.Data = Data;
|
||||||
|
this.Width = Width;
|
||||||
|
this.Height = Height;
|
||||||
|
this.Format = Format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Ryujinx.Graphics/Gal/GalTextureFilter.cs
Normal file
8
Ryujinx.Graphics/Gal/GalTextureFilter.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public enum GalTextureFilter
|
||||||
|
{
|
||||||
|
Nearest = 1,
|
||||||
|
Linear = 2
|
||||||
|
}
|
||||||
|
}
|
10
Ryujinx.Graphics/Gal/GalTextureFormat.cs
Normal file
10
Ryujinx.Graphics/Gal/GalTextureFormat.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public enum GalTextureFormat
|
||||||
|
{
|
||||||
|
A8B8G8R8 = 0x8,
|
||||||
|
BC1 = 0x24,
|
||||||
|
BC2 = 0x25,
|
||||||
|
BC3 = 0x26
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Graphics/Gal/GalTextureMipFilter.cs
Normal file
9
Ryujinx.Graphics/Gal/GalTextureMipFilter.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public enum GalTextureMipFilter
|
||||||
|
{
|
||||||
|
None = 1,
|
||||||
|
Nearest = 2,
|
||||||
|
Linear = 3
|
||||||
|
}
|
||||||
|
}
|
33
Ryujinx.Graphics/Gal/GalTextureSampler.cs
Normal file
33
Ryujinx.Graphics/Gal/GalTextureSampler.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public struct GalTextureSampler
|
||||||
|
{
|
||||||
|
public GalTextureWrap AddressU { get; private set; }
|
||||||
|
public GalTextureWrap AddressV { get; private set; }
|
||||||
|
public GalTextureWrap AddressP { get; private set; }
|
||||||
|
|
||||||
|
public GalTextureFilter MinFilter { get; private set; }
|
||||||
|
public GalTextureFilter MagFilter { get; private set; }
|
||||||
|
public GalTextureMipFilter MipFilter { get; private set; }
|
||||||
|
|
||||||
|
public GalColorF BorderColor { get; private set; }
|
||||||
|
|
||||||
|
public GalTextureSampler(
|
||||||
|
GalTextureWrap AddressU,
|
||||||
|
GalTextureWrap AddressV,
|
||||||
|
GalTextureWrap AddressP,
|
||||||
|
GalTextureFilter MinFilter,
|
||||||
|
GalTextureFilter MagFilter,
|
||||||
|
GalTextureMipFilter MipFilter,
|
||||||
|
GalColorF BorderColor)
|
||||||
|
{
|
||||||
|
this.AddressU = AddressU;
|
||||||
|
this.AddressV = AddressV;
|
||||||
|
this.AddressP = AddressP;
|
||||||
|
this.MinFilter = MinFilter;
|
||||||
|
this.MagFilter = MagFilter;
|
||||||
|
this.MipFilter = MipFilter;
|
||||||
|
this.BorderColor = BorderColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Graphics/Gal/GalTextureWrap.cs
Normal file
14
Ryujinx.Graphics/Gal/GalTextureWrap.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public enum GalTextureWrap
|
||||||
|
{
|
||||||
|
Repeat = 0,
|
||||||
|
MirroredRepeat = 1,
|
||||||
|
ClampToEdge = 2,
|
||||||
|
ClampToBorder = 3,
|
||||||
|
Clamp = 4,
|
||||||
|
MirrorClampToEdge = 5,
|
||||||
|
MirrorClampToBorder = 6,
|
||||||
|
MirrorClamp = 7
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal
|
namespace Ryujinx.Graphics.Gal
|
||||||
{
|
{
|
||||||
|
@ -21,10 +22,56 @@ namespace Ryujinx.Graphics.Gal
|
||||||
float OffsY,
|
float OffsY,
|
||||||
float Rotate);
|
float Rotate);
|
||||||
|
|
||||||
void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs);
|
//Blend
|
||||||
|
void SetBlendEnable(bool Enable);
|
||||||
|
|
||||||
void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);
|
void SetBlend(
|
||||||
|
GalBlendEquation Equation,
|
||||||
|
GalBlendFactor FuncSrc,
|
||||||
|
GalBlendFactor FuncDst);
|
||||||
|
|
||||||
void BindTexture(int Index);
|
void SetBlendSeparate(
|
||||||
|
GalBlendEquation EquationRgb,
|
||||||
|
GalBlendEquation EquationAlpha,
|
||||||
|
GalBlendFactor FuncSrcRgb,
|
||||||
|
GalBlendFactor FuncDstRgb,
|
||||||
|
GalBlendFactor FuncSrcAlpha,
|
||||||
|
GalBlendFactor FuncDstAlpha);
|
||||||
|
|
||||||
|
//Frame Buffer
|
||||||
|
void SetFb(int FbIndex, int Width, int Height);
|
||||||
|
|
||||||
|
void BindFrameBuffer(int FbIndex);
|
||||||
|
|
||||||
|
void DrawFrameBuffer(int FbIndex);
|
||||||
|
|
||||||
|
//Rasterizer
|
||||||
|
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
|
||||||
|
|
||||||
|
void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs);
|
||||||
|
|
||||||
|
void SetIndexArray(byte[] Buffer, GalIndexFormat Format);
|
||||||
|
|
||||||
|
void DrawArrays(int VbIndex, GalPrimitiveType PrimType);
|
||||||
|
|
||||||
|
void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType);
|
||||||
|
|
||||||
|
//Shader
|
||||||
|
void CreateShader(long Tag, GalShaderType Type, byte[] Data);
|
||||||
|
|
||||||
|
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag);
|
||||||
|
|
||||||
|
void SetConstBuffer(long Tag, int Cbuf, byte[] Data);
|
||||||
|
|
||||||
|
void SetUniform1(string UniformName, int Value);
|
||||||
|
|
||||||
|
void BindShader(long Tag);
|
||||||
|
|
||||||
|
void BindProgram();
|
||||||
|
|
||||||
|
//Texture
|
||||||
|
void SetTexture(int Index, GalTexture Tex);
|
||||||
|
|
||||||
|
void SetSampler(int Index, GalTextureSampler Sampler);
|
||||||
}
|
}
|
||||||
}
|
}
|
49
Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs
Normal file
49
Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
{
|
||||||
|
class OGLBlend
|
||||||
|
{
|
||||||
|
public void Enable()
|
||||||
|
{
|
||||||
|
GL.Enable(EnableCap.Blend);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Disable()
|
||||||
|
{
|
||||||
|
GL.Disable(EnableCap.Blend);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(
|
||||||
|
GalBlendEquation Equation,
|
||||||
|
GalBlendFactor FuncSrc,
|
||||||
|
GalBlendFactor FuncDst)
|
||||||
|
{
|
||||||
|
GL.BlendEquation(
|
||||||
|
OGLEnumConverter.GetBlendEquation(Equation));
|
||||||
|
|
||||||
|
GL.BlendFunc(
|
||||||
|
OGLEnumConverter.GetBlendFactorSrc(FuncSrc),
|
||||||
|
OGLEnumConverter.GetBlendFactorDst(FuncDst));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSeparate(
|
||||||
|
GalBlendEquation EquationRgb,
|
||||||
|
GalBlendEquation EquationAlpha,
|
||||||
|
GalBlendFactor FuncSrcRgb,
|
||||||
|
GalBlendFactor FuncDstRgb,
|
||||||
|
GalBlendFactor FuncSrcAlpha,
|
||||||
|
GalBlendFactor FuncDstAlpha)
|
||||||
|
{
|
||||||
|
GL.BlendEquationSeparate(
|
||||||
|
OGLEnumConverter.GetBlendEquation(EquationRgb),
|
||||||
|
OGLEnumConverter.GetBlendEquation(EquationAlpha));
|
||||||
|
|
||||||
|
GL.BlendFuncSeparate(
|
||||||
|
OGLEnumConverter.GetBlendFactorSrc(FuncSrcRgb),
|
||||||
|
OGLEnumConverter.GetBlendFactorDst(FuncDstRgb),
|
||||||
|
OGLEnumConverter.GetBlendFactorSrc(FuncSrcAlpha),
|
||||||
|
OGLEnumConverter.GetBlendFactorDst(FuncDstAlpha));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
129
Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
Normal file
129
Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
{
|
||||||
|
static class OGLEnumConverter
|
||||||
|
{
|
||||||
|
public static DrawElementsType GetDrawElementsType(GalIndexFormat Format)
|
||||||
|
{
|
||||||
|
switch (Format)
|
||||||
|
{
|
||||||
|
case GalIndexFormat.Byte: return DrawElementsType.UnsignedByte;
|
||||||
|
case GalIndexFormat.Int16: return DrawElementsType.UnsignedShort;
|
||||||
|
case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(Format));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type)
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case GalPrimitiveType.Points: return PrimitiveType.Points;
|
||||||
|
case GalPrimitiveType.Lines: return PrimitiveType.Lines;
|
||||||
|
case GalPrimitiveType.LineLoop: return PrimitiveType.LineLoop;
|
||||||
|
case GalPrimitiveType.LineStrip: return PrimitiveType.LineStrip;
|
||||||
|
case GalPrimitiveType.Triangles: return PrimitiveType.Triangles;
|
||||||
|
case GalPrimitiveType.TriangleStrip: return PrimitiveType.TriangleStrip;
|
||||||
|
case GalPrimitiveType.TriangleFan: return PrimitiveType.TriangleFan;
|
||||||
|
case GalPrimitiveType.Quads: return PrimitiveType.Quads;
|
||||||
|
case GalPrimitiveType.QuadStrip: return PrimitiveType.QuadStrip;
|
||||||
|
case GalPrimitiveType.Polygon: return PrimitiveType.Polygon;
|
||||||
|
case GalPrimitiveType.LinesAdjacency: return PrimitiveType.LinesAdjacency;
|
||||||
|
case GalPrimitiveType.LineStripAdjacency: return PrimitiveType.LineStripAdjacency;
|
||||||
|
case GalPrimitiveType.TrianglesAdjacency: return PrimitiveType.TrianglesAdjacency;
|
||||||
|
case GalPrimitiveType.TriangleStripAdjacency: return PrimitiveType.TriangleStripAdjacency;
|
||||||
|
case GalPrimitiveType.Patches: return PrimitiveType.Patches;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderType GetShaderType(GalShaderType Type)
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case GalShaderType.Vertex: return ShaderType.VertexShader;
|
||||||
|
case GalShaderType.TessControl: return ShaderType.TessControlShader;
|
||||||
|
case GalShaderType.TessEvaluation: return ShaderType.TessEvaluationShader;
|
||||||
|
case GalShaderType.Geometry: return ShaderType.GeometryShader;
|
||||||
|
case GalShaderType.Fragment: return ShaderType.FragmentShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PixelInternalFormat GetCompressedTextureFormat(GalTextureFormat Format)
|
||||||
|
{
|
||||||
|
switch (Format)
|
||||||
|
{
|
||||||
|
case GalTextureFormat.BC1: return PixelInternalFormat.CompressedRgbaS3tcDxt1Ext;
|
||||||
|
case GalTextureFormat.BC2: return PixelInternalFormat.CompressedRgbaS3tcDxt3Ext;
|
||||||
|
case GalTextureFormat.BC3: return PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException(Format.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
|
||||||
|
{
|
||||||
|
switch (Wrap)
|
||||||
|
{
|
||||||
|
case GalTextureWrap.Repeat: return TextureWrapMode.Repeat;
|
||||||
|
case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat;
|
||||||
|
case GalTextureWrap.ClampToEdge: return TextureWrapMode.ClampToEdge;
|
||||||
|
case GalTextureWrap.ClampToBorder: return TextureWrapMode.ClampToBorder;
|
||||||
|
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
|
||||||
|
|
||||||
|
//TODO: Those needs extensions (and are currently wrong).
|
||||||
|
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
|
||||||
|
case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
|
||||||
|
case GalTextureWrap.MirrorClamp: return TextureWrapMode.Clamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(Wrap));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextureMinFilter GetTextureMinFilter(
|
||||||
|
GalTextureFilter MinFilter,
|
||||||
|
GalTextureMipFilter MipFilter)
|
||||||
|
{
|
||||||
|
//TODO: Mip (needs mipmap support first).
|
||||||
|
switch (MinFilter)
|
||||||
|
{
|
||||||
|
case GalTextureFilter.Nearest: return TextureMinFilter.Nearest;
|
||||||
|
case GalTextureFilter.Linear: return TextureMinFilter.Linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(MinFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter)
|
||||||
|
{
|
||||||
|
switch (Filter)
|
||||||
|
{
|
||||||
|
case GalTextureFilter.Nearest: return TextureMagFilter.Nearest;
|
||||||
|
case GalTextureFilter.Linear: return TextureMagFilter.Linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(Filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
|
||||||
|
{
|
||||||
|
return (BlendEquationMode)BlendEquation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor)
|
||||||
|
{
|
||||||
|
return (BlendingFactorSrc)(BlendFactor - 0x4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor)
|
||||||
|
{
|
||||||
|
return (BlendingFactorDest)(BlendFactor - 0x4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
182
Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
Normal file
182
Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
{
|
||||||
|
class OGLFrameBuffer
|
||||||
|
{
|
||||||
|
private struct FrameBuffer
|
||||||
|
{
|
||||||
|
public int FbHandle;
|
||||||
|
public int RbHandle;
|
||||||
|
public int TexHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ShaderProgram
|
||||||
|
{
|
||||||
|
public int Handle;
|
||||||
|
public int VpHandle;
|
||||||
|
public int FpHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FrameBuffer[] Fbs;
|
||||||
|
|
||||||
|
private ShaderProgram Shader;
|
||||||
|
|
||||||
|
private bool IsInitialized;
|
||||||
|
|
||||||
|
private int VaoHandle;
|
||||||
|
private int VboHandle;
|
||||||
|
|
||||||
|
public OGLFrameBuffer()
|
||||||
|
{
|
||||||
|
Fbs = new FrameBuffer[16];
|
||||||
|
|
||||||
|
Shader = new ShaderProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(int Index, int Width, int Height)
|
||||||
|
{
|
||||||
|
if (Fbs[Index].FbHandle != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Fbs[Index].FbHandle = GL.GenFramebuffer();
|
||||||
|
Fbs[Index].RbHandle = GL.GenRenderbuffer();
|
||||||
|
Fbs[Index].TexHandle = GL.GenTexture();
|
||||||
|
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle);
|
||||||
|
|
||||||
|
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle);
|
||||||
|
|
||||||
|
GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, 1280, 720);
|
||||||
|
|
||||||
|
GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle);
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||||
|
|
||||||
|
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 1280, 720, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
|
||||||
|
|
||||||
|
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fbs[Index].TexHandle, 0);
|
||||||
|
|
||||||
|
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Bind(int Index)
|
||||||
|
{
|
||||||
|
if (Fbs[Index].FbHandle == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(int Index)
|
||||||
|
{
|
||||||
|
if (Fbs[Index].FbHandle == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
|
||||||
|
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
|
||||||
|
GL.BindVertexArray(VaoHandle);
|
||||||
|
|
||||||
|
GL.UseProgram(Shader.Handle);
|
||||||
|
|
||||||
|
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureInitialized()
|
||||||
|
{
|
||||||
|
if (!IsInitialized)
|
||||||
|
{
|
||||||
|
IsInitialized = true;
|
||||||
|
|
||||||
|
SetupShader();
|
||||||
|
SetupVertex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupShader()
|
||||||
|
{
|
||||||
|
Shader.VpHandle = GL.CreateShader(ShaderType.VertexShader);
|
||||||
|
Shader.FpHandle = GL.CreateShader(ShaderType.FragmentShader);
|
||||||
|
|
||||||
|
string VpSource = EmbeddedResource.GetString("GlFbVtxShader");
|
||||||
|
string FpSource = EmbeddedResource.GetString("GlFbFragShader");
|
||||||
|
|
||||||
|
GL.ShaderSource(Shader.VpHandle, VpSource);
|
||||||
|
GL.ShaderSource(Shader.FpHandle, FpSource);
|
||||||
|
GL.CompileShader(Shader.VpHandle);
|
||||||
|
GL.CompileShader(Shader.FpHandle);
|
||||||
|
|
||||||
|
Shader.Handle = GL.CreateProgram();
|
||||||
|
|
||||||
|
GL.AttachShader(Shader.Handle, Shader.VpHandle);
|
||||||
|
GL.AttachShader(Shader.Handle, Shader.FpHandle);
|
||||||
|
GL.LinkProgram(Shader.Handle);
|
||||||
|
GL.UseProgram(Shader.Handle);
|
||||||
|
|
||||||
|
Matrix2 Transform = Matrix2.CreateScale(1, -1);
|
||||||
|
|
||||||
|
int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex");
|
||||||
|
|
||||||
|
GL.Uniform1(TexUniformLocation, 0);
|
||||||
|
|
||||||
|
int WindowSizeUniformLocation = GL.GetUniformLocation(Shader.Handle, "window_size");
|
||||||
|
|
||||||
|
GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
|
||||||
|
|
||||||
|
int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform");
|
||||||
|
|
||||||
|
GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupVertex()
|
||||||
|
{
|
||||||
|
VaoHandle = GL.GenVertexArray();
|
||||||
|
VboHandle = GL.GenBuffer();
|
||||||
|
|
||||||
|
float[] Buffer = new float[]
|
||||||
|
{
|
||||||
|
-1, 1, 0, 0,
|
||||||
|
1, 1, 1, 0,
|
||||||
|
-1, -1, 0, 1,
|
||||||
|
1, -1, 1, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
IntPtr Length = new IntPtr(Buffer.Length * 4);
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
||||||
|
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
||||||
|
|
||||||
|
GL.BindVertexArray(VaoHandle);
|
||||||
|
|
||||||
|
GL.EnableVertexAttribArray(0);
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
||||||
|
|
||||||
|
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0);
|
||||||
|
|
||||||
|
GL.EnableVertexAttribArray(1);
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
||||||
|
|
||||||
|
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
231
Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
Normal file
231
Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
{
|
||||||
|
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 VbInfo
|
||||||
|
{
|
||||||
|
public int VaoHandle;
|
||||||
|
public int VboHandle;
|
||||||
|
|
||||||
|
public int PrimCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct IbInfo
|
||||||
|
{
|
||||||
|
public int IboHandle;
|
||||||
|
public int Count;
|
||||||
|
|
||||||
|
public DrawElementsType Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private VbInfo[] VertexBuffers;
|
||||||
|
|
||||||
|
private IbInfo IndexBuffer;
|
||||||
|
|
||||||
|
public OGLRasterizer()
|
||||||
|
{
|
||||||
|
VertexBuffers = new VbInfo[32];
|
||||||
|
|
||||||
|
IndexBuffer = new IbInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
VbInfo 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 SetIndexArray(byte[] Buffer, GalIndexFormat Format)
|
||||||
|
{
|
||||||
|
EnsureIbInitialized();
|
||||||
|
|
||||||
|
IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
|
||||||
|
|
||||||
|
IndexBuffer.Count = Buffer.Length >> (int)Format;
|
||||||
|
|
||||||
|
IntPtr Length = new IntPtr(Buffer.Length);
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle);
|
||||||
|
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
|
||||||
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawArrays(int VbIndex, GalPrimitiveType PrimType)
|
||||||
|
{
|
||||||
|
VbInfo Vb = VertexBuffers[VbIndex];
|
||||||
|
|
||||||
|
if (Vb.PrimCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.BindVertexArray(Vb.VaoHandle);
|
||||||
|
|
||||||
|
GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), 0, Vb.PrimCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
|
||||||
|
{
|
||||||
|
VbInfo Vb = VertexBuffers[VbIndex];
|
||||||
|
|
||||||
|
if (Vb.PrimCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
|
||||||
|
|
||||||
|
GL.BindVertexArray(Vb.VaoHandle);
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle);
|
||||||
|
|
||||||
|
GL.DrawElements(Mode, IndexBuffer.Count, IndexBuffer.Type, First);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureVbInitialized(int VbIndex)
|
||||||
|
{
|
||||||
|
VbInfo Vb = VertexBuffers[VbIndex];
|
||||||
|
|
||||||
|
if (Vb.VaoHandle == 0)
|
||||||
|
{
|
||||||
|
Vb.VaoHandle = GL.GenVertexArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Vb.VboHandle == 0)
|
||||||
|
{
|
||||||
|
Vb.VboHandle = GL.GenBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexBuffers[VbIndex] = Vb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureIbInitialized()
|
||||||
|
{
|
||||||
|
if (IndexBuffer.IboHandle == 0)
|
||||||
|
{
|
||||||
|
IndexBuffer.IboHandle = GL.GenBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
253
Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
Normal file
253
Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Graphics.Gal.Shader;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
{
|
||||||
|
class OGLShader
|
||||||
|
{
|
||||||
|
private class ShaderStage : IDisposable
|
||||||
|
{
|
||||||
|
public int Handle { get; private set; }
|
||||||
|
|
||||||
|
public bool IsCompiled { get; private set; }
|
||||||
|
|
||||||
|
public GalShaderType Type { get; private set; }
|
||||||
|
|
||||||
|
public string Code { get; private set; }
|
||||||
|
|
||||||
|
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
|
||||||
|
public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
|
||||||
|
|
||||||
|
public ShaderStage(
|
||||||
|
GalShaderType Type,
|
||||||
|
string Code,
|
||||||
|
IEnumerable<ShaderDeclInfo> TextureUsage,
|
||||||
|
IEnumerable<ShaderDeclInfo> UniformUsage)
|
||||||
|
{
|
||||||
|
this.Type = Type;
|
||||||
|
this.Code = Code;
|
||||||
|
this.TextureUsage = TextureUsage;
|
||||||
|
this.UniformUsage = UniformUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Compile()
|
||||||
|
{
|
||||||
|
if (Handle == 0)
|
||||||
|
{
|
||||||
|
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
|
||||||
|
|
||||||
|
CompileAndCheck(Handle, Code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool Disposing)
|
||||||
|
{
|
||||||
|
if (Disposing && Handle != 0)
|
||||||
|
{
|
||||||
|
GL.DeleteShader(Handle);
|
||||||
|
|
||||||
|
Handle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ShaderProgram
|
||||||
|
{
|
||||||
|
public ShaderStage Vertex;
|
||||||
|
public ShaderStage TessControl;
|
||||||
|
public ShaderStage TessEvaluation;
|
||||||
|
public ShaderStage Geometry;
|
||||||
|
public ShaderStage Fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShaderProgram Current;
|
||||||
|
|
||||||
|
private ConcurrentDictionary<long, ShaderStage> Stages;
|
||||||
|
|
||||||
|
private Dictionary<ShaderProgram, int> Programs;
|
||||||
|
|
||||||
|
public int CurrentProgramHandle { get; private set; }
|
||||||
|
|
||||||
|
public OGLShader()
|
||||||
|
{
|
||||||
|
Stages = new ConcurrentDictionary<long, ShaderStage>();
|
||||||
|
|
||||||
|
Programs = new Dictionary<ShaderProgram, int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Create(long Tag, GalShaderType Type, byte[] Data)
|
||||||
|
{
|
||||||
|
Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Type, Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShaderStage ShaderStageFactory(GalShaderType Type, byte[] Data)
|
||||||
|
{
|
||||||
|
GlslProgram Program = GetGlslProgram(Data, Type);
|
||||||
|
|
||||||
|
return new ShaderStage(
|
||||||
|
Type,
|
||||||
|
Program.Code,
|
||||||
|
Program.Textures,
|
||||||
|
Program.Uniforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GlslProgram GetGlslProgram(byte[] Data, GalShaderType Type)
|
||||||
|
{
|
||||||
|
int[] Code = new int[(Data.Length - 0x50) >> 2];
|
||||||
|
|
||||||
|
using (MemoryStream MS = new MemoryStream(Data))
|
||||||
|
{
|
||||||
|
MS.Seek(0x50, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
BinaryReader Reader = new BinaryReader(MS);
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Code.Length; Index++)
|
||||||
|
{
|
||||||
|
Code[Index] = Reader.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GlslDecompiler Decompiler = new GlslDecompiler();
|
||||||
|
|
||||||
|
return Decompiler.Decompile(Code, Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
|
||||||
|
{
|
||||||
|
if (Stages.TryGetValue(Tag, out ShaderStage Stage))
|
||||||
|
{
|
||||||
|
return Stage.TextureUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enumerable.Empty<ShaderDeclInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
|
||||||
|
{
|
||||||
|
BindProgram();
|
||||||
|
|
||||||
|
if (Stages.TryGetValue(Tag, out ShaderStage Stage))
|
||||||
|
{
|
||||||
|
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
|
||||||
|
{
|
||||||
|
float Value = BitConverter.ToSingle(Data, DeclInfo.Index * 4);
|
||||||
|
|
||||||
|
int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name);
|
||||||
|
|
||||||
|
GL.Uniform1(Location, Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetUniform1(string UniformName, int Value)
|
||||||
|
{
|
||||||
|
BindProgram();
|
||||||
|
|
||||||
|
int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
|
||||||
|
|
||||||
|
GL.Uniform1(Location, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Bind(long Tag)
|
||||||
|
{
|
||||||
|
if (Stages.TryGetValue(Tag, out ShaderStage Stage))
|
||||||
|
{
|
||||||
|
Bind(Stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Bind(ShaderStage Stage)
|
||||||
|
{
|
||||||
|
switch (Stage.Type)
|
||||||
|
{
|
||||||
|
case GalShaderType.Vertex: Current.Vertex = Stage; break;
|
||||||
|
case GalShaderType.TessControl: Current.TessControl = Stage; break;
|
||||||
|
case GalShaderType.TessEvaluation: Current.TessEvaluation = Stage; break;
|
||||||
|
case GalShaderType.Geometry: Current.Geometry = Stage; break;
|
||||||
|
case GalShaderType.Fragment: Current.Fragment = Stage; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BindProgram()
|
||||||
|
{
|
||||||
|
if (Current.Vertex == null ||
|
||||||
|
Current.Fragment == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Programs.TryGetValue(Current, out int Handle))
|
||||||
|
{
|
||||||
|
Handle = GL.CreateProgram();
|
||||||
|
|
||||||
|
AttachIfNotNull(Handle, Current.Vertex);
|
||||||
|
AttachIfNotNull(Handle, Current.TessControl);
|
||||||
|
AttachIfNotNull(Handle, Current.TessEvaluation);
|
||||||
|
AttachIfNotNull(Handle, Current.Geometry);
|
||||||
|
AttachIfNotNull(Handle, Current.Fragment);
|
||||||
|
|
||||||
|
GL.LinkProgram(Handle);
|
||||||
|
|
||||||
|
CheckProgramLink(Handle);
|
||||||
|
|
||||||
|
Programs.Add(Current, Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.UseProgram(Handle);
|
||||||
|
|
||||||
|
CurrentProgramHandle = Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AttachIfNotNull(int ProgramHandle, ShaderStage Stage)
|
||||||
|
{
|
||||||
|
if (Stage != null)
|
||||||
|
{
|
||||||
|
Stage.Compile();
|
||||||
|
|
||||||
|
GL.AttachShader(ProgramHandle, Stage.Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CompileAndCheck(int Handle, string Code)
|
||||||
|
{
|
||||||
|
GL.ShaderSource(Handle, Code);
|
||||||
|
GL.CompileShader(Handle);
|
||||||
|
|
||||||
|
CheckCompilation(Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CheckCompilation(int Handle)
|
||||||
|
{
|
||||||
|
int Status = 0;
|
||||||
|
|
||||||
|
GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status);
|
||||||
|
|
||||||
|
if (Status == 0)
|
||||||
|
{
|
||||||
|
throw new ShaderException(GL.GetShaderInfoLog(Handle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CheckProgramLink(int Handle)
|
||||||
|
{
|
||||||
|
int Status = 0;
|
||||||
|
|
||||||
|
GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out Status);
|
||||||
|
|
||||||
|
if (Status == 0)
|
||||||
|
{
|
||||||
|
throw new ShaderException(GL.GetProgramInfoLog(Handle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
96
Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
Normal file
96
Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
{
|
||||||
|
class OGLTexture
|
||||||
|
{
|
||||||
|
private int[] Textures;
|
||||||
|
|
||||||
|
public OGLTexture()
|
||||||
|
{
|
||||||
|
Textures = new int[80];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(int Index, GalTexture Tex)
|
||||||
|
{
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0 + Index);
|
||||||
|
|
||||||
|
int Handle = EnsureTextureInitialized(Index);
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
||||||
|
|
||||||
|
int W = Tex.Width;
|
||||||
|
int H = Tex.Height;
|
||||||
|
|
||||||
|
byte[] Data = Tex.Data;
|
||||||
|
|
||||||
|
int Length = Data.Length;
|
||||||
|
|
||||||
|
if (IsCompressedTextureFormat(Tex.Format))
|
||||||
|
{
|
||||||
|
PixelInternalFormat Pif = OGLEnumConverter.GetCompressedTextureFormat(Tex.Format);
|
||||||
|
|
||||||
|
GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Length, Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//TODO: Get those from Texture format.
|
||||||
|
const PixelInternalFormat Pif = PixelInternalFormat.Rgba;
|
||||||
|
|
||||||
|
const PixelFormat Pf = PixelFormat.Rgba;
|
||||||
|
|
||||||
|
const PixelType Pt = PixelType.UnsignedByte;
|
||||||
|
|
||||||
|
GL.TexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Pf, Pt, Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(int Index, GalTextureSampler Sampler)
|
||||||
|
{
|
||||||
|
int Handle = EnsureTextureInitialized(Index);
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
||||||
|
|
||||||
|
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
|
||||||
|
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
|
||||||
|
|
||||||
|
int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter);
|
||||||
|
int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter);
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, WrapS);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, WrapT);
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
|
||||||
|
|
||||||
|
float[] Color = new float[]
|
||||||
|
{
|
||||||
|
Sampler.BorderColor.Red,
|
||||||
|
Sampler.BorderColor.Green,
|
||||||
|
Sampler.BorderColor.Blue,
|
||||||
|
Sampler.BorderColor.Alpha
|
||||||
|
};
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCompressedTextureFormat(GalTextureFormat Format)
|
||||||
|
{
|
||||||
|
return Format == GalTextureFormat.BC1 ||
|
||||||
|
Format == GalTextureFormat.BC2 ||
|
||||||
|
Format == GalTextureFormat.BC3;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int EnsureTextureInitialized(int TexIndex)
|
||||||
|
{
|
||||||
|
int Handle = Textures[TexIndex];
|
||||||
|
|
||||||
|
if (Handle == 0)
|
||||||
|
{
|
||||||
|
Handle = Textures[TexIndex] = GL.GenTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics.OpenGL;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -8,22 +7,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
public class OpenGLRenderer : IGalRenderer
|
public class OpenGLRenderer : IGalRenderer
|
||||||
{
|
{
|
||||||
private struct VertexBuffer
|
private OGLBlend Blend;
|
||||||
{
|
|
||||||
public int VaoHandle;
|
|
||||||
public int VboHandle;
|
|
||||||
|
|
||||||
public int PrimCount;
|
private OGLFrameBuffer FrameBuffer;
|
||||||
}
|
|
||||||
|
|
||||||
private struct Texture
|
private OGLRasterizer Rasterizer;
|
||||||
{
|
|
||||||
public int Handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<VertexBuffer> VertexBuffers;
|
private OGLShader Shader;
|
||||||
|
|
||||||
private Texture[] Textures;
|
private OGLTexture Texture;
|
||||||
|
|
||||||
private ConcurrentQueue<Action> ActionsQueue;
|
private ConcurrentQueue<Action> ActionsQueue;
|
||||||
|
|
||||||
|
@ -31,9 +23,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
public OpenGLRenderer()
|
public OpenGLRenderer()
|
||||||
{
|
{
|
||||||
VertexBuffers = new List<VertexBuffer>();
|
Blend = new OGLBlend();
|
||||||
|
|
||||||
Textures = new Texture[8];
|
FrameBuffer = new OGLFrameBuffer();
|
||||||
|
|
||||||
|
Rasterizer = new OGLRasterizer();
|
||||||
|
|
||||||
|
Shader = new OGLShader();
|
||||||
|
|
||||||
|
Texture = new OGLTexture();
|
||||||
|
|
||||||
ActionsQueue = new ConcurrentQueue<Action>();
|
ActionsQueue = new ConcurrentQueue<Action>();
|
||||||
}
|
}
|
||||||
|
@ -66,18 +64,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)
|
||||||
|
@ -106,218 +92,161 @@ 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 SetBlendEnable(bool Enable)
|
||||||
{
|
{
|
||||||
if (Index < 0)
|
if (Enable)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
ActionsQueue.Enqueue(() => Blend.Enable());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (Buffer.Length == 0 || Stride == 0)
|
|
||||||
{
|
{
|
||||||
return;
|
ActionsQueue.Enqueue(() => Blend.Disable());
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureVbInitialized(Index);
|
|
||||||
|
|
||||||
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 SetBlend(
|
||||||
|
GalBlendEquation Equation,
|
||||||
|
GalBlendFactor FuncSrc,
|
||||||
|
GalBlendFactor FuncDst)
|
||||||
{
|
{
|
||||||
EnsureTexInitialized(Index);
|
ActionsQueue.Enqueue(() => Blend.Set(Equation, FuncSrc, FuncDst));
|
||||||
|
|
||||||
GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle);
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
|
||||||
GL.TexImage2D(TextureTarget.Texture2D,
|
|
||||||
0,
|
|
||||||
PixelInternalFormat.Rgba,
|
|
||||||
Width,
|
|
||||||
Height,
|
|
||||||
0,
|
|
||||||
PixelFormat.Rgba,
|
|
||||||
PixelType.UnsignedByte,
|
|
||||||
Buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BindTexture(int Index)
|
public void SetBlendSeparate(
|
||||||
|
GalBlendEquation EquationRgb,
|
||||||
|
GalBlendEquation EquationAlpha,
|
||||||
|
GalBlendFactor FuncSrcRgb,
|
||||||
|
GalBlendFactor FuncDstRgb,
|
||||||
|
GalBlendFactor FuncSrcAlpha,
|
||||||
|
GalBlendFactor FuncDstAlpha)
|
||||||
{
|
{
|
||||||
GL.ActiveTexture(TextureUnit.Texture0 + Index);
|
ActionsQueue.Enqueue(() =>
|
||||||
|
{
|
||||||
GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle);
|
Blend.SetSeparate(
|
||||||
|
EquationRgb,
|
||||||
|
EquationAlpha,
|
||||||
|
FuncSrcRgb,
|
||||||
|
FuncDstRgb,
|
||||||
|
FuncSrcAlpha,
|
||||||
|
FuncDstAlpha);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureVbInitialized(int VbIndex)
|
public void SetFb(int FbIndex, int Width, int Height)
|
||||||
{
|
{
|
||||||
while (VbIndex >= VertexBuffers.Count)
|
ActionsQueue.Enqueue(() => FrameBuffer.Set(FbIndex, Width, Height));
|
||||||
{
|
|
||||||
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)
|
public void BindFrameBuffer(int FbIndex)
|
||||||
{
|
{
|
||||||
Texture Tex = Textures[TexIndex];
|
ActionsQueue.Enqueue(() => FrameBuffer.Bind(FbIndex));
|
||||||
|
}
|
||||||
|
|
||||||
if (Tex.Handle == 0)
|
public void DrawFrameBuffer(int FbIndex)
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() => FrameBuffer.Draw(FbIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs)
|
||||||
|
{
|
||||||
|
if ((uint)VbIndex > 31)
|
||||||
{
|
{
|
||||||
Tex.Handle = GL.GenTexture();
|
throw new ArgumentOutOfRangeException(nameof(VbIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
Textures[TexIndex] = Tex;
|
ActionsQueue.Enqueue(() => Rasterizer.SetVertexArray(VbIndex, Stride,
|
||||||
|
Buffer ?? throw new ArgumentNullException(nameof(Buffer)),
|
||||||
|
Attribs ?? throw new ArgumentNullException(nameof(Attribs))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetIndexArray(byte[] Buffer, GalIndexFormat Format)
|
||||||
|
{
|
||||||
|
if (Buffer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(Buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Buffer, Format));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawArrays(int VbIndex, GalPrimitiveType PrimType)
|
||||||
|
{
|
||||||
|
if ((uint)VbIndex > 31)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(VbIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(VbIndex, PrimType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
|
||||||
|
{
|
||||||
|
if ((uint)VbIndex > 31)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(VbIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionsQueue.Enqueue(() => Rasterizer.DrawElements(VbIndex, First, PrimType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateShader(long Tag, GalShaderType Type, byte[] Data)
|
||||||
|
{
|
||||||
|
if (Data == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
Shader.Create(Tag, Type, Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
|
||||||
|
{
|
||||||
|
if (Data == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionsQueue.Enqueue(() => Shader.SetConstBuffer(Tag, Cbuf, Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetUniform1(string UniformName, int Value)
|
||||||
|
{
|
||||||
|
if (UniformName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(UniformName));
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionsQueue.Enqueue(() => Shader.SetUniform1(UniformName, Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
|
||||||
|
{
|
||||||
|
return Shader.GetTextureUsage(Tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BindShader(long Tag)
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() => Shader.Bind(Tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BindProgram()
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() => Shader.BindProgram());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTexture(int Index, GalTexture Tex)
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() => Texture.Set(Index, Tex));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSampler(int Index, GalTextureSampler Sampler)
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() => Texture.Set(Index, Sampler));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
212
Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
Normal file
212
Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class GlslDecl
|
||||||
|
{
|
||||||
|
public const int VertexIdAttr = 0x2fc;
|
||||||
|
public const int GlPositionWAttr = 0x7c;
|
||||||
|
|
||||||
|
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).Value;
|
||||||
|
|
||||||
|
int Index = Handle - TexStartIndex;
|
||||||
|
|
||||||
|
string Name = StagePrefix + TextureName + Index;
|
||||||
|
|
||||||
|
m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle));
|
||||||
|
}
|
||||||
|
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:
|
||||||
|
{
|
||||||
|
//This is a built-in input variable.
|
||||||
|
if (Abuf.Offs == VertexIdAttr)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
644
Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
Normal file
644
Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
Normal file
|
@ -0,0 +1,644 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class GlslDecompiler
|
||||||
|
{
|
||||||
|
private delegate string GetInstExpr(ShaderIrOp Op);
|
||||||
|
|
||||||
|
private Dictionary<ShaderIrInst, GetInstExpr> InstsExpr;
|
||||||
|
|
||||||
|
private enum OperType
|
||||||
|
{
|
||||||
|
Bool,
|
||||||
|
F32,
|
||||||
|
I32
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string IdentationStr = " ";
|
||||||
|
|
||||||
|
private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" };
|
||||||
|
|
||||||
|
private GlslDecl Decl;
|
||||||
|
|
||||||
|
private StringBuilder SB;
|
||||||
|
|
||||||
|
public GlslDecompiler()
|
||||||
|
{
|
||||||
|
InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
|
||||||
|
{
|
||||||
|
{ ShaderIrInst.And, GetAndExpr },
|
||||||
|
{ ShaderIrInst.Asr, GetAsrExpr },
|
||||||
|
{ ShaderIrInst.Band, GetBandExpr },
|
||||||
|
{ ShaderIrInst.Bnot, GetBnotExpr },
|
||||||
|
{ ShaderIrInst.Clt, GetCltExpr },
|
||||||
|
{ ShaderIrInst.Ceq, GetCeqExpr },
|
||||||
|
{ ShaderIrInst.Cle, GetCleExpr },
|
||||||
|
{ ShaderIrInst.Cgt, GetCgtExpr },
|
||||||
|
{ ShaderIrInst.Cne, GetCneExpr },
|
||||||
|
{ ShaderIrInst.Cge, GetCgeExpr },
|
||||||
|
{ ShaderIrInst.Exit, GetExitExpr },
|
||||||
|
{ ShaderIrInst.Fabs, GetFabsExpr },
|
||||||
|
{ ShaderIrInst.Fadd, GetFaddExpr },
|
||||||
|
{ ShaderIrInst.Fcos, GetFcosExpr },
|
||||||
|
{ ShaderIrInst.Fex2, GetFex2Expr },
|
||||||
|
{ ShaderIrInst.Ffma, GetFfmaExpr },
|
||||||
|
{ ShaderIrInst.Flg2, GetFlg2Expr },
|
||||||
|
{ ShaderIrInst.Fmul, GetFmulExpr },
|
||||||
|
{ ShaderIrInst.Fneg, GetFnegExpr },
|
||||||
|
{ ShaderIrInst.Frcp, GetFrcpExpr },
|
||||||
|
{ ShaderIrInst.Frsq, GetFrsqExpr },
|
||||||
|
{ ShaderIrInst.Fsin, GetFsinExpr },
|
||||||
|
{ ShaderIrInst.Ipa, GetIpaExpr },
|
||||||
|
{ ShaderIrInst.Kil, GetKilExpr },
|
||||||
|
{ ShaderIrInst.Lsr, GetLsrExpr },
|
||||||
|
{ ShaderIrInst.Not, GetNotExpr },
|
||||||
|
{ ShaderIrInst.Or, GetOrExpr },
|
||||||
|
{ ShaderIrInst.Stof, GetStofExpr },
|
||||||
|
{ ShaderIrInst.Utof, GetUtofExpr },
|
||||||
|
{ ShaderIrInst.Texr, GetTexrExpr },
|
||||||
|
{ ShaderIrInst.Texg, GetTexgExpr },
|
||||||
|
{ ShaderIrInst.Texb, GetTexbExpr },
|
||||||
|
{ ShaderIrInst.Texa, GetTexaExpr },
|
||||||
|
{ ShaderIrInst.Xor, GetXorExpr },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslProgram Decompile(int[] Code, GalShaderType ShaderType)
|
||||||
|
{
|
||||||
|
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, ShaderType);
|
||||||
|
|
||||||
|
ShaderIrNode[] Nodes = Block.GetNodes();
|
||||||
|
|
||||||
|
Decl = new GlslDecl(Nodes, ShaderType);
|
||||||
|
|
||||||
|
SB = new StringBuilder();
|
||||||
|
|
||||||
|
SB.AppendLine("#version 330 core");
|
||||||
|
|
||||||
|
PrintDeclTextures();
|
||||||
|
PrintDeclUniforms();
|
||||||
|
PrintDeclInAttributes();
|
||||||
|
PrintDeclOutAttributes();
|
||||||
|
PrintDeclGprs();
|
||||||
|
PrintDeclPreds();
|
||||||
|
|
||||||
|
PrintBlockScope("void main()", 1, Nodes);
|
||||||
|
|
||||||
|
string GlslCode = SB.ToString();
|
||||||
|
|
||||||
|
return new GlslProgram(
|
||||||
|
GlslCode,
|
||||||
|
Decl.Textures.Values,
|
||||||
|
Decl.Uniforms.Values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintDeclTextures()
|
||||||
|
{
|
||||||
|
PrintDecls(Decl.Textures, "uniform sampler2D");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintDeclUniforms()
|
||||||
|
{
|
||||||
|
foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))
|
||||||
|
{
|
||||||
|
SB.AppendLine($"uniform {GetDecl(DeclInfo)};");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Decl.Uniforms.Count > 0)
|
||||||
|
{
|
||||||
|
SB.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintDeclInAttributes()
|
||||||
|
{
|
||||||
|
PrintDeclAttributes(Decl.InAttributes.Values, "in");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintDeclOutAttributes()
|
||||||
|
{
|
||||||
|
PrintDeclAttributes(Decl.OutAttributes.Values, "out");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintDeclAttributes(IEnumerable<ShaderDeclInfo> Decls, string InOut)
|
||||||
|
{
|
||||||
|
int Count = 0;
|
||||||
|
|
||||||
|
foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector))
|
||||||
|
{
|
||||||
|
if (DeclInfo.Index >= 0)
|
||||||
|
{
|
||||||
|
SB.AppendLine($"layout (location = {DeclInfo.Index}) {InOut} {GetDecl(DeclInfo)};");
|
||||||
|
|
||||||
|
Count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Count > 0)
|
||||||
|
{
|
||||||
|
SB.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintDeclGprs()
|
||||||
|
{
|
||||||
|
PrintDecls(Decl.Gprs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintDeclPreds()
|
||||||
|
{
|
||||||
|
PrintDecls(Decl.Preds, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintDecls(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, string CustomType = null)
|
||||||
|
{
|
||||||
|
foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector))
|
||||||
|
{
|
||||||
|
string Name;
|
||||||
|
|
||||||
|
if (CustomType != null)
|
||||||
|
{
|
||||||
|
Name = CustomType + " " + DeclInfo.Name + ";";
|
||||||
|
}
|
||||||
|
else if (DeclInfo.Name == GlslDecl.FragmentOutputName)
|
||||||
|
{
|
||||||
|
Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Name = GetDecl(DeclInfo) + ";";
|
||||||
|
}
|
||||||
|
|
||||||
|
SB.AppendLine(Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Dict.Count > 0)
|
||||||
|
{
|
||||||
|
SB.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int DeclKeySelector(ShaderDeclInfo DeclInfo)
|
||||||
|
{
|
||||||
|
return DeclInfo.Cbuf << 24 | DeclInfo.Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetDecl(ShaderDeclInfo DeclInfo)
|
||||||
|
{
|
||||||
|
return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes)
|
||||||
|
{
|
||||||
|
string Identation = string.Empty;
|
||||||
|
|
||||||
|
for (int Index = 0; Index < IdentationLevel - 1; Index++)
|
||||||
|
{
|
||||||
|
Identation += IdentationStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ScopeName != string.Empty)
|
||||||
|
{
|
||||||
|
ScopeName += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
SB.AppendLine(Identation + ScopeName + "{");
|
||||||
|
|
||||||
|
string LastLine = Identation + "}";
|
||||||
|
|
||||||
|
if (IdentationLevel > 0)
|
||||||
|
{
|
||||||
|
Identation += IdentationStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Nodes.Length; Index++)
|
||||||
|
{
|
||||||
|
ShaderIrNode Node = Nodes[Index];
|
||||||
|
|
||||||
|
if (Node is ShaderIrCond Cond)
|
||||||
|
{
|
||||||
|
string SubScopeName = "if (" + GetSrcExpr(Cond.Pred, true) + ")";
|
||||||
|
|
||||||
|
PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child);
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrAsg Asg && IsValidOutOper(Asg.Dst))
|
||||||
|
{
|
||||||
|
string Expr = GetSrcExpr(Asg.Src, true);
|
||||||
|
|
||||||
|
Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
|
||||||
|
|
||||||
|
SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SB.AppendLine(LastLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsValidOutOper(ShaderIrNode Node)
|
||||||
|
{
|
||||||
|
if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrOperPred Pred && Pred.IsConst)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetDstOperName(ShaderIrNode Node)
|
||||||
|
{
|
||||||
|
if (Node is ShaderIrOperAbuf Abuf)
|
||||||
|
{
|
||||||
|
return GetOutAbufName(Abuf);
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrOperGpr Gpr)
|
||||||
|
{
|
||||||
|
return GetName(Gpr);
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrOperPred Pred)
|
||||||
|
{
|
||||||
|
return GetName(Pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(Node));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSrcExpr(ShaderIrNode Node, bool Entry = false)
|
||||||
|
{
|
||||||
|
switch (Node)
|
||||||
|
{
|
||||||
|
case ShaderIrOperAbuf Abuf: return GetName (Abuf);
|
||||||
|
case ShaderIrOperCbuf Cbuf: return GetName (Cbuf);
|
||||||
|
case ShaderIrOperGpr Gpr: return GetName (Gpr);
|
||||||
|
case ShaderIrOperImm Imm: return GetValue(Imm);
|
||||||
|
case ShaderIrOperImmf Immf: return GetValue(Immf);
|
||||||
|
case ShaderIrOperPred Pred: return GetName (Pred);
|
||||||
|
|
||||||
|
case ShaderIrOp Op:
|
||||||
|
string Expr;
|
||||||
|
|
||||||
|
if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr))
|
||||||
|
{
|
||||||
|
Expr = GetExpr(Op);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException(Op.Inst.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Entry && NeedsParentheses(Op))
|
||||||
|
{
|
||||||
|
Expr = "(" + Expr + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Expr;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool NeedsParentheses(ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
switch (Op.Inst)
|
||||||
|
{
|
||||||
|
case ShaderIrInst.Frcp:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case ShaderIrInst.Ipa:
|
||||||
|
case ShaderIrInst.Texr:
|
||||||
|
case ShaderIrInst.Texg:
|
||||||
|
case ShaderIrInst.Texb:
|
||||||
|
case ShaderIrInst.Texa:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Op.OperandB != null ||
|
||||||
|
Op.OperandC != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetName(ShaderIrOperCbuf Cbuf)
|
||||||
|
{
|
||||||
|
if (!Decl.Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out ShaderDeclInfo DeclInfo))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeclInfo.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetOutAbufName(ShaderIrOperAbuf Abuf)
|
||||||
|
{
|
||||||
|
return GetName(Decl.OutAttributes, Abuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetName(ShaderIrOperAbuf Abuf)
|
||||||
|
{
|
||||||
|
if (Abuf.Offs == GlslDecl.GlPositionWAttr && Decl.ShaderType == GalShaderType.Fragment)
|
||||||
|
{
|
||||||
|
return "(1f / gl_FragCoord.w)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Abuf.Offs == GlslDecl.VertexIdAttr)
|
||||||
|
{
|
||||||
|
return "gl_VertexID";
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetName(Decl.InAttributes, Abuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetName(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, ShaderIrOperAbuf Abuf)
|
||||||
|
{
|
||||||
|
int Index = Abuf.Offs >> 4;
|
||||||
|
int Elem = (Abuf.Offs >> 2) & 3;
|
||||||
|
|
||||||
|
if (!Dict.TryGetValue(Index, out ShaderDeclInfo DeclInfo))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeclInfo.Size > 1 ? DeclInfo.Name + "." + GetAttrSwizzle(Elem) : DeclInfo.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetName(ShaderIrOperGpr Gpr)
|
||||||
|
{
|
||||||
|
return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetValue(ShaderIrOperImm Imm)
|
||||||
|
{
|
||||||
|
//Only use hex is the value is too big and would likely be hard to read as int.
|
||||||
|
if (Imm.Value > 0xfff ||
|
||||||
|
Imm.Value < -0xfff)
|
||||||
|
{
|
||||||
|
return "0x" + Imm.Value.ToString("x8", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Imm.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetValue(ShaderIrOperImmf Immf)
|
||||||
|
{
|
||||||
|
return Immf.Value.ToString(CultureInfo.InvariantCulture) + "f";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetName(ShaderIrOperPred Pred)
|
||||||
|
{
|
||||||
|
return Pred.IsConst ? "true" : GetNameWithSwizzle(Decl.Preds, Pred.Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetNameWithSwizzle(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, int Index)
|
||||||
|
{
|
||||||
|
int VecIndex = Index >> 2;
|
||||||
|
|
||||||
|
if (Dict.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo))
|
||||||
|
{
|
||||||
|
if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size)
|
||||||
|
{
|
||||||
|
return DeclInfo.Name + "." + GetAttrSwizzle(Index & 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Dict.TryGetValue(Index, out DeclInfo))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeclInfo.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetAttrSwizzle(int Elem)
|
||||||
|
{
|
||||||
|
return "xyzw".Substring(Elem, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetAndExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&");
|
||||||
|
|
||||||
|
private string GetAsrExpr(ShaderIrOp Op) => GetBinaryExpr(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, "1f / ");
|
||||||
|
|
||||||
|
private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt");
|
||||||
|
|
||||||
|
private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin");
|
||||||
|
|
||||||
|
private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA);
|
||||||
|
|
||||||
|
private string GetKilExpr(ShaderIrOp Op) => "discard";
|
||||||
|
|
||||||
|
private string GetLsrExpr(ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
return "int(uint(" + GetOperExpr(Op, Op.OperandA) + ") >> " +
|
||||||
|
GetOperExpr(Op, Op.OperandB) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetNotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "~");
|
||||||
|
|
||||||
|
private string GetOrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "|");
|
||||||
|
|
||||||
|
private string GetStofExpr(ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
return "float(" + GetOperExpr(Op, Op.OperandA) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetUtofExpr(ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetXorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^");
|
||||||
|
|
||||||
|
private string GetUnaryCall(ShaderIrOp Op, string FuncName)
|
||||||
|
{
|
||||||
|
return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetUnaryExpr(ShaderIrOp Op, string Opr)
|
||||||
|
{
|
||||||
|
return Opr + GetOperExpr(Op, Op.OperandA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetBinaryExpr(ShaderIrOp Op, string Opr)
|
||||||
|
{
|
||||||
|
return GetOperExpr(Op, Op.OperandA) + " " + Opr + " " +
|
||||||
|
GetOperExpr(Op, Op.OperandB);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2)
|
||||||
|
{
|
||||||
|
return GetOperExpr(Op, Op.OperandA) + " " + Opr1 + " " +
|
||||||
|
GetOperExpr(Op, Op.OperandB) + " " + Opr2 + " " +
|
||||||
|
GetOperExpr(Op, Op.OperandC);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTexrExpr(ShaderIrOp Op) => GetTexExpr(Op, 'r');
|
||||||
|
private string GetTexgExpr(ShaderIrOp Op) => GetTexExpr(Op, 'g');
|
||||||
|
private string GetTexbExpr(ShaderIrOp Op) => GetTexExpr(Op, 'b');
|
||||||
|
private string GetTexaExpr(ShaderIrOp Op) => GetTexExpr(Op, 'a');
|
||||||
|
|
||||||
|
private string GetTexExpr(ShaderIrOp Op, char Ch)
|
||||||
|
{
|
||||||
|
return $"texture({GetTexSamplerName(Op)}, {GetTexSamplerCoords(Op)}).{Ch}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTexSamplerName(ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC;
|
||||||
|
|
||||||
|
int Handle = ((ShaderIrOperImm)Op.OperandC).Value;
|
||||||
|
|
||||||
|
if (!Decl.Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeclInfo.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTexSamplerCoords(ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " +
|
||||||
|
GetOperExpr(Op, Op.OperandB) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper)
|
||||||
|
{
|
||||||
|
return GetExprWithCast(Op, Oper, GetSrcExpr(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetExprWithCast(ShaderIrNode Dst, ShaderIrNode Src, string Expr)
|
||||||
|
{
|
||||||
|
//Note: The "DstType" (of the cast) is the type that the operation
|
||||||
|
//uses on the source operands, while the "SrcType" is the destination
|
||||||
|
//type of the operand result (if it is a operation) or just the type
|
||||||
|
//of the variable for registers/uniforms/attributes.
|
||||||
|
OperType DstType = GetSrcNodeType(Dst);
|
||||||
|
OperType SrcType = GetDstNodeType(Src);
|
||||||
|
|
||||||
|
if (DstType != SrcType)
|
||||||
|
{
|
||||||
|
//Check for invalid casts
|
||||||
|
//(like bool to int/float and others).
|
||||||
|
if (SrcType != OperType.F32 &&
|
||||||
|
SrcType != OperType.I32)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
//For integer immediates being used as float,
|
||||||
|
//it's better (for readability) to just return the float value.
|
||||||
|
if (Src is ShaderIrOperImm Imm && DstType == OperType.F32)
|
||||||
|
{
|
||||||
|
float Value = BitConverter.Int32BitsToSingle(Imm.Value);
|
||||||
|
|
||||||
|
return Value.ToString(CultureInfo.InvariantCulture) + "f";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (DstType)
|
||||||
|
{
|
||||||
|
case OperType.F32: Expr = "intBitsToFloat(" + Expr + ")"; break;
|
||||||
|
case OperType.I32: Expr = "floatBitsToInt(" + Expr + ")"; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OperType GetDstNodeType(ShaderIrNode Node)
|
||||||
|
{
|
||||||
|
if (Node is ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
switch (Op.Inst)
|
||||||
|
{
|
||||||
|
case ShaderIrInst.Stof: return OperType.F32;
|
||||||
|
case ShaderIrInst.Utof: return OperType.F32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetSrcNodeType(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OperType GetSrcNodeType(ShaderIrNode Node)
|
||||||
|
{
|
||||||
|
switch (Node)
|
||||||
|
{
|
||||||
|
case ShaderIrOperAbuf Abuf:
|
||||||
|
return Abuf.Offs == GlslDecl.VertexIdAttr
|
||||||
|
? OperType.I32
|
||||||
|
: OperType.F32;
|
||||||
|
|
||||||
|
case ShaderIrOperCbuf Cbuf: return OperType.F32;
|
||||||
|
case ShaderIrOperGpr Gpr: return OperType.F32;
|
||||||
|
case ShaderIrOperImm Imm: return OperType.I32;
|
||||||
|
case ShaderIrOperImmf Immf: return OperType.F32;
|
||||||
|
case ShaderIrOperPred Pred: return OperType.Bool;
|
||||||
|
|
||||||
|
case ShaderIrOp Op:
|
||||||
|
if (Op.Inst > ShaderIrInst.B_Start &&
|
||||||
|
Op.Inst < ShaderIrInst.B_End)
|
||||||
|
{
|
||||||
|
return OperType.Bool;
|
||||||
|
}
|
||||||
|
else if (Op.Inst > ShaderIrInst.F_Start &&
|
||||||
|
Op.Inst < ShaderIrInst.F_End)
|
||||||
|
{
|
||||||
|
return OperType.F32;
|
||||||
|
}
|
||||||
|
else if (Op.Inst > ShaderIrInst.I_Start &&
|
||||||
|
Op.Inst < ShaderIrInst.I_End)
|
||||||
|
{
|
||||||
|
return OperType.I32;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(Node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
Ryujinx.Graphics/Gal/Shader/GlslProgram.cs
Normal file
22
Ryujinx.Graphics/Gal/Shader/GlslProgram.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
struct GlslProgram
|
||||||
|
{
|
||||||
|
public string Code { get; private set; }
|
||||||
|
|
||||||
|
public IEnumerable<ShaderDeclInfo> Textures { get; private set; }
|
||||||
|
public IEnumerable<ShaderDeclInfo> Uniforms { get; private set; }
|
||||||
|
|
||||||
|
public GlslProgram(
|
||||||
|
string Code,
|
||||||
|
IEnumerable<ShaderDeclInfo> Textures,
|
||||||
|
IEnumerable<ShaderDeclInfo> Uniforms)
|
||||||
|
{
|
||||||
|
this.Code = Code;
|
||||||
|
this.Textures = Textures;
|
||||||
|
this.Uniforms = Uniforms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs
Normal file
4
Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode);
|
||||||
|
}
|
315
Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
Normal file
315
Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
static partial class ShaderDecode
|
||||||
|
{
|
||||||
|
public static void Fadd_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fadd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fadd_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluBinaryF(Block, OpCode, ShaderOper.Immf, ShaderIrInst.Fadd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fadd_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fadd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Ffma_CR(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluFfma(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Ffma_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluFfma(Block, OpCode, ShaderOper.Immf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Ffma_RC(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluFfma(Block, OpCode, ShaderOper.RC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Ffma_RR(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluFfma(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fmul_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fmul);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fmul_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluBinaryF(Block, OpCode, ShaderOper.Immf, ShaderIrInst.Fmul);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fmul_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fmul);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fsetp_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitFsetp(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fsetp_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitFsetp(Block, OpCode, ShaderOper.Immf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fsetp_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitFsetp(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Ipa(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
ShaderIrNode OperA = GetOperAbuf28(OpCode);
|
||||||
|
ShaderIrNode OperB = GetOperGpr20 (OpCode);
|
||||||
|
|
||||||
|
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ipa, OperA, OperB);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Lop32i(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
int SubOp = (int)(OpCode >> 53) & 3;
|
||||||
|
|
||||||
|
bool Ia = ((OpCode >> 55) & 1) != 0;
|
||||||
|
bool Ib = ((OpCode >> 56) & 1) != 0;
|
||||||
|
|
||||||
|
ShaderIrInst Inst = 0;
|
||||||
|
|
||||||
|
switch (SubOp)
|
||||||
|
{
|
||||||
|
case 0: Inst = ShaderIrInst.And; break;
|
||||||
|
case 1: Inst = ShaderIrInst.Or; break;
|
||||||
|
case 2: Inst = ShaderIrInst.Xor; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), Ia);
|
||||||
|
|
||||||
|
//SubOp == 3 is pass, used by the not instruction
|
||||||
|
//which just moves the inverted register value.
|
||||||
|
if (SubOp < 3)
|
||||||
|
{
|
||||||
|
ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), Ib);
|
||||||
|
|
||||||
|
ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Mufu(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
int SubOp = (int)(OpCode >> 20) & 7;
|
||||||
|
|
||||||
|
bool Aa = ((OpCode >> 46) & 1) != 0;
|
||||||
|
bool Na = ((OpCode >> 48) & 1) != 0;
|
||||||
|
|
||||||
|
ShaderIrInst Inst = 0;
|
||||||
|
|
||||||
|
switch (SubOp)
|
||||||
|
{
|
||||||
|
case 0: Inst = ShaderIrInst.Fcos; break;
|
||||||
|
case 1: Inst = ShaderIrInst.Fsin; break;
|
||||||
|
case 2: Inst = ShaderIrInst.Fex2; break;
|
||||||
|
case 3: Inst = ShaderIrInst.Flg2; break;
|
||||||
|
case 4: Inst = ShaderIrInst.Frcp; break;
|
||||||
|
case 5: Inst = ShaderIrInst.Frsq; break;
|
||||||
|
|
||||||
|
default: throw new NotImplementedException(SubOp.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrNode OperA = GetOperGpr8(OpCode);
|
||||||
|
|
||||||
|
ShaderIrOp Op = new ShaderIrOp(Inst, GetAluAbsNeg(OperA, Aa, Na));
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Shr_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Shr_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluBinary(Block, OpCode, ShaderOper.Imm, GetShrInst(OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Shr_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitAluBinary(Block, OpCode, ShaderOper.RR, GetShrInst(OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ShaderIrInst GetShrInst(long OpCode)
|
||||||
|
{
|
||||||
|
bool Signed = ((OpCode >> 48) & 1) != 0;
|
||||||
|
|
||||||
|
return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitAluBinary(
|
||||||
|
ShaderIrBlock Block,
|
||||||
|
long OpCode,
|
||||||
|
ShaderOper Oper,
|
||||||
|
ShaderIrInst Inst)
|
||||||
|
{
|
||||||
|
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitAluBinaryF(
|
||||||
|
ShaderIrBlock Block,
|
||||||
|
long OpCode,
|
||||||
|
ShaderOper Oper,
|
||||||
|
ShaderIrInst Inst)
|
||||||
|
{
|
||||||
|
bool Nb = ((OpCode >> 45) & 1) != 0;
|
||||||
|
bool Aa = ((OpCode >> 46) & 1) != 0;
|
||||||
|
bool Na = ((OpCode >> 48) & 1) != 0;
|
||||||
|
bool Ab = ((OpCode >> 49) & 1) != 0;
|
||||||
|
bool Ad = ((OpCode >> 50) & 1) != 0;
|
||||||
|
|
||||||
|
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
|
||||||
|
|
||||||
|
if (Inst == ShaderIrInst.Fadd)
|
||||||
|
{
|
||||||
|
OperA = GetAluAbsNeg(OperA, Aa, Na);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
OperB = GetAluAbsNeg(OperB, Ab, Nb);
|
||||||
|
|
||||||
|
ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
|
||||||
|
|
||||||
|
Op = GetAluAbs(Op, Ad);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitAluFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
bool Nb = ((OpCode >> 48) & 1) != 0;
|
||||||
|
bool Nc = ((OpCode >> 49) & 1) != 0;
|
||||||
|
|
||||||
|
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
|
||||||
|
case ShaderOper.RC: OperB = GetOperGpr39 (OpCode); break;
|
||||||
|
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
OperB = GetAluNeg(OperB, Nb);
|
||||||
|
|
||||||
|
if (Oper == ShaderOper.RC)
|
||||||
|
{
|
||||||
|
OperC = GetAluNeg(GetOperCbuf34(OpCode), Nc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OperC = GetAluNeg(GetOperGpr39(OpCode), Nc);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
bool Aa = ((OpCode >> 7) & 1) != 0;
|
||||||
|
bool Np = ((OpCode >> 42) & 1) != 0;
|
||||||
|
bool Na = ((OpCode >> 43) & 1) != 0;
|
||||||
|
bool Ab = ((OpCode >> 44) & 1) != 0;
|
||||||
|
|
||||||
|
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrInst CmpInst = GetCmp(OpCode);
|
||||||
|
|
||||||
|
ShaderIrOp Op = new ShaderIrOp(CmpInst,
|
||||||
|
GetAluAbsNeg(OperA, Aa, Na),
|
||||||
|
GetAluAbs (OperB, Ab));
|
||||||
|
|
||||||
|
ShaderIrOperPred P0Node = GetOperPred3 (OpCode);
|
||||||
|
ShaderIrOperPred P1Node = GetOperPred0 (OpCode);
|
||||||
|
ShaderIrOperPred P2Node = GetOperPred39(OpCode);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
|
||||||
|
|
||||||
|
ShaderIrInst LopInst = GetBLop(OpCode);
|
||||||
|
|
||||||
|
if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrNode P2NNode = P2Node;
|
||||||
|
|
||||||
|
if (Np)
|
||||||
|
{
|
||||||
|
P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node);
|
||||||
|
|
||||||
|
Op = new ShaderIrOp(LopInst, Op, P2NNode);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode));
|
||||||
|
|
||||||
|
Op = new ShaderIrOp(LopInst, P0Node, P2NNode);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
Normal file
17
Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
static partial class ShaderDecode
|
||||||
|
{
|
||||||
|
public static void Exit(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Exit), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Kil(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Kil), OpCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
211
Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
Normal file
211
Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
static class ShaderDecodeHelper
|
||||||
|
{
|
||||||
|
public static ShaderIrOperAbuf[] GetOperAbuf20(long OpCode)
|
||||||
|
{
|
||||||
|
int Abuf = (int)(OpCode >> 20) & 0x3ff;
|
||||||
|
int Reg = (int)(OpCode >> 39) & 0xff;
|
||||||
|
int Size = (int)(OpCode >> 47) & 3;
|
||||||
|
|
||||||
|
ShaderIrOperAbuf[] Opers = new ShaderIrOperAbuf[Size + 1];
|
||||||
|
|
||||||
|
for (int Index = 0; Index <= Size; Index++)
|
||||||
|
{
|
||||||
|
Opers[Index] = new ShaderIrOperAbuf(Abuf, Reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Opers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperAbuf GetOperAbuf28(long OpCode)
|
||||||
|
{
|
||||||
|
int Abuf = (int)(OpCode >> 28) & 0x3ff;
|
||||||
|
int Reg = (int)(OpCode >> 39) & 0xff;
|
||||||
|
|
||||||
|
return new ShaderIrOperAbuf(Abuf, Reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperCbuf GetOperCbuf34(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperCbuf(
|
||||||
|
(int)(OpCode >> 34) & 0x1f,
|
||||||
|
(int)(OpCode >> 20) & 0x3fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperGpr GetOperGpr8(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperGpr((int)(OpCode >> 8) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperGpr GetOperGpr20(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperGpr((int)(OpCode >> 20) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperGpr GetOperGpr39(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperGpr((int)(OpCode >> 39) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperGpr GetOperGpr0(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperGpr((int)(OpCode >> 0) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperGpr GetOperGpr28(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperGpr((int)(OpCode >> 28) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrNode GetOperImm19_20(long OpCode)
|
||||||
|
{
|
||||||
|
int Value = (int)(OpCode >> 20) & 0x7ffff;
|
||||||
|
|
||||||
|
bool Neg = ((OpCode >> 56) & 1) != 0;
|
||||||
|
|
||||||
|
if (Neg)
|
||||||
|
{
|
||||||
|
Value = -Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ShaderIrOperImm((int)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrNode GetOperImmf19_20(long OpCode)
|
||||||
|
{
|
||||||
|
uint Imm = (uint)(OpCode >> 20) & 0x7ffff;
|
||||||
|
|
||||||
|
bool Neg = ((OpCode >> 56) & 1) != 0;
|
||||||
|
|
||||||
|
Imm <<= 12;
|
||||||
|
|
||||||
|
if (Neg)
|
||||||
|
{
|
||||||
|
Imm |= 0x80000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Value = BitConverter.Int32BitsToSingle((int)Imm);
|
||||||
|
|
||||||
|
return new ShaderIrOperImmf(Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperImm GetOperImm13_36(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperImm GetOperImm32_20(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperImm((int)(OpCode >> 20));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperPred GetOperPred3(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperPred((int)(OpCode >> 3) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperPred GetOperPred0(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperPred((int)(OpCode >> 0) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrNode GetOperPred39N(long OpCode)
|
||||||
|
{
|
||||||
|
ShaderIrNode Node = GetOperPred39(OpCode);
|
||||||
|
|
||||||
|
if (((OpCode >> 42) & 1) != 0)
|
||||||
|
{
|
||||||
|
Node = new ShaderIrOp(ShaderIrInst.Bnot, Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperPred GetOperPred39(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperPred((int)(OpCode >> 39) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrInst GetCmp(long OpCode)
|
||||||
|
{
|
||||||
|
switch ((int)(OpCode >> 48) & 0xf)
|
||||||
|
{
|
||||||
|
case 0x1: return ShaderIrInst.Clt;
|
||||||
|
case 0x2: return ShaderIrInst.Ceq;
|
||||||
|
case 0x3: return ShaderIrInst.Cle;
|
||||||
|
case 0x4: return ShaderIrInst.Cgt;
|
||||||
|
case 0x5: return ShaderIrInst.Cne;
|
||||||
|
case 0x6: return ShaderIrInst.Cge;
|
||||||
|
case 0x7: return ShaderIrInst.Cnum;
|
||||||
|
case 0x8: return ShaderIrInst.Cnan;
|
||||||
|
case 0x9: return ShaderIrInst.Cltu;
|
||||||
|
case 0xa: return ShaderIrInst.Cequ;
|
||||||
|
case 0xb: return ShaderIrInst.Cleu;
|
||||||
|
case 0xc: return ShaderIrInst.Cgtu;
|
||||||
|
case 0xd: return ShaderIrInst.Cneu;
|
||||||
|
case 0xe: return ShaderIrInst.Cgeu;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrInst GetBLop(long OpCode)
|
||||||
|
{
|
||||||
|
switch ((int)(OpCode >> 45) & 3)
|
||||||
|
{
|
||||||
|
case 0: return ShaderIrInst.Band;
|
||||||
|
case 1: return ShaderIrInst.Bor;
|
||||||
|
case 2: return ShaderIrInst.Bxor;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrNode GetPredNode(ShaderIrNode Node, long OpCode)
|
||||||
|
{
|
||||||
|
ShaderIrOperPred Pred = GetPredNode(OpCode);
|
||||||
|
|
||||||
|
if (Pred.Index != ShaderIrOperPred.UnusedIndex)
|
||||||
|
{
|
||||||
|
Node = new ShaderIrCond(Pred, Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ShaderIrOperPred GetPredNode(long OpCode)
|
||||||
|
{
|
||||||
|
int Pred = (int)(OpCode >> 16) & 0xf;
|
||||||
|
|
||||||
|
if (Pred != 0xf)
|
||||||
|
{
|
||||||
|
Pred &= 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ShaderIrOperPred(Pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrNode GetAluAbsNeg(ShaderIrNode Node, bool Abs, bool Neg)
|
||||||
|
{
|
||||||
|
return GetAluNeg(GetAluAbs(Node, Abs), Neg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrNode GetAluAbs(ShaderIrNode Node, bool Abs)
|
||||||
|
{
|
||||||
|
return Abs ? new ShaderIrOp(ShaderIrInst.Fabs, Node) : Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrNode GetAluNeg(ShaderIrNode Node, bool Neg)
|
||||||
|
{
|
||||||
|
return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderIrNode GetAluNot(ShaderIrNode Node, bool Not)
|
||||||
|
{
|
||||||
|
return Not ? new ShaderIrOp(ShaderIrInst.Not, Node) : Node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
Normal file
59
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
static partial class ShaderDecode
|
||||||
|
{
|
||||||
|
public static void Ld_A(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
|
||||||
|
|
||||||
|
int Index = 0;
|
||||||
|
|
||||||
|
foreach (ShaderIrNode OperA in Opers)
|
||||||
|
{
|
||||||
|
ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
|
||||||
|
|
||||||
|
OperD.Index += Index++;
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, OperA), OpCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void St_A(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
|
||||||
|
|
||||||
|
int Index = 0;
|
||||||
|
|
||||||
|
foreach (ShaderIrNode OperA in Opers)
|
||||||
|
{
|
||||||
|
ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
|
||||||
|
|
||||||
|
OperD.Index += Index++;
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, OperD), OpCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Texs(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
//TODO: Support other formats.
|
||||||
|
ShaderIrNode OperA = GetOperGpr8 (OpCode);
|
||||||
|
ShaderIrNode OperB = GetOperGpr20 (OpCode);
|
||||||
|
ShaderIrNode OperC = GetOperGpr28 (OpCode);
|
||||||
|
ShaderIrNode OperD = GetOperImm13_36(OpCode);
|
||||||
|
|
||||||
|
for (int Ch = 0; Ch < 4; Ch++)
|
||||||
|
{
|
||||||
|
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Texr + Ch, OperA, OperB, OperD);
|
||||||
|
|
||||||
|
ShaderIrOperGpr Dst = GetOperGpr0(OpCode);
|
||||||
|
|
||||||
|
Dst.Index += Ch;
|
||||||
|
|
||||||
|
Block.AddNode(new ShaderIrAsg(Dst, Op));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
Normal file
128
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
static partial class ShaderDecode
|
||||||
|
{
|
||||||
|
private enum IntType
|
||||||
|
{
|
||||||
|
U8 = 0,
|
||||||
|
U16 = 1,
|
||||||
|
U32 = 2,
|
||||||
|
U64 = 3,
|
||||||
|
S8 = 4,
|
||||||
|
S16 = 5,
|
||||||
|
S32 = 6,
|
||||||
|
S64 = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum FloatType
|
||||||
|
{
|
||||||
|
F16 = 1,
|
||||||
|
F32 = 2,
|
||||||
|
F64 = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void I2f_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitI2f(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void I2f_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitI2f(Block, OpCode, ShaderOper.Imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void I2f_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitI2f(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitI2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
IntType Type = GetIntType(OpCode);
|
||||||
|
|
||||||
|
if (Type == IntType.U64 ||
|
||||||
|
Type == IntType.S64)
|
||||||
|
{
|
||||||
|
//TODO: 64-bits support.
|
||||||
|
//Note: GLSL doesn't support 64-bits integers.
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sel = (int)(OpCode >> 41) & 3;
|
||||||
|
|
||||||
|
bool Na = ((OpCode >> 45) & 1) != 0;
|
||||||
|
bool Aa = ((OpCode >> 49) & 1) != 0;
|
||||||
|
|
||||||
|
ShaderIrNode OperA;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperA = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Imm: OperA = GetOperImm19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: OperA = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
OperA = GetAluAbsNeg(OperA, Aa, Na);
|
||||||
|
|
||||||
|
bool Signed = Type >= IntType.S8;
|
||||||
|
|
||||||
|
int Shift = Sel * 8;
|
||||||
|
|
||||||
|
int Size = 8 << ((int)Type & 3);
|
||||||
|
|
||||||
|
ulong Mask = ulong.MaxValue >> (64 - Size);
|
||||||
|
|
||||||
|
int Mask32 = (int)Mask;
|
||||||
|
|
||||||
|
if (Shift != 0)
|
||||||
|
{
|
||||||
|
OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mask != uint.MaxValue)
|
||||||
|
{
|
||||||
|
OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm(Mask32));
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrInst Inst = Signed
|
||||||
|
? ShaderIrInst.Stof
|
||||||
|
: ShaderIrInst.Utof;
|
||||||
|
|
||||||
|
ShaderIrNode Op = new ShaderIrOp(Inst, OperA);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Mov32i(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IntType GetIntType(long OpCode)
|
||||||
|
{
|
||||||
|
bool Signed = ((OpCode >> 13) & 1) != 0;
|
||||||
|
|
||||||
|
IntType Type = (IntType)((OpCode >> 10) & 3);
|
||||||
|
|
||||||
|
if (Signed)
|
||||||
|
{
|
||||||
|
Type += (int)IntType.S8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FloatType GetFloatType(long OpCode)
|
||||||
|
{
|
||||||
|
return (FloatType)((OpCode >> 8) & 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
Normal file
41
Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
static class ShaderDecoder
|
||||||
|
{
|
||||||
|
public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, GalShaderType ShaderType)
|
||||||
|
{
|
||||||
|
ShaderIrBlock Block = new ShaderIrBlock();
|
||||||
|
|
||||||
|
while (Offset + 2 <= Code.Length)
|
||||||
|
{
|
||||||
|
uint Word0 = (uint)Code[Offset++];
|
||||||
|
uint Word1 = (uint)Code[Offset++];
|
||||||
|
|
||||||
|
long OpCode = Word0 | (long)Word1 << 32;
|
||||||
|
|
||||||
|
ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode);
|
||||||
|
|
||||||
|
if (Decode == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Decode(Block, OpCode);
|
||||||
|
|
||||||
|
if (Block.GetLastNode() is ShaderIrOp Op && IsFlowChange(Op.Inst))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Block.RunOptimizationPasses(ShaderType);
|
||||||
|
|
||||||
|
return Block;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsFlowChange(ShaderIrInst Inst)
|
||||||
|
{
|
||||||
|
return Inst == ShaderIrInst.Exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs
Normal file
14
Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrAsg : ShaderIrNode
|
||||||
|
{
|
||||||
|
public ShaderIrNode Dst { get; set; }
|
||||||
|
public ShaderIrNode Src { get; set; }
|
||||||
|
|
||||||
|
public ShaderIrAsg(ShaderIrNode Dst, ShaderIrNode Src)
|
||||||
|
{
|
||||||
|
this.Dst = Dst;
|
||||||
|
this.Src = Src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
Normal file
39
Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrBlock
|
||||||
|
{
|
||||||
|
private List<ShaderIrNode> Nodes;
|
||||||
|
|
||||||
|
public ShaderIrBlock()
|
||||||
|
{
|
||||||
|
Nodes = new List<ShaderIrNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddNode(ShaderIrNode Node)
|
||||||
|
{
|
||||||
|
Nodes.Add(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RunOptimizationPasses(GalShaderType ShaderType)
|
||||||
|
{
|
||||||
|
ShaderOptExprProp.Optimize(Nodes, ShaderType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderIrNode[] GetNodes()
|
||||||
|
{
|
||||||
|
return Nodes.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderIrNode GetLastNode()
|
||||||
|
{
|
||||||
|
if (Nodes.Count > 0)
|
||||||
|
{
|
||||||
|
return Nodes[Nodes.Count - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs
Normal file
14
Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrCond : ShaderIrNode
|
||||||
|
{
|
||||||
|
public ShaderIrNode Pred { get; set; }
|
||||||
|
public ShaderIrNode Child { get; set; }
|
||||||
|
|
||||||
|
public ShaderIrCond(ShaderIrNode Pred, ShaderIrNode Child)
|
||||||
|
{
|
||||||
|
this.Pred = Pred;
|
||||||
|
this.Child = Child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
Normal file
59
Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
enum ShaderIrInst
|
||||||
|
{
|
||||||
|
B_Start,
|
||||||
|
Band,
|
||||||
|
Bnot,
|
||||||
|
Bor,
|
||||||
|
Bxor,
|
||||||
|
Clt,
|
||||||
|
Ceq,
|
||||||
|
Cle,
|
||||||
|
Cgt,
|
||||||
|
Cne,
|
||||||
|
Cge,
|
||||||
|
Cnum,
|
||||||
|
Cnan,
|
||||||
|
Cltu,
|
||||||
|
Cequ,
|
||||||
|
Cleu,
|
||||||
|
Cgtu,
|
||||||
|
Cneu,
|
||||||
|
Cgeu,
|
||||||
|
B_End,
|
||||||
|
|
||||||
|
F_Start,
|
||||||
|
Fabs,
|
||||||
|
Fadd,
|
||||||
|
Fcos,
|
||||||
|
Fex2,
|
||||||
|
Ffma,
|
||||||
|
Flg2,
|
||||||
|
Fmul,
|
||||||
|
Fneg,
|
||||||
|
Frcp,
|
||||||
|
Frsq,
|
||||||
|
Fsin,
|
||||||
|
Ipa,
|
||||||
|
Texr,
|
||||||
|
Texg,
|
||||||
|
Texb,
|
||||||
|
Texa,
|
||||||
|
F_End,
|
||||||
|
|
||||||
|
I_Start,
|
||||||
|
And,
|
||||||
|
Asr,
|
||||||
|
Lsr,
|
||||||
|
Not,
|
||||||
|
Or,
|
||||||
|
Stof,
|
||||||
|
Utof,
|
||||||
|
Xor,
|
||||||
|
I_End,
|
||||||
|
|
||||||
|
Exit,
|
||||||
|
Kil
|
||||||
|
}
|
||||||
|
}
|
4
Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs
Normal file
4
Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrNode { }
|
||||||
|
}
|
22
Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs
Normal file
22
Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrOp : ShaderIrNode
|
||||||
|
{
|
||||||
|
public ShaderIrInst Inst { get; private set; }
|
||||||
|
public ShaderIrNode OperandA { get; set; }
|
||||||
|
public ShaderIrNode OperandB { get; set; }
|
||||||
|
public ShaderIrNode OperandC { get; set; }
|
||||||
|
|
||||||
|
public ShaderIrOp(
|
||||||
|
ShaderIrInst Inst,
|
||||||
|
ShaderIrNode OperandA = null,
|
||||||
|
ShaderIrNode OperandB = null,
|
||||||
|
ShaderIrNode OperandC = null)
|
||||||
|
{
|
||||||
|
this.Inst = Inst;
|
||||||
|
this.OperandA = OperandA;
|
||||||
|
this.OperandB = OperandB;
|
||||||
|
this.OperandC = OperandC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs
Normal file
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrOperAbuf : ShaderIrNode
|
||||||
|
{
|
||||||
|
public int Offs { get; private set; }
|
||||||
|
public int GprIndex { get; private set; }
|
||||||
|
|
||||||
|
public ShaderIrOperAbuf(int Offs, int GprIndex)
|
||||||
|
{
|
||||||
|
this.Offs = Offs;
|
||||||
|
this.GprIndex = GprIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs
Normal file
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrOperCbuf : ShaderIrNode
|
||||||
|
{
|
||||||
|
public int Index { get; private set; }
|
||||||
|
public int Offs { get; private set; }
|
||||||
|
|
||||||
|
public ShaderIrOperCbuf(int Index, int Offs)
|
||||||
|
{
|
||||||
|
this.Index = Index;
|
||||||
|
this.Offs = Offs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs
Normal file
16
Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrOperGpr : ShaderIrNode
|
||||||
|
{
|
||||||
|
public const int ZRIndex = 0xff;
|
||||||
|
|
||||||
|
public bool IsConst => Index == ZRIndex;
|
||||||
|
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
public ShaderIrOperGpr(int Index)
|
||||||
|
{
|
||||||
|
this.Index = Index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs
Normal file
12
Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrOperImm : ShaderIrNode
|
||||||
|
{
|
||||||
|
public int Value { get; private set; }
|
||||||
|
|
||||||
|
public ShaderIrOperImm(int Value)
|
||||||
|
{
|
||||||
|
this.Value = Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs
Normal file
12
Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrOperImmf : ShaderIrNode
|
||||||
|
{
|
||||||
|
public float Value { get; private set; }
|
||||||
|
|
||||||
|
public ShaderIrOperImmf(float Value)
|
||||||
|
{
|
||||||
|
this.Value = Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs
Normal file
17
Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
class ShaderIrOperPred : ShaderIrNode
|
||||||
|
{
|
||||||
|
public const int UnusedIndex = 0x7;
|
||||||
|
public const int NeverExecute = 0xf;
|
||||||
|
|
||||||
|
public bool IsConst => Index >= UnusedIndex;
|
||||||
|
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
public ShaderIrOperPred(int Index)
|
||||||
|
{
|
||||||
|
this.Index = Index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
97
Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
Normal file
97
Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
static class ShaderOpCodeTable
|
||||||
|
{
|
||||||
|
private const int EncodingBits = 14;
|
||||||
|
|
||||||
|
private static ShaderDecodeFunc[] OpCodes;
|
||||||
|
|
||||||
|
static ShaderOpCodeTable()
|
||||||
|
{
|
||||||
|
OpCodes = new ShaderDecodeFunc[1 << EncodingBits];
|
||||||
|
|
||||||
|
#region Instructions
|
||||||
|
Set("111000110000xx", ShaderDecode.Exit);
|
||||||
|
Set("0100110001011x", ShaderDecode.Fadd_C);
|
||||||
|
Set("0011100x01011x", ShaderDecode.Fadd_I);
|
||||||
|
Set("0101110001011x", ShaderDecode.Fadd_R);
|
||||||
|
Set("010010011xxxxx", ShaderDecode.Ffma_CR);
|
||||||
|
Set("001100101xxxxx", ShaderDecode.Ffma_I);
|
||||||
|
Set("010100011xxxxx", ShaderDecode.Ffma_RC);
|
||||||
|
Set("010110011xxxxx", ShaderDecode.Ffma_RR);
|
||||||
|
Set("0100110001101x", ShaderDecode.Fmul_C);
|
||||||
|
Set("0011100x01101x", ShaderDecode.Fmul_I);
|
||||||
|
Set("0101110001101x", ShaderDecode.Fmul_R);
|
||||||
|
Set("010010111011xx", ShaderDecode.Fsetp_C);
|
||||||
|
Set("0011011x1011xx", ShaderDecode.Fsetp_I);
|
||||||
|
Set("010110111011xx", ShaderDecode.Fsetp_R);
|
||||||
|
Set("0100110010111x", ShaderDecode.I2f_C);
|
||||||
|
Set("0011100x10111x", ShaderDecode.I2f_I);
|
||||||
|
Set("0101110010111x", ShaderDecode.I2f_R);
|
||||||
|
Set("11100000xxxxxx", ShaderDecode.Ipa);
|
||||||
|
Set("111000110011xx", ShaderDecode.Kil);
|
||||||
|
Set("1110111111011x", ShaderDecode.Ld_A);
|
||||||
|
Set("000001xxxxxxxx", ShaderDecode.Lop32i);
|
||||||
|
Set("000000010000xx", ShaderDecode.Mov32i);
|
||||||
|
Set("0101000010000x", ShaderDecode.Mufu);
|
||||||
|
Set("0100110000101x", ShaderDecode.Shr_C);
|
||||||
|
Set("0011100x00101x", ShaderDecode.Shr_I);
|
||||||
|
Set("0101110000101x", ShaderDecode.Shr_R);
|
||||||
|
Set("1110111111110x", ShaderDecode.St_A);
|
||||||
|
Set("1101100xxxxxxx", ShaderDecode.Texs);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Set(string Encoding, ShaderDecodeFunc Func)
|
||||||
|
{
|
||||||
|
if (Encoding.Length != EncodingBits)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(Encoding));
|
||||||
|
}
|
||||||
|
|
||||||
|
int Bit = Encoding.Length - 1;
|
||||||
|
int Value = 0;
|
||||||
|
int XMask = 0;
|
||||||
|
int XBits = 0;
|
||||||
|
|
||||||
|
int[] XPos = new int[Encoding.Length];
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Encoding.Length; Index++, Bit--)
|
||||||
|
{
|
||||||
|
char Chr = Encoding[Index];
|
||||||
|
|
||||||
|
if (Chr == '1')
|
||||||
|
{
|
||||||
|
Value |= 1 << Bit;
|
||||||
|
}
|
||||||
|
else if (Chr == 'x')
|
||||||
|
{
|
||||||
|
XMask |= 1 << Bit;
|
||||||
|
|
||||||
|
XPos[XBits++] = Bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XMask = ~XMask;
|
||||||
|
|
||||||
|
for (int Index = 0; Index < (1 << XBits); Index++)
|
||||||
|
{
|
||||||
|
Value &= XMask;
|
||||||
|
|
||||||
|
for (int X = 0; X < XBits; X++)
|
||||||
|
{
|
||||||
|
Value |= ((Index >> X) & 1) << XPos[X];
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCodes[Value] = Func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShaderDecodeFunc GetDecoder(long OpCode)
|
||||||
|
{
|
||||||
|
return OpCodes[(ulong)OpCode >> (64 - EncodingBits)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics/Gal/Shader/ShaderOper.cs
Normal file
11
Ryujinx.Graphics/Gal/Shader/ShaderOper.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
enum ShaderOper
|
||||||
|
{
|
||||||
|
CR,
|
||||||
|
RC,
|
||||||
|
RR,
|
||||||
|
Imm,
|
||||||
|
Immf
|
||||||
|
}
|
||||||
|
}
|
266
Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs
Normal file
266
Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
static class ShaderOptExprProp
|
||||||
|
{
|
||||||
|
private struct UseSite
|
||||||
|
{
|
||||||
|
public object Parent;
|
||||||
|
|
||||||
|
public int OperIndex;
|
||||||
|
|
||||||
|
public UseSite(object Parent, int OperIndex)
|
||||||
|
{
|
||||||
|
this.Parent = Parent;
|
||||||
|
this.OperIndex = OperIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RegUse
|
||||||
|
{
|
||||||
|
public ShaderIrAsg Asg { get; private set; }
|
||||||
|
|
||||||
|
public int AsgIndex { get; private set; }
|
||||||
|
|
||||||
|
private bool Propagate;
|
||||||
|
|
||||||
|
private List<UseSite> Sites;
|
||||||
|
|
||||||
|
public RegUse()
|
||||||
|
{
|
||||||
|
Sites = new List<UseSite>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddUseSite(UseSite Site)
|
||||||
|
{
|
||||||
|
Sites.Add(Site);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryPropagate()
|
||||||
|
{
|
||||||
|
//This happens when a untiliazied register is used,
|
||||||
|
//this usually indicates a decoding error, but may also
|
||||||
|
//be cased 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 || !Propagate)
|
||||||
|
{
|
||||||
|
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, bool Propagate)
|
||||||
|
{
|
||||||
|
this.Asg = Asg;
|
||||||
|
this.AsgIndex = AsgIndex;
|
||||||
|
this.Propagate = Propagate;
|
||||||
|
|
||||||
|
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 FindRegUses(List<(int, UseSite)> UseList, object Parent, ShaderIrNode Node, int OperIndex = 0)
|
||||||
|
{
|
||||||
|
if (Node is ShaderIrAsg Asg)
|
||||||
|
{
|
||||||
|
FindRegUses(UseList, Asg, Asg.Src);
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrCond Cond)
|
||||||
|
{
|
||||||
|
FindRegUses(UseList, Cond, Cond.Pred, 0);
|
||||||
|
FindRegUses(UseList, Cond, Cond.Child, 1);
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
FindRegUses(UseList, Op, Op.OperandA, 0);
|
||||||
|
FindRegUses(UseList, Op, Op.OperandB, 1);
|
||||||
|
FindRegUses(UseList, Op, Op.OperandC, 2);
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex)
|
||||||
|
{
|
||||||
|
UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, OperIndex)));
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrOperPred Pred)
|
||||||
|
{
|
||||||
|
UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, OperIndex)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TryAddRegUseSite(ShaderIrNode Node)
|
||||||
|
{
|
||||||
|
List<(int, UseSite)> UseList = new List<(int, UseSite)>();
|
||||||
|
|
||||||
|
FindRegUses(UseList, null, Node);
|
||||||
|
|
||||||
|
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)>();
|
||||||
|
|
||||||
|
FindRegUses(UseList, Use.Asg, Use.Asg.Src);
|
||||||
|
|
||||||
|
foreach ((int Key, UseSite Site) in UseList)
|
||||||
|
{
|
||||||
|
if (GetUse(Key).AsgIndex >= Use.AsgIndex)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Use.TryPropagate();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int Index = 0, AsgIndex = 0; Index < Nodes.Count; Index++, AsgIndex++)
|
||||||
|
{
|
||||||
|
ShaderIrNode Node = Nodes[Index];
|
||||||
|
|
||||||
|
bool IsConditional = Node is ShaderIrCond;
|
||||||
|
|
||||||
|
TryAddRegUseSite(Node);
|
||||||
|
|
||||||
|
while (Node is ShaderIrCond Cond)
|
||||||
|
{
|
||||||
|
Node = Cond.Child;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Node is ShaderIrAsg Asg))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegUse Use = null;
|
||||||
|
|
||||||
|
if (Asg.Dst is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex)
|
||||||
|
{
|
||||||
|
Use = GetGprUse(Gpr.Index);
|
||||||
|
}
|
||||||
|
else if (Asg.Dst is ShaderIrOperPred Pred)
|
||||||
|
{
|
||||||
|
Use = GetPredUse(Pred.Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsConditional && TryPropagate(Use))
|
||||||
|
{
|
||||||
|
Nodes.Remove(Use.Asg);
|
||||||
|
|
||||||
|
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, AsgIndex, !IsConditional);
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
Nodes.Remove(Use.Asg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
Normal file
27
Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
public class ShaderDeclInfo
|
||||||
|
{
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
public int Index { get; private set; }
|
||||||
|
public int Cbuf { get; private set; }
|
||||||
|
public int Size { get; private set; }
|
||||||
|
|
||||||
|
public ShaderDeclInfo(string Name, int Index, int Cbuf = 0, int Size = 1)
|
||||||
|
{
|
||||||
|
this.Name = Name;
|
||||||
|
this.Index = Index;
|
||||||
|
this.Cbuf = Cbuf;
|
||||||
|
this.Size = Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Enlarge(int NewSize)
|
||||||
|
{
|
||||||
|
if (Size < NewSize)
|
||||||
|
{
|
||||||
|
Size = NewSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics/Gal/ShaderException.cs
Normal file
11
Ryujinx.Graphics/Gal/ShaderException.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal
|
||||||
|
{
|
||||||
|
class ShaderException : Exception
|
||||||
|
{
|
||||||
|
public ShaderException() : base() { }
|
||||||
|
|
||||||
|
public ShaderException(string Message) : base(Message) { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gal.Texture
|
||||||
{
|
{
|
||||||
static class BCn
|
static class BCn
|
||||||
{
|
{
|
||||||
public static byte[] DecodeBC1(NsGpuTexture Tex, int Offset)
|
public static byte[] DecodeBC1(GalTexture Texture, int Offset)
|
||||||
{
|
{
|
||||||
int W = (Tex.Width + 3) / 4;
|
int W = (Texture.Width + 3) / 4;
|
||||||
int H = (Tex.Height + 3) / 4;
|
int H = (Texture.Height + 3) / 4;
|
||||||
|
|
||||||
byte[] Output = new byte[W * H * 64];
|
byte[] Output = new byte[W * H * 64];
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
int IOffs = Offset + Swizzle.GetSwizzledAddress64(X, Y) * 8;
|
int IOffs = Offset + Swizzle.GetSwizzledAddress64(X, Y) * 8;
|
||||||
|
|
||||||
byte[] Tile = BCnDecodeTile(Tex.Data, IOffs, true);
|
byte[] Tile = BCnDecodeTile(Texture.Data, IOffs, true);
|
||||||
|
|
||||||
int TOffset = 0;
|
int TOffset = 0;
|
||||||
|
|
||||||
|
@ -44,10 +44,10 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
return Output;
|
return Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] DecodeBC2(NsGpuTexture Tex, int Offset)
|
public static byte[] DecodeBC2(GalTexture Texture, int Offset)
|
||||||
{
|
{
|
||||||
int W = (Tex.Width + 3) / 4;
|
int W = (Texture.Width + 3) / 4;
|
||||||
int H = (Tex.Height + 3) / 4;
|
int H = (Texture.Height + 3) / 4;
|
||||||
|
|
||||||
byte[] Output = new byte[W * H * 64];
|
byte[] Output = new byte[W * H * 64];
|
||||||
|
|
||||||
|
@ -59,10 +59,10 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
|
int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
|
||||||
|
|
||||||
byte[] Tile = BCnDecodeTile(Tex.Data, IOffs + 8, false);
|
byte[] Tile = BCnDecodeTile(Texture.Data, IOffs + 8, false);
|
||||||
|
|
||||||
int AlphaLow = Get32(Tex.Data, IOffs + 0);
|
int AlphaLow = Get32(Texture.Data, IOffs + 0);
|
||||||
int AlphaHigh = Get32(Tex.Data, IOffs + 4);
|
int AlphaHigh = Get32(Texture.Data, IOffs + 4);
|
||||||
|
|
||||||
ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
|
ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
|
||||||
|
|
||||||
|
@ -90,10 +90,10 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
return Output;
|
return Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] DecodeBC3(NsGpuTexture Tex, int Offset)
|
public static byte[] DecodeBC3(GalTexture Texture, int Offset)
|
||||||
{
|
{
|
||||||
int W = (Tex.Width + 3) / 4;
|
int W = (Texture.Width + 3) / 4;
|
||||||
int H = (Tex.Height + 3) / 4;
|
int H = (Texture.Height + 3) / 4;
|
||||||
|
|
||||||
byte[] Output = new byte[W * H * 64];
|
byte[] Output = new byte[W * H * 64];
|
||||||
|
|
||||||
|
@ -105,17 +105,17 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
|
int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
|
||||||
|
|
||||||
byte[] Tile = BCnDecodeTile(Tex.Data, IOffs + 8, false);
|
byte[] Tile = BCnDecodeTile(Texture.Data, IOffs + 8, false);
|
||||||
|
|
||||||
byte[] Alpha = new byte[8];
|
byte[] Alpha = new byte[8];
|
||||||
|
|
||||||
Alpha[0] = Tex.Data[IOffs + 0];
|
Alpha[0] = Texture.Data[IOffs + 0];
|
||||||
Alpha[1] = Tex.Data[IOffs + 1];
|
Alpha[1] = Texture.Data[IOffs + 1];
|
||||||
|
|
||||||
CalculateBC3Alpha(Alpha);
|
CalculateBC3Alpha(Alpha);
|
||||||
|
|
||||||
int AlphaLow = Get32(Tex.Data, IOffs + 2);
|
int AlphaLow = Get32(Texture.Data, IOffs + 2);
|
||||||
int AlphaHigh = Get16(Tex.Data, IOffs + 6);
|
int AlphaHigh = Get16(Texture.Data, IOffs + 6);
|
||||||
|
|
||||||
ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
|
ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
|
||||||
|
|
||||||
|
@ -143,10 +143,10 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
return Output;
|
return Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] DecodeBC4(NsGpuTexture Tex, int Offset)
|
public static byte[] DecodeBC4(GalTexture Texture, int Offset)
|
||||||
{
|
{
|
||||||
int W = (Tex.Width + 3) / 4;
|
int W = (Texture.Width + 3) / 4;
|
||||||
int H = (Tex.Height + 3) / 4;
|
int H = (Texture.Height + 3) / 4;
|
||||||
|
|
||||||
byte[] Output = new byte[W * H * 64];
|
byte[] Output = new byte[W * H * 64];
|
||||||
|
|
||||||
|
@ -160,13 +160,13 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
byte[] Red = new byte[8];
|
byte[] Red = new byte[8];
|
||||||
|
|
||||||
Red[0] = Tex.Data[IOffs + 0];
|
Red[0] = Texture.Data[IOffs + 0];
|
||||||
Red[1] = Tex.Data[IOffs + 1];
|
Red[1] = Texture.Data[IOffs + 1];
|
||||||
|
|
||||||
CalculateBC3Alpha(Red);
|
CalculateBC3Alpha(Red);
|
||||||
|
|
||||||
int RedLow = Get32(Tex.Data, IOffs + 2);
|
int RedLow = Get32(Texture.Data, IOffs + 2);
|
||||||
int RedHigh = Get16(Tex.Data, IOffs + 6);
|
int RedHigh = Get16(Texture.Data, IOffs + 6);
|
||||||
|
|
||||||
ulong RedCh = (uint)RedLow | (ulong)RedHigh << 32;
|
ulong RedCh = (uint)RedLow | (ulong)RedHigh << 32;
|
||||||
|
|
||||||
|
@ -194,10 +194,10 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
return Output;
|
return Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] DecodeBC5(NsGpuTexture Tex, int Offset, bool SNorm)
|
public static byte[] DecodeBC5(GalTexture Texture, int Offset, bool SNorm)
|
||||||
{
|
{
|
||||||
int W = (Tex.Width + 3) / 4;
|
int W = (Texture.Width + 3) / 4;
|
||||||
int H = (Tex.Height + 3) / 4;
|
int H = (Texture.Height + 3) / 4;
|
||||||
|
|
||||||
byte[] Output = new byte[W * H * 64];
|
byte[] Output = new byte[W * H * 64];
|
||||||
|
|
||||||
|
@ -212,11 +212,11 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
byte[] Red = new byte[8];
|
byte[] Red = new byte[8];
|
||||||
byte[] Green = new byte[8];
|
byte[] Green = new byte[8];
|
||||||
|
|
||||||
Red[0] = Tex.Data[IOffs + 0];
|
Red[0] = Texture.Data[IOffs + 0];
|
||||||
Red[1] = Tex.Data[IOffs + 1];
|
Red[1] = Texture.Data[IOffs + 1];
|
||||||
|
|
||||||
Green[0] = Tex.Data[IOffs + 8];
|
Green[0] = Texture.Data[IOffs + 8];
|
||||||
Green[1] = Tex.Data[IOffs + 9];
|
Green[1] = Texture.Data[IOffs + 9];
|
||||||
|
|
||||||
if (SNorm)
|
if (SNorm)
|
||||||
{
|
{
|
||||||
|
@ -229,11 +229,11 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
CalculateBC3Alpha(Green);
|
CalculateBC3Alpha(Green);
|
||||||
}
|
}
|
||||||
|
|
||||||
int RedLow = Get32(Tex.Data, IOffs + 2);
|
int RedLow = Get32(Texture.Data, IOffs + 2);
|
||||||
int RedHigh = Get16(Tex.Data, IOffs + 6);
|
int RedHigh = Get16(Texture.Data, IOffs + 6);
|
||||||
|
|
||||||
int GreenLow = Get32(Tex.Data, IOffs + 10);
|
int GreenLow = Get32(Texture.Data, IOffs + 10);
|
||||||
int GreenHigh = Get16(Tex.Data, IOffs + 14);
|
int GreenHigh = Get16(Texture.Data, IOffs + 14);
|
||||||
|
|
||||||
ulong RedCh = (uint)RedLow | (ulong)RedHigh << 32;
|
ulong RedCh = (uint)RedLow | (ulong)RedHigh << 32;
|
||||||
ulong GreenCh = (uint)GreenLow | (ulong)GreenHigh << 32;
|
ulong GreenCh = (uint)GreenLow | (ulong)GreenHigh << 32;
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gal.Texture
|
||||||
{
|
{
|
||||||
class SwizzleAddr
|
class SwizzleAddr
|
||||||
{
|
{
|
19
Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs
Normal file
19
Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Texture
|
||||||
|
{
|
||||||
|
static class TextureDecoder
|
||||||
|
{
|
||||||
|
public static byte[] Decode(GalTexture Texture)
|
||||||
|
{
|
||||||
|
switch (Texture.Format)
|
||||||
|
{
|
||||||
|
case GalTextureFormat.BC1: return BCn.DecodeBC1(Texture, 0);
|
||||||
|
case GalTextureFormat.BC2: return BCn.DecodeBC2(Texture, 0);
|
||||||
|
case GalTextureFormat.BC3: return BCn.DecodeBC3(Texture, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException(Texture.Format.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs
Normal file
57
Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
class BlockLinearSwizzle : ISwizzle
|
||||||
|
{
|
||||||
|
private int BhShift;
|
||||||
|
private int BppShift;
|
||||||
|
private int BhMask;
|
||||||
|
|
||||||
|
private int XShift;
|
||||||
|
private int GobStride;
|
||||||
|
|
||||||
|
public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16)
|
||||||
|
{
|
||||||
|
BhMask = (BlockHeight * 8) - 1;
|
||||||
|
|
||||||
|
BhShift = CountLsbZeros(BlockHeight * 8);
|
||||||
|
BppShift = CountLsbZeros(Bpp);
|
||||||
|
|
||||||
|
int WidthInGobs = Width * Bpp / 64;
|
||||||
|
|
||||||
|
GobStride = 512 * BlockHeight * WidthInGobs;
|
||||||
|
|
||||||
|
XShift = CountLsbZeros(512 * BlockHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int CountLsbZeros(int Value)
|
||||||
|
{
|
||||||
|
int Count = 0;
|
||||||
|
|
||||||
|
while (((Value >> Count) & 1) == 0)
|
||||||
|
{
|
||||||
|
Count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSwizzleOffset(int X, int Y)
|
||||||
|
{
|
||||||
|
X <<= BppShift;
|
||||||
|
|
||||||
|
int Position = (Y >> BhShift) * GobStride;
|
||||||
|
|
||||||
|
Position += (X >> 6) << XShift;
|
||||||
|
|
||||||
|
Position += ((Y & BhMask) >> 3) << 9;
|
||||||
|
|
||||||
|
Position += ((X & 0x3f) >> 5) << 8;
|
||||||
|
Position += ((Y & 0x07) >> 1) << 6;
|
||||||
|
Position += ((X & 0x1f) >> 4) << 5;
|
||||||
|
Position += ((Y & 0x01) >> 0) << 4;
|
||||||
|
Position += ((X & 0x0f) >> 0) << 0;
|
||||||
|
|
||||||
|
return Position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics/Gpu/INvGpuEngine.cs
Normal file
11
Ryujinx.Graphics/Gpu/INvGpuEngine.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
interface INvGpuEngine
|
||||||
|
{
|
||||||
|
int[] Registers { get; }
|
||||||
|
|
||||||
|
void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry);
|
||||||
|
}
|
||||||
|
}
|
7
Ryujinx.Graphics/Gpu/ISwizzle.cs
Normal file
7
Ryujinx.Graphics/Gpu/ISwizzle.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
interface ISwizzle
|
||||||
|
{
|
||||||
|
int GetSwizzleOffset(int X, int Y);
|
||||||
|
}
|
||||||
|
}
|
20
Ryujinx.Graphics/Gpu/LinearSwizzle.cs
Normal file
20
Ryujinx.Graphics/Gpu/LinearSwizzle.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
class LinearSwizzle : ISwizzle
|
||||||
|
{
|
||||||
|
private int Bpp;
|
||||||
|
private int Stride;
|
||||||
|
|
||||||
|
public LinearSwizzle(int Width, int Bpp)
|
||||||
|
{
|
||||||
|
this.Bpp = Bpp;
|
||||||
|
|
||||||
|
Stride = Width * Bpp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSwizzleOffset(int X, int Y)
|
||||||
|
{
|
||||||
|
return X * Bpp + Y * Stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
420
Ryujinx.Graphics/Gpu/MacroInterpreter.cs
Normal file
420
Ryujinx.Graphics/Gpu/MacroInterpreter.cs
Normal file
|
@ -0,0 +1,420 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
class MacroInterpreter
|
||||||
|
{
|
||||||
|
private enum AssignmentOperation
|
||||||
|
{
|
||||||
|
IgnoreAndFetch = 0,
|
||||||
|
Move = 1,
|
||||||
|
MoveAndSetMaddr = 2,
|
||||||
|
FetchAndSend = 3,
|
||||||
|
MoveAndSend = 4,
|
||||||
|
FetchAndSetMaddr = 5,
|
||||||
|
MoveAndSetMaddrThenFetchAndSend = 6,
|
||||||
|
MoveAndSetMaddrThenSendHigh = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum AluOperation
|
||||||
|
{
|
||||||
|
AluReg = 0,
|
||||||
|
AddImmediate = 1,
|
||||||
|
BitfieldReplace = 2,
|
||||||
|
BitfieldExtractLslImm = 3,
|
||||||
|
BitfieldExtractLslReg = 4,
|
||||||
|
ReadImmediate = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum AluRegOperation
|
||||||
|
{
|
||||||
|
Add = 0,
|
||||||
|
AddWithCarry = 1,
|
||||||
|
Subtract = 2,
|
||||||
|
SubtractWithBorrow = 3,
|
||||||
|
BitwiseExclusiveOr = 8,
|
||||||
|
BitwiseOr = 9,
|
||||||
|
BitwiseAnd = 10,
|
||||||
|
BitwiseAndNot = 11,
|
||||||
|
BitwiseNotAnd = 12
|
||||||
|
}
|
||||||
|
|
||||||
|
private NvGpuFifo PFifo;
|
||||||
|
private INvGpuEngine Engine;
|
||||||
|
|
||||||
|
public Queue<int> Fifo { get; private set; }
|
||||||
|
|
||||||
|
private int[] Gprs;
|
||||||
|
|
||||||
|
private int MethAddr;
|
||||||
|
private int MethIncr;
|
||||||
|
|
||||||
|
private bool Carry;
|
||||||
|
|
||||||
|
private int OpCode;
|
||||||
|
|
||||||
|
private int PipeOp;
|
||||||
|
|
||||||
|
private long Pc;
|
||||||
|
|
||||||
|
public MacroInterpreter(NvGpuFifo PFifo, INvGpuEngine Engine)
|
||||||
|
{
|
||||||
|
this.PFifo = PFifo;
|
||||||
|
this.Engine = Engine;
|
||||||
|
|
||||||
|
Fifo = new Queue<int>();
|
||||||
|
|
||||||
|
Gprs = new int[8];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(AMemory Memory, long Position, int Param)
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
|
||||||
|
Gprs[1] = Param;
|
||||||
|
|
||||||
|
Pc = Position;
|
||||||
|
|
||||||
|
FetchOpCode(Memory);
|
||||||
|
|
||||||
|
while (Step(Memory));
|
||||||
|
|
||||||
|
//Due to the delay slot, we still need to execute
|
||||||
|
//one more instruction before we actually exit.
|
||||||
|
Step(Memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
for (int Index = 0; Index < Gprs.Length; Index++)
|
||||||
|
{
|
||||||
|
Gprs[Index] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethAddr = 0;
|
||||||
|
MethIncr = 0;
|
||||||
|
|
||||||
|
Carry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Step(AMemory Memory)
|
||||||
|
{
|
||||||
|
long BaseAddr = Pc - 4;
|
||||||
|
|
||||||
|
FetchOpCode(Memory);
|
||||||
|
|
||||||
|
if ((OpCode & 7) < 7)
|
||||||
|
{
|
||||||
|
//Operation produces a value.
|
||||||
|
AssignmentOperation AsgOp = (AssignmentOperation)((OpCode >> 4) & 7);
|
||||||
|
|
||||||
|
int Result = GetAluResult();
|
||||||
|
|
||||||
|
switch (AsgOp)
|
||||||
|
{
|
||||||
|
//Fetch parameter and ignore result.
|
||||||
|
case AssignmentOperation.IgnoreAndFetch:
|
||||||
|
{
|
||||||
|
SetDstGpr(FetchParam());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Move result.
|
||||||
|
case AssignmentOperation.Move:
|
||||||
|
{
|
||||||
|
SetDstGpr(Result);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Move result and use as Method Address.
|
||||||
|
case AssignmentOperation.MoveAndSetMaddr:
|
||||||
|
{
|
||||||
|
SetDstGpr(Result);
|
||||||
|
|
||||||
|
SetMethAddr(Result);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fetch parameter and send result.
|
||||||
|
case AssignmentOperation.FetchAndSend:
|
||||||
|
{
|
||||||
|
SetDstGpr(FetchParam());
|
||||||
|
|
||||||
|
Send(Memory, Result);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Move and send result.
|
||||||
|
case AssignmentOperation.MoveAndSend:
|
||||||
|
{
|
||||||
|
SetDstGpr(Result);
|
||||||
|
|
||||||
|
Send(Memory, Result);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fetch parameter and use result as Method Address.
|
||||||
|
case AssignmentOperation.FetchAndSetMaddr:
|
||||||
|
{
|
||||||
|
SetDstGpr(FetchParam());
|
||||||
|
|
||||||
|
SetMethAddr(Result);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Move result and use as Method Address, then fetch and send paramter.
|
||||||
|
case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend:
|
||||||
|
{
|
||||||
|
SetDstGpr(Result);
|
||||||
|
|
||||||
|
SetMethAddr(Result);
|
||||||
|
|
||||||
|
Send(Memory, FetchParam());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Move result and use as Method Address, then send bits 17:12 of result.
|
||||||
|
case AssignmentOperation.MoveAndSetMaddrThenSendHigh:
|
||||||
|
{
|
||||||
|
SetDstGpr(Result);
|
||||||
|
|
||||||
|
SetMethAddr(Result);
|
||||||
|
|
||||||
|
Send(Memory, (Result >> 12) & 0x3f);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Branch.
|
||||||
|
bool OnNotZero = ((OpCode >> 4) & 1) != 0;
|
||||||
|
|
||||||
|
bool Taken = OnNotZero
|
||||||
|
? GetGprA() != 0
|
||||||
|
: GetGprA() == 0;
|
||||||
|
|
||||||
|
if (Taken)
|
||||||
|
{
|
||||||
|
Pc = BaseAddr + (GetImm() << 2);
|
||||||
|
|
||||||
|
bool NoDelays = (OpCode & 0x20) != 0;
|
||||||
|
|
||||||
|
if (NoDelays)
|
||||||
|
{
|
||||||
|
FetchOpCode(Memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exit = (OpCode & 0x80) != 0;
|
||||||
|
|
||||||
|
return !Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FetchOpCode(AMemory Memory)
|
||||||
|
{
|
||||||
|
OpCode = PipeOp;
|
||||||
|
|
||||||
|
PipeOp = Memory.ReadInt32(Pc);
|
||||||
|
|
||||||
|
Pc += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetAluResult()
|
||||||
|
{
|
||||||
|
AluOperation Op = (AluOperation)(OpCode & 7);
|
||||||
|
|
||||||
|
switch (Op)
|
||||||
|
{
|
||||||
|
case AluOperation.AluReg:
|
||||||
|
{
|
||||||
|
AluRegOperation AluOp = (AluRegOperation)((OpCode >> 17) & 0x1f);
|
||||||
|
|
||||||
|
return GetAluResult(AluOp, GetGprA(), GetGprB());
|
||||||
|
}
|
||||||
|
|
||||||
|
case AluOperation.AddImmediate:
|
||||||
|
{
|
||||||
|
return GetGprA() + GetImm();
|
||||||
|
}
|
||||||
|
|
||||||
|
case AluOperation.BitfieldReplace:
|
||||||
|
case AluOperation.BitfieldExtractLslImm:
|
||||||
|
case AluOperation.BitfieldExtractLslReg:
|
||||||
|
{
|
||||||
|
int BfSrcBit = (OpCode >> 17) & 0x1f;
|
||||||
|
int BfSize = (OpCode >> 22) & 0x1f;
|
||||||
|
int BfDstBit = (OpCode >> 27) & 0x1f;
|
||||||
|
|
||||||
|
int BfMask = (1 << BfSize) - 1;
|
||||||
|
|
||||||
|
int Dst = GetGprA();
|
||||||
|
int Src = GetGprB();
|
||||||
|
|
||||||
|
switch (Op)
|
||||||
|
{
|
||||||
|
case AluOperation.BitfieldReplace:
|
||||||
|
{
|
||||||
|
Src = (int)((uint)Src >> BfSrcBit) & BfMask;
|
||||||
|
|
||||||
|
Dst &= ~(BfMask << BfDstBit);
|
||||||
|
|
||||||
|
Dst |= Src << BfDstBit;
|
||||||
|
|
||||||
|
return Dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AluOperation.BitfieldExtractLslImm:
|
||||||
|
{
|
||||||
|
Src = (int)((uint)Src >> Dst) & BfMask;
|
||||||
|
|
||||||
|
return Src << BfDstBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AluOperation.BitfieldExtractLslReg:
|
||||||
|
{
|
||||||
|
Src = (int)((uint)Src >> BfSrcBit) & BfMask;
|
||||||
|
|
||||||
|
return Src << Dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AluOperation.ReadImmediate:
|
||||||
|
{
|
||||||
|
return Read(GetGprA() + GetImm());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetAluResult(AluRegOperation AluOp, int A, int B)
|
||||||
|
{
|
||||||
|
switch (AluOp)
|
||||||
|
{
|
||||||
|
case AluRegOperation.Add:
|
||||||
|
{
|
||||||
|
ulong Result = (ulong)A + (ulong)B;
|
||||||
|
|
||||||
|
Carry = Result > 0xffffffff;
|
||||||
|
|
||||||
|
return (int)Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AluRegOperation.AddWithCarry:
|
||||||
|
{
|
||||||
|
ulong Result = (ulong)A + (ulong)B + (Carry ? 1UL : 0UL);
|
||||||
|
|
||||||
|
Carry = Result > 0xffffffff;
|
||||||
|
|
||||||
|
return (int)Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AluRegOperation.Subtract:
|
||||||
|
{
|
||||||
|
ulong Result = (ulong)A - (ulong)B;
|
||||||
|
|
||||||
|
Carry = Result < 0x100000000;
|
||||||
|
|
||||||
|
return (int)Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AluRegOperation.SubtractWithBorrow:
|
||||||
|
{
|
||||||
|
ulong Result = (ulong)A - (ulong)B - (Carry ? 0UL : 1UL);
|
||||||
|
|
||||||
|
Carry = Result < 0x100000000;
|
||||||
|
|
||||||
|
return (int)Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AluRegOperation.BitwiseExclusiveOr: return A ^ B;
|
||||||
|
case AluRegOperation.BitwiseOr: return A | B;
|
||||||
|
case AluRegOperation.BitwiseAnd: return A & B;
|
||||||
|
case AluRegOperation.BitwiseAndNot: return A & ~B;
|
||||||
|
case AluRegOperation.BitwiseNotAnd: return ~(A & B);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(AluOp));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetImm()
|
||||||
|
{
|
||||||
|
//Note: The immediate is signed, the sign-extension is intended here.
|
||||||
|
return OpCode >> 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetMethAddr(int Value)
|
||||||
|
{
|
||||||
|
MethAddr = (Value >> 0) & 0xfff;
|
||||||
|
MethIncr = (Value >> 12) & 0x3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetDstGpr(int Value)
|
||||||
|
{
|
||||||
|
Gprs[(OpCode >> 8) & 7] = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetGprA()
|
||||||
|
{
|
||||||
|
return GetGprValue((OpCode >> 11) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetGprB()
|
||||||
|
{
|
||||||
|
return GetGprValue((OpCode >> 14) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetGprValue(int Index)
|
||||||
|
{
|
||||||
|
return Index != 0 ? Gprs[Index] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int FetchParam()
|
||||||
|
{
|
||||||
|
int Value;
|
||||||
|
|
||||||
|
//If we don't have any parameters in the FIFO,
|
||||||
|
//keep running the PFIFO engine until it writes the parameters.
|
||||||
|
while (!Fifo.TryDequeue(out Value))
|
||||||
|
{
|
||||||
|
if (!PFifo.Step())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int Read(int Reg)
|
||||||
|
{
|
||||||
|
return Engine.Registers[Reg];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Send(AMemory Memory, int Value)
|
||||||
|
{
|
||||||
|
NsGpuPBEntry PBEntry = new NsGpuPBEntry(MethAddr, 0, Value);
|
||||||
|
|
||||||
|
Engine.CallMethod(Memory, PBEntry);
|
||||||
|
|
||||||
|
MethAddr += MethIncr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using Ryujinx.Graphics.Gal;
|
using Ryujinx.Graphics.Gal;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,13 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
internal NsGpuMemoryMgr MemoryMgr { get; private set; }
|
internal NsGpuMemoryMgr MemoryMgr { get; private set; }
|
||||||
|
|
||||||
internal NsGpuPGraph PGraph { get; private set; }
|
public NvGpuFifo Fifo;
|
||||||
|
|
||||||
|
internal NvGpuEngine3d Engine3d;
|
||||||
|
|
||||||
|
private Thread FifoProcessing;
|
||||||
|
|
||||||
|
private bool KeepRunning;
|
||||||
|
|
||||||
public NsGpu(IGalRenderer Renderer)
|
public NsGpu(IGalRenderer Renderer)
|
||||||
{
|
{
|
||||||
|
@ -17,7 +23,15 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
MemoryMgr = new NsGpuMemoryMgr();
|
MemoryMgr = new NsGpuMemoryMgr();
|
||||||
|
|
||||||
PGraph = new NsGpuPGraph(this);
|
Fifo = new NvGpuFifo(this);
|
||||||
|
|
||||||
|
Engine3d = new NvGpuEngine3d(this);
|
||||||
|
|
||||||
|
KeepRunning = true;
|
||||||
|
|
||||||
|
FifoProcessing = new Thread(ProcessFifo);
|
||||||
|
|
||||||
|
FifoProcessing.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetCpuAddr(long Position)
|
public long GetCpuAddr(long Position)
|
||||||
|
@ -35,11 +49,6 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
return MemoryMgr.Map(CpuAddr, GpuAddr, Size);
|
return MemoryMgr.Map(CpuAddr, GpuAddr, Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory)
|
|
||||||
{
|
|
||||||
PGraph.ProcessPushBuffer(PushBuffer, Memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long ReserveMemory(long Size, long Align)
|
public long ReserveMemory(long Size, long Align)
|
||||||
{
|
{
|
||||||
return MemoryMgr.Reserve(Size, Align);
|
return MemoryMgr.Reserve(Size, Align);
|
||||||
|
@ -49,5 +58,15 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
return MemoryMgr.Reserve(GpuAddr, Size, Align);
|
return MemoryMgr.Reserve(GpuAddr, Size, Align);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ProcessFifo()
|
||||||
|
{
|
||||||
|
while (KeepRunning)
|
||||||
|
{
|
||||||
|
Fifo.DispatchCalls();
|
||||||
|
|
||||||
|
Thread.Yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
public struct NsGpuPBEntry
|
public struct NsGpuPBEntry
|
||||||
{
|
{
|
||||||
public NsGpuRegister Register { get; private set; }
|
public int Method { get; private set; }
|
||||||
|
|
||||||
public int SubChannel { get; private set; }
|
public int SubChannel { get; private set; }
|
||||||
|
|
||||||
|
@ -15,65 +13,11 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
public ReadOnlyCollection<int> Arguments => Array.AsReadOnly(m_Arguments);
|
public ReadOnlyCollection<int> Arguments => Array.AsReadOnly(m_Arguments);
|
||||||
|
|
||||||
public NsGpuPBEntry(NsGpuRegister Register, int SubChannel, params int[] Arguments)
|
public NsGpuPBEntry(int Method, int SubChannel, params int[] Arguments)
|
||||||
{
|
{
|
||||||
this.Register = Register;
|
this.Method = Method;
|
||||||
this.SubChannel = SubChannel;
|
this.SubChannel = SubChannel;
|
||||||
this.m_Arguments = Arguments;
|
this.m_Arguments = Arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NsGpuPBEntry[] DecodePushBuffer(byte[] Data)
|
|
||||||
{
|
|
||||||
using (MemoryStream MS = new MemoryStream(Data))
|
|
||||||
{
|
|
||||||
BinaryReader Reader = new BinaryReader(MS);
|
|
||||||
|
|
||||||
List<NsGpuPBEntry> GpFifos = new List<NsGpuPBEntry>();
|
|
||||||
|
|
||||||
bool CanRead() => MS.Position + 4 <= MS.Length;
|
|
||||||
|
|
||||||
while (CanRead())
|
|
||||||
{
|
|
||||||
int Packed = Reader.ReadInt32();
|
|
||||||
|
|
||||||
int Reg = (Packed << 2) & 0x7ffc;
|
|
||||||
int SubC = (Packed >> 13) & 7;
|
|
||||||
int Args = (Packed >> 16) & 0x1fff;
|
|
||||||
int Mode = (Packed >> 29) & 7;
|
|
||||||
|
|
||||||
if (Mode == 4)
|
|
||||||
{
|
|
||||||
//Inline Mode.
|
|
||||||
GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Args));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Word mode.
|
|
||||||
if (Mode == 1)
|
|
||||||
{
|
|
||||||
//Sequential Mode.
|
|
||||||
for (int Index = 0; Index < Args && CanRead(); Index++, Reg += 4)
|
|
||||||
{
|
|
||||||
GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Reader.ReadInt32()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Non-Sequential Mode.
|
|
||||||
int[] Arguments = new int[Args];
|
|
||||||
|
|
||||||
for (int Index = 0; Index < Args && CanRead(); Index++)
|
|
||||||
{
|
|
||||||
Arguments[Index] = Reader.ReadInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Arguments));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GpFifos.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,305 +0,0 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using Ryujinx.Graphics.Gal;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
|
||||||
{
|
|
||||||
class NsGpuPGraph
|
|
||||||
{
|
|
||||||
private NsGpu Gpu;
|
|
||||||
|
|
||||||
private uint[] Registers;
|
|
||||||
|
|
||||||
public NsGpuEngine[] SubChannels;
|
|
||||||
|
|
||||||
private Dictionary<long, int> CurrentVertexBuffers;
|
|
||||||
|
|
||||||
public NsGpuPGraph(NsGpu Gpu)
|
|
||||||
{
|
|
||||||
this.Gpu = Gpu;
|
|
||||||
|
|
||||||
Registers = new uint[0x1000];
|
|
||||||
|
|
||||||
SubChannels = new NsGpuEngine[8];
|
|
||||||
|
|
||||||
CurrentVertexBuffers = new Dictionary<long, int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory)
|
|
||||||
{
|
|
||||||
bool HasQuery = false;
|
|
||||||
|
|
||||||
foreach (NsGpuPBEntry Entry in PushBuffer)
|
|
||||||
{
|
|
||||||
if (Entry.Arguments.Count == 1)
|
|
||||||
{
|
|
||||||
SetRegister(Entry.Register, (uint)Entry.Arguments[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (Entry.Register)
|
|
||||||
{
|
|
||||||
case NsGpuRegister.BindChannel:
|
|
||||||
if (Entry.Arguments.Count > 0)
|
|
||||||
{
|
|
||||||
SubChannels[Entry.SubChannel] = (NsGpuEngine)Entry.Arguments[0];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NsGpuRegister._3dVertexArray0Fetch:
|
|
||||||
SendVertexBuffers(Memory);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NsGpuRegister._3dCbData0:
|
|
||||||
if (GetRegister(NsGpuRegister._3dCbPos) == 0x20)
|
|
||||||
{
|
|
||||||
SendTexture(Memory);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NsGpuRegister._3dQueryAddressHigh:
|
|
||||||
case NsGpuRegister._3dQueryAddressLow:
|
|
||||||
case NsGpuRegister._3dQuerySequence:
|
|
||||||
case NsGpuRegister._3dQueryGet:
|
|
||||||
HasQuery = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NsGpuRegister._3dSetShader:
|
|
||||||
uint ShaderPrg = (uint)Entry.Arguments[0];
|
|
||||||
uint ShaderId = (uint)Entry.Arguments[1];
|
|
||||||
uint CodeAddr = (uint)Entry.Arguments[2];
|
|
||||||
uint ShaderType = (uint)Entry.Arguments[3];
|
|
||||||
uint CodeEnd = (uint)Entry.Arguments[4];
|
|
||||||
|
|
||||||
SendShader(
|
|
||||||
Memory,
|
|
||||||
ShaderPrg,
|
|
||||||
ShaderId,
|
|
||||||
CodeAddr,
|
|
||||||
ShaderType,
|
|
||||||
CodeEnd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasQuery)
|
|
||||||
{
|
|
||||||
long Position =
|
|
||||||
(long)GetRegister(NsGpuRegister._3dQueryAddressHigh) << 32 |
|
|
||||||
(long)GetRegister(NsGpuRegister._3dQueryAddressLow) << 0;
|
|
||||||
|
|
||||||
uint Seq = GetRegister(NsGpuRegister._3dQuerySequence);
|
|
||||||
uint Get = GetRegister(NsGpuRegister._3dQueryGet);
|
|
||||||
|
|
||||||
uint Mode = Get & 3;
|
|
||||||
|
|
||||||
if (Mode == 0)
|
|
||||||
{
|
|
||||||
//Write
|
|
||||||
Position = Gpu.MemoryMgr.GetCpuAddr(Position);
|
|
||||||
|
|
||||||
if (Position != -1)
|
|
||||||
{
|
|
||||||
Gpu.Renderer.QueueAction(delegate()
|
|
||||||
{
|
|
||||||
Memory.WriteUInt32(Position, Seq);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendVertexBuffers(AMemory Memory)
|
|
||||||
{
|
|
||||||
long Position =
|
|
||||||
(long)GetRegister(NsGpuRegister._3dVertexArray0StartHigh) << 32 |
|
|
||||||
(long)GetRegister(NsGpuRegister._3dVertexArray0StartLow) << 0;
|
|
||||||
|
|
||||||
long Limit =
|
|
||||||
(long)GetRegister(NsGpuRegister._3dVertexArray0LimitHigh) << 32 |
|
|
||||||
(long)GetRegister(NsGpuRegister._3dVertexArray0LimitLow) << 0;
|
|
||||||
|
|
||||||
int VbIndex = CurrentVertexBuffers.Count;
|
|
||||||
|
|
||||||
if (!CurrentVertexBuffers.TryAdd(Position, VbIndex))
|
|
||||||
{
|
|
||||||
VbIndex = CurrentVertexBuffers[Position];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Limit != 0)
|
|
||||||
{
|
|
||||||
long Size = (Limit - Position) + 1;
|
|
||||||
|
|
||||||
Position = Gpu.MemoryMgr.GetCpuAddr(Position);
|
|
||||||
|
|
||||||
if (Position != -1)
|
|
||||||
{
|
|
||||||
byte[] Buffer = AMemoryHelper.ReadBytes(Memory, Position, Size);
|
|
||||||
|
|
||||||
int Stride = (int)GetRegister(NsGpuRegister._3dVertexArray0Fetch) & 0xfff;
|
|
||||||
|
|
||||||
List<GalVertexAttrib> Attribs = new List<GalVertexAttrib>();
|
|
||||||
|
|
||||||
for (int Attr = 0; Attr < 16; Attr++)
|
|
||||||
{
|
|
||||||
int Packed = (int)GetRegister(NsGpuRegister._3dVertexAttrib0Format + Attr * 4);
|
|
||||||
|
|
||||||
GalVertexAttrib Attrib = new GalVertexAttrib(Attr,
|
|
||||||
(Packed >> 0) & 0x1f,
|
|
||||||
((Packed >> 6) & 0x1) != 0,
|
|
||||||
(Packed >> 7) & 0x3fff,
|
|
||||||
(GalVertexAttribSize)((Packed >> 21) & 0x3f),
|
|
||||||
(GalVertexAttribType)((Packed >> 27) & 0x7),
|
|
||||||
((Packed >> 31) & 0x1) != 0);
|
|
||||||
|
|
||||||
if (Attrib.Offset < Stride)
|
|
||||||
{
|
|
||||||
Attribs.Add(Attrib);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gpu.Renderer.QueueAction(delegate()
|
|
||||||
{
|
|
||||||
Gpu.Renderer.SendVertexBuffer(VbIndex, Buffer, Stride, Attribs.ToArray());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendTexture(AMemory Memory)
|
|
||||||
{
|
|
||||||
long TicPos = (long)GetRegister(NsGpuRegister._3dTicAddressHigh) << 32 |
|
|
||||||
(long)GetRegister(NsGpuRegister._3dTicAddressLow) << 0;
|
|
||||||
|
|
||||||
uint CbData = GetRegister(NsGpuRegister._3dCbData0);
|
|
||||||
|
|
||||||
uint TicIndex = (CbData >> 0) & 0xfffff;
|
|
||||||
uint TscIndex = (CbData >> 20) & 0xfff; //I guess?
|
|
||||||
|
|
||||||
TicPos = Gpu.MemoryMgr.GetCpuAddr(TicPos + TicIndex * 0x20);
|
|
||||||
|
|
||||||
if (TicPos != -1)
|
|
||||||
{
|
|
||||||
int Word0 = Memory.ReadInt32(TicPos + 0x0);
|
|
||||||
int Word1 = Memory.ReadInt32(TicPos + 0x4);
|
|
||||||
int Word2 = Memory.ReadInt32(TicPos + 0x8);
|
|
||||||
int Word3 = Memory.ReadInt32(TicPos + 0xc);
|
|
||||||
int Word4 = Memory.ReadInt32(TicPos + 0x10);
|
|
||||||
int Word5 = Memory.ReadInt32(TicPos + 0x14);
|
|
||||||
int Word6 = Memory.ReadInt32(TicPos + 0x18);
|
|
||||||
int Word7 = Memory.ReadInt32(TicPos + 0x1c);
|
|
||||||
|
|
||||||
long TexAddress = Word1;
|
|
||||||
|
|
||||||
TexAddress |= (long)(Word2 & 0xff) << 32;
|
|
||||||
|
|
||||||
TexAddress = Gpu.MemoryMgr.GetCpuAddr(TexAddress);
|
|
||||||
|
|
||||||
if (TexAddress != -1)
|
|
||||||
{
|
|
||||||
NsGpuTextureFormat Format = (NsGpuTextureFormat)(Word0 & 0x7f);
|
|
||||||
|
|
||||||
int Width = (Word4 & 0xffff) + 1;
|
|
||||||
int Height = (Word5 & 0xffff) + 1;
|
|
||||||
|
|
||||||
byte[] Buffer = GetDecodedTexture(Memory, Format, TexAddress, Width, Height);
|
|
||||||
|
|
||||||
if (Buffer != null)
|
|
||||||
{
|
|
||||||
Gpu.Renderer.QueueAction(delegate()
|
|
||||||
{
|
|
||||||
Gpu.Renderer.SendR8G8B8A8Texture(0, Buffer, Width, Height);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendShader(
|
|
||||||
AMemory Memory,
|
|
||||||
uint ShaderPrg,
|
|
||||||
uint ShaderId,
|
|
||||||
uint CodeAddr,
|
|
||||||
uint ShaderType,
|
|
||||||
uint CodeEnd)
|
|
||||||
{
|
|
||||||
long CodePos = Gpu.MemoryMgr.GetCpuAddr(CodeAddr);
|
|
||||||
|
|
||||||
byte[] Data = AMemoryHelper.ReadBytes(Memory, CodePos, 0x300);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] GetDecodedTexture(
|
|
||||||
AMemory Memory,
|
|
||||||
NsGpuTextureFormat Format,
|
|
||||||
long Position,
|
|
||||||
int Width,
|
|
||||||
int Height)
|
|
||||||
{
|
|
||||||
byte[] Data = null;
|
|
||||||
|
|
||||||
switch (Format)
|
|
||||||
{
|
|
||||||
case NsGpuTextureFormat.BC1:
|
|
||||||
{
|
|
||||||
int Size = (Width * Height) >> 1;
|
|
||||||
|
|
||||||
Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
|
|
||||||
|
|
||||||
Data = BCn.DecodeBC1(new NsGpuTexture()
|
|
||||||
{
|
|
||||||
Width = Width,
|
|
||||||
Height = Height,
|
|
||||||
Data = Data
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case NsGpuTextureFormat.BC2:
|
|
||||||
{
|
|
||||||
int Size = Width * Height;
|
|
||||||
|
|
||||||
Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
|
|
||||||
|
|
||||||
Data = BCn.DecodeBC2(new NsGpuTexture()
|
|
||||||
{
|
|
||||||
Width = Width,
|
|
||||||
Height = Height,
|
|
||||||
Data = Data
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case NsGpuTextureFormat.BC3:
|
|
||||||
{
|
|
||||||
int Size = Width * Height;
|
|
||||||
|
|
||||||
Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
|
|
||||||
|
|
||||||
Data = BCn.DecodeBC3(new NsGpuTexture()
|
|
||||||
{
|
|
||||||
Width = Width,
|
|
||||||
Height = Height,
|
|
||||||
Data = Data
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//default: throw new NotImplementedException(Format.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GetRegister(NsGpuRegister Register)
|
|
||||||
{
|
|
||||||
return Registers[((int)Register >> 2) & 0xfff];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetRegister(NsGpuRegister Register, uint Value)
|
|
||||||
{
|
|
||||||
Registers[((int)Register >> 2) & 0xfff] = Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu
|
|
||||||
{
|
|
||||||
public enum NsGpuRegister
|
|
||||||
{
|
|
||||||
BindChannel = 0,
|
|
||||||
|
|
||||||
_2dClipEnable = 0x0290,
|
|
||||||
_2dOperation = 0x02ac,
|
|
||||||
|
|
||||||
_3dGlobalBase = 0x02c8,
|
|
||||||
_3dRt0AddressHigh = 0x0800,
|
|
||||||
_3dRt0AddressLow = 0x0804,
|
|
||||||
_3dRt0Horiz = 0x0808,
|
|
||||||
_3dRt0Vert = 0x080c,
|
|
||||||
_3dRt0Format = 0x0810,
|
|
||||||
_3dRt0BlockDimensions = 0x0814,
|
|
||||||
_3dRt0ArrayMode = 0x0818,
|
|
||||||
_3dRt0LayerStride = 0x081c,
|
|
||||||
_3dRt0BaseLayer = 0x0820,
|
|
||||||
_3dViewportScaleX = 0x0a00,
|
|
||||||
_3dViewportScaleY = 0x0a04,
|
|
||||||
_3dViewportScaleZ = 0x0a08,
|
|
||||||
_3dViewportTranslateX = 0x0a0c,
|
|
||||||
_3dViewportTranslateY = 0x0a10,
|
|
||||||
_3dViewportTranslateZ = 0x0a14,
|
|
||||||
_3dViewportHoriz = 0x0c00,
|
|
||||||
_3dViewportVert = 0x0c04,
|
|
||||||
_3dDepthRangeNear = 0x0c08,
|
|
||||||
_3dDepthRangeFar = 0x0c0c,
|
|
||||||
_3dClearColorR = 0x0d80,
|
|
||||||
_3dClearColorG = 0x0d84,
|
|
||||||
_3dClearColorB = 0x0d88,
|
|
||||||
_3dClearColorA = 0x0d8c,
|
|
||||||
_3dScreenScissorHoriz = 0x0ff4,
|
|
||||||
_3dScreenScissorVert = 0x0ff8,
|
|
||||||
_3dVertexAttrib0Format = 0x1160,
|
|
||||||
_3dVertexAttrib1Format = 0x1164,
|
|
||||||
_3dVertexAttrib2Format = 0x1168,
|
|
||||||
_3dVertexAttrib3Format = 0x116c,
|
|
||||||
_3dVertexAttrib4Format = 0x1170,
|
|
||||||
_3dVertexAttrib5Format = 0x1174,
|
|
||||||
_3dVertexAttrib6Format = 0x1178,
|
|
||||||
_3dVertexAttrib7Format = 0x117c,
|
|
||||||
_3dVertexAttrib8Format = 0x1180,
|
|
||||||
_3dVertexAttrib9Format = 0x1184,
|
|
||||||
_3dVertexAttrib10Format = 0x1188,
|
|
||||||
_3dVertexAttrib11Format = 0x118c,
|
|
||||||
_3dVertexAttrib12Format = 0x1190,
|
|
||||||
_3dVertexAttrib13Format = 0x1194,
|
|
||||||
_3dVertexAttrib14Format = 0x1198,
|
|
||||||
_3dVertexAttrib15Format = 0x119c,
|
|
||||||
_3dScreenYControl = 0x13ac,
|
|
||||||
_3dTscAddressHigh = 0x155c,
|
|
||||||
_3dTscAddressLow = 0x1560,
|
|
||||||
_3dTscLimit = 0x1564,
|
|
||||||
_3dTicAddressHigh = 0x1574,
|
|
||||||
_3dTicAddressLow = 0x1578,
|
|
||||||
_3dTicLimit = 0x157c,
|
|
||||||
_3dMultiSampleMode = 0x15d0,
|
|
||||||
_3dVertexEndGl = 0x1614,
|
|
||||||
_3dVertexBeginGl = 0x1618,
|
|
||||||
_3dQueryAddressHigh = 0x1b00,
|
|
||||||
_3dQueryAddressLow = 0x1b04,
|
|
||||||
_3dQuerySequence = 0x1b08,
|
|
||||||
_3dQueryGet = 0x1b0c,
|
|
||||||
_3dVertexArray0Fetch = 0x1c00,
|
|
||||||
_3dVertexArray0StartHigh = 0x1c04,
|
|
||||||
_3dVertexArray0StartLow = 0x1c08,
|
|
||||||
_3dVertexArray1Fetch = 0x1c10, //todo: the rest
|
|
||||||
_3dVertexArray0LimitHigh = 0x1f00,
|
|
||||||
_3dVertexArray0LimitLow = 0x1f04,
|
|
||||||
_3dCbSize = 0x2380,
|
|
||||||
_3dCbAddressHigh = 0x2384,
|
|
||||||
_3dCbAddressLow = 0x2388,
|
|
||||||
_3dCbPos = 0x238c,
|
|
||||||
_3dCbData0 = 0x2390,
|
|
||||||
_3dCbData1 = 0x2394,
|
|
||||||
_3dCbData2 = 0x2398,
|
|
||||||
_3dCbData3 = 0x239c,
|
|
||||||
_3dCbData4 = 0x23a0,
|
|
||||||
_3dCbData5 = 0x23a4,
|
|
||||||
_3dCbData6 = 0x23a8,
|
|
||||||
_3dCbData7 = 0x23ac,
|
|
||||||
_3dCbData8 = 0x23b0,
|
|
||||||
_3dCbData9 = 0x23b4,
|
|
||||||
_3dCbData10 = 0x23b8,
|
|
||||||
_3dCbData11 = 0x23bc,
|
|
||||||
_3dCbData12 = 0x23c0,
|
|
||||||
_3dCbData13 = 0x23c4,
|
|
||||||
_3dCbData14 = 0x23c8,
|
|
||||||
_3dCbData15 = 0x23cc,
|
|
||||||
_3dSetShader = 0x3890
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu
|
|
||||||
{
|
|
||||||
struct NsGpuTexture
|
|
||||||
{
|
|
||||||
public int Width;
|
|
||||||
public int Height;
|
|
||||||
|
|
||||||
public byte[] Data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu
|
|
||||||
{
|
|
||||||
enum NsGpuTextureFormat
|
|
||||||
{
|
|
||||||
BC1 = 0x24,
|
|
||||||
BC2 = 0x25,
|
|
||||||
BC3 = 0x26
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,11 @@
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
enum NsGpuEngine
|
enum NvGpuEngine
|
||||||
{
|
{
|
||||||
None = 0,
|
|
||||||
_2d = 0x902d,
|
_2d = 0x902d,
|
||||||
_3d = 0xb197,
|
_3d = 0xb197,
|
||||||
Compute = 0xb1c0,
|
Compute = 0xb1c0,
|
||||||
Kepler = 0xa140,
|
Kepler = 0xa140,
|
||||||
Dma = 0xb0b5,
|
Dma = 0xb0b5
|
||||||
GpFifo = 0xb06f
|
|
||||||
}
|
}
|
||||||
}
|
}
|
469
Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
Normal file
469
Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using Ryujinx.Graphics.Gal;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
class NvGpuEngine3d : INvGpuEngine
|
||||||
|
{
|
||||||
|
public int[] Registers { get; private set; }
|
||||||
|
|
||||||
|
private NsGpu Gpu;
|
||||||
|
|
||||||
|
private Dictionary<int, NvGpuMethod> Methods;
|
||||||
|
|
||||||
|
private struct ConstBuffer
|
||||||
|
{
|
||||||
|
public bool Enabled;
|
||||||
|
public long Position;
|
||||||
|
public int Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConstBuffer[] Cbs;
|
||||||
|
|
||||||
|
private bool HasDataToRender;
|
||||||
|
|
||||||
|
public NvGpuEngine3d(NsGpu Gpu)
|
||||||
|
{
|
||||||
|
this.Gpu = Gpu;
|
||||||
|
|
||||||
|
Registers = new int[0xe00];
|
||||||
|
|
||||||
|
Methods = new Dictionary<int, NvGpuMethod>();
|
||||||
|
|
||||||
|
void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method)
|
||||||
|
{
|
||||||
|
while (Count-- > 0)
|
||||||
|
{
|
||||||
|
Methods.Add(Meth, Method);
|
||||||
|
|
||||||
|
Meth += Stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddMethod(0x585, 1, 1, VertexEndGl);
|
||||||
|
AddMethod(0x674, 1, 1, ClearBuffers);
|
||||||
|
AddMethod(0x6c3, 1, 1, QueryControl);
|
||||||
|
AddMethod(0x8e4, 16, 1, CbData);
|
||||||
|
AddMethod(0x904, 1, 1, CbBind);
|
||||||
|
|
||||||
|
Cbs = new ConstBuffer[18];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method))
|
||||||
|
{
|
||||||
|
Method(Memory, PBEntry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteRegister(PBEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void VertexEndGl(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
SetFrameBuffer(0);
|
||||||
|
|
||||||
|
long[] Tags = UploadShaders(Memory);
|
||||||
|
|
||||||
|
Gpu.Renderer.BindProgram();
|
||||||
|
|
||||||
|
SetAlphaBlending();
|
||||||
|
|
||||||
|
UploadTextures(Memory, Tags);
|
||||||
|
UploadUniforms(Memory);
|
||||||
|
UploadVertexArrays(Memory);
|
||||||
|
|
||||||
|
HasDataToRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
if (HasDataToRender)
|
||||||
|
{
|
||||||
|
HasDataToRender = false;
|
||||||
|
|
||||||
|
Gpu.Renderer.DrawFrameBuffer(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Arg0 = PBEntry.Arguments[0];
|
||||||
|
|
||||||
|
int FbIndex = (Arg0 >> 6) & 0xf;
|
||||||
|
|
||||||
|
int Layer = (Arg0 >> 10) & 0x3ff;
|
||||||
|
|
||||||
|
GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f);
|
||||||
|
|
||||||
|
SetFrameBuffer(0);
|
||||||
|
|
||||||
|
Gpu.Renderer.ClearBuffers(Layer, Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetFrameBuffer(int FbIndex)
|
||||||
|
{
|
||||||
|
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
|
||||||
|
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
|
||||||
|
|
||||||
|
Gpu.Renderer.SetFb(FbIndex, Width, Height);
|
||||||
|
Gpu.Renderer.BindFrameBuffer(FbIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long[] UploadShaders(AMemory Memory)
|
||||||
|
{
|
||||||
|
long[] Tags = new long[5];
|
||||||
|
|
||||||
|
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
|
||||||
|
|
||||||
|
for (int Index = 0; Index < 6; Index++)
|
||||||
|
{
|
||||||
|
int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + Index * 0x10);
|
||||||
|
int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + Index * 0x10);
|
||||||
|
|
||||||
|
//Note: Vertex Program (B) is always enabled.
|
||||||
|
bool Enable = (Control & 1) != 0 || Index == 1;
|
||||||
|
|
||||||
|
if (!Enable)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Tag = BasePosition + (uint)Offset;
|
||||||
|
|
||||||
|
long Position = Gpu.GetCpuAddr(Tag);
|
||||||
|
|
||||||
|
//TODO: Find a better way to calculate the size.
|
||||||
|
int Size = 0x20000;
|
||||||
|
|
||||||
|
byte[] Code = AMemoryHelper.ReadBytes(Memory, Position, (uint)Size);
|
||||||
|
|
||||||
|
GalShaderType ShaderType = GetTypeFromProgram(Index);
|
||||||
|
|
||||||
|
Tags[(int)ShaderType] = Tag;
|
||||||
|
|
||||||
|
Gpu.Renderer.CreateShader(Tag, ShaderType, Code);
|
||||||
|
Gpu.Renderer.BindShader(Tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GalShaderType GetTypeFromProgram(int Program)
|
||||||
|
{
|
||||||
|
switch (Program)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 1: return GalShaderType.Vertex;
|
||||||
|
case 2: return GalShaderType.TessControl;
|
||||||
|
case 3: return GalShaderType.TessEvaluation;
|
||||||
|
case 4: return GalShaderType.Geometry;
|
||||||
|
case 5: return GalShaderType.Fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(Program));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetAlphaBlending()
|
||||||
|
{
|
||||||
|
bool BlendEnableMaster = (ReadRegister(NvGpuEngine3dReg.BlendEnableMaster) & 1) != 0;
|
||||||
|
|
||||||
|
Gpu.Renderer.SetBlendEnable(BlendEnableMaster);
|
||||||
|
|
||||||
|
bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.BlendSeparateAlpha) & 1) != 0;
|
||||||
|
|
||||||
|
GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationRgb);
|
||||||
|
|
||||||
|
GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcRgb);
|
||||||
|
GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstRgb);
|
||||||
|
|
||||||
|
if (BlendSeparateAlpha)
|
||||||
|
{
|
||||||
|
GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationAlpha);
|
||||||
|
|
||||||
|
GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcAlpha);
|
||||||
|
GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstAlpha);
|
||||||
|
|
||||||
|
Gpu.Renderer.SetBlendSeparate(
|
||||||
|
EquationRgb,
|
||||||
|
EquationAlpha,
|
||||||
|
FuncSrcRgb,
|
||||||
|
FuncDstRgb,
|
||||||
|
FuncSrcAlpha,
|
||||||
|
FuncDstAlpha);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Gpu.Renderer.SetBlend(EquationRgb, FuncSrcRgb, FuncDstRgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UploadTextures(AMemory Memory, long[] Tags)
|
||||||
|
{
|
||||||
|
long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
|
||||||
|
|
||||||
|
int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
|
||||||
|
|
||||||
|
long BasePosition = Cbs[TextureCbIndex].Position;
|
||||||
|
|
||||||
|
long Size = (uint)Cbs[TextureCbIndex].Size;
|
||||||
|
|
||||||
|
int TexIndex = 0;
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Tags.Length; Index++)
|
||||||
|
{
|
||||||
|
foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index]))
|
||||||
|
{
|
||||||
|
long Position = BasePosition + Index * Size;
|
||||||
|
|
||||||
|
UploadTexture(Memory, Position, TexIndex, DeclInfo.Index);
|
||||||
|
|
||||||
|
Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex);
|
||||||
|
|
||||||
|
TexIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UploadTexture(AMemory Memory, long BasePosition, int TexIndex, int HndIndex)
|
||||||
|
{
|
||||||
|
long Position = BasePosition + HndIndex * 4;
|
||||||
|
|
||||||
|
int TextureHandle = Memory.ReadInt32(Position);
|
||||||
|
|
||||||
|
int TicIndex = (TextureHandle >> 0) & 0xfffff;
|
||||||
|
int TscIndex = (TextureHandle >> 20) & 0xfff;
|
||||||
|
|
||||||
|
TryGetCpuAddr(NvGpuEngine3dReg.TexHeaderPoolOffset, out long TicPosition);
|
||||||
|
TryGetCpuAddr(NvGpuEngine3dReg.TexSamplerPoolOffset, out long TscPosition);
|
||||||
|
|
||||||
|
TicPosition += TicIndex * 0x20;
|
||||||
|
TscPosition += TscIndex * 0x20;
|
||||||
|
|
||||||
|
Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition));
|
||||||
|
Gpu.Renderer.SetSampler(TexIndex, TextureFactory.MakeSampler(Gpu, Memory, TscPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UploadUniforms(AMemory Memory)
|
||||||
|
{
|
||||||
|
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
|
||||||
|
|
||||||
|
for (int Index = 0; Index < 5; Index++)
|
||||||
|
{
|
||||||
|
int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + (Index + 1) * 0x10);
|
||||||
|
int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + (Index + 1) * 0x10);
|
||||||
|
|
||||||
|
//Note: Vertex Program (B) is always enabled.
|
||||||
|
bool Enable = (Control & 1) != 0 || Index == 0;
|
||||||
|
|
||||||
|
if (!Enable)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int Cbuf = 0; Cbuf < Cbs.Length; Cbuf++)
|
||||||
|
{
|
||||||
|
ConstBuffer Cb = Cbs[Cbuf];
|
||||||
|
|
||||||
|
if (Cb.Enabled)
|
||||||
|
{
|
||||||
|
long CbPosition = Cb.Position + Index * Cb.Size;
|
||||||
|
|
||||||
|
byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size);
|
||||||
|
|
||||||
|
Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UploadVertexArrays(AMemory Memory)
|
||||||
|
{
|
||||||
|
long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
|
||||||
|
|
||||||
|
int IndexSize = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
|
||||||
|
int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst);
|
||||||
|
int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
|
||||||
|
|
||||||
|
GalIndexFormat IndexFormat = (GalIndexFormat)IndexSize;
|
||||||
|
|
||||||
|
IndexSize = 1 << IndexSize;
|
||||||
|
|
||||||
|
if (IndexSize > 4)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IndexSize != 0)
|
||||||
|
{
|
||||||
|
IndexPosition = Gpu.GetCpuAddr(IndexPosition);
|
||||||
|
|
||||||
|
int BufferSize = IndexCount * IndexSize;
|
||||||
|
|
||||||
|
byte[] Data = AMemoryHelper.ReadBytes(Memory, IndexPosition, BufferSize);
|
||||||
|
|
||||||
|
Gpu.Renderer.SetIndexArray(Data, IndexFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
|
||||||
|
long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 4);
|
||||||
|
|
||||||
|
long Size = (VertexEndPos - VertexPosition) + 1;
|
||||||
|
|
||||||
|
int Stride = Control & 0xfff;
|
||||||
|
|
||||||
|
VertexPosition = Gpu.GetCpuAddr(VertexPosition);
|
||||||
|
|
||||||
|
byte[] Data = AMemoryHelper.ReadBytes(Memory, VertexPosition, Size);
|
||||||
|
|
||||||
|
GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0];
|
||||||
|
|
||||||
|
Gpu.Renderer.SetVertexArray(Index, Stride, Data, AttribArray);
|
||||||
|
|
||||||
|
int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl);
|
||||||
|
|
||||||
|
GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
|
||||||
|
|
||||||
|
if (IndexCount != 0)
|
||||||
|
{
|
||||||
|
Gpu.Renderer.DrawElements(Index, IndexFirst, PrimType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Gpu.Renderer.DrawArrays(Index, PrimType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QueryControl(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddress, out long Position))
|
||||||
|
{
|
||||||
|
int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence];
|
||||||
|
int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl];
|
||||||
|
|
||||||
|
int Mode = Ctrl & 3;
|
||||||
|
|
||||||
|
if (Mode == 0)
|
||||||
|
{
|
||||||
|
//Write.
|
||||||
|
Memory.WriteInt32(Position, Seq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteRegister(PBEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CbData(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
|
||||||
|
{
|
||||||
|
int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferNOffset);
|
||||||
|
|
||||||
|
foreach (int Arg in PBEntry.Arguments)
|
||||||
|
{
|
||||||
|
Memory.WriteInt32(Position + Offset, Arg);
|
||||||
|
|
||||||
|
Offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteRegister(NvGpuEngine3dReg.ConstBufferNOffset, Offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CbBind(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
int Index = PBEntry.Arguments[0];
|
||||||
|
|
||||||
|
bool Enabled = (Index & 1) != 0;
|
||||||
|
|
||||||
|
Index = (Index >> 4) & 0x1f;
|
||||||
|
|
||||||
|
if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
|
||||||
|
{
|
||||||
|
Cbs[Index].Position = Position;
|
||||||
|
Cbs[Index].Enabled = Enabled;
|
||||||
|
|
||||||
|
Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ReadCb(AMemory Memory, int Cbuf, int Offset)
|
||||||
|
{
|
||||||
|
long Position = Cbs[Cbuf].Position;
|
||||||
|
|
||||||
|
int Value = Memory.ReadInt32(Position + Offset);
|
||||||
|
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetCpuAddr(NvGpuEngine3dReg Reg, out long Position)
|
||||||
|
{
|
||||||
|
Position = MakeInt64From2xInt32(Reg);
|
||||||
|
|
||||||
|
Position = Gpu.GetCpuAddr(Position);
|
||||||
|
|
||||||
|
return Position != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(long)Registers[(int)Reg + 0] << 32 |
|
||||||
|
(uint)Registers[(int)Reg + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteRegister(NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
int ArgsCount = PBEntry.Arguments.Count;
|
||||||
|
|
||||||
|
if (ArgsCount > 0)
|
||||||
|
{
|
||||||
|
Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ReadRegister(NvGpuEngine3dReg Reg)
|
||||||
|
{
|
||||||
|
return Registers[(int)Reg];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteRegister(NvGpuEngine3dReg Reg, int Value)
|
||||||
|
{
|
||||||
|
Registers[(int)Reg] = Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
Normal file
44
Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
enum NvGpuEngine3dReg
|
||||||
|
{
|
||||||
|
FrameBufferNAddress = 0x200,
|
||||||
|
FrameBufferNWidth = 0x202,
|
||||||
|
FrameBufferNHeight = 0x203,
|
||||||
|
FrameBufferNFormat = 0x204,
|
||||||
|
VertexAttribNFormat = 0x458,
|
||||||
|
BlendSeparateAlpha = 0x4cf,
|
||||||
|
BlendEquationRgb = 0x4d0,
|
||||||
|
BlendFuncSrcRgb = 0x4d1,
|
||||||
|
BlendFuncDstRgb = 0x4d2,
|
||||||
|
BlendEquationAlpha = 0x4d3,
|
||||||
|
BlendFuncSrcAlpha = 0x4d4,
|
||||||
|
BlendFuncDstAlpha = 0x4d6,
|
||||||
|
BlendEnableMaster = 0x4d7,
|
||||||
|
VertexArrayElemBase = 0x50d,
|
||||||
|
TexHeaderPoolOffset = 0x55d,
|
||||||
|
TexSamplerPoolOffset = 0x557,
|
||||||
|
ShaderAddress = 0x582,
|
||||||
|
VertexBeginGl = 0x586,
|
||||||
|
IndexArrayAddress = 0x5f2,
|
||||||
|
IndexArrayEndAddr = 0x5f4,
|
||||||
|
IndexArrayFormat = 0x5f6,
|
||||||
|
IndexBatchFirst = 0x5f7,
|
||||||
|
IndexBatchCount = 0x5f8,
|
||||||
|
QueryAddress = 0x6c0,
|
||||||
|
QuerySequence = 0x6c2,
|
||||||
|
QueryControl = 0x6c3,
|
||||||
|
VertexArrayNControl = 0x700,
|
||||||
|
VertexArrayNAddress = 0x701,
|
||||||
|
VertexArrayNDivisor = 0x703,
|
||||||
|
VertexArrayNEndAddr = 0x7c0,
|
||||||
|
ShaderNControl = 0x800,
|
||||||
|
ShaderNOffset = 0x801,
|
||||||
|
ShaderNMaxGprs = 0x803,
|
||||||
|
ShaderNType = 0x804,
|
||||||
|
ConstBufferNSize = 0x8e0,
|
||||||
|
ConstBufferNAddress = 0x8e1,
|
||||||
|
ConstBufferNOffset = 0x8e3,
|
||||||
|
TextureCbIndex = 0x982
|
||||||
|
}
|
||||||
|
}
|
171
Ryujinx.Graphics/Gpu/NvGpuFifo.cs
Normal file
171
Ryujinx.Graphics/Gpu/NvGpuFifo.cs
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
public class NvGpuFifo
|
||||||
|
{
|
||||||
|
private const int MacrosCount = 0x80;
|
||||||
|
private const int MacroIndexMask = MacrosCount - 1;
|
||||||
|
|
||||||
|
private NsGpu Gpu;
|
||||||
|
|
||||||
|
private ConcurrentQueue<(AMemory, NsGpuPBEntry)> BufferQueue;
|
||||||
|
|
||||||
|
private NvGpuEngine[] SubChannels;
|
||||||
|
|
||||||
|
private struct CachedMacro
|
||||||
|
{
|
||||||
|
public long Position { get; private set; }
|
||||||
|
|
||||||
|
private MacroInterpreter Interpreter;
|
||||||
|
|
||||||
|
public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, long Position)
|
||||||
|
{
|
||||||
|
this.Position = Position;
|
||||||
|
|
||||||
|
Interpreter = new MacroInterpreter(PFifo, Engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PushParam(int Param)
|
||||||
|
{
|
||||||
|
Interpreter?.Fifo.Enqueue(Param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(AMemory Memory, int Param)
|
||||||
|
{
|
||||||
|
Interpreter?.Execute(Memory, Position, Param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long CurrMacroPosition;
|
||||||
|
private int CurrMacroBindIndex;
|
||||||
|
|
||||||
|
private CachedMacro[] Macros;
|
||||||
|
|
||||||
|
public NvGpuFifo(NsGpu Gpu)
|
||||||
|
{
|
||||||
|
this.Gpu = Gpu;
|
||||||
|
|
||||||
|
BufferQueue = new ConcurrentQueue<(AMemory, NsGpuPBEntry)>();
|
||||||
|
|
||||||
|
SubChannels = new NvGpuEngine[8];
|
||||||
|
|
||||||
|
Macros = new CachedMacro[MacrosCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PushBuffer(AMemory Memory, NsGpuPBEntry[] Buffer)
|
||||||
|
{
|
||||||
|
foreach (NsGpuPBEntry PBEntry in Buffer)
|
||||||
|
{
|
||||||
|
BufferQueue.Enqueue((Memory, PBEntry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DispatchCalls()
|
||||||
|
{
|
||||||
|
while (Step());
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Step()
|
||||||
|
{
|
||||||
|
if (BufferQueue.TryDequeue(out (AMemory Memory, NsGpuPBEntry PBEntry) Tuple))
|
||||||
|
{
|
||||||
|
CallMethod(Tuple.Memory, Tuple.PBEntry);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
if (PBEntry.Method < 0x80)
|
||||||
|
{
|
||||||
|
switch ((NvGpuFifoMeth)PBEntry.Method)
|
||||||
|
{
|
||||||
|
case NvGpuFifoMeth.BindChannel:
|
||||||
|
{
|
||||||
|
NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0];
|
||||||
|
|
||||||
|
SubChannels[PBEntry.SubChannel] = Engine;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NvGpuFifoMeth.SetMacroUploadAddress:
|
||||||
|
{
|
||||||
|
CurrMacroPosition = (long)((ulong)PBEntry.Arguments[0] << 2);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NvGpuFifoMeth.SendMacroCodeData:
|
||||||
|
{
|
||||||
|
long Position = Gpu.GetCpuAddr(CurrMacroPosition);
|
||||||
|
|
||||||
|
foreach (int Arg in PBEntry.Arguments)
|
||||||
|
{
|
||||||
|
Memory.WriteInt32(Position, Arg);
|
||||||
|
|
||||||
|
CurrMacroPosition += 4;
|
||||||
|
|
||||||
|
Position += 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NvGpuFifoMeth.SetMacroBindingIndex:
|
||||||
|
{
|
||||||
|
CurrMacroBindIndex = PBEntry.Arguments[0];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NvGpuFifoMeth.BindMacro:
|
||||||
|
{
|
||||||
|
long Position = (long)((ulong)PBEntry.Arguments[0] << 2);
|
||||||
|
|
||||||
|
Position = Gpu.GetCpuAddr(Position);
|
||||||
|
|
||||||
|
Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (SubChannels[PBEntry.SubChannel])
|
||||||
|
{
|
||||||
|
case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
if (PBEntry.Method < 0xe00)
|
||||||
|
{
|
||||||
|
Gpu.Engine3d.CallMethod(Memory, PBEntry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int MacroIndex = (PBEntry.Method >> 1) & MacroIndexMask;
|
||||||
|
|
||||||
|
if ((PBEntry.Method & 1) != 0)
|
||||||
|
{
|
||||||
|
foreach (int Arg in PBEntry.Arguments)
|
||||||
|
{
|
||||||
|
Macros[MacroIndex].PushParam(Arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Macros[MacroIndex].Execute(Memory, PBEntry.Arguments[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs
Normal file
11
Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
enum NvGpuFifoMeth
|
||||||
|
{
|
||||||
|
BindChannel = 0,
|
||||||
|
SetMacroUploadAddress = 0x45,
|
||||||
|
SendMacroCodeData = 0x46,
|
||||||
|
SetMacroBindingIndex = 0x47,
|
||||||
|
BindMacro = 0x48
|
||||||
|
}
|
||||||
|
}
|
6
Ryujinx.Graphics/Gpu/NvGpuMethod.cs
Normal file
6
Ryujinx.Graphics/Gpu/NvGpuMethod.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
delegate void NvGpuMethod(AMemory Memory, NsGpuPBEntry PBEntry);
|
||||||
|
}
|
101
Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs
Normal file
101
Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
public static class NvGpuPushBuffer
|
||||||
|
{
|
||||||
|
private enum SubmissionMode
|
||||||
|
{
|
||||||
|
Incrementing = 1,
|
||||||
|
NonIncrementing = 3,
|
||||||
|
Immediate = 4,
|
||||||
|
IncrementOnce = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NsGpuPBEntry[] Decode(byte[] Data)
|
||||||
|
{
|
||||||
|
using (MemoryStream MS = new MemoryStream(Data))
|
||||||
|
{
|
||||||
|
BinaryReader Reader = new BinaryReader(MS);
|
||||||
|
|
||||||
|
List<NsGpuPBEntry> PushBuffer = new List<NsGpuPBEntry>();
|
||||||
|
|
||||||
|
bool CanRead() => MS.Position + 4 <= MS.Length;
|
||||||
|
|
||||||
|
while (CanRead())
|
||||||
|
{
|
||||||
|
int Packed = Reader.ReadInt32();
|
||||||
|
|
||||||
|
int Meth = (Packed >> 0) & 0x1fff;
|
||||||
|
int SubC = (Packed >> 13) & 7;
|
||||||
|
int Args = (Packed >> 16) & 0x1fff;
|
||||||
|
int Mode = (Packed >> 29) & 7;
|
||||||
|
|
||||||
|
switch ((SubmissionMode)Mode)
|
||||||
|
{
|
||||||
|
case SubmissionMode.Incrementing:
|
||||||
|
{
|
||||||
|
for (int Index = 0; Index < Args && CanRead(); Index++, Meth++)
|
||||||
|
{
|
||||||
|
PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SubmissionMode.NonIncrementing:
|
||||||
|
{
|
||||||
|
int[] Arguments = new int[Args];
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Arguments.Length; Index++)
|
||||||
|
{
|
||||||
|
if (!CanRead())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Arguments[Index] = Reader.ReadInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Arguments));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SubmissionMode.Immediate:
|
||||||
|
{
|
||||||
|
PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Args));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SubmissionMode.IncrementOnce:
|
||||||
|
{
|
||||||
|
if (CanRead())
|
||||||
|
{
|
||||||
|
PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CanRead() && Args > 1)
|
||||||
|
{
|
||||||
|
int[] Arguments = new int[Args - 1];
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Arguments.Length && CanRead(); Index++)
|
||||||
|
{
|
||||||
|
Arguments[Index] = Reader.ReadInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
PushBuffer.Add(new NsGpuPBEntry(Meth + 1, SubC, Arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PushBuffer.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
Ryujinx.Graphics/Gpu/Texture.cs
Normal file
34
Ryujinx.Graphics/Gpu/Texture.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using Ryujinx.Graphics.Gal;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
struct Texture
|
||||||
|
{
|
||||||
|
public long Position { get; private set; }
|
||||||
|
|
||||||
|
public int Width { get; private set; }
|
||||||
|
public int Height { get; private set; }
|
||||||
|
|
||||||
|
public int BlockHeight { get; private set; }
|
||||||
|
|
||||||
|
public TextureSwizzle Swizzle { get; private set; }
|
||||||
|
|
||||||
|
public GalTextureFormat Format { get; private set; }
|
||||||
|
|
||||||
|
public Texture(
|
||||||
|
long Position,
|
||||||
|
int Width,
|
||||||
|
int Height,
|
||||||
|
int BlockHeight,
|
||||||
|
TextureSwizzle Swizzle,
|
||||||
|
GalTextureFormat Format)
|
||||||
|
{
|
||||||
|
this.Position = Position;
|
||||||
|
this.Width = Width;
|
||||||
|
this.Height = Height;
|
||||||
|
this.BlockHeight = BlockHeight;
|
||||||
|
this.Swizzle = Swizzle;
|
||||||
|
this.Format = Format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
Ryujinx.Graphics/Gpu/TextureFactory.cs
Normal file
83
Ryujinx.Graphics/Gpu/TextureFactory.cs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using Ryujinx.Graphics.Gal;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
static class TextureFactory
|
||||||
|
{
|
||||||
|
public static GalTexture MakeTexture(NsGpu Gpu, AMemory Memory, long TicPosition)
|
||||||
|
{
|
||||||
|
int[] Tic = ReadWords(Memory, TicPosition, 8);
|
||||||
|
|
||||||
|
GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
|
||||||
|
|
||||||
|
long TextureAddress = (uint)Tic[1];
|
||||||
|
|
||||||
|
TextureAddress |= (long)((ushort)Tic[2]) << 32;
|
||||||
|
|
||||||
|
TextureAddress = Gpu.GetCpuAddr(TextureAddress);
|
||||||
|
|
||||||
|
TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7);
|
||||||
|
|
||||||
|
int BlockHeightLog2 = (Tic[3] >> 3) & 7;
|
||||||
|
|
||||||
|
int BlockHeight = 1 << BlockHeightLog2;
|
||||||
|
|
||||||
|
int Width = (Tic[4] & 0xffff) + 1;
|
||||||
|
int Height = (Tic[5] & 0xffff) + 1;
|
||||||
|
|
||||||
|
Texture Texture = new Texture(
|
||||||
|
TextureAddress,
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
BlockHeight,
|
||||||
|
Swizzle,
|
||||||
|
Format);
|
||||||
|
|
||||||
|
byte[] Data = TextureReader.Read(Memory, Texture);
|
||||||
|
|
||||||
|
return new GalTexture(Data, Width, Height, Format);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GalTextureSampler MakeSampler(NsGpu Gpu, AMemory Memory, long TscPosition)
|
||||||
|
{
|
||||||
|
int[] Tsc = ReadWords(Memory, TscPosition, 8);
|
||||||
|
|
||||||
|
GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7);
|
||||||
|
GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7);
|
||||||
|
GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7);
|
||||||
|
|
||||||
|
GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3);
|
||||||
|
GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3);
|
||||||
|
GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3);
|
||||||
|
|
||||||
|
GalColorF BorderColor = new GalColorF(
|
||||||
|
BitConverter.Int32BitsToSingle(Tsc[4]),
|
||||||
|
BitConverter.Int32BitsToSingle(Tsc[5]),
|
||||||
|
BitConverter.Int32BitsToSingle(Tsc[6]),
|
||||||
|
BitConverter.Int32BitsToSingle(Tsc[7]));
|
||||||
|
|
||||||
|
return new GalTextureSampler(
|
||||||
|
AddressU,
|
||||||
|
AddressV,
|
||||||
|
AddressP,
|
||||||
|
MinFilter,
|
||||||
|
MagFilter,
|
||||||
|
MipFilter,
|
||||||
|
BorderColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] ReadWords(AMemory Memory, long Position, int Count)
|
||||||
|
{
|
||||||
|
int[] Words = new int[Count];
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Count; Index++, Position += 4)
|
||||||
|
{
|
||||||
|
Words[Index] = Memory.ReadInt32(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Words;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
127
Ryujinx.Graphics/Gpu/TextureReader.cs
Normal file
127
Ryujinx.Graphics/Gpu/TextureReader.cs
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using Ryujinx.Graphics.Gal;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
static class TextureReader
|
||||||
|
{
|
||||||
|
public static byte[] Read(AMemory Memory, Texture Texture)
|
||||||
|
{
|
||||||
|
switch (Texture.Format)
|
||||||
|
{
|
||||||
|
case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture);
|
||||||
|
case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture);
|
||||||
|
case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture);
|
||||||
|
case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException(Texture.Format.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture)
|
||||||
|
{
|
||||||
|
int Width = Texture.Width;
|
||||||
|
int Height = Texture.Height;
|
||||||
|
|
||||||
|
byte[] Output = new byte[Width * Height * 4];
|
||||||
|
|
||||||
|
ISwizzle Swizzle = GetSwizzle(Texture.Swizzle, Width, 4, Texture.BlockHeight);
|
||||||
|
|
||||||
|
fixed (byte* BuffPtr = Output)
|
||||||
|
{
|
||||||
|
long OutOffs = 0;
|
||||||
|
|
||||||
|
for (int Y = 0; Y < Height; Y++)
|
||||||
|
for (int X = 0; X < Width; X++)
|
||||||
|
{
|
||||||
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
|
int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset);
|
||||||
|
|
||||||
|
*(int*)(BuffPtr + OutOffs) = Pixel;
|
||||||
|
|
||||||
|
OutOffs += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe static byte[] Read8Bpt4x4(AMemory Memory, Texture Texture)
|
||||||
|
{
|
||||||
|
int Width = (Texture.Width + 3) / 4;
|
||||||
|
int Height = (Texture.Height + 3) / 4;
|
||||||
|
|
||||||
|
byte[] Output = new byte[Width * Height * 8];
|
||||||
|
|
||||||
|
ISwizzle Swizzle = GetSwizzle(Texture.Swizzle, Width, 8, Texture.BlockHeight);
|
||||||
|
|
||||||
|
fixed (byte* BuffPtr = Output)
|
||||||
|
{
|
||||||
|
long OutOffs = 0;
|
||||||
|
|
||||||
|
for (int Y = 0; Y < Height; Y++)
|
||||||
|
for (int X = 0; X < Width; X++)
|
||||||
|
{
|
||||||
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
|
long Tile = Memory.ReadInt64Unchecked(Texture.Position + Offset);
|
||||||
|
|
||||||
|
*(long*)(BuffPtr + OutOffs) = Tile;
|
||||||
|
|
||||||
|
OutOffs += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe static byte[] Read16Bpt4x4(AMemory Memory, Texture Texture)
|
||||||
|
{
|
||||||
|
int Width = (Texture.Width + 3) / 4;
|
||||||
|
int Height = (Texture.Height + 3) / 4;
|
||||||
|
|
||||||
|
byte[] Output = new byte[Width * Height * 16];
|
||||||
|
|
||||||
|
ISwizzle Swizzle = GetSwizzle(Texture.Swizzle, Width, 16, Texture.BlockHeight);
|
||||||
|
|
||||||
|
fixed (byte* BuffPtr = Output)
|
||||||
|
{
|
||||||
|
long OutOffs = 0;
|
||||||
|
|
||||||
|
for (int Y = 0; Y < Height; Y++)
|
||||||
|
for (int X = 0; X < Width; X++)
|
||||||
|
{
|
||||||
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
|
long Tile0 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 0);
|
||||||
|
long Tile1 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 8);
|
||||||
|
|
||||||
|
*(long*)(BuffPtr + OutOffs + 0) = Tile0;
|
||||||
|
*(long*)(BuffPtr + OutOffs + 8) = Tile1;
|
||||||
|
|
||||||
|
OutOffs += 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ISwizzle GetSwizzle(TextureSwizzle Swizzle, int Width, int Bpp, int BlockHeight)
|
||||||
|
{
|
||||||
|
switch (Swizzle)
|
||||||
|
{
|
||||||
|
case TextureSwizzle.Pitch:
|
||||||
|
case TextureSwizzle.PitchColorKey:
|
||||||
|
return new LinearSwizzle(Width, Bpp);
|
||||||
|
|
||||||
|
case TextureSwizzle.BlockLinear:
|
||||||
|
case TextureSwizzle.BlockLinearColorKey:
|
||||||
|
return new BlockLinearSwizzle(Width, Bpp, BlockHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException(Swizzle.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics/Gpu/TextureSwizzle.cs
Normal file
11
Ryujinx.Graphics/Gpu/TextureSwizzle.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
enum TextureSwizzle
|
||||||
|
{
|
||||||
|
_1dBuffer = 0,
|
||||||
|
PitchColorKey = 1,
|
||||||
|
Pitch = 2,
|
||||||
|
BlockLinear = 3,
|
||||||
|
BlockLinearColorKey = 4
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
|
Reference in a new issue