aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-10-12 22:37:01 -0300
committerAc_K <Acoustik666@gmail.com>2018-10-13 01:37:01 +0000
commitaa1cd849cfe254dc7c8c9a0783a546a4b9b3c0ab (patch)
treee7d0f9d7399a835818068d7ca034b179cd95b91e
parentdd3cb33c9f43412617e2b2b76d9e2a4dec9a2a23 (diff)
Quads, QuadStrip, const attributes and half-float attributes support (#447)
* Quads, QuadStrip and const attributes support * Add support for half float attributes and fix texture pitch alignment * Throw when an unsupported float type is used as const attribute aswell
-rw-r--r--Ryujinx.Graphics/Gal/GalPipelineState.cs12
-rw-r--r--Ryujinx.Graphics/Gal/GalVertexAttrib.cs11
-rw-r--r--Ryujinx.Graphics/Gal/GalVertexBinding.cs14
-rw-r--r--Ryujinx.Graphics/Gal/IGalRasterizer.cs1
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs28
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs193
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs33
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs2
-rw-r--r--Ryujinx.Graphics/NvGpuEngine3d.cs83
-rw-r--r--Ryujinx.Graphics/QuadHelper.cs81
-rw-r--r--Ryujinx.Graphics/Texture/ImageUtils.cs6
-rw-r--r--Ryujinx.Graphics/Texture/TextureFactory.cs9
12 files changed, 420 insertions, 53 deletions
diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs
index 7b0f17d1..8837eb8c 100644
--- a/Ryujinx.Graphics/Gal/GalPipelineState.cs
+++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs
@@ -1,17 +1,5 @@
namespace Ryujinx.Graphics.Gal
{
- public struct GalVertexBinding
- {
- //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3
-
- public bool Enabled;
- public int Stride;
- public long VboKey;
- public bool Instanced;
- public int Divisor;
- public GalVertexAttrib[] Attribs;
- }
-
public class GalPipelineState
{
public const int Stages = 5;
diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
index dd040060..fa9a391f 100644
--- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
+++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
@@ -1,10 +1,13 @@
+using System;
+
namespace Ryujinx.Graphics.Gal
{
public struct GalVertexAttrib
{
- public int Index { get; private set; }
- public bool IsConst { get; private set; }
- public int Offset { get; private set; }
+ public int Index { get; private set; }
+ public bool IsConst { get; private set; }
+ public int Offset { get; private set; }
+ public IntPtr Pointer { get; private set; }
public GalVertexAttribSize Size { get; private set; }
public GalVertexAttribType Type { get; private set; }
@@ -15,12 +18,14 @@ namespace Ryujinx.Graphics.Gal
int Index,
bool IsConst,
int Offset,
+ IntPtr Pointer,
GalVertexAttribSize Size,
GalVertexAttribType Type,
bool IsBgra)
{
this.Index = Index;
this.IsConst = IsConst;
+ this.Pointer = Pointer;
this.Offset = Offset;
this.Size = Size;
this.Type = Type;
diff --git a/Ryujinx.Graphics/Gal/GalVertexBinding.cs b/Ryujinx.Graphics/Gal/GalVertexBinding.cs
new file mode 100644
index 00000000..0c3c845d
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalVertexBinding.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Gal
+{
+ public struct GalVertexBinding
+ {
+ //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3
+
+ public bool Enabled;
+ public int Stride;
+ public long VboKey;
+ public bool Instanced;
+ public int Divisor;
+ public GalVertexAttrib[] Attribs;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
index 1ee630e2..1572efa8 100644
--- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
@@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Gal
void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
void CreateIbo(long Key, int DataSize, IntPtr HostAddress);
+ void CreateIbo(long Key, int DataSize, byte[] Buffer);
void SetIndexArray(int Size, GalIndexFormat Format);
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
index 7f9e9fbe..388e06b2 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalFrontFace.CCW: return FrontFaceDirection.Ccw;
}
- throw new ArgumentException(nameof(FrontFace));
+ throw new ArgumentException(nameof(FrontFace) + " \"" + FrontFace + "\" is not valid!");
}
public static CullFaceMode GetCullFace(GalCullFace CullFace)
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack;
}
- throw new ArgumentException(nameof(CullFace));
+ throw new ArgumentException(nameof(CullFace) + " \"" + CullFace + "\" is not valid!");
}
public static StencilOp GetStencilOp(GalStencilOp Op)
@@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalStencilOp.DecrWrap: return StencilOp.DecrWrap;
}
- throw new ArgumentException(nameof(Op));
+ throw new ArgumentException(nameof(Op) + " \"" + Op + "\" is not valid!");
}
public static DepthFunction GetDepthFunc(GalComparisonOp Func)
@@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalComparisonOp.Always: return DepthFunction.Always;
}
- throw new ArgumentException(nameof(Func));
+ throw new ArgumentException(nameof(Func) + " \"" + Func + "\" is not valid!");
}
public static StencilFunction GetStencilFunc(GalComparisonOp Func)
@@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt;
}
- throw new ArgumentException(nameof(Format));
+ throw new ArgumentException(nameof(Format) + " \"" + Format + "\" is not valid!");
}
public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type)
@@ -98,8 +98,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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;
@@ -108,7 +106,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalPrimitiveType.Patches: return PrimitiveType.Patches;
}
- throw new ArgumentException(nameof(Type));
+ throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
}
public static ShaderType GetShaderType(GalShaderType Type)
@@ -122,7 +120,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalShaderType.Fragment: return ShaderType.FragmentShader;
}
- throw new ArgumentException(nameof(Type));
+ throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
}
public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat Format)
@@ -211,7 +209,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureSource.OneFloat: return All.One;
}
- throw new ArgumentException(nameof(Source));
+ throw new ArgumentException(nameof(Source) + " \"" + Source + "\" is not valid!");
}
public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
@@ -245,7 +243,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
- throw new ArgumentException(nameof(Wrap));
+ throw new ArgumentException(nameof(Wrap) + " \"" + Wrap + "\" is not valid!");
}
public static TextureMinFilter GetTextureMinFilter(
@@ -259,7 +257,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureFilter.Linear: return TextureMinFilter.Linear;
}
- throw new ArgumentException(nameof(MinFilter));
+ throw new ArgumentException(nameof(MinFilter) + " \"" + MinFilter + "\" is not valid!");
}
public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter)
@@ -270,7 +268,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureFilter.Linear: return TextureMagFilter.Linear;
}
- throw new ArgumentException(nameof(Filter));
+ throw new ArgumentException(nameof(Filter) + " \"" + Filter + "\" is not valid!");
}
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
@@ -284,7 +282,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalBlendEquation.Max: return BlendEquationMode.Max;
}
- throw new ArgumentException(nameof(BlendEquation));
+ throw new ArgumentException(nameof(BlendEquation) + " \"" + BlendEquation + "\" is not valid!");
}
public static BlendingFactor GetBlendFactor(GalBlendFactor BlendFactor)
@@ -315,7 +313,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return BlendingFactor.ConstantColor;
}
- throw new ArgumentException(nameof(BlendFactor));
+ throw new ArgumentException(nameof(BlendFactor) + " \"" + BlendFactor + "\" is not valid!");
}
}
}
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
index 20e92ff2..96da17f8 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
@@ -25,6 +25,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ GalVertexAttribSize._11_11_10, 3 }
};
+ private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> FloatAttribTypes =
+ new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
+ {
+ { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Float },
+ { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Float },
+ { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.HalfFloat },
+ { GalVertexAttribSize._32_32, VertexAttribPointerType.Float },
+ { GalVertexAttribSize._16_16_16, VertexAttribPointerType.HalfFloat },
+ { GalVertexAttribSize._16_16, VertexAttribPointerType.HalfFloat },
+ { GalVertexAttribSize._32, VertexAttribPointerType.Float },
+ { GalVertexAttribSize._16, VertexAttribPointerType.HalfFloat }
+ };
+
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> SignedAttribTypes =
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
{
@@ -356,8 +369,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
continue;
}
- GL.EnableVertexAttribArray(Attrib.Index);
-
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
bool Unsigned =
@@ -373,35 +384,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL
if (Attrib.Type == GalVertexAttribType.Float)
{
- Type = VertexAttribPointerType.Float;
+ Type = GetType(FloatAttribTypes, Attrib);
}
else
{
if (Unsigned)
{
- Type = UnsignedAttribTypes[Attrib.Size];
+ Type = GetType(UnsignedAttribTypes, Attrib);
}
else
{
- Type = SignedAttribTypes[Attrib.Size];
+ Type = GetType(SignedAttribTypes, Attrib);
}
}
- int Size = AttribElements[Attrib.Size];
+ if (!AttribElements.TryGetValue(Attrib.Size, out int Size))
+ {
+ throw new InvalidOperationException("Invalid attribute size \"" + Attrib.Size + "\"!");
+ }
+
int Offset = Attrib.Offset;
- if (Attrib.Type == GalVertexAttribType.Sint ||
- Attrib.Type == GalVertexAttribType.Uint)
+ if (Binding.Stride != 0)
{
- IntPtr Pointer = new IntPtr(Offset);
+ GL.EnableVertexAttribArray(Attrib.Index);
+
+ if (Attrib.Type == GalVertexAttribType.Sint ||
+ Attrib.Type == GalVertexAttribType.Uint)
+ {
+ IntPtr Pointer = new IntPtr(Offset);
- VertexAttribIntegerType IType = (VertexAttribIntegerType)Type;
+ VertexAttribIntegerType IType = (VertexAttribIntegerType)Type;
- GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer);
+ GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer);
+ }
+ else
+ {
+ GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
+ }
}
else
{
- GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
+ GL.DisableVertexAttribArray(Attrib.Index);
+
+ SetConstAttrib(Attrib);
}
if (Binding.Instanced && Binding.Divisor != 0)
@@ -416,6 +442,149 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
+ private static VertexAttribPointerType GetType(Dictionary<GalVertexAttribSize, VertexAttribPointerType> Dict, GalVertexAttrib Attrib)
+ {
+ if (!Dict.TryGetValue(Attrib.Size, out VertexAttribPointerType Type))
+ {
+ throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!");
+ }
+
+ return Type;
+ }
+
+ private unsafe static void SetConstAttrib(GalVertexAttrib Attrib)
+ {
+ void Unsupported()
+ {
+ throw new NotImplementedException("Constant attribute " + Attrib.Size + " not implemented!");
+ }
+
+ if (Attrib.Size == GalVertexAttribSize._10_10_10_2 ||
+ Attrib.Size == GalVertexAttribSize._11_11_10)
+ {
+ Unsupported();
+ }
+
+ if (Attrib.Type == GalVertexAttribType.Unorm)
+ {
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._8:
+ case GalVertexAttribSize._8_8:
+ case GalVertexAttribSize._8_8_8:
+ case GalVertexAttribSize._8_8_8_8:
+ GL.VertexAttrib4N((uint)Attrib.Index, (byte*)Attrib.Pointer);
+ break;
+
+ case GalVertexAttribSize._16:
+ case GalVertexAttribSize._16_16:
+ case GalVertexAttribSize._16_16_16:
+ case GalVertexAttribSize._16_16_16_16:
+ GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Attrib.Pointer);
+ break;
+
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Attrib.Pointer);
+ break;
+ }
+ }
+ else if (Attrib.Type == GalVertexAttribType.Snorm)
+ {
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._8:
+ case GalVertexAttribSize._8_8:
+ case GalVertexAttribSize._8_8_8:
+ case GalVertexAttribSize._8_8_8_8:
+ GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
+ break;
+
+ case GalVertexAttribSize._16:
+ case GalVertexAttribSize._16_16:
+ case GalVertexAttribSize._16_16_16:
+ case GalVertexAttribSize._16_16_16_16:
+ GL.VertexAttrib4N((uint)Attrib.Index, (short*)Attrib.Pointer);
+ break;
+
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttrib4N((uint)Attrib.Index, (int*)Attrib.Pointer);
+ break;
+ }
+ }
+ else if (Attrib.Type == GalVertexAttribType.Uint)
+ {
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._8:
+ case GalVertexAttribSize._8_8:
+ case GalVertexAttribSize._8_8_8:
+ case GalVertexAttribSize._8_8_8_8:
+ GL.VertexAttribI4((uint)Attrib.Index, (byte*)Attrib.Pointer);
+ break;
+
+ case GalVertexAttribSize._16:
+ case GalVertexAttribSize._16_16:
+ case GalVertexAttribSize._16_16_16:
+ case GalVertexAttribSize._16_16_16_16:
+ GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Attrib.Pointer);
+ break;
+
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttribI4((uint)Attrib.Index, (uint*)Attrib.Pointer);
+ break;
+ }
+ }
+ else if (Attrib.Type == GalVertexAttribType.Sint)
+ {
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._8:
+ case GalVertexAttribSize._8_8:
+ case GalVertexAttribSize._8_8_8:
+ case GalVertexAttribSize._8_8_8_8:
+ GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
+ break;
+
+ case GalVertexAttribSize._16:
+ case GalVertexAttribSize._16_16:
+ case GalVertexAttribSize._16_16_16:
+ case GalVertexAttribSize._16_16_16_16:
+ GL.VertexAttribI4((uint)Attrib.Index, (short*)Attrib.Pointer);
+ break;
+
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttribI4((uint)Attrib.Index, (int*)Attrib.Pointer);
+ break;
+ }
+ }
+ else if (Attrib.Type == GalVertexAttribType.Float)
+ {
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttrib4(Attrib.Index, (float*)Attrib.Pointer);
+ break;
+
+ default: Unsupported(); break;
+ }
+ }
+ }
+
private void Enable(EnableCap Cap, bool Enabled)
{
if (Enabled)
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
index a74aee07..7b435c45 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
@@ -119,6 +119,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
}
+ public void CreateIbo(long Key, int DataSize, byte[] Buffer)
+ {
+ int Handle = GL.GenBuffer();
+
+ IboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
+
+ IntPtr Length = new IntPtr(Buffer.Length);
+
+ GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
+ GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
+ }
+
public void SetIndexArray(int Size, GalIndexFormat Format)
{
IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
@@ -135,7 +147,26 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return;
}
- GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count);
+ if (PrimType == GalPrimitiveType.Quads)
+ {
+ for (int Offset = 0; Offset < Count; Offset += 4)
+ {
+ GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
+ }
+ }
+ else if (PrimType == GalPrimitiveType.QuadStrip)
+ {
+ GL.DrawArrays(PrimitiveType.TriangleFan, First, 4);
+
+ for (int Offset = 2; Offset < Count; Offset += 2)
+ {
+ GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
+ }
+ }
+ else
+ {
+ GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count);
+ }
}
public void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType)
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
index 78cf5d2f..0de070b5 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
@@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
Attachments.Zeta = Key;
}
-
+
public void UnbindZeta()
{
Attachments.Zeta = 0;
diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs
index 22c09377..3dd7746d 100644
--- a/Ryujinx.Graphics/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/NvGpuEngine3d.cs
@@ -433,7 +433,7 @@ namespace Ryujinx.Graphics
private void SetRenderTargets()
{
- //Commercial games do not seem to
+ //Commercial games do not seem to
//bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData);
uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl));
@@ -568,12 +568,15 @@ namespace Ryujinx.Graphics
private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State)
{
- long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
+ long IbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
- long IboKey = Vmm.GetPhysicalAddress(IndexPosition);
+ long IboKey = Vmm.GetPhysicalAddress(IbPosition);
int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
+ int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl);
+
+ GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt;
@@ -590,14 +593,50 @@ namespace Ryujinx.Graphics
bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize);
+ bool UsesLegacyQuads =
+ PrimType == GalPrimitiveType.Quads ||
+ PrimType == GalPrimitiveType.QuadStrip;
+
if (!IboCached || QueryKeyUpload(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index))
{
- IntPtr DataAddress = Vmm.GetHostAddress(IndexPosition, IbSize);
+ if (!UsesLegacyQuads)
+ {
+ IntPtr DataAddress = Vmm.GetHostAddress(IbPosition, IbSize);
- Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress);
+ Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress);
+ }
+ else
+ {
+ byte[] Buffer = Vmm.ReadBytes(IbPosition, IbSize);
+
+ if (PrimType == GalPrimitiveType.Quads)
+ {
+ Buffer = QuadHelper.ConvertIbQuadsToTris(Buffer, IndexEntrySize, IndexCount);
+ }
+ else /* if (PrimType == GalPrimitiveType.QuadStrip) */
+ {
+ Buffer = QuadHelper.ConvertIbQuadStripToTris(Buffer, IndexEntrySize, IndexCount);
+ }
+
+ Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Buffer);
+ }
}
- Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat);
+ if (!UsesLegacyQuads)
+ {
+ Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat);
+ }
+ else
+ {
+ if (PrimType == GalPrimitiveType.Quads)
+ {
+ Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadsToTris(IbSize), IndexFormat);
+ }
+ else /* if (PrimType == GalPrimitiveType.QuadStrip) */
+ {
+ Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadStripToTris(IbSize), IndexFormat);
+ }
+ }
}
List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
@@ -613,10 +652,19 @@ namespace Ryujinx.Graphics
Attribs[ArrayIndex] = new List<GalVertexAttrib>();
}
+ long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4);
+
+ int Offset = (Packed >> 7) & 0x3fff;
+
+ //Note: 16 is the maximum size of an attribute,
+ //having a component size of 32-bits with 4 elements (a vec4).
+ IntPtr Pointer = Vmm.GetHostAddress(VertexPosition + Offset, 16);
+
Attribs[ArrayIndex].Add(new GalVertexAttrib(
Attr,
((Packed >> 6) & 0x1) != 0,
- (Packed >> 7) & 0x3fff,
+ Offset,
+ Pointer,
(GalVertexAttribSize)((Packed >> 21) & 0x3f),
(GalVertexAttribType)((Packed >> 27) & 0x7),
((Packed >> 31) & 0x1) != 0));
@@ -722,6 +770,27 @@ namespace Ryujinx.Graphics
long IboKey = Vmm.GetPhysicalAddress(IndexPosition);
+ //Quad primitive types were deprecated on OpenGL 3.x,
+ //they are converted to a triangles index buffer on IB creation,
+ //so we should use the triangles type here too.
+ if (PrimType == GalPrimitiveType.Quads ||
+ PrimType == GalPrimitiveType.QuadStrip)
+ {
+ PrimType = GalPrimitiveType.Triangles;
+
+ //Note: We assume that index first points to the first
+ //vertex of a quad, if it points to the middle of a
+ //quad (First % 4 != 0 for Quads) then it will not work properly.
+ if (PrimType == GalPrimitiveType.Quads)
+ {
+ IndexFirst = QuadHelper.ConvertIbSizeQuadsToTris(IndexFirst);
+ }
+ else /* if (PrimType == GalPrimitiveType.QuadStrip) */
+ {
+ IndexFirst = QuadHelper.ConvertIbSizeQuadStripToTris(IndexFirst);
+ }
+ }
+
Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType);
}
else
diff --git a/Ryujinx.Graphics/QuadHelper.cs b/Ryujinx.Graphics/QuadHelper.cs
new file mode 100644
index 00000000..0dfffce0
--- /dev/null
+++ b/Ryujinx.Graphics/QuadHelper.cs
@@ -0,0 +1,81 @@
+using System;
+
+namespace Ryujinx.Graphics
+{
+ static class QuadHelper
+ {
+ public static int ConvertIbSizeQuadsToTris(int Size)
+ {
+ return Size <= 0 ? 0 : (Size / 4) * 6;
+ }
+
+ public static int ConvertIbSizeQuadStripToTris(int Size)
+ {
+ return Size <= 1 ? 0 : ((Size - 2) / 2) * 6;
+ }
+
+ public static byte[] ConvertIbQuadsToTris(byte[] Data, int EntrySize, int Count)
+ {
+ int PrimitivesCount = Count / 4;
+
+ int QuadPrimSize = 4 * EntrySize;
+ int TrisPrimSize = 6 * EntrySize;
+
+ byte[] Output = new byte[PrimitivesCount * 6 * EntrySize];
+
+ for (int Prim = 0; Prim < PrimitivesCount; Prim++)
+ {
+ void AssignIndex(int Src, int Dst, int CopyCount = 1)
+ {
+ Src = Prim * QuadPrimSize + Src * EntrySize;
+ Dst = Prim * TrisPrimSize + Dst * EntrySize;
+
+ Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize);
+ }
+
+ //0 1 2 -> 0 1 2.
+ AssignIndex(0, 0, 3);
+
+ //2 3 -> 3 4.
+ AssignIndex(2, 3, 2);
+
+ //0 -> 5.
+ AssignIndex(0, 5);
+ }
+
+ return Output;
+ }
+
+ public static byte[] ConvertIbQuadStripToTris(byte[] Data, int EntrySize, int Count)
+ {
+ int PrimitivesCount = (Count - 2) / 2;
+
+ int QuadPrimSize = 2 * EntrySize;
+ int TrisPrimSize = 6 * EntrySize;
+
+ byte[] Output = new byte[PrimitivesCount * 6 * EntrySize];
+
+ for (int Prim = 0; Prim < PrimitivesCount; Prim++)
+ {
+ void AssignIndex(int Src, int Dst, int CopyCount = 1)
+ {
+ Src = Prim * QuadPrimSize + Src * EntrySize + 2 * EntrySize;
+ Dst = Prim * TrisPrimSize + Dst * EntrySize;
+
+ Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize);
+ }
+
+ //-2 -1 0 -> 0 1 2.
+ AssignIndex(-2, 0, 3);
+
+ //0 1 -> 3 4.
+ AssignIndex(0, 3, 2);
+
+ //-2 -> 5.
+ AssignIndex(-2, 5);
+ }
+
+ return Output;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Texture/ImageUtils.cs b/Ryujinx.Graphics/Texture/ImageUtils.cs
index 18a179fb..1b043245 100644
--- a/Ryujinx.Graphics/Texture/ImageUtils.cs
+++ b/Ryujinx.Graphics/Texture/ImageUtils.cs
@@ -289,7 +289,11 @@ namespace Ryujinx.Graphics.Texture
{
ImageDescriptor Desc = GetImageDescriptor(Format);
- return Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth);
+ int Pitch = Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth);
+
+ Pitch = (Pitch + 0x1f) & ~0x1f;
+
+ return Pitch;
}
public static int GetBlockWidth(GalImageFormat Format)
diff --git a/Ryujinx.Graphics/Texture/TextureFactory.cs b/Ryujinx.Graphics/Texture/TextureFactory.cs
index 766c53da..c0c53b06 100644
--- a/Ryujinx.Graphics/Texture/TextureFactory.cs
+++ b/Ryujinx.Graphics/Texture/TextureFactory.cs
@@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Texture
int Width = (Tic[4] & 0xffff) + 1;
int Height = (Tic[5] & 0xffff) + 1;
- return new GalImage(
+ GalImage Image = new GalImage(
Width,
Height,
TileWidth,
@@ -51,6 +51,13 @@ namespace Ryujinx.Graphics.Texture
YSource,
ZSource,
WSource);
+
+ if (Layout == GalMemoryLayout.Pitch)
+ {
+ Image.Pitch = (Tic[3] & 0xffff) << 5;
+ }
+
+ return Image;
}
public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)