1use {
3 crate::{
4 core::{cast_cow, cast_slice},
5 factory::{Factory, ImageState, UploadError},
6 memory::Data,
7 pixel::AsPixel,
8 resource::{
9 Escape, Handle, Image, ImageCreationError, ImageInfo, ImageView,
10 ImageViewCreationError, ImageViewInfo, Sampler,
11 },
12 },
13 rendy_core::hal::{
14 format::{Component, Format, Swizzle},
15 image, Backend,
16 },
17 std::num::NonZeroU8,
18 thread_profiler::profile_scope,
19};
20
21#[derive(Debug)]
24pub struct Texture<B: Backend> {
25 image: Handle<Image<B>>,
26 view: Escape<ImageView<B>>,
27 sampler: Handle<Sampler<B>>,
28 premultiplied: bool,
29}
30
31impl<B> Texture<B>
32where
33 B: Backend,
34{
35 pub fn image(&self) -> &Handle<Image<B>> {
37 &self.image
38 }
39
40 pub fn sampler(&self) -> &Handle<Sampler<B>> {
42 &self.sampler
43 }
44
45 pub fn view(&self) -> &ImageView<B> {
47 &self.view
48 }
49
50 pub fn view_mut(&mut self) -> &mut ImageView<B> {
52 &mut self.view
53 }
54
55 pub fn premultiplied_alpha(&self) -> bool {
57 self.premultiplied
58 }
59}
60
61#[derive(Clone, Copy, Debug)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64pub enum MipLevels {
65 GenerateAuto,
68 GenerateLevels(NonZeroU8),
71 Levels(NonZeroU8),
74}
75
76pub fn mip_levels_from_dims(width: u32, height: u32) -> u8 {
78 ((32 - width.max(height).leading_zeros()).max(1) as u8).min(rendy_core::hal::image::MAX_LEVEL)
79}
80
81#[derive(Debug)]
82pub enum BuildError {
83 Format(Format),
84 Image(ImageCreationError),
85 Upload(UploadError),
86 ImageView(ImageViewCreationError),
87 Mipmap(rendy_core::hal::device::OutOfMemory),
88 Sampler(rendy_core::hal::device::AllocationError),
89}
90
91#[derive(Clone)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
94pub struct TextureBuilder<'a> {
96 kind: image::Kind,
97 view_kind: image::ViewKind,
98 format: Format,
99 data: std::borrow::Cow<'a, [u8]>,
100 data_width: u32,
101 data_height: u32,
102 sampler_info: rendy_core::hal::image::SamplerDesc,
103 swizzle: Swizzle,
104 mip_levels: MipLevels,
105 premultiplied: bool,
106}
107
108impl<'a> std::fmt::Debug for TextureBuilder<'a> {
109 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 fmt.debug_struct("TextureBuilder")
111 .field("kind", &self.kind)
112 .field("view_kind", &self.view_kind)
113 .field("format", &self.format)
114 .field("data", &"<raw-data>")
115 .field("data_width", &self.data_width)
116 .field("data_height", &self.data_height)
117 .field("sampler_info", &self.sampler_info)
118 .field("swizzle", &self.swizzle)
119 .field("mip_levels", &self.mip_levels)
120 .field("premultiplied", &self.premultiplied)
121 .finish()
122 }
123}
124
125impl<'a> TextureBuilder<'a> {
126 pub fn new() -> Self {
128 TextureBuilder {
129 kind: image::Kind::D1(0, 0),
130 view_kind: image::ViewKind::D1,
131 format: Format::Rgba8Unorm,
132 data: std::borrow::Cow::Borrowed(&[]),
133 data_width: 0,
134 data_height: 0,
135 sampler_info: rendy_core::hal::image::SamplerDesc::new(
136 rendy_core::hal::image::Filter::Linear,
137 rendy_core::hal::image::WrapMode::Clamp,
138 ),
139 swizzle: Swizzle::NO,
140 mip_levels: MipLevels::Levels(NonZeroU8::new(1).unwrap()),
141 premultiplied: false,
142 }
143 }
144
145 pub fn set_premultiplied_alpha(&mut self, premultiplied: bool) -> &mut Self {
147 self.premultiplied = premultiplied;
148 self
149 }
150
151 pub fn with_premultiplied_alpha(mut self, premultiplied: bool) -> Self {
153 self.set_premultiplied_alpha(premultiplied);
154 self
155 }
156
157 pub fn with_data<P: AsPixel>(mut self, data: impl Into<std::borrow::Cow<'a, [P]>>) -> Self {
159 self.set_data(data);
160 self
161 }
162
163 pub fn set_data<P: AsPixel>(
165 &mut self,
166 data: impl Into<std::borrow::Cow<'a, [P]>>,
167 ) -> &mut Self {
168 self.data = cast_cow(data.into());
169 self.format = P::FORMAT;
170 self
171 }
172
173 pub fn with_raw_data(
175 mut self,
176 data: impl Into<std::borrow::Cow<'a, [u8]>>,
177 format: Format,
178 ) -> Self {
179 self.set_raw_data(data, format);
180 self
181 }
182
183 pub fn set_raw_data(
185 &mut self,
186 data: impl Into<std::borrow::Cow<'a, [u8]>>,
187 format: Format,
188 ) -> &mut Self {
189 self.data = data.into();
190 self.format = format;
191 self
192 }
193
194 pub fn with_data_width(mut self, data_width: u32) -> Self {
196 self.set_data_width(data_width);
197 self
198 }
199
200 pub fn set_data_width(&mut self, data_width: u32) -> &mut Self {
202 self.data_width = data_width;
203 self
204 }
205
206 pub fn with_data_height(mut self, data_height: u32) -> Self {
208 self.set_data_height(data_height);
209 self
210 }
211
212 pub fn set_data_height(&mut self, data_height: u32) -> &mut Self {
214 self.data_height = data_height;
215 self
216 }
217
218 pub fn with_mip_levels(mut self, mip_levels: MipLevels) -> Self {
220 self.set_mip_levels(mip_levels);
221 self
222 }
223
224 pub fn set_mip_levels(&mut self, mip_levels: MipLevels) -> &mut Self {
226 self.mip_levels = mip_levels;
227 self
228 }
229
230 pub fn with_kind(mut self, kind: image::Kind) -> Self {
232 self.set_kind(kind);
233 self
234 }
235
236 pub fn set_kind(&mut self, kind: image::Kind) -> &mut Self {
238 self.kind = kind;
239 self
240 }
241
242 pub fn with_view_kind(mut self, view_kind: image::ViewKind) -> Self {
244 self.set_view_kind(view_kind);
245 self
246 }
247
248 pub fn set_view_kind(&mut self, view_kind: image::ViewKind) -> &mut Self {
250 self.view_kind = view_kind;
251 self
252 }
253
254 pub fn with_sampler_info(mut self, sampler_info: rendy_core::hal::image::SamplerDesc) -> Self {
256 self.set_sampler_info(sampler_info);
257 self
258 }
259
260 pub fn set_sampler_info(
262 &mut self,
263 sampler_info: rendy_core::hal::image::SamplerDesc,
264 ) -> &mut Self {
265 self.sampler_info = sampler_info;
266 self
267 }
268
269 pub fn with_swizzle(mut self, swizzle: Swizzle) -> Self {
271 self.set_swizzle(swizzle);
272 self
273 }
274
275 pub fn set_swizzle(&mut self, swizzle: Swizzle) -> &mut Self {
277 self.swizzle = swizzle;
278 self
279 }
280
281 pub fn build<B>(
288 &self,
289 next_state: ImageState,
290 factory: &'a mut Factory<B>,
291 ) -> Result<Texture<B>, BuildError>
292 where
293 B: Backend,
294 {
295 profile_scope!("build");
296
297 let view_caps = match self.view_kind {
298 rendy_core::hal::image::ViewKind::D2Array => {
299 rendy_core::hal::image::ViewCapabilities::KIND_2D_ARRAY
300 }
301 rendy_core::hal::image::ViewKind::Cube
302 | rendy_core::hal::image::ViewKind::CubeArray => {
303 rendy_core::hal::image::ViewCapabilities::KIND_CUBE
304 }
305 _ => rendy_core::hal::image::ViewCapabilities::empty(),
306 };
307
308 let (mip_levels, generate_mips) = match self.mip_levels {
309 MipLevels::GenerateLevels(val) => (val.get(), true),
310 MipLevels::Levels(val) => (val.get(), false),
311 MipLevels::GenerateAuto => match self.kind {
312 rendy_core::hal::image::Kind::D1(_, _) => (1, false),
313 rendy_core::hal::image::Kind::D2(w, h, _, _) => (mip_levels_from_dims(w, h), true),
314 rendy_core::hal::image::Kind::D3(_, _, _) => (1, false),
315 },
316 };
317
318 let (info, transform, transform_swizzle) = find_compatible_format(
319 factory,
320 ImageInfo {
321 kind: self.kind,
322 levels: mip_levels,
323 format: self.format,
324 tiling: rendy_core::hal::image::Tiling::Optimal,
325 view_caps,
326 usage: rendy_core::hal::image::Usage::SAMPLED
327 | rendy_core::hal::image::Usage::TRANSFER_DST
328 | rendy_core::hal::image::Usage::TRANSFER_SRC,
329 },
330 )
331 .ok_or(BuildError::Format(self.format))?;
332
333 let image: Handle<Image<B>> = factory
334 .create_image(info, Data)
335 .map_err(BuildError::Image)?
336 .into();
337
338 let mut transformed_vec: Vec<u8>;
339
340 let buffer: &[u8] = match transform {
341 BufferTransform::Intact => &self.data,
342 BufferTransform::AddPadding { stride, padding } => {
343 profile_scope!("add_padding");
344 let new_stride = stride + padding.len();
345 let data_len = self.data.len() / stride * new_stride;
346
347 transformed_vec = vec![0; data_len];
348 let dst_slice: &mut [u8] = &mut transformed_vec;
349 match (stride, padding) {
351 (2, &[0u8, std::u8::MAX]) => {
352 buf_add_padding(&self.data, dst_slice, stride, padding)
353 }
354 (3, &[std::u8::MAX]) => buf_add_padding(&self.data, dst_slice, stride, padding),
355 _ => buf_add_padding(&self.data, dst_slice, stride, padding),
356 }
357 &transformed_vec
358 }
359 };
360
361 let mip_state = ImageState {
362 queue: next_state.queue,
363 stage: rendy_core::hal::pso::PipelineStage::TRANSFER,
364 access: image::Access::TRANSFER_READ,
365 layout: image::Layout::TransferSrcOptimal,
366 };
367
368 let undef_state = ImageState {
369 queue: next_state.queue,
370 stage: rendy_core::hal::pso::PipelineStage::TOP_OF_PIPE,
371 access: image::Access::empty(),
372 layout: image::Layout::Undefined,
373 };
374
375 unsafe {
379 profile_scope!("upload_image");
380
381 factory
382 .upload_image(
383 image.clone(),
384 self.data_width,
385 self.data_height,
386 image::SubresourceLayers {
387 aspects: info.format.surface_desc().aspects,
388 level: 0,
389 layers: 0..info.kind.num_layers(),
390 },
391 image::Offset::ZERO,
392 info.kind.extent(),
393 buffer,
394 image::Layout::Undefined,
395 if !generate_mips || mip_levels == 1 {
396 next_state
397 } else {
398 mip_state
399 },
400 )
401 .map_err(BuildError::Upload)?;
402 }
403
404 if mip_levels > 1 && generate_mips {
405 profile_scope!("fill_mips");
406 unsafe {
407 factory
408 .blitter()
409 .fill_mips(
410 factory.device(),
411 image.clone(),
412 image::Filter::Linear,
413 std::iter::once(mip_state).chain(std::iter::repeat(undef_state)),
414 std::iter::repeat(next_state),
415 )
416 .map_err(BuildError::Mipmap)?;
417 }
418 } else if mip_levels > 1 && !generate_mips {
419 unsafe {
420 factory.transition_image(
421 image.clone(),
422 image::SubresourceRange {
423 aspects: info.format.surface_desc().aspects,
424 levels: 1..mip_levels,
425 layers: 0..info.kind.num_layers(),
426 },
427 image::Layout::Undefined,
428 next_state,
429 );
430 }
431 }
432
433 let view = {
434 profile_scope!("create_image_view");
435 factory
436 .create_image_view(
437 image.clone(),
438 ImageViewInfo {
439 view_kind: self.view_kind,
440 format: info.format,
441 swizzle: double_swizzle(self.swizzle, transform_swizzle),
442 range: image::SubresourceRange {
443 aspects: info.format.surface_desc().aspects,
444 levels: 0..info.levels,
445 layers: 0..info.kind.num_layers(),
446 },
447 },
448 )
449 .map_err(BuildError::ImageView)?
450 };
451
452 let sampler = factory
453 .get_sampler(self.sampler_info.clone())
454 .map_err(BuildError::Sampler)?;
455
456 Ok(Texture {
457 image,
458 view,
459 sampler,
460 premultiplied: self.premultiplied,
461 })
462 }
463}
464
465enum BufferTransform {
466 Intact,
467 AddPadding {
468 stride: usize,
469 padding: &'static [u8],
470 },
471}
472
473fn double_swizzle(src: Swizzle, overlay: Swizzle) -> Swizzle {
474 fn pick_component(src: Swizzle, component: Component) -> Component {
475 let Swizzle(r, g, b, a) = src;
476 match component {
477 Component::R => r,
478 Component::G => g,
479 Component::B => b,
480 Component::A => a,
481 Component::Zero => Component::Zero,
482 Component::One => Component::One,
483 }
484 }
485 Swizzle(
486 pick_component(src, overlay.0),
487 pick_component(src, overlay.1),
488 pick_component(src, overlay.2),
489 pick_component(src, overlay.3),
490 )
491}
492
493fn find_compatible_format<B: Backend>(
494 factory: &Factory<B>,
495 info: ImageInfo,
496) -> Option<(ImageInfo, BufferTransform, Swizzle)> {
497 profile_scope!("find_compatible_format");
498
499 if let Some(info) = image_format_supported(factory, info) {
500 return Some((info, BufferTransform::Intact, Swizzle::NO));
501 }
502 if let Some((format, transform, swizzle)) = expand_format_channels(info.format) {
503 let mut new_info = info.clone();
504 new_info.format = format;
505 if let Some(new_info) = image_format_supported(factory, new_info) {
506 log::trace!("Converting image from {:?} to {:?}", info, new_info);
507 return Some((new_info, transform, swizzle));
508 }
509 }
510
511 None
512}
513
514fn expand_format_channels(format: Format) -> Option<(Format, BufferTransform, Swizzle)> {
515 const ONE_F16: u16 = 15360u16;
516
517 let t2_u8 = BufferTransform::AddPadding {
518 stride: 2,
519 padding: &[0u8, std::u8::MAX],
520 };
521
522 let t2_u16 = BufferTransform::AddPadding {
523 stride: 4,
524 padding: cast_slice(&[0u16, std::u16::MAX]),
525 };
526
527 let t2_f16 = BufferTransform::AddPadding {
528 stride: 4,
529 padding: cast_slice(&[0u16, ONE_F16]),
530 };
531
532 let t2_u32 = BufferTransform::AddPadding {
533 stride: 8,
534 padding: cast_slice(&[0u32, std::u32::MAX]),
535 };
536
537 let t2_f32 = BufferTransform::AddPadding {
538 stride: 8,
539 padding: cast_slice(&[0.0f32, 1.0f32]),
540 };
541
542 let t3_u8 = BufferTransform::AddPadding {
543 stride: 3,
544 padding: &[std::u8::MAX],
545 };
546
547 let t3_u16 = BufferTransform::AddPadding {
548 stride: 6,
549 padding: cast_slice(&[std::u16::MAX]),
550 };
551
552 let t3_f16 = BufferTransform::AddPadding {
553 stride: 6,
554 padding: cast_slice(&[ONE_F16]),
555 };
556
557 let t3_u32 = BufferTransform::AddPadding {
558 stride: 12,
559 padding: cast_slice(&[std::u32::MAX]),
560 };
561
562 let t3_f32 = BufferTransform::AddPadding {
563 stride: 12,
564 padding: cast_slice(&[1.0f32]),
565 };
566
567 let intact = BufferTransform::Intact;
568
569 let rgba = Swizzle(Component::R, Component::G, Component::B, Component::A);
570 let bgra = Swizzle(Component::B, Component::G, Component::R, Component::A);
571
572 Some(match format {
573 Format::Rg8Unorm => (Format::Rgba8Unorm, t2_u8, rgba),
576 Format::Rg8Snorm => (Format::Rgba8Snorm, t2_u8, rgba),
577 Format::Rg8Uscaled => (Format::Rgba8Uscaled, t2_u8, rgba),
578 Format::Rg8Sscaled => (Format::Rgba8Sscaled, t2_u8, rgba),
579 Format::Rg8Uint => (Format::Rgba8Uint, t2_u8, rgba),
580 Format::Rg8Sint => (Format::Rgba8Sint, t2_u8, rgba),
581 Format::Rg8Srgb => (Format::Rgba8Srgb, t2_u8, rgba),
582
583 Format::Rgb8Unorm => (Format::Rgba8Unorm, t3_u8, rgba),
584 Format::Rgb8Snorm => (Format::Rgba8Snorm, t3_u8, rgba),
585 Format::Rgb8Uscaled => (Format::Rgba8Uscaled, t3_u8, rgba),
586 Format::Rgb8Sscaled => (Format::Rgba8Sscaled, t3_u8, rgba),
587 Format::Rgb8Uint => (Format::Rgba8Uint, t3_u8, rgba),
588 Format::Rgb8Sint => (Format::Rgba8Sint, t3_u8, rgba),
589 Format::Rgb8Srgb => (Format::Rgba8Srgb, t3_u8, rgba),
590
591 Format::Bgr8Unorm => (Format::Rgba8Unorm, t3_u8, bgra),
592 Format::Bgr8Snorm => (Format::Rgba8Snorm, t3_u8, bgra),
593 Format::Bgr8Uscaled => (Format::Rgba8Uscaled, t3_u8, bgra),
594 Format::Bgr8Sscaled => (Format::Rgba8Sscaled, t3_u8, bgra),
595 Format::Bgr8Uint => (Format::Rgba8Uint, t3_u8, bgra),
596 Format::Bgr8Sint => (Format::Rgba8Sint, t3_u8, bgra),
597 Format::Bgr8Srgb => (Format::Rgba8Srgb, t3_u8, bgra),
598
599 Format::Bgra8Unorm => (Format::Rgba8Unorm, intact, bgra),
600 Format::Bgra8Snorm => (Format::Rgba8Snorm, intact, bgra),
601 Format::Bgra8Uscaled => (Format::Rgba8Uscaled, intact, bgra),
602 Format::Bgra8Sscaled => (Format::Rgba8Sscaled, intact, bgra),
603 Format::Bgra8Uint => (Format::Rgba8Uint, intact, bgra),
604 Format::Bgra8Sint => (Format::Rgba8Sint, intact, bgra),
605 Format::Bgra8Srgb => (Format::Rgba8Srgb, intact, bgra),
606
607 Format::Rg16Unorm => (Format::Rgba16Unorm, t2_u16, rgba),
608 Format::Rg16Snorm => (Format::Rgba16Snorm, t2_u16, rgba),
609 Format::Rg16Uscaled => (Format::Rgba16Uscaled, t2_u16, rgba),
610 Format::Rg16Sscaled => (Format::Rgba16Sscaled, t2_u16, rgba),
611 Format::Rg16Uint => (Format::Rgba16Uint, t2_u16, rgba),
612 Format::Rg16Sint => (Format::Rgba16Sint, t2_u16, rgba),
613 Format::Rg16Sfloat => (Format::Rgba16Sfloat, t2_f16, rgba),
614
615 Format::Rgb16Unorm => (Format::Rgba16Unorm, t3_u16, rgba),
616 Format::Rgb16Snorm => (Format::Rgba16Snorm, t3_u16, rgba),
617 Format::Rgb16Uscaled => (Format::Rgba16Uscaled, t3_u16, rgba),
618 Format::Rgb16Sscaled => (Format::Rgba16Sscaled, t3_u16, rgba),
619 Format::Rgb16Uint => (Format::Rgba16Uint, t3_u16, rgba),
620 Format::Rgb16Sint => (Format::Rgba16Sint, t3_u16, rgba),
621 Format::Rgb16Sfloat => (Format::Rgba16Sfloat, t3_f16, rgba),
622
623 Format::Rg32Uint => (Format::Rgba32Uint, t2_u32, rgba),
624 Format::Rg32Sint => (Format::Rgba32Sint, t2_u32, rgba),
625 Format::Rg32Sfloat => (Format::Rgba32Sfloat, t2_f32, rgba),
626
627 Format::Rgb32Uint => (Format::Rgba32Uint, t3_u32, rgba),
628 Format::Rgb32Sint => (Format::Rgba32Sint, t3_u32, rgba),
629 Format::Rgb32Sfloat => (Format::Rgba32Sfloat, t3_f32, rgba),
630 _ => return None,
632 })
633}
634
635fn image_format_supported<B: Backend>(
636 factory: &Factory<B>,
637 mut info: ImageInfo,
638) -> Option<ImageInfo> {
639 factory
640 .image_format_properties(info)
641 .filter(|props| {
642 props.max_layers >= info.kind.num_layers()
643 && props.max_extent.width >= info.kind.extent().width
644 && props.max_extent.height >= info.kind.extent().height
645 && props.max_extent.depth >= info.kind.extent().depth
646 })
647 .map(|props| {
648 match &mut info.kind {
649 image::Kind::D2(_, _, _, s) if *s & props.sample_count_mask != *s => {
650 let mut new_samples = *s >> 1;
651 while new_samples > 1 && new_samples & props.sample_count_mask != new_samples {
652 new_samples = new_samples >> 1;
653 }
654 *s = new_samples;
655 }
656 _ => {}
657 };
658 info
659 })
660}
661
662#[inline(always)]
663fn buf_add_padding(buffer: &[u8], dst_slice: &mut [u8], stride: usize, padding: &'static [u8]) {
664 let lad_len = padding.len();
665 for (chunk, dst_chunk) in buffer
666 .chunks_exact(stride)
667 .zip(dst_slice.chunks_exact_mut(stride + lad_len))
668 {
669 for i in 0..stride {
671 dst_chunk[i] = chunk[i];
672 }
673 for i in 0..lad_len {
674 dst_chunk[stride + i] = padding[i];
675 }
676 }
677}