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            alignment: 0x40, // Vulkan `minStorageBufferOffsetAlignment` on Intel Xe
255        });
256        let atlas = Arc::new(BladeAtlas::new(&gpu));
257        let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc {
258            name: "atlas",
259            mag_filter: gpu::FilterMode::Linear,
260            min_filter: gpu::FilterMode::Linear,
261            ..Default::default()
262        });
263
264        Self {
265            gpu,
266            command_encoder,
267            last_sync_point: None,
268            pipelines,
269            instance_belt,
270            viewport_size: size,
271            path_tiles: HashMap::default(),
272            atlas,
273            atlas_sampler,
274        }
275    }
276
277    fn wait_for_gpu(&mut self) {
278        if let Some(last_sp) = self.last_sync_point.take() {
279            if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {
280                panic!("GPU hung");
281            }
282        }
283    }
284
285    pub fn destroy(&mut self) {
286        self.wait_for_gpu();
287        self.atlas.destroy();
288        self.instance_belt.destroy(&self.gpu);
289        self.gpu.destroy_command_encoder(&mut self.command_encoder);
290    }
291
292    pub fn resize(&mut self, size: gpu::Extent) {
293        self.wait_for_gpu();
294        self.gpu.resize(gpu::SurfaceConfig {
295            size,
296            usage: gpu::TextureUsage::TARGET,
297            frame_count: SURFACE_FRAME_COUNT,
298        });
299        self.viewport_size = size;
300    }
301
302    pub fn viewport_size(&self) -> gpu::Extent {
303        self.viewport_size
304    }
305
306    pub fn atlas(&self) -> &Arc<BladeAtlas> {
307        &self.atlas
308    }
309
310    fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
311        self.path_tiles.clear();
312        let mut vertices_by_texture_id = HashMap::default();
313
314        for path in paths {
315            let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
316            let tile = self
317                .atlas
318                .allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path);
319            vertices_by_texture_id
320                .entry(tile.texture_id)
321                .or_insert(Vec::new())
322                .extend(path.vertices.iter().map(|vertex| PathVertex {
323                    xy_position: vertex.xy_position - clipped_bounds.origin
324                        + tile.bounds.origin.map(Into::into),
325                    st_position: vertex.st_position,
326                    content_mask: ContentMask {
327                        bounds: tile.bounds.map(Into::into),
328                    },
329                }));
330            self.path_tiles.insert(path.id, tile);
331        }
332
333        for (texture_id, vertices) in vertices_by_texture_id {
334            let tex_info = self.atlas.get_texture_info(texture_id);
335            let globals = GlobalParams {
336                viewport_size: [tex_info.size.width as f32, tex_info.size.height as f32],
337                pad: [0; 2],
338            };
339
340            let vertex_buf = self.instance_belt.alloc_data(&vertices, &self.gpu);
341            let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
342                colors: &[gpu::RenderTarget {
343                    view: tex_info.raw_view,
344                    init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack),
345                    finish_op: gpu::FinishOp::Store,
346                }],
347                depth_stencil: None,
348            });
349
350            let mut encoder = pass.with(&self.pipelines.path_rasterization);
351            encoder.bind(
352                0,
353                &ShaderPathRasterizationData {
354                    globals,
355                    b_path_vertices: vertex_buf,
356                },
357            );
358            encoder.draw(0, vertices.len() as u32, 0, 1);
359        }
360    }
361
362    pub fn draw(&mut self, scene: &Scene) {
363        let frame = self.gpu.acquire_frame();
364        self.command_encoder.start();
365        self.command_encoder.init_texture(frame.texture());
366
367        self.atlas.before_frame(&mut self.command_encoder);
368        self.rasterize_paths(scene.paths());
369
370        let globals = GlobalParams {
371            viewport_size: [
372                self.viewport_size.width as f32,
373                self.viewport_size.height as f32,
374            ],
375            pad: [0; 2],
376        };
377
378        if let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
379            colors: &[gpu::RenderTarget {
380                view: frame.texture_view(),
381                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
382                finish_op: gpu::FinishOp::Store,
383            }],
384            depth_stencil: None,
385        }) {
386            for batch in scene.batches() {
387                match batch {
388                    PrimitiveBatch::Quads(quads) => {
389                        let instance_buf = self.instance_belt.alloc_data(quads, &self.gpu);
390                        let mut encoder = pass.with(&self.pipelines.quads);
391                        encoder.bind(
392                            0,
393                            &ShaderQuadsData {
394                                globals,
395                                b_quads: instance_buf,
396                            },
397                        );
398                        encoder.draw(0, 4, 0, quads.len() as u32);
399                    }
400                    PrimitiveBatch::Shadows(shadows) => {
401                        let instance_buf = self.instance_belt.alloc_data(shadows, &self.gpu);
402                        let mut encoder = pass.with(&self.pipelines.shadows);
403                        encoder.bind(
404                            0,
405                            &ShaderShadowsData {
406                                globals,
407                                b_shadows: instance_buf,
408                            },
409                        );
410                        encoder.draw(0, 4, 0, shadows.len() as u32);
411                    }
412                    PrimitiveBatch::Paths(paths) => {
413                        let mut encoder = pass.with(&self.pipelines.paths);
414                        //todo!(linux): group by texture ID
415                        for path in paths {
416                            let tile = &self.path_tiles[&path.id];
417                            let tex_info = self.atlas.get_texture_info(tile.texture_id);
418                            let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
419                            let sprites = [PathSprite {
420                                bounds: Bounds {
421                                    origin: origin.map(|p| p.floor()),
422                                    size: tile.bounds.size.map(Into::into),
423                                },
424                                color: path.color,
425                                tile: (*tile).clone(),
426                            }];
427
428                            let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu);
429                            encoder.bind(
430                                0,
431                                &ShaderPathsData {
432                                    globals,
433                                    t_sprite: tex_info.raw_view,
434                                    s_sprite: self.atlas_sampler,
435                                    b_path_sprites: instance_buf,
436                                },
437                            );
438                            encoder.draw(0, 4, 0, sprites.len() as u32);
439                        }
440                    }
441                    PrimitiveBatch::Underlines(underlines) => {
442                        let instance_buf = self.instance_belt.alloc_data(underlines, &self.gpu);
443                        let mut encoder = pass.with(&self.pipelines.underlines);
444                        encoder.bind(
445                            0,
446                            &ShaderUnderlinesData {
447                                globals,
448                                b_underlines: instance_buf,
449                            },
450                        );
451                        encoder.draw(0, 4, 0, underlines.len() as u32);
452                    }
453                    PrimitiveBatch::MonochromeSprites {
454                        texture_id,
455                        sprites,
456                    } => {
457                        let tex_info = self.atlas.get_texture_info(texture_id);
458                        let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu);
459                        let mut encoder = pass.with(&self.pipelines.mono_sprites);
460                        encoder.bind(
461                            0,
462                            &ShaderMonoSpritesData {
463                                globals,
464                                t_sprite: tex_info.raw_view,
465                                s_sprite: self.atlas_sampler,
466                                b_mono_sprites: instance_buf,
467                            },
468                        );
469                        encoder.draw(0, 4, 0, sprites.len() as u32);
470                    }
471                    PrimitiveBatch::PolychromeSprites {
472                        texture_id,
473                        sprites,
474                    } => {
475                        let tex_info = self.atlas.get_texture_info(texture_id);
476                        let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu);
477                        let mut encoder = pass.with(&self.pipelines.poly_sprites);
478                        encoder.bind(
479                            0,
480                            &ShaderPolySpritesData {
481                                globals,
482                                t_sprite: tex_info.raw_view,
483                                s_sprite: self.atlas_sampler,
484                                b_poly_sprites: instance_buf,
485                            },
486                        );
487                        encoder.draw(0, 4, 0, sprites.len() as u32);
488                    }
489                    PrimitiveBatch::Surfaces { .. } => {
490                        unimplemented!()
491                    }
492                }
493            }
494        }
495
496        self.command_encoder.present(frame);
497        let sync_point = self.gpu.submit(&mut self.command_encoder);
498
499        self.instance_belt.flush(&sync_point);
500        self.atlas.after_frame(&sync_point);
501        self.atlas.clear_textures(AtlasTextureKind::Path);
502
503        self.wait_for_gpu();
504        self.last_sync_point = Some(sync_point);
505    }
506}