Skip to main content

grafix_toolbox/kit/opengl/texture/
texture_ext.rs

1use super::{args::*, format::*, *};
2
3#[derive_as_val]
4pub struct TexParam {
5	pub w: i32,
6	pub h: i32,
7	pub d: i32,
8	pub l: i32,
9}
10impl TexParam {
11	pub fn gen_mips(mut self) -> Self {
12		self.l = self.mips_max();
13		self
14	}
15	pub fn size<A>(&self, lvl: A) -> usize
16	where
17		u32: Cast<A>,
18	{
19		let (w, h, d) = ulVec3(self.dim(lvl));
20		w * h * d
21	}
22	pub fn dim<A>(&self, lvl: A) -> iVec3
23	where
24		u32: Cast<A>,
25	{
26		let lvl = u32(lvl);
27		ASSERT!(i32(lvl) < self.l, "GL texture level {lvl} oob, max {}", self.l);
28		self.dim_unchecked(lvl)
29	}
30	pub fn dim_unchecked(&self, lvl: u32) -> iVec3 {
31		let (lvl, Self { w, h, d, .. }) = (u32(lvl), *self);
32		if lvl == 0 {
33			return (w, h, d);
34		}
35		let div = |v| 1.max(i32(f64(v) / f64(2_u32.pow(lvl))));
36		(div(w), div(h), div(d))
37	}
38	pub fn mips_max(&self) -> i32 {
39		let Self { w, h, d, .. } = *self;
40		let w = w.max(h).max(d);
41		1 + i32(f64(w).log2())
42	}
43	pub fn validate(self) -> Self {
44		let (_l, _m) = (self.l, self.mips_max());
45		ASSERT!(_l > 0 && _l <= _m, "GL texture level {_l} unsound, max {}", _m);
46		self
47	}
48}
49
50#[derive(Debug)]
51pub struct Tex<S, F, T: TexType> {
52	t: Dummy<(S, F)>,
53	param: TexParam,
54	tex: Obj<TextureT<T>>,
55	unit: Cell<u32>,
56}
57macro_rules! impl_tex {
58	($t: ty, $dim: ident, $arg_u: ident) => {
59		impl<S: TexSize, F: TexFmt> Tex<S, F, $t> {
60			pub fn none() -> Self {
61				Self::new(1, &<[F; 4]>::to([255, 0, 0, 0])[..S::SIZE])
62			}
63			pub fn new<D>(dimensions: D, args_u: impl $arg_u<F>) -> Self
64			where
65				$dim: Cast<D>,
66			{
67				Self::new_empty(dimensions, 1).tap(|t| t.Update(args_u))
68			}
69			pub fn new_mips<D>(dimensions: D, args_u: impl $arg_u<F>) -> Self
70			where
71				$dim: Cast<D>,
72			{
73				Self::new_empty(dimensions, -1)
74					.tap(|t| t.Update(args_u))
75					.tap(|t| GL!(glGenMipmaps(<$t>::TYPE, t.tex.obj)))
76			}
77			pub fn new_empty<D, M>(dim: D, mip_levels: M) -> Self
78			where
79				$dim: Cast<D>,
80				i16: Cast<M>,
81			{
82				let (fmt, tex, l) = (get_internal_fmt::<S, F>().pipe(normalize_internal_fmt), Obj::new(), i16(mip_levels) as i32);
83				macro_rules! tex_new {
84					(i32) => {{
85						let w = i32(dim);
86						let p = TexParam { w, h: 1, d: 1, l };
87						let p = if l > 0 { p.validate() } else { p.gen_mips() };
88						GL!(glTextureStorage1D(<$t>::TYPE, tex.obj, p.l, fmt, w));
89						p
90					}};
91					(iVec2) => {{
92						let (w, h) = vec2(dim);
93						let p = TexParam { w, h, d: 1, l };
94						let p = if l > 0 { p.validate() } else { p.gen_mips() };
95						GL!(glTextureStorage2D(<$t>::TYPE, tex.obj, p.l, fmt, w, h));
96						p
97					}};
98					(iVec3) => {{
99						let (w, h, d) = vec3(dim);
100						let p = TexParam { w, h, d, l };
101						let p = if l > 0 { p.validate() } else { p.gen_mips() };
102						GL!(glTextureStorage3D(<$t>::TYPE, tex.obj, p.l, fmt, w, h, d));
103						p
104					}};
105				}
106				let param = tex_new!($dim);
107				Self { t: Dummy, param, tex, unit: Def() }
108			}
109			pub fn Update(&mut self, args: impl $arg_u<F>) {
110				self.UpdateCustom::<S, F, _>(args);
111			}
112			pub fn UpdateCustom<RS: TexSize, RF: TexFmt, T: $arg_u<RF>>(&mut self, args: T) {
113				let mip_size = |lvl, _len| {
114					ASSERT!(
115						_len <= self.param.size(u32(lvl)) * S::SIZE,
116						"GL texture data {_len} at level {lvl} oob, len {}",
117						self.param.size(u32(lvl)) * S::SIZE
118					);
119					self.param.dim(lvl)
120				};
121				GL::PixelStoreUnpack::Set(1);
122				macro_rules! tex_new {
123					(UpdArgs1) => {{
124						let (data, lvl, x, len) = args.get1();
125						let (w, ..) = mip_size(lvl, len);
126						GL!(glTextureSubImage1D(<$t>::TYPE, self.tex.obj, lvl, x, w, RS::TYPE, RF::TYPE, data));
127					}};
128					(UpdArgs2) => {{
129						let (data, lvl, x, y, len) = args.get2();
130						let (w, h, _) = mip_size(lvl, len);
131						GL!(glTextureSubImage2D(<$t>::TYPE, self.tex.obj, lvl, x, y, w, h, RS::TYPE, RF::TYPE, data));
132					}};
133					(UpdArgs3) => {{
134						let (data, lvl, x, y, z, len) = args.get3();
135						let (w, h, d) = mip_size(lvl, len);
136						GL!(glTextureSubImage3D(<$t>::TYPE, self.tex.obj, lvl, x, y, z, w, h, d, RS::TYPE, RF::TYPE, data));
137					}};
138				}
139				tex_new!($arg_u);
140			}
141		}
142	};
143}
144impl<S, F, T: TexType> Tex<S, F, T> {
145	pub fn param(&self) -> &TexParam {
146		&self.param
147	}
148	pub fn whdl(&self) -> iVec4 {
149		let TexParam { w, h, d, l } = self.param;
150		vec4((w, h, d, l))
151	}
152	pub fn obj(&self) -> u32 {
153		self.tex.obj
154	}
155	pub fn Save<RS: TexSize, RF: TexFmt>(&self, lvl: u32) -> Box<[RF]> {
156		ASSERT!(T::TYPE != gl::TEXTURE_CUBE_MAP && T::TYPE != gl::TEXTURE_CUBE_MAP_ARRAY, "CUBE NOT IMPL");
157		let size = self.param.size(lvl) * RS::SIZE;
158		let v = vec![Def(); size].into_boxed_slice();
159		let size = i32(size * type_size::<RF>());
160		GL::PixelStorePack::Set(1);
161		GL!(glGetTexture(T::TYPE, self.tex.obj, i32(lvl), RS::TYPE, RF::TYPE, size, v.as_ptr() as *mut GLvoid));
162		v
163	}
164	pub fn Bind<'l>(&'l self, samp: &'l Sampler) -> TextureBind<'l, T> {
165		let unit = self.unit.take();
166		let (b, u) = TextureBind::new(&self.tex, samp, unit);
167		self.unit.set(u);
168		b
169	}
170}
171impl<S, F, T: TexType> Eq for Tex<S, F, T> {}
172impl<S, F, T: TexType> PartialEq for Tex<S, F, T> {
173	fn eq(&self, r: &Self) -> bool {
174		self.tex == r.tex
175	}
176}
177impl_tex!(GL_TEXTURE_1D, i32, UpdArgs1);
178impl_tex!(GL_TEXTURE_2D, iVec2, UpdArgs2);
179impl_tex!(GL_TEXTURE_3D, iVec3, UpdArgs3);
180impl_tex!(GL_TEXTURE_1D_ARRAY, iVec2, UpdArgs2);
181impl_tex!(GL_TEXTURE_2D_ARRAY, iVec3, UpdArgs3);
182impl_tex!(GL_TEXTURE_CUBE_MAP, iVec2, UpdArgs3);
183impl_tex!(GL_TEXTURE_CUBE_MAP_ARRAY, iVec3, UpdArgs3);
184
185#[derive(Debug)]
186pub struct TextureBind<'l, T> {
187	t: Dummy<&'l T>,
188	pub u: u32,
189}
190impl<'l, T: TexType> TextureBind<'l, T> {
191	fn new(o: &'l Obj<TextureT<T>>, s: &'l Sampler, hint: u32) -> (Self, u32) {
192		let u = TexState::Bind::<T>(o.obj, s.0.obj, hint);
193		(Self { t: Dummy, u }, u)
194	}
195}
196impl<T: TexType> Clone for TextureBind<'_, T> {
197	fn clone(&self) -> Self {
198		let Self { t, u } = *self;
199		TexState::Clone(u);
200		Self { t, u }
201	}
202}
203impl<T> Drop for TextureBind<'_, T> {
204	fn drop(&mut self) {
205		TexState::Unbind(self.u);
206	}
207}