aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2020-05-23 06:46:09 -0300
committerGitHub <noreply@github.com>2020-05-23 11:46:09 +0200
commit5011640b3086b86b0f0b39b60fdb2aa946d4f5c8 (patch)
tree1bd60b7714886dfe282ca1e52cfa6fca97912cdf /Ryujinx.Graphics.OpenGL/Image/TextureView.cs
parentcc8dbdd3fb58a02e1c3fc3b9d0b1c35bc7b9d00f (diff)
Spanify Graphics Abstraction Layer (#1226)
* Spanify Graphics Abstraction Layer * Be explicit about BufferHandle size
Diffstat (limited to 'Ryujinx.Graphics.OpenGL/Image/TextureView.cs')
-rw-r--r--Ryujinx.Graphics.OpenGL/Image/TextureView.cs433
1 files changed, 433 insertions, 0 deletions
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;
+ }
+ }
+ }
+}