Dashboard/Dashboard.Drawing.OpenGL/Pal/GLTextureExtension.cs

258 lines
10 KiB
C#

using System.Drawing;
using Dashboard.Pal;
using OpenTK.Graphics.OpenGL;
using OGL = OpenTK.Graphics.OpenGL;
namespace Dashboard.Drawing.OpenGL.Pal
{
public class GLTextureExtension : ITextureExtension, IContextExtensionBase<GLDeviceContext>
{
public string DriverName => "Dashboard OpenGL Texture Extension";
public string DriverVendor => "Dashboard";
public Version DriverVersion => new Version(0, 1, 0);
public GLDeviceContext Context { get; private set; } = null!;
public bool SupportsArbTextureStorage { get; private set; }
public bool SupportsAnisotropy { get; private set; }
IContextBase IContextExtensionBase.Context => Context;
DeviceContext IContextExtensionBase<DeviceContext>.Context => Context;
private List<GLTexture> _textures = new List<GLTexture>();
public void Dispose()
{
throw new NotImplementedException();
}
public void Require(GLDeviceContext context)
{
Context = context;
SupportsArbTextureStorage = Context.DriverVersion >= new Version(4, 2) ||
Context.IsGLExtensionAvailable("GL_ARB_texture_storage");
SupportsAnisotropy = Context.DriverVersion >= new Version() ||
Context.IsGLExtensionAvailable("GL_EXT_texture_filter_anisotropic") ||
Context.IsGLExtensionAvailable("GL_ARB_texture_filter_anisotropic");
}
public void Require(DeviceContext context) => Require((GLDeviceContext)context);
public void Require(IContextBase context) => Require((GLDeviceContext)context);
public GLTexture CreateTexture(TextureType type)
{
GLTexture texture = new GLTexture(this, type);
lock (_textures) _textures.Add(texture);
return texture;
}
internal void TextureDisposed(GLTexture texture)
{
lock (_textures) _textures.Remove(texture);
}
ITexture ITextureExtension.CreateTexture(TextureType type) => CreateTexture(type);
}
public class GLTexture(GLTextureExtension extension, TextureType type) : ITexture
{
public int Handle { get; private set; } = 0;
public bool IsValid => Handle != 0;
public TextureType Type { get; } = type;
public PixelFormat Format { get; private set; } = PixelFormat.Rgba8I;
public ColorSwizzle Swizzle { get; set; } = ColorSwizzle.Default;
public TextureFilter MinifyFilter { get; set; } = TextureFilter.Linear;
public TextureFilter MagnifyFilter { get; set; } = TextureFilter.Linear;
public Color BorderColor { get; set; } = Color.White;
public TextureRepeat RepeatS { get; set; } = TextureRepeat.Repeat;
public TextureRepeat RepeatT { get; set; } = TextureRepeat.Repeat;
public TextureRepeat RepeatR { get; set; } = TextureRepeat.Repeat;
public int Anisotropy { get; set; } = 0;
public int Width { get; private set; } = 0;
public int Height { get; private set; } = 0;
public int Depth { get; private set; } = 0;
public int Levels { get; private set; } = 0;
public bool Premultiplied { get; set; } = false;
private TextureTarget Target { get; } = type switch
{
TextureType.Texture1D => TextureTarget.Texture1d,
TextureType.Texture2D => TextureTarget.Texture2d,
TextureType.Texture3D => TextureTarget.Texture3d,
TextureType.Texture2DArray => TextureTarget.Texture2dArray,
TextureType.Texture2DCube => TextureTarget.TextureCubeMap,
_ => throw new NotSupportedException()
};
private GLTextureExtension Extension { get; } = extension;
private GLDeviceContext Context => Extension.Context;
public void SetStorage(PixelFormat format, int width, int height, int depth, int levels)
{
if (!Context.IsRenderThread)
{
Context.InvokeBeforeDraw(() => SetStorage(format, width, height, depth, levels)).Wait();
return;
}
Bind();
SizedInternalFormat glFormat = GetFormat(format);
if (Extension.SupportsArbTextureStorage)
{
switch (Type)
{
case TextureType.Texture1D:
GL.TexStorage1D(Target, levels, glFormat, width);
break;
case TextureType.Texture2D:
GL.TexStorage2D(Target, levels, glFormat, width, height);
break;
case TextureType.Texture3D:
case TextureType.Texture2DArray:
case TextureType.Texture2DCube:
GL.TexStorage3D(Target, levels, glFormat, width, height, depth);
break;
}
}
else
{
switch (Type)
{
case TextureType.Texture1D:
GL.TexImage1D(Target, 0, (InternalFormat)glFormat, width, 0, (OGL.PixelFormat)glFormat, PixelType.Byte, IntPtr.Zero);
break;
case TextureType.Texture2D:
GL.TexImage2D(Target, 0, (InternalFormat)glFormat, width, height, 0, (OGL.PixelFormat)glFormat, PixelType.Byte, IntPtr.Zero);
break;
case TextureType.Texture3D:
case TextureType.Texture2DArray:
case TextureType.Texture2DCube:
GL.TexImage3D(Target, 0, (InternalFormat)glFormat, width, height, depth, 0, (OGL.PixelFormat)glFormat, PixelType.Byte, IntPtr.Zero);
break;
}
}
}
public void Read<T>(Span<T> buffer, int level = 0, int align = 0) where T : unmanaged
{
throw new NotImplementedException();
}
public unsafe void Write<T>(PixelFormat format, ReadOnlySpan<T> buffer, int level = 0, int align = 4) where T : unmanaged
{
if (!Context.IsRenderThread)
{
throw new NotImplementedException();
}
Bind();
OGL::PixelFormat glFormat = format switch
{
PixelFormat.R8I or PixelFormat.R16F => OGL.PixelFormat.Red,
PixelFormat.Rg8I or PixelFormat.Rg16F => OGL.PixelFormat.Rg,
PixelFormat.Rgb8I or PixelFormat.Rgb16F => OGL.PixelFormat.Rgb,
PixelFormat.Rgba8I or PixelFormat.Rgba16F => OGL.PixelFormat.Rgba,
_ => throw new NotSupportedException()
};
PixelType glType = format switch
{
PixelFormat.R8I or PixelFormat.Rg8I or PixelFormat.Rgb8I or PixelFormat.Rgba8I => PixelType.Byte,
PixelFormat.R16F or PixelFormat.Rg16F or PixelFormat.Rgb16F or PixelFormat.Rgba16F => PixelType.HalfFloat,
_ => throw new NotSupportedException()
};
GL.PixelStorei(PixelStoreParameter.UnpackAlignment, align);
fixed (T* ptr = buffer)
{
switch (Type)
{
case TextureType.Texture1D:
GL.TexSubImage1D(Target, level, 0, Width, glFormat, glType, ptr);
break;
case TextureType.Texture2D:
GL.TexSubImage2D(Target, level, 0, 0, Width, Height, glFormat, glType, ptr);
break;
case TextureType.Texture2DCube:
case TextureType.Texture3D:
case TextureType.Texture2DArray:
GL.TexSubImage3D(Target, level, 0, 0, 0, Width, Height, Depth, glFormat, glType, ptr);
break;
}
}
}
public void Premultiply()
{
throw new NotImplementedException();
}
public void Unmultiply()
{
throw new NotImplementedException();
}
public void GenerateMipmaps()
{
if (!Context.IsRenderThread)
{
Context.InvokeBeforeDraw(GenerateMipmaps).Wait();
return;
}
Bind();
GL.GenerateMipmap(Target);
}
public void Dispose()
{
throw new NotImplementedException();
}
private void Bind()
{
if (Handle == 0)
{
Handle = GL.GenTexture();
}
GL.BindTexture(Target, Handle);
}
private static SizedInternalFormat GetFormat(PixelFormat format)
{
return format switch
{
PixelFormat.R8I => SizedInternalFormat.R8,
PixelFormat.R16F => SizedInternalFormat.R16f,
PixelFormat.Rg8I => SizedInternalFormat.Rg8,
PixelFormat.Rg16F => SizedInternalFormat.Rg16f,
PixelFormat.Rgb8I => SizedInternalFormat.Rgb8,
PixelFormat.Rgb16F => SizedInternalFormat.Rgb16f,
PixelFormat.Rgba8I => SizedInternalFormat.Rgba8,
PixelFormat.Rgba16F => SizedInternalFormat.Rgba16f,
_ => throw new ArgumentOutOfRangeException()
};
}
private static PixelFormat GetFormat(SizedInternalFormat format)
{
return format switch
{
SizedInternalFormat.R8 => PixelFormat.R8I,
SizedInternalFormat.R16f => PixelFormat.R16F,
SizedInternalFormat.Rg8 => PixelFormat.Rg8I,
SizedInternalFormat.Rg16f => PixelFormat.Rg16F,
SizedInternalFormat.Rgb8 => PixelFormat.Rgb8I,
SizedInternalFormat.Rgb16f => PixelFormat.Rgb16F,
SizedInternalFormat.Rgba8 => PixelFormat.Rgba8I,
SizedInternalFormat.Rgba16f => PixelFormat.Rgba16F,
_ => throw new ArgumentOutOfRangeException()
};
}
}
}