1use crate::gpu_types::{FillInstance, ProjectionUniform, StrokeInstance};
6use crate::instance_buffer::InstanceBuffer;
7use crate::pipeline::{
8 create_fill_pipeline, create_projection_bind_group_layout, create_stroke_pipeline,
9};
10use crate::vertex::{FillVertex, StrokeVertex, TessellatedMesh};
11use crate::{FillRule, Path, Shape, Stroke, Style, Tessellator};
12use astrelis_core::profiling::profile_scope;
13use astrelis_render::wgpu::util::DeviceExt;
14use astrelis_render::{Color, GraphicsContext, RenderWindow, Renderer, Viewport, wgpu};
15use glam::Vec2;
16use std::sync::Arc;
17
18#[derive(Clone, Debug)]
36pub struct GeometryRendererDescriptor {
37 pub name: String,
39 pub surface_format: wgpu::TextureFormat,
41 pub depth_format: Option<wgpu::TextureFormat>,
43}
44
45impl Default for GeometryRendererDescriptor {
46 fn default() -> Self {
47 Self {
48 name: "Geometry".to_string(),
49 surface_format: wgpu::TextureFormat::Bgra8UnormSrgb,
50 depth_format: None,
51 }
52 }
53}
54
55impl GeometryRendererDescriptor {
56 pub fn from_window(window: &RenderWindow) -> Self {
61 Self {
62 name: "Geometry".to_string(),
63 surface_format: window.surface_format(),
64 depth_format: window.depth_format(),
65 }
66 }
67
68 pub fn with_name(mut self, name: impl Into<String>) -> Self {
70 self.name = name.into();
71 self
72 }
73
74 pub fn with_depth(mut self, format: wgpu::TextureFormat) -> Self {
76 self.depth_format = Some(format);
77 self
78 }
79
80 pub fn without_depth(mut self) -> Self {
82 self.depth_format = None;
83 self
84 }
85}
86
87#[derive(Debug, Clone, Copy)]
89pub struct ScissorRect {
90 pub x: u32,
91 pub y: u32,
92 pub width: u32,
93 pub height: u32,
94}
95
96impl ScissorRect {
97 pub fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
99 Self {
100 x,
101 y,
102 width,
103 height,
104 }
105 }
106
107 pub fn from_f32(x: f32, y: f32, width: f32, height: f32) -> Self {
109 Self {
110 x: x.max(0.0) as u32,
111 y: y.max(0.0) as u32,
112 width: width.max(0.0) as u32,
113 height: height.max(0.0) as u32,
114 }
115 }
116}
117
118#[derive(Debug)]
120enum DrawCommand {
121 Fill {
123 mesh: TessellatedMesh<FillVertex>,
124 color: Color,
125 offset: Vec2,
126 },
127 Stroke {
129 mesh: TessellatedMesh<StrokeVertex>,
130 color: Color,
131 width: f32,
132 offset: Vec2,
133 },
134 SetScissor(ScissorRect),
136 ResetScissor,
138}
139
140pub struct GeometryRenderer {
144 context: Arc<GraphicsContext>,
145 renderer: Renderer,
146
147 descriptor: GeometryRendererDescriptor,
149
150 fill_pipeline: wgpu::RenderPipeline,
152 stroke_pipeline: wgpu::RenderPipeline,
153
154 projection_bind_group_layout: wgpu::BindGroupLayout,
156 projection_bind_group: wgpu::BindGroup,
157 projection_buffer: wgpu::Buffer,
158
159 tessellator: Tessellator,
161
162 draw_commands: Vec<DrawCommand>,
164
165 fill_vertex_buffer: Option<wgpu::Buffer>,
167 fill_index_buffer: Option<wgpu::Buffer>,
168 fill_instances: InstanceBuffer<FillInstance>,
169
170 stroke_vertex_buffer: Option<wgpu::Buffer>,
171 stroke_index_buffer: Option<wgpu::Buffer>,
172 stroke_instances: InstanceBuffer<StrokeInstance>,
173}
174
175impl GeometryRenderer {
176 pub fn new(context: Arc<GraphicsContext>) -> Self {
178 Self::with_descriptor(context, GeometryRendererDescriptor::default())
179 }
180
181 pub fn from_window(context: Arc<GraphicsContext>, window: &RenderWindow) -> Self {
186 Self::with_descriptor(context, GeometryRendererDescriptor::from_window(window))
187 }
188
189 pub fn with_descriptor(
191 context: Arc<GraphicsContext>,
192 descriptor: GeometryRendererDescriptor,
193 ) -> Self {
194 let renderer = Renderer::new(context.clone());
195
196 let projection_buffer = context.device().create_buffer(&wgpu::BufferDescriptor {
198 label: Some(&format!("{} Projection Buffer", descriptor.name)),
199 size: std::mem::size_of::<ProjectionUniform>() as u64,
200 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
201 mapped_at_creation: false,
202 });
203
204 let projection_bind_group_layout = create_projection_bind_group_layout(&renderer);
206 let projection_bind_group = renderer.create_bind_group(
207 Some(&format!("{} Projection Bind Group", descriptor.name)),
208 &projection_bind_group_layout,
209 &[wgpu::BindGroupEntry {
210 binding: 0,
211 resource: projection_buffer.as_entire_binding(),
212 }],
213 );
214
215 let fill_pipeline = create_fill_pipeline(
217 &renderer,
218 &projection_bind_group_layout,
219 descriptor.surface_format,
220 descriptor.depth_format,
221 &descriptor.name,
222 );
223 let stroke_pipeline = create_stroke_pipeline(
224 &renderer,
225 &projection_bind_group_layout,
226 descriptor.surface_format,
227 descriptor.depth_format,
228 &descriptor.name,
229 );
230
231 let fill_instances = InstanceBuffer::new(
233 context.device(),
234 Some(&format!("{} Fill Instances", descriptor.name)),
235 256,
236 );
237 let stroke_instances = InstanceBuffer::new(
238 context.device(),
239 Some(&format!("{} Stroke Instances", descriptor.name)),
240 256,
241 );
242
243 Self {
244 context,
245 renderer,
246 descriptor,
247 fill_pipeline,
248 stroke_pipeline,
249 projection_bind_group_layout,
250 projection_bind_group,
251 projection_buffer,
252 tessellator: Tessellator::new(),
253 draw_commands: Vec::new(),
254 fill_vertex_buffer: None,
255 fill_index_buffer: None,
256 fill_instances,
257 stroke_vertex_buffer: None,
258 stroke_index_buffer: None,
259 stroke_instances,
260 }
261 }
262
263 pub fn descriptor(&self) -> &GeometryRendererDescriptor {
265 &self.descriptor
266 }
267
268 pub fn reconfigure(&mut self, descriptor: GeometryRendererDescriptor) {
278 if self.descriptor.surface_format == descriptor.surface_format
280 && self.descriptor.depth_format == descriptor.depth_format
281 {
282 self.descriptor.name = descriptor.name;
284 return;
285 }
286
287 self.descriptor = descriptor;
288
289 self.fill_pipeline = create_fill_pipeline(
291 &self.renderer,
292 &self.projection_bind_group_layout,
293 self.descriptor.surface_format,
294 self.descriptor.depth_format,
295 &self.descriptor.name,
296 );
297 self.stroke_pipeline = create_stroke_pipeline(
298 &self.renderer,
299 &self.projection_bind_group_layout,
300 self.descriptor.surface_format,
301 self.descriptor.depth_format,
302 &self.descriptor.name,
303 );
304 }
305
306 pub fn reconfigure_from_window(&mut self, window: &RenderWindow) {
313 self.reconfigure(
314 GeometryRendererDescriptor::from_window(window).with_name(self.descriptor.name.clone()),
315 );
316 }
317
318 pub fn set_tolerance(&mut self, tolerance: f32) {
320 self.tessellator.tolerance = tolerance;
321 }
322
323 pub fn clear(&mut self) {
325 self.draw_commands.clear();
326 }
327
328 pub fn draw_shape(&mut self, shape: &Shape, style: &Style) {
330 let path = shape.to_path();
331 self.draw_path(&path, style);
332 }
333
334 pub fn draw_path(&mut self, path: &Path, style: &Style) {
336 if let Some(fill) = &style.fill
338 && let Some(color) = fill.effective_color()
339 {
340 let mesh = self.tessellator.tessellate_fill(path, fill.rule);
341 if !mesh.is_empty() {
342 self.draw_commands.push(DrawCommand::Fill {
343 mesh,
344 color,
345 offset: style.transform.translation(),
346 });
347 }
348 }
349
350 if let Some(stroke) = &style.stroke
352 && stroke.is_visible()
353 && let Some(color) = stroke.effective_color()
354 {
355 let mesh = self.tessellator.tessellate_stroke(path, stroke);
356 if !mesh.is_empty() {
357 self.draw_commands.push(DrawCommand::Stroke {
358 mesh,
359 color,
360 width: stroke.width,
361 offset: style.transform.translation(),
362 });
363 }
364 }
365 }
366
367 pub fn draw_rect(&mut self, position: Vec2, size: Vec2, color: Color) {
369 let mesh = self.tessellator.tessellate_rect_fill(position, size);
370 self.draw_commands.push(DrawCommand::Fill {
371 mesh,
372 color,
373 offset: Vec2::ZERO,
374 });
375 }
376
377 pub fn draw_circle(&mut self, center: Vec2, radius: f32, color: Color) {
379 profile_scope!("draw_circle");
380 let shape = Shape::circle(center, radius);
381 let style = Style::fill_color(color);
382 self.draw_shape(&shape, &style);
383 }
384
385 pub fn draw_line(&mut self, start: Vec2, end: Vec2, width: f32, color: Color) {
387 profile_scope!("draw_line");
388 let mesh = self.tessellator.tessellate_line(start, end, width);
389 self.draw_commands.push(DrawCommand::Fill {
390 mesh,
391 color,
392 offset: Vec2::ZERO,
393 });
394 }
395
396 pub fn draw_rect_stroke(&mut self, position: Vec2, size: Vec2, stroke: &Stroke) {
398 let shape = Shape::rect(position, size);
399 let style = Style::new().with_stroke(stroke.clone());
400 self.draw_shape(&shape, &style);
401 }
402
403 pub fn draw_circle_stroke(&mut self, center: Vec2, radius: f32, stroke: &Stroke) {
405 let shape = Shape::circle(center, radius);
406 let style = Style::new().with_stroke(stroke.clone());
407 self.draw_shape(&shape, &style);
408 }
409
410 pub fn draw_shape_fill(&mut self, shape: &Shape, color: Color) {
412 let style = Style::fill_color(color);
413 self.draw_shape(shape, &style);
414 }
415
416 pub fn draw_shape_stroke(&mut self, shape: &Shape, stroke: &Stroke) {
418 let style = Style::new().with_stroke(stroke.clone());
419 self.draw_shape(shape, &style);
420 }
421
422 pub fn draw_path_fill(&mut self, path: &Path, color: Color, fill_rule: FillRule) {
424 let mesh = self.tessellator.tessellate_fill(path, fill_rule);
425 if !mesh.is_empty() {
426 self.draw_commands.push(DrawCommand::Fill {
427 mesh,
428 color,
429 offset: Vec2::ZERO,
430 });
431 }
432 }
433
434 pub fn draw_path_stroke(&mut self, path: &Path, stroke: &Stroke) {
436 profile_scope!("draw_path_stroke");
437 if stroke.is_visible()
438 && let Some(color) = stroke.effective_color()
439 {
440 let mesh = self.tessellator.tessellate_stroke(path, stroke);
441 if !mesh.is_empty() {
442 self.draw_commands.push(DrawCommand::Stroke {
443 mesh,
444 color,
445 width: stroke.width,
446 offset: Vec2::ZERO,
447 });
448 }
449 }
450 }
451
452 pub fn set_scissor(&mut self, scissor: ScissorRect) {
457 self.draw_commands.push(DrawCommand::SetScissor(scissor));
458 }
459
460 pub fn reset_scissor(&mut self) {
462 self.draw_commands.push(DrawCommand::ResetScissor);
463 }
464
465 pub fn render(&mut self, pass: &mut wgpu::RenderPass, viewport: Viewport) {
467 profile_scope!("geometry_render_total");
468
469 if self.draw_commands.is_empty() {
470 return;
471 }
472
473 let num_commands = self.draw_commands.len();
474 tracing::trace!("Rendering {} draw commands", num_commands);
475
476 let logical_size = viewport.to_logical();
478 let physical_size = viewport.size; let projection = ProjectionUniform::orthographic(logical_size.width, logical_size.height);
480 self.renderer.queue().write_buffer(
481 &self.projection_buffer,
482 0,
483 bytemuck::cast_slice(&[projection]),
484 );
485
486 let scale = viewport.scale_factor.0 as f32;
488
489 let mut fill_vertices: Vec<FillVertex> = Vec::new();
491 let mut fill_indices: Vec<u32> = Vec::new();
492 let mut fill_instance_data: Vec<FillInstance> = Vec::new();
493
494 let mut stroke_vertices: Vec<StrokeVertex> = Vec::new();
495 let mut stroke_indices: Vec<u32> = Vec::new();
496 let mut stroke_instance_data: Vec<StrokeInstance> = Vec::new();
497
498 #[derive(Debug)]
500 enum RenderOp {
501 SetScissor(u32, u32, u32, u32), ResetScissor,
503 DrawFill {
504 index_start: u32,
505 index_count: u32,
506 instance_idx: u32,
507 },
508 DrawStroke {
509 index_start: u32,
510 index_count: u32,
511 instance_idx: u32,
512 },
513 }
514
515 let mut ops: Vec<RenderOp> = Vec::new();
516
517 profile_scope!("collect_geometry");
518 for cmd in &self.draw_commands {
519 match cmd {
520 DrawCommand::Fill {
521 mesh,
522 color,
523 offset,
524 } => {
525 let vertex_offset = fill_vertices.len() as u32;
526 let index_start = fill_indices.len() as u32;
527
528 fill_vertices.extend_from_slice(&mesh.vertices);
529 fill_indices.extend(mesh.indices.iter().map(|i| i + vertex_offset));
530
531 let instance_idx = fill_instance_data.len() as u32;
532 fill_instance_data.push(FillInstance::new(
533 offset.x,
534 offset.y,
535 [color.r, color.g, color.b, color.a],
536 ));
537
538 ops.push(RenderOp::DrawFill {
539 index_start,
540 index_count: mesh.indices.len() as u32,
541 instance_idx,
542 });
543 }
544 DrawCommand::Stroke {
545 mesh,
546 color,
547 width,
548 offset,
549 } => {
550 let vertex_offset = stroke_vertices.len() as u32;
551 let index_start = stroke_indices.len() as u32;
552
553 stroke_vertices.extend_from_slice(&mesh.vertices);
554 stroke_indices.extend(mesh.indices.iter().map(|i| i + vertex_offset));
555
556 let instance_idx = stroke_instance_data.len() as u32;
557 stroke_instance_data.push(StrokeInstance::new(
558 offset.x,
559 offset.y,
560 [color.r, color.g, color.b, color.a],
561 *width,
562 ));
563
564 ops.push(RenderOp::DrawStroke {
565 index_start,
566 index_count: mesh.indices.len() as u32,
567 instance_idx,
568 });
569 }
570 DrawCommand::SetScissor(scissor) => {
571 let x = (scissor.x as f32 * scale) as u32;
573 let y = (scissor.y as f32 * scale) as u32;
574 let w = (scissor.width as f32 * scale) as u32;
575 let h = (scissor.height as f32 * scale) as u32;
576 ops.push(RenderOp::SetScissor(x, y, w, h));
577 }
578 DrawCommand::ResetScissor => {
579 ops.push(RenderOp::ResetScissor);
580 }
581 }
582 }
583
584 {
586 profile_scope!("create_fill_buffers");
587 if !fill_vertices.is_empty() {
588 self.fill_vertex_buffer = Some(self.context.device().create_buffer_init(
589 &wgpu::util::BufferInitDescriptor {
590 label: Some("Fill Vertex Buffer"),
591 contents: bytemuck::cast_slice(&fill_vertices),
592 usage: wgpu::BufferUsages::VERTEX,
593 },
594 ));
595 self.fill_index_buffer = Some(self.context.device().create_buffer_init(
596 &wgpu::util::BufferInitDescriptor {
597 label: Some("Fill Index Buffer"),
598 contents: bytemuck::cast_slice(&fill_indices),
599 usage: wgpu::BufferUsages::INDEX,
600 },
601 ));
602 self.fill_instances
603 .set_instances(self.context.device(), fill_instance_data);
604 self.fill_instances.upload_dirty(self.renderer.queue());
605 }
606 }
607
608 {
610 profile_scope!("create_stroke_buffers");
611 if !stroke_vertices.is_empty() {
612 self.stroke_vertex_buffer = Some(self.context.device().create_buffer_init(
613 &wgpu::util::BufferInitDescriptor {
614 label: Some("Stroke Vertex Buffer"),
615 contents: bytemuck::cast_slice(&stroke_vertices),
616 usage: wgpu::BufferUsages::VERTEX,
617 },
618 ));
619 self.stroke_index_buffer = Some(self.context.device().create_buffer_init(
620 &wgpu::util::BufferInitDescriptor {
621 label: Some("Stroke Index Buffer"),
622 contents: bytemuck::cast_slice(&stroke_indices),
623 usage: wgpu::BufferUsages::INDEX,
624 },
625 ));
626 self.stroke_instances
627 .set_instances(self.context.device(), stroke_instance_data);
628 self.stroke_instances.upload_dirty(self.renderer.queue());
629 }
630 }
631
632 let mut fill_pipeline_bound = false;
634 let mut stroke_pipeline_bound = false;
635
636 profile_scope!("execute_draw_ops");
638 for op in ops {
639 match op {
640 RenderOp::SetScissor(x, y, w, h) => {
641 pass.set_scissor_rect(x, y, w, h);
642 }
643 RenderOp::ResetScissor => {
644 pass.set_scissor_rect(
645 0,
646 0,
647 physical_size.width as u32,
648 physical_size.height as u32,
649 );
650 }
651 RenderOp::DrawFill {
652 index_start,
653 index_count,
654 instance_idx,
655 } => {
656 if let (Some(vbo), Some(ibo)) =
657 (&self.fill_vertex_buffer, &self.fill_index_buffer)
658 {
659 if !fill_pipeline_bound {
660 pass.set_pipeline(&self.fill_pipeline);
661 pass.set_bind_group(0, &self.projection_bind_group, &[]);
662 pass.set_vertex_buffer(0, vbo.slice(..));
663 pass.set_vertex_buffer(1, self.fill_instances.buffer().slice(..));
664 pass.set_index_buffer(ibo.slice(..), wgpu::IndexFormat::Uint32);
665 fill_pipeline_bound = true;
666 stroke_pipeline_bound = false;
667 }
668 pass.draw_indexed(
669 index_start..(index_start + index_count),
670 0,
671 instance_idx..(instance_idx + 1),
672 );
673 }
674 }
675 RenderOp::DrawStroke {
676 index_start,
677 index_count,
678 instance_idx,
679 } => {
680 if let (Some(vbo), Some(ibo)) =
681 (&self.stroke_vertex_buffer, &self.stroke_index_buffer)
682 {
683 if !stroke_pipeline_bound {
684 pass.set_pipeline(&self.stroke_pipeline);
685 pass.set_bind_group(0, &self.projection_bind_group, &[]);
686 pass.set_vertex_buffer(0, vbo.slice(..));
687 pass.set_vertex_buffer(1, self.stroke_instances.buffer().slice(..));
688 pass.set_index_buffer(ibo.slice(..), wgpu::IndexFormat::Uint32);
689 stroke_pipeline_bound = true;
690 fill_pipeline_bound = false;
691 }
692 pass.draw_indexed(
693 index_start..(index_start + index_count),
694 0,
695 instance_idx..(instance_idx + 1),
696 );
697 }
698 }
699 }
700 }
701
702 self.draw_commands.clear();
704 }
705}