Skip to main content

ctt_intel_texture_compressor/
bc6h.rs

1//! BC6H block compression — RGB HDR (unsigned 16-bit).
2//!
3//! # Input format
4//!
5//! Expects an [`Rgba16Surface`] with **`R16 G16 B16 A16` interleaved** pixel
6//! data (8 bytes per pixel). Each channel is a **little-endian unsigned 16-bit
7//! integer** (u16) in the range `0..=65535`. The alpha channel is present in
8//! the layout but **ignored** by the encoder (it is set to zero internally).
9//!
10//! The ISPC kernel reads each channel with 2-byte offsets from the row base
11//! pointer and masks to 16 bits (`& 0xFFFF`). The quantization path normalizes
12//! against `65535` (`256² − 1`), confirming a full u16 value range.
13//!
14//! **Note:** despite the name "half-float" often associated with BC6H, this
15//! encoder takes raw u16 values, not IEEE 754 binary16 (half-precision)
16//! floats. Callers that have f16 data should transmute / bitcast their
17//! half-float bits into u16 before passing them in.
18//!
19//! # Output
20//!
21//! Each 4×4 texel block is encoded into **16 bytes** (1 byte/pixel). The
22//! format stores unsigned half-float endpoints; signed BC6H is not supported
23//! by this encoder.
24
25use crate::Rgba16Surface;
26use crate::bindings::kernel;
27
28#[derive(Debug, Copy, Clone)]
29pub struct EncodeSettings {
30    pub slow_mode: bool,
31    pub fast_mode: bool,
32    pub refine_iterations_1p: u32,
33    pub refine_iterations_2p: u32,
34    pub fast_skip_threshold: u32,
35}
36
37#[must_use]
38pub fn calc_output_size(width: u32, height: u32) -> usize {
39    // BC6H uses a fixed block size of 16 bytes (128 bits) and a fixed tile size of 4x4 texels.
40    let block_count = (width.div_ceil(4) * height.div_ceil(4)) as usize;
41    block_count * 16
42}
43
44#[must_use]
45pub fn compress_blocks(settings: &EncodeSettings, surface: &Rgba16Surface) -> Vec<u8> {
46    let output_size = calc_output_size(surface.width, surface.height);
47    let mut output = vec![0u8; output_size];
48    compress_blocks_into(settings, surface, &mut output);
49    output
50}
51
52/// Compresses an [`Rgba16Surface`] into BC6H blocks.
53///
54/// The surface must contain `R16 G16 B16 A16` interleaved pixel data (8 bytes
55/// per pixel) where each channel is a little-endian u16. Only the R, G, and B
56/// channels are encoded; the alpha channel is ignored.
57///
58/// # Panics
59///
60/// Panics if `blocks.len()` does not equal [`calc_output_size`] for the given
61/// surface dimensions.
62pub fn compress_blocks_into(settings: &EncodeSettings, surface: &Rgba16Surface, blocks: &mut [u8]) {
63    assert_eq!(
64        blocks.len(),
65        calc_output_size(surface.width, surface.height)
66    );
67    // SAFETY: The ISPC function does not mutate the source surface; the `*mut u8`
68    // pointer type is an artifact of the C header declaration.
69    let mut surface = kernel::rgba_surface {
70        width: surface.width as i32,
71        height: surface.height as i32,
72        stride: surface.stride as i32,
73        ptr: surface.data.as_ptr() as *mut u8,
74    };
75    let mut settings = kernel::bc6h_enc_settings {
76        slow_mode: settings.slow_mode,
77        fast_mode: settings.fast_mode,
78        refineIterations_1p: settings.refine_iterations_1p as i32,
79        refineIterations_2p: settings.refine_iterations_2p as i32,
80        fastSkipThreshold: settings.fast_skip_threshold as i32,
81    };
82
83    unsafe {
84        kernel::CompressBlocksBC6H_ispc(&mut surface, blocks.as_mut_ptr(), &mut settings);
85    }
86}
87
88pub fn very_fast_settings() -> EncodeSettings {
89    EncodeSettings {
90        slow_mode: false,
91        fast_mode: true,
92        fast_skip_threshold: 0,
93        refine_iterations_1p: 0,
94        refine_iterations_2p: 0,
95    }
96}
97
98pub fn fast_settings() -> EncodeSettings {
99    EncodeSettings {
100        slow_mode: false,
101        fast_mode: true,
102        fast_skip_threshold: 2,
103        refine_iterations_1p: 0,
104        refine_iterations_2p: 1,
105    }
106}
107
108pub fn basic_settings() -> EncodeSettings {
109    EncodeSettings {
110        slow_mode: false,
111        fast_mode: false,
112        fast_skip_threshold: 4,
113        refine_iterations_1p: 2,
114        refine_iterations_2p: 2,
115    }
116}
117
118pub fn slow_settings() -> EncodeSettings {
119    EncodeSettings {
120        slow_mode: true,
121        fast_mode: false,
122        fast_skip_threshold: 10,
123        refine_iterations_1p: 2,
124        refine_iterations_2p: 2,
125    }
126}
127
128pub fn very_slow_settings() -> EncodeSettings {
129    EncodeSettings {
130        slow_mode: true,
131        fast_mode: false,
132        fast_skip_threshold: 32,
133        refine_iterations_1p: 2,
134        refine_iterations_2p: 2,
135    }
136}