blade_renderer.rs

  1// Doing `if let` gives you nice scoping with passes/encoders
  2#![allow(irrefutable_let_patterns)]
  3
  4use super::{BladeBelt, BladeBeltDescriptor};
  5use crate::{
  6    AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, MonochromeSprite, Path,
  7    PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow,
  8    Underline, PATH_TEXTURE_FORMAT,
  9};
 10use bytemuck::{Pod, Zeroable};
 11use collections::HashMap;
 12
 13use blade_graphics as gpu;
 14use std::{mem, sync::Arc};
 15
 16const SURFACE_FRAME_COUNT: u32 = 3;
 17const MAX_FRAME_TIME_MS: u32 = 1000;
 18
 19#[repr(C)]
 20#[derive(Clone, Copy, Pod, Zeroable)]
 21struct GlobalParams {
 22    viewport_size: [f32; 2],
 23    pad: [u32; 2],
 24}
 25
 26#[derive(blade_macros::ShaderData)]
 27struct ShaderQuadsData {
 28    globals: GlobalParams,
 29    b_quads: gpu::BufferPiece,
 30}
 31
 32#[derive(blade_macros::ShaderData)]
 33struct ShaderShadowsData {
 34    globals: GlobalParams,
 35    b_shadows: gpu::BufferPiece,
 36}
 37
 38#[derive(blade_macros::ShaderData)]
 39struct ShaderPathRasterizationData {
 40    globals: GlobalParams,
 41    b_path_vertices: gpu::BufferPiece,
 42}
 43
 44#[derive(blade_macros::ShaderData)]
 45struct ShaderPathsData {
 46    globals: GlobalParams,
 47    t_sprite: gpu::TextureView,
 48    s_sprite: gpu::Sampler,
 49    b_path_sprites: gpu::BufferPiece,
 50}
 51
 52#[derive(blade_macros::ShaderData)]
 53struct ShaderUnderlinesData {
 54    globals: GlobalParams,
 55    b_underlines: gpu::BufferPiece,
 56}
 57
 58#[derive(blade_macros::ShaderData)]
 59struct ShaderMonoSpritesData {
 60    globals: GlobalParams,
 61    t_sprite: gpu::TextureView,
 62    s_sprite: gpu::Sampler,
 63    b_mono_sprites: gpu::BufferPiece,
 64}
 65
 66#[derive(blade_macros::ShaderData)]
 67struct ShaderPolySpritesData {
 68    globals: GlobalParams,
 69    t_sprite: gpu::TextureView,
 70    s_sprite: gpu::Sampler,
 71    b_poly_sprites: gpu::BufferPiece,
 72}
 73
 74#[derive(Clone, Debug, Eq, PartialEq)]
 75#[repr(C)]
 76struct PathSprite {
 77    bounds: Bounds<ScaledPixels>,
 78    color: Hsla,
 79    tile: AtlasTile,
 80}
 81
 82struct BladePipelines {
 83    quads: gpu::RenderPipeline,
 84    shadows: gpu::RenderPipeline,
 85    path_rasterization: gpu::RenderPipeline,
 86    paths: gpu::RenderPipeline,
 87    underlines: gpu::RenderPipeline,
 88    mono_sprites: gpu::RenderPipeline,
 89    poly_sprites: gpu::RenderPipeline,
 90}
 91
 92impl BladePipelines {
 93    fn new(gpu: &gpu::Context, surface_format: gpu::TextureFormat) -> Self {
 94        use gpu::ShaderData as _;
 95
 96        let shader = gpu.create_shader(gpu::ShaderDesc {
 97            source: include_str!("shaders.wgsl"),
 98        });
 99        shader.check_struct_size::<Quad>();
100        shader.check_struct_size::<Shadow>();
101        assert_eq!(
102            mem::size_of::<PathVertex<ScaledPixels>>(),
103            shader.get_struct_size("PathVertex") as usize,
104        );
105        shader.check_struct_size::<PathSprite>();
106        shader.check_struct_size::<Underline>();
107        shader.check_struct_size::<MonochromeSprite>();
108        shader.check_struct_size::<PolychromeSprite>();
109
110        Self {
111            quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
112                name: "quads",
113                data_layouts: &[&ShaderQuadsData::layout()],
114                vertex: shader.at("vs_quad"),
115                primitive: gpu::PrimitiveState {
116                    topology: gpu::PrimitiveTopology::TriangleStrip,
117                    ..Default::default()
118                },
119                depth_stencil: None,
120                fragment: shader.at("fs_quad"),
121                color_targets: &[gpu::ColorTargetState {
122                    format: surface_format,
123                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
124                    write_mask: gpu::ColorWrites::default(),
125                }],
126            }),
127            shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
128                name: "shadows",
129                data_layouts: &[&ShaderShadowsData::layout()],
130                vertex: shader.at("vs_shadow"),
131                primitive: gpu::PrimitiveState {
132                    topology: gpu::PrimitiveTopology::TriangleStrip,
133                    ..Default::default()
134                },
135                depth_stencil: None,
136                fragment: shader.at("fs_shadow"),
137                color_targets: &[gpu::ColorTargetState {
138                    format: surface_format,
139                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
140                    write_mask: gpu::ColorWrites::default(),
141                }],
142            }),
143            path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
144                name: "path_rasterization",
145                data_layouts: &[&ShaderPathRasterizationData::layout()],
146                vertex: shader.at("vs_path_rasterization"),
147                primitive: gpu::PrimitiveState {
148                    topology: gpu::PrimitiveTopology::TriangleStrip,
149                    ..Default::default()
150                },
151                depth_stencil: None,
152                fragment: shader.at("fs_path_rasterization"),
153                color_targets: &[gpu::ColorTargetState {
154                    format: PATH_TEXTURE_FORMAT,
155                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
156                    write_mask: gpu::ColorWrites::default(),
157                }],
158            }),
159            paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
160                name: "paths",
161                data_layouts: &[&ShaderPathsData::layout()],
162                vertex: shader.at("vs_path"),
163                primitive: gpu::PrimitiveState {
164                    topology: gpu::PrimitiveTopology::TriangleStrip,
165                    ..Default::default()
166                },
167                depth_stencil: None,
168                fragment: shader.at("fs_path"),
169                color_targets: &[gpu::ColorTargetState {
170                    format: surface_format,
171                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
172                    write_mask: gpu::ColorWrites::default(),
173                }],
174            }),
175            underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
176                name: "underlines",
177                data_layouts: &[&ShaderUnderlinesData::layout()],
178                vertex: shader.at("vs_underline"),
179                primitive: gpu::PrimitiveState {
180                    topology: gpu::PrimitiveTopology::TriangleStrip,
181                    ..Default::default()
182                },
183                depth_stencil: None,
184                fragment: shader.at("fs_underline"),
185                color_targets: &[gpu::ColorTargetState {
186                    format: surface_format,
187                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
188                    write_mask: gpu::ColorWrites::default(),
189                }],
190            }),
191            mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
192                name: "mono-sprites",
193                data_layouts: &[&ShaderMonoSpritesData::layout()],
194                vertex: shader.at("vs_mono_sprite"),
195                primitive: gpu::PrimitiveState {
196                    topology: gpu::PrimitiveTopology::TriangleStrip,
197                    ..Default::default()
198                },
199                depth_stencil: None,
200                fragment: shader.at("fs_mono_sprite"),
201                color_targets: &[gpu::ColorTargetState {
202                    format: surface_format,
203                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
204                    write_mask: gpu::ColorWrites::default(),
205                }],
206            }),
207            poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
208                name: "poly-sprites",
209                data_layouts: &[&ShaderPolySpritesData::layout()],
210                vertex: shader.at("vs_poly_sprite"),
211                primitive: gpu::PrimitiveState {
212                    topology: gpu::PrimitiveTopology::TriangleStrip,
213                    ..Default::default()
214                },
215                depth_stencil: None,
216                fragment: shader.at("fs_poly_sprite"),
217                color_targets: &[gpu::ColorTargetState {
218                    format: surface_format,
219                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
220                    write_mask: gpu::ColorWrites::default(),
221                }],
222            }),
223        }
224    }
225}
226
227pub struct BladeRenderer {
228    gpu: Arc<gpu::Context>,
229    command_encoder: gpu::CommandEncoder,
230    last_sync_point: Option<gpu::SyncPoint>,
231    pipelines: BladePipelines,
232    instance_belt: BladeBelt,
233    viewport_size: gpu::Extent,
234    path_tiles: HashMap<PathId, AtlasTile>,
235    atlas: Arc<BladeAtlas>,
236    atlas_sampler: gpu::Sampler,
237}
238
239impl BladeRenderer {
240    pub fn new(gpu: Arc<gpu::Context>, size: gpu::Extent) -> Self {
241        let surface_format = gpu.resize(gpu::SurfaceConfig {
242            size,
243            usage: gpu::TextureUsage::TARGET,
244            frame_count: SURFACE_FRAME_COUNT,
245        });
246        let command_encoder = gpu.create_command_encoder(gpu::CommandEncoderDesc {
247            name: "main",
248            buffer_count: 2,
249        });
250        let pipelines = BladePipelines::new(&gpu, surface_format);
251        let instance_belt = BladeBelt::new(BladeBeltDescriptor {
252            memory: gpu::Memory::Shared,
253            min_chunk_size: 0x1000,
254        });
255        let atlas = Arc::new(BladeAtlas::new(&gpu));
256        let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc {
257            name: "atlas",
258            mag_filter: gpu::FilterMode::Linear,
259            min_filter: gpu::FilterMode::Linear,
260            ..Default::default()
261        });
262
263        Self {
264            gpu,
265            command_encoder,
266            last_sync_point: None,
267            pipelines,
268            instance_belt,
269            viewport_size: size,
270            path_tiles: HashMap::default(),
271            atlas,
272            atlas_sampler,
273        }
274    }
275
276    fn wait_for_gpu(&mut self) {
277        if let Some(last_sp) = self.last_sync_point.take() {
278            if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {
279                panic!("GPU hung");
280            }
281        }
282    }
283
284    pub fn destroy(&mut self) {
285        self.wait_for_gpu();
286        self.atlas.destroy();
287        self.instance_belt.destroy(&self.gpu);
288        self.gpu.destroy_command_encoder(&mut self.command_encoder);
289    }
290
291    pub fn resize(&mut self, size: gpu::Extent) {
292        self.wait_for_gpu();
293        self.gpu.resize(gpu::SurfaceConfig {
294            size,
295            usage: gpu::TextureUsage::TARGET,
296            frame_count: SURFACE_FRAME_COUNT,
297        });
298        self.viewport_size = size;
299    }
300
301    pub fn atlas(&self) -> &Arc<BladeAtlas> {
302        &self.atlas
303    }
304
305    fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
306        self.path_tiles.clear();
307        let mut vertices_by_texture_id = HashMap::default();
308
309        for path in paths {
310            let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
311            let tile = self
312                .atlas
313                .allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path);
314            vertices_by_texture_id
315                .entry(tile.texture_id)
316                .or_insert(Vec::new())
317                .extend(path.vertices.iter().map(|vertex| PathVertex {
318                    xy_position: vertex.xy_position - clipped_bounds.origin
319                        + tile.bounds.origin.map(Into::into),
320                    st_position: vertex.st_position,
321                    content_mask: ContentMask {
322                        bounds: tile.bounds.map(Into::into),
323                    },
324                }));
325            self.path_tiles.insert(path.id, tile);
326        }
327
328        for (texture_id, vertices) in vertices_by_texture_id {
329            let tex_info = self.atlas.get_texture_info(texture_id);
330            let globals = GlobalParams {
331                viewport_size: [tex_info.size.width as f32, tex_info.size.height as f32],
332                pad: [0; 2],
333            };
334
335            let vertex_buf = self.instance_belt.alloc_data(&vertices, &self.gpu);
336            let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
337                colors: &[gpu::RenderTarget {
338                    view: tex_info.raw_view.unwrap(),
339                    init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack),
340                    finish_op: gpu::FinishOp::Store,
341                }],
342                depth_stencil: None,
343            });
344
345            let mut encoder = pass.with(&self.pipelines.path_rasterization);
346            encoder.bind(
347                0,
348                &ShaderPathRasterizationData {
349                    globals,
350                    b_path_vertices: vertex_buf,
351                },
352            );
353            encoder.draw(0, vertices.len() as u32, 0, 1);
354        }
355    }
356
357    pub fn draw(&mut self, scene: &Scene) {
358        let frame = self.gpu.acquire_frame();
359        self.command_encoder.start();
360        self.command_encoder.init_texture(frame.texture());
361
362        self.atlas.before_frame(&mut self.command_encoder);
363        self.rasterize_paths(scene.paths());
364
365        let globals = GlobalParams {
366            viewport_size: [
367                self.viewport_size.width as f32,
368                self.viewport_size.height as f32,
369            ],
370            pad: [0; 2],
371        };
372
373        if let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
374            colors: &[gpu::RenderTarget {
375                view: frame.texture_view(),
376                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
377                finish_op: gpu::FinishOp::Store,
378            }],
379            depth_stencil: None,
380        }) {
381            for batch in scene.batches() {
382                match batch {
383                    PrimitiveBatch::Quads(quads) => {
384                        let instance_buf = self.instance_belt.alloc_data(quads, &self.gpu);
385                        let mut encoder = pass.with(&self.pipelines.quads);
386                        encoder.bind(
387                            0,
388                            &ShaderQuadsData {
389                                globals,
390                                b_quads: instance_buf,
391                            },
392                        );
393                        encoder.draw(0, 4, 0, quads.len() as u32);
394                    }
395                    PrimitiveBatch::Shadows(shadows) => {
396                        let instance_buf = self.instance_belt.alloc_data(shadows, &self.gpu);
397                        let mut encoder = pass.with(&self.pipelines.shadows);
398                        encoder.bind(
399                            0,
400                            &ShaderShadowsData {
401                                globals,
402                                b_shadows: instance_buf,
403                            },
404                        );
405                        encoder.draw(0, 4, 0, shadows.len() as u32);
406                    }
407                    PrimitiveBatch::Paths(paths) => {
408                        let mut encoder = pass.with(&self.pipelines.paths);
409                        //TODO: group by texture ID
410                        for path in paths {
411                            let tile = &self.path_tiles[&path.id];
412                            let tex_info = self.atlas.get_texture_info(tile.texture_id);
413                            let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
414                            let sprites = [PathSprite {
415                                bounds: Bounds {
416                                    origin: origin.map(|p| p.floor()),
417                                    size: tile.bounds.size.map(Into::into),
418                                },
419                                color: path.color,
420                                tile: (*tile).clone(),
421                            }];
422
423                            let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu);
424                            encoder.bind(
425                                0,
426                                &ShaderPathsData {
427                                    globals,
428                                    t_sprite: tex_info.raw_view.unwrap(),
429                                    s_sprite: self.atlas_sampler,
430                                    b_path_sprites: instance_buf,
431                                },
432                            );
433                            encoder.draw(0, 4, 0, sprites.len() as u32);
434                        }
435                    }
436                    PrimitiveBatch::Underlines(underlines) => {
437                        let instance_buf = self.instance_belt.alloc_data(underlines, &self.gpu);
438                        let mut encoder = pass.with(&self.pipelines.underlines);
439                        encoder.bind(
440                            0,
441                            &ShaderUnderlinesData {
442                                globals,
443                                b_underlines: instance_buf,
444                            },
445                        );
446                        encoder.draw(0, 4, 0, underlines.len() as u32);
447                    }
448                    PrimitiveBatch::MonochromeSprites {
449                        texture_id,
450                        sprites,
451                    } => {
452                        let tex_info = self.atlas.get_texture_info(texture_id);
453                        let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu);
454                        let mut encoder = pass.with(&self.pipelines.mono_sprites);
455                        encoder.bind(
456                            0,
457                            &ShaderMonoSpritesData {
458                                globals,
459                                t_sprite: tex_info.raw_view.unwrap(),
460                                s_sprite: self.atlas_sampler,
461                                b_mono_sprites: instance_buf,
462                            },
463                        );
464                        encoder.draw(0, 4, 0, sprites.len() as u32);
465                    }
466                    PrimitiveBatch::PolychromeSprites {
467                        texture_id,
468                        sprites,
469                    } => {
470                        let tex_info = self.atlas.get_texture_info(texture_id);
471                        let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu);
472                        let mut encoder = pass.with(&self.pipelines.poly_sprites);
473                        encoder.bind(
474                            0,
475                            &ShaderPolySpritesData {
476                                globals,
477                                t_sprite: tex_info.raw_view.unwrap(),
478                                s_sprite: self.atlas_sampler,
479                                b_poly_sprites: instance_buf,
480                            },
481                        );
482                        encoder.draw(0, 4, 0, sprites.len() as u32);
483                    }
484                    PrimitiveBatch::Surfaces { .. } => {
485                        unimplemented!()
486                    }
487                }
488            }
489        }
490
491        self.command_encoder.present(frame);
492        let sync_point = self.gpu.submit(&mut self.command_encoder);
493
494        self.instance_belt.flush(&sync_point);
495        self.atlas.after_frame(&sync_point);
496        self.atlas.clear_textures(AtlasTextureKind::Path);
497
498        self.wait_for_gpu();
499        self.last_sync_point = Some(sync_point);
500    }
501}