peter_engine/
graphics.rs

1use std::sync::Arc;
2
3use nalgebra::{Matrix4, Point3, UnitQuaternion, Vector3};
4use wgpu::util::DeviceExt;
5use wgpu::{BindGroupLayoutEntry, BufferSlice};
6
7use crate::mipmapping::MipMapGen;
8use crate::PeterEngineApp;
9
10const DATA_TEXTURE_SIZE: usize = 2048;
11pub const MSAA_COUNT: u32 = 1;
12pub const MIP_LEVEL_COUNT: u32 = 4;
13
14pub const LINEAR_SAMPLER_DESCRIPTOR: wgpu::SamplerDescriptor = wgpu::SamplerDescriptor {
15  label:            Some("linear_sampler"),
16  address_mode_u:   wgpu::AddressMode::ClampToEdge,
17  address_mode_v:   wgpu::AddressMode::ClampToEdge,
18  address_mode_w:   wgpu::AddressMode::ClampToEdge,
19  mag_filter:       wgpu::FilterMode::Linear,
20  min_filter:       wgpu::FilterMode::Linear,
21  mipmap_filter:    wgpu::FilterMode::Linear,
22  lod_min_clamp:    0.0,
23  lod_max_clamp:    32.0,
24  compare:          None,
25  anisotropy_clamp: 1,
26  border_color:     None,
27};
28
29pub const NEAREST_SAMPLER_DESCRIPTOR: wgpu::SamplerDescriptor = wgpu::SamplerDescriptor {
30  label:            Some("nearest_sampler"),
31  address_mode_u:   wgpu::AddressMode::ClampToEdge,
32  address_mode_v:   wgpu::AddressMode::ClampToEdge,
33  address_mode_w:   wgpu::AddressMode::ClampToEdge,
34  mag_filter:       wgpu::FilterMode::Nearest,
35  min_filter:       wgpu::FilterMode::Nearest,
36  mipmap_filter:    wgpu::FilterMode::Nearest,
37  lod_min_clamp:    0.0,
38  lod_max_clamp:    32.0,
39  compare:          None,
40  anisotropy_clamp: 1,
41  border_color:     None,
42};
43
44pub struct Transform {
45  pub translation: Vector3<f32>,
46  pub rotation:    UnitQuaternion<f32>,
47  pub scale:       f32,
48}
49
50impl Default for Transform {
51  fn default() -> Self {
52    Self {
53      translation: Vector3::new(0.0, 0.0, 0.0),
54      rotation:    UnitQuaternion::identity(),
55      scale:       1.0,
56    }
57  }
58}
59
60impl Transform {
61  pub fn apply(&self, point: Point3<f32>) -> Point3<f32> {
62    return self.scale * (self.rotation * point) + self.translation;
63  }
64}
65
66pub struct Projection {
67  pub clip_near:         f32,
68  pub clip_far:          f32,
69  pub model_matrix:      Matrix4<f32>,
70  pub view_matrix:       Matrix4<f32>,
71  pub projection_matrix: Matrix4<f32>,
72  pub derived_pvm:       Matrix4<f32>,
73  pub derived_vm:        Matrix4<f32>,
74}
75
76impl Default for Projection {
77  fn default() -> Self {
78    Self {
79      clip_near:         0.1,
80      clip_far:          100.0,
81      model_matrix:      Matrix4::identity(),
82      view_matrix:       Matrix4::identity(),
83      projection_matrix: Matrix4::identity(),
84      derived_vm:        Matrix4::identity(),
85      derived_pvm:       Matrix4::identity(),
86    }
87  }
88}
89
90impl Projection {
91  fn recompute_derived(&mut self) {
92    self.derived_vm = self.view_matrix * self.model_matrix;
93    self.derived_pvm = self.projection_matrix * self.derived_vm;
94  }
95
96  pub fn set_model_matrix(&mut self, model_matrix: Matrix4<f32>) {
97    self.model_matrix = model_matrix;
98    self.recompute_derived();
99  }
100
101  pub fn set_view_matrix(&mut self, view_matrix: Matrix4<f32>) {
102    self.view_matrix = view_matrix;
103    self.recompute_derived();
104  }
105
106  pub fn set_projection_matrix(&mut self, projection_matrix: Matrix4<f32>) {
107    self.projection_matrix = projection_matrix;
108    self.recompute_derived();
109  }
110
111  pub fn set_model_transform(&mut self, model_transform: Transform) {
112    let mut model_matrix = Matrix4::identity();
113    let rotation = model_transform.rotation.to_rotation_matrix();
114    model_matrix.fixed_view_mut::<3, 3>(0, 0).copy_from(&rotation.matrix());
115    model_matrix.fixed_view_mut::<3, 1>(0, 3).copy_from(&model_transform.translation);
116    model_matrix.m43 *= model_transform.scale;
117    self.set_model_matrix(model_matrix);
118  }
119
120  #[rustfmt::skip]
121  pub fn set_view_look_at(&mut self, eye: Point3<f32>, look_at: Point3<f32>) {
122    let view = (look_at - eye).normalize();
123    let right = Vector3::y().cross(&view).normalize();
124    let up = view.cross(&right);
125    #[rustfmt::skip]
126    let view_matrix = Matrix4::new(
127      right.x, right.y, right.z, -right.dot(&eye.coords),
128      up.x,    up.y,    up.z,    -up.dot(&eye.coords),
129      view.x,  view.y,  view.z,  -view.dot(&eye.coords),
130      0.0,     0.0,     0.0,      1.0,
131    );
132    self.set_view_matrix(view_matrix);
133  }
134
135  /// fov_deg is the vertical field of view in degrees
136  /// aspect is width / height
137  #[rustfmt::skip]
138  pub fn set_perspective_projection(&mut self, fov_deg: f32, aspect: f32, near: f32, far: f32) {
139    self.clip_near = near;
140    self.clip_far = far;
141    let fov_rad = fov_deg * std::f32::consts::PI / 180.0;
142    let height = 1.0 / (fov_rad / 2.0).tan();
143    let width = height * aspect;
144    let m22 = far / (far - near);
145    #[rustfmt::skip]
146    let projection_matrix = Matrix4::new(
147      width, 0.0,    0.0,  0.0,
148      0.0,   height, 0.0,  0.0,
149      0.0,   0.0,    m22, -near * m22,
150      0.0,   0.0,    1.0,  0.0,
151    );
152    self.set_projection_matrix(projection_matrix);
153  }
154
155  #[rustfmt::skip]
156  pub fn set_ortho_projection(
157    &mut self,
158    w: f32,
159    h: f32,
160    near: f32,
161    far: f32,
162  ) {
163    self.clip_near = near;
164    self.clip_far = far;
165    let n = near;
166    let f = far;
167    #[rustfmt::skip]
168    let projection_matrix = Matrix4::new(
169      2.0/w,  0.0,    0.0,      -1.0,
170      0.0,   -2.0/h,  0.0,       1.0,
171      0.0,    0.0,    1.0/(f-n), n/(f-n),
172      0.0,    0.0,    0.0,       1.0,
173    );
174    self.set_projection_matrix(projection_matrix);
175  }
176
177  pub fn update_uniforms(&mut self, uniforms: &mut Uniforms) {
178    uniforms.transform_m = self.model_matrix.into();
179    uniforms.transform_vm = self.derived_vm.into();
180    uniforms.transform_pvm = self.derived_pvm.into();
181    if let Some(inv) = self.derived_pvm.try_inverse() {
182      uniforms.transform_pvm_inv = inv.into();
183    }
184    uniforms.near = self.clip_near;
185    uniforms.far = self.clip_far;
186  }
187
188  pub fn screen_space_to_world_space(&self, screen_space: Point3<f32>) -> Point3<f32> {
189    let inv_pvm = self.derived_pvm.try_inverse().unwrap();
190    inv_pvm.transform_point(&screen_space)
191  }
192}
193
194// ===== wgpu interfacing =====
195
196#[repr(C, align(16))]
197#[derive(Debug, Clone, Copy, Default)]
198pub struct Vertex {
199  pub position: [f32; 3],
200  pub color:    [f32; 4],
201  pub uv:       [f32; 2],
202}
203
204impl Vertex {
205  const ATTRIBS: [wgpu::VertexAttribute; 3] =
206    wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x4, 2 => Float32x2];
207
208  pub fn desc() -> wgpu::VertexBufferLayout<'static> {
209    wgpu::VertexBufferLayout {
210      array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
211      step_mode:    wgpu::VertexStepMode::Vertex,
212      attributes:   &Self::ATTRIBS,
213    }
214  }
215}
216
217#[repr(C, align(16))]
218#[derive(Debug, Clone)]
219pub struct Uniforms {
220  pub transform_m:       [[f32; 4]; 4],
221  pub transform_vm:      [[f32; 4]; 4],
222  pub transform_pvm:     [[f32; 4]; 4],
223  pub transform_pvm_inv: [[f32; 4]; 4],
224  pub near:              f32,
225  pub far:               f32,
226}
227
228impl Uniforms {
229  pub fn new() -> Self {
230    Self {
231      transform_m:       Matrix4::identity().into(),
232      transform_vm:      Matrix4::identity().into(),
233      transform_pvm:     Matrix4::identity().into(),
234      transform_pvm_inv: Matrix4::identity().into(),
235      near:              0.1,
236      far:               1000.0,
237    }
238  }
239}
240
241pub unsafe fn ref_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
242  std::slice::from_raw_parts((p as *const T) as *const u8, std::mem::size_of::<T>())
243}
244
245pub unsafe fn slice_as_u8_slice<T: Sized>(p: &[T]) -> &[u8] {
246  std::slice::from_raw_parts((&p[0] as *const T) as *const u8, std::mem::size_of::<T>() * p.len())
247}
248
249pub struct ImageTexture {
250  pub texture:      wgpu::Texture,
251  pub texture_view: wgpu::TextureView,
252  pub bind_group:   wgpu::BindGroup,
253}
254
255pub struct DataTexture {
256  pub texture:      wgpu::Texture,
257  pub texture_view: wgpu::TextureView,
258  pub bind_group:   wgpu::BindGroup,
259  pub cpu_buffer:   GpuDataTextureBuffer,
260}
261
262impl DataTexture {
263  pub fn flush_data_texture(&self, queue: &wgpu::Queue) {
264    let (row_count, bytes) = self.cpu_buffer.get_write_info();
265    if row_count == 0 {
266      return;
267    }
268    queue.write_texture(
269      wgpu::ImageCopyTexture {
270        texture:   &self.texture,
271        mip_level: 0,
272        origin:    wgpu::Origin3d::ZERO,
273        aspect:    wgpu::TextureAspect::All,
274      },
275      bytes,
276      wgpu::ImageDataLayout {
277        offset:         0,
278        bytes_per_row:  Some(4 * DATA_TEXTURE_SIZE as u32),
279        rows_per_image: Some(row_count),
280      },
281      wgpu::Extent3d {
282        width:                 DATA_TEXTURE_SIZE as u32,
283        height:                row_count,
284        depth_or_array_layers: 1,
285      },
286    );
287  }
288}
289
290pub struct ResizingBuffer<T: Copy> {
291  pub label:          String,
292  pub usage:          wgpu::BufferUsages,
293  /// The size of the buffer in bytes is `std::mem::size_of::<T>() * len`.
294  pub buffer_and_len: Option<(wgpu::Buffer, usize)>,
295  pub contents:       Vec<T>,
296}
297
298impl<T: Copy> ResizingBuffer<T> {
299  pub fn new(label: &str, usage: wgpu::BufferUsages) -> Self {
300    Self {
301      label: label.to_string(),
302      usage,
303      buffer_and_len: None,
304      contents: Vec::new(),
305    }
306  }
307
308  pub fn update<'a>(&'a mut self, device: &wgpu::Device, queue: &wgpu::Queue) -> usize {
309    let reallocate_length = match self.buffer_and_len.as_mut() {
310      Some((_, buffer_len)) if self.contents.len() <= *buffer_len => None,
311      Some((_, ref buffer_len)) => Some(self.contents.len().max(buffer_len + buffer_len / 2)),
312      None => Some(self.contents.len().max(1)),
313    };
314    match reallocate_length {
315      Some(reallocate_length) => {
316        match self.buffer_and_len.take() {
317          Some((buffer, _)) => buffer.destroy(),
318          None => {}
319        }
320        self.buffer_and_len = Some((
321          device.create_buffer(&wgpu::BufferDescriptor {
322            label:              Some(&self.label),
323            size:               (std::mem::size_of::<T>() * reallocate_length)
324              as wgpu::BufferAddress,
325            usage:              self.usage,
326            mapped_at_creation: false,
327          }),
328          reallocate_length,
329        ));
330      }
331      None => {}
332    }
333    let buffer = &self.buffer_and_len.as_ref().unwrap().0;
334    if !self.contents.is_empty() {
335      let slice: &[u8] = unsafe { slice_as_u8_slice(&self.contents) };
336      queue.write_buffer(buffer, 0, slice);
337      slice.len()
338    } else {
339      0
340    }
341  }
342
343  pub fn get_slice<'a>(&'a self) -> Option<BufferSlice<'a>> {
344    match &self.buffer_and_len {
345      Some((buffer, _)) => Some(buffer.slice(..)),
346      None => None,
347    }
348  }
349
350  #[inline]
351  pub fn clear(&mut self) {
352    self.contents.clear();
353  }
354
355  #[inline]
356  pub fn push(&mut self, value: T) {
357    self.contents.push(value);
358  }
359
360  #[inline]
361  pub fn extend_from_slice(&mut self, values: &[T]) {
362    self.contents.extend_from_slice(values);
363  }
364
365  #[inline]
366  pub fn len(&self) -> usize {
367    self.contents.len()
368  }
369}
370
371#[derive(Clone)]
372pub struct RenderBufferOptions {
373  pub format:                     wgpu::TextureFormat,
374  pub sampler:                    Arc<wgpu::Sampler>,
375  pub textured_bind_group_layout: Arc<wgpu::BindGroupLayout>,
376  pub msaa_count:                 u32,
377  pub mipmap_count:               u32,
378}
379
380pub struct RenderBufferContents {
381  pub size:              (u32, u32),
382  pub msaa_intermediate: Option<wgpu::Texture>,
383  pub color:             wgpu::Texture,
384  pub depth:             wgpu::Texture,
385  pub bind_group:        wgpu::BindGroup,
386}
387
388pub struct RenderBuffer {
389  pub label:    String,
390  pub options:  RenderBufferOptions,
391  pub contents: Option<RenderBufferContents>,
392}
393
394impl RenderBuffer {
395  pub fn new(label: String, options: RenderBufferOptions) -> Self {
396    Self {
397      label,
398      options,
399      contents: None,
400    }
401  }
402
403  pub fn get_buffers(
404    &mut self,
405    device: &wgpu::Device,
406    new_size: (u32, u32),
407  ) -> &RenderBufferContents {
408    let reallocate = match self.contents.as_ref() {
409      Some(RenderBufferContents { size, .. }) if *size == new_size => false,
410      _ => true,
411    };
412    if reallocate {
413      if let Some(contents) = self.contents.take() {
414        if let Some(msaa_intermediate) = contents.msaa_intermediate {
415          msaa_intermediate.destroy();
416        }
417        contents.color.destroy();
418        contents.depth.destroy();
419      }
420      let make_tex = |format, label, sample_count, mip_level_count| {
421        device.create_texture(&wgpu::TextureDescriptor {
422          label: Some(label),
423          size: wgpu::Extent3d {
424            width:                 new_size.0,
425            height:                new_size.1,
426            depth_or_array_layers: 1,
427          },
428          mip_level_count,
429          sample_count,
430          dimension: wgpu::TextureDimension::D2,
431          format,
432          usage: wgpu::TextureUsages::RENDER_ATTACHMENT
433            | wgpu::TextureUsages::COPY_SRC
434            | wgpu::TextureUsages::COPY_DST
435            | wgpu::TextureUsages::TEXTURE_BINDING,
436          view_formats: &[], // FIXME: put in formats
437        })
438      };
439      let label_color = format!("{}-color", self.label);
440      let label_depth = format!("{}-depth", self.label);
441      let msaa_intermediate = match self.options.msaa_count > 1 {
442        true => Some(make_tex(
443          self.options.format,
444          &label_color,
445          self.options.msaa_count,
446          self.options.mipmap_count,
447        )),
448        false => None,
449      };
450      let color = make_tex(
451        self.options.format,
452        &label_color,
453        self.options.msaa_count,
454        self.options.mipmap_count,
455      );
456      let depth =
457        make_tex(wgpu::TextureFormat::Depth32Float, &label_depth, self.options.msaa_count, 1);
458      let texture_view = color.create_view(&wgpu::TextureViewDescriptor::default());
459      let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
460        layout:  &self.options.textured_bind_group_layout,
461        entries: &[
462          wgpu::BindGroupEntry {
463            binding:  0,
464            resource: wgpu::BindingResource::TextureView(&texture_view),
465          },
466          wgpu::BindGroupEntry {
467            binding:  1,
468            resource: wgpu::BindingResource::Sampler(&self.options.sampler),
469          },
470        ],
471        label:   Some(&format!("{}-bind-group", self.label)),
472      });
473      self.contents = Some(RenderBufferContents {
474        size: new_size,
475        msaa_intermediate,
476        color,
477        depth,
478        bind_group,
479      });
480    }
481    &self.contents.as_ref().unwrap()
482  }
483}
484
485#[derive(Clone)]
486pub struct CameraSettings {
487  pub screen_size: (u32, u32),
488  pub position:    Point3<f32>,
489  pub heading:     f32,
490  pub pitch:       f32,
491}
492
493impl CameraSettings {
494  pub fn new() -> Self {
495    Self {
496      screen_size: (800, 600),
497      position:    Point3::origin(),
498      heading:     0.0,
499      pitch:       0.0,
500    }
501  }
502
503  pub fn aspect_ratio(&self) -> f32 {
504    self.screen_size.0 as f32 / self.screen_size.1 as f32
505  }
506
507  pub fn eye_vector(&self) -> Vector3<f32> {
508    let x = self.heading.cos() * self.pitch.cos();
509    let y = self.pitch.sin();
510    let z = self.heading.sin() * self.pitch.cos();
511    Vector3::new(x, y, z)
512  }
513
514  pub fn right_vector(&self) -> Vector3<f32> {
515    Vector3::new(self.heading.sin(), 0.0, -self.heading.cos())
516  }
517}
518
519pub struct UniformsBuffer {
520  pub uniforms:          Uniforms,
521  pub uniforms_buffer:   wgpu::Buffer,
522  pub bind_group_layout: wgpu::BindGroupLayout,
523  pub bind_group:        wgpu::BindGroup,
524}
525
526impl UniformsBuffer {
527  pub fn new(label: &str, device: &wgpu::Device) -> Self {
528    let uniforms = Uniforms::new();
529    let uniforms_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
530      label:    Some(label),
531      contents: unsafe { ref_as_u8_slice(&uniforms) },
532      usage:    wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
533    });
534    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
535      entries: &[wgpu::BindGroupLayoutEntry {
536        binding:    0,
537        visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
538        ty:         wgpu::BindingType::Buffer {
539          ty:                 wgpu::BufferBindingType::Uniform,
540          has_dynamic_offset: false,
541          min_binding_size:   None,
542        },
543        count:      None,
544      }],
545      label:   Some(label),
546    });
547    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
548      layout:  &bind_group_layout,
549      entries: &[wgpu::BindGroupEntry {
550        binding:  0,
551        resource: uniforms_buffer.as_entire_binding(),
552      }],
553      label:   Some(label),
554    });
555    Self {
556      uniforms,
557      uniforms_buffer,
558      bind_group_layout,
559      bind_group,
560    }
561  }
562
563  pub fn write_buffer(&self, queue: &wgpu::Queue) {
564    queue.write_buffer(&self.uniforms_buffer, 0, unsafe { ref_as_u8_slice(&self.uniforms) });
565  }
566}
567
568pub struct GeometryBuffer<V: Copy = Vertex> {
569  pub vertex_buffer: ResizingBuffer<V>,
570  pub index_buffer:  ResizingBuffer<u32>,
571}
572
573impl<V: Copy> GeometryBuffer<V> {
574  pub fn new(name: &str) -> Self {
575    Self {
576      vertex_buffer: ResizingBuffer::new(
577        name,
578        wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
579      ),
580      index_buffer:  ResizingBuffer::new(
581        name,
582        wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
583      ),
584    }
585  }
586
587  pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) -> usize {
588    self.vertex_buffer.update(device, queue) + self.index_buffer.update(device, queue)
589  }
590
591  pub fn clear(&mut self) {
592    self.vertex_buffer.clear();
593    self.index_buffer.clear();
594  }
595}
596
597impl GeometryBuffer<Vertex> {
598  pub fn add_line(&mut self, a: Point3<f32>, b: Point3<f32>, color: [f32; 4]) {
599    let index = self.vertex_buffer.len() as u32;
600    self.vertex_buffer.push(Vertex {
601      position: [a.x, a.y, a.z],
602      color,
603      uv: [0.0, 0.0],
604    });
605    self.vertex_buffer.push(Vertex {
606      position: [b.x, b.y, b.z],
607      color,
608      uv: [0.0, 0.0],
609    });
610    self.index_buffer.push(index);
611    self.index_buffer.push(index + 1);
612  }
613}
614
615pub struct GpuDataTextureBuffer {
616  pub data: Vec<u32>,
617  pub ptr:  usize,
618}
619
620impl GpuDataTextureBuffer {
621  pub fn new() -> Self {
622    Self {
623      data: vec![0; DATA_TEXTURE_SIZE * DATA_TEXTURE_SIZE],
624      ptr:  0,
625    }
626  }
627
628  pub fn push(&mut self, value: u32) {
629    self.data[self.ptr] = value;
630    self.ptr += 1;
631  }
632
633  pub fn reset(&mut self) {
634    self.ptr = 0;
635  }
636
637  /// Returns (rows of image data, slice of bytes).
638  pub fn get_write_info(&self) -> (u32, &[u8]) {
639    // Round up to a full row.
640    let rows = (self.ptr + DATA_TEXTURE_SIZE - 1) / DATA_TEXTURE_SIZE;
641    let bytes = 4 * rows * DATA_TEXTURE_SIZE;
642    (rows as u32, unsafe {
643      std::slice::from_raw_parts(self.data.as_ptr() as *const u8, bytes as usize)
644    })
645  }
646}
647
648pub static SHADER_PRELUDE: &str = include_str!("shaders.wgsl");
649
650// struct PipelineDesc {
651//   //layout:
652// }
653
654// pub trait PipelinesEnum: EnumArray<wgpu::RenderPipeline> + Send + Sync {
655//   fn describe(self) -> PipelineDesc;
656// }
657
658// pub trait TexturesEnum: EnumArray<wgpu::Texture> + Send + Sync {
659//   fn data(self) -> &'static [u8];
660// }
661
662// pub trait ResourceKey: Sized + Send + Sync + 'static + EnumArray<Self::Output> {
663//   type Output: Sized + Send + Sync + 'static;
664//   fn load<App: PeterEngineApp>(self, rd: &mut RenderData<App>) -> Self::Output;
665// }
666
667pub enum BindingDesc {
668  Uniforms,
669  // Simple {
670  //   visibility: wgpu::ShaderStages,
671  //   ty:         wgpu::BufferBindingType,
672  // },
673  Texture { filterable: bool },
674  Sampler { filterable: bool },
675  DataTexture,
676  Custom(wgpu::BindGroupLayoutEntry),
677}
678
679pub struct PipelineDesc {
680  pub layout:          Vec<Vec<BindingDesc>>,
681  pub vertex_buffers:  Vec<wgpu::VertexBufferLayout<'static>>,
682  pub vertex_shader:   &'static str,
683  pub fragment_shader: &'static str,
684  pub topology:        wgpu::PrimitiveTopology,
685  pub do_blend:        bool,
686  pub depth_compare:   bool,
687  pub depth_write:     bool,
688}
689
690impl Default for PipelineDesc {
691  fn default() -> Self {
692    Self {
693      layout:          Vec::new(),
694      vertex_buffers:  Vec::new(),
695      vertex_shader:   "vertex shader not specified!",
696      fragment_shader: "fragment shader not specified!",
697      topology:        wgpu::PrimitiveTopology::TriangleList,
698      do_blend:        false,
699      depth_compare:   true,
700      depth_write:     true,
701    }
702  }
703}
704
705// #[macro_export]
706// macro_rules! make_pipeline {
707//   (
708//     render_data = $rd:expr;
709//     layout = $layout:expr;
710//     vertex_buffers = $vertex_buffers:expr;
711//     vertex = $vertex:expr;
712//     fragment = $fragment:expr;
713//     topology = $topology:expr;
714//     blend_mode = $blend_mode:expr;
715//     $( depth_compare = $depth_compare:expr; )?
716//     $( depth_write = $depth_write:expr; )?
717//   ) => {{
718//     let rd = $rd;
719//     #[allow(unused_mut, unused_assignments)]
720//     let mut depth_compare_enabled = true;
721//     #[allow(unused_mut, unused_assignments)]
722//     let mut depth_write_enabled = true;
723//     $( depth_compare_enabled = $depth_compare; )?
724//     $( depth_write_enabled = $depth_write; )?
725//   }};
726// }
727
728pub struct RenderData {
729  pub target_format:              wgpu::TextureFormat,
730  pub main_uniforms:              UniformsBuffer,
731  pub device:                     Arc<wgpu::Device>,
732  pub queue:                      Arc<wgpu::Queue>,
733  pub shader:                     wgpu::ShaderModule,
734  pub linear_texture_sampler:     wgpu::Sampler,
735  pub nearest_texture_sampler:    wgpu::Sampler,
736  pub textured_bind_group_layout: wgpu::BindGroupLayout,
737  // pub main_data:               GpuDataTextureBuffer,
738  // pub main_data_texture:       wgpu::Texture,
739  // pub main_data_texture_view:  wgpu::TextureView,
740  // pub data_texture_bind_group: wgpu::BindGroup,
741  pub mipmap_gen:                 MipMapGen,
742  pub pixel_perfect_size:         (u32, u32),
743  // pub resources:          AnyMap,
744  // pub resources:          App::RenderResources,
745}
746
747// unsafe impl<App: PeterEngineApp> Send for RenderData<App> {}
748// unsafe impl<App: PeterEngineApp> Sync for RenderData<App> {}
749
750impl RenderData {
751  // pub fn load_resources<K: ResourceKey>(&mut self) {
752  //   if self.resources.contains::<EnumMap<K, K::Output>>() {
753  //     panic!("Resource type already loaded");
754  //   }
755  //   let mapping = EnumMap::from_fn(|key: K| key.load(self));
756  //   self.resources.insert::<EnumMap<K, K::Output>>(mapping);
757  // }
758
759  // pub fn get_resource<K: ResourceKey>(&self, key: K) -> &K::Output {
760  //   match self.resources.get::<EnumMap<K, K::Output>>() {
761  //     Some(mapping) => &mapping[key],
762  //     None => panic!("Resource not loaded: {}", std::any::type_name::<K>()),
763  //   }
764  // }
765
766  // pub fn get_resource_mut<K: ResourceKey>(&mut self, key: K) -> &mut K::Output {
767  //   match self.resources.get_mut::<EnumMap<K, K::Output>>() {
768  //     Some(mapping) => &mut mapping[key],
769  //     None => panic!("Resource not loaded: {}", std::any::type_name::<K>()),
770  //   }
771  // }
772
773  pub fn new(cc: &eframe::CreationContext, shader_source: &str) -> Self {
774    let wgpu_render_state = cc.wgpu_render_state.as_ref().unwrap();
775    let device = Arc::clone(&wgpu_render_state.device);
776    let queue = Arc::clone(&wgpu_render_state.queue);
777
778    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
779      label:  None,
780      source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(shader_source)),
781    });
782
783    let linear_texture_sampler = device.create_sampler(&LINEAR_SAMPLER_DESCRIPTOR);
784    let nearest_texture_sampler = device.create_sampler(&NEAREST_SAMPLER_DESCRIPTOR);
785    let textured_bind_group_layout =
786      device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
787        entries: &[
788          wgpu::BindGroupLayoutEntry {
789            binding:    0,
790            visibility: wgpu::ShaderStages::FRAGMENT,
791            ty:         wgpu::BindingType::Texture {
792              multisampled:   false,
793              view_dimension: wgpu::TextureViewDimension::D2,
794              sample_type:    wgpu::TextureSampleType::Float { filterable: true },
795            },
796            count:      None,
797          },
798          wgpu::BindGroupLayoutEntry {
799            binding:    1,
800            visibility: wgpu::ShaderStages::FRAGMENT,
801            ty:         wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
802            count:      None,
803          },
804        ],
805        label:   Some("textured_bind_group_layout"),
806      });
807
808    let main_uniforms = UniformsBuffer::new("main_uniforms", &device);
809
810    // let default_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
811    //   label:                None,
812    //   bind_group_layouts:   &[&main_uniforms.bind_group_layout],
813    //   push_constant_ranges: &[],
814    // });
815    // let data_texture_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
816    //   label:                None,
817    //   bind_group_layouts:   &[&main_uniforms.bind_group_layout, &data_texture_bind_group_layout],
818    //   push_constant_ranges: &[],
819    // });
820
821    let mipmap_gen = MipMapGen::new(&device, wgpu::TextureFormat::Rgba8UnormSrgb);
822
823    // let pipelines = EnumMap::from_fn(|pipeline_desc| {
824    //   todo!()
825    // });
826    // let textures = EnumMap::from_fn(|texture_desc| {
827    //   todo!()
828    // });
829    // let buffers = EnumMap::from_fn(|buffer_desc| {
830    //   todo!()
831    // });
832
833    Self {
834      target_format: wgpu_render_state.target_format,
835      main_uniforms,
836      device,
837      queue,
838      shader,
839      linear_texture_sampler,
840      nearest_texture_sampler,
841      textured_bind_group_layout,
842      // main_data: GpuDataTextureBuffer::new(),
843      // main_data_texture,
844      // main_data_texture_view,
845      // data_texture_bind_group,
846      mipmap_gen,
847      pixel_perfect_size: (1, 1),
848      // resources: AnyMap::new(),
849      // pipelines,
850      // textures,
851      // _phantom: std::marker::PhantomData,
852    }
853  }
854
855  pub fn create_pipeline(&self, desc: PipelineDesc) -> wgpu::RenderPipeline {
856    let mut target: wgpu::ColorTargetState = self.target_format.into();
857    if desc.do_blend {
858      target.blend = Some(wgpu::BlendState::ALPHA_BLENDING);
859    }
860    let mut bind_group_layouts = Vec::new();
861    for bind_group_desc in desc.layout {
862      let mut bindings = Vec::new();
863      for (binding_index, binding_desc) in bind_group_desc.iter().enumerate() {
864        bindings.push(match binding_desc {
865          BindingDesc::Uniforms => wgpu::BindGroupLayoutEntry {
866            binding:    binding_index as u32,
867            visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
868            ty:         wgpu::BindingType::Buffer {
869              ty:                 wgpu::BufferBindingType::Uniform,
870              has_dynamic_offset: false,
871              min_binding_size:   None,
872            },
873            count:      None,
874          },
875          // BindingDesc::Simple {
876          //   visibility,
877          //   ty,
878          // } => wgpu::BindGroupLayoutEntry {
879          //   binding:    binding_index as u32,
880          //   visibility: *visibility,
881          //   ty:         wgpu::BindingType::Buffer {
882          //     ty:                 *ty,
883          //     has_dynamic_offset: false,
884          //     min_binding_size:   None,
885          //   },
886          //   count:      None,
887          // },
888          BindingDesc::Texture { filterable } => wgpu::BindGroupLayoutEntry {
889            binding:    binding_index as u32,
890            visibility: wgpu::ShaderStages::FRAGMENT,
891            ty:         wgpu::BindingType::Texture {
892              multisampled:   false,
893              view_dimension: wgpu::TextureViewDimension::D2,
894              sample_type:    wgpu::TextureSampleType::Float {
895                filterable: *filterable,
896              },
897            },
898            count:      None,
899          },
900          BindingDesc::DataTexture => wgpu::BindGroupLayoutEntry {
901            binding:    binding_index as u32,
902            visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
903            ty:         wgpu::BindingType::Texture {
904              multisampled:   false,
905              view_dimension: wgpu::TextureViewDimension::D2,
906              sample_type:    wgpu::TextureSampleType::Uint,
907            },
908            count:      None,
909          },
910          BindingDesc::Sampler { filterable } => wgpu::BindGroupLayoutEntry {
911            binding:    binding_index as u32,
912            visibility: wgpu::ShaderStages::FRAGMENT,
913            ty:         wgpu::BindingType::Sampler(match *filterable {
914              true => wgpu::SamplerBindingType::Filtering,
915              false => wgpu::SamplerBindingType::NonFiltering,
916            }),
917            count:      None,
918          },
919          BindingDesc::Custom(bind_group_layout_entry) => bind_group_layout_entry.clone(),
920        });
921      }
922      bind_group_layouts.push(self.device.create_bind_group_layout(
923        &wgpu::BindGroupLayoutDescriptor {
924          label:   None,
925          entries: &bindings,
926        },
927      ));
928    }
929    let binding_group_layouts_by_ref =
930      bind_group_layouts.iter().map(|bgl| bgl as &wgpu::BindGroupLayout).collect::<Vec<_>>();
931    let pipeline_layout = self.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
932      label:                None,
933      bind_group_layouts:   &binding_group_layouts_by_ref,
934      push_constant_ranges: &[],
935    });
936    self.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
937      label:         None,
938      layout:        Some(&pipeline_layout),
939      vertex:        wgpu::VertexState {
940        module:      &self.shader,
941        entry_point: desc.vertex_shader,
942        buffers:     &desc.vertex_buffers,
943      },
944      fragment:      Some(wgpu::FragmentState {
945        module:      &self.shader,
946        entry_point: desc.fragment_shader,
947        targets:     &[Some(target)],
948      }),
949      primitive:     wgpu::PrimitiveState {
950        topology:           desc.topology,
951        strip_index_format: None,
952        front_face:         wgpu::FrontFace::Ccw,
953        cull_mode:          Some(wgpu::Face::Back),
954        polygon_mode:       wgpu::PolygonMode::Fill,
955        unclipped_depth:    false,
956        conservative:       false,
957      },
958      depth_stencil: Some(wgpu::DepthStencilState {
959        format:              wgpu::TextureFormat::Depth32Float,
960        depth_write_enabled: desc.depth_write,
961        depth_compare:       match desc.depth_compare {
962          true => wgpu::CompareFunction::Less,
963          false => wgpu::CompareFunction::Always,
964        },
965        stencil:             wgpu::StencilState::default(),
966        bias:                wgpu::DepthBiasState::default(),
967      }),
968      multisample:   wgpu::MultisampleState {
969        count:                     MSAA_COUNT,
970        mask:                      !0,
971        alpha_to_coverage_enabled: false,
972      },
973      multiview:     None,
974    })
975  }
976
977  pub fn load_texture(&self, bytes: &[u8], filter: bool) -> ImageTexture {
978    let mip_level_count = if filter { MIP_LEVEL_COUNT } else { 1 };
979    let diffuse_image = image::load_from_memory(bytes).unwrap();
980    let diffuse_rgba = diffuse_image.to_rgba8();
981    let dimensions = diffuse_rgba.dimensions();
982    let texture_size = wgpu::Extent3d {
983      width:                 dimensions.0,
984      height:                dimensions.1,
985      depth_or_array_layers: 1,
986    };
987    let diffuse_texture = self.device.create_texture(&wgpu::TextureDescriptor {
988      size: texture_size,
989      mip_level_count,
990      sample_count: 1,
991      dimension: wgpu::TextureDimension::D2,
992      format: wgpu::TextureFormat::Rgba8UnormSrgb,
993      usage: wgpu::TextureUsages::TEXTURE_BINDING
994        | wgpu::TextureUsages::COPY_DST
995        | wgpu::TextureUsages::RENDER_ATTACHMENT,
996      label: Some("font_texture"),
997      view_formats: &[],
998    });
999    self.queue.write_texture(
1000      wgpu::ImageCopyTexture {
1001        texture:   &diffuse_texture,
1002        mip_level: 0,
1003        origin:    wgpu::Origin3d::ZERO,
1004        aspect:    wgpu::TextureAspect::All,
1005      },
1006      &diffuse_rgba,
1007      wgpu::ImageDataLayout {
1008        offset:         0,
1009        bytes_per_row:  Some(4 * dimensions.0),
1010        rows_per_image: Some(dimensions.1),
1011      },
1012      texture_size,
1013    );
1014    if mip_level_count > 1 {
1015      let mut mipmap_encoder =
1016        self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
1017          label: Some("mipmap_gen"),
1018        });
1019      self.mipmap_gen.generate_mipmaps(
1020        &mut mipmap_encoder,
1021        &self.device,
1022        &diffuse_texture,
1023        mip_level_count,
1024      );
1025      self.queue.submit(Some(mipmap_encoder.finish()));
1026    }
1027    let texture_view = diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default());
1028    let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1029      layout:  &self.textured_bind_group_layout,
1030      entries: &[
1031        wgpu::BindGroupEntry {
1032          binding:  0,
1033          resource: wgpu::BindingResource::TextureView(&texture_view),
1034        },
1035        wgpu::BindGroupEntry {
1036          binding:  1,
1037          resource: wgpu::BindingResource::Sampler(match filter {
1038            true => &self.linear_texture_sampler,
1039            false => &self.nearest_texture_sampler,
1040          }),
1041        },
1042      ],
1043      label:   None,
1044    });
1045    ImageTexture {
1046      texture: diffuse_texture,
1047      texture_view,
1048      bind_group,
1049    }
1050  }
1051
1052  pub fn new_data_texture(&self) -> DataTexture {
1053    let data_texture = self.device.create_texture(&wgpu::TextureDescriptor {
1054      size:            wgpu::Extent3d {
1055        width:                 DATA_TEXTURE_SIZE as u32,
1056        height:                DATA_TEXTURE_SIZE as u32,
1057        depth_or_array_layers: 1,
1058      },
1059      mip_level_count: 1,
1060      sample_count:    1,
1061      dimension:       wgpu::TextureDimension::D2,
1062      format:          wgpu::TextureFormat::R32Uint,
1063      usage:           wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
1064      label:           Some("data_texture"),
1065      view_formats:    &[],
1066    });
1067    let data_texture_view = data_texture.create_view(&wgpu::TextureViewDescriptor {
1068      format: Some(wgpu::TextureFormat::R32Uint),
1069      ..Default::default()
1070    });
1071    let data_texture_bind_group_layout =
1072      self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1073        entries: &[wgpu::BindGroupLayoutEntry {
1074          binding:    0,
1075          visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
1076          ty:         wgpu::BindingType::Texture {
1077            multisampled:   false,
1078            view_dimension: wgpu::TextureViewDimension::D2,
1079            sample_type:    wgpu::TextureSampleType::Uint,
1080          },
1081          count:      None,
1082        }],
1083        label:   Some("data_texture_bind_group_layout"),
1084      });
1085    let data_texture_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1086      layout:  &data_texture_bind_group_layout,
1087      entries: &[wgpu::BindGroupEntry {
1088        binding:  0,
1089        resource: wgpu::BindingResource::TextureView(&data_texture_view),
1090      }],
1091      label:   Some("data_texture_bind_group"),
1092    });
1093    DataTexture {
1094      texture:      data_texture,
1095      texture_view: data_texture_view,
1096      bind_group:   data_texture_bind_group,
1097      cpu_buffer:   GpuDataTextureBuffer::new(),
1098    }
1099  }
1100}