1use crate::{GlyphToRender, Params};
2use wgpu::{
3 BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutEntry,
4 BindingResource, BindingType, BlendState, Buffer, BufferBindingType, ColorTargetState,
5 ColorWrites, DepthStencilState, Device, FilterMode, FragmentState, MultisampleState,
6 PipelineCompilationOptions, PipelineLayout, PipelineLayoutDescriptor, PrimitiveState,
7 PrimitiveTopology, RenderPipeline, RenderPipelineDescriptor, Sampler, SamplerBindingType,
8 SamplerDescriptor, ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages,
9 TextureFormat, TextureSampleType, TextureView, TextureViewDimension, VertexFormat, VertexState,
10};
11
12use std::borrow::Cow;
13use std::mem;
14use std::num::NonZeroU64;
15use std::ops::Deref;
16use std::sync::{Arc, Mutex};
17
18#[derive(Debug, Clone)]
19pub struct Cache(Arc<Inner>);
20
21#[derive(Debug)]
22struct Inner {
23 sampler: Sampler,
24 shader: ShaderModule,
25 vertex_buffers: [wgpu::VertexBufferLayout<'static>; 1],
26 atlas_layout: BindGroupLayout,
27 uniforms_layout: BindGroupLayout,
28 pipeline_layout: PipelineLayout,
29 cache: Mutex<
30 Vec<(
31 TextureFormat,
32 MultisampleState,
33 Option<DepthStencilState>,
34 RenderPipeline,
35 )>,
36 >,
37}
38
39impl Cache {
40 pub fn new(device: &Device) -> Self {
41 let sampler = device.create_sampler(&SamplerDescriptor {
42 label: Some("glyphon sampler"),
43 min_filter: FilterMode::Nearest,
44 mag_filter: FilterMode::Nearest,
45 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
46 lod_min_clamp: 0f32,
47 lod_max_clamp: 0f32,
48 ..Default::default()
49 });
50
51 let shader = device.create_shader_module(ShaderModuleDescriptor {
52 label: Some("glyphon shader"),
53 source: ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
54 });
55
56 let vertex_buffer_layout = wgpu::VertexBufferLayout {
57 array_stride: mem::size_of::<GlyphToRender>() as wgpu::BufferAddress,
58 step_mode: wgpu::VertexStepMode::Instance,
59 attributes: &[
60 wgpu::VertexAttribute {
61 format: VertexFormat::Sint32x2,
62 offset: 0,
63 shader_location: 0,
64 },
65 wgpu::VertexAttribute {
66 format: VertexFormat::Uint32,
67 offset: mem::size_of::<u32>() as u64 * 2,
68 shader_location: 1,
69 },
70 wgpu::VertexAttribute {
71 format: VertexFormat::Uint32,
72 offset: mem::size_of::<u32>() as u64 * 3,
73 shader_location: 2,
74 },
75 wgpu::VertexAttribute {
76 format: VertexFormat::Uint32,
77 offset: mem::size_of::<u32>() as u64 * 4,
78 shader_location: 3,
79 },
80 wgpu::VertexAttribute {
81 format: VertexFormat::Uint32,
82 offset: mem::size_of::<u32>() as u64 * 5,
83 shader_location: 4,
84 },
85 wgpu::VertexAttribute {
86 format: VertexFormat::Float32,
87 offset: mem::size_of::<u32>() as u64 * 6,
88 shader_location: 5,
89 },
90 ],
91 };
92
93 let atlas_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
94 entries: &[
95 BindGroupLayoutEntry {
96 binding: 0,
97 visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
98 ty: BindingType::Texture {
99 multisampled: false,
100 view_dimension: TextureViewDimension::D2,
101 sample_type: TextureSampleType::Float { filterable: true },
102 },
103 count: None,
104 },
105 BindGroupLayoutEntry {
106 binding: 1,
107 visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
108 ty: BindingType::Texture {
109 multisampled: false,
110 view_dimension: TextureViewDimension::D2,
111 sample_type: TextureSampleType::Float { filterable: true },
112 },
113 count: None,
114 },
115 BindGroupLayoutEntry {
116 binding: 2,
117 visibility: ShaderStages::FRAGMENT,
118 ty: BindingType::Sampler(SamplerBindingType::Filtering),
119 count: None,
120 },
121 ],
122 label: Some("glyphon atlas bind group layout"),
123 });
124
125 let uniforms_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
126 entries: &[BindGroupLayoutEntry {
127 binding: 0,
128 visibility: ShaderStages::VERTEX,
129 ty: BindingType::Buffer {
130 ty: BufferBindingType::Uniform,
131 has_dynamic_offset: false,
132 min_binding_size: NonZeroU64::new(mem::size_of::<Params>() as u64),
133 },
134 count: None,
135 }],
136 label: Some("glyphon uniforms bind group layout"),
137 });
138
139 let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
140 label: None,
141 bind_group_layouts: &[&atlas_layout, &uniforms_layout],
142 immediate_size: 0,
143 });
144
145 Self(Arc::new(Inner {
146 sampler,
147 shader,
148 vertex_buffers: [vertex_buffer_layout],
149 uniforms_layout,
150 atlas_layout,
151 pipeline_layout,
152 cache: Mutex::new(Vec::new()),
153 }))
154 }
155
156 pub(crate) fn create_atlas_bind_group(
157 &self,
158 device: &Device,
159 color_atlas: &TextureView,
160 mask_atlas: &TextureView,
161 ) -> BindGroup {
162 device.create_bind_group(&BindGroupDescriptor {
163 layout: &self.0.atlas_layout,
164 entries: &[
165 BindGroupEntry {
166 binding: 0,
167 resource: BindingResource::TextureView(color_atlas),
168 },
169 BindGroupEntry {
170 binding: 1,
171 resource: BindingResource::TextureView(mask_atlas),
172 },
173 BindGroupEntry {
174 binding: 2,
175 resource: BindingResource::Sampler(&self.0.sampler),
176 },
177 ],
178 label: Some("glyphon atlas bind group"),
179 })
180 }
181
182 pub(crate) fn create_uniforms_bind_group(&self, device: &Device, buffer: &Buffer) -> BindGroup {
183 device.create_bind_group(&BindGroupDescriptor {
184 layout: &self.0.uniforms_layout,
185 entries: &[BindGroupEntry {
186 binding: 0,
187 resource: buffer.as_entire_binding(),
188 }],
189 label: Some("glyphon uniforms bind group"),
190 })
191 }
192
193 pub(crate) fn get_or_create_pipeline(
194 &self,
195 device: &Device,
196 format: TextureFormat,
197 multisample: MultisampleState,
198 depth_stencil: Option<DepthStencilState>,
199 ) -> RenderPipeline {
200 let Inner {
201 cache,
202 pipeline_layout,
203 shader,
204 vertex_buffers,
205 ..
206 } = self.0.deref();
207
208 let mut cache = cache.lock().expect("Write pipeline cache");
209
210 cache
211 .iter()
212 .find(|(fmt, ms, ds, _)| fmt == &format && ms == &multisample && ds == &depth_stencil)
213 .map(|(_, _, _, p)| p.clone())
214 .unwrap_or_else(|| {
215 let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
216 label: Some("glyphon pipeline"),
217 layout: Some(pipeline_layout),
218 vertex: VertexState {
219 module: shader,
220 entry_point: Some("vs_main"),
221 buffers: vertex_buffers,
222 compilation_options: PipelineCompilationOptions::default(),
223 },
224 fragment: Some(FragmentState {
225 module: shader,
226 entry_point: Some("fs_main"),
227 targets: &[Some(ColorTargetState {
228 format,
229 blend: Some(BlendState::ALPHA_BLENDING),
230 write_mask: ColorWrites::default(),
231 })],
232 compilation_options: PipelineCompilationOptions::default(),
233 }),
234 primitive: PrimitiveState {
235 topology: PrimitiveTopology::TriangleStrip,
236 ..PrimitiveState::default()
237 },
238 depth_stencil: depth_stencil.clone(),
239 multisample,
240 multiview_mask: None,
241 cache: None,
242 });
243
244 cache.push((format, multisample, depth_stencil, pipeline.clone()));
245
246 pipeline
247 })
248 .clone()
249 }
250}