Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-renderer/src/rendering/wgpu/renderer/frame.rs
1use crate::rendering::wgpu::renderer::{glyph, image, rect, WGPUContext};
2 
3use crate::rendering::wgpu::Resources;
4use crate::scene::Layer;
5use crate::Scene;
6 
7use pathfinder_geometry::rect::RectF;
8use pathfinder_geometry::vector::Vector2F;
9use wgpu::{CommandEncoder, RenderPass, SurfaceTexture};
10 
11#[derive(Default)]
12struct PerFrameState {
13 rect: rect::PerFrameState,
14 glyph: glyph::PerFrameState,
15 image: image::PerFrameState,
16}
17 
18/// Struct responsible for rendering a frame by issuing draw calls.
19pub(super) struct Frame<'a> {
20 scene: &'a Scene,
21 layer_state: Vec<LayerState<'a>>,
22 per_frame_state: PerFrameState,
23 rect_pipeline: &'a rect::Pipeline,
24 glyph_pipeline: &'a mut glyph::Pipeline,
25 image_pipeline: &'a mut image::Pipeline,
26}
27 
28impl<'a> Frame<'a> {
29 pub(super) fn new(
30 scene: &'a Scene,
31 ctx: &'a mut WGPUContext<'a>,
32 rect_pipeline: &'a rect::Pipeline,
33 glyph_pipeline: &'a mut glyph::Pipeline,
34 image_pipeline: &'a mut image::Pipeline,
35 ) -> Self {
36 glyph_pipeline.update_config(&scene.rendering_config().glyphs);
37 
38 let mut layer_state = vec![];
39 let mut per_frame_state = PerFrameState::default();
40 
41 for layer in scene.layers() {
42 let rect_layer_state =
43 rect_pipeline.initialize_for_layer(layer, scene, &mut per_frame_state.rect);
44 let glyph_layer_state =
45 glyph_pipeline.initialize_for_layer(layer, scene, &mut per_frame_state.glyph, ctx);
46 let image_layer_state =
47 image_pipeline.initialize_for_layer(layer, scene, &mut per_frame_state.image, ctx);
48 layer_state.push(LayerState {
49 layer,
50 rect_layer_state,
51 glyph_layer_state,
52 image_layer_state,
53 });
54 }
55 
56 rect::Pipeline::finalize_per_frame_state(
57 &mut per_frame_state.rect,
58 &ctx.resources.device,
59 &ctx.resources.device_lost,
60 );
61 glyph::Pipeline::finalize_per_frame_state(
62 &mut per_frame_state.glyph,
63 &ctx.resources.device,
64 &ctx.resources.device_lost,
65 );
66 image::Pipeline::finalize_per_frame_state(
67 &mut per_frame_state.image,
68 &ctx.resources.device,
69 &ctx.resources.device_lost,
70 );
71 
72 Self {
73 scene,
74 layer_state,
75 per_frame_state,
76 rect_pipeline,
77 glyph_pipeline,
78 image_pipeline,
79 }
80 }
81 
82 /// Encodes draw calls into the [`wgpu::CommandEncoder`] to render the [`Scene`]. Callers are
83 /// responsible for finishing the [`wgpu::CommandEncoder`] and actually presenting the current
84 /// drawable on the screen.
85 pub(super) fn draw(
86 self,
87 resources: &Resources,
88 encoder: &mut CommandEncoder,
89 surface_texture: &SurfaceTexture,
90 ) {
91 let surface_size = Vector2F::new(
92 surface_texture.texture.width() as f32,
93 surface_texture.texture.height() as f32,
94 );
95 
96 let view = surface_texture
97 .texture
98 .create_view(&wgpu::TextureViewDescriptor {
99 format: Some(surface_texture.texture.format()),
100 ..Default::default()
101 });
102 
103 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
104 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
105 view: &view,
106 depth_slice: None,
107 resolve_target: None,
108 ops: wgpu::Operations {
109 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
110 store: wgpu::StoreOp::Store,
111 },
112 })],
113 ..Default::default()
114 });
115 resources.configure_render_pass(&mut render_pass, surface_size);
116 
117 let device_bounds = RectF::new(Vector2F::zero(), surface_size);
118 
119 for layer_state in &self.layer_state {
120 if let Some(bounds) = layer_state.layer.clip_bounds {
121 // Make sure the scissor rect doesn't extend beyond the boundaries
122 // of the window.
123 let bounds = (bounds * self.scene.scale_factor()).intersection(device_bounds);
124 let Some(intersection) = bounds else {
125 // The layer's clip bounds don't intersect the window bounds
126 // at all; we can skip drawing anything in this layer.
127 continue;
128 };
129 
130 Self::set_scissor_rect(&mut render_pass, intersection);
131 } else {
132 Self::set_scissor_rect(&mut render_pass, device_bounds);
133 }
134 
135 if let Some(rect_layer_state) = &layer_state.rect_layer_state {
136 self.rect_pipeline.draw(
137 &mut render_pass,
138 rect_layer_state,
139 &self.per_frame_state.rect,
140 );
141 }
142 
143 if let Some(image_layer_state) = &layer_state.image_layer_state {
144 self.image_pipeline.draw(
145 &mut render_pass,
146 image_layer_state,
147 &self.per_frame_state.image,
148 );
149 }
150 
151 if let Some(glyph_layer_state) = &layer_state.glyph_layer_state {
152 self.glyph_pipeline.draw(
153 &mut render_pass,
154 glyph_layer_state,
155 &self.per_frame_state.glyph,
156 );
157 }
158 }
159 }
160 
161 fn set_scissor_rect(render_pass: &mut RenderPass<'_>, scissor_rect_bounds: RectF) {
162 // Round the corners independently and derive width/height from those. Rounding origin and
163 // size independently can produce a rect that extends beyond the surface when the origin
164 // rounds up and the size also rounds up.
165 let origin_x = scissor_rect_bounds.origin_x().round() as u32;
166 let origin_y = scissor_rect_bounds.origin_y().round() as u32;
167 let max_x = scissor_rect_bounds.max_x().round() as u32;
168 let max_y = scissor_rect_bounds.max_y().round() as u32;
169 let width = max_x.saturating_sub(origin_x);
170 let height = max_y.saturating_sub(origin_y);
171 
172 // wgpu runtime assertions will fail if a scissor rect is set with a 0 width or height. See
173 // https://github.com/gfx-rs/wgpu/issues/1750
174 if height != 0 && width != 0 {
175 render_pass.set_scissor_rect(origin_x, origin_y, width, height);
176 }
177 }
178}
179 
180impl Drop for Frame<'_> {
181 fn drop(&mut self) {
182 // Let the image pipeline know that we've finished the frame so it can
183 // perform cache cleanup.
184 self.image_pipeline.end_frame();
185 }
186}
187 
188/// State for rendering a given [`Layer`] onto the screen.
189struct LayerState<'a> {
190 layer: &'a Layer,
191 rect_layer_state: Option<rect::LayerState>,
192 glyph_layer_state: Option<glyph::LayerState>,
193 image_layer_state: Option<image::LayerState>,
194}
195