Skip to content

Commit 44ebcf8

Browse files
DJMcNabcart
authored andcommitted
Faster gltf loader (#3165)
# Objective - @superdump was having trouble with this loop in the GLTF loader. ## Solution - Make it probably linear. - Measured times: - Old: 40s, new: 200ms I'm sure there's still room for improvement. For example, I think making the nodes be in `Arc`s could be a significant gain, since currently there's duplication all the way down the tree. Co-authored-by: Carter Anderson <[email protected]>
1 parent 0bf90bb commit 44ebcf8

File tree

3 files changed

+94
-66
lines changed

3 files changed

+94
-66
lines changed

pipelined/bevy_gltf2/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ bevy_pbr2 = { path = "../bevy_pbr2", version = "0.5.0" }
2222
bevy_reflect = { path = "../../crates/bevy_reflect", version = "0.5.0", features = ["bevy"] }
2323
bevy_render2 = { path = "../bevy_render2", version = "0.5.0" }
2424
bevy_transform = { path = "../../crates/bevy_transform", version = "0.5.0" }
25+
bevy_utils = { path = "../../crates/bevy_utils", version = "0.5.0" }
2526
bevy_math = { path = "../../crates/bevy_math", version = "0.5.0" }
2627
bevy_scene = { path = "../../crates/bevy_scene", version = "0.5.0" }
2728
bevy_log = { path = "../../crates/bevy_log", version = "0.5.0" }

pipelined/bevy_gltf2/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use bevy_utils::HashMap;
22

33
mod loader;
44
pub use loader::*;

pipelined/bevy_gltf2/src/loader.rs

Lines changed: 92 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,13 @@ use bevy_transform::{
2121
hierarchy::{BuildWorldChildren, WorldChildBuilder},
2222
prelude::{GlobalTransform, Transform},
2323
};
24+
use bevy_utils::{HashMap, HashSet};
2425
use gltf::{
2526
mesh::Mode,
2627
texture::{MagFilter, MinFilter, WrappingMode},
2728
Material, Primitive,
2829
};
29-
use std::{
30-
collections::{HashMap, HashSet},
31-
path::Path,
32-
};
30+
use std::{collections::VecDeque, path::Path};
3331
use thiserror::Error;
3432
use wgpu::{AddressMode, FilterMode, PrimitiveTopology, SamplerDescriptor, TextureFormat};
3533

@@ -83,8 +81,8 @@ async fn load_gltf<'a, 'b>(
8381
let buffer_data = load_buffers(&gltf, load_context, load_context.path()).await?;
8482

8583
let mut materials = vec![];
86-
let mut named_materials = HashMap::new();
87-
let mut linear_textures = HashSet::new();
84+
let mut named_materials = HashMap::default();
85+
let mut linear_textures = HashSet::default();
8886
for material in gltf.materials() {
8987
let handle = load_material(&material, load_context);
9088
if let Some(name) = material.name() {
@@ -106,7 +104,7 @@ async fn load_gltf<'a, 'b>(
106104
}
107105

108106
let mut meshes = vec![];
109-
let mut named_meshes = HashMap::new();
107+
let mut named_meshes = HashMap::default();
110108
for mesh in gltf.meshes() {
111109
let mut primitives = vec![];
112110
for primitive in mesh.primitives() {
@@ -195,7 +193,7 @@ async fn load_gltf<'a, 'b>(
195193
}
196194

197195
let mut nodes_intermediate = vec![];
198-
let mut named_nodes_intermediate = HashMap::new();
196+
let mut named_nodes_intermediate = HashMap::default();
199197
for node in gltf.nodes() {
200198
let node_label = node_label(&node);
201199
nodes_intermediate.push((
@@ -229,7 +227,7 @@ async fn load_gltf<'a, 'b>(
229227
named_nodes_intermediate.insert(name, node.index());
230228
}
231229
}
232-
let nodes = resolve_node_hierarchy(nodes_intermediate)
230+
let nodes = resolve_node_hierarchy(nodes_intermediate, load_context.path())
233231
.into_iter()
234232
.map(|(label, node)| load_context.set_labeled_asset(&label, LoadedAsset::new(node)))
235233
.collect::<Vec<bevy_asset::Handle<GltfNode>>>();
@@ -275,7 +273,7 @@ async fn load_gltf<'a, 'b>(
275273
});
276274

277275
let mut scenes = vec![];
278-
let mut named_scenes = HashMap::new();
276+
let mut named_scenes = HashMap::default();
279277
for scene in gltf.scenes() {
280278
let mut err = None;
281279
let mut world = World::default();
@@ -716,42 +714,51 @@ async fn load_buffers(
716714

717715
fn resolve_node_hierarchy(
718716
nodes_intermediate: Vec<(String, GltfNode, Vec<usize>)>,
717+
asset_path: &Path,
719718
) -> Vec<(String, GltfNode)> {
720-
let mut max_steps = nodes_intermediate.len();
721-
let mut nodes_step = nodes_intermediate
719+
let mut has_errored = false;
720+
let mut empty_children = VecDeque::new();
721+
let mut parents = vec![None; nodes_intermediate.len()];
722+
let mut unprocessed_nodes = nodes_intermediate
722723
.into_iter()
723724
.enumerate()
724-
.map(|(i, (label, node, children))| (i, label, node, children))
725-
.collect::<Vec<_>>();
726-
let mut nodes = std::collections::HashMap::<usize, (String, GltfNode)>::new();
727-
while max_steps > 0 && !nodes_step.is_empty() {
728-
if let Some((index, label, node, _)) = nodes_step
729-
.iter()
730-
.find(|(_, _, _, children)| children.is_empty())
731-
.cloned()
732-
{
733-
nodes.insert(index, (label, node));
734-
for (_, _, node, children) in nodes_step.iter_mut() {
735-
if let Some((i, _)) = children
736-
.iter()
737-
.enumerate()
738-
.find(|(_, child_index)| **child_index == index)
739-
{
740-
children.remove(i);
741-
742-
if let Some((_, child_node)) = nodes.get(&index) {
743-
node.children.push(child_node.clone())
744-
}
725+
.map(|(i, (label, node, children))| {
726+
for child in children.iter() {
727+
if let Some(parent) = parents.get_mut(*child) {
728+
*parent = Some(i);
729+
} else if !has_errored {
730+
has_errored = true;
731+
warn!("Unexpected child in GLTF Mesh {}", child);
745732
}
746733
}
747-
nodes_step = nodes_step
748-
.into_iter()
749-
.filter(|(i, _, _, _)| *i != index)
750-
.collect()
734+
let children = children.into_iter().collect::<HashSet<_>>();
735+
if children.is_empty() {
736+
empty_children.push_back(i);
737+
}
738+
(i, (label, node, children))
739+
})
740+
.collect::<HashMap<_, _>>();
741+
let mut nodes = std::collections::HashMap::<usize, (String, GltfNode)>::new();
742+
while let Some(index) = empty_children.pop_front() {
743+
let (label, node, children) = unprocessed_nodes.remove(&index).unwrap();
744+
assert!(children.is_empty());
745+
nodes.insert(index, (label, node));
746+
if let Some(parent_index) = parents[index] {
747+
let (_, parent_node, parent_children) =
748+
unprocessed_nodes.get_mut(&parent_index).unwrap();
749+
750+
assert!(parent_children.remove(&index));
751+
if let Some((_, child_node)) = nodes.get(&index) {
752+
parent_node.children.push(child_node.clone())
753+
}
754+
if parent_children.is_empty() {
755+
empty_children.push_back(parent_index);
756+
}
751757
}
752-
max_steps -= 1;
753758
}
754-
759+
if !unprocessed_nodes.is_empty() {
760+
warn!("GLTF model must be a tree: {:?}", asset_path);
761+
}
755762
let mut nodes_to_sort = nodes.into_iter().collect::<Vec<_>>();
756763
nodes_to_sort.sort_by_key(|(i, _)| *i);
757764
nodes_to_sort
@@ -799,6 +806,8 @@ impl<'a> DataUri<'a> {
799806

800807
#[cfg(test)]
801808
mod test {
809+
use std::path::PathBuf;
810+
802811
use super::resolve_node_hierarchy;
803812
use crate::GltfNode;
804813

@@ -813,7 +822,10 @@ mod test {
813822
}
814823
#[test]
815824
fn node_hierarchy_single_node() {
816-
let result = resolve_node_hierarchy(vec![("l1".to_string(), GltfNode::empty(), vec![])]);
825+
let result = resolve_node_hierarchy(
826+
vec![("l1".to_string(), GltfNode::empty(), vec![])],
827+
PathBuf::new().as_path(),
828+
);
817829

818830
assert_eq!(result.len(), 1);
819831
assert_eq!(result[0].0, "l1");
@@ -822,10 +834,13 @@ mod test {
822834

823835
#[test]
824836
fn node_hierarchy_no_hierarchy() {
825-
let result = resolve_node_hierarchy(vec![
826-
("l1".to_string(), GltfNode::empty(), vec![]),
827-
("l2".to_string(), GltfNode::empty(), vec![]),
828-
]);
837+
let result = resolve_node_hierarchy(
838+
vec![
839+
("l1".to_string(), GltfNode::empty(), vec![]),
840+
("l2".to_string(), GltfNode::empty(), vec![]),
841+
],
842+
PathBuf::new().as_path(),
843+
);
829844

830845
assert_eq!(result.len(), 2);
831846
assert_eq!(result[0].0, "l1");
@@ -836,10 +851,13 @@ mod test {
836851

837852
#[test]
838853
fn node_hierarchy_simple_hierarchy() {
839-
let result = resolve_node_hierarchy(vec![
840-
("l1".to_string(), GltfNode::empty(), vec![1]),
841-
("l2".to_string(), GltfNode::empty(), vec![]),
842-
]);
854+
let result = resolve_node_hierarchy(
855+
vec![
856+
("l1".to_string(), GltfNode::empty(), vec![1]),
857+
("l2".to_string(), GltfNode::empty(), vec![]),
858+
],
859+
PathBuf::new().as_path(),
860+
);
843861

844862
assert_eq!(result.len(), 2);
845863
assert_eq!(result[0].0, "l1");
@@ -850,15 +868,18 @@ mod test {
850868

851869
#[test]
852870
fn node_hierarchy_hierarchy() {
853-
let result = resolve_node_hierarchy(vec![
854-
("l1".to_string(), GltfNode::empty(), vec![1]),
855-
("l2".to_string(), GltfNode::empty(), vec![2]),
856-
("l3".to_string(), GltfNode::empty(), vec![3, 4, 5]),
857-
("l4".to_string(), GltfNode::empty(), vec![6]),
858-
("l5".to_string(), GltfNode::empty(), vec![]),
859-
("l6".to_string(), GltfNode::empty(), vec![]),
860-
("l7".to_string(), GltfNode::empty(), vec![]),
861-
]);
871+
let result = resolve_node_hierarchy(
872+
vec![
873+
("l1".to_string(), GltfNode::empty(), vec![1]),
874+
("l2".to_string(), GltfNode::empty(), vec![2]),
875+
("l3".to_string(), GltfNode::empty(), vec![3, 4, 5]),
876+
("l4".to_string(), GltfNode::empty(), vec![6]),
877+
("l5".to_string(), GltfNode::empty(), vec![]),
878+
("l6".to_string(), GltfNode::empty(), vec![]),
879+
("l7".to_string(), GltfNode::empty(), vec![]),
880+
],
881+
PathBuf::new().as_path(),
882+
);
862883

863884
assert_eq!(result.len(), 7);
864885
assert_eq!(result[0].0, "l1");
@@ -879,20 +900,26 @@ mod test {
879900

880901
#[test]
881902
fn node_hierarchy_cyclic() {
882-
let result = resolve_node_hierarchy(vec![
883-
("l1".to_string(), GltfNode::empty(), vec![1]),
884-
("l2".to_string(), GltfNode::empty(), vec![0]),
885-
]);
903+
let result = resolve_node_hierarchy(
904+
vec![
905+
("l1".to_string(), GltfNode::empty(), vec![1]),
906+
("l2".to_string(), GltfNode::empty(), vec![0]),
907+
],
908+
PathBuf::new().as_path(),
909+
);
886910

887911
assert_eq!(result.len(), 0);
888912
}
889913

890914
#[test]
891915
fn node_hierarchy_missing_node() {
892-
let result = resolve_node_hierarchy(vec![
893-
("l1".to_string(), GltfNode::empty(), vec![2]),
894-
("l2".to_string(), GltfNode::empty(), vec![]),
895-
]);
916+
let result = resolve_node_hierarchy(
917+
vec![
918+
("l1".to_string(), GltfNode::empty(), vec![2]),
919+
("l2".to_string(), GltfNode::empty(), vec![]),
920+
],
921+
PathBuf::new().as_path(),
922+
);
896923

897924
assert_eq!(result.len(), 1);
898925
assert_eq!(result[0].0, "l2");

0 commit comments

Comments
 (0)