aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-07-18 12:17:06 +0300
committerValentin Popov <valentin@popov.link>2026-07-18 12:17:06 +0300
commit2e14b1d5dd99ecd577c96ea4aaacdd584872a338 (patch)
tree7ac3d27e04c1326d8a01f79039d727647588616f
parent67308d4019891d607ca43807b6d0a3652923bb6d (diff)
downloadfparkan-2e14b1d5dd99ecd577c96ea4aaacdd584872a338.tar.xz
fparkan-2e14b1d5dd99ecd577c96ea4aaacdd584872a338.zip
perf(assets): cache visual wear validation
-rw-r--r--crates/fparkan-assets/src/lib.rs43
-rw-r--r--docs/tomes/05-render.md11
2 files changed, 52 insertions, 2 deletions
diff --git a/crates/fparkan-assets/src/lib.rs b/crates/fparkan-assets/src/lib.rs
index d133574..11e324f 100644
--- a/crates/fparkan-assets/src/lib.rs
+++ b/crates/fparkan-assets/src/lib.rs
@@ -50,6 +50,9 @@ const TEXTURES_ARCHIVE: &str = "textures.lib";
const LIGHTMAP_ARCHIVE: &str = "lightmap.lib";
const VISUAL_DEPENDENCY_PROGRESS_INTERVAL: usize = 64;
+type WearValidationCache =
+ HashMap<(NormalizedPath, Vec<u8>), Result<fparkan_material::WearTable, String>>;
+
/// Canonical terrain archive paths derived from a mission land reference.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MissionTerrainPaths {
@@ -1202,6 +1205,7 @@ pub fn extend_graph_report_with_visual_dependencies_with_progress<R: ResourceRep
let mut diffuse_texture_validation = HashMap::new();
let mut lightmap_texture_validation = HashMap::new();
let mut material_validation = HashMap::new();
+ let mut wear_validation = HashMap::new();
let mut wear_requests = 0usize;
let mut material_requests = 0usize;
let mut texture_requests = 0usize;
@@ -1236,7 +1240,7 @@ pub fn extend_graph_report_with_visual_dependencies_with_progress<R: ResourceRep
VisualDependencyPhase::Wear,
wear_requests,
);
- match resolve_wear_table(repository, mesh) {
+ match resolve_wear_table_cached(repository, mesh, &mut wear_validation) {
Ok(table) => {
report.wear_resolved_count += 1;
let wear_key = match wear_resource_key(mesh) {
@@ -1457,7 +1461,7 @@ pub fn extend_graph_report_with_visual_dependencies_with_progress<R: ResourceRep
mesh.name.0.clone(),
PrototypeGraphEdge::MeshToWear,
PrototypeGraphRequiredness::Required,
- &message.to_string(),
+ &message,
),
}
}
@@ -1899,6 +1903,21 @@ fn resolve_wear_table<R: ResourceRepository>(
decode_wear(&bytes).map_err(AssetError::Material)
}
+fn resolve_wear_table_cached<R: ResourceRepository>(
+ repository: &R,
+ mesh: &ResourceKey,
+ cache: &mut WearValidationCache,
+) -> Result<fparkan_material::WearTable, String> {
+ let wear_key = wear_resource_key(mesh).map_err(|error| error.to_string())?;
+ let cache_key = (wear_key.archive.clone(), wear_key.name.0.clone());
+ if let Some(result) = cache.get(&cache_key) {
+ return result.clone();
+ }
+ let result = resolve_wear_table(repository, mesh).map_err(|error| error.to_string());
+ cache.insert(cache_key, result.clone());
+ result
+}
+
fn push_visual_failure(
report: &mut PrototypeGraphReport,
graph: &PrototypeGraph,
@@ -2324,6 +2343,26 @@ mod tests {
}
#[test]
+ fn wear_validation_cache_replays_archive_qualified_failure() {
+ let repository = repository_with_archives(&[]);
+ let mesh = ResourceKey {
+ archive: parse_path("static.rlb").expect("archive"),
+ name: resource_name(b"tree.msh"),
+ type_id: Some(0x4853_454D),
+ };
+ let mut cache = WearValidationCache::new();
+ cache.insert(
+ (mesh.archive.clone(), b"tree.wea".to_vec()),
+ Err("cached WEAR failure".to_string()),
+ );
+
+ assert_eq!(
+ resolve_wear_table_cached(&repository, &mesh, &mut cache),
+ Err("cached WEAR failure".to_string())
+ );
+ }
+
+ #[test]
fn count_only_plan_uses_graph_requests() {
let graph = PrototypeGraph::default();
diff --git a/docs/tomes/05-render.md b/docs/tomes/05-render.md
index d6c9953..b647f19 100644
--- a/docs/tomes/05-render.md
+++ b/docs/tomes/05-render.md
@@ -1151,6 +1151,17 @@ the measured unfinished interval for this run. It still does not allocate that
time among MAT0 decode, graph allocation, archive I/O, OS cache state, or the
interleaved TEXM requests.
+Visual expansion now also caches WEAR validation by the complete derived WEAR
+archive/name key and replays both successes and failures. It still creates each
+per-prototype WEAR edge, increments the same request counters, and reports the
+same failure provenance. A unit test verifies an archive-qualified cached
+failure is replayed without consulting the repository. In a matched bounded
+Part 2 probe the first TEXM-64 marker occurred at 135,520 ms, versus 134,793
+ms in the preceding sample, and neither run reached `Assets`; therefore this
+corpus does not evidence a startup advance from WEAR caching. The path is kept
+as a correctness-preserving duplicate-resolve optimization, not a causal
+performance claim.
+
A rebuilt executable then ran a controlled 180-second Part 2 `Autodemo.00`
probe with the append-only trace. Before exact-child termination it recorded
`GraphVisualTextureRequests(64)`, `GraphVisualMaterialRequests(100)`, and every