Godot version:
Last stable Godot 3.2.3, tested in C# Build
OS/device including version:
Windows 10
Issue description:
If GetSurfaceMaterial() is called anytime it causes a VRAM memory leak when the object is freed. It happens in all free calls like QueueFree(), QueueFree() of parents and even scene changes.
To get the material of objects that have large textures is currently impossible without cause huge memory leaks (based on textures used by it). It's a critical bug and i could not find any workaround to fix it.
Steps to reproduce:
Do in any object:
GetNode<MeshInstance>("MeshInstance").QueueFree();
Check the VRAM Profiler. it released the VRAM.
Now do it:
SpatialMaterial mat = GetNode<MeshInstance>("MeshInstance").GetSurfaceMaterial(0) as SpatialMaterial;
GetNode<MeshInstance>("MeshInstance").QueueFree();
Check the Memory Graph. It do not free anything. VRAM is yet been used.
Or just use the sample project below to test.
Minimal reproduction project:
BugMemory.zip
Based on the description here, I am not sure there is a bug.
SpatialMaterial mat = GetNode("MeshInstance").GetSurfaceMaterial(0) as SpatialMaterial;
creates a reference to the material so when you call:
GetNode("MeshInstance").QueueFree();
The MeshInstance is freed, but the material is not freed because you still have a reference to it in mat. The material can't be freed until there are no references to it.
Looking at the example project:
public class Teste : Node
{
[Export] public bool getMaterial;
public override void _Input(InputEvent inputEvent)
{
if (inputEvent is InputEventKey keyEvent && keyEvent.Pressed)
{
if ((KeyList)keyEvent.Scancode == KeyList.Space)
{
if(getMaterial){
SpatialMaterial mat = GetNode<MeshInstance>("MeshInstance").GetSurfaceMaterial(0) as SpatialMaterial;
}
GetNode<MeshInstance>("MeshInstance").QueueFree();
}
}
}
}
My interpretation is that although mat should have a reference, afaik that reference should be released at the end of the function, it mat is on the stack, so that when the deferred QueueFree calls the ref count should be able to reach zero.
Does this also occur in gdscript? Could this be a c# specific issue?
It works in gdscript. I haven't tested the c#, but assuming the report is correct this could be a bug in the godot c# side.
gdscript project, press return to delete the object:
BugMemory2.zip
"The MeshInstance is freed, but the material is not freed because you still have a reference to it in mat. The material can't be freed until there are no references to it."
Tested without envolve any local variable only a print and the result is the same. My suspect is that GetSurfaceMaterial creates a reference count that is never released. No variable need to be envolved. This also causes the same bug:
GD.Print(GetNode<MeshInstance>("MeshInstance").GetSurfaceMaterial(0) as SpatialMaterial);
It works in gdscript. I haven't tested the c#, but assuming the report is correct this could be a bug in the godot c# side.
This was unexpected. You're right. I tested it now and works as should in GDScript. This is my code
extends Node
export var getMat = true;
func _input(event):
if event is InputEventKey and event.pressed:
if event.scancode == KEY_SPACE:
if getMat:
var a = get_node("MeshInstance").get_surface_material(0)
print(a);
get_node("MeshInstance").queue_free()
Any possible workaround in C# until a proper fix?
Most helpful comment
"The MeshInstance is freed, but the material is not freed because you still have a reference to it in mat. The material can't be freed until there are no references to it."
Tested without envolve any local variable only a print and the result is the same. My suspect is that GetSurfaceMaterial creates a reference count that is never released. No variable need to be envolved. This also causes the same bug:
GD.Print(GetNode<MeshInstance>("MeshInstance").GetSurfaceMaterial(0) as SpatialMaterial);It works in gdscript. I haven't tested the c#, but assuming the report is correct this could be a bug in the godot c# side.
This was unexpected. You're right. I tested it now and works as should in GDScript. This is my code
Any possible workaround in C# until a proper fix?