diff options
Diffstat (limited to 'Ryujinx.Graphics.OpenGL/Image')
| -rw-r--r-- | Ryujinx.Graphics.OpenGL/Image/Sampler.cs | 59 | ||||
| -rw-r--r-- | Ryujinx.Graphics.OpenGL/Image/TextureBase.cs | 36 | ||||
| -rw-r--r-- | Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs | 71 | ||||
| -rw-r--r-- | Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs | 150 | ||||
| -rw-r--r-- | Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs | 93 | ||||
| -rw-r--r-- | Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs | 178 | ||||
| -rw-r--r-- | Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 433 |
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; + } + } + } +} |
