Another project
1pub mod camera;
2pub mod diff;
3pub mod gpu;
4pub mod pick;
5pub mod pipelines;
6pub mod preview;
7pub mod scene;
8pub mod snapshot;
9pub mod surface;
10
11pub use camera::{Camera2, GridSpacing, PixelsPerMm, ViewportExtent, ViewportPx};
12pub use diff::{PixelDiff, PixelDiffError, PixelDiffReport, PixelDiffThreshold, PixelMismatch};
13pub use gpu::{BackendTag, Capabilities, Gpu, OffscreenContext};
14pub use pick::{EntityKindTag, PickId, PickIdError, PickIndex, PickQuery, PickedItem, Picker};
15pub use pipelines::{
16 ArcPipeline, ChromeInstance, ChromePipeline, ChromeTextPipeline, GlyphPipeline, GridPipeline,
17 LinesPipeline, SdfGlyphInstance, TextPipeline,
18};
19pub use preview::{PreviewArc, PreviewCircle, SketchPreview};
20pub use scene::{
21 RelationGlyphKind, SceneArc, SceneCircle, SceneDimension, SceneLine, ScenePoint,
22 SceneRelationGlyph, SketchScene,
23};
24pub use snapshot::{
25 ClearColor, GlyphStyle, GridStyle, SnapshotFrame, StrokeStyle, Style, TextStyle, decode_png,
26 encode_png,
27};
28pub use surface::{SurfaceContext, SurfaceError};
29
30#[derive(Copy, Clone)]
31pub struct RenderTargets<'a> {
32 pub color: &'a wgpu::TextureView,
33 pub pick: &'a wgpu::TextureView,
34}
35
36impl<'a> RenderTargets<'a> {
37 #[must_use]
38 pub const fn new(color: &'a wgpu::TextureView, pick: &'a wgpu::TextureView) -> Self {
39 Self { color, pick }
40 }
41}
42
43#[derive(Debug, thiserror::Error)]
44pub enum RenderError {
45 #[error("no wgpu adapter matched the offscreen request: {0}")]
46 NoAdapter(#[from] wgpu::RequestAdapterError),
47 #[error("wgpu device request failed: {0}")]
48 Device(#[from] wgpu::RequestDeviceError),
49 #[error("wgpu poll failed: {0}")]
50 Poll(wgpu::PollError),
51 #[error("wgpu buffer map failed: {0}")]
52 Map(wgpu::BufferAsyncError),
53 #[error("wgpu buffer map callback did not fire after poll")]
54 MapMissing,
55 #[error("png encode failed: {0}")]
56 PngEncode(#[from] png::EncodingError),
57 #[error("png decode failed: {0}")]
58 PngDecode(#[from] png::DecodingError),
59 #[error("png format unsupported: color={color_type:?}, depth={bit_depth:?}, require rgba8")]
60 PngFormat {
61 color_type: png::ColorType,
62 bit_depth: png::BitDepth,
63 },
64 #[error("viewport dimension is zero")]
65 ZeroExtent,
66 #[error("pick id construction failed: {0}")]
67 PickId(#[from] PickIdError),
68 #[error("pick query {query} outside viewport {extent}")]
69 PickOutOfBounds {
70 query: PickQuery,
71 extent: ViewportExtent,
72 },
73}
74
75pub type Result<T, E = RenderError> = core::result::Result<T, E>;
76
77#[derive(Debug)]
78pub struct SketchRenderer {
79 grid: GridPipeline,
80 arcs: ArcPipeline,
81 lines: LinesPipeline,
82 glyphs: GlyphPipeline,
83 text: TextPipeline,
84}
85
86impl SketchRenderer {
87 #[must_use]
88 pub fn new(gpu: &Gpu, color_format: wgpu::TextureFormat) -> Self {
89 Self {
90 grid: GridPipeline::new(gpu, color_format),
91 arcs: ArcPipeline::new(gpu, color_format),
92 lines: LinesPipeline::new(gpu, color_format),
93 glyphs: GlyphPipeline::new(gpu, color_format),
94 text: TextPipeline::new(gpu, color_format),
95 }
96 }
97
98 pub fn prepare(&mut self, scene: &SketchScene, style: &Style) {
99 self.text.prepare(scene, style);
100 }
101
102 #[must_use]
103 pub fn text_cache_len(&self) -> usize {
104 self.text.cache_len()
105 }
106
107 pub fn encode_passes(
108 &self,
109 encoder: &mut wgpu::CommandEncoder,
110 targets: RenderTargets<'_>,
111 scene: &SketchScene,
112 preview: &SketchPreview,
113 camera: Camera2,
114 style: &Style,
115 ) {
116 self.grid.draw(encoder, targets.color, camera, style);
117 gpu::clear_pick_attachment(encoder, targets.pick);
118 self.arcs
119 .draw(encoder, targets, camera, style, scene, preview);
120 self.lines
121 .draw(encoder, targets, camera, style, scene, preview);
122 self.glyphs.draw(encoder, targets, camera, style, scene);
123 self.text.draw(encoder, targets, camera, style, scene);
124 }
125
126 pub fn render(
127 &mut self,
128 ctx: &OffscreenContext,
129 scene: &SketchScene,
130 camera: Camera2,
131 style: &Style,
132 ) -> Result<SnapshotFrame> {
133 self.render_with_preview(ctx, scene, &SketchPreview::empty(), camera, style)
134 }
135
136 pub fn render_with_preview(
137 &mut self,
138 ctx: &OffscreenContext,
139 scene: &SketchScene,
140 preview: &SketchPreview,
141 camera: Camera2,
142 style: &Style,
143 ) -> Result<SnapshotFrame> {
144 debug_assert_eq!(
145 camera.extent(),
146 ctx.extent(),
147 "camera extent must match offscreen context extent",
148 );
149 self.prepare(scene, style);
150 ctx.render(|encoder, color_view, pick_view| {
151 self.encode_passes(
152 encoder,
153 RenderTargets::new(color_view, pick_view),
154 scene,
155 preview,
156 camera,
157 style,
158 );
159 })
160 }
161}