aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReinUsesLisp <reinuseslisp@airmail.cc>2018-07-05 15:47:29 -0300
committergdkchan <gab.dark.100@gmail.com>2018-07-05 15:47:29 -0300
commit97ca974213ec9564ed4a9c57e998ca726dbbb64f (patch)
tree5daf598d267d3ce05f3ef342621e7f93536fa554
parentc99b2884e4eb9adfb5b893ee84d7678262d19b06 (diff)
Implement some GPU features (#209)
* Implement stencil testing * Implement depth testing * Implement face culling * Implement front face * Comparison functions now take OGL enums too * Fix front facing when flipping was used * Add depth and stencil clear values
-rw-r--r--Ryujinx.Graphics/Gal/GalComparisonOp.cs16
-rw-r--r--Ryujinx.Graphics/Gal/GalCullFace.cs9
-rw-r--r--Ryujinx.Graphics/Gal/GalFrontFace.cs8
-rw-r--r--Ryujinx.Graphics/Gal/GalStencilOp.cs14
-rw-r--r--Ryujinx.Graphics/Gal/IGalRasterizer.cs18
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs59
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs53
-rw-r--r--Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs157
-rw-r--r--Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs18
9 files changed, 334 insertions, 18 deletions
diff --git a/Ryujinx.Graphics/Gal/GalComparisonOp.cs b/Ryujinx.Graphics/Gal/GalComparisonOp.cs
index ddddeceb..f26a7753 100644
--- a/Ryujinx.Graphics/Gal/GalComparisonOp.cs
+++ b/Ryujinx.Graphics/Gal/GalComparisonOp.cs
@@ -2,13 +2,13 @@ namespace Ryujinx.Graphics.Gal
{
public enum GalComparisonOp
{
- Never = 0x200,
- Less = 0x201,
- Equal = 0x202,
- Lequal = 0x203,
- Greater = 0x204,
- NotEqual = 0x205,
- Gequal = 0x206,
- Always = 0x207
+ Never = 0x1,
+ Less = 0x2,
+ Equal = 0x3,
+ Lequal = 0x4,
+ Greater = 0x5,
+ NotEqual = 0x6,
+ Gequal = 0x7,
+ Always = 0x8
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalCullFace.cs b/Ryujinx.Graphics/Gal/GalCullFace.cs
new file mode 100644
index 00000000..4ab3e174
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalCullFace.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.Gal
+{
+ public enum GalCullFace
+ {
+ Front = 0x404,
+ Back = 0x405,
+ FrontAndBack = 0x408
+ }
+}
diff --git a/Ryujinx.Graphics/Gal/GalFrontFace.cs b/Ryujinx.Graphics/Gal/GalFrontFace.cs
new file mode 100644
index 00000000..17ad1126
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalFrontFace.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.Gal
+{
+ public enum GalFrontFace
+ {
+ CW = 0x900,
+ CCW = 0x901
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalStencilOp.cs b/Ryujinx.Graphics/Gal/GalStencilOp.cs
new file mode 100644
index 00000000..fc83ca5e
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalStencilOp.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Gal
+{
+ public enum GalStencilOp
+ {
+ Keep = 0x1,
+ Zero = 0x2,
+ Replace = 0x3,
+ Incr = 0x4,
+ Decr = 0x5,
+ Invert = 0x6,
+ IncrWrap = 0x7,
+ DecrWrap = 0x8
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
index e0469382..586eae6b 100644
--- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
@@ -8,16 +8,34 @@ namespace Ryujinx.Graphics.Gal
bool IsIboCached(long Key, long DataSize);
+ void SetFrontFace(GalFrontFace FrontFace);
+
void EnableCullFace();
void DisableCullFace();
+ void SetCullFace(GalCullFace CullFace);
+
void EnableDepthTest();
void DisableDepthTest();
void SetDepthFunction(GalComparisonOp Func);
+ void SetClearDepth(float Depth);
+
+ void EnableStencilTest();
+
+ void DisableStencilTest();
+
+ void SetStencilFunction(bool IsFrontFace, GalComparisonOp Func, int Ref, int Mask);
+
+ void SetStencilOp(bool IsFrontFace, GalStencilOp Fail, GalStencilOp ZFail, GalStencilOp ZPass);
+
+ void SetStencilMask(bool IsFrontFace, int Mask);
+
+ void SetClearStencil(int Stencil);
+
void CreateVbo(long Key, byte[] Buffer);
void CreateIbo(long Key, byte[] Buffer);
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
index 349c695e..3a81150d 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -5,17 +5,76 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLEnumConverter
{
+ public static FrontFaceDirection GetFrontFace(GalFrontFace FrontFace)
+ {
+ switch (FrontFace)
+ {
+ case GalFrontFace.CW: return FrontFaceDirection.Cw;
+ case GalFrontFace.CCW: return FrontFaceDirection.Ccw;
+ }
+
+ throw new ArgumentException(nameof(FrontFace));
+ }
+
+ public static CullFaceMode GetCullFace(GalCullFace CullFace)
+ {
+ switch (CullFace)
+ {
+ case GalCullFace.Front: return CullFaceMode.Front;
+ case GalCullFace.Back: return CullFaceMode.Back;
+ case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack;
+ }
+
+ throw new ArgumentException(nameof(CullFace));
+ }
+
+ public static StencilOp GetStencilOp(GalStencilOp Op)
+ {
+ switch (Op)
+ {
+ case GalStencilOp.Keep: return StencilOp.Keep;
+ case GalStencilOp.Zero: return StencilOp.Zero;
+ case GalStencilOp.Replace: return StencilOp.Replace;
+ case GalStencilOp.Incr: return StencilOp.Incr;
+ case GalStencilOp.Decr: return StencilOp.Decr;
+ case GalStencilOp.Invert: return StencilOp.Invert;
+ case GalStencilOp.IncrWrap: return StencilOp.IncrWrap;
+ case GalStencilOp.DecrWrap: return StencilOp.DecrWrap;
+ }
+
+ throw new ArgumentException(nameof(Op));
+ }
+
public static DepthFunction GetDepthFunc(GalComparisonOp Func)
{
+ //Looks like the GPU can take it's own values (described in GalComparisonOp) and OpenGL values alike
if ((int)Func >= (int)DepthFunction.Never &&
(int)Func <= (int)DepthFunction.Always)
{
return (DepthFunction)Func;
}
+ switch (Func)
+ {
+ case GalComparisonOp.Never: return DepthFunction.Never;
+ case GalComparisonOp.Less: return DepthFunction.Less;
+ case GalComparisonOp.Equal: return DepthFunction.Equal;
+ case GalComparisonOp.Lequal: return DepthFunction.Lequal;
+ case GalComparisonOp.Greater: return DepthFunction.Greater;
+ case GalComparisonOp.NotEqual: return DepthFunction.Notequal;
+ case GalComparisonOp.Gequal: return DepthFunction.Gequal;
+ case GalComparisonOp.Always: return DepthFunction.Always;
+ }
+
throw new ArgumentException(nameof(Func));
}
+ public static StencilFunction GetStencilFunc(GalComparisonOp Func)
+ {
+ //OGL comparison values match, it's just an enum cast
+ return (StencilFunction)GetDepthFunc(Func);
+ }
+
public static DrawElementsType GetDrawElementsType(GalIndexFormat Format)
{
switch (Format)
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
index 8bff6bb3..b9885711 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
@@ -106,6 +106,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return IboCache.TryGetSize(Key, out long Size) && Size == DataSize;
}
+ public void SetFrontFace(GalFrontFace FrontFace)
+ {
+ GL.FrontFace(OGLEnumConverter.GetFrontFace(FrontFace));
+ }
+
public void EnableCullFace()
{
GL.Enable(EnableCap.CullFace);
@@ -116,6 +121,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Disable(EnableCap.CullFace);
}
+ public void SetCullFace(GalCullFace CullFace)
+ {
+ GL.CullFace(OGLEnumConverter.GetCullFace(CullFace));
+ }
+
public void EnableDepthTest()
{
GL.Enable(EnableCap.DepthTest);
@@ -131,6 +141,49 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.DepthFunc(OGLEnumConverter.GetDepthFunc(Func));
}
+ public void SetClearDepth(float Depth)
+ {
+ GL.ClearDepth(Depth);
+ }
+
+ public void EnableStencilTest()
+ {
+ GL.Enable(EnableCap.StencilTest);
+ }
+
+ public void DisableStencilTest()
+ {
+ GL.Disable(EnableCap.StencilTest);
+ }
+
+ public void SetStencilFunction(bool IsFrontFace, GalComparisonOp Func, int Ref, int Mask)
+ {
+ GL.StencilFuncSeparate(
+ IsFrontFace ? StencilFace.Front : StencilFace.Back,
+ OGLEnumConverter.GetStencilFunc(Func),
+ Ref,
+ Mask);
+ }
+
+ public void SetStencilOp(bool IsFrontFace, GalStencilOp Fail, GalStencilOp ZFail, GalStencilOp ZPass)
+ {
+ GL.StencilOpSeparate(
+ IsFrontFace ? StencilFace.Front : StencilFace.Back,
+ OGLEnumConverter.GetStencilOp(Fail),
+ OGLEnumConverter.GetStencilOp(ZFail),
+ OGLEnumConverter.GetStencilOp(ZPass));
+ }
+
+ public void SetStencilMask(bool IsFrontFace, int Mask)
+ {
+ GL.StencilMaskSeparate(IsFrontFace ? StencilFace.Front : StencilFace.Back, Mask);
+ }
+
+ public void SetClearStencil(int Stencil)
+ {
+ GL.ClearStencil(Stencil);
+ }
+
public void CreateVbo(long Key, byte[] Buffer)
{
int Handle = GL.GenBuffer();
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs
index b27f5c14..e0e769d4 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs
@@ -79,8 +79,10 @@ namespace Ryujinx.HLE.Gpu.Engines
Gpu.Renderer.Shader.BindProgram();
+ SetFrontFace();
SetCullFace();
SetDepth();
+ SetStencil();
SetAlphaBlending();
UploadTextures(Vmm, Keys);
@@ -173,14 +175,8 @@ namespace Ryujinx.HLE.Gpu.Engines
Gpu.Renderer.Shader.Bind(Key);
}
- int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX);
- int RawSY = ReadRegister(NvGpuEngine3dReg.ViewportScaleY);
-
- float SX = BitConverter.Int32BitsToSingle(RawSX);
- float SY = BitConverter.Int32BitsToSingle(RawSY);
-
- float SignX = MathF.Sign(SX);
- float SignY = MathF.Sign(SY);
+ float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportScaleX);
+ float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportScaleY);
Gpu.Renderer.Shader.SetFlip(SignX, SignY);
@@ -202,14 +198,145 @@ namespace Ryujinx.HLE.Gpu.Engines
throw new ArgumentOutOfRangeException(nameof(Program));
}
+ private void SetFrontFace()
+ {
+ float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportScaleX);
+ float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportScaleY);
+
+ GalFrontFace FrontFace = (GalFrontFace)ReadRegister(NvGpuEngine3dReg.FrontFace);
+
+ //Flipping breaks facing. Flipping front facing too fixes it
+ if (SignX != SignY)
+ {
+ switch (FrontFace)
+ {
+ case GalFrontFace.CW:
+ FrontFace = GalFrontFace.CCW;
+ break;
+
+ case GalFrontFace.CCW:
+ FrontFace = GalFrontFace.CW;
+ break;
+ }
+ }
+
+ Gpu.Renderer.Rasterizer.SetFrontFace(FrontFace);
+ }
+
private void SetCullFace()
{
- //TODO.
+ bool Enable = (ReadRegister(NvGpuEngine3dReg.CullFaceEnable) & 1) != 0;
+
+ if (Enable)
+ {
+ Gpu.Renderer.Rasterizer.EnableCullFace();
+ }
+ else
+ {
+ Gpu.Renderer.Rasterizer.DisableCullFace();
+ }
+
+ if (!Enable)
+ {
+ return;
+ }
+
+ GalCullFace CullFace = (GalCullFace)ReadRegister(NvGpuEngine3dReg.CullFace);
+
+ Gpu.Renderer.Rasterizer.SetCullFace(CullFace);
}
private void SetDepth()
{
- //TODO.
+ float ClearDepth = ReadRegisterFloat(NvGpuEngine3dReg.ClearDepth);
+
+ Gpu.Renderer.Rasterizer.SetClearDepth(ClearDepth);
+
+ bool Enable = (ReadRegister(NvGpuEngine3dReg.DepthTestEnable) & 1) != 0;
+
+ if (Enable)
+ {
+ Gpu.Renderer.Rasterizer.EnableDepthTest();
+ }
+ else
+ {
+ Gpu.Renderer.Rasterizer.DisableDepthTest();
+ }
+
+ if (!Enable)
+ {
+ return;
+ }
+
+ GalComparisonOp Func = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction);
+
+ Gpu.Renderer.Rasterizer.SetDepthFunction(Func);
+ }
+
+ private void SetStencil()
+ {
+ int ClearStencil = ReadRegister(NvGpuEngine3dReg.ClearStencil);
+
+ Gpu.Renderer.Rasterizer.SetClearStencil(ClearStencil);
+
+ bool Enable = (ReadRegister(NvGpuEngine3dReg.StencilEnable) & 1) != 0;
+
+ if (Enable)
+ {
+ Gpu.Renderer.Rasterizer.EnableStencilTest();
+ }
+ else
+ {
+ Gpu.Renderer.Rasterizer.DisableStencilTest();
+ }
+
+ if (!Enable)
+ {
+ return;
+ }
+
+ void SetFaceStencil(
+ bool IsFrontFace,
+ NvGpuEngine3dReg Func,
+ NvGpuEngine3dReg FuncRef,
+ NvGpuEngine3dReg FuncMask,
+ NvGpuEngine3dReg OpFail,
+ NvGpuEngine3dReg OpZFail,
+ NvGpuEngine3dReg OpZPass,
+ NvGpuEngine3dReg Mask)
+ {
+ Gpu.Renderer.Rasterizer.SetStencilFunction(
+ IsFrontFace,
+ (GalComparisonOp)ReadRegister(Func),
+ ReadRegister(FuncRef),
+ ReadRegister(FuncMask));
+
+ Gpu.Renderer.Rasterizer.SetStencilOp(
+ IsFrontFace,
+ (GalStencilOp)ReadRegister(OpFail),
+ (GalStencilOp)ReadRegister(OpZFail),
+ (GalStencilOp)ReadRegister(OpZPass));
+
+ Gpu.Renderer.Rasterizer.SetStencilMask(IsFrontFace, ReadRegister(Mask));
+ }
+
+ SetFaceStencil(false,
+ NvGpuEngine3dReg.StencilBackFuncFunc,
+ NvGpuEngine3dReg.StencilBackFuncRef,
+ NvGpuEngine3dReg.StencilBackFuncMask,
+ NvGpuEngine3dReg.StencilBackOpFail,
+ NvGpuEngine3dReg.StencilBackOpZFail,
+ NvGpuEngine3dReg.StencilBackOpZPass,
+ NvGpuEngine3dReg.StencilBackMask);
+
+ SetFaceStencil(true,
+ NvGpuEngine3dReg.StencilFrontFuncFunc,
+ NvGpuEngine3dReg.StencilFrontFuncRef,
+ NvGpuEngine3dReg.StencilFrontFuncMask,
+ NvGpuEngine3dReg.StencilFrontOpFail,
+ NvGpuEngine3dReg.StencilFrontOpZFail,
+ NvGpuEngine3dReg.StencilFrontOpZPass,
+ NvGpuEngine3dReg.StencilFrontMask);
}
private void SetAlphaBlending()
@@ -549,6 +676,11 @@ namespace Ryujinx.HLE.Gpu.Engines
ConstBuffers[Stage][Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize);
}
+ private float GetFlipSign(NvGpuEngine3dReg Reg)
+ {
+ return MathF.Sign(ReadRegisterFloat(Reg));
+ }
+
private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg)
{
return
@@ -571,6 +703,11 @@ namespace Ryujinx.HLE.Gpu.Engines
return Registers[(int)Reg];
}
+ private float ReadRegisterFloat(NvGpuEngine3dReg Reg)
+ {
+ return BitConverter.Int32BitsToSingle(ReadRegister(Reg));
+ }
+
private void WriteRegister(NvGpuEngine3dReg Reg, int Value)
{
Registers[(int)Reg] = Value;
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs
index 64866ce9..9eb2966d 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs
+++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs
@@ -14,6 +14,11 @@ namespace Ryujinx.HLE.Gpu.Engines
ViewportTranslateZ = 0x285,
VertexArrayFirst = 0x35d,
VertexArrayCount = 0x35e,
+ ClearDepth = 0x364,
+ ClearStencil = 0x368,
+ StencilBackFuncRef = 0x3d5,
+ StencilBackMask = 0x3d6,
+ StencilBackFuncMask = 0x3d7,
VertexAttribNFormat = 0x458,
DepthTestEnable = 0x4b3,
IBlendEnable = 0x4b9,
@@ -27,9 +32,22 @@ namespace Ryujinx.HLE.Gpu.Engines
BlendFuncDstAlpha = 0x4d6,
BlendEnableMaster = 0x4d7,
IBlendNEnable = 0x4d8,
+ StencilEnable = 0x4e0,
+ StencilFrontOpFail = 0x4e1,
+ StencilFrontOpZFail = 0x4e2,
+ StencilFrontOpZPass = 0x4e3,
+ StencilFrontFuncFunc = 0x4e4,
+ StencilFrontFuncRef = 0x4e5,
+ StencilFrontFuncMask = 0x4e6,
+ StencilFrontMask = 0x4e7,
VertexArrayElemBase = 0x50d,
TexHeaderPoolOffset = 0x55d,
TexSamplerPoolOffset = 0x557,
+ StencilTwoSideEnable = 0x565,
+ StencilBackOpFail = 0x566,
+ StencilBackOpZFail = 0x567,
+ StencilBackOpZPass = 0x568,
+ StencilBackFuncFunc = 0x569,
ShaderAddress = 0x582,
VertexBeginGl = 0x586,
IndexArrayAddress = 0x5f2,