2019-12-29 00:45:33 +01:00
|
|
|
using OpenTK.Graphics.OpenGL;
|
2019-10-13 08:02:07 +02:00
|
|
|
using Ryujinx.Graphics.GAL;
|
|
|
|
using System;
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
namespace Ryujinx.Graphics.OpenGL.Image
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2021-04-13 03:09:42 +02:00
|
|
|
class TextureView : TextureBase, ITexture, ITextureInfo
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2019-12-29 18:41:50 +01:00
|
|
|
private readonly Renderer _renderer;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2019-12-29 18:41:50 +01:00
|
|
|
private readonly TextureStorage _parent;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-04-17 22:16:28 +02:00
|
|
|
public ITextureInfo Storage => _parent;
|
|
|
|
|
2020-03-29 14:48:39 +02:00
|
|
|
public int FirstLayer { get; private set; }
|
|
|
|
public int FirstLevel { get; private set; }
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
public TextureView(
|
|
|
|
Renderer renderer,
|
|
|
|
TextureStorage parent,
|
|
|
|
TextureCreateInfo info,
|
|
|
|
int firstLayer,
|
2020-07-07 04:41:07 +02:00
|
|
|
int firstLevel) : base(info, parent.ScaleFactor)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
|
|
|
_renderer = renderer;
|
|
|
|
_parent = parent;
|
|
|
|
|
2020-03-29 14:48:39 +02:00
|
|
|
FirstLayer = firstLayer;
|
|
|
|
FirstLevel = firstLevel;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
CreateView();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void CreateView()
|
|
|
|
{
|
|
|
|
TextureTarget target = Target.Convert();
|
|
|
|
|
2020-04-25 15:02:18 +02:00
|
|
|
FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
PixelInternalFormat pixelInternalFormat;
|
|
|
|
|
|
|
|
if (format.IsCompressed)
|
|
|
|
{
|
|
|
|
pixelInternalFormat = (PixelInternalFormat)format.PixelFormat;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pixelInternalFormat = format.PixelInternalFormat;
|
|
|
|
}
|
|
|
|
|
2021-11-04 00:58:24 +01:00
|
|
|
int levels = Info.GetLevelsClamped();
|
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
GL.TextureView(
|
|
|
|
Handle,
|
|
|
|
target,
|
|
|
|
_parent.Handle,
|
|
|
|
pixelInternalFormat,
|
2020-03-29 14:48:39 +02:00
|
|
|
FirstLevel,
|
2021-11-04 00:58:24 +01:00
|
|
|
levels,
|
2020-03-29 14:48:39 +02:00
|
|
|
FirstLayer,
|
2020-04-25 15:02:18 +02:00
|
|
|
Info.GetLayers());
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
GL.ActiveTexture(TextureUnit.Texture0);
|
|
|
|
|
|
|
|
GL.BindTexture(target, Handle);
|
|
|
|
|
|
|
|
int[] swizzleRgba = new int[]
|
|
|
|
{
|
2020-04-25 15:02:18 +02:00
|
|
|
(int)Info.SwizzleR.Convert(),
|
|
|
|
(int)Info.SwizzleG.Convert(),
|
|
|
|
(int)Info.SwizzleB.Convert(),
|
|
|
|
(int)Info.SwizzleA.Convert()
|
2019-10-13 08:02:07 +02:00
|
|
|
};
|
|
|
|
|
2021-12-30 14:00:34 +01:00
|
|
|
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())
|
2020-07-26 05:03:40 +02:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba);
|
|
|
|
|
2021-11-04 00:58:24 +01:00
|
|
|
int maxLevel = levels - 1;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
if (maxLevel < 0)
|
|
|
|
{
|
|
|
|
maxLevel = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
GL.TexParameter(target, TextureParameterName.TextureMaxLevel, maxLevel);
|
2020-04-25 15:02:18 +02:00
|
|
|
GL.TexParameter(target, TextureParameterName.DepthStencilTextureMode, (int)Info.DepthStencilMode.Convert());
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel)
|
|
|
|
{
|
2021-03-02 23:30:54 +01:00
|
|
|
firstLayer += FirstLayer;
|
|
|
|
firstLevel += FirstLevel;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-03-02 23:30:54 +01:00
|
|
|
return _parent.CreateView(info, firstLayer, firstLevel);
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 00:45:01 +01:00
|
|
|
public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
|
|
|
TextureView destinationView = (TextureView)destination;
|
|
|
|
|
2020-11-20 17:30:59 +01:00
|
|
|
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
|
2021-03-02 23:30:54 +01:00
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-03-02 23:30:54 +01:00
|
|
|
public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
|
|
|
|
{
|
|
|
|
TextureView destinationView = (TextureView)destination;
|
|
|
|
|
|
|
|
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
|
|
|
|
{
|
|
|
|
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
|
|
|
}
|
|
|
|
|
Return mapped buffer pointer directly for flush, WriteableRegion for textures (#2494)
* Return mapped buffer pointer directly for flush, WriteableRegion for textures
A few changes here to generally improve performance, even for platforms not using the persistent buffer flush.
- Texture and buffer flush now return a ReadOnlySpan<byte>. It's guaranteed that this span is pinned in memory, but it will be overwritten on the next flush from that thread, so it is expected that the data is used before calling again.
- As a result, persistent mappings no longer copy to a new array - rather the persistent map is returned directly as a Span<>. A similar host array is used for the glGet flushes instead of allocating new arrays each time.
- Texture flushes now do their layout conversion into a WriteableRegion when the texture is not MultiRange, which allows the flush to happen directly into guest memory rather than into a temporary span, then copied over. This avoids another copy when doing layout conversion.
Overall, this saves 1 data copy for buffer flush, 1 copy for linear textures with matching source/target stride, and 2 copies for block textures or linear textures with mismatching strides.
* Fix tests
* Fix array pointer for Mesa/Intel path
* Address some feedback
* Update method for getting array pointer.
2021-07-20 00:10:54 +02:00
|
|
|
public unsafe ReadOnlySpan<byte> GetData()
|
2019-12-05 21:34:47 +01:00
|
|
|
{
|
2021-07-18 16:45:50 +02:00
|
|
|
int size = 0;
|
2021-11-04 00:58:24 +01:00
|
|
|
int levels = Info.GetLevelsClamped();
|
2021-07-18 16:45:50 +02:00
|
|
|
|
2021-11-04 00:58:24 +01:00
|
|
|
for (int level = 0; level < levels; level++)
|
2021-07-18 16:45:50 +02:00
|
|
|
{
|
|
|
|
size += Info.GetMipSize(level);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (HwCapabilities.UsePersistentBufferForFlush)
|
|
|
|
{
|
|
|
|
return _renderer.PersistentBuffers.Default.GetTextureData(this, size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Return mapped buffer pointer directly for flush, WriteableRegion for textures (#2494)
* Return mapped buffer pointer directly for flush, WriteableRegion for textures
A few changes here to generally improve performance, even for platforms not using the persistent buffer flush.
- Texture and buffer flush now return a ReadOnlySpan<byte>. It's guaranteed that this span is pinned in memory, but it will be overwritten on the next flush from that thread, so it is expected that the data is used before calling again.
- As a result, persistent mappings no longer copy to a new array - rather the persistent map is returned directly as a Span<>. A similar host array is used for the glGet flushes instead of allocating new arrays each time.
- Texture flushes now do their layout conversion into a WriteableRegion when the texture is not MultiRange, which allows the flush to happen directly into guest memory rather than into a temporary span, then copied over. This avoids another copy when doing layout conversion.
Overall, this saves 1 data copy for buffer flush, 1 copy for linear textures with matching source/target stride, and 2 copies for block textures or linear textures with mismatching strides.
* Fix tests
* Fix array pointer for Mesa/Intel path
* Address some feedback
* Update method for getting array pointer.
2021-07-20 00:10:54 +02:00
|
|
|
IntPtr target = _renderer.PersistentBuffers.Default.GetHostArray(size);
|
2021-07-18 16:45:50 +02:00
|
|
|
|
Return mapped buffer pointer directly for flush, WriteableRegion for textures (#2494)
* Return mapped buffer pointer directly for flush, WriteableRegion for textures
A few changes here to generally improve performance, even for platforms not using the persistent buffer flush.
- Texture and buffer flush now return a ReadOnlySpan<byte>. It's guaranteed that this span is pinned in memory, but it will be overwritten on the next flush from that thread, so it is expected that the data is used before calling again.
- As a result, persistent mappings no longer copy to a new array - rather the persistent map is returned directly as a Span<>. A similar host array is used for the glGet flushes instead of allocating new arrays each time.
- Texture flushes now do their layout conversion into a WriteableRegion when the texture is not MultiRange, which allows the flush to happen directly into guest memory rather than into a temporary span, then copied over. This avoids another copy when doing layout conversion.
Overall, this saves 1 data copy for buffer flush, 1 copy for linear textures with matching source/target stride, and 2 copies for block textures or linear textures with mismatching strides.
* Fix tests
* Fix array pointer for Mesa/Intel path
* Address some feedback
* Update method for getting array pointer.
2021-07-20 00:10:54 +02:00
|
|
|
WriteTo(target);
|
2021-07-18 16:45:50 +02:00
|
|
|
|
Return mapped buffer pointer directly for flush, WriteableRegion for textures (#2494)
* Return mapped buffer pointer directly for flush, WriteableRegion for textures
A few changes here to generally improve performance, even for platforms not using the persistent buffer flush.
- Texture and buffer flush now return a ReadOnlySpan<byte>. It's guaranteed that this span is pinned in memory, but it will be overwritten on the next flush from that thread, so it is expected that the data is used before calling again.
- As a result, persistent mappings no longer copy to a new array - rather the persistent map is returned directly as a Span<>. A similar host array is used for the glGet flushes instead of allocating new arrays each time.
- Texture flushes now do their layout conversion into a WriteableRegion when the texture is not MultiRange, which allows the flush to happen directly into guest memory rather than into a temporary span, then copied over. This avoids another copy when doing layout conversion.
Overall, this saves 1 data copy for buffer flush, 1 copy for linear textures with matching source/target stride, and 2 copies for block textures or linear textures with mismatching strides.
* Fix tests
* Fix array pointer for Mesa/Intel path
* Address some feedback
* Update method for getting array pointer.
2021-07-20 00:10:54 +02:00
|
|
|
return new ReadOnlySpan<byte>(target.ToPointer(), size);
|
2021-07-18 16:45:50 +02:00
|
|
|
}
|
2019-12-05 21:34:47 +01:00
|
|
|
}
|
|
|
|
|
2022-01-09 17:28:48 +01:00
|
|
|
public unsafe ReadOnlySpan<byte> GetData(int layer, int level)
|
|
|
|
{
|
|
|
|
int size = Info.GetMipSize(level);
|
|
|
|
|
|
|
|
if (HwCapabilities.UsePersistentBufferForFlush)
|
|
|
|
{
|
|
|
|
return _renderer.PersistentBuffers.Default.GetTextureData(this, size, layer, level);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IntPtr target = _renderer.PersistentBuffers.Default.GetHostArray(size);
|
|
|
|
|
|
|
|
int offset = WriteTo2D(target, layer, level);
|
|
|
|
|
|
|
|
return new ReadOnlySpan<byte>(target.ToPointer(), size).Slice(offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-26 05:03:40 +02:00
|
|
|
public void WriteToPbo(int offset, bool forceBgra)
|
|
|
|
{
|
|
|
|
WriteTo(IntPtr.Zero + offset, forceBgra);
|
|
|
|
}
|
|
|
|
|
2020-11-20 17:30:59 +01:00
|
|
|
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)
|
|
|
|
{
|
2022-01-09 17:28:48 +01:00
|
|
|
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);
|
2020-11-20 17:30:59 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GL.GetTexImage(target, level, pixelFormat, pixelType, data);
|
2022-01-09 17:28:48 +01:00
|
|
|
|
|
|
|
// 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
|
|
|
|
};
|
2020-11-20 17:30:59 +01:00
|
|
|
}
|
|
|
|
|
2022-01-09 17:28:48 +01:00
|
|
|
return 0;
|
2020-11-20 17:30:59 +01:00
|
|
|
}
|
|
|
|
|
2020-07-26 05:03:40 +02:00
|
|
|
private void WriteTo(IntPtr data, bool forceBgra = false)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
|
|
|
TextureTarget target = Target.Convert();
|
|
|
|
|
|
|
|
Bind(target, 0);
|
|
|
|
|
2020-04-25 15:02:18 +02:00
|
|
|
FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2020-07-26 05:03:40 +02:00
|
|
|
PixelFormat pixelFormat = format.PixelFormat;
|
|
|
|
PixelType pixelType = format.PixelType;
|
|
|
|
|
|
|
|
if (forceBgra)
|
|
|
|
{
|
2021-08-26 22:47:21 +02:00
|
|
|
if (pixelType == PixelType.UnsignedShort565)
|
|
|
|
{
|
|
|
|
pixelType = PixelType.UnsignedShort565Reversed;
|
|
|
|
}
|
|
|
|
else if (pixelType == PixelType.UnsignedShort565Reversed)
|
|
|
|
{
|
|
|
|
pixelType = PixelType.UnsignedShort565;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pixelFormat = PixelFormat.Bgra;
|
|
|
|
}
|
2020-07-26 05:03:40 +02:00
|
|
|
}
|
|
|
|
|
2019-12-05 21:34:47 +01:00
|
|
|
int faces = 1;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
if (target == TextureTarget.TextureCubeMap)
|
|
|
|
{
|
2019-12-05 21:34:47 +01:00
|
|
|
target = TextureTarget.TextureCubeMapPositiveX;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2019-12-05 21:34:47 +01:00
|
|
|
faces = 6;
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2021-11-04 00:58:24 +01:00
|
|
|
int levels = Info.GetLevelsClamped();
|
|
|
|
|
|
|
|
for (int level = 0; level < levels; level++)
|
2019-12-05 21:34:47 +01:00
|
|
|
{
|
|
|
|
for (int face = 0; face < faces; face++)
|
|
|
|
{
|
2020-04-25 15:02:18 +02:00
|
|
|
int faceOffset = face * Info.GetMipSize2D(level);
|
2019-12-05 21:34:47 +01:00
|
|
|
|
|
|
|
if (format.IsCompressed)
|
|
|
|
{
|
2020-07-26 05:03:40 +02:00
|
|
|
GL.GetCompressedTexImage(target + face, level, data + faceOffset);
|
2019-12-05 21:34:47 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-07-26 05:03:40 +02:00
|
|
|
GL.GetTexImage(target + face, level, pixelFormat, pixelType, data + faceOffset);
|
2019-12-05 21:34:47 +01:00
|
|
|
}
|
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2020-07-26 05:03:40 +02:00
|
|
|
data += Info.GetMipSize(level);
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-13 00:27:50 +01:00
|
|
|
public void SetData(ReadOnlySpan<byte> data)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
|
|
|
unsafe
|
|
|
|
{
|
|
|
|
fixed (byte* ptr = data)
|
|
|
|
{
|
2020-07-26 05:03:40 +02:00
|
|
|
ReadFrom((IntPtr)ptr, data.Length);
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-02 23:30:54 +01:00
|
|
|
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
|
|
|
{
|
|
|
|
unsafe
|
|
|
|
{
|
|
|
|
fixed (byte* ptr = data)
|
|
|
|
{
|
|
|
|
int width = Math.Max(Info.Width >> level, 1);
|
|
|
|
int height = Math.Max(Info.Height >> level, 1);
|
|
|
|
|
|
|
|
ReadFrom2D((IntPtr)ptr, layer, level, width, height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-26 05:03:40 +02:00
|
|
|
public void ReadFromPbo(int offset, int size)
|
|
|
|
{
|
|
|
|
ReadFrom(IntPtr.Zero + offset, size);
|
|
|
|
}
|
|
|
|
|
2020-11-20 17:30:59 +01:00
|
|
|
public void ReadFromPbo2D(int offset, int layer, int level, int width, int height)
|
|
|
|
{
|
|
|
|
ReadFrom2D(IntPtr.Zero + offset, layer, level, width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ReadFrom2D(IntPtr data, int layer, int level, int width, int height)
|
|
|
|
{
|
|
|
|
TextureTarget target = Target.Convert();
|
|
|
|
|
|
|
|
int mipSize = Info.GetMipSize2D(level);
|
|
|
|
|
|
|
|
Bind(target, 0);
|
|
|
|
|
|
|
|
FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
|
|
|
|
|
|
|
|
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:
|
|
|
|
if (format.IsCompressed)
|
|
|
|
{
|
|
|
|
GL.CompressedTexSubImage2D(
|
|
|
|
target,
|
|
|
|
level,
|
|
|
|
0,
|
|
|
|
layer,
|
|
|
|
width,
|
|
|
|
1,
|
|
|
|
format.PixelFormat,
|
|
|
|
mipSize,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GL.TexSubImage2D(
|
|
|
|
target,
|
|
|
|
level,
|
|
|
|
0,
|
|
|
|
layer,
|
|
|
|
width,
|
|
|
|
1,
|
|
|
|
format.PixelFormat,
|
|
|
|
format.PixelType,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
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,
|
|
|
|
layer,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
1,
|
|
|
|
format.PixelFormat,
|
|
|
|
mipSize,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GL.TexSubImage3D(
|
|
|
|
target,
|
|
|
|
level,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
layer,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
1,
|
|
|
|
format.PixelFormat,
|
|
|
|
format.PixelType,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Target.Cubemap:
|
|
|
|
if (format.IsCompressed)
|
|
|
|
{
|
|
|
|
GL.CompressedTexSubImage2D(
|
|
|
|
TextureTarget.TextureCubeMapPositiveX + layer,
|
|
|
|
level,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
format.PixelFormat,
|
|
|
|
mipSize,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GL.TexSubImage2D(
|
|
|
|
TextureTarget.TextureCubeMapPositiveX + layer,
|
|
|
|
level,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
format.PixelFormat,
|
|
|
|
format.PixelType,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-26 05:03:40 +02:00
|
|
|
private void ReadFrom(IntPtr data, int size)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
|
|
|
TextureTarget target = Target.Convert();
|
2021-08-11 23:19:28 +02:00
|
|
|
int baseLevel = 0;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-08-11 23:19:28 +02:00
|
|
|
// 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);
|
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2020-04-25 15:02:18 +02:00
|
|
|
FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2020-04-25 15:02:18 +02:00
|
|
|
int width = Info.Width;
|
|
|
|
int height = Info.Height;
|
|
|
|
int depth = Info.Depth;
|
2021-11-04 00:58:24 +01:00
|
|
|
int levels = Info.GetLevelsClamped();
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
int offset = 0;
|
|
|
|
|
2021-11-04 00:58:24 +01:00
|
|
|
for (int level = 0; level < levels; level++)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2020-04-25 15:02:18 +02:00
|
|
|
int mipSize = Info.GetMipSize(level);
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
int endOffset = offset + mipSize;
|
|
|
|
|
|
|
|
if ((uint)endOffset > (uint)size)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-11 23:19:28 +02:00
|
|
|
switch (Target)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
|
|
|
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,
|
2021-08-11 23:19:28 +02:00
|
|
|
baseLevel + level,
|
2019-10-13 08:02:07 +02:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
format.PixelFormat,
|
|
|
|
mipSize / 6,
|
|
|
|
data + faceOffset);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GL.TexSubImage2D(
|
|
|
|
TextureTarget.TextureCubeMapPositiveX + face,
|
2021-08-11 23:19:28 +02:00
|
|
|
baseLevel + level,
|
2019-10-13 08:02:07 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-25 15:02:18 +02:00
|
|
|
public void SetStorage(BufferRange buffer)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2020-04-25 15:02:18 +02:00
|
|
|
throw new NotSupportedException();
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2020-09-10 21:44:04 +02:00
|
|
|
private void DisposeHandles()
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
|
|
|
if (Handle != 0)
|
|
|
|
{
|
|
|
|
GL.DeleteTexture(Handle);
|
|
|
|
|
2020-09-10 21:44:04 +02:00
|
|
|
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)
|
|
|
|
{
|
2019-10-13 08:02:07 +02:00
|
|
|
_parent.DecrementViewsCount();
|
2020-09-10 21:44:04 +02:00
|
|
|
}
|
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2020-09-10 21:44:04 +02:00
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
if (_parent.DefaultView == this)
|
|
|
|
{
|
|
|
|
// Remove the default view (us), so that the texture cannot be released to the cache.
|
|
|
|
_parent.DeleteDefault();
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
2020-09-10 21:44:04 +02:00
|
|
|
|
|
|
|
Release();
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|