Skip to content

intersect_ray() leaks when reusing returned Dictionary #2048

Description

@kessleyd

Godot version

v4.7.stable.official (5b4e0cb0f)

godot-cpp version

9c8aeff

System information

Windows 11 (build 26200) - Multi-window, 2 monitors - Direct3D 12 (Forward+) - dedicated NVIDIA GeForce RTX 4070 SUPER (NVIDIA; 32.0.16.1047) - 12th Gen Intel(R) Core(TM) i7-12700K (20 threads) - 31.77 GiB memory - WASAPI (48000 Hz, Stereo/mono)

Issue description

When reusing the returned Dictionary from PhysicsDirectSpaceState3D::intersect_ray() for repeated intersect_ray calls there is a notable memory leak that does not get cleaned up until restarting the editor or project. This leak can occur both in the editor and in the export project.

I would expect this to work similar to other Variant types and unreference the reserved memory when reassigned.

leak.mp4

Steps to reproduce

I have included a MRP. To reproduce in the MRP:

  • open the raycast_leak_test.tscn scene. Open task manager to monitor the godot memory usage.
  • Add a RaycastLeaker node to the scene. You should be able to observe the memory usage of the godot process climbing.
  • Remove the RaycastLeaker node. You should see the climbing stop, but the memory usage does not return to its original value.

I have also included a version of the node that does not leak by simply declaring a new dictionary for every intersect_ray call.

  • Add a RaycastNoLeak node to the scene. You should see the memory usage of the engine remain stable.

The leak can also be reproduces by creating your own node with the following physics_process function, and setting up the scene so that the repeated raycasts hit a collision object.

// leaks
void _leaky_physics_process(double p_delta)
{
	PhysicsDirectSpaceState3D *the_space = get_world_3d()->get_direct_space_state();
	Ref<PhysicsRayQueryParameters3D> ray_query;
	ray_query.instantiate();
	ray_query->set_from(get_global_position());
	ray_query->set_to(get_global_position() + Vector3(0.0f, 0.0f, -1000.0f));
	Dictionary result = the_space->intersect_ray(ray_query);
	if(result.is_empty())
	{
		UtilityFunctions::print("Hit nothing");
		return;
	}
	Vector3 normal = result["normal"];
	for (int i=0; i<199; ++i)
	{
		result = the_space->intersect_ray(ray_query);
		if(result.is_empty())
		{
			UtilityFunctions::print("Hit nothing");
			return;
		}
		normal = result["normal"];
	}
};

// doesn't leak
void _good_physics_process(double p_delta)
{
	PhysicsDirectSpaceState3D *the_space = get_world_3d()->get_direct_space_state();
	Ref<PhysicsRayQueryParameters3D> ray_query;
	ray_query.instantiate();
	ray_query->set_from(get_global_position());
	ray_query->set_to(get_global_position() + Vector3(0.0f, 0.0f, -1000.0f));
	Dictionary result = the_space->intersect_ray(ray_query);
	if(result.is_empty())
	{
		UtilityFunctions::print("Hit nothing");
		return;
	}
	Vector3 normal = result["normal"];
	for (int i=0; i<199; ++i)
	{
		Dictionary result2 = the_space->intersect_ray(ray_query);
		if(result.is_empty())
		{
			UtilityFunctions::print("Hit nothing");
			return;
		}
		normal = result2["normal"];
	}
};

Minimal reproduction project

leaky_project.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions