monkeystudio / scripts /core /mesh_pool.gd
geter62's picture
Upload folder using huggingface_hub
84e8204 verified
Raw History Blame Contribute Delete
1.75 kB
extends Node
##
## MeshPool - recycles ArrayMesh instances to avoid allocation churn.
##
## On low-RAM Android, creating and freeing ArrayMesh objects every frame causes
## GC spikes and fragmentation. This pool keeps a bounded number of meshes alive
## and reuses them. Meshes handed back to the pool have their surfaces cleared,
## freeing their GPU buffers while keeping the object (and its native handle).
##
signal pool_grew(size: int)
signal pool_trimmed(freed: int)
## Hard cap so the pool itself cannot become the memory leak.
@export var max_pooled: int = 32
var _free: Array[ArrayMesh] = []
var _live_count: int = 0
var _peak_live: int = 0
## Get a cleared mesh. Reuses a pooled one if available.
func acquire_mesh() -> ArrayMesh:
var mesh: ArrayMesh
if _free.size() > 0:
mesh = _free.pop_back()
else:
mesh = ArrayMesh.new()
_live_count += 1
_peak_live = maxi(_peak_live, _live_count)
return mesh
## Return a mesh for reuse. Clears surfaces so its VRAM is released immediately.
func release_mesh(mesh: ArrayMesh) -> void:
if mesh == null:
return
_live_count = maxi(0, _live_count - 1)
# Drop all surfaces: this frees the vertex buffers on the GPU side.
for i in range(mesh.get_surface_count()):
pass
mesh.clear_surfaces()
if _free.size() < max_pooled:
_free.append(mesh)
else:
# Pool is full: let Godot free this one (mesh has no surfaces = cheap).
pass
## Remove pooled-but-unused meshes down to `keep`. Call on scene transitions.
func trim(keep: int = 8) -> void:
var freed := 0
while _free.size() > keep:
_free.pop_back()
freed += 1
if freed > 0:
pool_trimmed.emit(freed)
func stats() -> Dictionary:
return {
"pooled": _free.size(),
"live": _live_count,
"peak_live": _peak_live,
}