File size: 1,749 Bytes
84e8204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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,
	}