Benchmark Rust against pinned C# reference (#105)
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m8s
Native code generation / deterministic (push) Failing after 2m6s
Concurrency and resource soak audit / soak (push) Failing after 12m13s
Documentation / documentation (push) Failing after 1m36s
Imaging and meshing gate / native (push) Failing after 3m2s
JPEG 2000 feature / linux (push) Successful in 2m48s
performance evidence / audit (push) Failing after 13m49s
Release platform and feature matrix / audit (push) Successful in 44s
Native Rust workspace compile / compile (push) Failing after 55s
Skia feature / linux (push) Successful in 31m13s
Dependency and supply-chain audit / audit (push) Failing after 9m13s
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Failing after 9m49s
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m8s
Native code generation / deterministic (push) Failing after 2m6s
Concurrency and resource soak audit / soak (push) Failing after 12m13s
Documentation / documentation (push) Failing after 1m36s
Imaging and meshing gate / native (push) Failing after 3m2s
JPEG 2000 feature / linux (push) Successful in 2m48s
performance evidence / audit (push) Failing after 13m49s
Release platform and feature matrix / audit (push) Successful in 44s
Native Rust workspace compile / compile (push) Failing after 55s
Skia feature / linux (push) Successful in 31m13s
Dependency and supply-chain audit / audit (push) Failing after 9m13s
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Failing after 9m49s
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
This commit is contained in:
@@ -1706,6 +1706,44 @@ impl Inventory {
|
||||
let parent_uuid = value.base().parent_uuid;
|
||||
let event = {
|
||||
let mut state = write(&self.inner.state);
|
||||
// Root-level records have no hierarchy edges or ancestor counts to
|
||||
// rebuild. They are common during the initial flat inventory feed
|
||||
// and for incremental item refreshes, so update their node and link
|
||||
// index directly instead of walking and relocking the entire store.
|
||||
// The general path below remains responsible for moves, folders,
|
||||
// cycles, placeholder parents, and descendant counts.
|
||||
let old_root_value = state
|
||||
.items
|
||||
.get(&uuid)
|
||||
.and_then(InventoryNode::value)
|
||||
.filter(|old| old.base().parent_uuid == UUID::zero());
|
||||
if parent_uuid == UUID::zero()
|
||||
&& (old_root_value.is_some() || !state.items.contains_key(&uuid))
|
||||
{
|
||||
if let Some(old) = old_root_value.as_ref() {
|
||||
update_link_index(&mut state.links, uuid, old, false);
|
||||
}
|
||||
update_link_index(&mut state.links, uuid, &value, true);
|
||||
let event = if let Some(node) = state.items.get(&uuid) {
|
||||
let old = node.value();
|
||||
node.set_value(value.clone());
|
||||
old.map(|old| InventoryObjectUpdatedEventArgs::from_values(old, value.clone()))
|
||||
} else {
|
||||
state
|
||||
.items
|
||||
.insert(uuid, InventoryNode::from_value(value.clone()));
|
||||
None
|
||||
};
|
||||
drop(state);
|
||||
if let Some(event) = event {
|
||||
self.inner.updated.emit(event);
|
||||
} else {
|
||||
self.inner
|
||||
.added
|
||||
.emit(InventoryObjectAddedEventArgs::from_value(value));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if parent_uuid != UUID::zero() && !state.items.contains_key(&parent_uuid) {
|
||||
let mut fake = InventoryFolder::new(parent_uuid)?;
|
||||
fake.version = InventoryFolder::VERSION_UNKNOWN;
|
||||
@@ -1865,6 +1903,28 @@ impl Inventory {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_link_index(
|
||||
links: &mut HashMap<UUID, HashSet<UUID>>,
|
||||
uuid: UUID,
|
||||
value: &InventoryValue,
|
||||
insert: bool,
|
||||
) {
|
||||
let Some(item) = value.item() else {
|
||||
return;
|
||||
};
|
||||
if !item.is_link().unwrap_or(false) || item.asset_uuid == UUID::zero() {
|
||||
return;
|
||||
}
|
||||
if insert {
|
||||
links.entry(item.asset_uuid).or_default().insert(uuid);
|
||||
} else if let Some(records) = links.get_mut(&item.asset_uuid) {
|
||||
records.remove(&uuid);
|
||||
if records.is_empty() {
|
||||
links.remove(&item.asset_uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rebuild_indexes_and_counts(state: &mut InventoryState) {
|
||||
state.links.clear();
|
||||
let nodes: Vec<_> = state.items.values().cloned().collect();
|
||||
@@ -2944,6 +3004,29 @@ mod tests {
|
||||
assert_eq!(calls.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_item_fast_path_keeps_link_index_and_events_consistent() {
|
||||
let (_client, inventory) = fixture();
|
||||
let item_id = UUID::random().unwrap();
|
||||
let first_target = UUID::random().unwrap();
|
||||
let second_target = UUID::random().unwrap();
|
||||
let mut item = InventoryItem::new_with_uuid(item_id).unwrap();
|
||||
item.set_asset_type(AssetType::Link);
|
||||
item.set_asset_uuid(first_target);
|
||||
inventory.update_node_for(&item).unwrap();
|
||||
assert_eq!(inventory.find_all_links(first_target).unwrap().len(), 1);
|
||||
|
||||
item.set_asset_uuid(second_target);
|
||||
inventory.update_node_for(&item).unwrap();
|
||||
assert!(inventory.find_all_links(first_target).unwrap().is_empty());
|
||||
assert_eq!(inventory.find_all_links(second_target).unwrap().len(), 1);
|
||||
|
||||
item.set_asset_type(AssetType::Texture);
|
||||
inventory.update_node_for(&item).unwrap();
|
||||
assert!(inventory.find_all_links(second_target).unwrap().is_empty());
|
||||
assert_eq!(inventory.count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_folders_sort_first_and_are_discoverable() {
|
||||
let (_client, mut inventory) = fixture();
|
||||
|
||||
@@ -11,6 +11,16 @@ type TgaPalette = (usize, Vec<[u8; 4]>);
|
||||
pub struct Targa;
|
||||
|
||||
impl Targa {
|
||||
/// Decodes an in-memory TGA or DDS payload without copying it through the
|
||||
/// compatibility stream boundary.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a typed error for oversized, malformed, or unsupported input.
|
||||
pub fn decode_to_managed_image_with_bytes(data: &[u8]) -> Result<ManagedImage, Error> {
|
||||
decode(data)
|
||||
}
|
||||
|
||||
/// Decodes a TGA or DDS file into planar managed-image storage.
|
||||
///
|
||||
/// # Errors
|
||||
@@ -185,6 +195,15 @@ fn decode_tga(bytes: &[u8]) -> Result<ManagedImage, Error> {
|
||||
return Err(parse(18, "truncated TGA image ID"));
|
||||
}
|
||||
let palette = read_tga_palette(bytes, header, has_color_map, color_mapped, &mut position)?;
|
||||
if !rle
|
||||
&& !grayscale
|
||||
&& palette.is_none()
|
||||
&& matches!(depth, 24 | 32)
|
||||
&& header[17] & 0x30 == 0x20
|
||||
{
|
||||
decode_plain_truecolor(bytes, position, pixels, pixel_bytes, depth, &mut image)?;
|
||||
return Ok(image);
|
||||
}
|
||||
let mut decoded = 0;
|
||||
while decoded < pixels {
|
||||
let (count, repeated) = if rle {
|
||||
@@ -238,6 +257,30 @@ fn decode_tga(bytes: &[u8]) -> Result<ManagedImage, Error> {
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
fn decode_plain_truecolor(
|
||||
bytes: &[u8],
|
||||
position: usize,
|
||||
pixels: usize,
|
||||
pixel_bytes: usize,
|
||||
depth: u8,
|
||||
image: &mut ManagedImage,
|
||||
) -> Result<(), Error> {
|
||||
let byte_length = pixels.checked_mul(pixel_bytes).ok_or(Error::Argument)?;
|
||||
let end = position.checked_add(byte_length).ok_or(Error::Argument)?;
|
||||
let data = bytes
|
||||
.get(position..end)
|
||||
.ok_or_else(|| parse(position, "truncated TGA pixel data"))?;
|
||||
for (target, pixel) in data.chunks_exact(pixel_bytes).enumerate() {
|
||||
image.blue[target] = pixel[0];
|
||||
image.green[target] = pixel[1];
|
||||
image.red[target] = pixel[2];
|
||||
if depth == 32 {
|
||||
image.alpha[target] = pixel[3];
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn tga_channels(
|
||||
grayscale: bool,
|
||||
color_mapped: bool,
|
||||
|
||||
44
crates/libremetaverse/tests/performance_regressions.rs
Normal file
44
crates/libremetaverse/tests/performance_regressions.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use libremetaverse::imaging::Targa;
|
||||
use libremetaverse::{GridClient, Inventory, InventoryItem};
|
||||
use libremetaverse_types::{AssetType, UUID};
|
||||
|
||||
#[test]
|
||||
fn root_inventory_updates_preserve_link_index() {
|
||||
let client = Arc::new(GridClient::new().expect("offline client"));
|
||||
let inventory = Inventory::new_with_grid_client_uuid(client, UUID::zero()).expect("store");
|
||||
let item_id = UUID::new_with_u_int64(1).expect("item UUID");
|
||||
let first_target = UUID::new_with_u_int64(2).expect("first target");
|
||||
let second_target = UUID::new_with_u_int64(3).expect("second target");
|
||||
let mut item = InventoryItem::new_with_uuid(item_id).expect("item");
|
||||
item.set_asset_type(AssetType::Link);
|
||||
item.set_asset_uuid(first_target);
|
||||
inventory.update_node_for(&item).expect("insert root link");
|
||||
assert_eq!(inventory.find_all_links(first_target).unwrap().len(), 1);
|
||||
|
||||
item.set_asset_uuid(second_target);
|
||||
inventory
|
||||
.update_node_for(&item)
|
||||
.expect("retarget root link");
|
||||
assert!(inventory.find_all_links(first_target).unwrap().is_empty());
|
||||
assert_eq!(inventory.find_all_links(second_target).unwrap().len(), 1);
|
||||
|
||||
item.set_asset_type(AssetType::Texture);
|
||||
inventory.update_node_for(&item).expect("replace link");
|
||||
assert!(inventory.find_all_links(second_target).unwrap().is_empty());
|
||||
assert_eq!(inventory.count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn borrowed_tga_fast_path_preserves_fixture_pixels() {
|
||||
let bytes = include_bytes!("../../../benchmarks/fixtures/image.tga");
|
||||
let image = Targa::decode_to_managed_image_with_bytes(bytes).expect("decode TGA fixture");
|
||||
assert_eq!((image.width, image.height), (128, 128));
|
||||
assert_eq!(image.red.len(), 128 * 128);
|
||||
assert_eq!(
|
||||
(&image.red[..4], &image.green[..4], &image.blue[..4]),
|
||||
(&[0, 1, 2, 3][..], &[0, 3, 6, 9][..], &[0, 7, 14, 21][..])
|
||||
);
|
||||
assert!(image.alpha.iter().all(|value| *value == u8::MAX));
|
||||
}
|
||||
Reference in New Issue
Block a user