Skip to main content

ctt_intel_texture_compressor/
astc.rs

1//! ASTC (Adaptive Scalable Texture Compression) block compression.
2//!
3//! # Input format
4//!
5//! Expects an [`RgbaSurface`] with **`R8 G8 B8 A8` interleaved** pixel data
6//! (4 bytes per pixel, little-endian RGBA). Whether alpha is encoded depends
7//! on [`EncodeSettings::channels`]: the `opaque_*` presets use `channels: 3`,
8//! while the `alpha_*` presets use `channels: 4`.
9//!
10//! # Output
11//!
12//! Each block is encoded into **16 bytes** (128 bits). The block dimensions
13//! are configurable via [`EncodeSettings::block_width`] and
14//! [`EncodeSettings::block_height`] (up to 8×8), giving a variable data rate.
15//! For example, 4×4 blocks yield 1 byte/pixel, while 8×8 blocks yield
16//! 0.25 bytes/pixel.
17//!
18//! # Limitations
19//!
20//! The `astc_encode` path is currently unimplemented — only the ranking /
21//! mode-selection pass (`astc_rank`) is functional.
22
23#![allow(dead_code)]
24#![allow(unused_variables)]
25
26use crate::RgbaSurface;
27use crate::bindings::kernel_astc;
28
29#[derive(Debug, Copy, Clone)]
30pub struct EncodeSettings {
31    pub block_width: u32,
32    pub block_height: u32,
33    pub channels: u32,
34    pub fast_skip_threshold: u32,
35    pub refine_iterations: u32,
36}
37
38/*
39#[derive(Debug, Copy, Clone)]
40pub struct EncodeContext {
41    pub width: u32,
42    pub height: u32,
43    pub channels: u32,
44    pub dual_plane: bool,
45    pub partitions: u32,
46    pub color_endpoint_pairs: u32,
47}
48
49#[derive(Copy, Clone)]
50pub struct Block {
51    pub width: u32,
52    pub height: u32,
53    pub dual_plane: bool,
54    pub weight_range: u32,
55    pub weights: [u8; 64usize],
56    pub color_component_selector: u32,
57    pub partitions: u32,
58    pub partition_id: u32,
59    pub color_endpoint_pairs: u32,
60    pub channels: u32,
61    pub color_endpoint_modes: [u32; 4usize],
62    pub endpoint_range: u32,
63    pub endpoints: [u8; 18usize],
64}
65*/
66
67pub fn opaque_fast_settings(block_width: u32, block_height: u32) -> EncodeSettings {
68    EncodeSettings {
69        block_width,
70        block_height,
71        channels: 3,
72        fast_skip_threshold: 5,
73        refine_iterations: 2,
74    }
75}
76
77pub fn alpha_fast_settings(block_width: u32, block_height: u32) -> EncodeSettings {
78    EncodeSettings {
79        block_width,
80        block_height,
81        channels: 4,
82        fast_skip_threshold: 5,
83        refine_iterations: 2,
84    }
85}
86
87pub fn alpha_slow_settings(block_width: u32, block_height: u32) -> EncodeSettings {
88    EncodeSettings {
89        block_width,
90        block_height,
91        channels: 4,
92        fast_skip_threshold: 64,
93        refine_iterations: 2,
94    }
95}
96
97fn can_store(value: i32, bits: i32) -> bool {
98    if value < 0 {
99        return false;
100    }
101
102    if value >= 1 << bits {
103        return false;
104    }
105
106    true
107}
108
109fn astc_encode(
110    settings: &mut kernel_astc::astc_enc_settings,
111    surface: &mut kernel_astc::rgba_surface,
112    block_scores: &mut [f32],
113    blocks: &mut [u8],
114    list: &mut [u64],
115) {
116    unimplemented!();
117    /*
118    ispc::astc_enc_context list_context;
119    setup_list_context(&list_context, uint32_t(list[1] & 0xFFFFFFFF));
120
121    assert(sizeof(ispc::rgba_surface) == sizeof(rgba_surface));
122    assert(sizeof(ispc::astc_enc_settings) == sizeof(astc_enc_settings));
123    ispc::astc_encode_ispc((ispc::rgba_surface*)src, block_scores, dst, list, &list_context, (ispc::astc_enc_settings*)settings);
124    */
125}
126
127fn astc_rank(
128    settings: &mut kernel_astc::astc_enc_settings,
129    surface: &mut kernel_astc::rgba_surface,
130    xx: u32,
131    yy: u32,
132    mode_buffer: &mut [u32],
133) {
134    unsafe {
135        kernel_astc::astc_rank_ispc(
136            surface,
137            xx as i32,
138            yy as i32,
139            mode_buffer.as_mut_ptr(),
140            settings,
141        );
142    }
143}
144
145#[must_use]
146pub fn calc_output_size(width: u32, height: u32, block_width: u32, block_height: u32) -> usize {
147    // ASTC uses a fixed block size of 16 bytes (128 bits) with variable tile dimensions.
148    let block_count = (width.div_ceil(block_width) * height.div_ceil(block_height)) as usize;
149    block_count * 16
150}
151
152#[must_use]
153pub fn compress_blocks(settings: &EncodeSettings, surface: &RgbaSurface) -> Vec<u8> {
154    let output_size = calc_output_size(
155        surface.width,
156        surface.height,
157        settings.block_width,
158        settings.block_height,
159    );
160    let mut output = vec![0u8; output_size];
161    compress_blocks_into(settings, surface, &mut output);
162    output
163}
164
165/// Compresses an [`RgbaSurface`] into ASTC blocks.
166///
167/// The surface must contain `R8 G8 B8 A8` interleaved pixel data (4 bytes per
168/// pixel). Dimensions must be exact multiples of the configured block size.
169///
170/// # Panics
171///
172/// Panics if `blocks.len()` does not equal [`calc_output_size`], or if
173/// `surface.width` / `surface.height` are not multiples of the block
174/// dimensions.
175pub fn compress_blocks_into(settings: &EncodeSettings, surface: &RgbaSurface, blocks: &mut [u8]) {
176    assert_eq!(surface.height % settings.block_height, 0);
177    assert_eq!(surface.width % settings.block_width, 0);
178    assert!(settings.block_height <= 8);
179    assert!(settings.block_width <= 8);
180    assert_eq!(
181        blocks.len(),
182        calc_output_size(
183            surface.width,
184            surface.height,
185            settings.block_width,
186            settings.block_height
187        )
188    );
189
190    let tex_width = surface.width / settings.block_width;
191    let program_count = unsafe { kernel_astc::get_programCount() as u32 };
192
193    let mut block_scores =
194        vec![f32::INFINITY; (tex_width * surface.height / settings.block_height) as usize];
195
196    let mode_list_size = 3334usize;
197    let list_size = program_count as usize;
198
199    let mut mode_lists: Vec<u64> = vec![0; list_size * mode_list_size];
200    let mut mode_buffer: Vec<u32> =
201        vec![0; (program_count * settings.fast_skip_threshold) as usize];
202
203    // SAFETY: The ISPC function does not mutate the source surface; the `*mut u8`
204    // pointer type is an artifact of the C header declaration.
205    let mut surface = kernel_astc::rgba_surface {
206        width: surface.width as i32,
207        height: surface.height as i32,
208        stride: surface.stride as i32,
209        ptr: surface.data.as_ptr() as *mut u8,
210    };
211
212    let mut settings = kernel_astc::astc_enc_settings {
213        block_width: settings.block_width as i32,
214        block_height: settings.block_height as i32,
215        channels: settings.channels as i32,
216        fastSkipThreshold: settings.fast_skip_threshold as i32,
217        refineIterations: settings.refine_iterations as i32,
218    };
219
220    for yy in 0..(surface.height / settings.block_height) as u32 {
221        for xx in 0..tex_width.div_ceil(program_count) {
222            let xx = xx * program_count;
223            astc_rank(&mut settings, &mut surface, xx, yy, &mut mode_buffer);
224            for i in 0..settings.fastSkipThreshold as u32 {
225                for k in 0..program_count {
226                    if xx + k >= tex_width {
227                        continue;
228                    }
229                    let offset = (yy << 16) + (xx + k);
230                    let mode = u64::from(mode_buffer[(program_count * i + k) as usize]);
231                    let mode_bin = mode >> 20;
232                    let list_start = list_size * mode_bin as usize;
233                    if mode_lists[list_start] < u64::from(program_count - 1) {
234                        let index = (mode_lists[list_start] + 1) as usize;
235                        mode_lists[list_start] = index as u64;
236                        mode_lists[list_start + index] = (u64::from(offset) << 32) + mode;
237                    } else {
238                        mode_lists[list_start] = (u64::from(offset) << 32) + mode;
239                        astc_encode(
240                            &mut settings,
241                            &mut surface,
242                            &mut block_scores,
243                            blocks,
244                            &mut mode_lists[list_start..(list_start + list_size)],
245                        );
246                        for mode_list in mode_lists.iter_mut().skip(list_start).take(list_size) {
247                            *mode_list = 0;
248                        }
249                    }
250                }
251            }
252        }
253    }
254
255    for mode_bin in 0usize..mode_list_size {
256        let list_start = list_size * mode_bin;
257        if mode_lists[list_start] == 0 {
258            continue;
259        }
260        mode_lists[list_start] = 0;
261        astc_encode(
262            &mut settings,
263            &mut surface,
264            &mut block_scores,
265            blocks,
266            &mut mode_lists[list_start..(list_start + list_size)],
267        );
268        for mode_list in mode_lists.iter_mut().skip(list_start).take(list_size) {
269            *mode_list = 0;
270        }
271    }
272}
273
274/*
275#[repr(C)]
276#[derive(Debug, Copy, Clone)]
277pub struct astc_enc_context {
278    pub width: i32,
279    pub height: i32,
280    pub channels: i32,
281    pub dual_plane: bool,
282    pub partitions: i32,
283    pub color_endpoint_pairs: i32,
284}
285
286#[repr(C)]
287#[derive(Copy, Clone)]
288pub struct astc_block {
289    pub width: i32,
290    pub height: i32,
291    pub dual_plane: bool,
292    pub weight_range: i32,
293    pub weights: [u8; 64usize],
294    pub color_component_selector: i32,
295    pub partitions: i32,
296    pub partition_id: i32,
297    pub color_endpoint_pairs: i32,
298    pub channels: i32,
299    pub color_endpoint_modes: [i32; 4usize],
300    pub endpoint_range: i32,
301    pub endpoints: [u8; 18usize],
302}
303
304extern "C" {
305    #[link_name = "\u{1}_astc_encode_ispc"]
306    pub fn astc_encode_ispc(
307        src: *mut rgba_surface,
308        block_scores: *mut f32,
309        dst: *mut u8,
310        list: *mut u64,
311        list_context: *mut astc_enc_context,
312        settings: *mut astc_enc_settings,
313    );
314}
315extern "C" {
316    #[link_name = "\u{1}_astc_rank_ispc"]
317    pub fn astc_rank_ispc(
318        src: *mut rgba_surface,
319        xx: i32,
320        yy: i32,
321        mode_buffer: *mut u32,
322        settings: *mut astc_enc_settings,
323    );
324}
325*/