258 lines
10 KiB
C#
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()
|
|
};
|
|
}
|
|
}
|
|
}
|