diff options
author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
---|---|---|
committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs | |
parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs')
-rw-r--r-- | src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs new file mode 100644 index 00000000..804b3b03 --- /dev/null +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -0,0 +1,867 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common; +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class TextureView : TextureBase, ITexture, ITextureInfo + { + private readonly OpenGLRenderer _renderer; + + private readonly TextureStorage _parent; + + public ITextureInfo Storage => _parent; + + public int FirstLayer { get; private set; } + public int FirstLevel { get; private set; } + + public TextureView( + OpenGLRenderer renderer, + TextureStorage parent, + TextureCreateInfo info, + int firstLayer, + int firstLevel) : base(info, parent.ScaleFactor) + { + _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; + } + + int levels = Info.GetLevelsClamped(); + + GL.TextureView( + Handle, + target, + _parent.Handle, + pixelInternalFormat, + FirstLevel, + 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() + }; + + if (Info.Format == Format.A1B5G5R5Unorm) + { + int temp = swizzleRgba[0]; + int temp2 = swizzleRgba[1]; + swizzleRgba[0] = swizzleRgba[3]; + swizzleRgba[1] = swizzleRgba[2]; + swizzleRgba[2] = temp2; + swizzleRgba[3] = temp; + } + else if (Info.Format.IsBgr()) + { + // Swap B <-> R for BGRA formats, as OpenGL has no support for them + // and we need to manually swap the components on read/write on the GPU. + int temp = swizzleRgba[0]; + swizzleRgba[0] = swizzleRgba[2]; + swizzleRgba[2] = temp; + } + + GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); + + int maxLevel = 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) + { + firstLayer += FirstLayer; + firstLevel += FirstLevel; + + return _parent.CreateView(info, firstLayer, firstLevel); + } + + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + TextureView destinationView = (TextureView)destination; + + bool srcIsMultisample = Target.IsMultisample(); + bool dstIsMultisample = destinationView.Target.IsMultisample(); + + if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil()) + { + int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); + CopyWithBlitForDepthMS(destinationView, 0, firstLayer, layers); + } + else if (!dstIsMultisample && srcIsMultisample) + { + int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); + _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, 0, firstLayer, layers); + } + else if (dstIsMultisample && !srcIsMultisample) + { + int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); + _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers); + } + else if (destinationView.Info.BytesPerPixel != Info.BytesPerPixel) + { + int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); + int levels = Math.Min(Info.Levels, destinationView.Info.Levels - firstLevel); + _renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, 0, firstLayer, 0, firstLevel, layers, levels); + } + else + { + _renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel); + } + } + + public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + TextureView destinationView = (TextureView)destination; + + bool srcIsMultisample = Target.IsMultisample(); + bool dstIsMultisample = destinationView.Target.IsMultisample(); + + if (dstIsMultisample != srcIsMultisample && Info.Format.IsDepthOrStencil()) + { + CopyWithBlitForDepthMS(destinationView, srcLayer, dstLayer, 1); + } + else if (!dstIsMultisample && srcIsMultisample) + { + _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer, 1); + } + else if (dstIsMultisample && !srcIsMultisample) + { + _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1); + } + else if (destinationView.Info.BytesPerPixel != Info.BytesPerPixel) + { + _renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + } + else + { + _renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + } + } + + private void CopyWithBlitForDepthMS(TextureView destinationView, int srcLayer, int dstLayer, int layers) + { + // This is currently used for multisample <-> non-multisample copies. + // We can't do that with compute because it's not possible to write depth textures on compute. + // It can be done with draws, but we don't have support for saving and restoring the OpenGL state + // for a draw with different state right now. + // This approach uses blit, which causes a resolution loss since some samples will be lost + // in the process. + + Extents2D srcRegion = new Extents2D(0, 0, Width, Height); + Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height); + + if (destinationView.Target.IsMultisample()) + { + TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( + Info.Target, + Info.BlockWidth, + Info.BlockHeight, + Info.BytesPerPixel, + Format, + destinationView.Width, + destinationView.Height, + Info.Depth, + 1, + 1); + + _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, dstRegion, false); + _renderer.TextureCopy.Copy(intermmediate, destinationView, dstRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1); + } + else + { + Target target = Target switch + { + Target.Texture2DMultisample => Target.Texture2D, + Target.Texture2DMultisampleArray => Target.Texture2DArray, + _ => Target + }; + + TextureView intermmediate = _renderer.TextureCopy.IntermediatePool.GetOrCreateWithAtLeast( + target, + Info.BlockWidth, + Info.BlockHeight, + Info.BytesPerPixel, + Format, + Width, + Height, + Info.Depth, + 1, + 1); + + _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, false); + _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, false, srcLayer, dstLayer, 0, 0, layers, 1); + } + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter); + } + + public unsafe PinnedSpan<byte> GetData() + { + int size = 0; + int levels = Info.GetLevelsClamped(); + + for (int level = 0; level < levels; level++) + { + size += Info.GetMipSize(level); + } + + ReadOnlySpan<byte> data; + + if (HwCapabilities.UsePersistentBufferForFlush) + { + data = _renderer.PersistentBuffers.Default.GetTextureData(this, size); + } + else + { + IntPtr target = _renderer.PersistentBuffers.Default.GetHostArray(size); + + WriteTo(target); + + data = new ReadOnlySpan<byte>(target.ToPointer(), size); + } + + if (Format == Format.S8UintD24Unorm) + { + data = FormatConverter.ConvertD24S8ToS8D24(data); + } + + return PinnedSpan<byte>.UnsafeFromSpan(data); + } + + public unsafe PinnedSpan<byte> GetData(int layer, int level) + { + int size = Info.GetMipSize(level); + + if (HwCapabilities.UsePersistentBufferForFlush) + { + return PinnedSpan<byte>.UnsafeFromSpan(_renderer.PersistentBuffers.Default.GetTextureData(this, size, layer, level)); + } + else + { + IntPtr target = _renderer.PersistentBuffers.Default.GetHostArray(size); + + int offset = WriteTo2D(target, layer, level); + + return new PinnedSpan<byte>((byte*)target.ToPointer() + offset, size); + } + } + + public void WriteToPbo(int offset, bool forceBgra) + { + WriteTo(IntPtr.Zero + offset, forceBgra); + } + + public int WriteToPbo2D(int offset, int layer, int level) + { + return WriteTo2D(IntPtr.Zero + offset, layer, level); + } + + private int WriteTo2D(IntPtr data, int layer, int level) + { + TextureTarget target = Target.Convert(); + + Bind(target, 0); + + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + + PixelFormat pixelFormat = format.PixelFormat; + PixelType pixelType = format.PixelType; + + if (target == TextureTarget.TextureCubeMap || target == TextureTarget.TextureCubeMapArray) + { + target = TextureTarget.TextureCubeMapPositiveX + (layer % 6); + } + + int mipSize = Info.GetMipSize2D(level); + + if (format.IsCompressed) + { + GL.GetCompressedTextureSubImage(Handle, level, 0, 0, layer, Math.Max(1, Info.Width >> level), Math.Max(1, Info.Height >> level), 1, mipSize, data); + } + else if (format.PixelFormat != PixelFormat.DepthStencil) + { + GL.GetTextureSubImage(Handle, level, 0, 0, layer, Math.Max(1, Info.Width >> level), Math.Max(1, Info.Height >> level), 1, pixelFormat, pixelType, mipSize, data); + } + else + { + GL.GetTexImage(target, level, pixelFormat, pixelType, data); + + // The GL function returns all layers. Must return the offset of the layer we're interested in. + return target switch + { + TextureTarget.TextureCubeMapArray => (layer / 6) * mipSize, + TextureTarget.Texture1DArray => layer * mipSize, + TextureTarget.Texture2DArray => layer * mipSize, + _ => 0 + }; + } + + return 0; + } + + private void WriteTo(IntPtr data, bool forceBgra = false) + { + TextureTarget target = Target.Convert(); + + Bind(target, 0); + + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + + PixelFormat pixelFormat = format.PixelFormat; + PixelType pixelType = format.PixelType; + + if (forceBgra) + { + if (pixelType == PixelType.UnsignedShort565) + { + pixelType = PixelType.UnsignedShort565Reversed; + } + else if (pixelType == PixelType.UnsignedShort565Reversed) + { + pixelType = PixelType.UnsignedShort565; + } + else + { + pixelFormat = PixelFormat.Bgra; + } + } + + int faces = 1; + + if (target == TextureTarget.TextureCubeMap) + { + target = TextureTarget.TextureCubeMapPositiveX; + + faces = 6; + } + + int levels = Info.GetLevelsClamped(); + + for (int level = 0; level < levels; level++) + { + for (int face = 0; face < faces; face++) + { + int faceOffset = face * Info.GetMipSize2D(level); + + if (format.IsCompressed) + { + GL.GetCompressedTexImage(target + face, level, data + faceOffset); + } + else + { + GL.GetTexImage(target + face, level, pixelFormat, pixelType, data + faceOffset); + } + } + + data += Info.GetMipSize(level); + } + } + + public void SetData(SpanOrArray<byte> data) + { + var dataSpan = data.AsSpan(); + + if (Format == Format.S8UintD24Unorm) + { + dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan); + } + + unsafe + { + fixed (byte* ptr = dataSpan) + { + ReadFrom((IntPtr)ptr, dataSpan.Length); + } + } + } + + public void SetData(SpanOrArray<byte> data, int layer, int level) + { + var dataSpan = data.AsSpan(); + + if (Format == Format.S8UintD24Unorm) + { + dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan); + } + + unsafe + { + fixed (byte* ptr = dataSpan) + { + int width = Math.Max(Info.Width >> level, 1); + int height = Math.Max(Info.Height >> level, 1); + + ReadFrom2D((IntPtr)ptr, layer, level, 0, 0, width, height); + } + } + } + + public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region) + { + var dataSpan = data.AsSpan(); + + if (Format == Format.S8UintD24Unorm) + { + dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan); + } + + int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth); + int hInBlocks = BitUtils.DivRoundUp(region.Height, Info.BlockHeight); + + unsafe + { + fixed (byte* ptr = dataSpan) + { + ReadFrom2D( + (IntPtr)ptr, + layer, + level, + region.X, + region.Y, + region.Width, + region.Height, + BitUtils.AlignUp(wInBlocks * Info.BytesPerPixel, 4) * hInBlocks); + } + } + } + + public void ReadFromPbo(int offset, int size) + { + ReadFrom(IntPtr.Zero + offset, size); + } + + public void ReadFromPbo2D(int offset, int layer, int level, int width, int height) + { + ReadFrom2D(IntPtr.Zero + offset, layer, level, 0, 0, width, height); + } + + private void ReadFrom2D(IntPtr data, int layer, int level, int x, int y, int width, int height) + { + int mipSize = Info.GetMipSize2D(level); + + ReadFrom2D(data, layer, level, x, y, width, height, mipSize); + } + + private void ReadFrom2D(IntPtr data, int layer, int level, int x, int y, int width, int height, int mipSize) + { + TextureTarget target = Target.Convert(); + + Bind(target, 0); + + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + + switch (Target) + { + case Target.Texture1D: + if (format.IsCompressed) + { + GL.CompressedTexSubImage1D( + target, + level, + x, + width, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage1D( + target, + level, + x, + width, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Texture1DArray: + if (format.IsCompressed) + { + GL.CompressedTexSubImage2D( + target, + level, + x, + layer, + width, + 1, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage2D( + target, + level, + x, + layer, + width, + 1, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Texture2D: + if (format.IsCompressed) + { + GL.CompressedTexSubImage2D( + target, + level, + x, + y, + width, + height, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage2D( + target, + level, + x, + y, + width, + height, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Texture2DArray: + case Target.Texture3D: + case Target.CubemapArray: + if (format.IsCompressed) + { + GL.CompressedTexSubImage3D( + target, + level, + x, + y, + layer, + width, + height, + 1, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage3D( + target, + level, + x, + y, + layer, + width, + height, + 1, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Cubemap: + if (format.IsCompressed) + { + GL.CompressedTexSubImage2D( + TextureTarget.TextureCubeMapPositiveX + layer, + level, + x, + y, + width, + height, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage2D( + TextureTarget.TextureCubeMapPositiveX + layer, + level, + x, + y, + width, + height, + format.PixelFormat, + format.PixelType, + data); + } + break; + } + } + + private void ReadFrom(IntPtr data, int size) + { + TextureTarget target = Target.Convert(); + int baseLevel = 0; + + // glTexSubImage on cubemap views is broken on Intel, we have to use the storage instead. + if (Target == Target.Cubemap && HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows) + { + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(target, Storage.Handle); + baseLevel = FirstLevel; + } + else + { + Bind(target, 0); + } + + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + + int width = Info.Width; + int height = Info.Height; + int depth = Info.Depth; + int levels = Info.GetLevelsClamped(); + + int offset = 0; + + for (int level = 0; level < levels; level++) + { + int mipSize = Info.GetMipSize(level); + + int endOffset = offset + mipSize; + + if ((uint)endOffset > (uint)size) + { + return; + } + + switch (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, + baseLevel + level, + 0, + 0, + width, + height, + format.PixelFormat, + mipSize / 6, + data + faceOffset); + } + else + { + GL.TexSubImage2D( + TextureTarget.TextureCubeMapPositiveX + face, + baseLevel + 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(); + } + + private void DisposeHandles() + { + if (Handle != 0) + { + GL.DeleteTexture(Handle); + + Handle = 0; + } + } + + /// <summary> + /// Release the view without necessarily disposing the parent if we are the default view. + /// This allows it to be added to the resource pool and reused later. + /// </summary> + public void Release() + { + bool hadHandle = Handle != 0; + + if (_parent.DefaultView != this) + { + DisposeHandles(); + } + + if (hadHandle) + { + _parent.DecrementViewsCount(); + } + } + + public void Dispose() + { + if (_parent.DefaultView == this) + { + // Remove the default view (us), so that the texture cannot be released to the cache. + _parent.DeleteDefault(); + } + + Release(); + } + } +} |