1use image::GenericImageView;
8use wgpu::{BindGroupLayout, Sampler, Texture, TextureView, util::DeviceExt};
9
10use crate::{
11 InstanceRaw, UnsignedIntType,
12 prelude::{Shaders, Textures, UniformBuffers, Vertex},
13};
14
15#[derive(Debug)]
18pub struct Pipeline {
19 pub shader: PipelineData<crate::Shaders>,
21 pub vertex_buffer: PipelineData<VertexBuffers>,
23 pub texture: PipelineData<crate::Textures>,
25 pub uniform: PipelineData<Option<crate::UniformBuffers>>,
27}
28unsafe impl Send for Pipeline {}
29unsafe impl Sync for Pipeline {}
30
31#[derive(Debug)]
33pub enum PipelineData<T> {
34 Copy(std::sync::Arc<str>),
36 Data(T),
38}
39
40#[derive(Debug)]
42pub struct VertexBuffers {
43 pub vertex_buffer: wgpu::Buffer,
46 pub index_buffer: wgpu::Buffer,
49 pub length: u32,
51}
52unsafe impl Send for VertexBuffers {}
53unsafe impl Sync for VertexBuffers {}
54
55#[derive(Debug, Clone)]
57pub enum TextureData {
58 Bytes(Vec<u8>),
60 Image(image::DynamicImage),
62 Path(String),
64}
65unsafe impl Send for TextureData {}
66unsafe impl Sync for TextureData {}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum TextureMode {
71 Clamp,
73 Repeat,
75 MirrorRepeat,
77}
78unsafe impl Send for TextureMode {}
79unsafe impl Sync for TextureMode {}
80
81#[derive(Debug, Clone, Copy)]
83pub struct ShaderSettings {
84 pub topology: crate::ShaderPrimitive,
87 pub strip_index_format: Option<crate::IndexFormat>,
91 pub front_face: crate::FrontFace,
94 pub cull_mode: Option<crate::CullMode>,
96 pub polygon_mode: crate::PolygonMode,
102 pub clamp_depth: bool,
108 pub conservative: bool,
115 pub count: u32,
119 pub mask: u64,
123 pub alpha_to_coverage_enabled: bool,
132}
133impl Default for ShaderSettings {
134 fn default() -> Self {
135 Self {
136 topology: wgpu::PrimitiveTopology::TriangleList,
137 strip_index_format: None,
138 front_face: wgpu::FrontFace::Ccw,
139 cull_mode: Some(wgpu::Face::Back),
140 polygon_mode: wgpu::PolygonMode::Fill,
141 clamp_depth: false,
142 conservative: false,
143 count: 1,
144 mask: !0,
145 alpha_to_coverage_enabled: true,
146 }
147 }
148}
149unsafe impl Send for ShaderSettings {}
150unsafe impl Sync for ShaderSettings {}
151
152pub fn pixel_to_cartesian(value: f32, max: u32) -> f32 {
154 let mut result = value / max as f32;
155
156 if value == max as f32 {
157 result = 0.0;
158 } else if result < max as f32 / 2.0 {
159 }
160
161 if result > -1.0 { result } else { -1.0 }
162}
163
164impl crate::prelude::Renderer {
165 pub fn build_pipeline(
167 &mut self,
168 shader: Shaders,
169 vertex_buffer: VertexBuffers,
170 texture: Textures,
171 uniform: Option<UniformBuffers>,
172 ) -> Pipeline {
173 Pipeline {
174 shader: PipelineData::Data(shader),
175 vertex_buffer: PipelineData::Data(vertex_buffer),
176 texture: PipelineData::Data(texture),
177 uniform: PipelineData::Data(uniform),
178 }
179 }
180
181 pub fn build_shader(
183 &mut self,
184 name: impl AsRef<str>,
185 shader_source: String,
186 uniform_layout: Option<&BindGroupLayout>,
187 settings: ShaderSettings,
188 ) -> Shaders {
189 let shader = self
190 .device
191 .create_shader_module(wgpu::ShaderModuleDescriptor {
192 label: Some(format!("{} Shader", name.as_ref()).as_str()),
193 source: wgpu::ShaderSource::Wgsl(shader_source.into()),
194 });
195
196 let mut bind_group_layouts = vec![
197 &self.texture_bind_group_layout,
198 &self.default_uniform_bind_group_layout,
199 ];
200 if let Some(uniform_layout) = uniform_layout {
201 bind_group_layouts.push(uniform_layout);
202 }
203
204 let render_pipeline_layout =
205 self.device
206 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
207 label: Some("Render Pipeline Layout"),
208 bind_group_layouts: bind_group_layouts.as_slice(),
209 push_constant_ranges: &[],
210 });
211
212 let render_pipeline = self
213 .device
214 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
215 label: Some(name.as_ref()),
216 layout: Some(&render_pipeline_layout),
217 vertex: wgpu::VertexState {
218 module: &shader,
219 entry_point: Some("vs_main"),
220 buffers: &[Vertex::desc(), InstanceRaw::desc()],
221 compilation_options: wgpu::PipelineCompilationOptions::default(),
222 },
223 fragment: Some(wgpu::FragmentState {
224 module: &shader,
225 entry_point: Some("fs_main"),
226 targets: &[Some(wgpu::ColorTargetState {
227 format: self.config.format,
228 write_mask: wgpu::ColorWrites::ALL,
229 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
230 })],
231 compilation_options: wgpu::PipelineCompilationOptions::default(),
232 }),
233 primitive: wgpu::PrimitiveState {
234 topology: settings.topology,
235 strip_index_format: settings.strip_index_format,
236 front_face: settings.front_face,
237 cull_mode: settings.cull_mode, polygon_mode: settings.polygon_mode,
239 conservative: settings.conservative,
240 unclipped_depth: false,
242 },
243 depth_stencil: Some(wgpu::DepthStencilState {
244 format: crate::DEPTH_FORMAT,
245 depth_write_enabled: true,
246 depth_compare: wgpu::CompareFunction::Less,
247 stencil: wgpu::StencilState::default(),
248 bias: wgpu::DepthBiasState::default(),
249 }),
250 multisample: wgpu::MultisampleState {
251 count: settings.count,
252 mask: settings.mask,
253 alpha_to_coverage_enabled: settings.alpha_to_coverage_enabled,
254 },
255 multiview: None,
256 cache: None,
257 });
258
259 render_pipeline
260 }
261
262 pub fn build_texture(
264 &mut self,
265 name: impl AsRef<str>,
266 texture_data: TextureData,
267 texture_mode: TextureMode,
268 ) -> Result<Textures, crate::error::Error> {
269 let mode: wgpu::AddressMode = match texture_mode {
270 TextureMode::Clamp => wgpu::AddressMode::Repeat,
271 TextureMode::Repeat => wgpu::AddressMode::MirrorRepeat,
272 TextureMode::MirrorRepeat => wgpu::AddressMode::ClampToEdge,
273 };
274
275 let img = match texture_data {
276 TextureData::Bytes(data) => image::load_from_memory(data.as_slice())?,
277 TextureData::Image(data) => data,
278 TextureData::Path(path) => image::open(path)?,
279 };
280
281 fn rgba_to_bgra_pixels_par(
282 mut buf: image::ImageBuffer<image::Rgba<u8>, Vec<u8>>,
283 ) -> image::ImageBuffer<image::Rgba<u8>, Vec<u8>> {
284 buf.enumerate_pixels_mut().for_each(|(_, _, px)| {
285 let data = px.0;
286 *px = image::Rgba([data[2], data[1], data[0], data[3]]);
287 });
288 buf
289 }
290
291 let pixels = match self.config.format {
292 wgpu::TextureFormat::Bgra8Unorm => rgba_to_bgra_pixels_par(img.to_rgba8()),
293 wgpu::TextureFormat::Bgra8UnormSrgb => rgba_to_bgra_pixels_par(img.to_rgba8()),
294 _ => img.to_rgba8(),
295 };
296 let dimensions = img.dimensions();
297
298 let size = wgpu::Extent3d {
299 width: dimensions.0,
300 height: dimensions.1,
301 depth_or_array_layers: 1,
302 };
303 let texture = self.device.create_texture(&wgpu::TextureDescriptor {
304 label: Some(name.as_ref()),
305 size,
306 mip_level_count: 1,
307 sample_count: 1,
308 dimension: wgpu::TextureDimension::D2,
309 format: self.config.format,
310 usage: wgpu::TextureUsages::TEXTURE_BINDING
311 | wgpu::TextureUsages::COPY_DST
312 | wgpu::TextureUsages::COPY_SRC
313 | wgpu::TextureUsages::RENDER_ATTACHMENT,
314 view_formats: &[],
315 });
316
317 self.queue.write_texture(
318 wgpu::TexelCopyTextureInfo {
319 texture: &texture,
320 mip_level: 0,
321 origin: wgpu::Origin3d::ZERO,
322 aspect: wgpu::TextureAspect::All,
323 },
324 &pixels,
325 wgpu::TexelCopyBufferLayout {
326 offset: 0,
327 bytes_per_row: Some(4 * dimensions.0),
328 rows_per_image: Some(dimensions.1),
329 },
330 size,
331 );
332
333 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
334 let sampler = self.device.create_sampler(&wgpu::SamplerDescriptor {
335 address_mode_u: mode,
336 address_mode_v: mode,
337 address_mode_w: mode,
338 mag_filter: wgpu::FilterMode::Linear,
339 min_filter: wgpu::FilterMode::Nearest,
340 mipmap_filter: wgpu::FilterMode::Nearest,
341 ..Default::default()
342 });
343
344 let diffuse_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
345 layout: &self.texture_bind_group_layout,
346 label: Some("Diffuse Bind Group"),
347 entries: &[
348 wgpu::BindGroupEntry {
349 binding: 0,
350 resource: wgpu::BindingResource::TextureView(&view),
351 },
352 wgpu::BindGroupEntry {
353 binding: 1,
354 resource: wgpu::BindingResource::Sampler(&sampler),
355 },
356 ],
357 });
358
359 Ok(diffuse_bind_group)
360 }
361
362 pub(crate) fn build_depth_buffer(
363 label: impl AsRef<str>,
364 device: &wgpu::Device,
365 config: &wgpu::SurfaceConfiguration,
366 ) -> (Texture, TextureView, Sampler) {
367 let size = wgpu::Extent3d {
368 width: config.width,
369 height: config.height,
370 depth_or_array_layers: 1,
371 };
372 let desc = wgpu::TextureDescriptor {
373 label: Some(label.as_ref()),
374 size,
375 mip_level_count: 1,
376 sample_count: 1,
377 dimension: wgpu::TextureDimension::D2,
378 format: crate::DEPTH_FORMAT,
379 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
380 view_formats: &[wgpu::TextureFormat::Depth32Float],
381 };
382 let texture = device.create_texture(&desc);
383
384 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
385 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
386 address_mode_u: wgpu::AddressMode::ClampToEdge,
387 address_mode_v: wgpu::AddressMode::ClampToEdge,
388 address_mode_w: wgpu::AddressMode::ClampToEdge,
389 mag_filter: wgpu::FilterMode::Linear,
390 min_filter: wgpu::FilterMode::Linear,
391 mipmap_filter: wgpu::FilterMode::Nearest,
392 compare: Some(wgpu::CompareFunction::LessEqual),
393 lod_min_clamp: 0.0,
394 lod_max_clamp: 100.0,
395 ..Default::default()
396 });
397
398 (texture, view, sampler)
399 }
400
401 pub fn build_uniform_buffer_part<T: bytemuck::Zeroable + bytemuck::Pod>(
405 &self,
406 name: impl AsRef<str>,
407 value: T,
408 ) -> wgpu::Buffer {
409 self.device
410 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
411 label: Some(name.as_ref()),
412 contents: bytemuck::cast_slice(&[value]),
413 usage: wgpu::BufferUsages::UNIFORM,
414 })
415 }
416
417 pub fn build_uniform_buffer(
419 &mut self,
420 uniforms: &[wgpu::Buffer],
421 ) -> (UniformBuffers, BindGroupLayout) {
422 let mut buffer_entry = Vec::<wgpu::BindGroupEntry>::new();
423 let mut buffer_layout = Vec::<wgpu::BindGroupLayoutEntry>::new();
424
425 for i in 0..uniforms.len() {
426 if let Some(uniform) = uniforms.get(i) {
427 let descriptor = wgpu::BindGroupEntry {
428 binding: i as u32,
429 resource: uniform.as_entire_binding(),
430 };
431 buffer_entry.push(descriptor);
432 buffer_layout.push(wgpu::BindGroupLayoutEntry {
433 binding: i as u32,
434 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
435 ty: wgpu::BindingType::Buffer {
436 ty: wgpu::BufferBindingType::Uniform,
437 has_dynamic_offset: false,
438 min_binding_size: None,
439 },
440 count: None,
441 });
442 }
443 }
444
445 let uniform_bind_group_layout =
446 self.device
447 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
448 label: Some("uniform dynamic bind group layout"),
449 entries: buffer_layout.as_slice(),
450 });
451
452 let uniform_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
453 label: Some("Uniform Bind Groups"),
454 layout: &uniform_bind_group_layout,
455 entries: buffer_entry.as_slice(),
456 });
457
458 (uniform_bind_group, uniform_bind_group_layout)
459 }
460
461 pub fn build_vertex_buffer(
463 &mut self,
464 vertices: &Vec<Vertex>,
465 indices: &Vec<UnsignedIntType>,
466 ) -> VertexBuffers {
467 let vertex_buffer = self
468 .device
469 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
470 label: Some("Vertex Buffer"),
471 contents: bytemuck::cast_slice(vertices.as_slice()),
472 usage: wgpu::BufferUsages::VERTEX,
473 });
474
475 let index_buffer = self
476 .device
477 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
478 label: Some("Index Buffer"),
479 contents: bytemuck::cast_slice(indices.as_slice()),
480 usage: wgpu::BufferUsages::INDEX,
481 });
482
483 VertexBuffers {
484 vertex_buffer,
485 index_buffer,
486 length: indices.len() as u32,
487 }
488 }
489
490 pub fn build_instance(&self, instance_data: Vec<InstanceRaw>) -> wgpu::Buffer {
492 self.device
493 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
494 label: Some("Instance Buffer"),
495 contents: bytemuck::cast_slice(&instance_data),
496 usage: wgpu::BufferUsages::VERTEX,
497 })
498 }
499}
500
501impl crate::SignalStorage {
502 pub fn new() -> Self {
504 Self { events: vec![] }
505 }
506
507 pub fn add_signal(&mut self, key: impl AsRef<str>, event: Box<dyn crate::Signal>) {
509 self.events.push((key.as_ref().to_string(), event));
510 }
511
512 pub fn remove_signal(&mut self, key: impl AsRef<str>) {
514 self.events.retain(|k| k.0 != key.as_ref());
515 }
516
517 pub fn get_signal<T: 'static>(
519 &mut self,
520 key: impl AsRef<str>,
521 ) -> Option<Result<&mut T, downcast::TypeMismatch>> {
522 let event = self
524 .events
525 .iter_mut()
526 .find(|k| k.0 == key.as_ref())
527 .map(|k| &mut k.1);
528
529 if let Some(event) = event {
530 let event_type = event.downcast_mut::<T>();
532 Some(event_type)
533 } else {
534 None
535 }
536 }
537}
538
539impl Default for crate::SignalStorage {
540 fn default() -> Self {
541 Self::new()
542 }
543}