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 FloatAttribTypes = + new Dictionary() + { + { 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 SignedAttribTypes = new Dictionary() { @@ -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); - VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; + if (Attrib.Type == GalVertexAttribType.Sint || + Attrib.Type == GalVertexAttribType.Uint) + { + IntPtr Pointer = new IntPtr(Offset); - GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer); + VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; + + 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 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[] Attribs = new List[32]; @@ -613,10 +652,19 @@ namespace Ryujinx.Graphics Attribs[ArrayIndex] = new List(); } + 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)