From 5011640b3086b86b0f0b39b60fdb2aa946d4f5c8 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 23 May 2020 06:46:09 -0300 Subject: Spanify Graphics Abstraction Layer (#1226) * Spanify Graphics Abstraction Layer * Be explicit about BufferHandle size --- Ryujinx.Graphics.GAL/BufferHandle.cs | 22 ++ Ryujinx.Graphics.GAL/BufferRange.cs | 8 +- Ryujinx.Graphics.GAL/IBuffer.cs | 15 - Ryujinx.Graphics.GAL/IPipeline.cs | 11 +- Ryujinx.Graphics.GAL/IRenderer.cs | 8 +- Ryujinx.Graphics.Gpu/Constants.cs | 5 + Ryujinx.Graphics.Gpu/Engine/Methods.cs | 8 +- Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 16 +- Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 7 +- Ryujinx.Graphics.OpenGL/Buffer.cs | 43 +- Ryujinx.Graphics.OpenGL/Constants.cs | 10 + Ryujinx.Graphics.OpenGL/Framebuffer.cs | 1 + Ryujinx.Graphics.OpenGL/Handle.cs | 23 ++ Ryujinx.Graphics.OpenGL/Image/Sampler.cs | 59 +++ Ryujinx.Graphics.OpenGL/Image/TextureBase.cs | 36 ++ Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs | 71 ++++ Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs | 150 +++++++ .../Image/TextureCopyUnscaled.cs | 93 +++++ Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs | 178 +++++++++ Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 433 +++++++++++++++++++++ Ryujinx.Graphics.OpenGL/Pipeline.cs | 51 +-- Ryujinx.Graphics.OpenGL/Renderer.cs | 20 +- Ryujinx.Graphics.OpenGL/Sampler.cs | 59 --- Ryujinx.Graphics.OpenGL/TextureBase.cs | 39 -- Ryujinx.Graphics.OpenGL/TextureBuffer.cs | 71 ---- Ryujinx.Graphics.OpenGL/TextureCopy.cs | 150 ------- Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs | 93 ----- Ryujinx.Graphics.OpenGL/TextureStorage.cs | 178 --------- Ryujinx.Graphics.OpenGL/TextureView.cs | 433 --------------------- Ryujinx.Graphics.OpenGL/VertexArray.cs | 64 +-- Ryujinx.Graphics.OpenGL/Window.cs | 1 + 31 files changed, 1215 insertions(+), 1141 deletions(-) create mode 100644 Ryujinx.Graphics.GAL/BufferHandle.cs delete mode 100644 Ryujinx.Graphics.GAL/IBuffer.cs create mode 100644 Ryujinx.Graphics.OpenGL/Constants.cs create mode 100644 Ryujinx.Graphics.OpenGL/Handle.cs create mode 100644 Ryujinx.Graphics.OpenGL/Image/Sampler.cs create mode 100644 Ryujinx.Graphics.OpenGL/Image/TextureBase.cs create mode 100644 Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs create mode 100644 Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs create mode 100644 Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs create mode 100644 Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs create mode 100644 Ryujinx.Graphics.OpenGL/Image/TextureView.cs delete mode 100644 Ryujinx.Graphics.OpenGL/Sampler.cs delete mode 100644 Ryujinx.Graphics.OpenGL/TextureBase.cs delete mode 100644 Ryujinx.Graphics.OpenGL/TextureBuffer.cs delete mode 100644 Ryujinx.Graphics.OpenGL/TextureCopy.cs delete mode 100644 Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs delete mode 100644 Ryujinx.Graphics.OpenGL/TextureStorage.cs delete mode 100644 Ryujinx.Graphics.OpenGL/TextureView.cs diff --git a/Ryujinx.Graphics.GAL/BufferHandle.cs b/Ryujinx.Graphics.GAL/BufferHandle.cs new file mode 100644 index 00000000..49f83442 --- /dev/null +++ b/Ryujinx.Graphics.GAL/BufferHandle.cs @@ -0,0 +1,22 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.GAL +{ + [StructLayout(LayoutKind.Sequential, Size = 8)] + public struct BufferHandle : IEquatable + { + private readonly ulong _value; + + public static BufferHandle Null => new BufferHandle(0); + + private BufferHandle(ulong value) => _value = value; + + public override bool Equals(object obj) => obj is BufferHandle handle && Equals(handle); + public bool Equals([AllowNull] BufferHandle other) => other._value == _value; + public override int GetHashCode() => _value.GetHashCode(); + public static bool operator ==(BufferHandle left, BufferHandle right) => left.Equals(right); + public static bool operator !=(BufferHandle left, BufferHandle right) => !(left == right); + } +} diff --git a/Ryujinx.Graphics.GAL/BufferRange.cs b/Ryujinx.Graphics.GAL/BufferRange.cs index a35636aa..34d523f9 100644 --- a/Ryujinx.Graphics.GAL/BufferRange.cs +++ b/Ryujinx.Graphics.GAL/BufferRange.cs @@ -2,18 +2,18 @@ namespace Ryujinx.Graphics.GAL { public struct BufferRange { - private static BufferRange _empty = new BufferRange(null, 0, 0); + private static readonly BufferRange _empty = new BufferRange(BufferHandle.Null, 0, 0); public BufferRange Empty => _empty; - public IBuffer Buffer { get; } + public BufferHandle Handle { get; } public int Offset { get; } public int Size { get; } - public BufferRange(IBuffer buffer, int offset, int size) + public BufferRange(BufferHandle handle, int offset, int size) { - Buffer = buffer; + Handle = handle; Offset = offset; Size = size; } diff --git a/Ryujinx.Graphics.GAL/IBuffer.cs b/Ryujinx.Graphics.GAL/IBuffer.cs deleted file mode 100644 index 43e37691..00000000 --- a/Ryujinx.Graphics.GAL/IBuffer.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.GAL -{ - public interface IBuffer : IDisposable - { - void CopyTo(IBuffer destination, int srcOffset, int dstOffset, int size); - - byte[] GetData(int offset, int size); - - void SetData(ReadOnlySpan data); - - void SetData(int offset, ReadOnlySpan data); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 3bf7ab93..22e4e9e2 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader; +using System; namespace Ryujinx.Graphics.GAL { @@ -14,6 +15,8 @@ namespace Ryujinx.Graphics.GAL int stencilValue, int stencilMask); + void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size); + void DispatchCompute(int groupsX, int groupsY, int groupsZ); void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance); @@ -49,7 +52,7 @@ namespace Ryujinx.Graphics.GAL void SetRasterizerDiscard(bool discard); - void SetRenderTargetColorMasks(uint[] componentMask); + void SetRenderTargetColorMasks(ReadOnlySpan componentMask); void SetRenderTargets(ITexture[] colors, ITexture depthStencil); @@ -68,10 +71,10 @@ namespace Ryujinx.Graphics.GAL void SetUserClipDistance(int index, bool enableClip); - void SetVertexAttribs(VertexAttribDescriptor[] vertexAttribs); - void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers); + void SetVertexAttribs(ReadOnlySpan vertexAttribs); + void SetVertexBuffers(ReadOnlySpan vertexBuffers); - void SetViewports(int first, Viewport[] viewports); + void SetViewports(int first, ReadOnlySpan viewports); void TextureBarrier(); void TextureBarrierTiled(); diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs index 4a45f5cb..c41b19fe 100644 --- a/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/Ryujinx.Graphics.GAL/IRenderer.cs @@ -11,15 +11,21 @@ namespace Ryujinx.Graphics.GAL IShader CompileShader(ShaderProgram shader); - IBuffer CreateBuffer(int size); + BufferHandle CreateBuffer(int size); IProgram CreateProgram(IShader[] shaders); ISampler CreateSampler(SamplerCreateInfo info); ITexture CreateTexture(TextureCreateInfo info); + void DeleteBuffer(BufferHandle buffer); + + byte[] GetBufferData(BufferHandle buffer, int offset, int size); + Capabilities GetCapabilities(); + void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data); + void UpdateCounters(); ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler); diff --git a/Ryujinx.Graphics.Gpu/Constants.cs b/Ryujinx.Graphics.Gpu/Constants.cs index 1ae90273..ac6b6139 100644 --- a/Ryujinx.Graphics.Gpu/Constants.cs +++ b/Ryujinx.Graphics.Gpu/Constants.cs @@ -45,6 +45,11 @@ namespace Ryujinx.Graphics.Gpu /// public const int ShaderStages = 5; + /// + /// Maximum number of vertex attributes. + /// + public const int TotalVertexAttribs = 16; + /// /// Maximum number of vertex buffers. /// diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index 5ead87a0..acb5ad5c 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -570,9 +570,9 @@ namespace Ryujinx.Graphics.Gpu.Engine /// Current GPU state private void UpdateVertexAttribState(GpuState state) { - VertexAttribDescriptor[] vertexAttribs = new VertexAttribDescriptor[16]; + Span vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs]; - for (int index = 0; index < 16; index++) + for (int index = 0; index < Constants.TotalVertexAttribs; index++) { var vertexAttrib = state.Get(MethodOffset.VertexAttribState, index); @@ -660,7 +660,7 @@ namespace Ryujinx.Graphics.Gpu.Engine { _isAnyVbInstanced = false; - for (int index = 0; index < 16; index++) + for (int index = 0; index < Constants.TotalVertexBuffers; index++) { var vertexBuffer = state.Get(MethodOffset.VertexBufferState, index); @@ -728,7 +728,7 @@ namespace Ryujinx.Graphics.Gpu.Engine { bool rtColorMaskShared = state.Get(MethodOffset.RtColorMaskShared); - uint[] componentMasks = new uint[Constants.TotalRenderTargets]; + Span componentMasks = stackalloc uint[Constants.TotalRenderTargets]; for (int index = 0; index < Constants.TotalRenderTargets; index++) { diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index 4dd96878..5fe85d2e 100644 --- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -11,9 +11,9 @@ namespace Ryujinx.Graphics.Gpu.Memory private readonly GpuContext _context; /// - /// Host buffer object. + /// Host buffer handle. /// - public IBuffer HostBuffer { get; } + public BufferHandle Handle { get; } /// /// Start address of the buffer in guest memory. @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Gpu.Memory Address = address; Size = size; - HostBuffer = context.Renderer.CreateBuffer((int)size); + Handle = context.Renderer.CreateBuffer((int)size); _modifiedRanges = new (ulong, ulong)[size / PhysicalMemory.PageSize]; @@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { int offset = (int)(address - Address); - return new BufferRange(HostBuffer, offset, (int)size); + return new BufferRange(Handle, offset, (int)size); } /// @@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Gpu.Memory int offset = (int)(mAddress - Address); - HostBuffer.SetData(offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize)); + _context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize)); } } @@ -136,7 +136,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The offset of the destination buffer to copy into public void CopyTo(Buffer destination, int dstOffset) { - HostBuffer.CopyTo(destination.HostBuffer, 0, dstOffset, (int)Size); + _context.Renderer.Pipeline.CopyBuffer(Handle, destination.Handle, 0, dstOffset, (int)Size); } /// @@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { int offset = (int)(address - Address); - byte[] data = HostBuffer.GetData(offset, (int)size); + byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size); _context.PhysicalMemory.Write(address, data); } @@ -159,7 +159,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public void Dispose() { - HostBuffer.Dispose(); + _context.Renderer.DeleteBuffer(Handle); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 2fe0ecbb..39d1cd6f 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -477,7 +477,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { _vertexBuffersDirty = false; - VertexBufferDescriptor[] vertexBuffers = new VertexBufferDescriptor[Constants.TotalVertexBuffers]; + Span vertexBuffers = stackalloc VertexBufferDescriptor[Constants.TotalVertexBuffers]; for (int index = 0; (vbEnableMask >> index) != 0; index++) { @@ -666,8 +666,9 @@ namespace Ryujinx.Graphics.Gpu.Memory int srcOffset = (int)(srcAddress - srcBuffer.Address); int dstOffset = (int)(dstAddress - dstBuffer.Address); - srcBuffer.HostBuffer.CopyTo( - dstBuffer.HostBuffer, + _context.Renderer.Pipeline.CopyBuffer( + srcBuffer.Handle, + dstBuffer.Handle, srcOffset, dstOffset, (int)size); diff --git a/Ryujinx.Graphics.OpenGL/Buffer.cs b/Ryujinx.Graphics.OpenGL/Buffer.cs index db3e94ba..e8fd9a6b 100644 --- a/Ryujinx.Graphics.OpenGL/Buffer.cs +++ b/Ryujinx.Graphics.OpenGL/Buffer.cs @@ -4,22 +4,22 @@ using System; namespace Ryujinx.Graphics.OpenGL { - class Buffer : IBuffer + static class Buffer { - public int Handle { get; } - - public Buffer(int size) + public static BufferHandle Create(int size) { - Handle = GL.GenBuffer(); + int handle = GL.GenBuffer(); - GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle); GL.BufferData(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero, BufferUsageHint.DynamicDraw); + + return Handle.FromInt32(handle); } - public void CopyTo(IBuffer destination, int srcOffset, int dstOffset, int size) + public static void Copy(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) { - GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle); - GL.BindBuffer(BufferTarget.CopyWriteBuffer, ((Buffer)destination).Handle); + GL.BindBuffer(BufferTarget.CopyReadBuffer, source.ToInt32()); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, destination.ToInt32()); GL.CopyBufferSubData( BufferTarget.CopyReadBuffer, @@ -29,9 +29,9 @@ namespace Ryujinx.Graphics.OpenGL (IntPtr)size); } - public byte[] GetData(int offset, int size) + public static byte[] GetData(BufferHandle buffer, int offset, int size) { - GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle); + GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer.ToInt32()); byte[] data = new byte[size]; @@ -40,22 +40,9 @@ namespace Ryujinx.Graphics.OpenGL return data; } - public void SetData(ReadOnlySpan data) - { - unsafe - { - GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle); - - fixed (byte* ptr = data) - { - GL.BufferData(BufferTarget.CopyWriteBuffer, data.Length, (IntPtr)ptr, BufferUsageHint.DynamicDraw); - } - } - } - - public void SetData(int offset, ReadOnlySpan data) + public static void SetData(BufferHandle buffer, int offset, ReadOnlySpan data) { - GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, buffer.ToInt32()); unsafe { @@ -66,9 +53,9 @@ namespace Ryujinx.Graphics.OpenGL } } - public void Dispose() + public static void Delete(BufferHandle buffer) { - GL.DeleteBuffer(Handle); + GL.DeleteBuffer(buffer.ToInt32()); } } } diff --git a/Ryujinx.Graphics.OpenGL/Constants.cs b/Ryujinx.Graphics.OpenGL/Constants.cs new file mode 100644 index 00000000..9775b240 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Constants.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.OpenGL +{ + static class Constants + { + public const int MaxRenderTargets = 8; + public const int MaxViewports = 16; + public const int MaxVertexAttribs = 16; + public const int MaxVertexBuffers = 16; + } +} diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs index 23f015b1..e66dcaca 100644 --- a/Ryujinx.Graphics.OpenGL/Framebuffer.cs +++ b/Ryujinx.Graphics.OpenGL/Framebuffer.cs @@ -1,5 +1,6 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.OpenGL.Image; using System; namespace Ryujinx.Graphics.OpenGL diff --git a/Ryujinx.Graphics.OpenGL/Handle.cs b/Ryujinx.Graphics.OpenGL/Handle.cs new file mode 100644 index 00000000..4b2f05e6 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Handle.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Graphics.OpenGL +{ + static class Handle + { + public static T FromInt32(int handle) where T : unmanaged + { + Debug.Assert(Unsafe.SizeOf() == sizeof(ulong)); + + ulong handle64 = (uint)handle; + + return Unsafe.As(ref handle64); + } + + public static int ToInt32(this BufferHandle handle) + { + return (int)Unsafe.As(ref handle); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Image/Sampler.cs b/Ryujinx.Graphics.OpenGL/Image/Sampler.cs new file mode 100644 index 00000000..e13f0da3 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/Sampler.cs @@ -0,0 +1,59 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class Sampler : ISampler + { + public int Handle { get; private set; } + + public Sampler(SamplerCreateInfo info) + { + Handle = GL.GenSampler(); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureMinFilter, (int)info.MinFilter.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureMagFilter, (int)info.MagFilter.Convert()); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapS, (int)info.AddressU.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapT, (int)info.AddressV.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapR, (int)info.AddressP.Convert()); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureCompareMode, (int)info.CompareMode.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureCompareFunc, (int)info.CompareOp.Convert()); + + unsafe + { + float* borderColor = stackalloc float[4] + { + info.BorderColor.Red, + info.BorderColor.Green, + info.BorderColor.Blue, + info.BorderColor.Alpha + }; + + GL.SamplerParameter(Handle, SamplerParameterName.TextureBorderColor, borderColor); + } + + GL.SamplerParameter(Handle, SamplerParameterName.TextureMinLod, info.MinLod); + GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxLod, info.MaxLod); + GL.SamplerParameter(Handle, SamplerParameterName.TextureLodBias, info.MipLodBias); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxAnisotropyExt, info.MaxAnisotropy); + } + + public void Bind(int unit) + { + GL.BindSampler(unit, Handle); + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteSampler(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs new file mode 100644 index 00000000..a4209ea1 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs @@ -0,0 +1,36 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class TextureBase + { + public int Handle { get; protected set; } + + protected TextureCreateInfo Info { get; } + + public int Width => Info.Width; + public int Height => Info.Height; + + public Target Target => Info.Target; + public Format Format => Info.Format; + + public TextureBase(TextureCreateInfo info) + { + Info = info; + + Handle = GL.GenTexture(); + } + + public void Bind(int unit) + { + Bind(Target.Convert(), unit); + } + + protected void Bind(TextureTarget target, int unit) + { + GL.ActiveTexture(TextureUnit.Texture0 + unit); + GL.BindTexture(target, Handle); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs new file mode 100644 index 00000000..2c69571c --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -0,0 +1,71 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class TextureBuffer : TextureBase, ITexture + { + private int _bufferOffset; + private int _bufferSize; + + private BufferHandle _buffer; + + public TextureBuffer(TextureCreateInfo info) : base(info) {} + + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + throw new NotSupportedException(); + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + throw new NotSupportedException(); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + throw new NotSupportedException(); + } + + public byte[] GetData() + { + return Buffer.GetData(_buffer, _bufferOffset, _bufferSize); + } + + public void SetData(ReadOnlySpan data) + { + Buffer.SetData(_buffer, _bufferOffset, data.Slice(0, Math.Min(data.Length, _bufferSize))); + } + + public void SetStorage(BufferRange buffer) + { + if (buffer.Handle == _buffer && + buffer.Offset == _bufferOffset && + buffer.Size == _bufferSize) + { + return; + } + + _buffer = buffer.Handle; + _bufferOffset = buffer.Offset; + _bufferSize = buffer.Size; + + Bind(0); + + SizedInternalFormat format = (SizedInternalFormat)FormatTable.GetFormatInfo(Info.Format).PixelInternalFormat; + + GL.TexBufferRange(TextureBufferTarget.TextureBuffer, format, _buffer.ToInt32(), (IntPtr)buffer.Offset, buffer.Size); + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteTexture(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs new file mode 100644 index 00000000..1cef61a9 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -0,0 +1,150 @@ +using Ryujinx.Graphics.GAL; +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class TextureCopy : IDisposable + { + private readonly Renderer _renderer; + + private int _srcFramebuffer; + private int _dstFramebuffer; + + public TextureCopy(Renderer renderer) + { + _renderer = renderer; + } + + public void Copy( + TextureView src, + TextureView dst, + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter) + { + int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); + int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); + + Attach(FramebufferTarget.ReadFramebuffer, src.Format, src.Handle); + Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle); + + ClearBufferMask mask = GetMask(src.Format); + + BlitFramebufferFilter filter = linearFilter + ? BlitFramebufferFilter.Linear + : BlitFramebufferFilter.Nearest; + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + + GL.Disable(EnableCap.RasterizerDiscard); + GL.Disable(IndexedEnableCap.ScissorTest, 0); + + GL.BlitFramebuffer( + srcRegion.X1, + srcRegion.Y1, + srcRegion.X2, + srcRegion.Y2, + dstRegion.X1, + dstRegion.Y1, + dstRegion.X2, + dstRegion.Y2, + mask, + filter); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + + ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); + ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); + } + + private static void Attach(FramebufferTarget target, Format format, int handle) + { + if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) + { + GL.FramebufferTexture(target, FramebufferAttachment.DepthStencilAttachment, handle, 0); + } + else if (IsDepthOnly(format)) + { + GL.FramebufferTexture(target, FramebufferAttachment.DepthAttachment, handle, 0); + } + else if (format == Format.S8Uint) + { + GL.FramebufferTexture(target, FramebufferAttachment.StencilAttachment, handle, 0); + } + else + { + GL.FramebufferTexture(target, FramebufferAttachment.ColorAttachment0, handle, 0); + } + } + + private static ClearBufferMask GetMask(Format format) + { + if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) + { + return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit; + } + else if (IsDepthOnly(format)) + { + return ClearBufferMask.DepthBufferBit; + } + else if (format == Format.S8Uint) + { + return ClearBufferMask.StencilBufferBit; + } + else + { + return ClearBufferMask.ColorBufferBit; + } + } + + private static bool IsDepthOnly(Format format) + { + return format == Format.D16Unorm || + format == Format.D24X8Unorm || + format == Format.D32Float; + } + + private int GetSrcFramebufferLazy() + { + if (_srcFramebuffer == 0) + { + _srcFramebuffer = GL.GenFramebuffer(); + } + + return _srcFramebuffer; + } + + private int GetDstFramebufferLazy() + { + if (_dstFramebuffer == 0) + { + _dstFramebuffer = GL.GenFramebuffer(); + } + + return _dstFramebuffer; + } + + public void Dispose() + { + if (_srcFramebuffer != 0) + { + GL.DeleteFramebuffer(_srcFramebuffer); + + _srcFramebuffer = 0; + } + + if (_dstFramebuffer != 0) + { + GL.DeleteFramebuffer(_dstFramebuffer); + + _dstFramebuffer = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs new file mode 100644 index 00000000..28401138 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs @@ -0,0 +1,93 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + static class TextureCopyUnscaled + { + public static void Copy( + TextureCreateInfo srcInfo, + TextureCreateInfo dstInfo, + int srcHandle, + int dstHandle, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel) + { + int srcWidth = srcInfo.Width; + int srcHeight = srcInfo.Height; + int srcDepth = srcInfo.GetDepthOrLayers(); + int srcLevels = srcInfo.Levels; + + int dstWidth = dstInfo.Width; + int dstHeight = dstInfo.Height; + int dstDepth = dstInfo.GetDepthOrLayers(); + int dstLevels = dstInfo.Levels; + + dstWidth = Math.Max(1, dstWidth >> dstLevel); + dstHeight = Math.Max(1, dstHeight >> dstLevel); + + if (dstInfo.Target == Target.Texture3D) + { + dstDepth = Math.Max(1, dstDepth >> dstLevel); + } + + // When copying from a compressed to a non-compressed format, + // the non-compressed texture will have the size of the texture + // in blocks (not in texels), so we must adjust that size to + // match the size in texels of the compressed texture. + if (!srcInfo.IsCompressed && dstInfo.IsCompressed) + { + dstWidth = BitUtils.DivRoundUp(dstWidth, dstInfo.BlockWidth); + dstHeight = BitUtils.DivRoundUp(dstHeight, dstInfo.BlockHeight); + } + else if (srcInfo.IsCompressed && !dstInfo.IsCompressed) + { + dstWidth *= dstInfo.BlockWidth; + dstHeight *= dstInfo.BlockHeight; + } + + int width = Math.Min(srcWidth, dstWidth); + int height = Math.Min(srcHeight, dstHeight); + int depth = Math.Min(srcDepth, dstDepth); + int levels = Math.Min(srcLevels, dstLevels); + + for (int level = 0; level < levels; level++) + { + // Stop copy if we are already out of the levels range. + if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels) + { + break; + } + + GL.CopyImageSubData( + srcHandle, + srcInfo.Target.ConvertToImageTarget(), + srcLevel + level, + 0, + 0, + srcLayer, + dstHandle, + dstInfo.Target.ConvertToImageTarget(), + dstLevel + level, + 0, + 0, + dstLayer, + width, + height, + depth); + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (srcInfo.Target == Target.Texture3D) + { + depth = Math.Max(1, depth >> 1); + } + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs b/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs new file mode 100644 index 00000000..baf8e65d --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs @@ -0,0 +1,178 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class TextureStorage + { + public int Handle { get; private set; } + + public TextureCreateInfo Info { get; } + + private readonly Renderer _renderer; + + private int _viewsCount; + + public TextureStorage(Renderer renderer, TextureCreateInfo info) + { + _renderer = renderer; + Info = info; + + Handle = GL.GenTexture(); + + CreateImmutableStorage(); + } + + private void CreateImmutableStorage() + { + TextureTarget target = Info.Target.Convert(); + + GL.ActiveTexture(TextureUnit.Texture0); + + GL.BindTexture(target, Handle); + + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + + SizedInternalFormat internalFormat; + + if (format.IsCompressed) + { + internalFormat = (SizedInternalFormat)format.PixelFormat; + } + else + { + internalFormat = (SizedInternalFormat)format.PixelInternalFormat; + } + + switch (Info.Target) + { + case Target.Texture1D: + GL.TexStorage1D( + TextureTarget1d.Texture1D, + Info.Levels, + internalFormat, + Info.Width); + break; + + case Target.Texture1DArray: + GL.TexStorage2D( + TextureTarget2d.Texture1DArray, + Info.Levels, + internalFormat, + Info.Width, + Info.Height); + break; + + case Target.Texture2D: + GL.TexStorage2D( + TextureTarget2d.Texture2D, + Info.Levels, + internalFormat, + Info.Width, + Info.Height); + break; + + case Target.Texture2DArray: + GL.TexStorage3D( + TextureTarget3d.Texture2DArray, + Info.Levels, + internalFormat, + Info.Width, + Info.Height, + Info.Depth); + break; + + case Target.Texture2DMultisample: + GL.TexStorage2DMultisample( + TextureTargetMultisample2d.Texture2DMultisample, + Info.Samples, + internalFormat, + Info.Width, + Info.Height, + true); + break; + + case Target.Texture2DMultisampleArray: + GL.TexStorage3DMultisample( + TextureTargetMultisample3d.Texture2DMultisampleArray, + Info.Samples, + internalFormat, + Info.Width, + Info.Height, + Info.Depth, + true); + break; + + case Target.Texture3D: + GL.TexStorage3D( + TextureTarget3d.Texture3D, + Info.Levels, + internalFormat, + Info.Width, + Info.Height, + Info.Depth); + break; + + case Target.Cubemap: + GL.TexStorage2D( + TextureTarget2d.TextureCubeMap, + Info.Levels, + internalFormat, + Info.Width, + Info.Height); + break; + + case Target.CubemapArray: + GL.TexStorage3D( + (TextureTarget3d)All.TextureCubeMapArray, + Info.Levels, + internalFormat, + Info.Width, + Info.Height, + Info.Depth); + break; + + default: + Logger.PrintDebug(LogClass.Gpu, $"Invalid or unsupported texture target: {target}."); + break; + } + } + + public ITexture CreateDefaultView() + { + return CreateView(Info, 0, 0); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + IncrementViewsCount(); + + return new TextureView(_renderer, this, info, firstLayer, firstLevel); + } + + private void IncrementViewsCount() + { + _viewsCount++; + } + + public void DecrementViewsCount() + { + // If we don't have any views, then the storage is now useless, delete it. + if (--_viewsCount == 0) + { + Dispose(); + } + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteTexture(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs new file mode 100644 index 00000000..0b24a296 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -0,0 +1,433 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class TextureView : TextureBase, ITexture + { + private readonly Renderer _renderer; + + private readonly TextureStorage _parent; + + private TextureView _emulatedViewParent; + + private TextureView _incompatibleFormatView; + + public int FirstLayer { get; private set; } + public int FirstLevel { get; private set; } + + public TextureView( + Renderer renderer, + TextureStorage parent, + TextureCreateInfo info, + int firstLayer, + int firstLevel) : base(info) + { + _renderer = renderer; + _parent = parent; + + FirstLayer = firstLayer; + FirstLevel = firstLevel; + + CreateView(); + } + + private void CreateView() + { + TextureTarget target = Target.Convert(); + + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + + PixelInternalFormat pixelInternalFormat; + + if (format.IsCompressed) + { + pixelInternalFormat = (PixelInternalFormat)format.PixelFormat; + } + else + { + pixelInternalFormat = format.PixelInternalFormat; + } + + GL.TextureView( + Handle, + target, + _parent.Handle, + pixelInternalFormat, + FirstLevel, + Info.Levels, + FirstLayer, + Info.GetLayers()); + + GL.ActiveTexture(TextureUnit.Texture0); + + GL.BindTexture(target, Handle); + + int[] swizzleRgba = new int[] + { + (int)Info.SwizzleR.Convert(), + (int)Info.SwizzleG.Convert(), + (int)Info.SwizzleB.Convert(), + (int)Info.SwizzleA.Convert() + }; + + GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); + + int maxLevel = Info.Levels - 1; + + if (maxLevel < 0) + { + maxLevel = 0; + } + + GL.TexParameter(target, TextureParameterName.TextureMaxLevel, maxLevel); + GL.TexParameter(target, TextureParameterName.DepthStencilTextureMode, (int)Info.DepthStencilMode.Convert()); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + if (Info.IsCompressed == info.IsCompressed) + { + firstLayer += FirstLayer; + firstLevel += FirstLevel; + + return _parent.CreateView(info, firstLayer, firstLevel); + } + else + { + // TODO: Most graphics APIs doesn't support creating a texture view from a compressed format + // with a non-compressed format (or vice-versa), however NVN seems to support it. + // So we emulate that here with a texture copy (see the first CopyTo overload). + // However right now it only does a single copy right after the view is created, + // so it doesn't work for all cases. + TextureView emulatedView = (TextureView)_renderer.CreateTexture(info); + + emulatedView._emulatedViewParent = this; + + emulatedView.FirstLayer = firstLayer; + emulatedView.FirstLevel = firstLevel; + + return emulatedView; + } + } + + public int GetIncompatibleFormatViewHandle() + { + // AMD and Intel has a bug where the view format is always ignored, + // it uses the parent format instead. + // As workaround we create a new texture with the correct + // format, and then do a copy after the draw. + if (_parent.Info.Format != Format) + { + if (_incompatibleFormatView == null) + { + _incompatibleFormatView = (TextureView)_renderer.CreateTexture(Info); + } + + TextureCopyUnscaled.Copy(_parent.Info, _incompatibleFormatView.Info, _parent.Handle, _incompatibleFormatView.Handle, FirstLayer, 0, FirstLevel, 0); + + return _incompatibleFormatView.Handle; + } + + return Handle; + } + + public void SignalModified() + { + if (_incompatibleFormatView != null) + { + TextureCopyUnscaled.Copy(_incompatibleFormatView.Info, _parent.Info, _incompatibleFormatView.Handle, _parent.Handle, 0, FirstLayer, 0, FirstLevel); + } + } + + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + TextureView destinationView = (TextureView)destination; + + TextureCopyUnscaled.Copy(Info, destinationView.Info, Handle, destinationView.Handle, 0, firstLayer, 0, firstLevel); + + if (destinationView._emulatedViewParent != null) + { + TextureCopyUnscaled.Copy( + Info, + destinationView._emulatedViewParent.Info, + Handle, + destinationView._emulatedViewParent.Handle, + 0, + destinationView.FirstLayer, + 0, + destinationView.FirstLevel); + } + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter); + } + + public byte[] GetData() + { + int size = 0; + + for (int level = 0; level < Info.Levels; level++) + { + size += Info.GetMipSize(level); + } + + byte[] data = new byte[size]; + + unsafe + { + fixed (byte* ptr = data) + { + WriteTo((IntPtr)ptr); + } + } + + return data; + } + + private void WriteTo(IntPtr ptr) + { + TextureTarget target = Target.Convert(); + + Bind(target, 0); + + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + + int faces = 1; + + if (target == TextureTarget.TextureCubeMap) + { + target = TextureTarget.TextureCubeMapPositiveX; + + faces = 6; + } + + for (int level = 0; level < Info.Levels; level++) + { + for (int face = 0; face < faces; face++) + { + int faceOffset = face * Info.GetMipSize2D(level); + + if (format.IsCompressed) + { + GL.GetCompressedTexImage(target + face, level, ptr + faceOffset); + } + else + { + GL.GetTexImage( + target + face, + level, + format.PixelFormat, + format.PixelType, + ptr + faceOffset); + } + } + + ptr += Info.GetMipSize(level); + } + } + + public void SetData(ReadOnlySpan data) + { + unsafe + { + fixed (byte* ptr = data) + { + SetData((IntPtr)ptr, data.Length); + } + } + } + + private void SetData(IntPtr data, int size) + { + TextureTarget target = Target.Convert(); + + Bind(target, 0); + + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + + int width = Info.Width; + int height = Info.Height; + int depth = Info.Depth; + + int offset = 0; + + for (int level = 0; level < Info.Levels; level++) + { + int mipSize = Info.GetMipSize(level); + + int endOffset = offset + mipSize; + + if ((uint)endOffset > (uint)size) + { + return; + } + + switch (Info.Target) + { + case Target.Texture1D: + if (format.IsCompressed) + { + GL.CompressedTexSubImage1D( + target, + level, + 0, + width, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage1D( + target, + level, + 0, + width, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Texture1DArray: + case Target.Texture2D: + if (format.IsCompressed) + { + GL.CompressedTexSubImage2D( + target, + level, + 0, + 0, + width, + height, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage2D( + target, + level, + 0, + 0, + width, + height, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Texture2DArray: + case Target.Texture3D: + case Target.CubemapArray: + if (format.IsCompressed) + { + GL.CompressedTexSubImage3D( + target, + level, + 0, + 0, + 0, + width, + height, + depth, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage3D( + target, + level, + 0, + 0, + 0, + width, + height, + depth, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Cubemap: + int faceOffset = 0; + + for (int face = 0; face < 6; face++, faceOffset += mipSize / 6) + { + if (format.IsCompressed) + { + GL.CompressedTexSubImage2D( + TextureTarget.TextureCubeMapPositiveX + face, + level, + 0, + 0, + width, + height, + format.PixelFormat, + mipSize / 6, + data + faceOffset); + } + else + { + GL.TexSubImage2D( + TextureTarget.TextureCubeMapPositiveX + face, + level, + 0, + 0, + width, + height, + format.PixelFormat, + format.PixelType, + data + faceOffset); + } + } + break; + } + + data += mipSize; + offset += mipSize; + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (Target == Target.Texture3D) + { + depth = Math.Max(1, depth >> 1); + } + } + } + + public void SetStorage(BufferRange buffer) + { + throw new NotSupportedException(); + } + + public void Dispose() + { + if (_incompatibleFormatView != null) + { + _incompatibleFormatView.Dispose(); + + _incompatibleFormatView = null; + } + + if (Handle != 0) + { + GL.DeleteTexture(Handle); + + _parent.DecrementViewsCount(); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 80b07108..05383f5d 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -1,6 +1,7 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.OpenGL.Image; using Ryujinx.Graphics.OpenGL.Queries; using Ryujinx.Graphics.Shader; using System; @@ -32,7 +33,7 @@ namespace Ryujinx.Graphics.OpenGL private ClipOrigin _clipOrigin; private ClipDepthMode _clipDepthMode; - private uint[] _componentMasks; + private readonly uint[] _componentMasks; private bool _scissor0Enable = false; @@ -43,6 +44,13 @@ namespace Ryujinx.Graphics.OpenGL _rasterizerDiscard = false; _clipOrigin = ClipOrigin.LowerLeft; _clipDepthMode = ClipDepthMode.NegativeOneToOne; + + _componentMasks = new uint[Constants.MaxRenderTargets]; + + for (int index = 0; index < Constants.MaxRenderTargets; index++) + { + _componentMasks[index] = 0xf; + } } public void Barrier() @@ -112,6 +120,11 @@ namespace Ryujinx.Graphics.OpenGL _framebuffer.SignalModified(); } + public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) + { + Buffer.Copy(source, destination, srcOffset, dstOffset, size); + } + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { if (!_program.IsLinked) @@ -631,7 +644,7 @@ namespace Ryujinx.Graphics.OpenGL EnsureVertexArray(); - _vertexArray.SetIndexBuffer((Buffer)buffer.Buffer); + _vertexArray.SetIndexBuffer(buffer.Handle); } public void SetPointSize(float size) @@ -661,7 +674,6 @@ namespace Ryujinx.Graphics.OpenGL public void SetProgram(IProgram program) { _program = (Program)program; - _program.Bind(); } @@ -679,12 +691,12 @@ namespace Ryujinx.Graphics.OpenGL _rasterizerDiscard = discard; } - public void SetRenderTargetColorMasks(uint[] componentMasks) + public void SetRenderTargetColorMasks(ReadOnlySpan componentMasks) { - _componentMasks = (uint[])componentMasks.Clone(); - for (int index = 0; index < componentMasks.Length; index++) { + _componentMasks[index] = componentMasks[index]; + RestoreComponentMask(index); } } @@ -823,21 +835,21 @@ namespace Ryujinx.Graphics.OpenGL GL.Enable(EnableCap.ClipDistance0 + index); } - public void SetVertexAttribs(VertexAttribDescriptor[] vertexAttribs) + public void SetVertexAttribs(ReadOnlySpan vertexAttribs) { EnsureVertexArray(); _vertexArray.SetVertexAttributes(vertexAttribs); } - public void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers) + public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { EnsureVertexArray(); _vertexArray.SetVertexBuffers(vertexBuffers); } - public void SetViewports(int first, Viewport[] viewports) + public void SetViewports(int first, ReadOnlySpan viewports) { bool flipY = false; @@ -906,18 +918,16 @@ namespace Ryujinx.Graphics.OpenGL ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer; - if (buffer.Buffer == null) + if (buffer.Handle == null) { GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0); return; } - int bufferHandle = ((Buffer)buffer.Buffer).Handle; - IntPtr bufferOffset = (IntPtr)buffer.Offset; - GL.BindBufferRange(target, bindingPoint, bufferHandle, bufferOffset, buffer.Size); + GL.BindBufferRange(target, bindingPoint, buffer.Handle.ToInt32(), bufferOffset, buffer.Size); } private void SetOrigin(ClipOrigin origin) @@ -997,15 +1007,12 @@ namespace Ryujinx.Graphics.OpenGL private void RestoreComponentMask(int index) { - if (_componentMasks != null) - { - GL.ColorMask( - index, - (_componentMasks[index] & 1u) != 0, - (_componentMasks[index] & 2u) != 0, - (_componentMasks[index] & 4u) != 0, - (_componentMasks[index] & 8u) != 0); - } + GL.ColorMask( + index, + (_componentMasks[index] & 1u) != 0, + (_componentMasks[index] & 2u) != 0, + (_componentMasks[index] & 4u) != 0, + (_componentMasks[index] & 8u) != 0); } public void RestoreScissor0Enable() diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs index b3ae8c33..15b223f5 100644 --- a/Ryujinx.Graphics.OpenGL/Renderer.cs +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -1,6 +1,7 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.OpenGL.Image; using Ryujinx.Graphics.OpenGL.Queries; using Ryujinx.Graphics.Shader; using System; @@ -38,9 +39,9 @@ namespace Ryujinx.Graphics.OpenGL return new Shader(shader); } - public IBuffer CreateBuffer(int size) + public BufferHandle CreateBuffer(int size) { - return new Buffer(size); + return Buffer.Create(size); } public IProgram CreateProgram(IShader[] shaders) @@ -58,6 +59,16 @@ namespace Ryujinx.Graphics.OpenGL return info.Target == Target.TextureBuffer ? new TextureBuffer(info) : new TextureStorage(this, info).CreateDefaultView(); } + public void DeleteBuffer(BufferHandle buffer) + { + Buffer.Delete(buffer); + } + + public byte[] GetBufferData(BufferHandle buffer, int offset, int size) + { + return Buffer.GetData(buffer, offset, size); + } + public Capabilities GetCapabilities() { return new Capabilities( @@ -68,6 +79,11 @@ namespace Ryujinx.Graphics.OpenGL HwCapabilities.MaxSupportedAnisotropy); } + public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) + { + Buffer.SetData(buffer, offset, data); + } + public void UpdateCounters() { _counters.Update(); diff --git a/Ryujinx.Graphics.OpenGL/Sampler.cs b/Ryujinx.Graphics.OpenGL/Sampler.cs deleted file mode 100644 index 674fc797..00000000 --- a/Ryujinx.Graphics.OpenGL/Sampler.cs +++ /dev/null @@ -1,59 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.GAL; - -namespace Ryujinx.Graphics.OpenGL -{ - class Sampler : ISampler - { - public int Handle { get; private set; } - - public Sampler(SamplerCreateInfo info) - { - Handle = GL.GenSampler(); - - GL.SamplerParameter(Handle, SamplerParameterName.TextureMinFilter, (int)info.MinFilter.Convert()); - GL.SamplerParameter(Handle, SamplerParameterName.TextureMagFilter, (int)info.MagFilter.Convert()); - - GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapS, (int)info.AddressU.Convert()); - GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapT, (int)info.AddressV.Convert()); - GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapR, (int)info.AddressP.Convert()); - - GL.SamplerParameter(Handle, SamplerParameterName.TextureCompareMode, (int)info.CompareMode.Convert()); - GL.SamplerParameter(Handle, SamplerParameterName.TextureCompareFunc, (int)info.CompareOp.Convert()); - - unsafe - { - float* borderColor = stackalloc float[4] - { - info.BorderColor.Red, - info.BorderColor.Green, - info.BorderColor.Blue, - info.BorderColor.Alpha - }; - - GL.SamplerParameter(Handle, SamplerParameterName.TextureBorderColor, borderColor); - } - - GL.SamplerParameter(Handle, SamplerParameterName.TextureMinLod, info.MinLod); - GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxLod, info.MaxLod); - GL.SamplerParameter(Handle, SamplerParameterName.TextureLodBias, info.MipLodBias); - - GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxAnisotropyExt, info.MaxAnisotropy); - } - - public void Bind(int unit) - { - GL.BindSampler(unit, Handle); - } - - public void Dispose() - { - if (Handle != 0) - { - GL.DeleteSampler(Handle); - - Handle = 0; - } - } - } -} diff --git a/Ryujinx.Graphics.OpenGL/TextureBase.cs b/Ryujinx.Graphics.OpenGL/TextureBase.cs deleted file mode 100644 index f4ab0bda..00000000 --- a/Ryujinx.Graphics.OpenGL/TextureBase.cs +++ /dev/null @@ -1,39 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.GAL; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Ryujinx.Graphics.OpenGL -{ - class TextureBase - { - public int Handle { get; protected set; } - - protected TextureCreateInfo Info { get; } - - public int Width => Info.Width; - public int Height => Info.Height; - - public Target Target => Info.Target; - public Format Format => Info.Format; - - public TextureBase(TextureCreateInfo info) - { - Info = info; - - Handle = GL.GenTexture(); - } - - public void Bind(int unit) - { - Bind(Target.Convert(), unit); - } - - protected void Bind(TextureTarget target, int unit) - { - GL.ActiveTexture(TextureUnit.Texture0 + unit); - GL.BindTexture(target, Handle); - } - } -} diff --git a/Ryujinx.Graphics.OpenGL/TextureBuffer.cs b/Ryujinx.Graphics.OpenGL/TextureBuffer.cs deleted file mode 100644 index fb18c6ee..00000000 --- a/Ryujinx.Graphics.OpenGL/TextureBuffer.cs +++ /dev/null @@ -1,71 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.GAL; -using System; - -namespace Ryujinx.Graphics.OpenGL -{ - class TextureBuffer : TextureBase, ITexture - { - private int _bufferOffset; - private int _bufferSize; - - private Buffer _buffer; - - public TextureBuffer(TextureCreateInfo info) : base(info) {} - - public void CopyTo(ITexture destination, int firstLayer, int firstLevel) - { - throw new NotSupportedException(); - } - - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) - { - throw new NotSupportedException(); - } - - public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) - { - throw new NotSupportedException(); - } - - public byte[] GetData() - { - return _buffer?.GetData(_bufferOffset, _bufferSize); - } - - public void SetData(ReadOnlySpan data) - { - _buffer?.SetData(_bufferOffset, data.Slice(0, Math.Min(data.Length, _bufferSize))); - } - - public void SetStorage(BufferRange buffer) - { - if (buffer.Buffer == _buffer && - buffer.Offset == _bufferOffset && - buffer.Size == _bufferSize) - { - return; - } - - _buffer = (Buffer)buffer.Buffer; - _bufferOffset = buffer.Offset; - _bufferSize = buffer.Size; - - Bind(0); - - SizedInternalFormat format = (SizedInternalFormat)FormatTable.GetFormatInfo(Info.Format).PixelInternalFormat; - - GL.TexBufferRange(TextureBufferTarget.TextureBuffer, format, _buffer.Handle, (IntPtr)buffer.Offset, buffer.Size); - } - - public void Dispose() - { - if (Handle != 0) - { - GL.DeleteTexture(Handle); - - Handle = 0; - } - } - } -} diff --git a/Ryujinx.Graphics.OpenGL/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/TextureCopy.cs deleted file mode 100644 index 59db94a4..00000000 --- a/Ryujinx.Graphics.OpenGL/TextureCopy.cs +++ /dev/null @@ -1,150 +0,0 @@ -using Ryujinx.Graphics.GAL; -using OpenTK.Graphics.OpenGL; -using System; - -namespace Ryujinx.Graphics.OpenGL -{ - class TextureCopy : IDisposable - { - private readonly Renderer _renderer; - - private int _srcFramebuffer; - private int _dstFramebuffer; - - public TextureCopy(Renderer renderer) - { - _renderer = renderer; - } - - public void Copy( - TextureView src, - TextureView dst, - Extents2D srcRegion, - Extents2D dstRegion, - bool linearFilter) - { - int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); - int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); - - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); - - Attach(FramebufferTarget.ReadFramebuffer, src.Format, src.Handle); - Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle); - - ClearBufferMask mask = GetMask(src.Format); - - BlitFramebufferFilter filter = linearFilter - ? BlitFramebufferFilter.Linear - : BlitFramebufferFilter.Nearest; - - GL.ReadBuffer(ReadBufferMode.ColorAttachment0); - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); - - GL.Disable(EnableCap.RasterizerDiscard); - GL.Disable(IndexedEnableCap.ScissorTest, 0); - - GL.BlitFramebuffer( - srcRegion.X1, - srcRegion.Y1, - srcRegion.X2, - srcRegion.Y2, - dstRegion.X1, - dstRegion.Y1, - dstRegion.X2, - dstRegion.Y2, - mask, - filter); - - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); - - ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); - ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); - } - - private static void Attach(FramebufferTarget target, Format format, int handle) - { - if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) - { - GL.FramebufferTexture(target, FramebufferAttachment.DepthStencilAttachment, handle, 0); - } - else if (IsDepthOnly(format)) - { - GL.FramebufferTexture(target, FramebufferAttachment.DepthAttachment, handle, 0); - } - else if (format == Format.S8Uint) - { - GL.FramebufferTexture(target, FramebufferAttachment.StencilAttachment, handle, 0); - } - else - { - GL.FramebufferTexture(target, FramebufferAttachment.ColorAttachment0, handle, 0); - } - } - - private static ClearBufferMask GetMask(Format format) - { - if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) - { - return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit; - } - else if (IsDepthOnly(format)) - { - return ClearBufferMask.DepthBufferBit; - } - else if (format == Format.S8Uint) - { - return ClearBufferMask.StencilBufferBit; - } - else - { - return ClearBufferMask.ColorBufferBit; - } - } - - private static bool IsDepthOnly(Format format) - { - return format == Format.D16Unorm || - format == Format.D24X8Unorm || - format == Format.D32Float; - } - - private int GetSrcFramebufferLazy() - { - if (_srcFramebuffer == 0) - { - _srcFramebuffer = GL.GenFramebuffer(); - } - - return _srcFramebuffer; - } - - private int GetDstFramebufferLazy() - { - if (_dstFramebuffer == 0) - { - _dstFramebuffer = GL.GenFramebuffer(); - } - - return _dstFramebuffer; - } - - public void Dispose() - { - if (_srcFramebuffer != 0) - { - GL.DeleteFramebuffer(_srcFramebuffer); - - _srcFramebuffer = 0; - } - - if (_dstFramebuffer != 0) - { - GL.DeleteFramebuffer(_dstFramebuffer); - - _dstFramebuffer = 0; - } - } - } -} diff --git a/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs b/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs deleted file mode 100644 index 5ae75d9c..00000000 --- a/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs +++ /dev/null @@ -1,93 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Common; -using Ryujinx.Graphics.GAL; -using System; - -namespace Ryujinx.Graphics.OpenGL -{ - static class TextureCopyUnscaled - { - public static void Copy( - TextureCreateInfo srcInfo, - TextureCreateInfo dstInfo, - int srcHandle, - int dstHandle, - int srcLayer, - int dstLayer, - int srcLevel, - int dstLevel) - { - int srcWidth = srcInfo.Width; - int srcHeight = srcInfo.Height; - int srcDepth = srcInfo.GetDepthOrLayers(); - int srcLevels = srcInfo.Levels; - - int dstWidth = dstInfo.Width; - int dstHeight = dstInfo.Height; - int dstDepth = dstInfo.GetDepthOrLayers(); - int dstLevels = dstInfo.Levels; - - dstWidth = Math.Max(1, dstWidth >> dstLevel); - dstHeight = Math.Max(1, dstHeight >> dstLevel); - - if (dstInfo.Target == Target.Texture3D) - { - dstDepth = Math.Max(1, dstDepth >> dstLevel); - } - - // When copying from a compressed to a non-compressed format, - // the non-compressed texture will have the size of the texture - // in blocks (not in texels), so we must adjust that size to - // match the size in texels of the compressed texture. - if (!srcInfo.IsCompressed && dstInfo.IsCompressed) - { - dstWidth = BitUtils.DivRoundUp(dstWidth, dstInfo.BlockWidth); - dstHeight = BitUtils.DivRoundUp(dstHeight, dstInfo.BlockHeight); - } - else if (srcInfo.IsCompressed && !dstInfo.IsCompressed) - { - dstWidth *= dstInfo.BlockWidth; - dstHeight *= dstInfo.BlockHeight; - } - - int width = Math.Min(srcWidth, dstWidth); - int height = Math.Min(srcHeight, dstHeight); - int depth = Math.Min(srcDepth, dstDepth); - int levels = Math.Min(srcLevels, dstLevels); - - for (int level = 0; level < levels; level++) - { - // Stop copy if we are already out of the levels range. - if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels) - { - break; - } - - GL.CopyImageSubData( - srcHandle, - srcInfo.Target.ConvertToImageTarget(), - srcLevel + level, - 0, - 0, - srcLayer, - dstHandle, - dstInfo.Target.ConvertToImageTarget(), - dstLevel + level, - 0, - 0, - dstLayer, - width, - height, - depth); - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - - if (srcInfo.Target == Target.Texture3D) - { - depth = Math.Max(1, depth >> 1); - } - } - } - } -} diff --git a/Ryujinx.Graphics.OpenGL/TextureStorage.cs b/Ryujinx.Graphics.OpenGL/TextureStorage.cs deleted file mode 100644 index b680f3a6..00000000 --- a/Ryujinx.Graphics.OpenGL/TextureStorage.cs +++ /dev/null @@ -1,178 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; - -namespace Ryujinx.Graphics.OpenGL -{ - class TextureStorage - { - public int Handle { get; private set; } - - public TextureCreateInfo Info { get; } - - private readonly Renderer _renderer; - - private int _viewsCount; - - public TextureStorage(Renderer renderer, TextureCreateInfo info) - { - _renderer = renderer; - Info = info; - - Handle = GL.GenTexture(); - - CreateImmutableStorage(); - } - - private void CreateImmutableStorage() - { - TextureTarget target = Info.Target.Convert(); - - GL.ActiveTexture(TextureUnit.Texture0); - - GL.BindTexture(target, Handle); - - FormatInfo format = FormatTable.GetFormatInfo(Info.Format); - - SizedInternalFormat internalFormat; - - if (format.IsCompressed) - { - internalFormat = (SizedInternalFormat)format.PixelFormat; - } - else - { - internalFormat = (SizedInternalFormat)format.PixelInternalFormat; - } - - switch (Info.Target) - { - case Target.Texture1D: - GL.TexStorage1D( - TextureTarget1d.Texture1D, - Info.Levels, - internalFormat, - Info.Width); - break; - - case Target.Texture1DArray: - GL.TexStorage2D( - TextureTarget2d.Texture1DArray, - Info.Levels, - internalFormat, - Info.Width, - Info.Height); - break; - - case Target.Texture2D: - GL.TexStorage2D( - TextureTarget2d.Texture2D, - Info.Levels, - internalFormat, - Info.Width, - Info.Height); - break; - - case Target.Texture2DArray: - GL.TexStorage3D( - TextureTarget3d.Texture2DArray, - Info.Levels, - internalFormat, - Info.Width, - Info.Height, - Info.Depth); - break; - - case Target.Texture2DMultisample: - GL.TexStorage2DMultisample( - TextureTargetMultisample2d.Texture2DMultisample, - Info.Samples, - internalFormat, - Info.Width, - Info.Height, - true); - break; - - case Target.Texture2DMultisampleArray: - GL.TexStorage3DMultisample( - TextureTargetMultisample3d.Texture2DMultisampleArray, - Info.Samples, - internalFormat, - Info.Width, - Info.Height, - Info.Depth, - true); - break; - - case Target.Texture3D: - GL.TexStorage3D( - TextureTarget3d.Texture3D, - Info.Levels, - internalFormat, - Info.Width, - Info.Height, - Info.Depth); - break; - - case Target.Cubemap: - GL.TexStorage2D( - TextureTarget2d.TextureCubeMap, - Info.Levels, - internalFormat, - Info.Width, - Info.Height); - break; - - case Target.CubemapArray: - GL.TexStorage3D( - (TextureTarget3d)All.TextureCubeMapArray, - Info.Levels, - internalFormat, - Info.Width, - Info.Height, - Info.Depth); - break; - - default: - Logger.PrintDebug(LogClass.Gpu, $"Invalid or unsupported texture target: {target}."); - break; - } - } - - public ITexture CreateDefaultView() - { - return CreateView(Info, 0, 0); - } - - public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) - { - IncrementViewsCount(); - - return new TextureView(_renderer, this, info, firstLayer, firstLevel); - } - - private void IncrementViewsCount() - { - _viewsCount++; - } - - public void DecrementViewsCount() - { - // If we don't have any views, then the storage is now useless, delete it. - if (--_viewsCount == 0) - { - Dispose(); - } - } - - public void Dispose() - { - if (Handle != 0) - { - GL.DeleteTexture(Handle); - - Handle = 0; - } - } - } -} diff --git a/Ryujinx.Graphics.OpenGL/TextureView.cs b/Ryujinx.Graphics.OpenGL/TextureView.cs deleted file mode 100644 index 0ab59d42..00000000 --- a/Ryujinx.Graphics.OpenGL/TextureView.cs +++ /dev/null @@ -1,433 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.GAL; -using System; - -namespace Ryujinx.Graphics.OpenGL -{ - class TextureView : TextureBase, ITexture - { - private readonly Renderer _renderer; - - private readonly TextureStorage _parent; - - private TextureView _emulatedViewParent; - - private TextureView _incompatibleFormatView; - - public int FirstLayer { get; private set; } - public int FirstLevel { get; private set; } - - public TextureView( - Renderer renderer, - TextureStorage parent, - TextureCreateInfo info, - int firstLayer, - int firstLevel) : base(info) - { - _renderer = renderer; - _parent = parent; - - FirstLayer = firstLayer; - FirstLevel = firstLevel; - - CreateView(); - } - - private void CreateView() - { - TextureTarget target = Target.Convert(); - - FormatInfo format = FormatTable.GetFormatInfo(Info.Format); - - PixelInternalFormat pixelInternalFormat; - - if (format.IsCompressed) - { - pixelInternalFormat = (PixelInternalFormat)format.PixelFormat; - } - else - { - pixelInternalFormat = format.PixelInternalFormat; - } - - GL.TextureView( - Handle, - target, - _parent.Handle, - pixelInternalFormat, - FirstLevel, - Info.Levels, - FirstLayer, - Info.GetLayers()); - - GL.ActiveTexture(TextureUnit.Texture0); - - GL.BindTexture(target, Handle); - - int[] swizzleRgba = new int[] - { - (int)Info.SwizzleR.Convert(), - (int)Info.SwizzleG.Convert(), - (int)Info.SwizzleB.Convert(), - (int)Info.SwizzleA.Convert() - }; - - GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); - - int maxLevel = Info.Levels - 1; - - if (maxLevel < 0) - { - maxLevel = 0; - } - - GL.TexParameter(target, TextureParameterName.TextureMaxLevel, maxLevel); - GL.TexParameter(target, TextureParameterName.DepthStencilTextureMode, (int)Info.DepthStencilMode.Convert()); - } - - public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) - { - if (Info.IsCompressed == info.IsCompressed) - { - firstLayer += FirstLayer; - firstLevel += FirstLevel; - - return _parent.CreateView(info, firstLayer, firstLevel); - } - else - { - // TODO: Most graphics APIs doesn't support creating a texture view from a compressed format - // with a non-compressed format (or vice-versa), however NVN seems to support it. - // So we emulate that here with a texture copy (see the first CopyTo overload). - // However right now it only does a single copy right after the view is created, - // so it doesn't work for all cases. - TextureView emulatedView = (TextureView)_renderer.CreateTexture(info); - - emulatedView._emulatedViewParent = this; - - emulatedView.FirstLayer = firstLayer; - emulatedView.FirstLevel = firstLevel; - - return emulatedView; - } - } - - public int GetIncompatibleFormatViewHandle() - { - // AMD and Intel has a bug where the view format is always ignored, - // it uses the parent format instead. - // As workaround we create a new texture with the correct - // format, and then do a copy after the draw. - if (_parent.Info.Format != Format) - { - if (_incompatibleFormatView == null) - { - _incompatibleFormatView = (TextureView)_renderer.CreateTexture(Info); - } - - TextureCopyUnscaled.Copy(_parent.Info, _incompatibleFormatView.Info, _parent.Handle, _incompatibleFormatView.Handle, FirstLayer, 0, FirstLevel, 0); - - return _incompatibleFormatView.Handle; - } - - return Handle; - } - - public void SignalModified() - { - if (_incompatibleFormatView != null) - { - TextureCopyUnscaled.Copy(_incompatibleFormatView.Info, _parent.Info, _incompatibleFormatView.Handle, _parent.Handle, 0, FirstLayer, 0, FirstLevel); - } - } - - public void CopyTo(ITexture destination, int firstLayer, int firstLevel) - { - TextureView destinationView = (TextureView)destination; - - TextureCopyUnscaled.Copy(Info, destinationView.Info, Handle, destinationView.Handle, 0, firstLayer, 0, firstLevel); - - if (destinationView._emulatedViewParent != null) - { - TextureCopyUnscaled.Copy( - Info, - destinationView._emulatedViewParent.Info, - Handle, - destinationView._emulatedViewParent.Handle, - 0, - destinationView.FirstLayer, - 0, - destinationView.FirstLevel); - } - } - - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) - { - _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter); - } - - public byte[] GetData() - { - int size = 0; - - for (int level = 0; level < Info.Levels; level++) - { - size += Info.GetMipSize(level); - } - - byte[] data = new byte[size]; - - unsafe - { - fixed (byte* ptr = data) - { - WriteTo((IntPtr)ptr); - } - } - - return data; - } - - private void WriteTo(IntPtr ptr) - { - TextureTarget target = Target.Convert(); - - Bind(target, 0); - - FormatInfo format = FormatTable.GetFormatInfo(Info.Format); - - int faces = 1; - - if (target == TextureTarget.TextureCubeMap) - { - target = TextureTarget.TextureCubeMapPositiveX; - - faces = 6; - } - - for (int level = 0; level < Info.Levels; level++) - { - for (int face = 0; face < faces; face++) - { - int faceOffset = face * Info.GetMipSize2D(level); - - if (format.IsCompressed) - { - GL.GetCompressedTexImage(target + face, level, ptr + faceOffset); - } - else - { - GL.GetTexImage( - target + face, - level, - format.PixelFormat, - format.PixelType, - ptr + faceOffset); - } - } - - ptr += Info.GetMipSize(level); - } - } - - public void SetData(ReadOnlySpan data) - { - unsafe - { - fixed (byte* ptr = data) - { - SetData((IntPtr)ptr, data.Length); - } - } - } - - private void SetData(IntPtr data, int size) - { - TextureTarget target = Target.Convert(); - - Bind(target, 0); - - FormatInfo format = FormatTable.GetFormatInfo(Info.Format); - - int width = Info.Width; - int height = Info.Height; - int depth = Info.Depth; - - int offset = 0; - - for (int level = 0; level < Info.Levels; level++) - { - int mipSize = Info.GetMipSize(level); - - int endOffset = offset + mipSize; - - if ((uint)endOffset > (uint)size) - { - return; - } - - switch (Info.Target) - { - case Target.Texture1D: - if (format.IsCompressed) - { - GL.CompressedTexSubImage1D( - target, - level, - 0, - width, - format.PixelFormat, - mipSize, - data); - } - else - { - GL.TexSubImage1D( - target, - level, - 0, - width, - format.PixelFormat, - format.PixelType, - data); - } - break; - - case Target.Texture1DArray: - case Target.Texture2D: - if (format.IsCompressed) - { - GL.CompressedTexSubImage2D( - target, - level, - 0, - 0, - width, - height, - format.PixelFormat, - mipSize, - data); - } - else - { - GL.TexSubImage2D( - target, - level, - 0, - 0, - width, - height, - format.PixelFormat, - format.PixelType, - data); - } - break; - - case Target.Texture2DArray: - case Target.Texture3D: - case Target.CubemapArray: - if (format.IsCompressed) - { - GL.CompressedTexSubImage3D( - target, - level, - 0, - 0, - 0, - width, - height, - depth, - format.PixelFormat, - mipSize, - data); - } - else - { - GL.TexSubImage3D( - target, - level, - 0, - 0, - 0, - width, - height, - depth, - format.PixelFormat, - format.PixelType, - data); - } - break; - - case Target.Cubemap: - int faceOffset = 0; - - for (int face = 0; face < 6; face++, faceOffset += mipSize / 6) - { - if (format.IsCompressed) - { - GL.CompressedTexSubImage2D( - TextureTarget.TextureCubeMapPositiveX + face, - level, - 0, - 0, - width, - height, - format.PixelFormat, - mipSize / 6, - data + faceOffset); - } - else - { - GL.TexSubImage2D( - TextureTarget.TextureCubeMapPositiveX + face, - level, - 0, - 0, - width, - height, - format.PixelFormat, - format.PixelType, - data + faceOffset); - } - } - break; - } - - data += mipSize; - offset += mipSize; - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - - if (Target == Target.Texture3D) - { - depth = Math.Max(1, depth >> 1); - } - } - } - - public void SetStorage(BufferRange buffer) - { - throw new NotSupportedException(); - } - - public void Dispose() - { - if (_incompatibleFormatView != null) - { - _incompatibleFormatView.Dispose(); - - _incompatibleFormatView = null; - } - - if (Handle != 0) - { - GL.DeleteTexture(Handle); - - _parent.DecrementViewsCount(); - - Handle = 0; - } - } - } -} diff --git a/Ryujinx.Graphics.OpenGL/VertexArray.cs b/Ryujinx.Graphics.OpenGL/VertexArray.cs index 43d200a4..cc352761 100644 --- a/Ryujinx.Graphics.OpenGL/VertexArray.cs +++ b/Ryujinx.Graphics.OpenGL/VertexArray.cs @@ -10,12 +10,18 @@ namespace Ryujinx.Graphics.OpenGL private bool _needsAttribsUpdate; - private VertexBufferDescriptor[] _vertexBuffers; - private VertexAttribDescriptor[] _vertexAttribs; + private readonly VertexAttribDescriptor[] _vertexAttribs; + private readonly VertexBufferDescriptor[] _vertexBuffers; + + private int _vertexAttribsCount; + private int _vertexBuffersCount; public VertexArray() { Handle = GL.GenVertexArray(); + + _vertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttribs]; + _vertexBuffers = new VertexBufferDescriptor[Constants.MaxVertexBuffers]; } public void Bind() @@ -23,17 +29,17 @@ namespace Ryujinx.Graphics.OpenGL GL.BindVertexArray(Handle); } - public void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers) + public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { int bindingIndex = 0; - foreach (VertexBufferDescriptor vb in vertexBuffers) + for (int index = 0; index < vertexBuffers.Length; index++) { - if (vb.Buffer.Buffer != null) - { - int bufferHandle = ((Buffer)vb.Buffer.Buffer).Handle; + VertexBufferDescriptor vb = vertexBuffers[index]; - GL.BindVertexBuffer(bindingIndex, bufferHandle, (IntPtr)vb.Buffer.Offset, vb.Stride); + if (vb.Buffer.Handle != null) + { + GL.BindVertexBuffer(bindingIndex, vb.Buffer.Handle.ToInt32(), (IntPtr)vb.Buffer.Offset, vb.Stride); GL.VertexBindingDivisor(bindingIndex, vb.Divisor); } @@ -42,31 +48,35 @@ namespace Ryujinx.Graphics.OpenGL GL.BindVertexBuffer(bindingIndex, 0, IntPtr.Zero, 0); } + _vertexBuffers[index] = vb; + bindingIndex++; } - _vertexBuffers = vertexBuffers; + _vertexBuffersCount = bindingIndex; _needsAttribsUpdate = true; } - public void SetVertexAttributes(VertexAttribDescriptor[] vertexAttribs) + public void SetVertexAttributes(ReadOnlySpan vertexAttribs) { - int attribIndex = 0; + int index = 0; - foreach (VertexAttribDescriptor attrib in vertexAttribs) + for (; index < vertexAttribs.Length; index++) { + VertexAttribDescriptor attrib = vertexAttribs[index]; + FormatInfo fmtInfo = FormatTable.GetFormatInfo(attrib.Format); if (attrib.IsZero) { // Disabling the attribute causes the shader to read a constant value. // The value is configurable, but by default is a vector of (0, 0, 0, 1). - GL.DisableVertexAttribArray(attribIndex); + GL.DisableVertexAttribArray(index); } else { - GL.EnableVertexAttribArray(attribIndex); + GL.EnableVertexAttribArray(index); } int offset = attrib.Offset; @@ -79,47 +89,47 @@ namespace Ryujinx.Graphics.OpenGL { VertexAttribType type = (VertexAttribType)fmtInfo.PixelType; - GL.VertexAttribFormat(attribIndex, size, type, fmtInfo.Normalized, offset); + GL.VertexAttribFormat(index, size, type, fmtInfo.Normalized, offset); } else { VertexAttribIntegerType type = (VertexAttribIntegerType)fmtInfo.PixelType; - GL.VertexAttribIFormat(attribIndex, size, type, offset); + GL.VertexAttribIFormat(index, size, type, offset); } - GL.VertexAttribBinding(attribIndex, attrib.BufferIndex); + GL.VertexAttribBinding(index, attrib.BufferIndex); - attribIndex++; + _vertexAttribs[index] = attrib; } - for (; attribIndex < 16; attribIndex++) + _vertexAttribsCount = index; + + for (; index < Constants.MaxVertexAttribs; index++) { - GL.DisableVertexAttribArray(attribIndex); + GL.DisableVertexAttribArray(index); } - - _vertexAttribs = vertexAttribs; } - public void SetIndexBuffer(Buffer indexBuffer) + public void SetIndexBuffer(BufferHandle buffer) { - GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer?.Handle ?? 0); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, buffer.ToInt32()); } public void Validate() { - for (int attribIndex = 0; attribIndex < _vertexAttribs.Length; attribIndex++) + for (int attribIndex = 0; attribIndex < _vertexAttribsCount; attribIndex++) { VertexAttribDescriptor attrib = _vertexAttribs[attribIndex]; - if ((uint)attrib.BufferIndex >= _vertexBuffers.Length) + if ((uint)attrib.BufferIndex >= _vertexBuffersCount) { GL.DisableVertexAttribArray(attribIndex); continue; } - if (_vertexBuffers[attrib.BufferIndex].Buffer.Buffer == null) + if (_vertexBuffers[attrib.BufferIndex].Buffer.Handle == null) { GL.DisableVertexAttribArray(attribIndex); diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index 9f0007c4..6e7ddbac 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -1,5 +1,6 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.OpenGL.Image; using System; namespace Ryujinx.Graphics.OpenGL -- cgit v1.2.3