1use alloc::string::String;
2use alloc::vec::Vec;
3use core::ops::Range;
4
5use scenix_core::ValidationError;
6
7use crate::TextureFormat;
8
9#[derive(Clone, Debug, PartialEq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Texture2D {
13 pub width: u32,
15 pub height: u32,
17 pub format: TextureFormat,
19 pub data: Vec<u8>,
21 pub mip_levels: u32,
23 pub label: Option<String>,
25}
26
27#[derive(Clone, Debug, PartialEq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct TextureCube {
31 pub size: u32,
33 pub format: TextureFormat,
35 pub faces: [Vec<u8>; 6],
37 pub mip_levels: u32,
39 pub label: Option<String>,
41}
42
43#[derive(Clone, Debug, PartialEq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub struct Texture3D {
47 pub width: u32,
49 pub height: u32,
51 pub depth: u32,
53 pub format: TextureFormat,
55 pub data: Vec<u8>,
57 pub mip_levels: u32,
59 pub label: Option<String>,
61}
62
63impl Texture2D {
64 #[inline]
66 pub fn new(
67 width: u32,
68 height: u32,
69 format: TextureFormat,
70 data: Vec<u8>,
71 ) -> Result<Self, ValidationError> {
72 Self::with_mip_levels(width, height, format, data, 1)
73 }
74
75 pub fn with_mip_levels(
77 width: u32,
78 height: u32,
79 format: TextureFormat,
80 data: Vec<u8>,
81 mip_levels: u32,
82 ) -> Result<Self, ValidationError> {
83 let texture = Self {
84 width,
85 height,
86 format,
87 data,
88 mip_levels,
89 label: None,
90 };
91 texture.validate()?;
92 Ok(texture)
93 }
94
95 pub fn from_mips(
97 width: u32,
98 height: u32,
99 format: TextureFormat,
100 mips: Vec<Vec<u8>>,
101 ) -> Result<Self, ValidationError> {
102 if mips.is_empty() {
103 return Err(ValidationError::OutOfRange);
104 }
105 validate_2d_mips(format, width, height, &mips)?;
106
107 let mip_levels = mips.len() as u32;
108 let total_len = mips.iter().try_fold(0_usize, |total, mip| {
109 total
110 .checked_add(mip.len())
111 .ok_or(ValidationError::OutOfRange)
112 })?;
113 let mut data = Vec::with_capacity(total_len);
114 for mip in mips {
115 data.extend_from_slice(&mip);
116 }
117
118 Ok(Self {
119 width,
120 height,
121 format,
122 data,
123 mip_levels,
124 label: None,
125 })
126 }
127
128 #[inline]
130 pub fn labeled(mut self, label: impl Into<String>) -> Self {
131 self.label = Some(label.into());
132 self
133 }
134
135 #[inline]
137 pub fn base_level_len(&self) -> Result<usize, ValidationError> {
138 self.format.expected_2d_len(self.width, self.height)
139 }
140
141 pub fn mip_level_range(&self, level: u32) -> Result<Range<usize>, ValidationError> {
143 mip_level_range_2d(self.format, self.width, self.height, self.mip_levels, level)
144 }
145
146 pub fn validate(&self) -> Result<(), ValidationError> {
148 let expected =
149 expected_2d_len_for_mip_count(self.format, self.width, self.height, self.mip_levels)?;
150 if self.data.len() == expected {
151 Ok(())
152 } else {
153 Err(ValidationError::OutOfRange)
154 }
155 }
156}
157
158impl TextureCube {
159 pub fn new(
161 size: u32,
162 format: TextureFormat,
163 faces: [Vec<u8>; 6],
164 ) -> Result<Self, ValidationError> {
165 let texture = Self {
166 size,
167 format,
168 faces,
169 mip_levels: 1,
170 label: None,
171 };
172 texture.validate()?;
173 Ok(texture)
174 }
175
176 #[inline]
178 pub fn labeled(mut self, label: impl Into<String>) -> Self {
179 self.label = Some(label.into());
180 self
181 }
182
183 pub fn validate(&self) -> Result<(), ValidationError> {
185 let expected =
186 expected_2d_len_for_mip_count(self.format, self.size, self.size, self.mip_levels)?;
187 if self.faces.iter().all(|face| face.len() == expected) {
188 Ok(())
189 } else {
190 Err(ValidationError::OutOfRange)
191 }
192 }
193
194 pub fn mip_level_range(&self, level: u32) -> Result<Range<usize>, ValidationError> {
196 mip_level_range_2d(self.format, self.size, self.size, self.mip_levels, level)
197 }
198}
199
200impl Texture3D {
201 #[inline]
203 pub fn new(
204 width: u32,
205 height: u32,
206 depth: u32,
207 format: TextureFormat,
208 data: Vec<u8>,
209 ) -> Result<Self, ValidationError> {
210 Self::with_mip_levels(width, height, depth, format, data, 1)
211 }
212
213 pub fn with_mip_levels(
215 width: u32,
216 height: u32,
217 depth: u32,
218 format: TextureFormat,
219 data: Vec<u8>,
220 mip_levels: u32,
221 ) -> Result<Self, ValidationError> {
222 let texture = Self {
223 width,
224 height,
225 depth,
226 format,
227 data,
228 mip_levels,
229 label: None,
230 };
231 texture.validate()?;
232 Ok(texture)
233 }
234
235 #[inline]
237 pub fn labeled(mut self, label: impl Into<String>) -> Self {
238 self.label = Some(label.into());
239 self
240 }
241
242 pub fn validate(&self) -> Result<(), ValidationError> {
244 let levels = self.mip_levels.max(1);
245 if levels > max_mip_levels_3d(self.width, self.height, self.depth)? {
246 return Err(ValidationError::OutOfRange);
247 }
248 let mut expected = 0_usize;
249 for level in 0..levels {
250 let (width, height) = TextureFormat::mip_dimensions(self.width, self.height, level);
251 let depth = mip_dimension(self.depth, level);
252 expected = expected
253 .checked_add(self.format.expected_3d_len(width, height, depth)?)
254 .ok_or(ValidationError::OutOfRange)?;
255 }
256 if self.data.len() == expected {
257 Ok(())
258 } else {
259 Err(ValidationError::OutOfRange)
260 }
261 }
262
263 pub fn mip_level_range(&self, level: u32) -> Result<Range<usize>, ValidationError> {
265 let levels = self.mip_levels.max(1);
266 if level >= levels || levels > max_mip_levels_3d(self.width, self.height, self.depth)? {
267 return Err(ValidationError::OutOfRange);
268 }
269 let mut offset = 0_usize;
270 for current in 0..level {
271 let (width, height) = TextureFormat::mip_dimensions(self.width, self.height, current);
272 let depth = mip_dimension(self.depth, current);
273 offset = offset
274 .checked_add(self.format.expected_3d_len(width, height, depth)?)
275 .ok_or(ValidationError::OutOfRange)?;
276 }
277 let (width, height) = TextureFormat::mip_dimensions(self.width, self.height, level);
278 let depth = mip_dimension(self.depth, level);
279 let len = self.format.expected_3d_len(width, height, depth)?;
280 Ok(offset..offset + len)
281 }
282}
283
284fn mip_level_range_2d(
285 format: TextureFormat,
286 width: u32,
287 height: u32,
288 mip_levels: u32,
289 level: u32,
290) -> Result<Range<usize>, ValidationError> {
291 let levels = mip_levels.max(1);
292 if level >= levels || levels > max_mip_levels_2d(width, height)? {
293 return Err(ValidationError::OutOfRange);
294 }
295 let mut offset = 0_usize;
296 for current in 0..level {
297 let (w, h) = TextureFormat::mip_dimensions(width, height, current);
298 offset = offset
299 .checked_add(format.expected_2d_len(w, h)?)
300 .ok_or(ValidationError::OutOfRange)?;
301 }
302 let (w, h) = TextureFormat::mip_dimensions(width, height, level);
303 let len = format.expected_2d_len(w, h)?;
304 Ok(offset..offset + len)
305}
306
307fn validate_2d_mips(
308 format: TextureFormat,
309 width: u32,
310 height: u32,
311 mips: &[Vec<u8>],
312) -> Result<(), ValidationError> {
313 if mips.len() > max_mip_levels_2d(width, height)? as usize {
314 return Err(ValidationError::OutOfRange);
315 }
316 for (level, mip) in mips.iter().enumerate() {
317 let (w, h) = TextureFormat::mip_dimensions(width, height, level as u32);
318 if mip.len() != format.expected_2d_len(w, h)? {
319 return Err(ValidationError::OutOfRange);
320 }
321 }
322 Ok(())
323}
324
325fn expected_2d_len_for_mip_count(
326 format: TextureFormat,
327 width: u32,
328 height: u32,
329 mip_levels: u32,
330) -> Result<usize, ValidationError> {
331 let levels = mip_levels.max(1);
332 if levels > max_mip_levels_2d(width, height)? {
333 return Err(ValidationError::OutOfRange);
334 }
335 let mut expected = 0_usize;
336 for level in 0..levels {
337 let (w, h) = TextureFormat::mip_dimensions(width, height, level);
338 expected = expected
339 .checked_add(format.expected_2d_len(w, h)?)
340 .ok_or(ValidationError::OutOfRange)?;
341 }
342 Ok(expected)
343}
344
345fn mip_dimension(value: u32, level: u32) -> u32 {
346 if level >= u32::BITS {
347 1
348 } else {
349 (value >> level).max(1)
350 }
351}
352
353fn max_mip_levels_2d(width: u32, height: u32) -> Result<u32, ValidationError> {
354 let max_dimension = width.max(height);
355 if max_dimension == 0 {
356 Err(ValidationError::OutOfRange)
357 } else {
358 Ok(u32::BITS - max_dimension.leading_zeros())
359 }
360}
361
362fn max_mip_levels_3d(width: u32, height: u32, depth: u32) -> Result<u32, ValidationError> {
363 let max_dimension = width.max(height).max(depth);
364 if max_dimension == 0 {
365 Err(ValidationError::OutOfRange)
366 } else {
367 Ok(u32::BITS - max_dimension.leading_zeros())
368 }
369}