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, ContentMask, Path, PathId, PathVertex, PrimitiveBatch,
  7    Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT,
  8};
  9use bytemuck::{Pod, Zeroable};
 10use collections::HashMap;
 11
 12use blade_graphics as gpu;
 13use std::sync::Arc;
 14
 15const SURFACE_FRAME_COUNT: u32 = 3;
 16const MAX_FRAME_TIME_MS: u32 = 1000;
 17
 18#[repr(C)]
 19#[derive(Clone, Copy, Pod, Zeroable)]
 20struct GlobalParams {
 21    viewport_size: [f32; 2],
 22    pad: [u32; 2],
 23}
 24
 25#[derive(blade_macros::ShaderData)]
 26struct ShaderQuadsData {
 27    globals: GlobalParams,
 28    b_quads: gpu::BufferPiece,
 29}
 30
 31#[derive(blade_macros::ShaderData)]
 32struct ShaderShadowsData {
 33    globals: GlobalParams,
 34    b_shadows: gpu::BufferPiece,
 35}
 36
 37struct BladePipelines {
 38    quads: gpu::RenderPipeline,
 39    shadows: gpu::RenderPipeline,
 40    path_rasterization: gpu::RenderPipeline,
 41}
 42
 43impl BladePipelines {
 44    fn new(gpu: &gpu::Context, surface_format: gpu::TextureFormat) -> Self {
 45        let shader = gpu.create_shader(gpu::ShaderDesc {
 46            source: include_str!("shaders.wgsl"),
 47        });
 48        shader.check_struct_size::<Quad>();
 49        shader.check_struct_size::<Shadow>();
 50        let quads_layout = <ShaderQuadsData as gpu::ShaderData>::layout();
 51        let shadows_layout = <ShaderShadowsData as gpu::ShaderData>::layout();
 52        Self {
 53            quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
 54                name: "quads",
 55                data_layouts: &[&quads_layout],
 56                vertex: shader.at("vs_quad"),
 57                primitive: gpu::PrimitiveState {
 58                    topology: gpu::PrimitiveTopology::TriangleStrip,
 59                    ..Default::default()
 60                },
 61                depth_stencil: None,
 62                fragment: shader.at("fs_quad"),
 63                color_targets: &[gpu::ColorTargetState {
 64                    format: surface_format,
 65                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
 66                    write_mask: gpu::ColorWrites::default(),
 67                }],
 68            }),
 69            shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
 70                name: "shadows",
 71                data_layouts: &[&shadows_layout],
 72                vertex: shader.at("vs_shadow"),
 73                primitive: gpu::PrimitiveState {
 74                    topology: gpu::PrimitiveTopology::TriangleStrip,
 75                    ..Default::default()
 76                },
 77                depth_stencil: None,
 78                fragment: shader.at("fs_shadow"),
 79                color_targets: &[gpu::ColorTargetState {
 80                    format: surface_format,
 81                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
 82                    write_mask: gpu::ColorWrites::default(),
 83                }],
 84            }),
 85            path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
 86                name: "path_rasterization",
 87                data_layouts: &[&shadows_layout],
 88                vertex: shader.at("vs_path_rasterization"),
 89                primitive: gpu::PrimitiveState {
 90                    topology: gpu::PrimitiveTopology::TriangleStrip,
 91                    ..Default::default()
 92                },
 93                depth_stencil: None,
 94                fragment: shader.at("fs_path_rasterization"),
 95                color_targets: &[gpu::ColorTargetState {
 96                    format: PATH_TEXTURE_FORMAT,
 97                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
 98                    write_mask: gpu::ColorWrites::default(),
 99                }],
