1use crate::{
2 extra::BuHeap,
3 transcoder as trans_sys,
4 utils::{BasisTextureFormat, TranscodeTargetFormat},
5};
6use alloc::vec::Vec;
7use async_lock::OnceCell;
8use core::num::NonZero;
9use wgpu_types::{
10 AstcBlock, AstcChannel, Extent3d, TextureDataOrder, TextureFormat, TextureViewDimension,
11};
12
13#[derive(Debug, Clone, PartialEq)]
14pub struct TranscodedImage {
15 pub data: Vec<u8>,
17 pub data_order: TextureDataOrder,
19 pub size: Extent3d,
21 pub format: TextureFormat,
23 pub mip_level_count: u32,
25 pub view_dimension: TextureViewDimension,
27}
28
29static BASISU_TRANSCODER_INITIALIZED: OnceCell<()> = OnceCell::new();
30
31pub async fn basisu_transcoder_init() {
33 BASISU_TRANSCODER_INITIALIZED
34 .get_or_init(async || {
35 #[cfg(all(
36 target_arch = "wasm32",
37 target_vendor = "unknown",
38 target_os = "unknown",
39 ))]
40 crate::instantiate_basisu_wasm().await;
41 unsafe { trans_sys::bt_init() };
42 })
43 .await;
44}
45
46pub fn basisu_transcoder_enable_debug_printf(enable: bool) {
48 unsafe { trans_sys::bt_enable_debug_printf(enable as u32) };
49}
50
51#[derive(Debug, thiserror::Error, PartialEq)]
52#[non_exhaustive]
53pub enum BasisuTranscodeError {
54 #[error("Input data is empty")]
55 EmptyInputData,
56 #[error("Failed to load ktx2 data, likely the input data isn't valid")]
57 LoadKtx2DataFailed,
58 #[error("Invalid ktx2 face count. It must be 1 or 6, got {0}")]
59 InvalidFaceCount(u32),
60 #[error("`BasisuTranscoder::prepare` isn't called before transcoding")]
61 UnsupportedTranscodeTarget,
62 #[error("`bt_ktx2_start_transcoding` failed")]
63 BtStartTranscodingFailed,
64 #[error("`bt_ktx2_transcode_image_level` failed")]
65 BtTranscodeImageLevelFailed,
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub struct TranscodeInfo {
70 pub width: u32,
71 pub height: u32,
72 pub levels: u32,
73 pub layers: u32,
74 pub faces: u32,
75 pub is_srgb: bool,
76 pub basis_format: BasisTextureFormat,
77 pub preferred_target: TranscodeTargetFormat,
78}
79
80pub struct BasisuTranscoder {
81 ktx2_data: Ktx2Data,
82 info: TranscodeInfo,
83}
84
85struct Ktx2Data {
86 #[expect(
87 unused,
88 reason = "This is kept to remain memory, which is referenced by ktx2 handle"
89 )]
90 data: BuHeap,
91 ktx2_handle: NonZero<u64>,
92}
93
94impl Ktx2Data {
95 fn new(data: BuHeap) -> Option<Self> {
96 NonZero::try_from(unsafe {
97 trans_sys::bt_ktx2_open(
98 data.ptr().into(),
99 u32::try_from(u64::from(data.capacity())).unwrap(),
100 )
101 })
102 .ok()
103 .map(|ktx2_handle| Self { data, ktx2_handle })
104 }
105 #[inline]
106 fn ktx2_handle(&self) -> NonZero<u64> {
107 self.ktx2_handle
108 }
109}
110
111impl Drop for Ktx2Data {
112 fn drop(&mut self) {
113 unsafe { trans_sys::bt_ktx2_close(self.ktx2_handle().into()) };
114 }
115}
116
117impl BasisuTranscoder {
118 pub fn new(
134 input: &[u8],
135 supported_compressed_formats: SupportedTextureCompression,
136 channel_type_hint: ChannelType,
137 ) -> Result<Self, BasisuTranscodeError> {
138 if !BASISU_TRANSCODER_INITIALIZED.is_initialized() {
139 panic!("`basisu_transcoder_init` must be called before create transcoder");
140 }
141 unsafe {
142 let Some(input_data) = BuHeap::new(input) else {
143 return Err(BasisuTranscodeError::EmptyInputData);
144 };
145 let Some(ktx2_data) = Ktx2Data::new(input_data) else {
146 return Err(BasisuTranscodeError::LoadKtx2DataFailed);
147 };
148 let ktx2_handle_ptr = u64::from(ktx2_data.ktx2_handle());
149 if trans_sys::bt_ktx2_start_transcoding(ktx2_handle_ptr).is_err() {
150 return Err(BasisuTranscodeError::BtStartTranscodingFailed);
151 }
152 let faces = trans_sys::bt_ktx2_get_faces(ktx2_handle_ptr);
153 if faces != 1 && faces != 6 {
154 return Err(BasisuTranscodeError::InvalidFaceCount(faces));
155 }
156 let width = trans_sys::bt_ktx2_get_width(ktx2_handle_ptr);
157 let height = trans_sys::bt_ktx2_get_height(ktx2_handle_ptr);
158 let layers = trans_sys::bt_ktx2_get_layers(ktx2_handle_ptr);
159 let levels = trans_sys::bt_ktx2_get_levels(ktx2_handle_ptr);
160 let is_srgb = trans_sys::bt_ktx2_is_srgb(ktx2_handle_ptr).is_ok();
161 let basis_format = BasisTextureFormat::try_from(
162 trans_sys::bt_ktx2_get_basis_tex_format(ktx2_handle_ptr),
163 )
164 .unwrap();
165 let channel_id0 = trans_sys::bt_ktx2_get_dfd_channel_id0(ktx2_handle_ptr);
166 let channel_id1 = trans_sys::bt_ktx2_get_dfd_channel_id1(ktx2_handle_ptr);
167 let is_uastc = trans_sys::bt_ktx2_is_uastc_ldr_4x4(ktx2_handle_ptr).is_ok();
168 let channel_type = if channel_type_hint != ChannelType::Auto {
169 channel_type_hint
170 } else {
171 channel_id_to_type(is_uastc, channel_id0, channel_id1)
172 };
173 let preferred_target = select_preferred_transcode_target(
174 basis_format,
175 channel_type,
176 supported_compressed_formats,
177 );
178
179 let info = TranscodeInfo {
180 width,
181 height,
182 levels,
183 layers,
184 faces,
185 is_srgb,
186 basis_format,
187 preferred_target,
188 };
189 Ok(Self { ktx2_data, info })
190 }
191 }
192
193 pub fn get_info(&self) -> TranscodeInfo {
195 self.info
196 }
197
198 pub fn transcode(
202 &self,
203 transcode_target: Option<TranscodeTargetFormat>,
204 is_srgb: Option<bool>,
205 ) -> Result<TranscodedImage, BasisuTranscodeError> {
206 let info = self.info;
207 let transcode_target = transcode_target.unwrap_or(info.preferred_target);
208 if unsafe {
209 trans_sys::bt_basis_is_format_supported(
210 transcode_target as u32,
211 info.basis_format as u32,
212 )
213 .is_err()
214 } {
215 return Err(BasisuTranscodeError::UnsupportedTranscodeTarget);
216 }
217 let out_format = transcode_target_to_wgpu_format(transcode_target)
218 .ok_or(BasisuTranscodeError::UnsupportedTranscodeTarget)?;
219 let total_layers = info.layers.max(1);
220 let mut total_bytes = 0;
221 let ktx2_handle_ptr = u64::from(self.ktx2_data.ktx2_handle());
222 let data = unsafe {
223 for level_index in 0..info.levels {
224 for layer_index in 0..total_layers {
225 for face_index in 0..info.faces {
226 let orig_width = trans_sys::bt_ktx2_get_level_orig_width(
227 ktx2_handle_ptr,
228 level_index,
229 layer_index,
230 face_index,
231 );
232 let orig_height = trans_sys::bt_ktx2_get_level_orig_height(
233 ktx2_handle_ptr,
234 level_index,
235 layer_index,
236 face_index,
237 );
238 let bytes = trans_sys::bt_basis_compute_transcoded_image_size_in_bytes(
239 transcode_target as u32,
240 orig_width,
241 orig_height,
242 );
243 total_bytes += bytes;
244 }
245 }
246 }
247
248 let basisu_heap = BuHeap::new_uninit(NonZero::new(total_bytes.into()).unwrap());
249 let basisu_ptr = u64::from(basisu_heap.ptr());
250 let mut offset = 0u64;
251 for level_index in 0..info.levels {
252 for layer_index in 0..total_layers {
253 for face_index in 0..info.faces {
254 let bytes_per_block_or_pixel =
255 trans_sys::bt_basis_get_bytes_per_block_or_pixel(
256 transcode_target as u32,
257 );
258 let orig_width = trans_sys::bt_ktx2_get_level_orig_width(
259 ktx2_handle_ptr,
260 level_index,
261 layer_index,
262 face_index,
263 );
264 let orig_height = trans_sys::bt_ktx2_get_level_orig_height(
265 ktx2_handle_ptr,
266 level_index,
267 layer_index,
268 face_index,
269 );
270 let bytes = trans_sys::bt_basis_compute_transcoded_image_size_in_bytes(
271 transcode_target as u32,
272 orig_width,
273 orig_height,
274 );
275 let blocks = bytes / bytes_per_block_or_pixel;
276 if trans_sys::bt_ktx2_transcode_image_level(
277 ktx2_handle_ptr,
278 level_index,
279 layer_index,
280 face_index,
281 basisu_ptr + offset,
282 blocks,
283 transcode_target as u32,
284 0,
285 0,
286 0,
287 -1,
288 -1,
289 0,
290 )
291 .is_err()
292 {
293 return Err(BasisuTranscodeError::BtTranscodeImageLevelFailed);
294 }
295 offset += bytes as u64;
296 }
297 }
298 }
299 basisu_heap.try_read(..).unwrap()
300 };
301
302 let view_dimension = if info.layers == 0 {
303 if info.faces == 1 {
304 TextureViewDimension::D2
305 } else if info.faces == 6 {
306 TextureViewDimension::Cube
307 } else {
308 return Err(BasisuTranscodeError::InvalidFaceCount(info.faces));
309 }
310 } else if info.faces == 1 {
311 TextureViewDimension::D2Array
312 } else if info.faces == 6 {
313 TextureViewDimension::CubeArray
314 } else {
315 return Err(BasisuTranscodeError::InvalidFaceCount(info.faces));
316 };
317 let extent = Extent3d {
318 width: info.width,
319 height: info.height,
320 depth_or_array_layers: total_layers * info.faces,
321 };
322 let out_format = if is_srgb.unwrap_or(info.is_srgb) {
323 out_format.add_srgb_suffix()
324 } else {
325 out_format.remove_srgb_suffix()
326 };
327 Ok(TranscodedImage {
328 data,
329 data_order: TextureDataOrder::MipMajor,
330 size: extent,
334 format: out_format,
335 mip_level_count: info.levels,
336 view_dimension,
337 })
338 }
339}
340
341#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
342#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
343pub enum ChannelType {
344 #[default]
345 Auto,
346 Rgba,
347 Rgb,
348 Rg,
349 R,
350}
351
352bitflags::bitflags! {
353 #[derive(Clone, Copy, Debug)]
354 pub struct SupportedTextureCompression: u8 {
355 const ETC2 = 1;
356 const BC = 1 << 1;
357 const ASTC_LDR = 1 << 2;
358 const ASTC_HDR = 1 << 3;
359 }
360}
361
362const KTX2_DF_CHANNEL_ETC1S_RGB: u32 = 0;
363const KTX2_DF_CHANNEL_ETC1S_RRR: u32 = 3;
364const KTX2_DF_CHANNEL_ETC1S_GGG: u32 = 4;
365const KTX2_DF_CHANNEL_ETC1S_AAA: u32 = 15;
366
367const KTX2_DF_CHANNEL_UASTC_RGB: u32 = 0;
368const KTX2_DF_CHANNEL_UASTC_RGBA: u32 = 3;
369const KTX2_DF_CHANNEL_UASTC_RRR: u32 = 4;
370const KTX2_DF_CHANNEL_UASTC_RRRG: u32 = 5;
371const KTX2_DF_CHANNEL_UASTC_RG: u32 = 6;
372
373fn channel_id_to_type(is_uastc: bool, channel_id0: u32, channel_id1: u32) -> ChannelType {
374 if is_uastc {
375 match channel_id0 {
376 KTX2_DF_CHANNEL_UASTC_RGB => ChannelType::Rgb,
377 KTX2_DF_CHANNEL_UASTC_RGBA => ChannelType::Rgba,
378 KTX2_DF_CHANNEL_UASTC_RRR => ChannelType::R,
379 KTX2_DF_CHANNEL_UASTC_RRRG => ChannelType::Rg,
380 KTX2_DF_CHANNEL_UASTC_RG => ChannelType::Rg,
381 _ => ChannelType::Rgba,
382 }
383 } else {
384 if channel_id0 == KTX2_DF_CHANNEL_ETC1S_RGB && channel_id1 != KTX2_DF_CHANNEL_ETC1S_AAA {
385 ChannelType::Rgb
386 } else if channel_id0 == KTX2_DF_CHANNEL_ETC1S_RGB
387 && channel_id1 == KTX2_DF_CHANNEL_ETC1S_AAA
388 {
389 ChannelType::Rgba
390 } else if channel_id0 == KTX2_DF_CHANNEL_ETC1S_RRR
391 && channel_id1 != KTX2_DF_CHANNEL_ETC1S_GGG
392 {
393 ChannelType::R
394 } else if channel_id0 == KTX2_DF_CHANNEL_ETC1S_RRR
395 && channel_id1 == KTX2_DF_CHANNEL_ETC1S_GGG
396 {
397 ChannelType::Rg
398 } else {
399 ChannelType::Rgba
400 }
401 }
402}
403
404fn select_preferred_transcode_target(
406 basis_format: BasisTextureFormat,
407 channel_type: ChannelType,
408 supported_compressed_formats: SupportedTextureCompression,
409) -> TranscodeTargetFormat {
410 let select_hdr_4x4 = || {
411 if supported_compressed_formats.contains(SupportedTextureCompression::ASTC_HDR) {
412 TranscodeTargetFormat::AstcHdr4x4Rgba
413 } else if supported_compressed_formats.contains(SupportedTextureCompression::BC) {
414 TranscodeTargetFormat::Bc6H
415 } else {
416 TranscodeTargetFormat::RgbaHalf
417 }
418 };
419 let select_hdr_6x6 = || {
420 if supported_compressed_formats.contains(SupportedTextureCompression::ASTC_HDR) {
421 TranscodeTargetFormat::AstcHdr6x6Rgba
422 } else if supported_compressed_formats.contains(SupportedTextureCompression::BC) {
423 TranscodeTargetFormat::Bc6H
424 } else {
425 TranscodeTargetFormat::RgbaHalf
426 }
427 };
428 let select_astc_ldr = || {
429 if supported_compressed_formats.contains(SupportedTextureCompression::ASTC_LDR) {
430 TranscodeTargetFormat::try_from(unsafe {
431 trans_sys::bt_basis_get_transcoder_texture_format_from_basis_tex_format(
432 basis_format as u32,
433 )
434 })
435 .unwrap()
436 } else if supported_compressed_formats.contains(SupportedTextureCompression::BC) {
437 TranscodeTargetFormat::Bc7Rgba
438 } else if supported_compressed_formats.contains(SupportedTextureCompression::ETC2) {
439 match channel_type {
440 ChannelType::Rgb => TranscodeTargetFormat::Etc1Rgb,
441 ChannelType::Rgba | ChannelType::Auto => TranscodeTargetFormat::Etc2Rgba,
442 ChannelType::R => TranscodeTargetFormat::Etc2EacR11,
443 ChannelType::Rg => TranscodeTargetFormat::Etc2EacRg11,
444 }
445 } else {
446 TranscodeTargetFormat::RGBA32
447 }
448 };
449 let select_etc1s = || {
450 if supported_compressed_formats.contains(SupportedTextureCompression::ETC2) {
451 match channel_type {
452 ChannelType::Rgb => TranscodeTargetFormat::Etc1Rgb,
453 ChannelType::Rgba | ChannelType::Auto => TranscodeTargetFormat::Etc2Rgba,
454 ChannelType::R => TranscodeTargetFormat::Etc2EacR11,
455 ChannelType::Rg => TranscodeTargetFormat::Etc2EacRg11,
456 }
457 } else if supported_compressed_formats.contains(SupportedTextureCompression::BC) {
458 match channel_type {
459 ChannelType::Rgb => TranscodeTargetFormat::Bc7Rgba,
460 ChannelType::Rgba | ChannelType::Auto => TranscodeTargetFormat::Bc7Rgba,
461 ChannelType::R => TranscodeTargetFormat::Bc4R,
462 ChannelType::Rg => TranscodeTargetFormat::Bc5Rg,
463 }
464 } else {
465 TranscodeTargetFormat::RGBA32
466 }
467 };
468 match basis_format {
469 BasisTextureFormat::Etc1s => select_etc1s(),
470 BasisTextureFormat::UastcLdr4x4 => select_astc_ldr(),
471 BasisTextureFormat::UastcHdr4x4 => select_hdr_4x4(),
472 BasisTextureFormat::AstcHdr6x6 => select_hdr_6x6(),
473 BasisTextureFormat::UastcHdr6x6 => select_hdr_6x6(),
474 BasisTextureFormat::XuastcLdr4x4
475 | BasisTextureFormat::XuastcLdr5x4
476 | BasisTextureFormat::XuastcLdr5x5
477 | BasisTextureFormat::XuastcLdr6x5
478 | BasisTextureFormat::XuastcLdr6x6
479 | BasisTextureFormat::XuastcLdr8x5
480 | BasisTextureFormat::XuastcLdr8x6
481 | BasisTextureFormat::XuastcLdr10x5
482 | BasisTextureFormat::XuastcLdr10x6
483 | BasisTextureFormat::XuastcLdr8x8
484 | BasisTextureFormat::XuastcLdr10x8
485 | BasisTextureFormat::XuastcLdr10x10
486 | BasisTextureFormat::XuastcLdr12x10
487 | BasisTextureFormat::XuastcLdr12x12
488 | BasisTextureFormat::AstcLdr4x4
489 | BasisTextureFormat::AstcLdr5x4
490 | BasisTextureFormat::AstcLdr5x5
491 | BasisTextureFormat::AstcLdr6x5
492 | BasisTextureFormat::AstcLdr6x6
493 | BasisTextureFormat::AstcLdr8x5
494 | BasisTextureFormat::AstcLdr8x6
495 | BasisTextureFormat::AstcLdr10x5
496 | BasisTextureFormat::AstcLdr10x6
497 | BasisTextureFormat::AstcLdr8x8
498 | BasisTextureFormat::AstcLdr10x8
499 | BasisTextureFormat::AstcLdr10x10
500 | BasisTextureFormat::AstcLdr12x10
501 | BasisTextureFormat::AstcLdr12x12 => select_astc_ldr(),
502 }
503}
504
505pub fn transcode_target_to_wgpu_format(transcode: TranscodeTargetFormat) -> Option<TextureFormat> {
506 Some(match transcode {
507 TranscodeTargetFormat::Etc1Rgb => TextureFormat::Etc2Rgb8Unorm,
508 TranscodeTargetFormat::Etc2Rgba => TextureFormat::Etc2Rgba8Unorm,
509 TranscodeTargetFormat::Bc1Rgb => TextureFormat::Bc1RgbaUnorm,
510 TranscodeTargetFormat::Bc3Rgba => TextureFormat::Bc3RgbaUnorm,
511 TranscodeTargetFormat::Bc4R => TextureFormat::Bc4RUnorm,
512 TranscodeTargetFormat::Bc5Rg => TextureFormat::Bc5RgUnorm,
513 TranscodeTargetFormat::Bc7Rgba => TextureFormat::Bc7RgbaUnorm,
514 TranscodeTargetFormat::Pvrtc1_4Rgb => return None,
515 TranscodeTargetFormat::Pvrtc1_4Rgba => return None,
516 TranscodeTargetFormat::AstcLdr4x4Rgba => TextureFormat::Astc {
517 block: AstcBlock::B4x4,
518 channel: AstcChannel::Unorm,
519 },
520 TranscodeTargetFormat::AtcRgb => return None,
521 TranscodeTargetFormat::AtcRgba => return None,
522 TranscodeTargetFormat::Fxt1Rgb => return None,
523 TranscodeTargetFormat::Pvrtc2_4Rgb => return None,
524 TranscodeTargetFormat::Pvrtc2_4Rgba => return None,
525 TranscodeTargetFormat::Etc2EacR11 => TextureFormat::EacR11Unorm,
526 TranscodeTargetFormat::Etc2EacRg11 => TextureFormat::EacRg11Unorm,
527 TranscodeTargetFormat::Bc6H => TextureFormat::Bc6hRgbUfloat,
528 TranscodeTargetFormat::AstcHdr4x4Rgba => TextureFormat::Astc {
529 block: AstcBlock::B4x4,
530 channel: AstcChannel::Hdr,
531 },
532 TranscodeTargetFormat::RGBA32 => TextureFormat::Rgba8Unorm,
533 TranscodeTargetFormat::RGB565 => return None,
534 TranscodeTargetFormat::BGR565 => return None,
535 TranscodeTargetFormat::RGBA4444 => return None,
536 TranscodeTargetFormat::RgbHalf => return None,
537 TranscodeTargetFormat::RgbaHalf => TextureFormat::Rgba16Float,
538 TranscodeTargetFormat::Rgb9e5 => TextureFormat::Rgb9e5Ufloat,
539 TranscodeTargetFormat::AstcHdr6x6Rgba => TextureFormat::Astc {
540 block: AstcBlock::B6x6,
541 channel: AstcChannel::Hdr,
542 },
543 TranscodeTargetFormat::AstcLdr5x4Rgba => TextureFormat::Astc {
544 block: AstcBlock::B5x4,
545 channel: AstcChannel::Unorm,
546 },
547 TranscodeTargetFormat::AstcLdr5x5Rgba => TextureFormat::Astc {
548 block: AstcBlock::B5x5,
549 channel: AstcChannel::Unorm,
550 },
551 TranscodeTargetFormat::AstcLdr6x5Rgba => TextureFormat::Astc {
552 block: AstcBlock::B6x5,
553 channel: AstcChannel::Unorm,
554 },
555 TranscodeTargetFormat::AstcLdr6x6Rgba => TextureFormat::Astc {
556 block: AstcBlock::B6x6,
557 channel: AstcChannel::Unorm,
558 },
559 TranscodeTargetFormat::AstcLdr8x5Rgba => TextureFormat::Astc {
560 block: AstcBlock::B8x5,
561 channel: AstcChannel::Unorm,
562 },
563 TranscodeTargetFormat::AstcLdr8x6Rgba => TextureFormat::Astc {
564 block: AstcBlock::B8x6,
565 channel: AstcChannel::Unorm,
566 },
567 TranscodeTargetFormat::AstcLdr10x5Rgba => TextureFormat::Astc {
568 block: AstcBlock::B10x5,
569 channel: AstcChannel::Unorm,
570 },
571 TranscodeTargetFormat::AstcLdr10x6Rgba => TextureFormat::Astc {
572 block: AstcBlock::B10x6,
573 channel: AstcChannel::Unorm,
574 },
575 TranscodeTargetFormat::AstcLdr8x8Rgba => TextureFormat::Astc {
576 block: AstcBlock::B8x8,
577 channel: AstcChannel::Unorm,
578 },
579 TranscodeTargetFormat::AstcLdr10x8Rgba => TextureFormat::Astc {
580 block: AstcBlock::B10x8,
581 channel: AstcChannel::Unorm,
582 },
583 TranscodeTargetFormat::AstcLdr10x10Rgba => TextureFormat::Astc {
584 block: AstcBlock::B10x10,
585 channel: AstcChannel::Unorm,
586 },
587 TranscodeTargetFormat::AstcLdr12x10Rgba => TextureFormat::Astc {
588 block: AstcBlock::B12x10,
589 channel: AstcChannel::Unorm,
590 },
591 TranscodeTargetFormat::AstcLdr12x12Rgba => TextureFormat::Astc {
592 block: AstcBlock::B12x12,
593 channel: AstcChannel::Unorm,
594 },
595 })
596}
597
598#[cfg(test)]
599mod tests {
600
601 #[test]
602 #[should_panic]
603 fn transcoder_create_before_init() {
604 if super::BASISU_TRANSCODER_INITIALIZED.is_initialized() {
605 panic!("Basisu is already initialized, panic to skip this test");
606 } else {
607 let _ = super::BasisuTranscoder::new(
608 &[],
609 super::SupportedTextureCompression::empty(),
610 super::ChannelType::Auto,
611 );
612 }
613 }
614}