1use std::{io, mem::size_of, vec};
2
3use thiserror::Error;
4use tracing::{debug, error, trace};
5use wgpu::util::DeviceExt as _;
6
7use crate::{
8 camera::Camera,
9 screen::{Screen, ScreenSize},
10};
11
12use super::{
13 device::Device, draw_config::DrawConfig, drawables::Drawables,
14 geometries::Geometries, navigation_cube::NavigationCubeRenderer,
15 pipelines::Pipelines, transform::Transform, uniforms::Uniforms,
16 vertices::Vertices, DeviceError, DEPTH_FORMAT, SAMPLE_COUNT,
17};
18
19#[derive(Debug)]
21pub struct Renderer {
22 surface: wgpu::Surface<'static>,
23 device: Device,
24
25 surface_config: wgpu::SurfaceConfiguration,
26 frame_buffer: wgpu::TextureView,
27 depth_view: wgpu::TextureView,
28
29 uniform_buffer: wgpu::Buffer,
30 bind_group: wgpu::BindGroup,
31
32 geometries: Geometries,
33 pipelines: Pipelines,
34
35 navigation_cube_renderer: NavigationCubeRenderer,
36}
37
38impl Renderer {
39 pub async fn new(screen: &impl Screen) -> Result<Self, RendererInitError> {
41 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
42 backends: wgpu::Backends::all(),
43 ..Default::default()
44 });
45
46 let surface = instance.create_surface(screen.window())?;
48
49 for adapter in instance.enumerate_adapters(wgpu::Backends::all()) {
50 debug!("Available adapter: {:?}", adapter.get_info());
51 }
52
53 let result = Device::from_preferred_adapter(&instance, &surface).await;
54 let (device, adapter, features) = match result {
55 Ok((device, adapter, features)) => (device, adapter, features),
56 Err(_) => {
57 error!("Failed to acquire device from preferred adapter");
58
59 match Device::try_from_all_adapters(&instance).await {
60 Ok((device, adapter, features)) => {
61 (device, adapter, features)
62 }
63 Err(err) => {
64 error!("Prepend `RUST_LOG=fj_viewer=debug` and re-run");
65 error!("Then open an issue and post your output");
66 error!(
67 "https://github.com/hannobraun/fornjot/issues/new"
68 );
69
70 return Err(err.into());
71 }
72 }
73 }
74 };
75
76 let color_format = 'color_format: {
77 let capabilities = surface.get_capabilities(&adapter);
78 let supported_formats = capabilities.formats;
79
80 let preferred_formats = [
84 wgpu::TextureFormat::Rgba8Unorm,
85 wgpu::TextureFormat::Bgra8Unorm,
86 ];
87
88 for format in preferred_formats {
89 if supported_formats.contains(&format) {
90 break 'color_format format;
91 }
92 }
93
94 supported_formats
97 .into_iter()
98 .next()
99 .expect("No color formats supported")
100 };
101
102 let ScreenSize { width, height } = screen.size();
103 let surface_config = wgpu::SurfaceConfiguration {
104 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
105 format: color_format,
106 width,
107 height,
108 present_mode: wgpu::PresentMode::AutoVsync,
109 desired_maximum_frame_latency: 2,
110 alpha_mode: wgpu::CompositeAlphaMode::Auto,
121 view_formats: vec![],
122 };
123 surface.configure(&device.device, &surface_config);
124
125 let frame_buffer =
126 Self::create_frame_buffer(&device.device, &surface_config);
127 let depth_view =
128 Self::create_depth_buffer(&device.device, &surface_config);
129
130 let uniform_buffer = device.device.create_buffer_init(
131 &wgpu::util::BufferInitDescriptor {
132 label: None,
133 contents: bytemuck::cast_slice(&[Uniforms::default()]),
134 usage: wgpu::BufferUsages::UNIFORM
135 | wgpu::BufferUsages::COPY_DST,
136 },
137 );
138 let bind_group_layout = device.device.create_bind_group_layout(
139 &wgpu::BindGroupLayoutDescriptor {
140 entries: &[wgpu::BindGroupLayoutEntry {
141 binding: 0,
142 visibility: wgpu::ShaderStages::all(),
143 ty: wgpu::BindingType::Buffer {
144 ty: wgpu::BufferBindingType::Uniform,
145 has_dynamic_offset: false,
146 min_binding_size: wgpu::BufferSize::new(size_of::<
147 Uniforms,
148 >(
149 )
150 as u64),
151 },
152 count: None,
153 }],
154 label: None,
155 },
156 );
157 let bind_group =
158 device.device.create_bind_group(&wgpu::BindGroupDescriptor {
159 layout: &bind_group_layout,
160 entries: &[wgpu::BindGroupEntry {
161 binding: 0,
162 resource: wgpu::BindingResource::Buffer(
163 wgpu::BufferBinding {
164 buffer: &uniform_buffer,
165 offset: 0,
166 size: None,
167 },
168 ),
169 }],
170 label: None,
171 });
172
173 let geometries = Geometries::new(&device.device, &Vertices::empty());
174 let pipelines = Pipelines::new(
175 &device.device,
176 &bind_group_layout,
177 color_format,
178 features,
179 );
180
181 let navigation_cube_renderer = NavigationCubeRenderer::new(
182 &device.device,
183 &device.queue,
184 &surface_config,
185 );
186
187 Ok(Self {
188 surface,
189 device,
190
191 surface_config,
192 frame_buffer,
193 depth_view,
194
195 uniform_buffer,
196 bind_group,
197
198 geometries,
199 pipelines,
200
201 navigation_cube_renderer,
202 })
203 }
204
205 pub fn update_geometry(&mut self, mesh: Vertices) {
207 self.geometries = Geometries::new(&self.device.device, &mesh);
208 }
209
210 pub fn handle_resize(&mut self, size: ScreenSize) {
215 self.surface_config.width = size.width;
216 self.surface_config.height = size.height;
217
218 self.surface
219 .configure(&self.device.device, &self.surface_config);
220
221 self.frame_buffer = Self::create_frame_buffer(
222 &self.device.device,
223 &self.surface_config,
224 );
225 self.depth_view = Self::create_depth_buffer(
226 &self.device.device,
227 &self.surface_config,
228 );
229 }
230
231 pub fn draw(
233 &mut self,
234 camera: &Camera,
235 config: &DrawConfig,
236 ) -> Result<(), DrawError> {
237 let aspect_ratio = f64::from(self.surface_config.width)
238 / f64::from(self.surface_config.height);
239 let uniforms = Uniforms {
240 transform: Transform::for_vertices(camera, aspect_ratio),
241 transform_normals: Transform::for_normals(camera),
242 };
243
244 self.device.queue.write_buffer(
245 &self.uniform_buffer,
246 0,
247 bytemuck::cast_slice(&[uniforms]),
248 );
249
250 let surface_texture = match self.surface.get_current_texture() {
251 Ok(surface_texture) => surface_texture,
252 Err(wgpu::SurfaceError::Timeout) => {
253 return Ok(());
261 }
262 result => result?,
263 };
264 let color_view = surface_texture
265 .texture
266 .create_view(&wgpu::TextureViewDescriptor::default());
267
268 let mut encoder = self.device.device.create_command_encoder(
269 &wgpu::CommandEncoderDescriptor { label: None },
270 );
271
272 {
275 let mut render_pass =
276 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
277 color_attachments: &[Some(
278 wgpu::RenderPassColorAttachment {
279 view: &self.frame_buffer,
280 resolve_target: Some(&color_view),
281 ops: wgpu::Operations {
282 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
283 store: wgpu::StoreOp::Discard,
285 },
286 },
287 )],
288 depth_stencil_attachment: Some(
289 wgpu::RenderPassDepthStencilAttachment {
290 view: &self.depth_view,
291 depth_ops: Some(wgpu::Operations {
292 load: wgpu::LoadOp::Clear(1.0),
293 store: wgpu::StoreOp::Store,
294 }),
295 stencil_ops: None,
296 },
297 ),
298 ..Default::default()
299 });
300 render_pass.set_bind_group(0, &self.bind_group, &[]);
301
302 let drawables = Drawables::new(&self.geometries, &self.pipelines);
303
304 if config.draw_model {
305 drawables.model.draw(&mut render_pass);
306 }
307
308 if let Some(drawable) = drawables.mesh {
309 if config.draw_mesh {
310 drawable.draw(&mut render_pass);
311 }
312 }
313 }
314
315 self.navigation_cube_renderer.draw(
316 &color_view,
317 &mut encoder,
318 &self.device.queue,
319 aspect_ratio,
320 camera.rotation,
321 );
322
323 let command_buffer = encoder.finish();
324 self.device.queue.submit(Some(command_buffer));
325
326 trace!("Presenting...");
327 surface_texture.present();
328
329 trace!("Finished drawing.");
330 Ok(())
331 }
332
333 fn create_frame_buffer(
334 device: &wgpu::Device,
335 surface_config: &wgpu::SurfaceConfiguration,
336 ) -> wgpu::TextureView {
337 let texture = device.create_texture(&wgpu::TextureDescriptor {
338 label: None,
339 size: wgpu::Extent3d {
340 width: surface_config.width,
341 height: surface_config.height,
342 depth_or_array_layers: 1,
343 },
344 mip_level_count: 1,
345 sample_count: SAMPLE_COUNT,
346 dimension: wgpu::TextureDimension::D2,
347 format: surface_config.format,
348 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
349 view_formats: &[],
350 });
351 texture.create_view(&wgpu::TextureViewDescriptor::default())
352 }
353
354 fn create_depth_buffer(
355 device: &wgpu::Device,
356 surface_config: &wgpu::SurfaceConfiguration,
357 ) -> wgpu::TextureView {
358 let texture = device.create_texture(&wgpu::TextureDescriptor {
359 label: None,
360 size: wgpu::Extent3d {
361 width: surface_config.width,
362 height: surface_config.height,
363 depth_or_array_layers: 1,
364 },
365 mip_level_count: 1,
366 sample_count: SAMPLE_COUNT,
367 dimension: wgpu::TextureDimension::D2,
368 format: DEPTH_FORMAT,
369 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
370 view_formats: &[],
371 });
372
373 texture.create_view(&wgpu::TextureViewDescriptor::default())
374 }
375}
376
377#[derive(Error, Debug)]
379pub enum RendererInitError {
380 #[error("I/O error")]
382 Io(#[from] io::Error),
383
384 #[error("Error creating surface")]
386 CreateSurface(#[from] wgpu::CreateSurfaceError),
387
388 #[error(transparent)]
390 Device(#[from] DeviceError),
391}
392
393#[derive(Error, Debug)]
397#[error("Error acquiring output surface: {0}")]
398pub struct DrawError(#[from] wgpu::SurfaceError);