aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.OpenGL/Image
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.OpenGL/Image')
-rw-r--r--Ryujinx.Graphics.OpenGL/Image/Sampler.cs59
-rw-r--r--Ryujinx.Graphics.OpenGL/Image/TextureBase.cs36
-rw-r--r--Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs71
-rw-r--r--Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs150
-rw-r--r--Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs93
-rw-r--r--Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs178
-rw-r--r--Ryujinx.Graphics.OpenGL/Image/TextureView.cs433
7 files changed, 1020 insertions, 0 deletions
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<byte> 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<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;
+ }
+ }
+ }
+}