1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! Manager of render pipelines.
use std::collections::HashMap;
use std::sync::Arc;
use wgpu::*;
use super::texture::RawTexture;
use super::vertex::Vertex;
use super::RenderObject;
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct RenderPipelineInfo {
/// WGSL shader source code.
pub shader: &'static str,
/// Optional name of the shader.
pub label: Option<&'static str>,
/// Whether the pipeline should have a texture binding.
pub has_texture: bool,
}
/// Pipeline manager allows to create new render pipelines on demand, without duplication.
pub struct RenderPipelineManager {
device: Arc<Device>,
texture_bind_group_layout: BindGroupLayout,
uniform_bind_group_layout: BindGroupLayout,
pipelines: HashMap<RenderPipelineInfo, Arc<RenderPipeline>>,
surface_format: TextureFormat,
msaa_samples: u32,
}
impl RenderPipelineManager {
/// Create a new pipeline manager.
///
/// # Arguments
///
/// * `device` - WGPU device to use.
/// * `surface_format` - Texture surface format for textured pipelines.
/// * `msaa_samples` - Number of MSAA samples to use. Set to 1 to have no anti-aliasing.
pub fn new(device: Arc<Device>, surface_format: TextureFormat, msaa_samples: u32) -> Self {
// Bind a texture and its sampler to fragment shader.
let texture_bind_group_layout =
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
view_dimension: TextureViewDimension::D2,
sample_type: TextureSampleType::Float { filterable: true },
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
label: Some("texture_bind_group_layout"),
});
// Bind uniform buffer to vertex shader.
let uniform_bind_group_layout =
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
label: Some("uniform_bind_group_layout"),
});
Self {
device,
texture_bind_group_layout,
uniform_bind_group_layout,
surface_format,
pipelines: Default::default(),
msaa_samples,
}
}
/// Get the underlying WGPU device.
pub fn device(&self) -> &Arc<Device> {
&self.device
}
/// Get the layout of texture bind group.
pub fn texture_bind_group_layout(&self) -> &BindGroupLayout {
&self.texture_bind_group_layout
}
/// Get the layout of camera's uniform buffer bind group.
pub fn uniform_bind_group_layout(&self) -> &BindGroupLayout {
&self.uniform_bind_group_layout
}
/// Get a render pipeline that matches the provided info.
///
/// # Arguments
///
/// * `info` - shader and binding information for the pipeline.
pub fn pipeline(&mut self, info: RenderPipelineInfo) -> Arc<RenderPipeline> {
// Destruct self to access fields in the closure
let RenderPipelineManager {
texture_bind_group_layout,
uniform_bind_group_layout,
pipelines,
device,
surface_format,
msaa_samples,
} = self;
pipelines
.entry(info)
.or_insert_with_key(|info| {
let shader = device.create_shader_module(ShaderModuleDescriptor {
label: info.label,
source: ShaderSource::Wgsl(info.shader.into()),
});
// Depending on whether the pipeline needs textures, use one of these layouts.
let textured_bind_group_layouts =
&[&*uniform_bind_group_layout, &*texture_bind_group_layout][..];
let untextured_bind_group_layouts = &[&*uniform_bind_group_layout][..];
let render_pipeline_layout =
device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: if info.has_texture {
textured_bind_group_layouts
} else {
untextured_bind_group_layouts
},
push_constant_ranges: &[],
});
// Create the render pipeline
device
.create_render_pipeline(&RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
vertex: VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[Vertex::desc(), RenderObject::desc()],
},
fragment: Some(FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(ColorTargetState {
format: *surface_format,
blend: Some(BlendState::REPLACE),
write_mask: ColorWrites::ALL,
})],
}),
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: Some(Face::Back),
// Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
polygon_mode: PolygonMode::Fill,
// Requires Features::DEPTH_CLIP_CONTROL
unclipped_depth: false,
// Requires Features::CONSERVATIVE_RASTERIZATION
conservative: false,
},
depth_stencil: Some(DepthStencilState {
format: RawTexture::DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: CompareFunction::Less,
stencil: StencilState::default(),
bias: DepthBiasState::default(),
}),
multisample: MultisampleState {
count: *msaa_samples,
..Default::default()
},
multiview: None,
})
.into()
})
.clone()
}
}