grafix_toolbox/kit/opengl/texture/
texture_ext.rs

1use super::{args::*, format::*, spec::*, *};
2
3derive_common_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<T>(self, lvl: T) -> usize
16	where
17		u32: Cast<T>,
18	{
19		let (w, h, d) = ulVec3(self.dim(lvl));
20		w * h * d
21	}
22	pub fn dim<T>(self, lvl: T) -> iVec3
23	where
24		u32: Cast<T>,
25	{
26		let lvl = u32(lvl);
27		ASSERT!(i32(lvl) < self.l, "Texture level {lvl} out of bounds, only has {} levels", 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))).floor()));
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, "Texture dimensions allow 1 to {} levels, asked for {_l}", _m);
46		self
47	}
48}
49
50#[derive(Default, Debug)]
51pub struct Tex<S, F, T: TexType> {
52	pub param: TexParam,
53	tex: Object<Texture<T>>,
54	unit: Cell<u32>,
55	s: Dummy<S>,
56	f: Dummy<F>,
57}
58macro_rules! impl_tex {
59	($t: ty, $dim: ident, $arg_u: ident) => {
60		impl<S: TexSize, F: TexFmt> Tex<S, F, $t> {
61			pub fn new<D>(dimensions: D, args_u: impl $arg_u<F>) -> Self
62			where
63				$dim: Cast<D>,
64			{
65				let mut tex = Self::new_empty(dimensions, 1);
66				tex.Update(args_u);
67				tex
68			}
69			pub fn new_mips<D>(dimensions: D, args_u: impl $arg_u<F>) -> Self
70			where
71				$dim: Cast<D>,
72			{
73				let mut tex = Self::new_empty(dimensions, -1);
74				tex.Update(args_u);
75				GL!(glGenMipmaps(<$t>::TYPE, tex.tex.obj));
76				tex
77			}
78			pub fn new_empty<D, M>(dim: D, mip_levels: M) -> Self
79			where
80				$dim: Cast<D>,
81				i16: Cast<M>,
82			{
83				let (tex, f, s, l) = (Object::new(), Dummy, Dummy, i16(mip_levels) as i32);
84				let fmt = normalize_internal_fmt(get_internal_fmt::<S, F>());
85				macro_rules! tex_new {
86					(i32) => {{
87						let w = i32(dim);
88						let p = TexParam { w, h: 1, d: 1, l };
89						let p = if l > 0 { p.validate() } else { p.gen_mips() };
90						GL!(glTextureStorage1D(<$t>::TYPE, tex.obj, p.l, fmt, w));
91						p
92					}};
93					(iVec2) => {{
94						let (w, h) = iVec2(dim);
95						let p = TexParam { w, h, d: 1, l };
96						let p = if l > 0 { p.validate() } else { p.gen_mips() };
97						GL!(glTextureStorage2D(<$t>::TYPE, tex.obj, p.l, fmt, w, h));
98						p
99					}};
100					(iVec3) => {{
101						let (w, h, d) = iVec3(dim);
102						let p = TexParam { w, h, d, l };
103						let p = if l > 0 { p.validate() } else { p.gen_mips() };
104						GL!(glTextureStorage3D(<$t>::TYPE, tex.obj, p.l, fmt, w, h, d));
105						p
106					}};
107				}
108				let param = tex_new!($dim);
109				Self { param, tex, unit: Def(), s, f }
110			}
111			pub fn Update(&mut self, args: impl $arg_u<F>) {
112				self.UpdateCustom::<S, F, _>(args);
113			}
114			pub fn UpdateCustom<RS: TexSize, RF: TexFmt, T: $arg_u<RF>>(&mut self, args: T) {
115				let mip_size = |lvl, _len| {
116					ASSERT!(
117						_len <= self.param.size(u32(lvl)) * usize(S::SIZE),
118						"Texture data out of bounds at level {lvl}, size should be {}, given {_len}",
119						self.param.size(u32(lvl)) * usize(S::SIZE)
120					);
121					self.param.dim(lvl)
122				};
123				GL::PixelStoreUnpack::Set(1);
124				macro_rules! tex_new {
125					(UpdArgs1) => {{
126						let (data, lvl, x, len) = args.get1();
127						let (w, _, _) = mip_size(lvl, len);
128						GL!(glTextureSubImage1D(<$t>::TYPE, self.tex.obj, lvl, x, w, RS::TYPE, RF::TYPE, data));
129					}};
130					(UpdArgs2) => {{
131						let (data, lvl, x, y, len) = args.get2();
132						let (w, h, _) = mip_size(lvl, len);
133						GL!(glTextureSubImage2D(<$t>::TYPE, self.tex.obj, lvl, x, y, w, h, RS::TYPE, RF::TYPE, data));
134					}};
135					(UpdArgs3) => {{
136						let (data, lvl, x, y, z, len) = args.get3();
137						let (w, h, d) = mip_size(lvl, len);
138						GL!(glTextureSubImage3D(<$t>::TYPE, self.tex.obj, lvl, x, y, z, w, h, d, RS::TYPE, RF::TYPE, data));
139					}};
140				}
141				tex_new!($arg_u);
142			}
143		}
144	};
145}
146impl<S, F, T: TexType> Tex<S, F, T> {
147	pub fn obj(&self) -> u32 {
148		self.tex.obj
149	}
150	pub fn Save<RS: TexSize, RF: TexFmt>(&self, lvl: u32) -> Box<[RF]> {
151		ASSERT!(T::TYPE != gl::TEXTURE_CUBE_MAP && T::TYPE != gl::TEXTURE_CUBE_MAP_ARRAY, "unimpl");
152		let size = self.param.size(lvl) * usize(RS::SIZE);
153		let v = vec![Def(); size].into_boxed_slice();
154		let size = i32(size * type_size::<RF>());
155		GL::PixelStorePack::Set(1);
156		GL!(glGetTexture(T::TYPE, self.tex.obj, i32(lvl), RS::TYPE, RF::TYPE, size, v.as_ptr() as *mut GLvoid));
157		v
158	}
159	pub fn Bind<'l>(&'l self, samp: &'l Sampler) -> TextureBinding<T> {
160		let unit = self.unit.take();
161		let (b, u) = TextureBinding::new(&self.tex, samp, unit);
162		self.unit.set(u);
163		b
164	}
165}
166impl_tex!(GL_TEXTURE_1D, i32, UpdArgs1);
167impl_tex!(GL_TEXTURE_2D, iVec2, UpdArgs2);
168impl_tex!(GL_TEXTURE_3D, iVec3, UpdArgs3);
169impl_tex!(GL_TEXTURE_1D_ARRAY, iVec2, UpdArgs2);
170impl_tex!(GL_TEXTURE_2D_ARRAY, iVec3, UpdArgs3);
171impl_tex!(GL_TEXTURE_CUBE_MAP, iVec2, UpdArgs3);
172impl_tex!(GL_TEXTURE_CUBE_MAP_ARRAY, iVec3, UpdArgs3);
173
174#[derive(Debug)]
175pub struct TextureBinding<'l, T> {
176	t: Dummy<&'l T>,
177	pub u: u32,
178}
179impl<'l, T: TexType> TextureBinding<'l, T> {
180	pub fn new(o: &'l Object<Texture<T>>, samp: &'l Sampler, hint: u32) -> (Self, u32) {
181		let u = TexState::Bind::<T>(o.obj, samp.obj, hint);
182		(Self { t: Dummy, u }, u)
183	}
184}
185impl<T: TexType> Clone for TextureBinding<'_, T> {
186	fn clone(&self) -> Self {
187		let &Self { t, u } = self;
188		TexState::Clone(u);
189		Self { t, u }
190	}
191}
192impl<T> Drop for TextureBinding<'_, T> {
193	fn drop(&mut self) {
194		TexState::Unbind(self.u);
195	}
196}