Skip to main content

easy_gpu/
renderer.rs

1use std::sync::Arc;
2use image::GenericImageView;
3use wgpu::{BufferUsages, Color, Device, Extent3d, Queue, ShaderModule, StoreOp, Surface, SurfaceConfiguration, TextureDimension};
4use winit::dpi::PhysicalSize;
5use winit::window::Window;
6use crate::{frame::Frame};
7use crate::assets::buffer::Buffer;
8use crate::assets::render::mesh::Mesh;
9use crate::assets::Texture;
10use crate::assets::vertex_layout::GpuVertex;
11use crate::assets_manager::asset_manager::AssetManager;
12use crate::assets_manager::handle::Handle;
13use crate::wgpu::TextureFormat;
14
15pub struct Renderer {
16    pub(crate) device: Device,
17    queue: Queue,
18    surface: Surface<'static>,
19    pub(crate)surface_config: SurfaceConfiguration,
20
21    pub asset_manager: AssetManager,
22
23    pub(crate)depth_texture: Option<wgpu::Texture>,
24    depth_view: Option<wgpu::TextureView>,
25
26    frame: Frame,
27
28    clear_colour: Color,
29}
30
31impl Renderer {
32    pub async fn new(window: Arc<Window>) -> Self {
33        let instance = wgpu::Instance::default();
34
35        let surface = instance.create_surface(window.clone()).unwrap();
36
37        let adapter = instance.request_adapter(
38            &wgpu::RequestAdapterOptions {
39                compatible_surface: Some(&surface),
40                ..Default::default()
41            },
42        ).await.unwrap();
43
44        let (device, queue) = adapter.request_device(
45            &wgpu::DeviceDescriptor::default(),
46        ).await.unwrap();
47
48        let caps = surface.get_capabilities(&adapter);
49
50        let surface_format = caps.formats
51            .iter()
52            .copied()
53            .find(|f| f.is_srgb())
54            .unwrap_or(caps.formats[0]);
55
56        let surface_config = SurfaceConfiguration {
57            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
58            format: surface_format,
59            width: window.inner_size().width,
60            height: window.inner_size().height,
61            present_mode: if caps.present_modes.contains(&wgpu::PresentMode::Mailbox) {
62                wgpu::PresentMode::Mailbox
63            } else {
64                wgpu::PresentMode::Fifo
65            },
66            alpha_mode: caps.alpha_modes[0],
67            view_formats: vec![],
68            desired_maximum_frame_latency: 2,
69        };
70
71        surface.configure(&device, &surface_config);
72
73        let asset_manager = AssetManager::new();
74
75        let frame = Frame::new();
76
77        Self {
78            device,
79            queue,
80            surface,
81            surface_config,
82            asset_manager,
83            depth_texture: None,
84            depth_view: None,
85            frame,
86            clear_colour: wgpu::Color::BLACK,
87        }
88    }
89
90    pub fn clear_colour(mut self,r: f64,g: f64,b: f64,a: f64) -> Self{
91        self.clear_colour = Color{
92            r,
93            g,
94            b,
95            a,
96        };
97        self
98    }
99
100    pub fn render(&mut self) {
101        let output = match self.surface.get_current_texture() {
102            wgpu::CurrentSurfaceTexture::Success(frame) => frame,
103            wgpu::CurrentSurfaceTexture::Suboptimal(frame) => {
104                // still usable, but should reconfigure soon
105                frame
106            }
107            wgpu::CurrentSurfaceTexture::Timeout => {
108                return; // skip frame
109            }
110            wgpu::CurrentSurfaceTexture::Occluded => {
111                return; // window hidden
112            }
113            wgpu::CurrentSurfaceTexture::Outdated => {
114                // reconfigure surface
115                self.surface.configure(&self.device, &self.surface_config);
116                return;
117            }
118            wgpu::CurrentSurfaceTexture::Lost => {
119                // recreate surface ideally, but reconfigure for now
120                self.surface.configure(&self.device, &self.surface_config);
121                return;
122            }
123            wgpu::CurrentSurfaceTexture::Validation => {
124                return;
125            }
126        };
127
128        let view = output
129            .texture
130            .create_view(&wgpu::TextureViewDescriptor::default());
131
132        let mut encoder = self.device.create_command_encoder(
133            &wgpu::CommandEncoderDescriptor {
134                label: Some("Render Encoder"),
135            },
136        );
137
138        for texture in self.frame.textures_to_clear.drain(..){
139            let view = &self.asset_manager.textures.get(texture).unwrap().view;
140
141            let _rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
142                label: Some("Clear Texture Pass"),
143                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
144                    view,
145                    depth_slice: None,
146                    resolve_target: None,
147                    ops: wgpu::Operations {
148                        load: wgpu::LoadOp::Clear(wgpu::Color {
149                            r: 0.0,
150                            g: 0.0,
151                            b: 0.0,
152                            a: 1.0,
153                        }),
154                        store: Default::default(),
155                    },
156                })],
157                depth_stencil_attachment: None,
158                timestamp_writes: None,
159                occlusion_query_set: None,
160                multiview_mask: None,
161            });
162        }
163
164        for compute_task in &self.frame.compute_tasks{
165            compute_task.execute(&mut encoder, &self.asset_manager)
166        }
167
168        {
169            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
170                label: Some("Render Pass"),
171
172                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
173                    view: &view,
174                    depth_slice: None,
175                    resolve_target: None,
176                    ops: wgpu::Operations {
177                        load: wgpu::LoadOp::Clear(self.clear_colour),
178                        store: StoreOp::Store,
179                    },
180                })],
181                depth_stencil_attachment: self.depth_view.as_ref().map(|view| wgpu::RenderPassDepthStencilAttachment {
182                    view,
183                    depth_ops: Some(wgpu::Operations {
184                        load: wgpu::LoadOp::Clear(1.0),
185                        store: StoreOp::Store,
186                    }),
187                    stencil_ops: None,
188                }) ,
189                occlusion_query_set: None,
190                timestamp_writes: None,
191                multiview_mask: None,
192            });
193
194            let mut current_material = None;
195            let mut current_mesh = None;
196
197            for item in &self.frame.render_tasks {
198                if Some(item.material) != current_material{
199                    current_material = Some(item.material);
200                    let material = self.asset_manager.materials.get(item.material).unwrap();
201                    let pipeline = self.asset_manager.render_pipelines.get(material.pipeline).unwrap();
202
203                    render_pass.set_pipeline(&pipeline.pipeline);
204                    render_pass.set_bind_group(0, &material.bind_group, &[]);
205                }
206
207                let mesh = self.asset_manager.meshes.get(item.mesh).unwrap();
208                if Some(item.mesh) != current_mesh{
209                    current_mesh = Some(item.mesh);
210                    render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
211                    render_pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
212                }
213
214                if let Some(instances) = item.instances{
215                    let instances = self.asset_manager.buffers.get(instances).unwrap();
216                    render_pass.set_vertex_buffer(1, instances.buffer.slice(..));
217                    render_pass.draw_indexed(0..mesh.index_count, 0,item.range.clone().unwrap());
218                }
219                else{
220                    render_pass.draw_indexed(0..mesh.index_count, 0,0..1);
221                }
222
223
224            }
225        }
226
227        self.queue.submit(Some(encoder.finish()));
228
229        output.present();
230    }
231
232    pub fn resize_surface(&mut self, size: PhysicalSize<u32>) {
233        self.surface_config.width = size.width;
234        self.surface_config.height = size.height;
235        self.surface.configure(&self.device, &self.surface_config);
236        self.create_depth_texture(size.width, size.height);
237    }
238    
239    pub fn window_aspect(&self) -> f32 {
240        self.surface_config.width as f32 / self.surface_config.height as f32
241    }
242
243    pub fn width(&self) -> u32{
244        self.surface_config.width
245    }
246    pub fn height(&self) -> u32{
247        self.surface_config.height
248    }
249
250    pub fn begin_frame(&mut self) -> &mut Frame {
251        self.frame.clear();
252        &mut self.frame
253    }
254
255    pub fn current_frame(&mut self) -> &mut Frame {
256        &mut self.frame
257    }
258
259    pub fn create_mesh<T: GpuVertex>(&mut self, vertices: &[T],indices: &[u16]) -> Handle<Mesh>{
260        let mesh = Mesh::new(&self.device,vertices,indices);
261        self.asset_manager.meshes.insert(mesh)
262    }
263
264    pub(crate) fn create_depth_texture(&mut self, width: u32, height: u32){
265        let texture = self.device.create_texture(&wgpu::TextureDescriptor {
266            size: wgpu::Extent3d {
267                width,
268                height,
269                depth_or_array_layers: 1,
270            },
271            mip_level_count: 1,
272            sample_count: 1,
273            dimension: wgpu::TextureDimension::D2,
274            format: wgpu::TextureFormat::Depth24Plus,
275            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
276            label: Some("depth_texture"),
277            view_formats: &[],
278        });
279
280        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
281
282        self.depth_view = Some(view);
283        self.depth_texture = Some(texture);
284    }
285
286    pub fn create_buffer(&mut self,buffer_usages: BufferUsages,size:u64) -> Handle<Buffer> {
287        let buffer = Buffer::new(&self.device,size,buffer_usages);
288        self.asset_manager.buffers.insert(buffer)
289    }
290
291    pub fn create_buffer_with_contents(&mut self,buffer_usages: BufferUsages,contents:&[u8]) -> Handle<Buffer> {
292        let buffer = Buffer::from_contents(&self.device,contents,buffer_usages);
293        self.asset_manager.buffers.insert(buffer)
294    }
295
296    pub fn load_shader(&mut self,src: &'static str) -> Handle<ShaderModule>{
297        let shader = self.device.create_shader_module(wgpu::ShaderModuleDescriptor {
298            label: Some("Shader"),
299            source: wgpu::ShaderSource::Wgsl(src.into()),
300        });
301        self.asset_manager.shaders.insert(shader)
302    }
303
304    pub fn write_buffer<T: bytemuck::Pod>(&self,handle: Handle<Buffer>,data: T){
305        let uniform = self.asset_manager.buffers.get(handle).unwrap();
306        self.queue.write_buffer(&uniform.buffer, 0, bytemuck::cast_slice(&[data]));
307    }
308    pub fn load_texture_from_file(&mut self,texture_bytes: Vec<u8>) -> Handle<Texture> {
309        let image = image::load_from_memory(texture_bytes.as_slice()).unwrap();
310        let rgba = image.to_rgba8();
311
312        let dims = image.dimensions();
313
314        let texture_size = wgpu::Extent3d{
315            width: dims.0,
316            height: dims.1,
317            depth_or_array_layers: 1,
318        };
319
320        let texture = self.device.create_texture(&wgpu::TextureDescriptor{
321            label: None,
322            size: texture_size,
323            mip_level_count: 1,
324            sample_count: 1,
325            dimension: TextureDimension::D2,
326            format: TextureFormat::Rgba8UnormSrgb,
327            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
328            view_formats: &[],
329        });
330
331        self.queue.write_texture(
332            wgpu::TexelCopyTextureInfo{
333                texture: &texture,
334                mip_level: 0,
335                origin: wgpu::Origin3d::ZERO,
336                aspect: wgpu::TextureAspect::All,
337            },
338            &rgba,
339            wgpu::TexelCopyBufferLayout{
340                offset: 0,
341                bytes_per_row: Some(4 * dims.0),
342                rows_per_image: Some(dims.1),
343            },
344            texture_size
345        );
346
347        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
348
349        self.asset_manager.textures.insert(Texture::new(texture,view))
350    }
351
352    pub fn write_texture(&self,texture: Handle<Texture>,bytes: &[u8],byte_per_pixel: u32, texture_size: Extent3d){
353        let texture = &self.asset_manager.textures.get(texture).unwrap().texture;
354
355        self.queue.write_texture(
356            wgpu::TexelCopyTextureInfo{
357                texture,
358                mip_level: 0,
359                origin: wgpu::Origin3d::ZERO,
360                aspect: wgpu::TextureAspect::All,
361            },
362            bytes,
363            wgpu::TexelCopyBufferLayout{
364                offset: 0,
365                bytes_per_row: Some(byte_per_pixel * texture_size.width),
366                rows_per_image: Some(texture_size.height),
367            },
368            texture_size
369        );
370    }
371}
372