diff --git a/crates/fparkan-assets/src/lib.rs b/crates/fparkan-assets/src/lib.rs index e4a9b40..8f815e0 100644 --- a/crates/fparkan-assets/src/lib.rs +++ b/crates/fparkan-assets/src/lib.rs @@ -1064,6 +1064,8 @@ pub fn extend_graph_report_with_visual_dependencies( .map(|edge| edge.id.0) .max() .map_or(0, |value| value.saturating_add(1)); + let mut diffuse_texture_validation = HashMap::new(); + let mut lightmap_texture_validation = HashMap::new(); for (prototype_index, prototype) in prototypes.iter().enumerate() { let PrototypeGeometry::Mesh(mesh) = &prototype.geometry else { @@ -1159,7 +1161,13 @@ pub fn extend_graph_report_with_visual_dependencies( ); for texture in material.document.texture_requests() { report.texture_request_count += 1; - match resolve_texture(repository, &texture) { + match resolve_texm_validation_cached( + repository, + &texture, + TEXTURES_ARCHIVE, + "texture", + &mut diffuse_texture_validation, + ) { Ok(()) => { report.texture_resolved_count += 1; if let Ok(texture_key) = @@ -1187,7 +1195,6 @@ pub fn extend_graph_report_with_visual_dependencies( } } Err(message) => { - let message = message.to_string(); push_visual_failure( report, graph, @@ -1214,7 +1221,13 @@ pub fn extend_graph_report_with_visual_dependencies( } for lightmap in &table.lightmaps { report.lightmap_request_count += 1; - match resolve_lightmap(repository, &lightmap.lightmap) { + match resolve_texm_validation_cached( + repository, + &lightmap.lightmap, + LIGHTMAP_ARCHIVE, + "lightmap", + &mut lightmap_texture_validation, + ) { Ok(()) => { report.lightmap_resolved_count += 1; if let Ok(lightmap_key) = @@ -1242,7 +1255,6 @@ pub fn extend_graph_report_with_visual_dependencies( } } Err(message) => { - let message = message.to_string(); push_visual_failure( report, graph, @@ -1776,18 +1788,19 @@ fn mesh_dependency_resource( .and_then(|node| node.resource.as_ref()) } -fn resolve_texture( +fn resolve_texm_validation_cached( repository: &R, name: &ResourceName, -) -> Result<(), AssetError> { - resolve_texm(repository, name, TEXTURES_ARCHIVE, "texture") -} - -fn resolve_lightmap( - repository: &R, - name: &ResourceName, -) -> Result<(), AssetError> { - resolve_texm(repository, name, LIGHTMAP_ARCHIVE, "lightmap") + archive: &str, + label: &'static str, + cache: &mut HashMap, Result<(), String>>, +) -> Result<(), String> { + if let Some(result) = cache.get(&name.0) { + return result.clone(); + } + let result = resolve_texm(repository, name, archive, label).map_err(|error| error.to_string()); + cache.insert(name.0.clone(), result.clone()); + result } fn prepare_texture( diff --git a/docs/tomes/05-render.md b/docs/tomes/05-render.md index 98b1965..28a7b7c 100644 --- a/docs/tomes/05-render.md +++ b/docs/tomes/05-render.md @@ -1047,6 +1047,15 @@ graph expansion, rather than the base prototype graph, asset decoding, window creation or Vulkan submission. This is a diagnostic boundary, not a claim that the visual expansion is semantically optional or may be skipped. +Visual expansion now caches the validation result of each unique diffuse TEXM +and each unique lightmap TEXM independently. It still emits an edge and request +count for every original material reference, and it replays the same cached +success or failure at every reference; only duplicate archive open/read/decode +work is removed. Under the same controlled 60-second GOG first-root probe, the +last checkpoint advanced to `Assets`. The process was deliberately terminated +before a frame, so this proves reduced visual-graph work rather than a Vulkan +acceptance result. + The same source-axis proof now applies to `TerrainWorld`: its `Land.msh` surface height query and `Land.map` areal/grid lookup use XY ground coordinates, return source Z height, and leave raycasts as full 3D intersections. The Part 1 and