summaryrefslogtreecommitdiff
path: root/src/graphics/texture.rs
blob: 95b9727aa0d59a18e002dc1543901302f09ba82e (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use wgpu::*;

pub struct TextureWrapper {
    pub width: u32,
    pub height: u32,
    pub block_size: u32,
    pub texture: Texture,
    pub view: TextureView,
}

impl TextureWrapper {
    pub fn new(
        device: &Device,
        width: u32,
        height: u32,
        format: TextureFormat,
        usage: TextureUsages,
    ) -> Self {
        let texture = device.create_texture(&TextureDescriptor {
            label: None,
            size: Extent3d {
                width,
                height,
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: TextureDimension::D2,
            format,
            usage,
            view_formats: &[],
        });
        let view = texture.create_view(&TextureViewDescriptor::default());

        // NOTE: Depth, Stencil, YUVでない限りはNoneにはならないが、念の為`1`にunwrapする。
        let block_size = format.block_copy_size(None).unwrap_or(1);

        Self {
            width,
            height,
            block_size,
            texture,
            view,
        }
    }

    /// テクスチャ全体を更新するメソッド
    ///
    /// * queue - queue
    /// * data - 更新データ
    ///
    /// `data`がテクスチャサイズと一致しない場合は、クラッシュしないように不足分が白色で埋められる。
    pub fn write(&self, queue: &Queue, data: &[u8]) {
        self.write_partial(queue, data, 0, 0, self.width, self.height);
    }

    /// テクスチャを部分的に更新するメソッド
    ///
    /// * queue - queue
    /// * data - 更新データ
    /// * x - 更新領域の開始X座標
    /// * y - 更新領域の開始Y座標
    /// * width - 更新領域の幅
    /// * height - 更新領域の高さ
    ///
    /// `data`が更新領域サイズと一致しない場合は、クラッシュしないように不足分が白色で埋められる。
    pub fn write_partial(
        &self,
        queue: &Queue,
        data: &[u8],
        x: u32,
        y: u32,
        width: u32,
        height: u32,
    ) {
        let expected_len = (width * height * self.block_size) as usize;

        // `data`が想定サイズより短い場合はエラーになるので不足分を白色で埋める。
        let buffer;
        let data = if data.len() >= expected_len {
            data
        } else {
            let mut v = Vec::with_capacity(expected_len);
            v.extend_from_slice(data);
            v.resize(expected_len, 0xff);
            buffer = v;
            buffer.as_slice()
        };

        queue.write_texture(
            TexelCopyTextureInfo {
                texture: &self.texture,
                mip_level: 0,
                origin: Origin3d { x, y, z: 0 },
                aspect: TextureAspect::All,
            },
            data,
            TexelCopyBufferLayout {
                offset: 0,
                bytes_per_row: Some(width * self.block_size),
                rows_per_image: Some(height),
            },
            Extent3d {
                width,
                height,
                depth_or_array_layers: 1,
            },
        );
    }
}