perf(assets): reuse textures during mission preparation
This commit is contained in:
@@ -693,6 +693,7 @@ fn prepare_mission_assets_with_repository_internal<R: ResourceRepository>(
|
|||||||
let mut textures = Vec::new();
|
let mut textures = Vec::new();
|
||||||
let mut visuals = Vec::new();
|
let mut visuals = Vec::new();
|
||||||
let mut prototype_visual_ids = Vec::with_capacity(prototypes.len());
|
let mut prototype_visual_ids = Vec::with_capacity(prototypes.len());
|
||||||
|
let mut texture_cache = TexturePreparationCache::default();
|
||||||
|
|
||||||
for proto in prototypes {
|
for proto in prototypes {
|
||||||
let visual_id = AssetId::new((identity_policy.visual)(proto));
|
let visual_id = AssetId::new((identity_policy.visual)(proto));
|
||||||
@@ -706,8 +707,12 @@ fn prepare_mission_assets_with_repository_internal<R: ResourceRepository>(
|
|||||||
Some(_) => {}
|
Some(_) => {}
|
||||||
None => {
|
None => {
|
||||||
visual_index_by_id.insert(visual_id, signature);
|
visual_index_by_id.insert(visual_id, signature);
|
||||||
let bundle =
|
let bundle = prepare_visual_with_repository_internal(
|
||||||
prepare_visual_with_repository_internal(repository, proto, identity_policy)?;
|
repository,
|
||||||
|
proto,
|
||||||
|
identity_policy,
|
||||||
|
&mut texture_cache,
|
||||||
|
)?;
|
||||||
if bundle.visual.id != visual_id {
|
if bundle.visual.id != visual_id {
|
||||||
// Defensive check. stable IDs are deterministic for the same inputs.
|
// Defensive check. stable IDs are deterministic for the same inputs.
|
||||||
return Err(AssetError::InvalidPrototype(
|
return Err(AssetError::InvalidPrototype(
|
||||||
@@ -1380,10 +1385,14 @@ pub fn prepare_visual_with_repository<R: ResourceRepository>(
|
|||||||
repository: &R,
|
repository: &R,
|
||||||
proto: &EffectivePrototype,
|
proto: &EffectivePrototype,
|
||||||
) -> Result<PreparedVisual, AssetError> {
|
) -> Result<PreparedVisual, AssetError> {
|
||||||
Ok(
|
let mut texture_cache = TexturePreparationCache::default();
|
||||||
prepare_visual_with_repository_internal(repository, proto, AssetIdentityPolicy::default())?
|
Ok(prepare_visual_with_repository_internal(
|
||||||
.visual,
|
repository,
|
||||||
)
|
proto,
|
||||||
|
AssetIdentityPolicy::default(),
|
||||||
|
&mut texture_cache,
|
||||||
|
)?
|
||||||
|
.visual)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PreparedVisualBundle {
|
struct PreparedVisualBundle {
|
||||||
@@ -1394,12 +1403,19 @@ struct PreparedVisualBundle {
|
|||||||
textures: Vec<PreparedTexture>,
|
textures: Vec<PreparedTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct TexturePreparationCache {
|
||||||
|
diffuse: HashMap<Vec<u8>, PreparedTexture>,
|
||||||
|
lightmaps: HashMap<Vec<u8>, PreparedTexture>,
|
||||||
|
}
|
||||||
|
|
||||||
// Keeps the complete model -> WEAR -> material -> texture transaction in one fallible boundary.
|
// Keeps the complete model -> WEAR -> material -> texture transaction in one fallible boundary.
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
fn prepare_visual_with_repository_internal<R: ResourceRepository>(
|
fn prepare_visual_with_repository_internal<R: ResourceRepository>(
|
||||||
repository: &R,
|
repository: &R,
|
||||||
proto: &EffectivePrototype,
|
proto: &EffectivePrototype,
|
||||||
identity_policy: AssetIdentityPolicy,
|
identity_policy: AssetIdentityPolicy,
|
||||||
|
texture_cache: &mut TexturePreparationCache,
|
||||||
) -> Result<PreparedVisualBundle, AssetError> {
|
) -> Result<PreparedVisualBundle, AssetError> {
|
||||||
let PrototypeGeometry::Mesh(mesh_key) = &proto.geometry else {
|
let PrototypeGeometry::Mesh(mesh_key) = &proto.geometry else {
|
||||||
return Ok(PreparedVisualBundle {
|
return Ok(PreparedVisualBundle {
|
||||||
@@ -1483,11 +1499,12 @@ fn prepare_visual_with_repository_internal<R: ResourceRepository>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
for texture in texture_requests {
|
for texture in texture_requests {
|
||||||
let prepared_texture = prepare_texture(
|
let prepared_texture = prepare_texture_cached(
|
||||||
repository,
|
repository,
|
||||||
&texture,
|
&texture,
|
||||||
PreparedTextureUsage::Diffuse,
|
PreparedTextureUsage::Diffuse,
|
||||||
identity_policy,
|
identity_policy,
|
||||||
|
texture_cache,
|
||||||
)?;
|
)?;
|
||||||
texture_ids.push(prepared_texture.id);
|
texture_ids.push(prepared_texture.id);
|
||||||
prepared_textures.push(prepared_texture);
|
prepared_textures.push(prepared_texture);
|
||||||
@@ -1496,11 +1513,12 @@ fn prepare_visual_with_repository_internal<R: ResourceRepository>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for lightmap in &wear.lightmaps {
|
for lightmap in &wear.lightmaps {
|
||||||
let prepared_lightmap = prepare_texture(
|
let prepared_lightmap = prepare_texture_cached(
|
||||||
repository,
|
repository,
|
||||||
&lightmap.lightmap,
|
&lightmap.lightmap,
|
||||||
PreparedTextureUsage::Lightmap,
|
PreparedTextureUsage::Lightmap,
|
||||||
identity_policy,
|
identity_policy,
|
||||||
|
texture_cache,
|
||||||
)?;
|
)?;
|
||||||
lightmap_ids.push(prepared_lightmap.id);
|
lightmap_ids.push(prepared_lightmap.id);
|
||||||
prepared_textures.push(prepared_lightmap);
|
prepared_textures.push(prepared_lightmap);
|
||||||
@@ -1785,6 +1803,25 @@ fn prepare_texture<R: ResourceRepository>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepare_texture_cached<R: ResourceRepository>(
|
||||||
|
repository: &R,
|
||||||
|
name: &ResourceName,
|
||||||
|
usage: PreparedTextureUsage,
|
||||||
|
identity_policy: AssetIdentityPolicy,
|
||||||
|
cache: &mut TexturePreparationCache,
|
||||||
|
) -> Result<PreparedTexture, AssetError> {
|
||||||
|
let entries = match usage {
|
||||||
|
PreparedTextureUsage::Diffuse => &mut cache.diffuse,
|
||||||
|
PreparedTextureUsage::Lightmap => &mut cache.lightmaps,
|
||||||
|
};
|
||||||
|
if let Some(texture) = entries.get(&name.0) {
|
||||||
|
return Ok(texture.clone());
|
||||||
|
}
|
||||||
|
let texture = prepare_texture(repository, name, usage, identity_policy)?;
|
||||||
|
entries.insert(name.0.clone(), texture.clone());
|
||||||
|
Ok(texture)
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_texm<R: ResourceRepository>(
|
fn resolve_texm<R: ResourceRepository>(
|
||||||
repository: &R,
|
repository: &R,
|
||||||
name: &ResourceName,
|
name: &ResourceName,
|
||||||
@@ -2425,6 +2462,43 @@ mod tests {
|
|||||||
assert_eq!(visual.lightmap_ids.len(), 1);
|
assert_eq!(visual.lightmap_ids.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn texture_preparation_cache_reuses_exact_request_within_one_mission() {
|
||||||
|
let texm = texm_payload();
|
||||||
|
let repo = repository_with_archives_meta(&[(
|
||||||
|
TEXTURES_ARCHIVE,
|
||||||
|
&[TestNresEntry {
|
||||||
|
name: b"TEX_A",
|
||||||
|
payload: &texm,
|
||||||
|
type_id: 0,
|
||||||
|
attr2: 0,
|
||||||
|
}],
|
||||||
|
)]);
|
||||||
|
let name = resource_name(b"TEX_A");
|
||||||
|
let mut cache = TexturePreparationCache::default();
|
||||||
|
|
||||||
|
let first = prepare_texture_cached(
|
||||||
|
&repo,
|
||||||
|
&name,
|
||||||
|
PreparedTextureUsage::Diffuse,
|
||||||
|
AssetIdentityPolicy::default(),
|
||||||
|
&mut cache,
|
||||||
|
)
|
||||||
|
.expect("first texture");
|
||||||
|
let second = prepare_texture_cached(
|
||||||
|
&repo,
|
||||||
|
&name,
|
||||||
|
PreparedTextureUsage::Diffuse,
|
||||||
|
AssetIdentityPolicy::default(),
|
||||||
|
&mut cache,
|
||||||
|
)
|
||||||
|
.expect("cached texture");
|
||||||
|
|
||||||
|
assert_eq!(first.id, second.id);
|
||||||
|
assert_eq!(cache.diffuse.len(), 1);
|
||||||
|
assert!(cache.lightmaps.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forced_model_id_collision_is_rejected() {
|
fn forced_model_id_collision_is_rejected() {
|
||||||
assert_forced_collision(
|
assert_forced_collision(
|
||||||
|
|||||||
Reference in New Issue
Block a user