Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NavMesh handles are not stable #48

Open
indietyp opened this issue Aug 15, 2024 · 7 comments
Open

NavMesh handles are not stable #48

indietyp opened this issue Aug 15, 2024 · 7 comments

Comments

@indietyp
Copy link

I am currently using this crate for navmesh generation in a 2d top-down gam where I want to create a navmesh for every room, and associate some data with it.

After some debugging, I found out that it doesn't seem that the Handle to the navmesh doesn't seem to be persistent when updates occur, I found this by using the following example, when I do this with multiple navmeshes, the navmesh jumps around, even tho the entity is always the same, this means that you cannot attach any data, or component to it, as the handle might suddenly change between frames.

fn display_navmesh(
    live_navmeshes: Query<(Entity, &Handle<NavMesh>)>,
    mut gizmos: Gizmos<DebugGismos>,
    navmeshes: Res<Assets<NavMesh>>,
    controls: Option<Res<NavMeshesDebug>>,
	selected: Local<Option<Entity>>
) {
    let color = controls.as_ref().map_or(Color::Srgba(RED), |c| c.0);
	let mesh = if let Some(entity) = selected {
		live_navmeshes.get(entity).expect("selected navmesh should exist").1
	} else {
		let (entity, handle) = live_navmeshes.iter().next().expect("should have at least one live navmesh");
		*selected = Some(entity);
		handle
	};

    let Some(navmesh) = navmeshes.get(mesh) else {
        continue;
    };

    let transform = navmesh.transform();
    let navmesh = navmesh.get();

    let compute_matrix = transform.compute_matrix();

    for polygon in &navmesh.polygons {
        if polygon.vertices.is_empty() {
            continue;
        }

        let vertices = polygon
            .vertices
            .iter()
            .map(|i| &navmesh.vertices[*i as usize].coords)
            .map(|v| compute_matrix * Vec4::from((*v, 0.0, 1.0)))
            .map(Vec4Swizzles::xy);

        let first = polygon.vertices[0];
        let first = &navmesh.vertices[first as usize];

        let vertices = vertices.chain(iter::once(
            (compute_matrix * Vec4::from((first.coords, 0.0, 1.0))).xy(),
        ));

        gizmos.linestrip_2d(vertices, color);
    }
}
@indietyp
Copy link
Author

After some investigation, this seems to correlate to overlapping nav meshes / multiple navmeshes.

In a completely static scene with some colliders and overlapping nav meshes I get:

Built: 0
Building: 18
Failed: 0
Cancelled: 32
Built: 0
Building: 18
Failed: 0
Cancelled: 32

over and over again.

@indietyp
Copy link
Author

looking further into this, this seems to be an unrelated problem.

        #[cfg(feature = "avian2d")]
        {
            app.observe(crate::obstacles::avian2d::on_sleeping_inserted)
                .observe(crate::obstacles::avian2d::on_sleeping_removed);
        }

seems to cause an update loop.

@indietyp
Copy link
Author

looking at the debug output further, it seems like the same navmesh is associated with every Handle<NavMesh> instance at one point. I don't know why tho.

Logging a bit further, printing out the unique triangulation meshes, and actual meshes, I get:

unique meshes: 1
unique triangulations: 34
unique meshes: 1
unique triangulations: 34
unique meshes: 1
unique triangulations: 34

meaning the Arc<Mesh> is shared for every NavMesh even though they do have different triangulations. That seems... off.

@indietyp
Copy link
Author

I figured out the problem, it corresponds with the following line:

pub struct NavMeshBundle {
/// Settings for this navmesh updates.
pub settings: NavMeshSettings,
/// Status of the last navmesh update.
pub status: NavMeshStatus,
/// Handle to the navmesh.
pub handle: Handle<NavMesh>,
/// Transform of the navmesh. USed to transform point in 3d to 2d (by ignoring the `z` axis).
pub transform: Transform,
/// How to trigger navmesh updates.
pub update_mode: NavMeshUpdateMode,
}
impl Default for NavMeshBundle {
fn default() -> Self {
Self {
settings: NavMeshSettings::default(),
status: NavMeshStatus::Building,
handle: Default::default(),
transform: Default::default(),
update_mode: NavMeshUpdateMode::OnDemand(false),
}
}
}

in conjunction with:

navmeshes.insert(handle, navmesh);

because every NavMesh gets the same default handle, and it isn't checked that the Handle is not the default one, everyone just ends up sharing the same handle.

I fixed this by manually creating a handle with a Uuid asset id, that seems to work. Although the caching issue seems to persist.

@mockersf
Copy link
Member

mockersf commented Sep 6, 2024

Thanks for the investigation!

I removed the Default impl which was hiding that in #43 in favour of functions making it hopefully clearer when the navmesh will be reused or not (with_default_id and with_unique_id).

Could you give more details on the caching issue?

@indietyp
Copy link
Author

indietyp commented Sep 8, 2024

Thank you very much!

Sure thing! It seems like - and I haven't tracked down the issue further, but when using avian2d - the meshes are constantly rebuilt because some colliders are constantly re-categorized as cached and then again as not cached.

This leads to maxed out CPU cores and also to a memory leak - it seems like - on every rebuild - some memory is leaked - going from 400MiB to 4GiB in a matter of about a minute. I think this might be due to a Local in a system, but I am not sure. I wasn't sure if this was caused by my code, but looking at tracy's output, these allocations are coming from this crate - and sure enough - disabling mesh regeneration solved the memory usage getting bigger and bigger.

@mockersf
Copy link
Member

@indietyp I could not reproduce using the current main branch of this repo and the latest version of polyanya. Could you update and retry, or maybe share a reproducer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants