ailloli_ui_render_wgpu/text/glyph_upload.rs
1//! GPU upload helpers for glyph / atlas regions (`bytes_per_row` padding invariants).
2
3/// Writes a tight RGBA8 rectangle into a texture, padding rows to 256 bytes.
4///
5/// `rgba` should contain `w * h * 4` bytes. Short data is zero-padded only when
6/// row repacking is required; an already aligned short slice is passed directly
7/// to wgpu and fails validation. Zero width produces a zero row pitch, which is
8/// not a valid nonempty texture copy.
9///
10/// # Panics
11///
12/// Panics on arithmetic overflow, allocation failure, or a short source row
13/// during the padding copy.
14///
15/// # Examples
16///
17/// ```no_run
18/// use ailloli_ui_render_wgpu::text::glyph_upload::write_subtexture_rgba;
19/// fn upload(queue: &wgpu::Queue, texture: &wgpu::Texture) {
20/// write_subtexture_rgba(queue, texture, wgpu::Origin3d::ZERO,
21/// 2, 2, &[255; 2 * 2 * 4]);
22/// }
23/// ```
24pub fn write_subtexture_rgba(
25 queue: &wgpu::Queue,
26 texture: &wgpu::Texture,
27 origin: wgpu::Origin3d,
28 w: u32,
29 h: u32,
30 rgba: &[u8],
31) {
32 let unpadded_bpr = w * 4;
33 let padded_bpr = unpadded_bpr.div_ceil(256) * 256;
34
35 let data: Vec<u8>;
36 let bytes = if padded_bpr == unpadded_bpr {
37 rgba
38 } else {
39 data = pad_rows(rgba, unpadded_bpr as usize, padded_bpr as usize, h as usize);
40 &data
41 };
42
43 queue.write_texture(
44 wgpu::ImageCopyTexture {
45 texture,
46 mip_level: 0,
47 origin,
48 aspect: wgpu::TextureAspect::All,
49 },
50 bytes,
51 wgpu::ImageDataLayout {
52 offset: 0,
53 bytes_per_row: Some(padded_bpr),
54 rows_per_image: Some(h),
55 },
56 wgpu::Extent3d {
57 width: w,
58 height: h,
59 depth_or_array_layers: 1,
60 },
61 );
62}
63
64/// Copies tight rows into a zero-filled wider row pitch.
65fn pad_rows(src: &[u8], src_bpr: usize, dst_bpr: usize, rows: usize) -> Vec<u8> {
66 let mut out = vec![0u8; dst_bpr * rows];
67 for row in 0..rows {
68 let src_off = row * src_bpr;
69 let dst_off = row * dst_bpr;
70 let src_end = (src_off + src_bpr).min(src.len());
71 out[dst_off..dst_off + (src_end - src_off)].copy_from_slice(&src[src_off..src_end]);
72 }
73 out
74}