Also did some Refactoring for Classes that implement and use INativeBuffer. The memory usage reduction for rendertargets resulted in some refactoring of the directx texture classes. Also added DebugNames for some DirectX resources in case of a debug build.
339 lines
8.6 KiB
C#
339 lines
8.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using ANX.Framework;
|
|
using ANX.Framework.Graphics;
|
|
using ANX.Framework.NonXNA.RenderSystem;
|
|
using ANX.RenderSystem.GL3.Helpers;
|
|
using OpenTK.Graphics.OpenGL;
|
|
|
|
// This file is part of the ANX.Framework created by the
|
|
// "ANX.Framework developer group" and released under the Ms-PL license.
|
|
// For details see: http://anxframework.codeplex.com/license
|
|
|
|
namespace ANX.RenderSystem.GL3
|
|
{
|
|
public class Texture2DGL3 : INativeTexture2D
|
|
{
|
|
#region Private
|
|
private PixelInternalFormat nativeFormat;
|
|
|
|
/// <summary>
|
|
/// [1-n]
|
|
/// </summary>
|
|
private int numberOfMipMaps;
|
|
|
|
private int width;
|
|
private int height;
|
|
private bool isCompressed;
|
|
internal bool IsDisposed;
|
|
private int uncompressedDataSize;
|
|
private byte[] texData;
|
|
private int maxSetDataSize;
|
|
#endregion
|
|
|
|
#region Public
|
|
protected internal int NativeHandle { get; protected set; }
|
|
#endregion
|
|
|
|
#region Constructor
|
|
internal Texture2DGL3()
|
|
{
|
|
GraphicsResourceManager.UpdateResource(this, true);
|
|
}
|
|
|
|
internal Texture2DGL3(SurfaceFormat surfaceFormat, int setWidth, int setHeight, int mipCount)
|
|
{
|
|
GraphicsResourceManager.UpdateResource(this, true);
|
|
|
|
width = setWidth;
|
|
height = setHeight;
|
|
numberOfMipMaps = mipCount;
|
|
nativeFormat = DatatypesMapping.SurfaceToPixelInternalFormat(surfaceFormat);
|
|
isCompressed = nativeFormat.ToString().StartsWith("Compressed");
|
|
|
|
uncompressedDataSize = GetUncompressedDataSize();
|
|
|
|
CreateTexture();
|
|
}
|
|
|
|
~Texture2DGL3()
|
|
{
|
|
GraphicsResourceManager.UpdateResource(this, false);
|
|
}
|
|
#endregion
|
|
|
|
#region CreateTexture
|
|
private void CreateTexture()
|
|
{
|
|
NativeHandle = GL.GenTexture();
|
|
GL.BindTexture(TextureTarget.Texture2D, NativeHandle);
|
|
|
|
int wrapMode = (int)All.ClampToEdge;
|
|
All minFilter = numberOfMipMaps > 1 ? All.LinearMipmapLinear : All.Linear;
|
|
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, wrapMode);
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, wrapMode);
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)minFilter);
|
|
#if DEBUG
|
|
ErrorHelper.Check("TexParameter");
|
|
#endif
|
|
}
|
|
#endregion
|
|
|
|
// TODO: offsetInBytes
|
|
// TODO: elementCount
|
|
#region SetData
|
|
public void SetData<T>(T[] data) where T : struct
|
|
{
|
|
SetData<T>(0, data, 0, data.Length);
|
|
}
|
|
|
|
public void SetData<T>(T[] data, int startIndex, int elementCount) where T : struct
|
|
{
|
|
SetData<T>(0, data, 0, data.Length);
|
|
}
|
|
|
|
public void SetData<T>(int level, Rectangle? rect, T[] data, int startIndex, int elementCount) where T : struct
|
|
{
|
|
int size = Marshal.SizeOf(typeof(T)) * data.Length;
|
|
if (size > maxSetDataSize)
|
|
maxSetDataSize = size;
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, NativeHandle);
|
|
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
|
|
|
try
|
|
{
|
|
IntPtr dataPointer = handle.AddrOfPinnedObject();
|
|
// Go to the starting point.
|
|
dataPointer += startIndex;
|
|
|
|
int mipmapWidth = Math.Max(width >> level, 1);
|
|
int mipmapHeight = Math.Max(height >> level, 1);
|
|
|
|
if (isCompressed)
|
|
{
|
|
GL.CompressedTexImage2D(TextureTarget.Texture2D, level, nativeFormat, width, height, 0, data.Length,
|
|
dataPointer);
|
|
#if DEBUG
|
|
ErrorHelper.Check("CompressedTexImage2D Format=" + nativeFormat);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
GL.TexImage2D(TextureTarget.Texture2D, level, nativeFormat, mipmapWidth, mipmapHeight, 0,
|
|
(PixelFormat)nativeFormat, PixelType.UnsignedByte, dataPointer);
|
|
#if DEBUG
|
|
ErrorHelper.Check("TexImage2D Format=" + nativeFormat);
|
|
#endif
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
handle.Free();
|
|
}
|
|
}
|
|
|
|
public void SetData<T>(int offsetInBytes, T[] data, int startIndex, int elementCount) where T : struct
|
|
{
|
|
int size = Marshal.SizeOf(typeof(T)) * data.Length;
|
|
if (size > maxSetDataSize)
|
|
maxSetDataSize = size;
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, NativeHandle);
|
|
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
|
|
|
try
|
|
{
|
|
IntPtr dataPointer = handle.AddrOfPinnedObject();
|
|
dataPointer += startIndex;
|
|
|
|
if (isCompressed)
|
|
{
|
|
GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, nativeFormat, width, height, 0, data.Length, dataPointer);
|
|
#if DEBUG
|
|
ErrorHelper.Check("CompressedTexImage2D Format=" + nativeFormat);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
GL.TexImage2D(TextureTarget.Texture2D, 0, nativeFormat, width, height, 0, (PixelFormat)nativeFormat,
|
|
PixelType.UnsignedByte, dataPointer);
|
|
#if DEBUG
|
|
ErrorHelper.Check("TexImage2D Format=" + nativeFormat);
|
|
#endif
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
handle.Free();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region GetData (TODO)
|
|
public void GetData<T>(int level, Rectangle? rect, T[] data, int startIndex, int elementCount) where T : struct
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
#endregion
|
|
|
|
#region GetData
|
|
public void GetData<T>(T[] data) where T : struct
|
|
{
|
|
GetData(data, 0, data.Length);
|
|
}
|
|
#endregion
|
|
|
|
// TODO: compressed texture (see: http://www.bearisgaming.com/texture2d-getdata-and-dxt-compression/)
|
|
// TODO: elementCount
|
|
#region GetData
|
|
public void GetData<T>(T[] data, int startIndex, int elementCount) where T : struct
|
|
{
|
|
if (isCompressed)
|
|
throw new NotImplementedException("GetData is currently not implemented for compressed texture format " +
|
|
nativeFormat + ".");
|
|
|
|
if (data == null)
|
|
throw new ArgumentNullException("data");
|
|
|
|
int size = Marshal.SizeOf(typeof(T)) * data.Length;
|
|
|
|
if (size < uncompressedDataSize)
|
|
throw new InvalidDataException("The size of the data passed in is too large or too small for this resource.");
|
|
|
|
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
|
IntPtr ptr = handle.AddrOfPinnedObject();
|
|
ptr += startIndex;
|
|
|
|
GL.GetTexImage(TextureTarget.Texture2D, 0, (PixelFormat)nativeFormat, PixelType.UnsignedByte, ptr);
|
|
|
|
handle.Free();
|
|
}
|
|
#endregion
|
|
|
|
#region GetTextureData
|
|
private void GetTextureData()
|
|
{
|
|
GL.BindTexture(TextureTarget.Texture2D, NativeHandle);
|
|
|
|
if (isCompressed)
|
|
{
|
|
texData = new byte[maxSetDataSize];
|
|
GCHandle handle = GCHandle.Alloc(texData, GCHandleType.Pinned);
|
|
|
|
GL.GetCompressedTexImage(TextureTarget.Texture2D, 0, handle.AddrOfPinnedObject());
|
|
|
|
handle.Free();
|
|
}
|
|
else
|
|
{
|
|
texData = new byte[uncompressedDataSize];
|
|
GCHandle handle = GCHandle.Alloc(texData, GCHandleType.Pinned);
|
|
|
|
GL.GetTexImage(TextureTarget.Texture2D, 0, (PixelFormat)nativeFormat, PixelType.UnsignedByte,
|
|
handle.AddrOfPinnedObject());
|
|
|
|
handle.Free();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region RecreateData
|
|
internal void RecreateData()
|
|
{
|
|
CreateTexture();
|
|
SetData(texData);
|
|
texData = null;
|
|
}
|
|
#endregion
|
|
|
|
#region GetUncompressedDataSize
|
|
private int GetUncompressedDataSize()
|
|
{
|
|
int size = width * height;
|
|
|
|
switch (nativeFormat)
|
|
{
|
|
default:
|
|
case PixelInternalFormat.R32f:
|
|
case PixelInternalFormat.Rgb10A2:
|
|
case PixelInternalFormat.Rg16:
|
|
case PixelInternalFormat.Rgba:
|
|
size *= 4;
|
|
break;
|
|
|
|
case PixelInternalFormat.Rg32f:
|
|
case PixelInternalFormat.Rgba16f:
|
|
size *= 8;
|
|
break;
|
|
|
|
case PixelInternalFormat.R16f:
|
|
case PixelInternalFormat.Rgb5A1:
|
|
case PixelInternalFormat.Rgba4:
|
|
size *= 2;
|
|
break;
|
|
|
|
case PixelInternalFormat.Alpha8:
|
|
//size *= 1;
|
|
break;
|
|
|
|
case PixelInternalFormat.Rgba32f:
|
|
size *= 16;
|
|
break;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
#endregion
|
|
|
|
#region SaveAsJpeg (TODO)
|
|
public void SaveAsJpeg(Stream stream, int width, int height)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
#endregion
|
|
|
|
#region SaveAsPng (TODO)
|
|
public void SaveAsPng(Stream stream, int width, int height)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
#endregion
|
|
|
|
#region Dispose
|
|
/// <summary>
|
|
/// Dispose the native OpenGL texture handle.
|
|
/// </summary>
|
|
public virtual void Dispose()
|
|
{
|
|
if (IsDisposed == false)
|
|
{
|
|
IsDisposed = true;
|
|
DisposeResource();
|
|
}
|
|
}
|
|
|
|
internal void DisposeResource()
|
|
{
|
|
if (IsDisposed == false)
|
|
{
|
|
GetTextureData();
|
|
}
|
|
|
|
if (NativeHandle != -1 &&
|
|
GraphicsDeviceWindowsGL3.IsContextCurrent)
|
|
{
|
|
GL.DeleteTexture(NativeHandle);
|
|
NativeHandle = -1;
|
|
#if DEBUG
|
|
ErrorHelper.Check("DeleteTexture");
|
|
#endif
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|