1use {
2 super::BitmapFont,
3 anyhow::{Context, bail},
4 bmfont::BMFont,
5 log::{info, warn},
6 std::sync::Arc,
7 vk_graph::{
8 Graph,
9 driver::{
10 DriverError,
11 ash::vk,
12 buffer::{Buffer, BufferInfo},
13 compute::{ComputePipeline, ComputePipelineInfo},
14 device::Device,
15 image::{Image, ImageInfo},
16 shader::Shader,
17 sync::AccessType,
18 },
19 pool::{Pool as _, hash::HashPool},
20 },
21 vk_shader_macros::include_glsl,
22};
23
24#[derive(Clone, Copy, Debug)]
26pub enum ImageFormat {
27 R8,
29
30 R8G8,
32
33 R8G8B8,
35
36 R8G8B8A8,
38}
39
40impl ImageFormat {
41 fn stride(self) -> usize {
42 match self {
43 Self::R8 => 1,
44 Self::R8G8 => 2,
45 Self::R8G8B8 => 3,
46 Self::R8G8B8A8 => 4,
47 }
48 }
49}
50
51#[derive(Debug)]
53pub struct ImageLoader {
54 pool: HashPool,
55 _decode_r_rg: ComputePipeline,
56 decode_rgb_rgba: ComputePipeline,
57
58 pub device: Device,
60}
61
62impl ImageLoader {
63 pub fn new(device: &Device) -> Result<Self, DriverError> {
65 Ok(Self {
66 pool: HashPool::new(device),
67 _decode_r_rg: ComputePipeline::create(
68 device,
69 ComputePipelineInfo::default(),
70 Shader::new_compute(
71 include_glsl!("res/shader/compute/decode_bitmap_r_rg.comp").as_slice(),
72 ),
73 )?,
74 decode_rgb_rgba: ComputePipeline::create(
75 device,
76 ComputePipelineInfo::default(),
77 Shader::new_compute(
78 include_glsl!("res/shader/compute/decode_bitmap_rgb_rgba.comp").as_slice(),
79 ),
80 )?,
81 device: device.clone(),
82 })
83 }
84
85 fn create_image(
86 &self,
87 format: ImageFormat,
88 width: u32,
89 height: u32,
90 is_srgb: bool,
91 is_temporary: bool,
92 ) -> anyhow::Result<Arc<Image>> {
93 let format = match format {
94 ImageFormat::R8 | ImageFormat::R8G8 => {
95 if is_temporary {
96 vk::Format::R8G8_UINT
97 } else if is_srgb {
98 panic!("Unsupported format: R8G8_SRGB");
99 } else {
100 vk::Format::R8G8_UNORM
101 }
102 }
103 ImageFormat::R8G8B8 | ImageFormat::R8G8B8A8 => {
104 if is_temporary {
105 vk::Format::R8G8B8A8_UINT
106 } else if is_srgb {
107 vk::Format::R8G8B8A8_SRGB
108 } else {
109 vk::Format::R8G8B8A8_UNORM
110 }
111 }
112 };
113 let usage = if is_temporary {
114 vk::ImageUsageFlags::STORAGE
115 | vk::ImageUsageFlags::TRANSFER_DST
116 | vk::ImageUsageFlags::TRANSFER_SRC
117 } else {
118 vk::ImageUsageFlags::SAMPLED
119 | vk::ImageUsageFlags::TRANSFER_DST
120 | vk::ImageUsageFlags::TRANSFER_SRC
121 };
122
123 Ok(Arc::new(
124 Image::create(
125 &self.device,
126 ImageInfo::image_2d(width, height, format, usage),
127 )
128 .context("Unable to create new image")?,
129 ))
130 }
131
132 #[allow(clippy::too_many_arguments)]
134 pub fn decode_bitmap(
135 &mut self,
136 queue_family_index: u32,
137 queue_index: u32,
138 pixels: &[u8],
139 format: ImageFormat,
140 width: u32,
141 height: u32,
142 is_srgb: bool,
143 ) -> anyhow::Result<Arc<Image>> {
144 info!(
145 "decoding {}x{} {:?} bitmap ({} K)",
146 width,
147 height,
148 format,
149 pixels.len() / 1024
150 );
151
152 debug_assert!(
153 pixels.len() >= format.stride() * (width * height) as usize,
154 "insufficient data"
155 );
156
157 if pixels.len() > (format.stride() as u32 * width * height).next_multiple_of(4) as usize {
158 warn!("unused data");
159 }
160
161 let mut graph = Graph::default();
162 let image = graph.bind_resource(self.create_image(format, width, height, is_srgb, false)?);
163
164 match format {
166 ImageFormat::R8 => {
167 info!("Converting R to RG");
169 bail!("unsupported bitmap decode format: R8")
170 }
171 ImageFormat::R8G8B8 => {
172 let stride = width * format.stride() as u32;
176
177 let pixel_buf_stride = stride.next_multiple_of(12);
180 let pixel_buf_len = (pixel_buf_stride * height) as vk::DeviceSize;
181
182 let mut pixel_buf = self.pool.resource(BufferInfo::host_mem(
186 pixel_buf_len,
187 vk::BufferUsageFlags::STORAGE_BUFFER,
188 ))?;
189
190 {
191 let pixel_buf =
192 &mut Buffer::mapped_slice_mut(&mut pixel_buf)[0..pixel_buf_len as usize];
193
194 for y in 0..height {
196 let src_offset = y * stride;
197 let src = &pixels[src_offset as usize..(src_offset + stride) as usize];
198
199 let dst_offset = y * pixel_buf_stride;
200 let dst =
201 &mut pixel_buf[dst_offset as usize..(dst_offset + stride) as usize];
202
203 dst.copy_from_slice(src);
204 }
205 }
206
207 let pixel_buf = graph.bind_resource(pixel_buf);
208
209 let temp_image =
212 graph.bind_resource(self.create_image(format, width, height, false, true)?);
213
214 let dispatch_x = (width + 3) >> 2;
218 let dispatch_y = height;
219 graph
220 .begin_cmd()
221 .debug_name("Decode RGB image")
222 .bind_pipeline(&self.decode_rgb_rgba)
223 .shader_resource_access(0, pixel_buf, AccessType::ComputeShaderReadOther)
224 .shader_resource_access(1, temp_image, AccessType::ComputeShaderWrite)
225 .record_cmd(move |cmd| {
226 cmd.push_constants(0, &(pixel_buf_stride >> 2).to_ne_bytes())
227 .dispatch(dispatch_x, dispatch_y, 1);
228 })
229 .end_cmd()
230 .copy_image(temp_image, image);
231 }
232 ImageFormat::R8G8 | ImageFormat::R8G8B8A8 => {
233 let mut pixel_buf = self.pool.resource(BufferInfo::host_mem(
235 pixels.len() as _,
236 vk::BufferUsageFlags::TRANSFER_SRC,
237 ))?;
238
239 {
240 let pixel_buf = &mut Buffer::mapped_slice_mut(&mut pixel_buf)[0..pixels.len()];
242 pixel_buf.copy_from_slice(pixels);
243 }
244
245 let pixel_buf = graph.bind_resource(pixel_buf);
246 graph.copy_buffer_to_image(pixel_buf, image);
247 }
248 }
249
250 let image = graph.resource(image).clone();
251
252 graph
253 .finalize()
254 .queue_submit(&mut self.pool, queue_family_index, queue_index)?;
255
256 Ok(image)
257 }
258
259 pub fn decode_linear(
261 &mut self,
262 queue_family_index: u32,
263 queue_index: u32,
264 pixels: &[u8],
265 format: ImageFormat,
266 width: u32,
267 height: u32,
268 ) -> anyhow::Result<Arc<Image>> {
269 self.decode_bitmap(
270 queue_family_index,
271 queue_index,
272 pixels,
273 format,
274 width,
275 height,
276 false,
277 )
278 }
279
280 pub fn decode_srgb(
282 &mut self,
283 queue_family_index: u32,
284 queue_index: u32,
285 pixels: &[u8],
286 format: ImageFormat,
287 width: u32,
288 height: u32,
289 ) -> anyhow::Result<Arc<Image>> {
290 self.decode_bitmap(
291 queue_family_index,
292 queue_index,
293 pixels,
294 format,
295 width,
296 height,
297 true,
298 )
299 }
300
301 pub fn load_bitmap_font<'a>(
303 &mut self,
304 queue_family_index: u32,
305 queue_index: u32,
306 font: BMFont,
307 pages: impl IntoIterator<Item = (&'a [u8], u32, u32)>,
308 ) -> anyhow::Result<BitmapFont> {
309 let pages = pages
310 .into_iter()
311 .map(|(pixels, width, height)| {
312 self.decode_linear(
313 queue_family_index,
314 queue_index,
315 pixels,
316 ImageFormat::R8G8B8,
317 width,
318 height,
319 )
320 })
321 .collect::<Result<Vec<_>, _>>()?;
322
323 BitmapFont::new(&self.device, font, pages)
324 }
325}
326
327#[cfg(test)]
328mod test {
329 use super::ImageFormat;
330
331 #[test]
332 fn image_format_stride_matches_channel_count() {
333 assert_eq!(ImageFormat::R8.stride(), 1);
334 assert_eq!(ImageFormat::R8G8.stride(), 2);
335 assert_eq!(ImageFormat::R8G8B8.stride(), 3);
336 assert_eq!(ImageFormat::R8G8B8A8.stride(), 4);
337 }
338}