From 781241742dc650353bd354868bd497d1f65011a9 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Sat, 18 Jul 2026 11:43:33 +0400 Subject: [PATCH] feat(runtime): trace visual graph phases --- crates/fparkan-assets/src/lib.rs | 38 +++++++++++++++++++++++++++++++ crates/fparkan-runtime/src/lib.rs | 29 +++++++++++++++++++---- docs/tomes/05-render.md | 7 ++++++ 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/crates/fparkan-assets/src/lib.rs b/crates/fparkan-assets/src/lib.rs index ba40ca8..aeb91d9 100644 --- a/crates/fparkan-assets/src/lib.rs +++ b/crates/fparkan-assets/src/lib.rs @@ -468,6 +468,17 @@ pub enum AssetPreparationPhase { Textures, } +/// Visual dependency class currently being expanded into a prototype graph. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum VisualDependencyPhase { + /// Resolve a mesh's WEAR table. + Wear, + /// Resolve MAT0 material documents selected by WEAR. + Material, + /// Validate diffuse TEXM textures and lightmaps. + Texture, +} + /// Errors raised while preparing CPU-side assets. #[derive(Debug)] pub enum AssetError { @@ -1103,6 +1114,29 @@ pub fn extend_graph_report_with_visual_dependencies( report: &mut PrototypeGraphReport, graph: &mut PrototypeGraph, prototypes: &[EffectivePrototype], +) { + extend_graph_report_with_visual_dependencies_and_progress( + repository, + report, + graph, + prototypes, + |_| {}, + ); +} + +/// Extends a prototype dependency report while reporting each visual dependency class. +/// +/// # Panics +/// +/// Panics only if the hard-coded, host-compatible `material.lib` path stops +/// satisfying the path policy, which indicates a programming error in this module. +#[allow(clippy::too_many_lines)] +pub fn extend_graph_report_with_visual_dependencies_and_progress( + repository: &R, + report: &mut PrototypeGraphReport, + graph: &mut PrototypeGraph, + prototypes: &[EffectivePrototype], + mut on_phase: impl FnMut(VisualDependencyPhase), ) { if graph.visual_dependencies_expanded { return; @@ -1140,6 +1174,7 @@ pub fn extend_graph_report_with_visual_dependencies( let mesh_parent_edge = mesh_edge_id(graph, prototype_node_id); let root_index = root_index_for_prototype(graph, prototype_index); + on_phase(VisualDependencyPhase::Wear); match resolve_wear_table(repository, mesh) { Ok(table) => { report.wear_resolved_count += 1; @@ -1178,6 +1213,7 @@ pub fn extend_graph_report_with_visual_dependencies( &mut next_edge, ); report.material_slot_count += table.entries.len(); + on_phase(VisualDependencyPhase::Material); for (material_index, _entry) in table.entries.iter().enumerate() { let Ok(material_index) = u16::try_from(material_index) else { push_visual_failure( @@ -1224,6 +1260,7 @@ pub fn extend_graph_report_with_visual_dependencies( &mut next_edge, ); for texture in material.document.texture_requests() { + on_phase(VisualDependencyPhase::Texture); report.texture_request_count += 1; match resolve_texm_validation_cached( repository, @@ -1284,6 +1321,7 @@ pub fn extend_graph_report_with_visual_dependencies( } } for lightmap in &table.lightmaps { + on_phase(VisualDependencyPhase::Texture); report.lightmap_request_count += 1; match resolve_texm_validation_cached( repository, diff --git a/crates/fparkan-runtime/src/lib.rs b/crates/fparkan-runtime/src/lib.rs index 9a35d65..ff7beda 100644 --- a/crates/fparkan-runtime/src/lib.rs +++ b/crates/fparkan-runtime/src/lib.rs @@ -22,10 +22,11 @@ use fparkan_assets::{ decode_mission_land_path, decode_mission_payload, decode_nres_payload, - derive_mission_land_paths, extend_graph_report_with_visual_dependencies, prepare_terrain_world, - AssetError as AssetPreparationError, AssetId, AssetManager, AssetPreparationPhase, - BuildCategory, MissionAssetPlan, MissionDocument, MissionError, MissionTerrainPaths, NresError, - PreparedVisual, TerrainFormatError, TerrainPreparationError, TerrainWorld, TmaProfile, + derive_mission_land_paths, extend_graph_report_with_visual_dependencies_and_progress, + prepare_terrain_world, AssetError as AssetPreparationError, AssetId, AssetManager, + AssetPreparationPhase, BuildCategory, MissionAssetPlan, MissionDocument, MissionError, + MissionTerrainPaths, NresError, PreparedVisual, TerrainFormatError, TerrainPreparationError, + TerrainWorld, TmaProfile, VisualDependencyPhase, }; use fparkan_path::{normalize_relative, NormalizedPath, PathError, PathPolicy}; use fparkan_prototype::{ @@ -118,6 +119,12 @@ pub enum MissionLoadPhase { Graph, /// Expand model-backed visual dependencies of the prototype graph. GraphVisuals, + /// Resolve WEAR tables while expanding visual dependencies. + GraphVisualWears, + /// Resolve MAT0 documents while expanding visual dependencies. + GraphVisualMaterials, + /// Validate TEXM documents while expanding visual dependencies. + GraphVisualTextures, /// Prepare all reachable visual/resource dependencies. Assets, /// Decode and validate MSH model meshes. @@ -693,11 +700,23 @@ fn load_mission_with_options_and_progress( let (mut prototype_graph, resolved_prototypes, mut prototype_report) = build_prototype_graph_report(&repository, vfs.as_ref(), scoped_graph_roots); record_load_phase(&mut trace, &mut on_phase, MissionLoadPhase::GraphVisuals); - extend_graph_report_with_visual_dependencies( + let mut last_graph_visual_phase = None; + extend_graph_report_with_visual_dependencies_and_progress( &repository, &mut prototype_report, &mut prototype_graph, &resolved_prototypes, + |phase| { + let runtime_phase = match phase { + VisualDependencyPhase::Wear => MissionLoadPhase::GraphVisualWears, + VisualDependencyPhase::Material => MissionLoadPhase::GraphVisualMaterials, + VisualDependencyPhase::Texture => MissionLoadPhase::GraphVisualTextures, + }; + if last_graph_visual_phase != Some(runtime_phase) { + record_load_phase(&mut trace, &mut on_phase, runtime_phase); + last_graph_visual_phase = Some(runtime_phase); + } + }, ); if !prototype_report.is_success() { return Err(EngineError::PrototypeGraph { diff --git a/docs/tomes/05-render.md b/docs/tomes/05-render.md index dc8790d..5899182 100644 --- a/docs/tomes/05-render.md +++ b/docs/tomes/05-render.md @@ -1094,6 +1094,13 @@ process was terminated. This gives no Part 2 GPU acceptance or pixel result and does not attribute the delay to Vulkan; its visual-dependency corpus remains a separate profiling and compatibility target. +`GraphVisuals` is now split observationally into `GraphVisualWears`, +`GraphVisualMaterials` and `GraphVisualTextures`; it preserves the existing +graph traversal, edge order and validation semantics. Repeating the controlled +180-second Part 2 probe ended at `GraphVisualTextures`. Thus Part 2 had passed +WEAR and MAT0 expansion before timeout; its remaining bottleneck is TEXM +validation/archive work, not Vulkan submission or the base prototype graph. + `Assets` now has four ordered diagnostic sub-checkpoints: `AssetModelMeshes` (MSH), `AssetWearTables` (WEAR), `AssetMaterials` (MAT0) and `AssetTextures` (diffuse TEXM and lightmaps). The callback is observational: it neither changes