feat(runtime): trace visual dependency requests
This commit is contained in:
@@ -48,6 +48,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
const TEXTURES_ARCHIVE: &str = "textures.lib";
|
const TEXTURES_ARCHIVE: &str = "textures.lib";
|
||||||
const LIGHTMAP_ARCHIVE: &str = "lightmap.lib";
|
const LIGHTMAP_ARCHIVE: &str = "lightmap.lib";
|
||||||
|
const VISUAL_DEPENDENCY_PROGRESS_INTERVAL: usize = 64;
|
||||||
|
|
||||||
/// Canonical terrain archive paths derived from a mission land reference.
|
/// Canonical terrain archive paths derived from a mission land reference.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
@@ -479,6 +480,15 @@ pub enum VisualDependencyPhase {
|
|||||||
Texture,
|
Texture,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Throttled visual-dependency expansion progress.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct VisualDependencyProgress {
|
||||||
|
/// Dependency class currently being expanded.
|
||||||
|
pub phase: VisualDependencyPhase,
|
||||||
|
/// Number of requests reached in this class during the current expansion.
|
||||||
|
pub request_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
/// Errors raised while preparing CPU-side assets.
|
/// Errors raised while preparing CPU-side assets.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AssetError {
|
pub enum AssetError {
|
||||||
@@ -1137,6 +1147,39 @@ pub fn extend_graph_report_with_visual_dependencies_and_progress<R: ResourceRepo
|
|||||||
graph: &mut PrototypeGraph,
|
graph: &mut PrototypeGraph,
|
||||||
prototypes: &[EffectivePrototype],
|
prototypes: &[EffectivePrototype],
|
||||||
mut on_phase: impl FnMut(VisualDependencyPhase),
|
mut on_phase: impl FnMut(VisualDependencyPhase),
|
||||||
|
) {
|
||||||
|
let mut last_phase = None;
|
||||||
|
extend_graph_report_with_visual_dependencies_with_progress(
|
||||||
|
repository,
|
||||||
|
report,
|
||||||
|
graph,
|
||||||
|
prototypes,
|
||||||
|
|progress| {
|
||||||
|
if last_phase != Some(progress.phase) {
|
||||||
|
on_phase(progress.phase);
|
||||||
|
last_phase = Some(progress.phase);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extends a prototype dependency report with throttled request progress.
|
||||||
|
///
|
||||||
|
/// The callback receives each dependency-class entrance, its first request,
|
||||||
|
/// and every subsequent multiple of 64 requests. Graph traversal, edge order
|
||||||
|
/// and validation remain unchanged.
|
||||||
|
///
|
||||||
|
/// # 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_with_progress<R: ResourceRepository>(
|
||||||
|
repository: &R,
|
||||||
|
report: &mut PrototypeGraphReport,
|
||||||
|
graph: &mut PrototypeGraph,
|
||||||
|
prototypes: &[EffectivePrototype],
|
||||||
|
mut on_progress: impl FnMut(VisualDependencyProgress),
|
||||||
) {
|
) {
|
||||||
if graph.visual_dependencies_expanded {
|
if graph.visual_dependencies_expanded {
|
||||||
return;
|
return;
|
||||||
@@ -1159,6 +1202,9 @@ pub fn extend_graph_report_with_visual_dependencies_and_progress<R: ResourceRepo
|
|||||||
let mut diffuse_texture_validation = HashMap::new();
|
let mut diffuse_texture_validation = HashMap::new();
|
||||||
let mut lightmap_texture_validation = HashMap::new();
|
let mut lightmap_texture_validation = HashMap::new();
|
||||||
let mut material_validation = HashMap::new();
|
let mut material_validation = HashMap::new();
|
||||||
|
let mut wear_requests = 0usize;
|
||||||
|
let mut material_requests = 0usize;
|
||||||
|
let mut texture_requests = 0usize;
|
||||||
|
|
||||||
for (prototype_index, prototype) in prototypes.iter().enumerate() {
|
for (prototype_index, prototype) in prototypes.iter().enumerate() {
|
||||||
let PrototypeGeometry::Mesh(mesh) = &prototype.geometry else {
|
let PrototypeGeometry::Mesh(mesh) = &prototype.geometry else {
|
||||||
@@ -1174,7 +1220,12 @@ pub fn extend_graph_report_with_visual_dependencies_and_progress<R: ResourceRepo
|
|||||||
let mesh_parent_edge = mesh_edge_id(graph, prototype_node_id);
|
let mesh_parent_edge = mesh_edge_id(graph, prototype_node_id);
|
||||||
let root_index = root_index_for_prototype(graph, prototype_index);
|
let root_index = root_index_for_prototype(graph, prototype_index);
|
||||||
|
|
||||||
on_phase(VisualDependencyPhase::Wear);
|
wear_requests = wear_requests.saturating_add(1);
|
||||||
|
report_visual_dependency_progress(
|
||||||
|
&mut on_progress,
|
||||||
|
VisualDependencyPhase::Wear,
|
||||||
|
wear_requests,
|
||||||
|
);
|
||||||
match resolve_wear_table(repository, mesh) {
|
match resolve_wear_table(repository, mesh) {
|
||||||
Ok(table) => {
|
Ok(table) => {
|
||||||
report.wear_resolved_count += 1;
|
report.wear_resolved_count += 1;
|
||||||
@@ -1213,8 +1264,17 @@ pub fn extend_graph_report_with_visual_dependencies_and_progress<R: ResourceRepo
|
|||||||
&mut next_edge,
|
&mut next_edge,
|
||||||
);
|
);
|
||||||
report.material_slot_count += table.entries.len();
|
report.material_slot_count += table.entries.len();
|
||||||
on_phase(VisualDependencyPhase::Material);
|
on_progress(VisualDependencyProgress {
|
||||||
|
phase: VisualDependencyPhase::Material,
|
||||||
|
request_count: material_requests,
|
||||||
|
});
|
||||||
for (material_index, _entry) in table.entries.iter().enumerate() {
|
for (material_index, _entry) in table.entries.iter().enumerate() {
|
||||||
|
material_requests = material_requests.saturating_add(1);
|
||||||
|
report_visual_dependency_progress(
|
||||||
|
&mut on_progress,
|
||||||
|
VisualDependencyPhase::Material,
|
||||||
|
material_requests,
|
||||||
|
);
|
||||||
let Ok(material_index) = u16::try_from(material_index) else {
|
let Ok(material_index) = u16::try_from(material_index) else {
|
||||||
push_visual_failure(
|
push_visual_failure(
|
||||||
report,
|
report,
|
||||||
@@ -1260,7 +1320,12 @@ pub fn extend_graph_report_with_visual_dependencies_and_progress<R: ResourceRepo
|
|||||||
&mut next_edge,
|
&mut next_edge,
|
||||||
);
|
);
|
||||||
for texture in material.document.texture_requests() {
|
for texture in material.document.texture_requests() {
|
||||||
on_phase(VisualDependencyPhase::Texture);
|
texture_requests = texture_requests.saturating_add(1);
|
||||||
|
report_visual_dependency_progress(
|
||||||
|
&mut on_progress,
|
||||||
|
VisualDependencyPhase::Texture,
|
||||||
|
texture_requests,
|
||||||
|
);
|
||||||
report.texture_request_count += 1;
|
report.texture_request_count += 1;
|
||||||
match resolve_texm_validation_cached(
|
match resolve_texm_validation_cached(
|
||||||
repository,
|
repository,
|
||||||
@@ -1321,7 +1386,12 @@ pub fn extend_graph_report_with_visual_dependencies_and_progress<R: ResourceRepo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for lightmap in &table.lightmaps {
|
for lightmap in &table.lightmaps {
|
||||||
on_phase(VisualDependencyPhase::Texture);
|
texture_requests = texture_requests.saturating_add(1);
|
||||||
|
report_visual_dependency_progress(
|
||||||
|
&mut on_progress,
|
||||||
|
VisualDependencyPhase::Texture,
|
||||||
|
texture_requests,
|
||||||
|
);
|
||||||
report.lightmap_request_count += 1;
|
report.lightmap_request_count += 1;
|
||||||
match resolve_texm_validation_cached(
|
match resolve_texm_validation_cached(
|
||||||
repository,
|
repository,
|
||||||
@@ -1384,6 +1454,19 @@ pub fn extend_graph_report_with_visual_dependencies_and_progress<R: ResourceRepo
|
|||||||
graph.visual_dependencies_expanded = true;
|
graph.visual_dependencies_expanded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn report_visual_dependency_progress(
|
||||||
|
on_progress: &mut impl FnMut(VisualDependencyProgress),
|
||||||
|
phase: VisualDependencyPhase,
|
||||||
|
request_count: usize,
|
||||||
|
) {
|
||||||
|
if request_count == 1 || request_count.is_multiple_of(VISUAL_DEPENDENCY_PROGRESS_INTERVAL) {
|
||||||
|
on_progress(VisualDependencyProgress {
|
||||||
|
phase,
|
||||||
|
request_count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn push_graph_resource_node(
|
fn push_graph_resource_node(
|
||||||
graph: &mut PrototypeGraph,
|
graph: &mut PrototypeGraph,
|
||||||
kind: PrototypeGraphNodeKind,
|
kind: PrototypeGraphNodeKind,
|
||||||
@@ -2200,6 +2283,36 @@ mod tests {
|
|||||||
use fparkan_vfs::{DirectoryVfs, MemoryVfs, Vfs};
|
use fparkan_vfs::{DirectoryVfs, MemoryVfs, Vfs};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn visual_dependency_progress_is_throttled_at_fixed_request_intervals() {
|
||||||
|
let mut progress = Vec::new();
|
||||||
|
for request_count in 1..=129 {
|
||||||
|
report_visual_dependency_progress(
|
||||||
|
&mut |event| progress.push(event),
|
||||||
|
VisualDependencyPhase::Texture,
|
||||||
|
request_count,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
progress,
|
||||||
|
vec![
|
||||||
|
VisualDependencyProgress {
|
||||||
|
phase: VisualDependencyPhase::Texture,
|
||||||
|
request_count: 1,
|
||||||
|
},
|
||||||
|
VisualDependencyProgress {
|
||||||
|
phase: VisualDependencyPhase::Texture,
|
||||||
|
request_count: 64,
|
||||||
|
},
|
||||||
|
VisualDependencyProgress {
|
||||||
|
phase: VisualDependencyPhase::Texture,
|
||||||
|
request_count: 128,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_only_plan_uses_graph_requests() {
|
fn count_only_plan_uses_graph_requests() {
|
||||||
let graph = PrototypeGraph::default();
|
let graph = PrototypeGraph::default();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
use fparkan_assets::{
|
use fparkan_assets::{
|
||||||
decode_mission_land_path, decode_mission_payload, decode_nres_payload,
|
decode_mission_land_path, decode_mission_payload, decode_nres_payload,
|
||||||
derive_mission_land_paths, extend_graph_report_with_visual_dependencies_and_progress,
|
derive_mission_land_paths, extend_graph_report_with_visual_dependencies_with_progress,
|
||||||
prepare_terrain_world, AssetError as AssetPreparationError, AssetId, AssetManager,
|
prepare_terrain_world, AssetError as AssetPreparationError, AssetId, AssetManager,
|
||||||
AssetPreparationPhase, BuildCategory, MissionAssetPlan, MissionDocument, MissionError,
|
AssetPreparationPhase, BuildCategory, MissionAssetPlan, MissionDocument, MissionError,
|
||||||
MissionTerrainPaths, NresError, PreparedVisual, TerrainFormatError, TerrainPreparationError,
|
MissionTerrainPaths, NresError, PreparedVisual, TerrainFormatError, TerrainPreparationError,
|
||||||
@@ -127,6 +127,8 @@ pub enum MissionLoadPhase {
|
|||||||
GraphVisualMaterials,
|
GraphVisualMaterials,
|
||||||
/// Validate TEXM documents while expanding visual dependencies.
|
/// Validate TEXM documents while expanding visual dependencies.
|
||||||
GraphVisualTextures,
|
GraphVisualTextures,
|
||||||
|
/// Progress marker emitted after each 64 TEXM validation requests.
|
||||||
|
GraphVisualTextureRequests(usize),
|
||||||
/// Prepare all reachable visual/resource dependencies.
|
/// Prepare all reachable visual/resource dependencies.
|
||||||
Assets,
|
Assets,
|
||||||
/// Decode and validate MSH model meshes.
|
/// Decode and validate MSH model meshes.
|
||||||
@@ -706,13 +708,13 @@ fn load_mission_with_options_and_progress(
|
|||||||
build_prototype_graph_report(&repository, vfs.as_ref(), scoped_graph_roots);
|
build_prototype_graph_report(&repository, vfs.as_ref(), scoped_graph_roots);
|
||||||
record_load_phase(&mut trace, &mut on_phase, MissionLoadPhase::GraphVisuals);
|
record_load_phase(&mut trace, &mut on_phase, MissionLoadPhase::GraphVisuals);
|
||||||
let mut last_graph_visual_phase = None;
|
let mut last_graph_visual_phase = None;
|
||||||
extend_graph_report_with_visual_dependencies_and_progress(
|
extend_graph_report_with_visual_dependencies_with_progress(
|
||||||
&repository,
|
&repository,
|
||||||
&mut prototype_report,
|
&mut prototype_report,
|
||||||
&mut prototype_graph,
|
&mut prototype_graph,
|
||||||
&resolved_prototypes,
|
&resolved_prototypes,
|
||||||
|phase| {
|
|progress| {
|
||||||
let runtime_phase = match phase {
|
let runtime_phase = match progress.phase {
|
||||||
VisualDependencyPhase::Wear => MissionLoadPhase::GraphVisualWears,
|
VisualDependencyPhase::Wear => MissionLoadPhase::GraphVisualWears,
|
||||||
VisualDependencyPhase::Material => MissionLoadPhase::GraphVisualMaterials,
|
VisualDependencyPhase::Material => MissionLoadPhase::GraphVisualMaterials,
|
||||||
VisualDependencyPhase::Texture => MissionLoadPhase::GraphVisualTextures,
|
VisualDependencyPhase::Texture => MissionLoadPhase::GraphVisualTextures,
|
||||||
@@ -721,6 +723,13 @@ fn load_mission_with_options_and_progress(
|
|||||||
record_load_phase(&mut trace, &mut on_phase, runtime_phase);
|
record_load_phase(&mut trace, &mut on_phase, runtime_phase);
|
||||||
last_graph_visual_phase = Some(runtime_phase);
|
last_graph_visual_phase = Some(runtime_phase);
|
||||||
}
|
}
|
||||||
|
if progress.phase == VisualDependencyPhase::Texture && progress.request_count > 1 {
|
||||||
|
record_load_phase(
|
||||||
|
&mut trace,
|
||||||
|
&mut on_phase,
|
||||||
|
MissionLoadPhase::GraphVisualTextureRequests(progress.request_count),
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if !prototype_report.is_success() {
|
if !prototype_report.is_success() {
|
||||||
|
|||||||
+12
-3
@@ -1097,9 +1097,18 @@ separate profiling and compatibility target.
|
|||||||
`GraphVisuals` is now split observationally into `GraphVisualWears`,
|
`GraphVisuals` is now split observationally into `GraphVisualWears`,
|
||||||
`GraphVisualMaterials` and `GraphVisualTextures`; it preserves the existing
|
`GraphVisualMaterials` and `GraphVisualTextures`; it preserves the existing
|
||||||
graph traversal, edge order and validation semantics. Repeating the controlled
|
graph traversal, edge order and validation semantics. Repeating the controlled
|
||||||
180-second Part 2 probe ended at `GraphVisualTextures`. Thus Part 2 had passed
|
180-second Part 2 probe once ended at `GraphVisualTextures`. That particular
|
||||||
WEAR and MAT0 expansion before timeout; its remaining bottleneck is TEXM
|
execution had passed WEAR and MAT0 expansion before timeout, but a checkpoint
|
||||||
validation/archive work, not Vulkan submission or the base prototype graph.
|
is only the last phase reached in one run: it is not proof of a single global
|
||||||
|
bottleneck or of Vulkan involvement.
|
||||||
|
|
||||||
|
Visual expansion now reports each dependency-class entrance, its first request
|
||||||
|
and every 64th later request; this is diagnostic-only and does not alter
|
||||||
|
traversal, edges or validation. A
|
||||||
|
later controlled 180-second Part 2 probe ended at `GraphVisualMaterials`,
|
||||||
|
before the first TEXM progress marker. The differing checkpoints rule out the
|
||||||
|
previous overly narrow claim that TEXM is the sole remaining target. Profiling
|
||||||
|
must compare WEAR, MAT0, TEXM, graph allocation and archive I/O quantitatively.
|
||||||
|
|
||||||
Mission loading now raises the decoded-payload cache entry budget from 64 to
|
Mission loading now raises the decoded-payload cache entry budget from 64 to
|
||||||
256 while retaining its 64 MiB byte budget. This avoids premature entry-count
|
256 while retaining its 64 MiB byte budget. This avoids premature entry-count
|
||||||
|
|||||||
Reference in New Issue
Block a user