aboutsummaryrefslogtreecommitdiff
path: root/old/vgcodec/src/export.rs
blob: e020c5df5e5d0fe663239211f5da432d792cedbd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use crate::app::App;
use image::RgbaImage;
use std::{num::NonZeroU32, sync::Arc};
use wgpu::{
    Buffer, Extent3d, ImageCopyBuffer, ImageCopyTexture, ImageDataLayout, Origin3d, Texture,
};

pub struct Exporter {
    app: Arc<App>,
    size: Extent3d,

    padded_bytes_per_row: u32,
    export_buffer: Buffer,
}

impl Exporter {
    pub fn new(app: &Arc<App>, size: Extent3d) -> Self {
        let App { device, .. } = app.as_ref();

        let bytes_per_pixel = std::mem::size_of::<u32>() as u32;
        let unpadded_bytes_per_row = size.width * bytes_per_pixel;
        let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
        let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;
        let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;

        let export_buffer_size = (padded_bytes_per_row * size.height) as u64;
        let export_buffer = device.create_buffer(&wgpu::BufferDescriptor {
            label: None,
            size: export_buffer_size,
            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
            mapped_at_creation: false,
        });

        Self {
            padded_bytes_per_row,
            app: app.clone(),
            size,
            export_buffer,
        }
    }
    pub async fn run(&self, texture: &Texture, save_path: &str) {
        let App { device, queue, .. } = self.app.as_ref();

        let mut encoder =
            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });

        encoder.copy_texture_to_buffer(
            ImageCopyTexture {
                texture,
                aspect: wgpu::TextureAspect::All,
                mip_level: 0,
                origin: Origin3d::ZERO,
            },
            ImageCopyBuffer {
                buffer: &self.export_buffer,
                layout: ImageDataLayout {
                    offset: 0,
                    bytes_per_row: Some(NonZeroU32::new(self.padded_bytes_per_row).unwrap()),
                    rows_per_image: None,
                },
            },
            self.size,
        );

        queue.submit(Some(encoder.finish()));

        let buffer_slice = self.export_buffer.slice(..);
        let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
        buffer_slice.map_async(wgpu::MapMode::Read, move |v| {
            sender.send(v.unwrap()).unwrap()
        });
        device.poll(wgpu::Maintain::Wait);
        receiver.receive().await;

        let data = buffer_slice.get_mapped_range();
        let result: Vec<u8> = bytemuck::cast_slice(&data).to_vec();
        let image = RgbaImage::from_raw(self.size.width, self.size.height, result).unwrap();
        image.save(save_path).unwrap();

        drop(data);
        self.export_buffer.unmap();
    }
}