Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 6.7 kB View raw
1use uom::si::length::millimeter; 2 3use crate::camera::{Camera2, GridSpacing}; 4use crate::gpu::Gpu; 5use crate::snapshot::Style; 6 7#[repr(C, align(16))] 8#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] 9struct GridUniform { 10 world_from_clip: [f32; 16], 11 minor: [f32; 4], 12 major: [f32; 4], 13 axis_x: [f32; 4], 14 axis_y: [f32; 4], 15 origin: [f32; 4], 16 viewport: [f32; 2], 17 minor_spacing: f32, 18 major_every: f32, 19 line_width_px: f32, 20 axis_width_px: f32, 21 origin_radius_px: f32, 22 pixels_per_mm: f32, 23} 24 25const UNIFORM_SIZE: u64 = core::mem::size_of::<GridUniform>() as u64; 26 27pub struct GridPipeline { 28 pipeline: wgpu::RenderPipeline, 29 uniform_buffer: wgpu::Buffer, 30 bind_group: wgpu::BindGroup, 31 queue: wgpu::Queue, 32} 33 34impl GridPipeline { 35 #[must_use] 36 pub fn new(gpu: &Gpu, color_format: wgpu::TextureFormat) -> Self { 37 let device = gpu.device(); 38 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { 39 label: Some("bone-render:grid-shader"), 40 source: wgpu::ShaderSource::Wgsl(include_str!("grid.wgsl").into()), 41 }); 42 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { 43 label: Some("bone-render:grid-bgl"), 44 entries: &[wgpu::BindGroupLayoutEntry { 45 binding: 0, 46 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, 47 ty: wgpu::BindingType::Buffer { 48 ty: wgpu::BufferBindingType::Uniform, 49 has_dynamic_offset: false, 50 min_binding_size: wgpu::BufferSize::new(UNIFORM_SIZE), 51 }, 52 count: None, 53 }], 54 }); 55 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { 56 label: Some("bone-render:grid-layout"), 57 bind_group_layouts: &[Some(&bind_group_layout)], 58 immediate_size: 0, 59 }); 60 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { 61 label: Some("bone-render:grid-pipeline"), 62 layout: Some(&pipeline_layout), 63 vertex: wgpu::VertexState { 64 module: &shader, 65 entry_point: Some("vs"), 66 compilation_options: wgpu::PipelineCompilationOptions::default(), 67 buffers: &[], 68 }, 69 fragment: Some(wgpu::FragmentState { 70 module: &shader, 71 entry_point: Some("fs"), 72 compilation_options: wgpu::PipelineCompilationOptions::default(), 73 targets: &[Some(wgpu::ColorTargetState { 74 format: color_format, 75 blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING), 76 write_mask: wgpu::ColorWrites::ALL, 77 })], 78 }), 79 primitive: wgpu::PrimitiveState { 80 topology: wgpu::PrimitiveTopology::TriangleList, 81 strip_index_format: None, 82 front_face: wgpu::FrontFace::Ccw, 83 cull_mode: None, 84 polygon_mode: wgpu::PolygonMode::Fill, 85 conservative: false, 86 unclipped_depth: false, 87 }, 88 depth_stencil: None, 89 multisample: wgpu::MultisampleState::default(), 90 multiview_mask: None, 91 cache: None, 92 }); 93 let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor { 94 label: Some("bone-render:grid-uniform"), 95 size: UNIFORM_SIZE, 96 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, 97 mapped_at_creation: false, 98 }); 99 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { 100 label: Some("bone-render:grid-bg"), 101 layout: &bind_group_layout, 102 entries: &[wgpu::BindGroupEntry { 103 binding: 0, 104 resource: uniform_buffer.as_entire_binding(), 105 }], 106 }); 107 Self { 108 pipeline, 109 uniform_buffer, 110 bind_group, 111 queue: gpu.queue().clone(), 112 } 113 } 114 115 pub fn draw( 116 &self, 117 encoder: &mut wgpu::CommandEncoder, 118 view: &wgpu::TextureView, 119 camera: Camera2, 120 style: &Style, 121 ) { 122 let spacing = GridSpacing::from_zoom(camera.zoom(), style.grid().minor_spacing_target_px()); 123 let uniform = build_uniform(camera, spacing, style); 124 self.queue 125 .write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&uniform)); 126 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { 127 label: Some("bone-render:grid-pass"), 128 color_attachments: &[Some(wgpu::RenderPassColorAttachment { 129 view, 130 resolve_target: None, 131 depth_slice: None, 132 ops: wgpu::Operations { 133 load: wgpu::LoadOp::Clear(style.background().into()), 134 store: wgpu::StoreOp::Store, 135 }, 136 })], 137 depth_stencil_attachment: None, 138 timestamp_writes: None, 139 occlusion_query_set: None, 140 multiview_mask: None, 141 }); 142 pass.set_pipeline(&self.pipeline); 143 pass.set_bind_group(0, &self.bind_group, &[]); 144 pass.draw(0..3, 0..1); 145 } 146} 147 148impl core::fmt::Debug for GridPipeline { 149 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 150 f.debug_struct("GridPipeline").finish_non_exhaustive() 151 } 152} 153 154#[allow( 155 clippy::cast_possible_truncation, 156 clippy::cast_precision_loss, 157 reason = "viewport extents and grid spacing fit f32 mantissa" 158)] 159fn build_uniform(camera: Camera2, spacing: GridSpacing, style: &Style) -> GridUniform { 160 let grid = style.grid(); 161 let extent = camera.extent(); 162 GridUniform { 163 world_from_clip: camera.world_mm_from_clip(), 164 minor: grid.minor().to_rgba_array(), 165 major: grid.major().to_rgba_array(), 166 axis_x: grid.axis_x().to_rgba_array(), 167 axis_y: grid.axis_y().to_rgba_array(), 168 origin: grid.origin().to_rgba_array(), 169 viewport: [ 170 extent.width().value() as f32, 171 extent.height().value() as f32, 172 ], 173 minor_spacing: spacing.minor().get::<millimeter>() as f32, 174 major_every: spacing.major_every() as f32, 175 line_width_px: grid.line_width_px(), 176 axis_width_px: grid.axis_width_px(), 177 origin_radius_px: grid.origin_radius_px(), 178 pixels_per_mm: camera.zoom().value() as f32, 179 } 180}