From 5011640b3086b86b0f0b39b60fdb2aa946d4f5c8 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
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.OpenGL/Image/TextureView.cs | 433 +++++++++++++++++++++++++++
 1 file changed, 433 insertions(+)
 create mode 100644 Ryujinx.Graphics.OpenGL/Image/TextureView.cs

(limited to 'Ryujinx.Graphics.OpenGL/Image/TextureView.cs')

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<byte> 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;
+            }
+        }
+    }
+}
-- 
cgit v1.2.3-70-g09d2