1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Building the frame's shared surface fields before any slot binds.
//!
//! A field is created while the field cache is borrowed exclusively, and the
//! binding pass only ever reads that cache. Every field the frame needs must
//! therefore exist before binding starts, which is why resolution is its own
//! pass rather than something each slot does as it binds.
//!
//! The pass also resolves the per-slot state the key is built from: the
//! selection bounds, the representation uniforms and the identity of the shared
//! records. Those are the same inputs the binding pass uses, so the key cannot
//! disagree with the uniforms its field is generated under.
use super::super::record_cache::{RecordCache, RecordSet};
use super::super::scalar_overlay;
use super::super::slots::GpuSlot;
use super::super::surface_field::FieldSync;
use super::super::uniforms::RepresentationUniforms;
use super::GpuScene;
use crate::error::RenderError;
use molgfx_core::Scene;
use molgfx_gpu::Device;
impl<D: Device> GpuScene<D> {
/// Resolves each slot's field key and builds every field the frame needs.
///
/// A key shared by four surfaces builds one field: the cache keys on
/// geometry and sampling policy, so the second surface through finds it
/// already resident and allocates nothing.
pub(super) fn prepare_surface_fields(
&mut self,
device: &D,
queue: &D::Queue,
scene: &Scene,
quality: bool,
) -> Result<(), RenderError> {
// Destructured so the slot list can be walked mutably while the shared
// tables stay borrowed immutably, exactly as the binding pass does.
let slots = &mut self.slots;
let records = &self.records;
let structures = &self.structures;
let volume_resources = &self.volume_resources;
let fallback = &self.surface_field_fallback;
let fields = &mut self.surface_fields;
for slot in slots.iter_mut() {
let Some(representation) = scene.representation(slot.key.representation) else {
continue;
};
let Some(placed) = scene.structure(slot.key.structure) else {
continue;
};
let Some(selection_handle) = representation.selection() else {
continue;
};
let Some(structure_gpu) = structures.get(slot.structure_index) else {
continue;
};
let Some(selection) = scene.selection_for(selection_handle, slot.key.structure) else {
continue;
};
let (_, _, property_revisions) =
super::properties::resolve(scene, representation, slot.key.structure);
let record_key = RecordCache::<D>::key(
scene,
placed,
slot.key.structure,
representation,
selection_handle,
structure_gpu.asset_identity(),
property_revisions,
);
let Some(set) = records.get(record_key).and_then(RecordSet::as_ref) else {
continue;
};
let (atoms, compaction, atom_count) = (set.atoms, set.compaction, set.atom_count);
let selection_bounds =
slot.selection_bounds
.resolve(placed, representation, selection)?;
let (overlay_volume, _, _) =
scalar_overlay::resolve(volume_resources, scene, representation, fallback);
let uniforms = RepresentationUniforms::for_quality(
representation,
selection_bounds,
overlay_volume,
quality,
);
// The colour column is resolved from the same arena the visual
// programs sample, so a property scheme adds no second upload path.
if let Some(handle) = representation.color.property_handle() {
slot.adopt_color_column(self.visual_properties.color_column(handle));
}
slot.surface
.resolve_key(&uniforms, representation, record_key.geometry(), atom_count);
let Some(key) = slot.surface.key() else {
continue;
};
fields.prepare(&FieldSync {
device,
queue,
output_layout: &self.surface_field_output_layout,
input_layout: &self.surface_field_input_layout,
erosion_layout: &self.surface_field_erosion_layout,
normal_layout: &self.surface_field_normal_layout,
component_layout: &self.surface_component_layout,
key,
policy: representation.params.surface_components,
dimensions: [
uniforms.grid_size[0],
uniforms.grid_size[1],
uniforms.grid_size[2],
],
atoms,
structure: structure_gpu,
asset_arena: &self.asset_arena,
compaction,
uniforms: &uniforms,
})?;
}
Ok(())
}
/// Releases every field no surface asked for, and charges the rest.
///
/// The ledger is charged against the first resident key, because a field
/// cache is one scene-wide allocation shared by every surface rather than
/// one resource per representation.
pub(super) fn retain_surface_fields(
&mut self,
derived_cache: &mut crate::DerivedCache,
derived_frame: u64,
) {
let live: Vec<_> = self.slots.iter().filter_map(GpuSlot::surface_key).collect();
self.surface_fields.retain(&live);
let Some(first) = live.first().copied() else {
return;
};
let _ = derived_cache.retain(
crate::DerivedCacheKey::SurfaceField(first),
crate::DerivedCacheClass::SurfaceField,
crate::DerivedFootprint {
cpu_bytes: 0,
gpu_bytes: self.surface_fields.resident_bytes(),
},
derived_frame,
);
}
}