gloss_renderer/forward_renderer/render_passes/
line_pipeline.rs

1use std::collections::HashMap;
2
3use crate::{
4    components::{EdgesV1GPU, EdgesV2GPU, ModelMatrix, Name, Renderable, VisLines},
5    config::RenderConfig,
6    forward_renderer::{bind_group_collection::BindGroupCollection, locals::LocalEntData},
7    scene::Scene,
8};
9
10use easy_wgpu::{
11    bind_group::{BindGroupBuilder, BindGroupWrapper},
12    bind_group_layout::{BindGroupLayoutBuilder, BindGroupLayoutDesc},
13    buffer::Buffer,
14    gpu::Gpu,
15    pipeline::RenderPipelineDescBuilder,
16    utils::create_empty_group,
17};
18
19use gloss_hecs::Entity;
20
21use super::{pipeline_runner::PipelineRunner, upload_pass::PerFrameUniforms};
22
23use encase;
24use gloss_utils::numerical::align;
25
26//shaders
27#[include_wgsl_oil::include_wgsl_oil("../../../shaders/line_instanced.wgsl")]
28mod shader_code {}
29
30/// Render all the meshes from the scene to the `GBuffer`
31pub struct LinePipeline {
32    render_pipeline: wgpu::RenderPipeline,
33    _empty_group: wgpu::BindGroup,
34    locals_uniform: Buffer, // a uniform buffer that we suballocate for the locals of every mesh
35    locals_bind_groups: LocalsBindGroups,
36}
37
38impl LinePipeline {
39    /// # Panics
40    /// Will panic if the gbuffer does not have the correct textures that are
41    /// needed for the pipeline creation
42    pub fn new(gpu: &Gpu, params: &RenderConfig, color_target_format: wgpu::TextureFormat, depth_target_format: wgpu::TextureFormat) -> Self {
43        //wasm likes everything to be 16 bytes aligned
44        const_assert!(std::mem::size_of::<Locals>() % 16 == 0);
45
46        //render pipeline
47        let render_pipeline = RenderPipelineDescBuilder::new()
48            .label("line_pipeline")
49            .shader_code(shader_code::SOURCE)
50            .shader_label("line_shader")
51            .add_bind_group_layout_desc(PerFrameUniforms::build_layout_desc())
52            // .add_bind_group_layout_desc(input_layout_desc) //no need for this because we don't use shadow maps here
53            .add_bind_group_layout_desc(LocalsBindGroups::build_layout_desc())
54            // .add_vertex_buffer_layout(VertsGPU::vertex_buffer_layout_instanced::<0>())
55            // .add_vertex_buffer_layout(ColorsGPU::vertex_buffer_layout_instanced::<1>())
56            .add_vertex_buffer_layout(EdgesV1GPU::vertex_buffer_layout_instanced::<0>())
57            .add_vertex_buffer_layout(EdgesV2GPU::vertex_buffer_layout_instanced::<1>())
58            .add_render_target(wgpu::ColorTargetState {
59                format: color_target_format,
60                blend: None,
61                write_mask: wgpu::ColorWrites::ALL,
62            })
63            .depth_state(Some(wgpu::DepthStencilState {
64                format: depth_target_format,
65                depth_write_enabled: true,
66                depth_compare: wgpu::CompareFunction::Greater, // 1.
67                stencil: wgpu::StencilState::default(),        // 2.
68                bias: wgpu::DepthBiasState::default(),
69            }))
70            .multisample(wgpu::MultisampleState {
71                count: params.msaa_nr_samples,
72                ..Default::default()
73            })
74            .build_pipeline(gpu.device());
75
76        let empty_group = create_empty_group(gpu.device());
77
78        let size_bytes = 0x10000;
79        let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
80        let locals_uniform = Buffer::new_empty(gpu.device(), usage, Some("local_buffer"), size_bytes);
81
82        let locals_bind_groups = LocalsBindGroups::new(gpu);
83
84        Self {
85            render_pipeline,
86            _empty_group: empty_group,
87            locals_uniform,
88            locals_bind_groups,
89        }
90    }
91}
92
93impl PipelineRunner for LinePipeline {
94    type QueryItems<'a> = (&'a EdgesV1GPU, &'a EdgesV2GPU, &'a VisLines, &'a Name);
95    type QueryState<'a> = gloss_hecs::QueryBorrow<'a, gloss_hecs::With<Self::QueryItems<'a>, &'a Renderable>>;
96
97    fn query_state(scene: &Scene) -> Self::QueryState<'_> {
98        scene.world.query::<Self::QueryItems<'_>>().with::<&Renderable>()
99    }
100    fn prepare<'a>(&mut self, gpu: &Gpu, _per_frame_uniforms: &PerFrameUniforms, scene: &'a Scene) -> Self::QueryState<'a> {
101        self.begin_pass();
102        self.update_locals(gpu, scene);
103        Self::query_state(scene)
104    }
105    fn run<'r>(
106        &'r mut self,
107        render_pass: &mut wgpu::RenderPass<'r>,
108        per_frame_uniforms: &'r PerFrameUniforms,
109        _render_params: &RenderConfig,
110        query_state: &'r mut Self::QueryState<'_>,
111    ) {
112        //completely skip this if there are no entities to draw
113        if query_state.iter().count() == 0 {
114            return;
115        }
116        render_pass.set_pipeline(&self.render_pipeline);
117
118        //global binding
119        render_pass.set_bind_group(0, &per_frame_uniforms.bind_group, &[]);
120
121        // No need for the input binding because we don't use shadow maps during point
122        // rendering
123        for (_id, (ev1, ev2, vis_lines, name)) in query_state.iter() {
124            if !vis_lines.show_lines {
125                continue;
126            }
127            //local bindings
128            let (local_bg, offset) = &self.locals_bind_groups.mesh2local_bind[&name.0.clone()];
129            render_pass.set_bind_group(1, local_bg.bg(), &[*offset]);
130            render_pass.set_vertex_buffer(0, ev1.buf.slice(..));
131            render_pass.set_vertex_buffer(1, ev2.buf.slice(..));
132            render_pass.draw(0..6, 0..ev1.nr_vertices);
133        }
134    }
135
136    fn begin_pass(&mut self) {}
137
138    fn update_locals(&mut self, gpu: &Gpu, scene: &Scene) {
139        Self::update_locals_inner::<Locals, _>(
140            gpu,
141            scene,
142            &mut self.locals_uniform,
143            &mut self.locals_bind_groups,
144            &mut Self::query_state(scene),
145        );
146    }
147}
148
149/// Keep in sync with shader `gbuffer_mesh.wgsl`
150#[repr(C)]
151#[derive(Clone, Copy, encase::ShaderType)]
152struct Locals {
153    model_matrix: nalgebra::Matrix4<f32>,
154    color_type: i32,
155    line_color: nalgebra::Vector4<f32>,
156    line_width: f32,
157    zbuffer: u32,
158    antialias_edges: u32,
159    is_floor: u32,
160    //wasm needs padding to 16 bytes https://github.com/gfx-rs/wgpu/issues/2932
161    pad_b: f32,
162    pad_c: f32,
163    pad_d: f32,
164}
165impl LocalEntData for Locals {
166    fn new(entity: Entity, scene: &Scene) -> Self {
167        let model_matrix = scene.get_comp::<&ModelMatrix>(&entity).unwrap().0.to_homogeneous();
168        let vis_lines = scene.get_comp::<&VisLines>(&entity).unwrap();
169        let color_type = vis_lines.color_type as i32;
170        let is_floor = if let Some(floor) = scene.get_floor() {
171            floor.entity == entity
172        } else {
173            false
174        };
175        let is_floor = u32::from(is_floor);
176        Locals {
177            model_matrix,
178            color_type,
179            line_color: vis_lines.line_color,
180            line_width: vis_lines.line_width,
181            zbuffer: u32::from(vis_lines.zbuffer),
182            antialias_edges: u32::from(vis_lines.antialias_edges),
183            is_floor,
184            pad_b: 0.0,
185            pad_c: 0.0,
186            pad_d: 0.0,
187        }
188    }
189}
190
191struct LocalsBindGroups {
192    layout: wgpu::BindGroupLayout,
193    pub mesh2local_bind: HashMap<String, (BindGroupWrapper, u32)>,
194}
195impl BindGroupCollection for LocalsBindGroups {
196    fn new(gpu: &Gpu) -> Self {
197        Self {
198            layout: Self::build_layout_desc().into_bind_group_layout(gpu.device()),
199            mesh2local_bind: HashMap::default(),
200        }
201    }
202
203    fn build_layout_desc() -> BindGroupLayoutDesc {
204        BindGroupLayoutBuilder::new()
205            .label("gbuffer_pass_locals_layout")
206            //locals
207            .add_entry_uniform(
208                wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
209                true,
210                wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<Locals>()).unwrap(), 256))),
211            )
212            .build()
213    }
214
215    fn update_bind_group(&mut self, _entity: Entity, gpu: &Gpu, mesh_name: &str, ubo: &Buffer, offset_in_ubo: u32, _scene: &Scene) {
216        let entries = BindGroupBuilder::new().add_entry_buf_chunk::<Locals>(&ubo.buffer).build_entries();
217
218        self.update_if_stale(mesh_name, entries, offset_in_ubo, gpu);
219    }
220
221    fn get_layout(&self) -> &wgpu::BindGroupLayout {
222        &self.layout
223    }
224    fn get_mut_entity2binds(&mut self) -> &mut HashMap<String, (BindGroupWrapper, u32)> {
225        &mut self.mesh2local_bind
226    }
227}