diff --git a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs index e7b06cfe9e..fa80fee23a 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs @@ -98,7 +98,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv { int Fd = Context.RequestData.ReadInt32(); int Cmd = Context.RequestData.ReadInt32() & 0xffff; - + NvFd FdData = Fds.GetData(Context.Process, Fd); long Position = Context.Request.GetSendBuffPtr(); @@ -206,7 +206,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv int Flags = Reader.ReadInt32(); int Kind = Reader.ReadInt32(); int Handle = Reader.ReadInt32(); - int PageSize = Reader.ReadInt32(); + int PageSize = Reader.ReadInt32(); long BuffAddr = Reader.ReadInt64(); long MapSize = Reader.ReadInt64(); long Offset = Reader.ReadInt64(); @@ -226,7 +226,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv if (Map == null) { Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); - + return -1; //TODO: Corrent error code. } @@ -590,7 +590,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv if (Map == null) { Logging.Warn($"Trying to use invalid NvMap Id {Id}!"); - + return -1; //TODO: Corrent error code. } @@ -617,13 +617,13 @@ namespace Ryujinx.Core.OsHle.Services.Nv if (Map == null) { Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); - + return -1; //TODO: Corrent error code. } Map.CpuAddress = Addr; - Map.Align = Align; - Map.Kind = Kind; + Map.Align = Align; + Map.Kind = Kind; return 0; } @@ -643,7 +643,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv if (Map == null) { Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); - + return -1; //TODO: Corrent error code. } @@ -668,7 +668,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv if (Map == null) { Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); - + return -1; //TODO: Corrent error code. } @@ -698,7 +698,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv if (Map == null) { Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); - + return -1; //TODO: Corrent error code. } diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 36030d20f8..b7c30d2a44 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -62,7 +62,7 @@ namespace Ryujinx.Core.OsHle.Services.Android private BufferEntry[] BufferQueue; private ManualResetEvent WaitBufferFree; - + private object RenderQueueLock; private int RenderQueueCount; @@ -85,7 +85,7 @@ namespace Ryujinx.Core.OsHle.Services.Android { ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect }, { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer } }; - + this.Renderer = Renderer; this.ReleaseEvent = ReleaseEvent; @@ -139,7 +139,7 @@ namespace Ryujinx.Core.OsHle.Services.Android using (MemoryStream MS = new MemoryStream()) { BinaryWriter Writer = new BinaryWriter(MS); - + BufferEntry Entry = BufferQueue[Slot]; int BufferCount = 1; //? @@ -243,7 +243,7 @@ namespace Ryujinx.Core.OsHle.Services.Android private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader) { int Slot = ParcelReader.ReadInt32(); - + int BufferCount = ParcelReader.ReadInt32(); long BufferSize = ParcelReader.ReadInt64(); @@ -293,7 +293,7 @@ namespace Ryujinx.Core.OsHle.Services.Android NvMapFb MapFb = (NvMapFb)ServiceNvDrv.NvMapsFb.GetData(Context.Process, 0); long Address = Map.CpuAddress; - + if (MapFb.HasBufferOffset(Slot)) { Address += MapFb.GetBufferOffset(Slot); diff --git a/Ryujinx.Graphics/Gal/GalBlendEquation.cs b/Ryujinx.Graphics/Gal/GalBlendEquation.cs new file mode 100644 index 0000000000..d9f8e79934 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalBlendEquation.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalBlendEquation + { + FuncAdd = 0x8006, + Min = 0x8007, + Max = 0x8008, + FuncSubtract = 0x800a, + FuncReverseSubtract = 0x800b + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalBlendFactor.cs b/Ryujinx.Graphics/Gal/GalBlendFactor.cs new file mode 100644 index 0000000000..de7d45fd48 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalBlendFactor.cs @@ -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 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalColorF.cs b/Ryujinx.Graphics/Gal/GalColorF.cs new file mode 100644 index 0000000000..7cfb171dcd --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalColorF.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalIndexFormat.cs b/Ryujinx.Graphics/Gal/GalIndexFormat.cs new file mode 100644 index 0000000000..71a50cdba8 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalIndexFormat.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalIndexFormat + { + Byte = 0, + Int16 = 1, + Int32 = 2 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureFilter.cs b/Ryujinx.Graphics/Gal/GalTextureFilter.cs new file mode 100644 index 0000000000..8e9669f00f --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalTextureFilter.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalTextureFilter + { + Nearest = 1, + Linear = 2 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureMipFilter.cs b/Ryujinx.Graphics/Gal/GalTextureMipFilter.cs new file mode 100644 index 0000000000..2123ec7d24 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalTextureMipFilter.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalTextureMipFilter + { + None = 1, + Nearest = 2, + Linear = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureSampler.cs b/Ryujinx.Graphics/Gal/GalTextureSampler.cs new file mode 100644 index 0000000000..b9e5c7658d --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalTextureSampler.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureWrap.cs b/Ryujinx.Graphics/Gal/GalTextureWrap.cs new file mode 100644 index 0000000000..66e5315409 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalTextureWrap.cs @@ -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 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 2503b8e6cb..99534672d1 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace Ryujinx.Graphics.Gal { @@ -21,23 +22,56 @@ namespace Ryujinx.Graphics.Gal float OffsY, float Rotate); + //Blend + void SetBlendEnable(bool Enable); + + void SetBlend( + GalBlendEquation Equation, + GalBlendFactor FuncSrc, + GalBlendFactor FuncDst); + + 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 RenderVertexArray(int VbIndex); + 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); - void SetShaderConstBuffer(long Tag, int Cbuf, byte[] Data); + IEnumerable 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 UpdateTextures(Func RequestTextureCallback); + void SetTexture(int Index, GalTexture Tex); + + void SetSampler(int Index, GalTextureSampler Sampler); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs new file mode 100644 index 0000000000..97ff8e13b4 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs @@ -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)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 3a450606eb..6518de5fbf 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -5,6 +5,42 @@ 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) @@ -30,5 +66,64 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs new file mode 100644 index 0000000000..f9c42ae014 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index 235bbc6874..9e0d45233d 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //? }; - private struct VertexBuffer + private struct VbInfo { public int VaoHandle; public int VboHandle; @@ -52,11 +52,23 @@ namespace Ryujinx.Graphics.Gal.OpenGL public int PrimCount; } - private VertexBuffer[] VertexBuffers; + private struct IbInfo + { + public int IboHandle; + public int Count; + + public DrawElementsType Type; + } + + private VbInfo[] VertexBuffers; + + private IbInfo IndexBuffer; public OGLRasterizer() { - VertexBuffers = new VertexBuffer[32]; + VertexBuffers = new VbInfo[32]; + + IndexBuffer = new IbInfo(); } public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) @@ -92,7 +104,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL VertexBuffers[VbIndex].PrimCount = Buffer.Length / Stride; - VertexBuffer Vb = VertexBuffers[VbIndex]; + VbInfo Vb = VertexBuffers[VbIndex]; IntPtr Length = new IntPtr(Buffer.Length); @@ -144,9 +156,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BindVertexArray(0); } - public void RenderVertexArray(int VbIndex) + public void SetIndexArray(byte[] Buffer, GalIndexFormat Format) { - VertexBuffer Vb = VertexBuffers[VbIndex]; + 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) { @@ -155,12 +182,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BindVertexArray(Vb.VaoHandle); - GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount); + 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) { - VertexBuffer Vb = VertexBuffers[VbIndex]; + VbInfo Vb = VertexBuffers[VbIndex]; if (Vb.VaoHandle == 0) { @@ -174,5 +219,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL VertexBuffers[VbIndex] = Vb; } + + private void EnsureIbInitialized() + { + if (IndexBuffer.IboHandle == 0) + { + IndexBuffer.IboHandle = GL.GenBuffer(); + } + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 571aa2074f..6d6ac555d6 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -1,6 +1,7 @@ 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; @@ -13,23 +14,37 @@ namespace Ryujinx.Graphics.Gal.OpenGL { 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 TextureUsage { get; private set; } public IEnumerable UniformUsage { get; private set; } public ShaderStage( GalShaderType Type, + string Code, IEnumerable TextureUsage, IEnumerable UniformUsage) { - Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); - 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); @@ -57,7 +72,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL private ShaderProgram Current; - private Dictionary Stages; + private ConcurrentDictionary Stages; private Dictionary Programs; @@ -65,49 +80,62 @@ namespace Ryujinx.Graphics.Gal.OpenGL public OGLShader() { - Stages = new Dictionary(); + Stages = new ConcurrentDictionary(); Programs = new Dictionary(); } public void Create(long Tag, GalShaderType Type, byte[] Data) { - if (!Stages.ContainsKey(Tag)) - { - GlslProgram Program = GetGlslProgram(Data, Type); - - ShaderStage Stage = new ShaderStage( - Type, - Program.Textures, - Program.Uniforms); - - Stages.Add(Tag, Stage); - - CompileAndCheck(Stage.Handle, Program.Code); - } + Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Type, Data)); } - public IEnumerable GetTextureUsage(GalShaderType ShaderType) + private ShaderStage ShaderStageFactory(GalShaderType Type, byte[] Data) { - switch (ShaderType) - { - case GalShaderType.Vertex: return GetTextureUsage(Current.Vertex); - case GalShaderType.TessControl: return GetTextureUsage(Current.TessControl); - case GalShaderType.TessEvaluation: return GetTextureUsage(Current.TessEvaluation); - case GalShaderType.Geometry: return GetTextureUsage(Current.Geometry); - case GalShaderType.Fragment: return GetTextureUsage(Current.Fragment); - } + GlslProgram Program = GetGlslProgram(Data, Type); - return null; + return new ShaderStage( + Type, + Program.Code, + Program.Textures, + Program.Uniforms); } - private IEnumerable GetTextureUsage(ShaderStage Stage) + private GlslProgram GetGlslProgram(byte[] Data, GalShaderType Type) { - return Stage?.TextureUsage ?? Enumerable.Empty(); + 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 GetTextureUsage(long Tag) + { + if (Stages.TryGetValue(Tag, out ShaderStage Stage)) + { + return Stage.TextureUsage; + } + + return Enumerable.Empty(); } 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)) @@ -121,18 +149,32 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + 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)) { - 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; - } + 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; } } @@ -170,32 +212,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL { if (Stage != null) { + Stage.Compile(); + GL.AttachShader(ProgramHandle, Stage.Handle); } } - 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); - } - - private static void CompileAndCheck(int Handle, string Code) + public static void CompileAndCheck(int Handle, string Code) { GL.ShaderSource(Handle, Code); GL.CompileShader(Handle); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index b8a5963995..559e0eda74 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -1,72 +1,34 @@ using OpenTK.Graphics.OpenGL; -using System; namespace Ryujinx.Graphics.Gal.OpenGL { class OGLTexture { - private OGLShader Shader; - private int[] Textures; - private int CurrentTextureIndex; - - public OGLTexture(OGLShader Shader) + public OGLTexture() { - this.Shader = Shader; - Textures = new int[80]; } - public void UpdateTextures(Func RequestTextureCallback) + public void Set(int Index, GalTexture Tex) { - CurrentTextureIndex = 0; + GL.ActiveTexture(TextureUnit.Texture0 + Index); - UpdateTextures(RequestTextureCallback, GalShaderType.Vertex); - UpdateTextures(RequestTextureCallback, GalShaderType.TessControl); - UpdateTextures(RequestTextureCallback, GalShaderType.TessEvaluation); - UpdateTextures(RequestTextureCallback, GalShaderType.Geometry); - UpdateTextures(RequestTextureCallback, GalShaderType.Fragment); - } - - private void UpdateTextures(Func RequestTextureCallback, GalShaderType ShaderType) - { - foreach (ShaderDeclInfo DeclInfo in Shader.GetTextureUsage(ShaderType)) - { - GalTexture Texture = RequestTextureCallback(DeclInfo.Index, ShaderType); - - GL.ActiveTexture(TextureUnit.Texture0 + CurrentTextureIndex); - - UploadTexture(Texture); - - int Location = GL.GetUniformLocation(Shader.CurrentProgramHandle, DeclInfo.Name); - - GL.Uniform1(Location, CurrentTextureIndex++); - } - } - - private void UploadTexture(GalTexture Texture) - { - int Handle = EnsureTextureInitialized(CurrentTextureIndex); + int Handle = EnsureTextureInitialized(Index); GL.BindTexture(TextureTarget.Texture2D, Handle); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); + int W = Tex.Width; + int H = Tex.Height; - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - - int W = Texture.Width; - int H = Texture.Height; - - byte[] Data = Texture.Data; + byte[] Data = Tex.Data; int Length = Data.Length; - if (IsCompressedTextureFormat(Texture.Format)) + if (IsCompressedTextureFormat(Tex.Format)) { - PixelInternalFormat Pif = OGLEnumConverter.GetCompressedTextureFormat(Texture.Format); + PixelInternalFormat Pif = OGLEnumConverter.GetCompressedTextureFormat(Tex.Format); GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Length, Data); } @@ -83,6 +45,35 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + 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 || @@ -90,13 +81,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL Format == GalTextureFormat.BC3; } - private int EnsureTextureInitialized(int TextureIndex) + private int EnsureTextureInitialized(int TexIndex) { - int Handle = Textures[TextureIndex]; + int Handle = Textures[TexIndex]; if (Handle == 0) { - Handle = Textures[TextureIndex] = GL.GenTexture(); + Handle = Textures[TexIndex] = GL.GenTexture(); } return Handle; diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index 985ef17d19..0b7bf92ac4 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -1,11 +1,16 @@ using OpenTK; using System; using System.Collections.Concurrent; +using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { public class OpenGLRenderer : IGalRenderer { + private OGLBlend Blend; + + private OGLFrameBuffer FrameBuffer; + private OGLRasterizer Rasterizer; private OGLShader Shader; @@ -18,11 +23,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL public OpenGLRenderer() { + Blend = new OGLBlend(); + + FrameBuffer = new OGLFrameBuffer(); + Rasterizer = new OGLRasterizer(); Shader = new OGLShader(); - Texture = new OGLTexture(Shader); + Texture = new OGLTexture(); ActionsQueue = new ConcurrentQueue(); } @@ -83,6 +92,61 @@ namespace Ryujinx.Graphics.Gal.OpenGL FbRenderer.Set(Fb, Width, Height, Transform, Offs); } + public void SetBlendEnable(bool Enable) + { + if (Enable) + { + ActionsQueue.Enqueue(() => Blend.Enable()); + } + else + { + ActionsQueue.Enqueue(() => Blend.Disable()); + } + } + + public void SetBlend( + GalBlendEquation Equation, + GalBlendFactor FuncSrc, + GalBlendFactor FuncDst) + { + ActionsQueue.Enqueue(() => Blend.Set(Equation, FuncSrc, FuncDst)); + } + + public void SetBlendSeparate( + GalBlendEquation EquationRgb, + GalBlendEquation EquationAlpha, + GalBlendFactor FuncSrcRgb, + GalBlendFactor FuncDstRgb, + GalBlendFactor FuncSrcAlpha, + GalBlendFactor FuncDstAlpha) + { + ActionsQueue.Enqueue(() => + { + Blend.SetSeparate( + EquationRgb, + EquationAlpha, + FuncSrcRgb, + FuncDstRgb, + FuncSrcAlpha, + FuncDstAlpha); + }); + } + + public void SetFb(int FbIndex, int Width, int Height) + { + ActionsQueue.Enqueue(() => FrameBuffer.Set(FbIndex, Width, Height)); + } + + public void BindFrameBuffer(int FbIndex) + { + ActionsQueue.Enqueue(() => FrameBuffer.Bind(FbIndex)); + } + + public void DrawFrameBuffer(int FbIndex) + { + ActionsQueue.Enqueue(() => FrameBuffer.Draw(FbIndex)); + } + public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) { ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags)); @@ -100,14 +164,34 @@ namespace Ryujinx.Graphics.Gal.OpenGL Attribs ?? throw new ArgumentNullException(nameof(Attribs)))); } - public void RenderVertexArray(int VbIndex) + 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.RenderVertexArray(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) @@ -117,10 +201,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ArgumentNullException(nameof(Data)); } - ActionsQueue.Enqueue(() => Shader.Create(Tag, Type, Data)); + Shader.Create(Tag, Type, Data); } - public void SetShaderConstBuffer(long Tag, int Cbuf, byte[] Data) + public void SetConstBuffer(long Tag, int Cbuf, byte[] Data) { if (Data == null) { @@ -130,6 +214,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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 GetTextureUsage(long Tag) + { + return Shader.GetTextureUsage(Tag); + } + public void BindShader(long Tag) { ActionsQueue.Enqueue(() => Shader.Bind(Tag)); @@ -140,9 +239,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue.Enqueue(() => Shader.BindProgram()); } - public void UpdateTextures(Func RequestTextureCallback) + public void SetTexture(int Index, GalTexture Tex) { - ActionsQueue.Enqueue(() => Texture.UpdateTextures(RequestTextureCallback)); + ActionsQueue.Enqueue(() => Texture.Set(Index, Tex)); + } + + public void SetSampler(int Index, GalTextureSampler Sampler) + { + ActionsQueue.Enqueue(() => Texture.Set(Index, Sampler)); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 3426c0a366..edd28fa8dd 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -152,7 +152,7 @@ namespace Ryujinx.Graphics.Gal.Shader } else if (DeclInfo.Name == GlslDecl.FragmentOutputName) { - Name = "out " + GetDecl(DeclInfo) + ";"; + Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";"; } else { diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs index de009fdca2..f856e9fa37 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs @@ -22,6 +22,8 @@ namespace Ryujinx.Graphics.Gpu private ConstBuffer[] Cbs; + private bool HasDataToRender; + public NvGpuEngine3d(NsGpu Gpu) { this.Gpu = Gpu; @@ -61,34 +63,60 @@ namespace Ryujinx.Graphics.Gpu } } + private const long GoodFbAddress = 0x1615b2a00; + private void VertexEndGl(AMemory Memory, NsGpuPBEntry PBEntry) { - int TexCbuf = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); + SetFrameBuffer(0); - int TexHandle = ReadCb(Memory, TexCbuf, 0x20); - - UploadShaders(Memory); + long[] Tags = UploadShaders(Memory); Gpu.Renderer.BindProgram(); - UploadTextures(Memory); + 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 Rt = (Arg0 >> 6) & 0xf; + int FbIndex = (Arg0 >> 6) & 0xf; + + int Layer = (Arg0 >> 10) & 0x3ff; GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); - Gpu.Renderer.ClearBuffers(Rt, Flags); + SetFrameBuffer(0); + + Gpu.Renderer.ClearBuffers(Layer, Flags); } - private void UploadShaders(AMemory Memory) + 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++) @@ -115,9 +143,108 @@ namespace Ryujinx.Graphics.Gpu 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) @@ -147,7 +274,7 @@ namespace Ryujinx.Graphics.Gpu byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size); - Gpu.Renderer.SetShaderConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); + Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); } } } @@ -155,6 +282,32 @@ namespace Ryujinx.Graphics.Gpu 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[] Attribs = new List[32]; for (int Attr = 0; Attr < 16; Attr++) @@ -187,83 +340,36 @@ namespace Ryujinx.Graphics.Gpu continue; } - long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); - long EndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 4); + long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); + long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 4); - long Size = (EndPos - Position) + 1; + long Size = (VertexEndPos - VertexPosition) + 1; int Stride = Control & 0xfff; - Position = Gpu.GetCpuAddr(Position); + VertexPosition = Gpu.GetCpuAddr(VertexPosition); - byte[] Data = AMemoryHelper.ReadBytes(Memory, Position, Size); + byte[] Data = AMemoryHelper.ReadBytes(Memory, VertexPosition, Size); GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0]; Gpu.Renderer.SetVertexArray(Index, Stride, Data, AttribArray); - Gpu.Renderer.RenderVertexArray(Index); + + 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 UploadTextures(AMemory Memory) - { - int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); - - long BasePosition = Cbs[TextureCbIndex].Position; - - long Size = (uint)Cbs[TextureCbIndex].Size; - - Gpu.Renderer.UpdateTextures((int Index, GalShaderType ShaderType) => - { - long Position = BasePosition + (int)ShaderType * Size; - - return TextureRequestHandler(Memory, Position, Index); - }); - } - - private GalTexture TextureRequestHandler(AMemory Memory, long BasePosition, int Index) - { - long Position = BasePosition + Index * 4; - - int TextureHandle = Memory.ReadInt32(Position); - - int TicIndex = (TextureHandle >> 0) & 0xfffff; - int TscIndex = (TextureHandle >> 20) & 0xfff; - - TryGetCpuAddr(NvGpuEngine3dReg.TexHeaderPoolOffset, out long TicPosition); - - TicPosition += TicIndex * 0x20; - - return TextureFactory.MakeTexture(Gpu, Memory, TicPosition); - } - - private 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; - } - - 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 QueryControl(AMemory Memory, NsGpuPBEntry PBEntry) { if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddress, out long Position)) diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs index db7496e0e7..4bba9abe0d 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs @@ -2,23 +2,43 @@ namespace Ryujinx.Graphics.Gpu { enum NvGpuEngine3dReg { - VertexAttribNFormat = 0x458, - TexHeaderPoolOffset = 0x55d, - ShaderAddress = 0x582, - 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 + 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 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/TextureFactory.cs b/Ryujinx.Graphics/Gpu/TextureFactory.cs index 2f42772070..0a0497f3fd 100644 --- a/Ryujinx.Graphics/Gpu/TextureFactory.cs +++ b/Ryujinx.Graphics/Gpu/TextureFactory.cs @@ -1,5 +1,6 @@ using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; +using System; namespace Ryujinx.Graphics.Gpu { @@ -39,6 +40,34 @@ namespace Ryujinx.Graphics.Gpu 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];