1pub mod types;
2pub mod texture;
3pub mod sprite;
4pub mod model;
5mod draw_pass;
6pub mod extras;
7
8pub use draw_pass::DrawPass;
9pub use types::*;
10pub use texture::Texture;
11pub use sprite::Sprite;
12pub use model::Model;
13
14use crate::{camera::{Camera, CameraUniform}, types::Color};
15
16pub struct Renderer<'a> {
19 camera: Camera,
20 camera_uniform: CameraUniform,
21 pub(crate) camera_buffer: wgpu::Buffer,
22 window: std::sync::Arc<winit::window::Window>,
23 device: wgpu::Device,
24 surface: wgpu::Surface<'a>,
25 queue: wgpu::Queue,
26 config: wgpu::SurfaceConfiguration,
27 size: winit::dpi::PhysicalSize<u32>,
28 immediate_pipeline: wgpu::RenderPipeline,
29 pub(crate) texture_bind_group_layout: wgpu::BindGroupLayout,
30 pub(crate) sampler: wgpu::Sampler,
31 pub(crate) sprite_pipeline: wgpu::RenderPipeline,
32 pub(crate) world_sprite_pipeline: wgpu::RenderPipeline,
33 pub(crate) world_sprite_outline_pipeline: wgpu::RenderPipeline,
34 pub(crate) model_pipeline: wgpu::RenderPipeline,
35 pub(crate) model_outline_pipeline: wgpu::RenderPipeline,
36 model_transform_bind_group_layout: wgpu::BindGroupLayout,
37 pub(crate) camera_bind_group: wgpu::BindGroup,
38 depth_view: wgpu::TextureView,
39}
40
41fn create_depth_view(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> wgpu::TextureView {
43 let texture = device.create_texture(&wgpu::TextureDescriptor {
44 label: Some("Depth Texture"),
45 size: wgpu::Extent3d {
46 width: config.width.max(1),
47 height: config.height.max(1),
48 depth_or_array_layers: 1,
49 },
50 mip_level_count: 1,
51 sample_count: 1,
52 dimension: wgpu::TextureDimension::D2,
53 format: extras::DEPTH_FORMAT,
54 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
55 view_formats: &[],
56 });
57 texture.create_view(&wgpu::TextureViewDescriptor::default())
58}
59
60impl<'a> Renderer<'a> {
61 pub async fn new(window: std::sync::Arc<winit::window::Window>) -> Self {
62 let size = window.inner_size();
63
64 let instance = wgpu::Instance::default();
65 let surface = match instance.create_surface(window.clone()) {
66 Ok(surface) => surface,
67 Err(e) => panic!("Failed to create surface: {:?}", e),
68 };
69
70 let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
71 power_preference: wgpu::PowerPreference::HighPerformance,
72 compatible_surface: Some(&surface),
73 force_fallback_adapter: false,
74 apply_limit_buckets: false,
75 }).await.expect("Failed to find an appropriate adapter");
76
77 let (device, queue) = adapter.request_device(
78 &wgpu::DeviceDescriptor {
79 label: Some("Device Descriptor"),
80 required_features: wgpu::Features::empty(),
81 required_limits: wgpu::Limits::default(),
82 experimental_features: wgpu::ExperimentalFeatures::default(),
83 memory_hints: wgpu::MemoryHints::default(),
84 trace: wgpu::Trace::default(),
85 }
86 ).await.expect("Failed to create device");
87
88 let surface_caps = surface.get_capabilities(&adapter);
89 let surface_format = surface_caps.formats.iter().copied().find(|f| f.is_srgb()).unwrap_or(surface_caps.formats[0]);
90
91 let config = wgpu::SurfaceConfiguration {
92 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
93 format: surface_format,
94 width: size.width,
95 height: size.height,
96 present_mode: surface_caps.present_modes[0],
97 alpha_mode: surface_caps.alpha_modes[0],
98 view_formats: vec![],
99 desired_maximum_frame_latency: 2,
100 color_space: wgpu::SurfaceColorSpace::Auto,
101 };
102 surface.configure(&device, &config);
103
104 let shape_pipeline = extras::create_shape_pipeline(&device, config.format);
105
106 let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
107 entries: &[
108 wgpu::BindGroupLayoutEntry {
109 binding: 0,
110 visibility: wgpu::ShaderStages::FRAGMENT,
111 ty: wgpu::BindingType::Texture {
112 multisampled: false,
113 view_dimension: wgpu::TextureViewDimension::D2,
114 sample_type: wgpu::TextureSampleType::Float { filterable: true },
115 },
116 count: None,
117 },
118 wgpu::BindGroupLayoutEntry {
119 binding: 1,
120 visibility: wgpu::ShaderStages::FRAGMENT,
121 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
122 count: None,
123 },
124 ],
125 label: Some("texture_bind_group_layout"),
126 });
127
128 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
129 address_mode_u: wgpu::AddressMode::ClampToEdge,
130 address_mode_v: wgpu::AddressMode::ClampToEdge,
131 address_mode_w: wgpu::AddressMode::ClampToEdge,
132 mag_filter: wgpu::FilterMode::Linear,
133 min_filter: wgpu::FilterMode::Nearest,
134 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
135 ..Default::default()
136 });
137
138 let sprite_pipeline = extras::create_sprite_pipeline(&device, config.format, &texture_bind_group_layout);
139
140 let mut camera = Camera {
141 position: glam::Vec3::new(0.0, 1.0, 2.0),
142 yaw: 0.0,
143 pitch: 0.0,
144 aspect: config.width as f32 / config.height as f32,
145 fov: 45.0,
146 znear: 0.1,
147 zfar: 100.0,
148 };
149 camera.look_at(glam::Vec3::ZERO);
150 let mut camera_uniform = CameraUniform::new();
151 camera_uniform.update(&camera);
152
153 let camera_buffer = device.create_buffer(&wgpu::BufferDescriptor {
154 label: Some("Camera Buffer"),
155 size: std::mem::size_of::<CameraUniform>() as u64,
156 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
157 mapped_at_creation: false,
158 });
159 queue.write_buffer(&camera_buffer, 0, crate::util::slice_to_bytes(&[camera_uniform]));
160
161 let camera_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
162 entries: &[
163 wgpu::BindGroupLayoutEntry {
164 binding: 0,
165 visibility: wgpu::ShaderStages::VERTEX,
166 ty: wgpu::BindingType::Buffer {
167 ty: wgpu::BufferBindingType::Uniform,
168 has_dynamic_offset: false,
169 min_binding_size: None,
170 },
171 count: None,
172 },
173 ],
174 label: Some("camera_bind_group_layout"),
175 });
176
177 let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
178 layout: &camera_bind_group_layout,
179 entries: &[
180 wgpu::BindGroupEntry {
181 binding: 0,
182 resource: camera_buffer.as_entire_binding(),
183 },
184 ],
185 label: Some("camera_bind_group"),
186 });
187
188 let world_sprite_pipeline = extras::create_world_sprite_pipeline(
189 &device, config.format, &texture_bind_group_layout, &camera_bind_group_layout,
190 );
191 let world_sprite_outline_pipeline = extras::create_world_sprite_outline_pipeline(
192 &device, config.format, &camera_bind_group_layout,
193 );
194
195 let model_transform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
196 entries: &[
197 wgpu::BindGroupLayoutEntry {
198 binding: 0,
199 visibility: wgpu::ShaderStages::VERTEX.union(wgpu::ShaderStages::FRAGMENT),
202 ty: wgpu::BindingType::Buffer {
203 ty: wgpu::BufferBindingType::Uniform,
204 has_dynamic_offset: false,
205 min_binding_size: None,
206 },
207 count: None,
208 },
209 ],
210 label: Some("model_transform_bind_group_layout"),
211 });
212
213 let model_pipeline = extras::create_model_pipeline(
214 &device, config.format, &texture_bind_group_layout, &camera_bind_group_layout, &model_transform_bind_group_layout,
215 );
216 let model_outline_pipeline = extras::create_model_outline_pipeline(
217 &device, config.format, &camera_bind_group_layout, &model_transform_bind_group_layout,
218 );
219
220 let depth_view = create_depth_view(&device, &config);
221
222 Self {
223 camera,
224 camera_uniform,
225 camera_buffer,
226 window,
227 device,
228 surface,
229 queue,
230 config,
231 size,
232 immediate_pipeline: shape_pipeline,
233 texture_bind_group_layout,
234 sampler,
235 sprite_pipeline,
236 world_sprite_pipeline,
237 world_sprite_outline_pipeline,
238 model_pipeline,
239 model_outline_pipeline,
240 model_transform_bind_group_layout,
241 camera_bind_group,
242 depth_view,
243 }
244 }
245
246 pub const DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
249
250 pub fn camera(&self) -> &Camera {
251 &self.camera
252 }
253
254 pub fn camera_mut(&mut self) -> &mut Camera {
255 &mut self.camera
256 }
257
258 pub fn window(&self) -> std::sync::Arc<winit::window::Window> {
262 self.window.clone()
263 }
264
265 pub fn device(&self) -> &wgpu::Device {
266 &self.device
267 }
268
269 pub fn queue(&self) -> &wgpu::Queue {
270 &self.queue
271 }
272
273 pub fn wgpu_surface_format(&self) -> wgpu::TextureFormat {
277 self.config.format
278 }
279
280 pub fn size(&self) -> winit::dpi::PhysicalSize<u32> {
281 self.size
282 }
283
284 pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
285 if new_size.width > 0 && new_size.height > 0 {
286 self.size = new_size;
287 self.config.width = new_size.width;
288 self.config.height = new_size.height;
289 self.surface.configure(&self.device, &self.config);
290 self.depth_view = create_depth_view(&self.device, &self.config);
291 self.camera.aspect = new_size.width as f32 / new_size.height as f32;
292 }
293 }
294
295 pub fn surface_format(&self) -> TextureFormat {
296 TextureFormat::from_wgpu(self.config.format)
297 }
298
299 pub fn render<T>(
310 &mut self,
311 clear_color: Color,
312 context: &mut T,
313 draw_fn: impl FnOnce(&mut T, &mut DrawPass),
314 overlay_fn: impl FnOnce(&mut T, &wgpu::Device, &wgpu::Queue, &mut wgpu::CommandEncoder, &wgpu::TextureView),
315 post_overlay_fn: impl FnOnce(&mut T, &mut DrawPass),
316 ) {
317 self.camera_uniform.update(&self.camera);
318 self.queue.write_buffer(&self.camera_buffer, 0, crate::util::slice_to_bytes(&[self.camera_uniform]));
319
320 let output = match self.surface.get_current_texture() {
321 wgpu::CurrentSurfaceTexture::Success(surface_texture) => surface_texture,
322 wgpu::CurrentSurfaceTexture::Suboptimal(surface_texture) => surface_texture,
323 wgpu::CurrentSurfaceTexture::Timeout => panic!("Surface timeout"),
324 wgpu::CurrentSurfaceTexture::Occluded => panic!("Surface occluded"),
325 wgpu::CurrentSurfaceTexture::Outdated => panic!("Surface outdated"),
326 wgpu::CurrentSurfaceTexture::Lost => panic!("Surface lost"),
327 wgpu::CurrentSurfaceTexture::Validation => todo!(),
328 };
329 let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
330 let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
331 label: Some("Render Encoder"),
332 });
333
334 {
335 let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
336 label: Some("Render Pass"),
337 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
338 view: &view,
339 resolve_target: None,
340 ops: wgpu::Operations {
341 load: wgpu::LoadOp::Clear(clear_color.as_wgpu_color()),
342 store: wgpu::StoreOp::Store,
343 },
344 depth_slice: None,
345 })],
346 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
347 view: &self.depth_view,
348 depth_ops: Some(wgpu::Operations {
349 load: wgpu::LoadOp::Clear(1.0),
350 store: wgpu::StoreOp::Store,
351 }),
352 stencil_ops: None,
353 }),
354 timestamp_writes: None,
355 occlusion_query_set: None,
356 multiview_mask: None,
357 });
358 let mut draw_pass = DrawPass {
359 pass: render_pass,
360 device: &self.device,
361 queue: &self.queue,
362 immediate_pipeline: &self.immediate_pipeline,
363 sprite_pipeline: &self.sprite_pipeline,
364 world_sprite_pipeline: &self.world_sprite_pipeline,
365 world_sprite_outline_pipeline: &self.world_sprite_outline_pipeline,
366 model_pipeline: &self.model_pipeline,
367 model_outline_pipeline: &self.model_outline_pipeline,
368 camera_bind_group: &self.camera_bind_group,
369 screen_w: self.size.width,
370 screen_h: self.size.height,
371 };
372 draw_fn(context, &mut draw_pass);
373 }
374
375 overlay_fn(context, &self.device, &self.queue, &mut encoder, &view);
376
377 {
378 let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
379 label: Some("Post-Overlay Pass"),
380 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
381 view: &view,
382 resolve_target: None,
383 ops: wgpu::Operations {
384 load: wgpu::LoadOp::Load,
385 store: wgpu::StoreOp::Store,
386 },
387 depth_slice: None,
388 })],
389 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
396 view: &self.depth_view,
397 depth_ops: Some(wgpu::Operations {
398 load: wgpu::LoadOp::Load,
399 store: wgpu::StoreOp::Store,
400 }),
401 stencil_ops: None,
402 }),
403 timestamp_writes: None,
404 occlusion_query_set: None,
405 multiview_mask: None,
406 });
407 let mut draw_pass = DrawPass {
408 pass: render_pass,
409 device: &self.device,
410 queue: &self.queue,
411 immediate_pipeline: &self.immediate_pipeline,
412 sprite_pipeline: &self.sprite_pipeline,
413 world_sprite_pipeline: &self.world_sprite_pipeline,
414 world_sprite_outline_pipeline: &self.world_sprite_outline_pipeline,
415 model_pipeline: &self.model_pipeline,
416 model_outline_pipeline: &self.model_outline_pipeline,
417 camera_bind_group: &self.camera_bind_group,
418 screen_w: self.size.width,
419 screen_h: self.size.height,
420 };
421 post_overlay_fn(context, &mut draw_pass);
422 }
423
424 self.queue.submit(std::iter::once(encoder.finish()));
425 self.queue.present(output);
426 }
427
428 pub fn create_shader_module(&self, desc: &ShaderModuleDescriptor) -> wgpu::ShaderModule {
429 let module = self.device.create_shader_module(wgpu::ShaderModuleDescriptor {
430 label: desc.label,
431 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(desc.source)),
432 });
433 module
434 }
435
436 pub fn create_render_pipeline(&self, desc: &RenderPipelineDescriptor) -> wgpu::RenderPipeline {
437 let attrs_per_buffer: Vec<Vec<wgpu::VertexAttribute>> = desc.vertex.buffers.iter()
438 .map(|vb| {
439 vb.attributes.iter().map(|a| wgpu::VertexAttribute {
440 format: a.format.to_wgpu(),
441 offset: a.offset,
442 shader_location: a.shader_location,
443 }).collect()
444 })
445 .collect();
446
447 let vertex_buffers: Vec<Option<wgpu::VertexBufferLayout>> = desc.vertex.buffers.iter().enumerate()
448 .map(|(i, vb)| Some(wgpu::VertexBufferLayout {
449 array_stride: vb.array_stride,
450 step_mode: vb.step_mode.to_wgpu(),
451 attributes: &attrs_per_buffer[i],
452 }))
453 .collect();
454
455 let primitive = wgpu::PrimitiveState {
456 topology: desc.primitive.topology.to_wgpu(),
457 strip_index_format: desc.primitive.strip_index_format.map(|f| f.to_wgpu()),
458 front_face: desc.primitive.front_face.to_wgpu(),
459 cull_mode: desc.primitive.cull_mode.to_wgpu(),
460 unclipped_depth: desc.primitive.unclipped_depth,
461 polygon_mode: desc.primitive.polygon_mode.to_wgpu(),
462 conservative: false,
463 };
464
465 let depth_stencil = Some(desc.depth_stencil.as_ref().map(|ds| wgpu::DepthStencilState {
469 format: ds.format.to_wgpu(),
470 depth_write_enabled: Some(ds.depth_write_enabled),
471 depth_compare: Some(ds.depth_compare.to_wgpu()),
472 stencil: wgpu::StencilState {
473 front: wgpu::StencilFaceState {
474 compare: ds.stencil.front.compare.to_wgpu(),
475 fail_op: ds.stencil.front.fail_op.to_wgpu(),
476 depth_fail_op: ds.stencil.front.depth_fail_op.to_wgpu(),
477 pass_op: ds.stencil.front.pass_op.to_wgpu(),
478 },
479 back: wgpu::StencilFaceState {
480 compare: ds.stencil.back.compare.to_wgpu(),
481 fail_op: ds.stencil.back.fail_op.to_wgpu(),
482 depth_fail_op: ds.stencil.back.depth_fail_op.to_wgpu(),
483 pass_op: ds.stencil.back.pass_op.to_wgpu(),
484 },
485 read_mask: ds.stencil.read_mask,
486 write_mask: ds.stencil.write_mask,
487 },
488 bias: wgpu::DepthBiasState {
489 constant: ds.bias.bias,
490 slope_scale: ds.bias.slope_scale,
491 clamp: ds.bias.clamp,
492 },
493 }).unwrap_or_else(extras::overlay_depth_state));
494
495 let multisample = wgpu::MultisampleState {
496 count: desc.multisample.count,
497 mask: desc.multisample.mask,
498 alpha_to_coverage_enabled: desc.multisample.alpha_to_coverage_enabled,
499 };
500
501 let color_targets: Vec<Option<wgpu::ColorTargetState>> = match &desc.fragment {
502 Some(frag) => {
503 frag.targets.iter().map(|t| {
504 Some(wgpu::ColorTargetState {
505 format: t.format.to_wgpu(),
506 blend: t.blend.as_ref().map(|b| wgpu::BlendState {
507 color: wgpu::BlendComponent {
508 src_factor: b.color.src_factor.to_wgpu(),
509 dst_factor: b.color.dst_factor.to_wgpu(),
510 operation: b.color.operation.to_wgpu(),
511 },
512 alpha: wgpu::BlendComponent {
513 src_factor: b.alpha.src_factor.to_wgpu(),
514 dst_factor: b.alpha.dst_factor.to_wgpu(),
515 operation: b.alpha.operation.to_wgpu(),
516 },
517 }),
518 write_mask: t.write_mask.to_wgpu(),
519 })
520 }).collect()
521 }
522 None => vec![],
523 };
524
525 let wgpu_desc = wgpu::RenderPipelineDescriptor {
526 label: desc.label,
527 layout: None,
528 vertex: wgpu::VertexState {
529 module: &desc.vertex.module,
530 entry_point: Some(desc.vertex.entry_point),
531 buffers: &vertex_buffers,
532 compilation_options: wgpu::PipelineCompilationOptions::default(),
533 },
534 primitive,
535 depth_stencil,
536 multisample,
537 fragment: desc.fragment.as_ref().map(|frag| wgpu::FragmentState {
538 module: &frag.module,
539 entry_point: Some(frag.entry_point),
540 targets: &color_targets,
541 compilation_options: wgpu::PipelineCompilationOptions::default(),
542 }),
543 multiview_mask: None,
544 cache: None,
545 };
546
547 let pipeline = self.device.create_render_pipeline(&wgpu_desc);
548 pipeline
549 }
550
551 pub fn create_buffer_init(&self, desc: &BufferInitDescriptor) -> wgpu::Buffer {
552 let usage = desc.usage.to_wgpu() | wgpu::BufferUsages::COPY_DST;
553 let buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
554 label: desc.label,
555 size: desc.contents.len() as u64,
556 usage,
557 mapped_at_creation: false,
558 });
559 self.queue.write_buffer(&buffer, 0, desc.contents);
560 buffer
561 }
562
563 pub fn create_buffer(&self, desc: &BufferDescriptor) -> wgpu::Buffer {
564 let buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
565 label: desc.label,
566 size: desc.size,
567 usage: desc.usage.to_wgpu(),
568 mapped_at_creation: false,
569 });
570 buffer
571 }
572}