block_compression/
lib.rs

1//! # block_compression
2//!
3//! Texture block compression using WGPU compute shader.
4//! The shaders are a port of Intel's ISPC Texture Compressor's kernel to WGSL compute shader.
5//!
6//! Tested with the following backends:
7//!
8//! * DX12
9//! * Metal
10//! * Vulkan
11//!
12//! ## DX12 pipeline creation
13//!
14//! The pipeline creation for BC7 and especially BC6H takes a long time under DX12. The DXC compiler
15//! seems to take a very long time to compile the shader. For this reason we moved them behind
16//! features, which are included in the default features.
17//!
18//! ## Supported block compressions
19//!
20//! Currently supported block compressions are:
21//!
22//!  * BC1
23//!  * BC2
24//!  * BC3
25//!  * BC4
26//!  * BC5
27//!  * BC6H
28//!  * BC7
29
30#![cfg_attr(docsrs, feature(doc_cfg))]
31
32#[cfg(all(
33    feature = "wgpu",
34    any(feature = "bc15", feature = "bc6h", feature = "bc7")
35))]
36mod block_compressor;
37pub mod decode;
38pub mod encode;
39mod settings;
40
41#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
42use std::hash::{Hash, Hasher};
43
44#[cfg(all(
45    feature = "wgpu",
46    any(feature = "bc15", feature = "bc6h", feature = "bc7")
47))]
48#[cfg_attr(
49    docsrs,
50    doc(cfg(all(
51        feature = "wgpu",
52        any(feature = "bc15", feature = "bc6h", feature = "bc7")
53    )))
54)]
55pub use block_compressor::GpuBlockCompressor;
56pub use bytemuck;
57#[cfg(feature = "bc6h")]
58#[cfg_attr(docsrs, doc(cfg(feature = "bc6h")))]
59pub use half;
60#[cfg(feature = "bc6h")]
61#[cfg_attr(docsrs, doc(cfg(feature = "bc6h")))]
62pub use settings::BC6HSettings;
63#[cfg(feature = "bc7")]
64#[cfg_attr(docsrs, doc(cfg(feature = "bc7")))]
65pub use settings::BC7Settings;
66
67/// Block compression variants supported by this crate.
68#[derive(Copy, Clone, Debug)]
69#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
70#[cfg_attr(
71    docsrs,
72    doc(cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7")))
73)]
74pub enum CompressionVariant {
75    #[cfg(feature = "bc15")]
76    #[cfg_attr(docsrs, doc(cfg(feature = "bc15")))]
77    /// BC1 compression (RGB)
78    BC1,
79    #[cfg(feature = "bc15")]
80    #[cfg_attr(docsrs, doc(cfg(feature = "bc15")))]
81    /// BC2 compression with sharp alpha (RGBA)
82    BC2,
83    #[cfg(feature = "bc15")]
84    #[cfg_attr(docsrs, doc(cfg(feature = "bc15")))]
85    /// BC3 compression with smooth alpha (RGBA)
86    BC3,
87    #[cfg(feature = "bc15")]
88    #[cfg_attr(docsrs, doc(cfg(feature = "bc15")))]
89    /// BC4 compression (R)
90    BC4,
91    #[cfg(feature = "bc15")]
92    #[cfg_attr(docsrs, doc(cfg(feature = "bc15")))]
93    /// BC5 compression (RG)
94    BC5,
95    #[cfg(feature = "bc6h")]
96    #[cfg_attr(docsrs, doc(cfg(feature = "bc6h")))]
97    /// BC6H compression (RGB)
98    BC6H(BC6HSettings),
99    #[cfg(feature = "bc7")]
100    #[cfg_attr(docsrs, doc(cfg(feature = "bc7")))]
101    /// BC7 compression with smooth alpha (RGBA)
102    BC7(BC7Settings),
103}
104
105#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
106impl PartialEq for CompressionVariant {
107    fn eq(&self, other: &Self) -> bool {
108        std::mem::discriminant(self) == std::mem::discriminant(other)
109    }
110}
111
112#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
113impl Eq for CompressionVariant {}
114
115#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
116impl Hash for CompressionVariant {
117    fn hash<H: Hasher>(&self, state: &mut H) {
118        std::mem::discriminant(self).hash(state);
119    }
120}
121
122#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
123impl CompressionVariant {
124    /// Returns the bytes per row for the given width.
125    ///
126    /// The width is used to calculate how many blocks are needed per row,
127    /// which is then multiplied by the block size.
128    /// Width is rounded up to the nearest multiple of 4.
129    pub const fn bytes_per_row(self, width: u32) -> u32 {
130        let blocks_per_row = width.div_ceil(4);
131        blocks_per_row * self.block_byte_size()
132    }
133
134    /// Returns the byte size required for storing compressed blocks for the given dimensions.
135    ///
136    /// The size is calculated based on the block compression format and rounded up dimensions.
137    /// Width and height are rounded up to the nearest multiple of 4.
138    pub const fn blocks_byte_size(self, width: u32, height: u32) -> usize {
139        let block_width = (width as usize).div_ceil(4);
140        let block_height = (height as usize).div_ceil(4);
141        let block_count = block_width * block_height;
142        let block_size = self.block_byte_size() as usize;
143        block_count * block_size
144    }
145
146    const fn block_byte_size(self) -> u32 {
147        match self {
148            #[cfg(feature = "bc15")]
149            Self::BC1 | Self::BC4 => 8,
150            #[cfg(feature = "bc15")]
151            Self::BC2 | Self::BC3 | Self::BC5 => 16,
152            #[cfg(feature = "bc6h")]
153            Self::BC6H(..) => 16,
154            #[cfg(feature = "bc7")]
155            Self::BC7(..) => 16,
156        }
157    }
158
159    #[cfg(feature = "wgpu")]
160    const fn name(self) -> &'static str {
161        match self {
162            #[cfg(feature = "bc15")]
163            Self::BC1 => "bc1",
164            #[cfg(feature = "bc15")]
165            Self::BC2 => "bc2",
166            #[cfg(feature = "bc15")]
167            Self::BC3 => "bc3",
168            #[cfg(feature = "bc15")]
169            Self::BC4 => "bc4",
170            #[cfg(feature = "bc15")]
171            Self::BC5 => "bc5",
172            #[cfg(feature = "bc6h")]
173            Self::BC6H(..) => "bc6h",
174            #[cfg(feature = "bc7")]
175            Self::BC7(..) => "bc7",
176        }
177    }
178
179    #[cfg(feature = "wgpu")]
180    const fn entry_point(self) -> &'static str {
181        match self {
182            #[cfg(feature = "bc15")]
183            Self::BC1 => "compress_bc1",
184            #[cfg(feature = "bc15")]
185            Self::BC2 => "compress_bc2",
186            #[cfg(feature = "bc15")]
187            Self::BC3 => "compress_bc3",
188            #[cfg(feature = "bc15")]
189            Self::BC4 => "compress_bc4",
190            #[cfg(feature = "bc15")]
191            Self::BC5 => "compress_bc5",
192            #[cfg(feature = "bc6h")]
193            Self::BC6H(..) => "compress_bc6h",
194            #[cfg(feature = "bc7")]
195            Self::BC7(..) => "compress_bc7",
196        }
197    }
198}