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