1use super::graph_setup::{realtime_nodes, realtime_resources};
4use super::{
5 AdaptiveQuality, DerivedCache, EngineConfig, FocusTracker, GpuProfiler, PassRegistry, Picker,
6 RenderMode, RenderProfile, ResolvedRenderPlan, ShadowBoundCache, TemporalState,
7 chunk_residency::ChunkGpuResidency,
8};
9use crate::error::RenderError;
10use crate::graph::{self, PassNode, ResourceDesc, TransientPool};
11use crate::passes::{
12 AmbientOcclusionPass, AoDenoisePass, BloomPass, BondPass, CartoonPass, CullPass,
13 DepthOfFieldPass, FrameBindings, InteractionPass, LabelPass, LightingPass, MotionBlurPass,
14 OccupancyBoundsFormat, OccupancyPass, OitCompositePass, OitPass, OverlayPass,
15 ParticleMotionPass, PointPass, PrimitivePass, RelationResolvePass, ShadowPass, SpherePass,
16 SurfaceComponentPass, SurfaceFieldPass, SurfacePass, TemporalPass, TonemapPass, TrajectoryPass,
17};
18use crate::scene_gpu::GpuScene;
19use molgfx_gpu::{Device, DeviceDesc, Opened, TextureFormat, WindowTarget};
20
21#[derive(Debug)]
24pub struct Engine<D: Device> {
25 pub(crate) device: D,
26 pub(crate) queue: D::Queue,
27 pub(crate) surface: Option<D::Surface>,
28 pub(crate) passes: PassRegistry<D>,
29 pub(crate) resources: Vec<ResourceDesc>,
30 pub(crate) pass_nodes: Vec<PassNode<D>>,
31 pub(crate) order: Vec<usize>,
32 pub(crate) pool: Option<TransientPool<D>>,
33 pub(crate) bindings: Option<FrameBindings<D>>,
34 pub(crate) scene_gpu: GpuScene<D>,
35 pub(crate) width: u32,
36 pub(crate) height: u32,
37 pub(crate) target_format: TextureFormat,
38 pub(crate) profiler: Option<GpuProfiler<D>>,
39 pub(crate) picker: Picker<D>,
40 pub(crate) temporal: TemporalState,
41 pub(crate) temporal_scene_identity: Option<u64>,
42 pub(crate) mode: RenderMode,
43 pub(crate) adaptive: AdaptiveQuality,
45 pub(crate) profile: RenderProfile,
46 pub(crate) resolved_plan: ResolvedRenderPlan,
47 pub(crate) focus_tracker: FocusTracker,
48 pub(crate) shadow_bound: ShadowBoundCache,
49 pub(crate) derived_cache: DerivedCache,
50 pub(super) derived_frame: u64,
51 pub(crate) host_working_set: molgfx_core::HostWorkingSet,
52 pub(super) chunk_residency: ChunkGpuResidency<D>,
53}
54
55fn cull_pass<D: Device>(device: &D, scene: &GpuScene<D>) -> Result<CullPass<D>, RenderError> {
56 let layouts = crate::passes::CullLayouts {
57 atoms: &scene.atom_cull_layout,
58 bonds: &scene.bond_cull_layout,
59 visuals: &scene.visual_cull_layout,
60 frame: &scene.group0_layout,
61 paged: scene.paged_chunk_layout(),
62 paged_bonds: scene.paged_bond_layout(),
63 points: &scene.generic_point_cull_layout,
64 instances: &scene.generic_instance_cull_layout,
65 instance_timeline: &scene.instance_timeline_layout,
66 attribute_timeline: &scene.attribute_timeline_layout,
67 relations: &scene.relation_cull_layout,
68 };
69 CullPass::new(device, &layouts)
70}
71
72fn realtime_passes<D: Device>(
73 device: &D,
74 target_format: TextureFormat,
75 scene: &GpuScene<D>,
76 plan: &ResolvedRenderPlan,
77) -> Result<PassRegistry<D>, RenderError> {
78 Ok(PassRegistry {
79 sphere: SpherePass::new(
80 device,
81 target_format,
82 &scene.group0_layout,
83 &scene.group2_layout,
84 scene.paged_chunk_layout(),
85 )?,
86 point: PointPass::new(
87 device,
88 &scene.group0_layout,
89 &scene.group2_layout,
90 scene.paged_chunk_layout(),
91 &scene.generic_point_render_layout,
92 )?,
93 primitive: PrimitivePass::new(
94 device,
95 &scene.group0_layout,
96 &scene.primitive_layout,
97 &scene.ligand_pose_layout,
98 &scene.generic_instance_render_layout,
99 )?,
100 surface: SurfacePass::new(device, &scene.group0_layout, &scene.group2_layout)?,
101 surface_field: SurfaceFieldPass::new(
102 device,
103 &scene.surface_field_output_layout,
104 &scene.surface_field_erosion_layout,
105 &scene.surface_field_normal_layout,
106 &scene.surface_field_input_layout,
107 )?,
108 surface_components: SurfaceComponentPass::new(device, &scene.surface_component_layout)?,
109 bond: BondPass::new(
110 device,
111 target_format,
112 &scene.group0_layout,
113 &scene.group2_layout,
114 scene.paged_bond_layout(),
115 )?,
116 cartoon: CartoonPass::new(device, &scene.group0_layout, &scene.ribbon_layout)?,
117 cull: cull_pass(device, scene)?,
118 depth_of_field: plan
119 .depth_of_field()
120 .map(|_| DepthOfFieldPass::new(device, &scene.group0_layout))
121 .transpose()?,
122 ambient_occlusion: AmbientOcclusionPass::new(
123 device,
124 &scene.group0_layout,
125 &scene.quality_layout,
126 )?,
127 ao_denoise: AoDenoisePass::new(device, &scene.group0_layout)?,
128 lighting: LightingPass::new(device, &scene.group0_layout)?,
129 shadow: ShadowPass::new(
130 device,
131 &scene.group0_layout,
132 &scene.group2_layout,
133 &scene.ribbon_layout,
134 &scene.primitive_shadow_layout,
135 &scene.ligand_pose_layout,
136 )?,
137 oit: OitPass::new(
138 device,
139 &scene.group0_layout,
140 &scene.group2_layout,
141 &scene.ribbon_layout,
142 (&scene.primitive_layout, &scene.ligand_pose_layout),
143 (
144 &scene.generic_point_render_layout,
145 &scene.generic_instance_render_layout,
146 ),
147 (&scene.volume_layout, &scene.segmentation_layout),
148 )?,
149 interaction: InteractionPass::new(device, &scene.group0_layout, &scene.interaction_layout)?,
150 relation_resolve: RelationResolvePass::new(device, &scene.relation_resolve_layout)?,
151 label: LabelPass::new(
152 device,
153 &scene.group0_layout,
154 &scene.label_declutter_layout,
155 &scene.label_render_layout,
156 )?,
157 oit_composite: OitCompositePass::new(device)?,
158 temporal: TemporalPass::new(device, &scene.group0_layout)?,
159 bloom: plan
160 .bloom()
161 .map(|_| BloomPass::new(device, &scene.group0_layout))
162 .transpose()?,
163 motion_blur: plan
164 .motion_blur()
165 .map(|_| MotionBlurPass::new(device, &scene.group0_layout))
166 .transpose()?,
167 tonemap: TonemapPass::new(device, target_format, &scene.group0_layout)?,
168 overlay: OverlayPass::new(
169 device,
170 target_format,
171 &scene.group0_layout,
172 &scene.overlay_layout,
173 )?,
174 trajectory: TrajectoryPass::new(device, &scene.trajectory_layout)?,
175 occupancy: None,
176 particle_motion: ParticleMotionPass::new(device, &scene.primitive_motion_layout)?,
177 })
178}
179
180impl<D: Device> Engine<D> {
181 pub(super) fn ensure_occupancy(
182 &mut self,
183 scene: &molgfx_core::Scene,
184 ) -> Result<(), RenderError> {
185 if !scene.has_occupancy_stream() || self.passes.occupancy.is_some() {
186 return Ok(());
187 }
188 let format = OccupancyBoundsFormat::resolve(self.device.capabilities())?;
189 let layout = self
190 .scene_gpu
191 .ensure_occupancy_layout(&self.device, format.texture_format());
192 self.passes.occupancy = Some(OccupancyPass::new(&self.device, layout, format)?);
193 Ok(())
194 }
195 pub async fn new_async(
202 config: &EngineConfig,
203 window: Option<WindowTarget>,
204 ) -> Result<Self, RenderError> {
205 let opened = D::open_async(
206 &DeviceDesc {
207 power: config.power,
208 resource_memory_limit_bytes: config.resource_memory_limit_bytes,
209 },
210 window,
211 )
212 .await?;
213 Self::from_opened(config, opened)
214 }
215
216 #[cfg(not(target_arch = "wasm32"))]
223 pub fn new(config: &EngineConfig, window: Option<WindowTarget>) -> Result<Self, RenderError> {
224 let opened = D::open_blocking(
225 &DeviceDesc {
226 power: config.power,
227 resource_memory_limit_bytes: config.resource_memory_limit_bytes,
228 },
229 window,
230 )?;
231 Self::from_opened(config, opened)
232 }
233
234 fn from_opened(config: &EngineConfig, opened: Opened<D>) -> Result<Self, RenderError> {
235 let mut surface = opened.surface;
236 let device = opened.device;
237
238 let target_format = super::target::configure(&device, &mut surface, config);
239
240 let profile = config.profile.clone();
241 let resolved_plan = profile.resolve();
242 let scene_gpu = GpuScene::new(&device, config.residency, config.picking_page_capacity)?;
243 let passes = realtime_passes(&device, target_format, &scene_gpu, &resolved_plan)?;
244 let pass_nodes = realtime_nodes(
245 resolved_plan.depth_of_field().is_some(),
246 resolved_plan.bloom().is_some(),
247 resolved_plan.motion_blur().is_some(),
248 );
249 let resources = realtime_resources();
250 let order = graph::schedule(&pass_nodes)?;
251
252 let profiler = GpuProfiler::new(&device, target_format)?;
253 let picker = Picker::new(&device, config.picking_page_capacity)?;
254 let chunk_residency = ChunkGpuResidency::new(&device, config.residency)?;
255 Ok(Self {
256 device,
257 queue: opened.queue,
258 surface,
259 passes,
260 resources,
261 pass_nodes,
262 order,
263 pool: None,
264 bindings: None,
265 scene_gpu,
266 width: config.width,
267 height: config.height,
268 target_format,
269 profiler,
270 picker,
271 temporal: TemporalState::default(),
272 temporal_scene_identity: None,
273 mode: config.mode,
274 adaptive: AdaptiveQuality::new(config.adaptive, config.mode == RenderMode::Cinematic),
275 profile,
276 resolved_plan,
277 focus_tracker: FocusTracker::default(),
278 shadow_bound: ShadowBoundCache::default(),
279 derived_cache: DerivedCache::new(config.derived_cache),
280 derived_frame: 0,
281 host_working_set: molgfx_core::HostWorkingSet::new(config.source_budget),
282 chunk_residency,
283 })
284 }
285}