1use super::{
12 Engine, FrameCompleteness, FrameDegradation, FrameMetrics, FrameReport, FrameStatus,
13 MotionBlur, QualityTier, RenderMode, TemporalOptions,
14};
15use crate::error::RenderError;
16use crate::graph::{DisplayEncoding, PassContext, ResourceTable, TransientPool, plan_aliases};
17use crate::passes::FrameBindings;
18use molgfx_core::Scene;
19use molgfx_gpu::Queue as _;
20use molgfx_gpu::{Device, Surface as _, SurfaceError, SurfaceFrame as _};
21use molgfx_math::Camera;
22use web_time::Instant;
25
26impl<D: Device> Engine<D> {
27 fn sync_scene(&mut self, scene: &Scene) -> Result<bool, RenderError> {
32 let scene_changed = self.scene_gpu.sync(crate::scene_gpu::SceneSync {
33 device: &self.device,
34 queue: &self.queue,
35 scene,
36 quality: self.tier() >= QualityTier::Standard,
37 extent: [self.width, self.height],
38 ray_query_layout: self.passes.ambient_occlusion.ray_query_layout(),
39 derived_cache: &mut self.derived_cache,
40 derived_frame: self.derived_frame,
41 })?;
42 self.chunk_residency.sync_scene(
43 &mut self.scene_gpu,
44 &self.device,
45 &self.queue,
46 &mut self.derived_cache,
47 self.derived_frame,
48 )?;
49 self.derived_frame = self.derived_frame.wrapping_add(1);
50 if scene_changed || self.chunk_residency.metrics().uploads.active_tickets != 0 {
51 self.temporal.invalidate_convergence();
52 }
53 Ok(scene_changed)
54 }
55
56 pub fn render(&mut self, scene: &Scene, camera: &Camera) -> Result<FrameReport, RenderError> {
67 let frame_start = Instant::now();
68 self.sync_quality_tier();
69 self.device.check_errors()?;
70 self.chunk_residency.begin_epoch();
71 self.scene_gpu.begin_frame();
72 let scene_changed = self.sync_scene(scene)?;
74
75 let rebuild = self.rebuild_pool_if_needed()?;
77 self.scene_gpu
81 .settle_specializations(&self.device, scene, &self.passes);
82 let camera_changed = self.temporal.camera_changed(camera);
83 let identity = scene.cache_identity();
84 let scene_reset = self.temporal_scene_identity.replace(identity) != Some(identity);
85 let cinematic = self.tier() >= QualityTier::Standard;
86 let optics = self.resolve_optics(scene, camera)?;
87 let shadow =
88 self.shadow_bound
89 .fit(scene, camera, self.resolved_plan.lighting(), scene_changed);
90 let uniforms = self.temporal.prepare(
91 camera,
92 &TemporalOptions {
93 extent: [self.width, self.height],
94 reset: scene_reset
95 || rebuild
96 || (self.mode == RenderMode::Cinematic && camera_changed),
97 quality: cinematic,
98 publication: false,
99 illustration: self.resolved_plan.illustration(),
100 optics,
101 motion_blur: self
102 .resolved_plan
103 .motion_blur()
104 .map_or([0.0; 4], MotionBlur::packed),
105 atmosphere: self
106 .resolved_plan
107 .packed_presentation(self.scene_gpu.has_translucency()),
108 lighting: self.resolved_plan.packed_lighting(),
109 shadow_view: shadow.view,
110 shadow_projection: shadow.projection,
111 shadow_view_proj: shadow.view_projection,
112 },
113 );
114 self.scene_gpu
115 .write_frame_uniforms(&self.queue, &uniforms)?;
116 let frame = self.acquire_surface_frame()?;
117 let Some(frame) = frame else {
118 return Ok(self.frame_report(FrameStatus::Skipped));
120 };
121 let temporal_write = self.temporal.write_index();
122
123 let mut encoder = self.device.create_command_encoder();
125 self.scene_gpu
126 .settle_specializations(&self.device, scene, &self.passes);
127 self.record_scene_compute(&mut encoder, cinematic);
128 let Some(pool) = &self.pool else {
129 return Ok(self.frame_report(FrameStatus::Skipped));
130 };
131 {
132 let table = ResourceTable {
133 pool,
134 swapchain: frame.view(),
135 };
136 for &index in &self.order {
137 let Some(node) = self.pass_nodes.get(index) else {
138 continue;
139 };
140 let mut ctx = PassContext {
141 encoder: &mut encoder,
142 resources: &table,
143 passes: &self.passes,
144 bindings: self.bindings.as_ref(),
145 scene: &self.scene_gpu,
146 timestamps: None,
147 temporal_write,
148 quality: cinematic,
149 display_encoding: self.display_encoding(),
150 };
151 (node.record)(&mut ctx);
152 }
153 }
154
155 self.queue.submit(encoder);
157 self.device.check_errors()?;
158 frame.present();
159 let report = self.frame_report(FrameStatus::Presented);
162 let frame_time = frame_start.elapsed();
168 let elapsed = frame_time
169 .as_secs()
170 .saturating_mul(1_000_000_000)
171 .saturating_add(u64::from(frame_time.subsec_nanos()));
172 self.adaptive.observe(elapsed);
173 Ok(report)
174 }
175
176 fn record_scene_compute(&mut self, encoder: &mut D::CommandEncoder, cinematic: bool) {
177 self.passes
178 .cull
179 .record_attribute_timelines(&self.scene_gpu, encoder);
180 self.passes
181 .cull
182 .record_instance_timelines(&self.scene_gpu, encoder);
183 let point_coordinates_changed = self
184 .passes
185 .cull
186 .record_point_timelines(&self.scene_gpu, encoder);
187 self.scene_gpu
188 .record_particle_motion(encoder, &self.passes.particle_motion);
189 let structure_coordinates_changed =
190 self.scene_gpu
191 .record_trajectories(encoder, &self.passes.trajectory, None);
192 let paged_coordinates_changed = self
193 .passes
194 .cull
195 .record_paged_trajectories(&self.scene_gpu, encoder);
196 self.scene_gpu.record_dynamic_relations(
197 encoder,
198 &self.passes.relation_resolve,
199 structure_coordinates_changed || paged_coordinates_changed || point_coordinates_changed,
200 );
201 self.scene_gpu
202 .record_occupancies(encoder, &self.passes.occupancy);
203 self.scene_gpu.record_surface_fields(
204 encoder,
205 &self.passes.surface_field,
206 &self.passes.surface_components,
207 );
208 self.scene_gpu.record_quality_hardware(encoder, cinematic);
209 }
210
211 fn frame_report(&self, status: FrameStatus) -> FrameReport {
212 let residency = self.chunk_residency.metrics();
213 let derived = self.derived_cache.usage();
214 let physical = self.device.resource_memory();
215 let pending = residency.uploads.active_tickets;
216 FrameReport {
217 status,
218 completeness: if pending == 0 {
219 FrameCompleteness::Complete
220 } else {
221 FrameCompleteness::Progressive {
222 pending_chunks: pending,
223 }
224 },
225 degradation: FrameDegradation::streaming_proxy(
226 self.mode == RenderMode::Realtime && pending > 0,
227 ),
228 metrics: FrameMetrics {
229 tracked_chunks: residency.tracked_chunks,
230 upload_in_flight_bytes: residency.uploads.in_flight_bytes,
231 derived_cache_gpu_bytes: derived.gpu_bytes,
232 derived_cache_peak_gpu_bytes: derived.peak_gpu_bytes,
233 physical_buffer_bytes: physical.buffer_bytes,
234 physical_texture_bytes: physical.texture_bytes,
235 physical_total_bytes: physical.total_bytes(),
236 physical_peak_bytes: physical.peak_bytes,
237 },
238 needs_another_frame: status == FrameStatus::Skipped
239 || pending != 0
240 || self
241 .temporal
242 .needs_another_frame(self.tier().temporal_samples()),
243 quality_tier: self.tier(),
244 }
245 }
246
247 pub(super) fn display_encoding(&self) -> DisplayEncoding {
252 let display = self.resolved_plan.display();
253 DisplayEncoding {
254 gamut: display.gamut,
255 transfer: display.transfer,
256 }
257 }
258
259 pub(super) fn rebuild_pool_if_needed(&mut self) -> Result<bool, RenderError> {
260 let rebuild = self
261 .pool
262 .as_ref()
263 .is_none_or(|pool| !pool.matches(self.width, self.height));
264 if !rebuild {
265 return Ok(false);
266 }
267 let plan = plan_aliases(&self.resources, &self.pass_nodes, &self.order);
268 self.bindings = None;
271 self.pool = None;
272 self.temporal.reset();
273 self.pool = Some(TransientPool::build(
274 &self.device,
275 &self.resources,
276 plan,
277 self.width,
278 self.height,
279 )?);
280 self.bindings = self
281 .pool
282 .as_ref()
283 .and_then(|pool| FrameBindings::new(&self.device, pool, &self.passes));
284 Ok(true)
285 }
286
287 fn acquire_surface_frame(
288 &mut self,
289 ) -> Result<Option<<D::Surface as molgfx_gpu::Surface<D>>::Frame>, RenderError> {
290 let Some(surface) = &mut self.surface else {
291 return Ok(None);
292 };
293 match surface.acquire() {
294 Ok(frame) => Ok(Some(frame)),
295 Err(SurfaceError::Lost | SurfaceError::Outdated) => {
296 surface.configure(
297 &self.device,
298 &molgfx_gpu::SurfaceConfig {
299 width: self.width,
300 height: self.height,
301 format: self.target_format,
302 },
303 );
304 Ok(None)
305 }
306 Err(SurfaceError::Timeout) => Ok(None),
307 Err(error) => Err(RenderError::Gpu(error.into())),
308 }
309 }
310}