mraphics_core/render/
renderer.rs

1use wgpu::util::DeviceExt;
2
3use crate::constants::{
4    INDEX_BUFFER_LABEL, PROJECTION_MAT_INDEX, PROJECTION_MAT_LABEL, VIEW_MAT_INDEX, VIEW_MAT_LABEL,
5};
6use crate::{
7    Camera, Color, Conveyor, ConveyorManager, GadgetData, GadgetDescriptor, GeometryIndices,
8    PipelineManager, RenderInstance, Scene,
9};
10
11pub struct Renderer<'window> {
12    pub surface: wgpu::Surface<'window>,
13    pub surface_config: wgpu::SurfaceConfiguration,
14    pub device: wgpu::Device,
15    pub queue: wgpu::Queue,
16
17    pipeline_manager: PipelineManager,
18    attr_conveyor_manager: ConveyorManager,
19    material_conveyor_manager: ConveyorManager,
20    shared_conveyor: Conveyor,
21}
22
23impl<'window> Renderer<'window> {
24    pub fn new(
25        surface: wgpu::Surface<'window>,
26        device: wgpu::Device,
27        queue: wgpu::Queue,
28        adapter: &wgpu::Adapter,
29    ) -> Self {
30        let surface_caps = surface.get_capabilities(adapter);
31        let surface_config = wgpu::SurfaceConfiguration {
32            width: 100,
33            height: 100,
34            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
35            format: wgpu::TextureFormat::Rgba8Unorm,
36            present_mode: surface_caps.present_modes[0],
37            alpha_mode: surface_caps.alpha_modes[0],
38            view_formats: vec![],
39            desired_maximum_frame_latency: 2,
40        };
41
42        surface.configure(&device, &surface_config);
43
44        let mut shared_conveyor = Conveyor::new();
45        shared_conveyor.upsert_gadget(
46            &device,
47            &GadgetDescriptor {
48                label: VIEW_MAT_LABEL,
49                index: VIEW_MAT_INDEX,
50                size: 4 * 4 * 4,
51                usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
52                ty: wgpu::BufferBindingType::Uniform,
53            },
54        );
55        shared_conveyor.upsert_gadget(
56            &device,
57            &GadgetDescriptor {
58                label: PROJECTION_MAT_LABEL,
59                index: PROJECTION_MAT_INDEX,
60                size: 4 * 4 * 4,
61                usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
62                ty: wgpu::BufferBindingType::Uniform,
63            },
64        );
65
66        Self {
67            surface,
68            surface_config,
69            device,
70            queue,
71            pipeline_manager: PipelineManager::new(),
72            attr_conveyor_manager: ConveyorManager::new(),
73            material_conveyor_manager: ConveyorManager::new(),
74            shared_conveyor,
75        }
76    }
77
78    pub fn render<C: Camera>(
79        &mut self,
80        scene: &mut Scene,
81        camera: &C,
82        clear_color: &Color<f64>,
83    ) -> Result<(), wgpu::SurfaceError> {
84        let output = self.surface.get_current_texture()?;
85        let view = output
86            .texture
87            .create_view(&wgpu::TextureViewDescriptor::default());
88
89        let mut encoder = self
90            .device
91            .create_command_encoder(&wgpu::CommandEncoderDescriptor {
92                label: Some("Mraphics Command Encoder"),
93            });
94
95        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
96            label: Some("Mraphics Render Pass"),
97            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
98                view: &view,
99                resolve_target: None,
100                ops: wgpu::Operations {
101                    load: wgpu::LoadOp::Clear(wgpu::Color {
102                        r: clear_color[0],
103                        g: clear_color[1],
104                        b: clear_color[2],
105                        a: clear_color[3],
106                    }),
107                    store: wgpu::StoreOp::Store,
108                },
109                depth_slice: None,
110            })],
111            ..Default::default()
112        });
113
114        // SAFETY: initialized these gadgets in Renderer::new()
115        self.shared_conveyor
116            .update_gadget(&self.queue, VIEW_MAT_LABEL, camera.view_mat_data())
117            .unwrap();
118        self.shared_conveyor
119            .update_gadget(
120                &self.queue,
121                PROJECTION_MAT_LABEL,
122                camera.projection_mat_data(),
123            )
124            .unwrap();
125
126        for instance in &mut scene.instances {
127            self.render_instance(&mut render_pass, instance);
128        }
129
130        drop(render_pass);
131
132        self.queue.submit(std::iter::once(encoder.finish()));
133
134        output.present();
135
136        Ok(())
137    }
138
139    pub fn render_instance(
140        &mut self,
141        render_pass: &mut wgpu::RenderPass,
142        instance: &mut RenderInstance,
143    ) {
144        let attr_conveyor = self
145            .attr_conveyor_manager
146            .acquire_attr_conveyor(&instance.identifier);
147
148        update_gadgets(
149            &self.device,
150            &self.queue,
151            attr_conveyor,
152            &mut instance.geometry.attributes,
153            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
154            wgpu::BufferBindingType::Storage { read_only: true },
155        );
156
157        update_gadgets(
158            &self.device,
159            &self.queue,
160            attr_conveyor,
161            &mut instance.geometry.uniforms,
162            wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
163            wgpu::BufferBindingType::Uniform,
164        );
165
166        let material_conveyor = self.material_conveyor_manager.acquire_attr_conveyor(
167            &(instance.material.identifier.to_string() + &instance.identifier),
168        );
169
170        update_gadgets(
171            &self.device,
172            &self.queue,
173            material_conveyor,
174            &mut instance.material.attributes,
175            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
176            wgpu::BufferBindingType::Storage { read_only: true },
177        );
178
179        update_gadgets(
180            &self.device,
181            &self.queue,
182            material_conveyor,
183            &mut instance.material.uniforms,
184            wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
185            wgpu::BufferBindingType::Uniform,
186        );
187
188        let needs_update = self.shared_conveyor.needs_update
189            || attr_conveyor.needs_update
190            || material_conveyor.needs_update;
191        if needs_update {
192            self.shared_conveyor.update_bundles(&self.device);
193            attr_conveyor.update_bundles(&self.device);
194            material_conveyor.update_bundles(&self.device);
195        }
196
197        let pipeline = self.pipeline_manager.acquire_pipeline(
198            &self.device,
199            self.surface_config.format,
200            &instance.material,
201            &Conveyor::collect_bind_group_layouts(vec![
202                &self.shared_conveyor.bundles,
203                &attr_conveyor.bundles,
204                &material_conveyor.bundles,
205            ]),
206            needs_update,
207        );
208
209        self.shared_conveyor.attach_bundles(render_pass);
210        attr_conveyor.attach_bundles(render_pass);
211        material_conveyor.attach_bundles(render_pass);
212
213        render_pass.set_pipeline(pipeline);
214
215        match &mut instance.geometry.indices {
216            GeometryIndices::Sequential(indices) => {
217                render_pass.draw(0..*indices, 0..1);
218            }
219            GeometryIndices::CustomU16(indices) => {
220                if indices.buffer.is_none() {
221                    indices.buffer.replace(self.device.create_buffer_init(
222                        &wgpu::util::BufferInitDescriptor {
223                            label: Some(INDEX_BUFFER_LABEL),
224                            contents: bytemuck::cast_slice(&indices.data),
225                            usage: wgpu::BufferUsages::INDEX,
226                        },
227                    ));
228                }
229
230                // SAFETY: Checked upon
231                let buffer = indices.buffer.as_ref().unwrap();
232                render_pass.set_index_buffer(buffer.slice(..), wgpu::IndexFormat::Uint16);
233                render_pass.draw_indexed(0..(indices.data.len() as u32), 0, 0..1);
234            }
235            GeometryIndices::CustomU32(indices) => {
236                if indices.buffer.is_none() {
237                    if indices.buffer.is_none() {
238                        indices.buffer.replace(self.device.create_buffer_init(
239                            &wgpu::util::BufferInitDescriptor {
240                                label: Some(INDEX_BUFFER_LABEL),
241                                contents: bytemuck::cast_slice(&indices.data),
242                                usage: wgpu::BufferUsages::INDEX,
243                            },
244                        ));
245                    }
246                }
247
248                // SAFETY: Checked upon
249                let buffer = indices.buffer.as_ref().unwrap();
250                render_pass.set_index_buffer(buffer.slice(..), wgpu::IndexFormat::Uint32);
251                render_pass.draw_indexed(0..(indices.data.len() as u32), 0, 0..1);
252            }
253        }
254    }
255
256    pub fn resize(&mut self, width: u32, height: u32) {
257        self.surface_config.width = width;
258        self.surface_config.height = height;
259
260        self.surface.configure(&self.device, &self.surface_config);
261    }
262}
263
264fn update_gadgets(
265    device: &wgpu::Device,
266    queue: &wgpu::Queue,
267    conveyor: &mut Conveyor,
268    gadget_data: &mut Vec<GadgetData>,
269    usage: wgpu::BufferUsages,
270    ty: wgpu::BufferBindingType,
271) {
272    for data in gadget_data {
273        update_gadget(device, queue, conveyor, data, usage, ty);
274    }
275}
276
277fn update_gadget(
278    device: &wgpu::Device,
279    queue: &wgpu::Queue,
280    conveyor: &mut Conveyor,
281    data: &mut GadgetData,
282    usage: wgpu::BufferUsages,
283    ty: wgpu::BufferBindingType,
284) {
285    if data.needs_update_buffer {
286        conveyor.upsert_gadget(
287            device,
288            &GadgetDescriptor {
289                label: &data.label,
290                index: data.index,
291                size: data.data.len() as u64,
292                usage,
293                ty,
294            },
295        );
296
297        data.needs_update_buffer = false;
298    }
299
300    if !data.needs_update_value {
301        return;
302    }
303
304    // SAFETY: This may panic, but it's developer's responsibility
305    conveyor
306        .update_gadget(queue, &data.label, &data.data)
307        .unwrap();
308
309    data.needs_update_value = false;
310}