100            }),
101        }
102    }
103}
104
105pub struct BladeRenderer {
106    gpu: Arc<gpu::Context>,
107    command_encoder: gpu::CommandEncoder,
108    last_sync_point: Option<gpu::SyncPoint>,
109    pipelines: BladePipelines,
110    instance_belt: BladeBelt,
111    viewport_size: gpu::Extent,
112    path_tiles: HashMap<PathId, AtlasTile>,
113    atlas: Arc<BladeAtlas>,
114}
115
116impl BladeRenderer {
117    pub fn new(gpu: Arc<gpu::Context>, size: gpu::Extent) -> Self {
118        let surface_format = gpu.resize(gpu::SurfaceConfig {
119            size,
120            usage: gpu::TextureUsage::TARGET,
121            frame_count: SURFACE_FRAME_COUNT,
122        });
123        let command_encoder = gpu.create_command_encoder(gpu::CommandEncoderDesc {
124            name: "main",
125            buffer_count: 2,
126        });
127        let pipelines = BladePipelines::new(&gpu, surface_format);
128        let instance_belt = BladeBelt::new(BladeBeltDescriptor {
129            memory: gpu::Memory::Shared,
130            min_chunk_size: 0x1000,
131        });
132        let atlas = Arc::new(BladeAtlas::new(&gpu));
133
134        Self {
135            gpu,
136            command_encoder,
137            last_sync_point: None,
138            pipelines,
139            instance_belt,
140            viewport_size: size,
141            path_tiles: HashMap::default(),
142            atlas,
143        }
144    }
145
146    fn wait_for_gpu(&mut self) {
147        if let Some(last_sp) = self.last_sync_point.take() {
148            if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {
149                panic!("GPU hung");
150            }
151        }
152    }
153
154    pub fn destroy(&mut self) {
155        self.wait_for_gpu();
156        self.atlas.destroy();
157        self.instance_belt.destroy(&self.gpu);
158        self.gpu.destroy_command_encoder(&mut self.command_encoder);
159    }
160
161    pub fn resize(&mut self, size: gpu::Extent) {
162        self.wait_for_gpu();
163        self.gpu.resize(gpu::SurfaceConfig {
164            size,
165            usage: gpu::TextureUsage::TARGET,
166            frame_count: SURFACE_FRAME_COUNT,
167        });
168        self.viewport_size = size;
169    }
170
171    pub fn atlas(&self) -> &Arc<BladeAtlas> {
172        &self.atlas
173    }
174
175    fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
176        self.path_tiles.clear();
177        let mut vertices_by_texture_id = HashMap::default();
178
179        for path in paths {
180            let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
181            let tile = self
182                .atlas
183                .allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path);
184            vertices_by_texture_id
185                .entry(tile.texture_id)
186                .or_insert(Vec::new())
187                .extend(path.vertices.iter().map(|vertex| PathVertex {
188                    xy_position: vertex.xy_position - clipped_bounds.origin
189                        + tile.bounds.origin.map(Into::into),
190                    st_position: vertex.st_position,
191                    content_mask: ContentMask {
192                        bounds: tile.bounds.map(Into::into),
193                    },
194                }));
195            self.path_tiles.insert(path.id, tile);
196        }
197
198        for (texture_id, vertices) in vertices_by_texture_id {
199            let instances = self.instance_belt.alloc_data(&vertices, &self.gpu);
200            let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
201                colors: &[gpu::RenderTarget {
202                    view: self.atlas.get_texture_view(texture_id),
203                    init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack),
204                    finish_op: gpu::FinishOp::Store,
205                }],
206                depth_stencil: None,
207            });
208
209            let mut encoder = pass.with(&self.pipelines.path_rasterization);
210            encoder.draw(0, vertices.len() as u32, 0, 1);
211        }
212    }
213
214    pub fn draw(&mut self, scene: &Scene) {
215        let frame = self.gpu.acquire_frame();
216        self.command_encoder.start();
217        self.command_encoder.init_texture(frame.texture());
218
219        self.rasterize_paths(scene.paths());
220
221        let globals = GlobalParams {
222            viewport_size: [
223                self.viewport_size.width as f32,
224                self.viewport_size.height as f32,
225            ],
226            pad: [0; 2],
227        };
228
229        if let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
230            colors: &[gpu::RenderTarget {
231                view: frame.texture_view(),
232                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
233                finish_op: gpu::FinishOp::Store,
234            }],
235            depth_stencil: None,
236        }) {
237            for batch in scene.batches() {
238                match batch {
239                    PrimitiveBatch::Quads(quads) => {
240                        let instances = self.instance_belt.alloc_data(quads, &self.gpu);
241                        let mut encoder = pass.with(&self.pipelines.quads);
242                        encoder.bind(
243                            0,
244                            &ShaderQuadsData {
245                                globals,
246                                b_quads: instances,
247                            },
248                        );
249                        encoder.draw(0, 4, 0, quads.len() as u32);
250                    }
251                    PrimitiveBatch::Shadows(shadows) => {
252                        let instances = self.instance_belt.alloc_data(shadows, &self.gpu);
253                        let mut encoder = pass.with(&self.pipelines.shadows);
254                        encoder.bind(
255                            0,
256                            &ShaderShadowsData {
257                                globals,
258                                b_shadows: instances,
259                            },
260                        );
261                        encoder.draw(0, 4, 0, shadows.len() as u32);
262                    }
263                    PrimitiveBatch::Paths(paths) => {}
264                    _ => continue,
265                }
266            }
267        }
268
269        self.command_encoder.present(frame);
270        let sync_point = self.gpu.submit(&mut self.command_encoder);
271        self.instance_belt.flush(&sync_point);
272        self.wait_for_gpu();
273        self.last_sync_point = Some(sync_point);
274    }
275}