diff options
author | gdkchan <gab.dark.100@gmail.com> | 2020-05-23 06:46:09 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-23 11:46:09 +0200 |
commit | 5011640b3086b86b0f0b39b60fdb2aa946d4f5c8 (patch) | |
tree | 1bd60b7714886dfe282ca1e52cfa6fca97912cdf /Ryujinx.Graphics.OpenGL/Image/TextureView.cs | |
parent | cc8dbdd3fb58a02e1c3fc3b9d0b1c35bc7b9d00f (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.cs | 433 |
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; + } + } + } +} |