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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
package makamys.lodmod.renderer;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glBufferSubData;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import makamys.lodmod.renderer.Mesh.GPUStatus;
public class GPUMemoryManager {
private static int BUFFER_SIZE = 1024 * 1024 * 1024;
public int VBO;
private int nextTri;
private int nextMeshOffset;
private int nextMesh;
private List<Mesh> sentMeshes = new ArrayList<>();
public GPUMemoryManager() {
VBO = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, BUFFER_SIZE, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
public void runGC() {
nextMeshOffset = 0;
nextTri = 0;
glBindBuffer(GL_ARRAY_BUFFER, VBO);
int deletedNum = 0;
int deletedRAM = 0;
long t0 = System.nanoTime();
for(Iterator<Mesh> it = sentMeshes.iterator(); it.hasNext(); ) {
Mesh mesh = it.next();
if(mesh.gpuStatus == GPUStatus.SENT) {
if(mesh.offset != nextMeshOffset) {
glBufferSubData(GL_ARRAY_BUFFER, nextMeshOffset, mesh.buffer);
}
mesh.iFirst = nextTri;
mesh.offset = nextMeshOffset;
nextMeshOffset += mesh.bufferSize();
nextTri += mesh.quadCount * 6;
} else if(mesh.gpuStatus == GPUStatus.PENDING_DELETE) {
mesh.iFirst = mesh.offset = -1;
mesh.visible = false;
mesh.gpuStatus = GPUStatus.UNSENT;
mesh.destroyBuffer();
it.remove();
deletedNum++;
deletedRAM += mesh.bufferSize();
}
}
long t1 = System.nanoTime();
System.out.println("Deleted " + deletedNum + " meshes in " + ((t1 - t0) / 1_000_000.0) + " ms, freeing up " + (deletedRAM / 1024 / 1024) + "MB of VRAM");
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
public void sendMeshToGPU(Mesh mesh) {
if(mesh == null) {
return;
}
if(mesh.gpuStatus == GPUStatus.UNSENT) {
mesh.prepareBuffer();
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, nextMeshOffset, mesh.buffer);
mesh.iFirst = nextTri;
mesh.iCount = mesh.quadCount * 6;
mesh.offset = nextMeshOffset;
nextTri += mesh.quadCount * 6;
nextMeshOffset += mesh.buffer.limit();
sentMeshes.add(mesh);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
mesh.gpuStatus = GPUStatus.SENT;
}
public void deleteMeshFromGPU(Mesh mesh) {
if(mesh == null || mesh.gpuStatus == GPUStatus.UNSENT) {
return;
}
mesh.gpuStatus = GPUStatus.PENDING_DELETE;
}
public void destroy() {
glDeleteBuffers(VBO);
}
public List<String> getDebugText() {
return Arrays.asList("VRAM: " + (nextMeshOffset / 1024 / 1024) + "MB / " + (BUFFER_SIZE / 1024 / 1024) + "MB");
}
}
|