use super::*;
struct GlTextureFormat {
internal_format: GLenum,
format: GLenum,
type_: GLenum,
align: GLint,
}
impl GlTextureFormat {
fn get(format: crate::TextureFormat, config: &GlConfig) -> GlTextureFormat {
let (internal_format, format, type_, align) = if config.srgb {
match format {
crate::TextureFormat::RGBA8 => (gl::RGBA8, gl::RGBA, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::RGB8 => (gl::RGB8, gl::RGB, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::RG8 => (gl::RG8, gl::RG, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::R8 => (gl::R8, gl::RED, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::RGBA32F => (gl::RGBA32F, gl::RGBA, gl::FLOAT, 4),
crate::TextureFormat::RGB32F => (gl::RGB32F, gl::RGB, gl::FLOAT, 4),
crate::TextureFormat::RG32F => (gl::RG32F, gl::RG, gl::FLOAT, 4),
crate::TextureFormat::R32F => (gl::R32F, gl::RED, gl::FLOAT, 4),
crate::TextureFormat::SRGBA8 => (gl::SRGB8_ALPHA8, gl::RGBA, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::SRGB8 => (gl::SRGB8, gl::RGB, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::Depth16 => (gl::DEPTH_COMPONENT16, gl::DEPTH_COMPONENT, gl::UNSIGNED_SHORT, 2),
crate::TextureFormat::Depth24 => (gl::DEPTH_COMPONENT24, gl::DEPTH_COMPONENT, gl::UNSIGNED_INT, 4),
crate::TextureFormat::Depth32F => (gl::DEPTH_COMPONENT32F, gl::DEPTH_COMPONENT, gl::FLOAT, 4),
crate::TextureFormat::Depth24Stencil8 => (gl::DEPTH24_STENCIL8, gl::DEPTH_STENCIL, gl::UNSIGNED_INT_24_8, 4),
}
}
else {
match format {
crate::TextureFormat::RGBA8 => (gl::RGBA8, gl::RGBA, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::RGB8 => (gl::RGB8, gl::RGB, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::RG8 => (gl::RG8, gl::RG, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::R8 => (gl::R8, gl::RED, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::RGBA32F => (gl::RGBA32F, gl::RGBA, gl::FLOAT, 4),
crate::TextureFormat::RGB32F => (gl::RGB32F, gl::RGB, gl::FLOAT, 4),
crate::TextureFormat::RG32F => (gl::RG32F, gl::RG, gl::FLOAT, 4),
crate::TextureFormat::R32F => (gl::R32F, gl::RED, gl::FLOAT, 4),
crate::TextureFormat::SRGBA8 => (gl::RGBA8, gl::RGBA, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::SRGB8 => (gl::RGB8, gl::RGB, gl::UNSIGNED_BYTE, 1),
crate::TextureFormat::Depth16 => (gl::DEPTH_COMPONENT16, gl::DEPTH_COMPONENT, gl::UNSIGNED_SHORT, 2),
crate::TextureFormat::Depth24 => (gl::DEPTH_COMPONENT24, gl::DEPTH_COMPONENT, gl::UNSIGNED_INT, 4),
crate::TextureFormat::Depth32F => (gl::DEPTH_COMPONENT32F, gl::DEPTH_COMPONENT, gl::FLOAT, 4),
crate::TextureFormat::Depth24Stencil8 => (gl::DEPTH24_STENCIL8, gl::DEPTH_STENCIL, gl::UNSIGNED_INT_24_8, 4),
}
};
GlTextureFormat { internal_format, format, type_, align }
}
}
fn gl_texture_wrap(wrap: crate::TextureWrap) -> GLint {
(match wrap {
crate::TextureWrap::Edge => gl::CLAMP_TO_EDGE,
crate::TextureWrap::Border => gl::CLAMP_TO_BORDER,
crate::TextureWrap::Repeat => gl::REPEAT,
crate::TextureWrap::Mirror => gl::MIRRORED_REPEAT,
}) as GLint
}
fn gl_texture_filter_mag(filter: crate::TextureFilter) -> GLint {
(match filter {
crate::TextureFilter::Nearest => gl::NEAREST,
crate::TextureFilter::Linear => gl::LINEAR,
}) as GLint
}
fn gl_texture_filter_min(props: &crate::TextureProps) -> GLint {
(if props.mip_levels > 1 {
match props.filter_min {
crate::TextureFilter::Nearest => gl::NEAREST_MIPMAP_NEAREST,
crate::TextureFilter::Linear => gl::LINEAR_MIPMAP_LINEAR,
}
}
else {
match props.filter_min {
crate::TextureFilter::Nearest => gl::NEAREST,
crate::TextureFilter::Linear => gl::LINEAR,
}
}) as GLint
}
pub fn create(this: &mut GlGraphics, info: &crate::Texture2DInfo) -> crate::Texture2D {
let mut texture = 0;
gl_check!(gl::GenTextures(1, &mut texture));
gl_check!(gl::BindTexture(gl::TEXTURE_2D, texture));
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl_texture_wrap(info.props.wrap_u)));
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl_texture_wrap(info.props.wrap_v)));
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl_texture_filter_mag(info.props.filter_mag)));
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl_texture_filter_min(&info.props)));
if let Some(compare) = info.props.compare {
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_COMPARE_MODE, gl::COMPARE_REF_TO_TEXTURE as GLint));
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_COMPARE_FUNC, gl_depth_func(compare) as GLint));
}
else {
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_COMPARE_MODE, gl::NONE as GLint));
}
gl_check!(gl::TexParameterfv(gl::TEXTURE_2D, gl::TEXTURE_BORDER_COLOR, info.props.border_color.as_ptr()));
let GlTextureFormat { internal_format, .. } = GlTextureFormat::get(info.format, &this.config);
gl_check!(gl::TexStorage2D(gl::TEXTURE_2D, info.props.mip_levels as GLsizei, internal_format, info.width, info.height));
gl_check!(gl::BindTexture(gl::TEXTURE_2D, 0));
this.objects.insert(GlTexture2D { texture, info: *info })
}
pub fn get_info(this: &GlGraphics, id: crate::Texture2D) -> Option<&crate::Texture2DInfo> {
let Some(tex2d) = this.objects.get_texture2d(id) else { return None };
return Some(&tex2d.info);
}
pub fn generate_mipmap(this: &mut GlGraphics, id: crate::Texture2D) {
let Some(tex2d) = this.objects.get_texture2d(id) else { return };
gl_check!(gl::BindTexture(gl::TEXTURE_2D, tex2d.texture));
gl_check!(gl::GenerateMipmap(gl::TEXTURE_2D));
gl_check!(gl::BindTexture(gl::TEXTURE_2D, 0));
}
pub fn update(this: &mut GlGraphics, id: crate::Texture2D, info: &crate::Texture2DInfo) -> crate::Texture2D {
if id == crate::Texture2D::INVALID {
return create(this, info);
}
let Some(tex2d) = this.objects.get_texture2d_mut(id) else {
return crate::Texture2D::INVALID;
};
if &tex2d.info == info {
return id;
}
let realloc = tex2d.info.width != info.width
|| tex2d.info.height != info.height
|| tex2d.info.format != info.format
|| tex2d.info.props.mip_levels != info.props.mip_levels;
if realloc {
gl_check!(gl::DeleteTextures(1, &tex2d.texture));
gl_check!(gl::GenTextures(1, &mut tex2d.texture));
}
gl_check!(gl::BindTexture(gl::TEXTURE_2D, tex2d.texture));
if realloc {
let GlTextureFormat { internal_format, .. } = GlTextureFormat::get(info.format, &this.config);
gl_check!(gl::TexStorage2D(gl::TEXTURE_2D, info.props.mip_levels as GLsizei, internal_format, info.width, info.height));
}
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl_texture_wrap(info.props.wrap_u)));
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl_texture_wrap(info.props.wrap_v)));
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl_texture_filter_mag(info.props.filter_mag)));
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl_texture_filter_min(&info.props)));
if let Some(compare) = info.props.compare {
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_COMPARE_MODE, gl::COMPARE_REF_TO_TEXTURE as GLint));
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_COMPARE_FUNC, gl_depth_func(compare) as GLint));
}
else {
gl_check!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_COMPARE_MODE, gl::NONE as GLint));
}
gl_check!(gl::TexParameterfv(gl::TEXTURE_2D, gl::TEXTURE_BORDER_COLOR, info.props.border_color.as_ptr()));
gl_check!(gl::BindTexture(gl::TEXTURE_2D, 0));
tex2d.info = *info;
return id;
}
pub fn write(this: &mut GlGraphics, id: crate::Texture2D, level: u8, data: &[u8]) {
let Some(texture) = this.objects.get_texture2d(id) else { return };
assert!(level < texture.info.props.mip_levels, "Invalid mip level {}", level);
assert!(texture.info.props.usage.has(crate::TextureUsage::WRITE), "Texture was not created with WRITE usage");
this.metrics.bytes_uploaded = usize::wrapping_add(this.metrics.bytes_uploaded, data.len());
gl_check!(gl::BindTexture(gl::TEXTURE_2D, texture.texture));
let GlTextureFormat { format, type_, align, .. } = GlTextureFormat::get(texture.info.format, &this.config);
let (w, h, expected_size) = texture.info.mip_size(level);
assert_eq!(data.len(), expected_size, "Data size does not match texture mip dimensions");
gl_check!(gl::PixelStorei(gl::UNPACK_ALIGNMENT, align)); gl_check!(gl::TexSubImage2D(gl::TEXTURE_2D, level as GLint, 0, 0, w, h, format, type_, data.as_ptr() as *const _));
gl_check!(gl::BindTexture(gl::TEXTURE_2D, 0));
}
pub fn read_into(this: &mut GlGraphics, id: crate::Texture2D, level: u8, data: &mut [u8]) {
let Some(texture) = this.objects.get_texture2d(id) else { return };
assert!(level < texture.info.props.mip_levels, "Invalid mip level {}", level);
assert!(texture.info.props.usage.has(crate::TextureUsage::READBACK), "Texture was not created with READBACK usage");
this.metrics.bytes_downloaded = usize::wrapping_add(this.metrics.bytes_downloaded, data.len());
gl_check!(gl::BindTexture(gl::TEXTURE_2D, texture.texture));
let GlTextureFormat { format, type_, align, .. } = GlTextureFormat::get(texture.info.format, &this.config);
let (_w, _h, expected_size) = texture.info.mip_size(level);
assert_eq!(data.len(), expected_size, "Data size does not match texture mip dimensions");
gl_check!(gl::PixelStorei(gl::PACK_ALIGNMENT, align)); gl_check!(gl::GetTexImage(gl::TEXTURE_2D, level as GLint, format, type_, data.as_mut_ptr() as *mut _));
gl_check!(gl::BindTexture(gl::TEXTURE_2D, 0));
}
pub fn release(texture: &GlTexture2D) {
gl_check!(gl::DeleteTextures(1, &texture.texture));
}