effect_wgpu/layer/
mod.rs

1use anyhow::Result;
2use effect_core::{
3    id::{LayerID, TextureID},
4    primitives::vertex::Vertex,
5    raw::entityraw::Entity2DRaw,
6};
7use std::collections::{hash_map::Keys, HashMap};
8use winit::dpi::PhysicalSize;
9
10use crate::{
11    entity::entity2d::WebEntity2D,
12    texture::{texture2d::WebTexture2D, texture_atlas::WebTextureAtlas2D},
13};
14
15// Takes final ownership of textures, the data etc.
16// When a entity wants to get the texture offset, it must get the data from here.
17pub struct WebLayer2D {
18    id: LayerID,
19    textures: HashMap<TextureID, WebTexture2D>,
20    atlas: WebTextureAtlas2D,
21    vertex_buffer: wgpu::Buffer,
22    entity_count: usize,
23    entity_maximum: usize,
24    entity_buffer: Option<wgpu::Buffer>,
25    dimensions: winit::dpi::PhysicalSize<u32>,
26}
27
28impl WebLayer2D {
29    pub fn new(
30        id: LayerID,
31        dimensions: winit::dpi::PhysicalSize<u32>,
32        mut textures: Vec<WebTexture2D>,
33        device: &wgpu::Device,
34        queue: &wgpu::Queue,
35        bind_group_layout: &wgpu::BindGroupLayout,
36        texture_size: PhysicalSize<u32>,
37        pixel_art: bool,
38    ) -> Result<Self> {
39        let atlas = WebTextureAtlas2D::new(
40            &mut textures,
41            device,
42            queue,
43            bind_group_layout,
44            texture_size,
45            pixel_art,
46        )?;
47        let mut textures_layer = HashMap::new();
48        for texture in textures {
49            textures_layer.insert(texture.id().clone(), texture);
50        }
51        let entity_count = 0;
52        let entity_maximum = 0;
53        let vertex_buffer = WebLayer2DSystem::create_vertex_buffer(
54            texture_size,
55            atlas.tex_coord_size(),
56            device,
57            queue,
58        );
59        Ok(Self {
60            id,
61            textures: textures_layer,
62            atlas,
63            vertex_buffer,
64            entity_count,
65            entity_buffer: None,
66            entity_maximum,
67            dimensions,
68        })
69    }
70
71    pub fn tex_coord_size(&self) -> PhysicalSize<f32> {
72        self.atlas.tex_coord_size()
73    }
74
75    pub fn id(&self) -> LayerID {
76        self.id
77    }
78
79    pub fn contains_texture(&self, texture_id: &TextureID) -> bool {
80        self.textures.contains_key(texture_id)
81    }
82
83    pub fn texture_ids<'a>(&'a self) -> Keys<'a, TextureID, WebTexture2D> {
84        self.textures.keys()
85    }
86
87    pub fn vertex_buffer(&self) -> wgpu::BufferSlice {
88        self.vertex_buffer.slice(..)
89    }
90
91    pub fn entity_buffer(&self) -> Option<wgpu::BufferSlice> {
92        match self.entity_buffer.as_ref() {
93            Some(e_buf) => {
94                let length = self.entity_count * std::mem::size_of::<Entity2DRaw>();
95                Some(e_buf.slice(0..length as u64))
96            }
97            None => None,
98        }
99    }
100
101    pub fn index_count(&self) -> usize {
102        self.entity_count * 6
103    }
104
105    pub fn entity_count(&self) -> usize {
106        self.entity_count
107    }
108
109    pub fn entity_maximum(&self) -> usize {
110        self.entity_maximum
111    }
112
113    pub fn get_texture(&self, id: TextureID) -> Option<&WebTexture2D> {
114        self.textures.get(&id)
115    }
116
117    pub fn width(&self) -> u32 {
118        self.dimensions.width
119    }
120
121    pub fn height(&self) -> u32 {
122        self.dimensions.height
123    }
124
125    pub fn bind_group(&self) -> &wgpu::BindGroup {
126        self.atlas.bind_group()
127    }
128
129    pub fn atlas_dimensions(&self) -> PhysicalSize<u32> {
130        self.atlas.dimensions()
131    }
132}
133
134pub struct WebLayer2DSystem;
135
136impl WebLayer2DSystem {
137    fn alloc_buffer(
138        data: &[u8],
139        size: u64,
140        device: &wgpu::Device,
141        queue: &wgpu::Queue,
142        label: &str,
143        index: bool,
144    ) -> wgpu::Buffer {
145        let usage;
146        if index {
147            usage = wgpu::BufferUsages::INDEX;
148        } else {
149            usage = wgpu::BufferUsages::VERTEX;
150        }
151        let buffer = device.create_buffer(&wgpu::BufferDescriptor {
152            label: Some(label),
153            size,
154            usage: usage | wgpu::BufferUsages::COPY_DST,
155            mapped_at_creation: false,
156        });
157        queue.write_buffer(&buffer, 0, data);
158        buffer
159    }
160
161    // alloc buffer to 2x the size and set new max entity count
162    fn create_entity_buffer(
163        entities: &[&WebEntity2D],
164        device: &wgpu::Device,
165        queue: &wgpu::Queue,
166    ) -> wgpu::Buffer {
167        let ents = entities.iter().map(|e| e.to_raw()).collect::<Vec<_>>();
168        let data: &[u8] = bytemuck::cast_slice(ents.as_slice());
169        let size = std::mem::size_of_val(data) as u64;
170        WebLayer2DSystem::alloc_buffer(data, size * 2, device, queue, "Entity Buffer", false)
171    }
172
173    fn _create_index_buffer(device: &wgpu::Device, queue: &wgpu::Queue) -> wgpu::Buffer {
174        let mut indices: Vec<u16> = Vec::new();
175        indices.extend_from_slice(&[0, 1, 2, 0, 2, 3]);
176        let data: &[u8] = bytemuck::cast_slice(&indices);
177        let size = (std::mem::size_of::<u16>() * 6) as u64;
178        WebLayer2DSystem::alloc_buffer(data, size, device, queue, "Index Buffer", true)
179    }
180
181    fn create_vertex_buffer(
182        _texture_size: PhysicalSize<u32>,
183        tex_coord_size: PhysicalSize<f32>,
184        device: &wgpu::Device,
185        queue: &wgpu::Queue,
186    ) -> wgpu::Buffer {
187        // Vrushing bug comes from here, figure out how to calculate these properly.
188        let width = 0.5;
189        let height = 0.5;
190        let verts = vec![
191            Vertex {
192                position: [width, height, 0.0],
193                tex_coords: [tex_coord_size.width, 0.0],
194            },
195            Vertex {
196                position: [-width, height, 0.0],
197                tex_coords: [0.0, 0.0],
198            },
199            Vertex {
200                position: [-width, -height, 0.0],
201                tex_coords: [0.0, tex_coord_size.height],
202            },
203            Vertex {
204                position: [width, -height, 0.0],
205                tex_coords: [tex_coord_size.width, tex_coord_size.height],
206            },
207        ];
208        let data: &[u8] = bytemuck::cast_slice(verts.as_slice());
209        let size = (std::mem::size_of::<Vertex>() * verts.len()) as u64;
210        WebLayer2DSystem::alloc_buffer(data, size, device, queue, "Vertex Buffer", false)
211    }
212
213    /// Set the entity data for the particular layer.
214    /// Ensure every entity has a texture from the specified layer otherwise you will run into problems.
215    pub fn set_entities(
216        layer: &mut WebLayer2D,
217        entities: &[&WebEntity2D],
218        device: &wgpu::Device,
219        queue: &wgpu::Queue,
220    ) {
221        // allocating exactly amount needed each time may increase the number of allocations needed..
222        // perhaps a strategy of allocatin 2X needed data would be better
223        layer.entity_count = entities.len();
224
225        if layer.entity_count() > layer.entity_maximum() || layer.entity_buffer().is_none() {
226            // Allocate new buffers
227            layer.entity_buffer = Some(WebLayer2DSystem::create_entity_buffer(
228                &entities, device, queue,
229            ));
230            layer.entity_maximum = layer.entity_count * 2;
231        } else {
232            // Reuse buffers
233            let ents = entities.iter().map(|e| e.to_raw()).collect::<Vec<_>>();
234            let entity_data: &[u8] = bytemuck::cast_slice(ents.as_slice());
235            queue.write_buffer(&layer.entity_buffer.as_ref().unwrap(), 0, entity_data);
236        }
237    }
238}