aboutsummaryrefslogtreecommitdiff
path: root/crates/fparkan-assets/src/lib.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-07-18 10:15:43 +0300
committerValentin Popov <valentin@popov.link>2026-07-18 10:15:43 +0300
commitc55d835d351c276e5f53ee190d5608dc73627d7a (patch)
tree4d18fb9107304f114da57f08a640823f1a2233b3 /crates/fparkan-assets/src/lib.rs
parent8f2e9d930875adca980fb94755bd98a40c2e58c4 (diff)
downloadfparkan-c55d835d351c276e5f53ee190d5608dc73627d7a.tar.xz
fparkan-c55d835d351c276e5f53ee190d5608dc73627d7a.zip
feat(runtime): trace asset preparation phases
Diffstat (limited to 'crates/fparkan-assets/src/lib.rs')
-rw-r--r--crates/fparkan-assets/src/lib.rs63
1 files changed, 63 insertions, 0 deletions
diff --git a/crates/fparkan-assets/src/lib.rs b/crates/fparkan-assets/src/lib.rs
index 0bafcfa..423e6ec 100644
--- a/crates/fparkan-assets/src/lib.rs
+++ b/crates/fparkan-assets/src/lib.rs
@@ -455,6 +455,17 @@ pub struct AssetPreparationReport {
pub texture_pixels: u64,
}
+/// Asset preparation checkpoint emitted while resolving visual dependencies.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum AssetPreparationPhase {
+ /// Decode a model and its corresponding WEAR table.
+ ModelAndWear,
+ /// Resolve MAT0 material documents referenced by a WEAR table.
+ Materials,
+ /// Decode diffuse textures and baked lightmaps.
+ Textures,
+}
+
/// Errors raised while preparing CPU-side assets.
#[derive(Debug)]
pub enum AssetError {
@@ -571,6 +582,25 @@ impl<R: ResourceRepository> AssetManager<R> {
prepare_mission_assets_with_repository(&self.repository, root_prototype_spans, prototypes)
}
+ /// Builds mission assets and reports the active dependency class.
+ ///
+ /// # Errors
+ ///
+ /// Returns [`AssetError`] if any visual dependency is missing or malformed.
+ pub fn prepare_mission_assets_with_progress(
+ &self,
+ root_prototype_spans: &[std::ops::Range<usize>],
+ prototypes: &[EffectivePrototype],
+ on_phase: impl FnMut(AssetPreparationPhase),
+ ) -> Result<MissionAssets, AssetError> {
+ prepare_mission_assets_with_repository_with_progress(
+ &self.repository,
+ root_prototype_spans,
+ prototypes,
+ on_phase,
+ )
+ }
+
/// Builds mission assets together with a bounded preparation report.
///
/// # Errors
@@ -656,6 +686,28 @@ pub fn prepare_mission_assets_with_repository<R: ResourceRepository>(
.0)
}
+/// Builds mission assets and reports the active dependency class.
+///
+/// # Errors
+///
+/// Returns [`AssetError`] if any visual dependency is missing or malformed.
+pub fn prepare_mission_assets_with_repository_with_progress<R: ResourceRepository>(
+ repository: &R,
+ root_prototype_spans: &[std::ops::Range<usize>],
+ prototypes: &[EffectivePrototype],
+ mut on_phase: impl FnMut(AssetPreparationPhase),
+) -> Result<MissionAssets, AssetError> {
+ Ok(prepare_mission_assets_with_repository_internal(
+ repository,
+ root_prototype_spans,
+ prototypes,
+ AssetIdentityPolicy::default(),
+ AssetPreparationLimits::default(),
+ &mut on_phase,
+ )?
+ .0)
+}
+
/// Builds mission assets while enforcing explicit preparation limits.
///
/// # Errors
@@ -668,12 +720,14 @@ pub fn prepare_mission_assets_profiled_with_repository<R: ResourceRepository>(
prototypes: &[EffectivePrototype],
limits: AssetPreparationLimits,
) -> Result<(MissionAssets, AssetPreparationReport), AssetError> {
+ let mut ignore_phase = |_| {};
prepare_mission_assets_with_repository_internal(
repository,
root_prototype_spans,
prototypes,
AssetIdentityPolicy::default(),
limits,
+ &mut ignore_phase,
)
}
@@ -686,6 +740,7 @@ fn prepare_mission_assets_with_repository_internal<R: ResourceRepository>(
prototypes: &[EffectivePrototype],
identity_policy: AssetIdentityPolicy,
limits: AssetPreparationLimits,
+ on_phase: &mut dyn FnMut(AssetPreparationPhase),
) -> Result<(MissionAssets, AssetPreparationReport), AssetError> {
if prototypes.is_empty() {
return Ok((MissionAssets::default(), AssetPreparationReport::default()));
@@ -725,6 +780,7 @@ fn prepare_mission_assets_with_repository_internal<R: ResourceRepository>(
proto,
identity_policy,
&mut preparation_cache,
+ on_phase,
)?;
if bundle.visual.id != visual_id {
// Defensive check. stable IDs are deterministic for the same inputs.
@@ -1416,6 +1472,7 @@ pub fn prepare_visual_with_repository<R: ResourceRepository>(
proto,
AssetIdentityPolicy::default(),
&mut preparation_cache,
+ &mut |_| {},
)?
.visual)
}
@@ -1444,6 +1501,7 @@ fn prepare_visual_with_repository_internal<R: ResourceRepository>(
proto: &EffectivePrototype,
identity_policy: AssetIdentityPolicy,
preparation_cache: &mut PreparationCache,
+ on_phase: &mut dyn FnMut(AssetPreparationPhase),
) -> Result<PreparedVisualBundle, AssetError> {
let PrototypeGeometry::Mesh(mesh_key) = &proto.geometry else {
return Ok(PreparedVisualBundle {
@@ -1455,6 +1513,7 @@ fn prepare_visual_with_repository_internal<R: ResourceRepository>(
});
};
+ on_phase(AssetPreparationPhase::ModelAndWear);
let model = prepare_model_cached(repository, mesh_key, preparation_cache)?;
let model_id = AssetId::new((identity_policy.model)(proto));
let prepared_model = PreparedModel {
@@ -1491,6 +1550,7 @@ fn prepare_visual_with_repository_internal<R: ResourceRepository>(
.iter()
.map(|lightmap| lightmap.lightmap.clone())
.collect();
+ on_phase(AssetPreparationPhase::Materials);
for material_index in 0..wear.entries.len() {
let material_index = u16::try_from(material_index).map_err(|_| {
AssetError::InvalidPrototype("material index does not fit archive format".to_string())
@@ -1520,6 +1580,7 @@ fn prepare_visual_with_repository_internal<R: ResourceRepository>(
});
for texture in texture_requests {
+ on_phase(AssetPreparationPhase::Textures);
let prepared_texture = prepare_texture_cached(
repository,
&texture,
@@ -1534,6 +1595,7 @@ fn prepare_visual_with_repository_internal<R: ResourceRepository>(
}
for lightmap in &wear.lightmaps {
+ on_phase(AssetPreparationPhase::Textures);
let prepared_lightmap = prepare_texture_cached(
repository,
&lightmap.lightmap,
@@ -2830,6 +2892,7 @@ mod tests {
&prototypes,
policy,
AssetPreparationLimits::default(),
+ &mut |_| {},
)
.expect_err("collision should fail");