use super::GpuScene;
use crate::engine::pipeline_cache::{SpecializationKey, VisualFamily};
use crate::passes::PassRegistry;
use crate::scene_gpu::slot_types::DrawFamily;
use molgfx_core::{Scene, VisualStage};
use molgfx_gpu::Device;
impl<D: Device> GpuScene<D> {
pub(crate) fn settle_specializations(
&mut self,
device: &D,
scene: &Scene,
passes: &PassRegistry<D>,
) {
let layouts = SpecializationLayouts::<D> {
group0: &self.group0_layout,
group2: &self.group2_layout,
ribbon: &self.ribbon_layout,
quality: &self.quality_layout,
};
let specialized = &mut self.specialized;
let slot_index = &mut self.slots;
for slot in slot_index {
let mut drawn = [false; DrawFamily::COUNT];
if slot.has_fragment_style()
&& scene
.representation(slot.key.representation)
.is_some_and(|representation| representation.visual.is_some())
{
for family in slot.drawn_families() {
drawn[family.index()] = true;
}
}
let program = if drawn.iter().any(|drawn| *drawn) {
scene
.representation(slot.key.representation)
.and_then(|representation| representation.visual.as_ref())
.map(molgfx_core::VisualStyle::program)
} else {
None
};
let keys = slot.keys_mut();
for (index, family) in DrawFamily::ALL.into_iter().enumerate() {
if !drawn[index] {
keys[index] = None;
continue;
}
let Some(program) = program else {
keys[index] = None;
continue;
};
let key = SpecializationKey::new(
program.fingerprint(),
VisualStage::Fragment,
VisualFamily::new(family.name()),
);
specialized.settle(key, program, || {
build_specialized(device, &layouts, passes, family)
});
keys[index] = Some(key);
}
}
}
pub(crate) fn specialized_pipeline(
&self,
key: Option<SpecializationKey>,
) -> Option<&D::Pipeline> {
self.specialized.resolve(key?).pipeline()
}
pub(crate) fn specialization_report(&self) -> String {
let mut report = String::new();
for slot in &self.slots {
for key in slot.keys() {
let Some(key) = key else { continue };
report.push_str(&self.specialized.resolve(*key).report(*key));
report.push('\n');
}
}
report
}
}
fn build_specialized<D: Device>(
device: &D,
layouts: &SpecializationLayouts<'_, D>,
passes: &PassRegistry<D>,
family: DrawFamily,
) -> Result<D::Pipeline, crate::RenderError> {
match family {
DrawFamily::Sphere => crate::passes::SpherePass::build_specialized(
device,
layouts.group0,
layouts.group2,
false,
),
DrawFamily::SphereClipped => crate::passes::SpherePass::build_specialized(
device,
layouts.group0,
layouts.group2,
true,
),
DrawFamily::Bond => crate::passes::BondPass::build_specialized(
device,
layouts.group0,
layouts.group2,
false,
),
DrawFamily::BondWire => {
crate::passes::BondPass::build_specialized(device, layouts.group0, layouts.group2, true)
}
DrawFamily::Point => {
crate::passes::PointPass::build_specialized(device, layouts.group0, layouts.group2)
}
DrawFamily::Cartoon => {
crate::passes::CartoonPass::build_specialized(device, layouts.group0, layouts.ribbon)
}
DrawFamily::SurfaceUnion => crate::passes::SurfacePass::build_specialized(
device,
layouts.group0,
layouts.group2,
false,
),
DrawFamily::SurfaceGrid => crate::passes::SurfacePass::build_specialized(
device,
layouts.group0,
layouts.group2,
true,
),
DrawFamily::ShadowSphere => crate::passes::ShadowPass::build_specialized(
device,
layouts.group0,
layouts.group2,
false,
),
DrawFamily::ShadowBond => crate::passes::ShadowPass::build_specialized(
device,
layouts.group0,
layouts.group2,
true,
),
DrawFamily::ShadowRibbon => crate::passes::ShadowPass::build_specialized_ribbon(
device,
layouts.group0,
layouts.ribbon,
),
DrawFamily::AmbientOcclusion => passes.ambient_occlusion.build_specialized(
device,
layouts.group0,
layouts.quality,
false,
),
DrawFamily::AmbientOcclusionRayQuery => passes.ambient_occlusion.build_specialized(
device,
layouts.group0,
layouts.quality,
true,
),
}
}
#[derive(Clone, Copy)]
struct SpecializationLayouts<'a, D: Device> {
group0: &'a D::BindGroupLayout,
group2: &'a D::BindGroupLayout,
ribbon: &'a D::BindGroupLayout,
quality: &'a D::BindGroupLayout,
}