1use std::ops::{Deref, DerefMut};
4
5use downcast_rs::{impl_downcast, DowncastSync};
6use fxhash::FxHashMap;
7use glam::{Mat4, UVec2};
8use wgpu::{
9 util::{BufferInitDescriptor, DeviceExt},
10 Buffer, CommandEncoder, Device, Queue, RenderPass, Sampler, Surface, SurfaceConfiguration,
11 TextureView,
12};
13use winit::{dpi::PhysicalSize, window::Window};
14
15use crate::{game_data, GameInit};
16
17pub struct Graphics {
19 pub device: Device,
20 pub queue: Queue,
21 pub surface: Surface,
22 pub config: SurfaceConfiguration,
23}
24
25impl Graphics {
26 pub(crate) async unsafe fn new(window: &Window, game_init: &GameInit) -> Self {
27 let size = window.inner_size();
28
29 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
30 backends: wgpu::Backends::all(),
31 dx12_shader_compiler: wgpu::Dx12Compiler::default(),
32 });
33 let surface = unsafe {
34 instance.create_surface(&window)
36 }
37 .unwrap();
38 let adapter = instance
39 .request_adapter(&wgpu::RequestAdapterOptions {
40 power_preference: wgpu::PowerPreference::default(),
41 force_fallback_adapter: false,
42 compatible_surface: Some(&surface),
43 })
44 .await
45 .unwrap();
46
47 let (device, queue) = adapter
48 .request_device(
49 &wgpu::DeviceDescriptor {
50 label: None,
51 features: game_init.features,
52 limits: wgpu::Limits::default(),
53 },
54 None,
55 )
56 .await
57 .unwrap();
58
59 let config = wgpu::SurfaceConfiguration {
60 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
61 format: surface
62 .get_capabilities(&adapter)
63 .formats
64 .into_iter()
65 .find(|f| f.describe().srgb)
66 .expect("Could not get compatible surface format"),
67 width: size.width,
68 height: size.height,
69 present_mode: game_init.present_mode,
70 alpha_mode: wgpu::CompositeAlphaMode::Auto,
71 view_formats: vec![],
72 };
73 surface.configure(&device, &config);
74
75 Self {
76 device,
77 queue,
78 surface,
79 config,
80 }
81 }
82
83 pub(crate) fn render(&self, renderer: &mut dyn Renderer) -> 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 { label: None });
92
93 {
94 renderer.render(&view, &mut encoder);
95 }
96
97 self.queue.submit(std::iter::once(encoder.finish()));
98 output.present();
99
100 Ok(())
101 }
102
103 pub(crate) fn resize(&mut self, size: UVec2) {
104 let size = PhysicalSize::new(size.x, size.y);
105 self.config.width = size.width;
106 self.config.height = size.height;
107 self.surface.configure(&self.device, &self.config);
108 }
109}
110
111#[allow(unused)]
112pub trait Renderer: DowncastSync {
114 fn render(&mut self, view: &TextureView, encoder: &mut CommandEncoder) {}
116 fn render_child<'a, 'b: 'a>(&'b self, render_pass: &mut RenderPass<'a>) {}
119 fn update(&mut self) {}
122 fn resize(&mut self, size: UVec2) {}
125}
126
127impl_downcast!(sync Renderer);
128
129pub struct EmptyRenderer;
131
132impl Renderer for EmptyRenderer {}
133
134#[derive(Debug, Default)]
135pub struct SamplerManager {
136 pub samplers: FxHashMap<SamplerType, Sampler>,
137}
138
139impl SamplerManager {
140 #[must_use]
141 pub fn new() -> Self {
142 Self {
143 samplers: FxHashMap::default(),
144 }
145 }
146
147 pub fn get(&mut self, ty: SamplerType) -> &Sampler {
148 self.samplers.entry(ty).or_insert_with(|| (&ty).into())
149 }
150}
151
152#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
153pub struct SamplerType {
154 pub address_mode: wgpu::AddressMode,
155 pub filter_mode: wgpu::FilterMode,
156}
157
158impl SamplerType {
159 pub const LINEAR: Self = Self {
160 address_mode: wgpu::AddressMode::Repeat,
161 filter_mode: wgpu::FilterMode::Linear,
162 };
163
164 pub const NEAREST: Self = Self {
165 address_mode: wgpu::AddressMode::Repeat,
166 filter_mode: wgpu::FilterMode::Nearest,
167 };
168}
169
170impl Default for SamplerType {
171 fn default() -> Self {
172 Self {
173 address_mode: wgpu::AddressMode::Repeat,
174 filter_mode: wgpu::FilterMode::Linear,
175 }
176 }
177}
178
179impl From<&SamplerType> for Sampler {
180 fn from(value: &SamplerType) -> Self {
181 game_data()
182 .graphics()
183 .device
184 .create_sampler(&wgpu::SamplerDescriptor {
185 address_mode_u: value.address_mode,
186 address_mode_v: value.address_mode,
187 address_mode_w: value.address_mode,
188 mag_filter: value.filter_mode,
189 min_filter: value.filter_mode,
190 border_color: match value.address_mode {
192 wgpu::AddressMode::ClampToBorder => Some(wgpu::SamplerBorderColor::OpaqueBlack),
193 _ => None,
194 },
195 ..Default::default()
196 })
197 }
198}
199
200#[must_use]
201pub const fn size_to_uvec2(size: PhysicalSize<u32>) -> UVec2 {
202 UVec2::new(size.width, size.height)
203}
204
205pub struct BufferData<T: BufferCompatible> {
212 pub values: Vec<T>,
214 pub buffer: Buffer,
216 pub modified: bool,
219 pub buffer_len: u32,
220}
221
222impl<T: BufferCompatible> BufferData<T> {
223 pub fn new(values: Vec<T>, usage: wgpu::BufferUsages) -> Self {
224 let buffer = game_data()
225 .graphics()
226 .device
227 .create_buffer_init(&BufferInitDescriptor {
228 label: None,
229 contents: bytemuck::cast_slice(&values.iter().map(T::as_pod).collect::<Vec<_>>()),
230 usage,
231 });
232
233 Self {
234 buffer_len: values.len() as u32,
235 values,
236 buffer,
237 modified: false,
238 }
239 }
240
241 pub fn update(&mut self) {
242 if self.modified {
244 if self.needs_new_buffer() {
245 self.buffer =
246 game_data()
247 .graphics()
248 .device
249 .create_buffer_init(&BufferInitDescriptor {
250 label: None,
251 contents: bytemuck::cast_slice(
252 &self.values.iter().map(T::as_pod).collect::<Vec<_>>(),
253 ),
254 usage: self.buffer.usage(),
255 });
256 self.buffer_len = self.values.len() as u32;
257 } else {
258 game_data().graphics().queue.write_buffer(
259 &self.buffer,
260 0,
261 bytemuck::cast_slice(&self.values.iter().map(T::as_pod).collect::<Vec<_>>()),
262 );
263 }
264 self.modified = false;
265 }
266 }
267
268 pub fn needs_new_buffer(&self) -> bool {
271 self.buffer_len < self.values.len() as u32
272 }
273
274 pub fn get(&mut self) -> &Buffer {
276 if self.modified {
277 self.update();
278 self.modified = false;
279 }
280 &self.buffer
281 }
282
283 pub fn push(&mut self, value: T) -> usize {
284 self.modified = true;
285 self.values.push(value);
286 self.values.len() - 1
287 }
288
289 pub fn replace(&mut self, values: Vec<T>) {
290 self.modified = true;
291 self.values = values;
292 }
293
294 pub fn remove(&mut self, index: usize) -> T {
295 self.modified = true;
296 self.values.remove(index)
297 }
298
299 pub fn buffer_slice(&self) -> wgpu::BufferSlice {
300 self.buffer.slice(..)
301 }
302
303 pub fn default(usage: wgpu::BufferUsages) -> Self
305 where
306 T: Default,
307 {
308 Self::new(vec![T::default()], usage)
309 }
310}
311
312impl<T: BufferCompatible> Deref for BufferData<T> {
313 type Target = [T];
314
315 fn deref(&self) -> &Self::Target {
316 &self.values
317 }
318}
319
320impl<T: BufferCompatible> DerefMut for BufferData<T> {
321 fn deref_mut(&mut self) -> &mut Self::Target {
322 self.modified = true;
323 &mut self.values
324 }
325}
326
327pub trait BufferCompatible {
330 type PodFormat: bytemuck::Pod;
332 fn as_pod(&self) -> Self::PodFormat;
334}
335
336impl<T> BufferCompatible for T
337where
338 T: bytemuck::Pod,
339{
340 type PodFormat = T;
341 fn as_pod(&self) -> Self::PodFormat {
342 *self
343 }
344}
345
346pub trait Bindable {
348 fn bind_group_layout() -> wgpu::BindGroupLayout;
349}
350
351impl Bindable for Mat4 {
352 fn bind_group_layout() -> wgpu::BindGroupLayout {
353 game_data()
354 .graphics()
355 .device
356 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
357 label: None,
358 entries: &[wgpu::BindGroupLayoutEntry {
359 binding: 0,
360 visibility: wgpu::ShaderStages::VERTEX,
361 ty: wgpu::BindingType::Buffer {
362 ty: wgpu::BufferBindingType::Uniform,
363 has_dynamic_offset: false,
364 min_binding_size: None,
365 },
366 count: None,
367 }],
368 })
369 }
370}