mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-08 12:35:01 +00:00
Cut terrain rebuilds and draw-buffer churn (#2312)
This commit is contained in:
1 parent
08aa37712f
commit
bbbdc12b2a
14 files changed
+301
-38
No files matched your search
@@ -110,7 +110,7 @@ public final class GLSMAttribStack {
|
||||
if (!vaos.vaoMap.containsKey(saved.boundVAO)) return;
|
||||
GLSMVertexInput.bindVertexArrayInternal(glCtx, saved.boundVAO);
|
||||
glCtx.clientActiveTextureUnit = saved.clientActiveTextureUnit;
|
||||
if (vaos.isBufferLive(saved.boundVBO, saved)) {
|
||||
if (vaos.isSavedVboLive(saved)) {
|
||||
final int changed = vaos.restoreClientArrays(saved);
|
||||
for (int pointers = changed & 0xFFFF; pointers != 0; pointers &= pointers - 1) {
|
||||
GLSMVertexInput.pointAttrib(glCtx, Integer.numberOfTrailingZeros(pointers));
|
||||
@@ -120,7 +120,7 @@ public final class GLSMAttribStack {
|
||||
}
|
||||
if (glCtx.boundVBO != saved.boundVBO) GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, saved.boundVBO);
|
||||
}
|
||||
if (vaos.boundEBO != saved.boundEBO && vaos.isBufferLive(saved.boundEBO, saved)) GLSMBuffers.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, saved.boundEBO);
|
||||
if (vaos.boundEBO != saved.boundEBO && vaos.isSavedEboLive(saved)) GLSMBuffers.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, saved.boundEBO);
|
||||
}
|
||||
|
||||
public static void pushState(int mask) {
|
||||
|
||||
@@ -64,9 +64,12 @@ public final class GLSMBuffers {
|
||||
}
|
||||
}
|
||||
|
||||
public static long bufferDeletes;
|
||||
|
||||
private static void invalidateDeletedBuffer(int buffer) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (buffer == 0) return;
|
||||
bufferDeletes++;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
FfpExtendedAttribs.onDeleteBuffer(glCtx, buffer);
|
||||
@@ -87,7 +90,7 @@ public final class GLSMBuffers {
|
||||
}
|
||||
writeMappedBuffers.remove(buffer);
|
||||
|
||||
glCtx.vaos.onDeleteBuffer(buffer);
|
||||
glCtx.vaos.onDeleteBuffer(buffer, glCtx.clientAttribSavedArrays);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
|
||||
@@ -123,15 +123,63 @@ public final class VAOManager {
|
||||
private int clientSideEnabledCount = 0;
|
||||
private int arrayVersion = 0;
|
||||
private int versionClock = 0;
|
||||
private static final int DELETED_NAME_PRUNE_THRESHOLD = 4096;
|
||||
private static final int DEAD_EPOCH = -1;
|
||||
private final int pruneThreshold;
|
||||
private int bufferEpoch = 0;
|
||||
private final Int2IntOpenHashMap bufferDeletedAt = new Int2IntOpenHashMap();
|
||||
|
||||
public VAOManager() {
|
||||
this(DELETED_NAME_PRUNE_THRESHOLD);
|
||||
}
|
||||
|
||||
VAOManager(int pruneThreshold) {
|
||||
this.pruneThreshold = pruneThreshold;
|
||||
}
|
||||
|
||||
private void touch() {
|
||||
arrayVersion = ++versionClock;
|
||||
}
|
||||
|
||||
private boolean isBufferLive(int buffer, int sinceEpoch) {
|
||||
return bufferDeletedAt.get(buffer) <= sinceEpoch;
|
||||
return buffer == 0 || bufferDeletedAt.get(buffer) <= sinceEpoch;
|
||||
}
|
||||
|
||||
private int retiredEpoch(int buffer, int epoch, boolean rebase) {
|
||||
if (rebase) return epoch == DEAD_EPOCH ? DEAD_EPOCH : 0;
|
||||
return isBufferLive(buffer, epoch) ? epoch : DEAD_EPOCH;
|
||||
}
|
||||
|
||||
private void retire(@Nullable Attrib[] slots, boolean rebase) {
|
||||
if (slots == null) return;
|
||||
for (final Attrib a : slots) {
|
||||
if (a != null) a.epoch = retiredEpoch(a.vboId, a.epoch, rebase);
|
||||
}
|
||||
}
|
||||
|
||||
private void retireAll(ClientArrays[] savedStack, boolean rebase) {
|
||||
for (final VAOData data : vaoMap.values()) {
|
||||
retire(data.attribs, rebase);
|
||||
retire(data.generic, rebase);
|
||||
retire(data.conventional, rebase);
|
||||
}
|
||||
retire(currentAttribs, rebase);
|
||||
retire(currentGeneric, rebase);
|
||||
retire(currentConventional, rebase);
|
||||
for (final ClientArrays saved : savedStack) {
|
||||
if (saved == null) continue;
|
||||
saved.vboEpoch = retiredEpoch(saved.boundVBO, saved.vboEpoch, rebase);
|
||||
saved.eboEpoch = retiredEpoch(saved.boundEBO, saved.eboEpoch, rebase);
|
||||
retire(saved.generic, rebase);
|
||||
retire(saved.conventional, rebase);
|
||||
}
|
||||
}
|
||||
|
||||
private void pruneDeletedNames(ClientArrays[] savedStack) {
|
||||
retireAll(savedStack, false);
|
||||
bufferDeletedAt.clear();
|
||||
retireAll(savedStack, true);
|
||||
bufferEpoch = 0;
|
||||
}
|
||||
|
||||
public boolean isBufferLive(Attrib a) {
|
||||
@@ -206,9 +254,10 @@ public final class VAOManager {
|
||||
return detached;
|
||||
}
|
||||
|
||||
public void onDeleteBuffer(int buffer) {
|
||||
public void onDeleteBuffer(int buffer, ClientArrays[] savedStack) {
|
||||
if (buffer == 0) return;
|
||||
bufferDeletedAt.put(buffer, ++bufferEpoch);
|
||||
if (bufferEpoch >= pruneThreshold) pruneDeletedNames(savedStack);
|
||||
if (currentAttribs != null) {
|
||||
boolean detached = detach(currentGeneric != null ? currentGeneric : currentAttribs, buffer);
|
||||
if (currentConventional != null) detached |= detach(currentConventional, buffer);
|
||||
@@ -447,7 +496,8 @@ public final class VAOManager {
|
||||
public int boundEBO;
|
||||
public int clientActiveTextureUnit;
|
||||
int arrayVersion;
|
||||
int bufferEpoch;
|
||||
int vboEpoch;
|
||||
int eboEpoch;
|
||||
int vertexFlags;
|
||||
int genericEnabled;
|
||||
int conventionalEnabled;
|
||||
@@ -463,13 +513,18 @@ public final class VAOManager {
|
||||
saved[i].copyPointer(source);
|
||||
}
|
||||
|
||||
public boolean isBufferLive(int buffer, ClientArrays saved) {
|
||||
return isBufferLive(buffer, saved.bufferEpoch);
|
||||
public boolean isSavedVboLive(ClientArrays saved) {
|
||||
return isBufferLive(saved.boundVBO, saved.vboEpoch);
|
||||
}
|
||||
|
||||
public boolean isSavedEboLive(ClientArrays saved) {
|
||||
return isBufferLive(saved.boundEBO, saved.eboEpoch);
|
||||
}
|
||||
|
||||
public void saveClientArrays(ClientArrays out) {
|
||||
out.arrayVersion = arrayVersion;
|
||||
out.bufferEpoch = bufferEpoch;
|
||||
out.vboEpoch = bufferEpoch;
|
||||
out.eboEpoch = bufferEpoch;
|
||||
out.vertexFlags = currentVertexFlags;
|
||||
out.genericEnabled = genericEnabled;
|
||||
out.conventionalEnabled = conventionalEnabled;
|
||||
|
||||
@@ -158,6 +158,8 @@ final class GLSMOwners {
|
||||
static final String[][] FIELDS = {
|
||||
{"com/gtnewhorizons/angelica/glsm/GLSMAttribStack",
|
||||
"attribBackendCalls", "attribDiscards", "attribPushes", "attribSlotsSaved", "attribValueRestores"},
|
||||
{"com/gtnewhorizons/angelica/glsm/GLSMBuffers",
|
||||
"bufferDeletes"},
|
||||
{"com/gtnewhorizons/angelica/glsm/GLSMDraws",
|
||||
"drawCalls"},
|
||||
{"com/gtnewhorizons/angelica/glsm/GLSMFramebuffer",
|
||||
|
||||
@@ -487,29 +487,39 @@ public class GLSM_ClientArrays_GLTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void popKeepsCurrentArraysWhenTheSavedArrayBufferWasDeleted() {
|
||||
void popKeepsCurrentStateWhenTheSavedBuffersWereDeleted() {
|
||||
final int vbo2 = GLSMBuffers.glGenBuffers();
|
||||
final int ebo = GLSMBuffers.glGenBuffers();
|
||||
final int ebo2 = GLSMBuffers.glGenBuffers();
|
||||
try {
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLSMTextures.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLSMVertexInput.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
|
||||
|
||||
GLSMAttribStack.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo2);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo2);
|
||||
GLSMVertexInput.glVertexPointer(2, GL11.GL_SHORT, 4, 0L);
|
||||
GLSMTextures.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLSMBuffers.glDeleteBuffers(vbo);
|
||||
GLSMBuffers.glDeleteBuffers(ebo);
|
||||
GLSMAttribStack.glPopClientAttrib();
|
||||
|
||||
assertEquals(2, GLSMQueries.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE), "array state is not restored once its array buffer is gone");
|
||||
assertEquals(vbo2, GLSMQueries.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(vbo2, GL11.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(ebo2, GLSMQueries.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING), "a deleted element buffer is not rebound by the pop");
|
||||
assertEquals(ebo2, GL11.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(GL13.GL_TEXTURE0, GLSMQueries.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLSMTextures.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
GLSMBuffers.glDeleteBuffers(vbo2);
|
||||
GLSMBuffers.glDeleteBuffers(ebo2);
|
||||
GLSMBuffers.glDeleteBuffers(ebo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+192
-1
@@ -1,10 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@@ -15,6 +19,7 @@ public class VAOManagerEboIndexTest {
|
||||
|
||||
private static final int DEFAULT_VAO = 0;
|
||||
private static final int NO_VAO = -1;
|
||||
private static final VAOManager.ClientArrays[] NO_SAVED = new VAOManager.ClientArrays[0];
|
||||
|
||||
private final VAOManager vaos = new VAOManager();
|
||||
private final Int2IntOpenHashMap expectedEbo = new Int2IntOpenHashMap();
|
||||
@@ -62,7 +67,7 @@ public class VAOManagerEboIndexTest {
|
||||
}
|
||||
|
||||
private void deleteBuffer(int buffer) {
|
||||
vaos.onDeleteBuffer(buffer);
|
||||
vaos.onDeleteBuffer(buffer, NO_SAVED);
|
||||
for (int vao : expectedEbo.keySet().toIntArray()) {
|
||||
if (expectedEbo.get(vao) == buffer) expectedEbo.put(vao, 0);
|
||||
}
|
||||
@@ -126,4 +131,190 @@ public class VAOManagerEboIndexTest {
|
||||
}
|
||||
assertTrue(sharedEboDeletes > 20, "op stream never freed a multi-owner EBO (hits: " + sharedEboDeletes + ")");
|
||||
}
|
||||
|
||||
private static final int PRUNE_INTERVAL = 4;
|
||||
private static final int OPS = 40_000;
|
||||
private static final int SLOTS = 4;
|
||||
private static final int[] CONVS = {VAOManager.CONV_POSITION, VAOManager.CONV_COLOR, VAOManager.CONV_SECONDARY_COLOR, VAOManager.CONV_TEX0};
|
||||
private static final String[] CURRENT_FIELDS = {"currentAttribs", "currentGeneric", "currentConventional"};
|
||||
private static final int STACK_DEPTH = 16;
|
||||
private static final int MAX_VAOS = 8;
|
||||
private static final int MAX_LIVE_BUFFERS = 24;
|
||||
|
||||
private final VAOManager pruned = new VAOManager(PRUNE_INTERVAL);
|
||||
private final VAOManager oracle = new VAOManager(OPS + 1);
|
||||
private final VAOManager[] pair = {pruned, oracle};
|
||||
private final VAOManager.ClientArrays[][] stacks = new VAOManager.ClientArrays[pair.length][STACK_DEPTH];
|
||||
private int deadSlotChecks;
|
||||
|
||||
private static int pick(Random random, IntArrayList list) {
|
||||
return list.getInt(random.nextInt(list.size()));
|
||||
}
|
||||
|
||||
private int staleName(Random random, IntArrayList vaoIds, IntLinkedOpenHashSet freeNames) {
|
||||
final VAOManager.VAOData data = oracle.vaoMap.get(pick(random, vaoIds));
|
||||
final VAOManager.Attrib[] slots = random.nextBoolean() ? data.attribs : data.conventional;
|
||||
if (slots == null) return 0;
|
||||
final VAOManager.Attrib a = slots[random.nextInt(slots.length)];
|
||||
return a != null && a.vboId != 0 && freeNames.remove(a.vboId) ? a.vboId : 0;
|
||||
}
|
||||
|
||||
private void bindBoth(int vao) {
|
||||
if (vao == currentVao) return;
|
||||
for (VAOManager m : pair) m.onBindVertexArrayPre(vao);
|
||||
currentVao = vao;
|
||||
}
|
||||
|
||||
private static void restore(VAOManager m, VAOManager.ClientArrays saved) {
|
||||
if (m.isSavedVboLive(saved)) m.restoreClientArrays(saved);
|
||||
if (m.boundEBO != saved.boundEBO && m.isSavedEboLive(saved)) m.onBindEBO(saved.boundEBO);
|
||||
}
|
||||
|
||||
private static String where(int op, String kind, int id, int slot) {
|
||||
return "op " + op + ": " + kind + " " + id + " slot " + slot;
|
||||
}
|
||||
|
||||
private void assertSameSlot(int op, String kind, int id, int slot, VAOManager.Attrib p, VAOManager.Attrib o, boolean countDead) {
|
||||
assertEquals(o == null, p == null, () -> where(op, kind, id, slot) + " presence");
|
||||
if (o == null) return;
|
||||
assertEquals(o.vboId, p.vboId, () -> where(op, kind, id, slot) + " vbo");
|
||||
final boolean live = oracle.isBufferLive(o);
|
||||
assertEquals(live, pruned.isBufferLive(p), () -> where(op, kind, id, slot) + " liveness of buffer " + o.vboId);
|
||||
if (countDead && !live && o.vboId != 0) deadSlotChecks++;
|
||||
}
|
||||
|
||||
private void assertSameSlots(int op, String kind, int id, VAOManager.Attrib[] p, VAOManager.Attrib[] o, boolean countDead) {
|
||||
assertEquals(o == null, p == null, () -> where(op, kind, id, -1) + " array presence");
|
||||
if (o == null) return;
|
||||
for (int i = 0; i < o.length; i++) assertSameSlot(op, kind, id, i, p[i], o[i], countDead);
|
||||
}
|
||||
|
||||
private void assertSameLiveness(int op, int sp) {
|
||||
assertEquals(oracle.vaoMap.keySet(), pruned.vaoMap.keySet(), () -> "op " + op + ": vao sets differ");
|
||||
for (var entry : oracle.vaoMap.int2ObjectEntrySet()) {
|
||||
final int vao = entry.getIntKey();
|
||||
final VAOManager.VAOData o = entry.getValue();
|
||||
final VAOManager.VAOData p = pruned.vaoMap.get(vao);
|
||||
final boolean countDead = vao != currentVao;
|
||||
assertSameSlots(op, "vao attribs", vao, p.attribs, o.attribs, countDead);
|
||||
assertSameSlots(op, "vao generic", vao, p.generic, o.generic, countDead);
|
||||
assertSameSlots(op, "vao conventional", vao, p.conventional, o.conventional, countDead);
|
||||
}
|
||||
for (String field : CURRENT_FIELDS) {
|
||||
assertSameSlots(op, field, currentVao, Reflect.get(pruned, field), Reflect.get(oracle, field), false);
|
||||
}
|
||||
for (int s = 0; s < sp; s++) {
|
||||
final VAOManager.ClientArrays p = stacks[0][s];
|
||||
final VAOManager.ClientArrays o = stacks[1][s];
|
||||
final int depth = s;
|
||||
assertEquals(oracle.isSavedVboLive(o), pruned.isSavedVboLive(p), () -> "op " + op + ": saved " + depth + " vbo liveness");
|
||||
assertEquals(oracle.isSavedEboLive(o), pruned.isSavedEboLive(p), () -> "op " + op + ": saved " + depth + " ebo liveness");
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
if ((o.genericPresent >> i & 1) != 0) assertSameSlot(op, "saved generic", s, i, p.generic[i], o.generic[i], true);
|
||||
}
|
||||
for (int i = 0; i < VAOManager.CONV_COUNT; i++) {
|
||||
if ((o.conventionalPresent >> i & 1) != 0) assertSameSlot(op, "saved conventional", s, i, p.conventional[i], o.conventional[i], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void prunedDeletedNamesAnswerLivenessLikeAnUnprunedReference() {
|
||||
final Random random = new Random(0x5EED);
|
||||
for (VAOManager m : pair) m.init(DEFAULT_VAO);
|
||||
for (VAOManager.ClientArrays[] stack : stacks) {
|
||||
for (int s = 0; s < STACK_DEPTH; s++) stack[s] = new VAOManager.ClientArrays();
|
||||
}
|
||||
final IntArrayList vaoIds = IntArrayList.of(DEFAULT_VAO);
|
||||
final IntArrayList liveBuffers = new IntArrayList();
|
||||
final IntLinkedOpenHashSet freeNames = new IntLinkedOpenHashSet();
|
||||
final IntOpenHashSet everDeleted = new IntOpenHashSet();
|
||||
int sp = 0;
|
||||
int nextVao = 1;
|
||||
int nextBuffer = 1;
|
||||
int prunes = 0;
|
||||
int reusedDeletes = 0;
|
||||
|
||||
for (int op = 0; op < OPS; op++) {
|
||||
final int epochBefore = Reflect.get(pruned, "bufferEpoch");
|
||||
switch (random.nextInt(11)) {
|
||||
case 0, 1 -> {
|
||||
if (liveBuffers.size() >= MAX_LIVE_BUFFERS) break;
|
||||
final int stale = random.nextInt(3) == 0 ? staleName(random, vaoIds, freeNames) : 0;
|
||||
liveBuffers.add(stale != 0 ? stale : !freeNames.isEmpty() && random.nextInt(3) == 0 ? freeNames.removeLastInt() : nextBuffer++);
|
||||
}
|
||||
case 2, 3 -> {
|
||||
if (liveBuffers.isEmpty()) break;
|
||||
final int buffer = liveBuffers.removeInt(random.nextInt(liveBuffers.size()));
|
||||
if (!everDeleted.add(buffer)) reusedDeletes++;
|
||||
for (int m = 0; m < pair.length; m++) {
|
||||
if (pair[m].boundEBO == buffer) pair[m].onBindEBO(0);
|
||||
pair[m].onDeleteBuffer(buffer, stacks[m]);
|
||||
}
|
||||
freeNames.add(buffer);
|
||||
}
|
||||
case 4 -> {
|
||||
final int index = random.nextInt(SLOTS);
|
||||
final int buffer = liveBuffers.isEmpty() || random.nextInt(8) == 0 ? 0 : pick(random, liveBuffers);
|
||||
for (VAOManager m : pair) m.setAttribute(index, 4, GL11.GL_FLOAT, false, 0, 0L, buffer);
|
||||
}
|
||||
case 5 -> {
|
||||
final int conv = CONVS[random.nextInt(CONVS.length)];
|
||||
final int buffer = liveBuffers.isEmpty() || random.nextInt(8) == 0 ? 0 : pick(random, liveBuffers);
|
||||
for (VAOManager m : pair) m.setConventional(conv, 4, GL11.GL_FLOAT, false, 0, 0L, buffer);
|
||||
}
|
||||
case 6 -> {
|
||||
final int vao;
|
||||
if (vaoIds.size() < MAX_VAOS && random.nextBoolean()) {
|
||||
vao = nextVao++;
|
||||
vaoIds.add(vao);
|
||||
} else {
|
||||
vao = pick(random, vaoIds);
|
||||
}
|
||||
bindBoth(vao);
|
||||
}
|
||||
case 7 -> {
|
||||
final int ebo = liveBuffers.isEmpty() || random.nextInt(4) == 0 ? 0 : pick(random, liveBuffers);
|
||||
for (VAOManager m : pair) m.onBindEBO(ebo);
|
||||
}
|
||||
case 8 -> {
|
||||
if (sp == STACK_DEPTH) break;
|
||||
final int vbo = liveBuffers.isEmpty() || random.nextInt(4) == 0 ? 0 : pick(random, liveBuffers);
|
||||
for (int m = 0; m < pair.length; m++) {
|
||||
final VAOManager.ClientArrays saved = stacks[m][sp];
|
||||
saved.boundVAO = currentVao;
|
||||
saved.boundVBO = vbo;
|
||||
saved.boundEBO = pair[m].boundEBO;
|
||||
pair[m].saveClientArrays(saved);
|
||||
}
|
||||
sp++;
|
||||
}
|
||||
case 9 -> {
|
||||
if (sp == 0) break;
|
||||
sp--;
|
||||
final int vao = stacks[1][sp].boundVAO;
|
||||
if (!oracle.vaoMap.containsKey(vao)) break;
|
||||
bindBoth(vao);
|
||||
for (int m = 0; m < pair.length; m++) restore(pair[m], stacks[m][sp]);
|
||||
}
|
||||
case 10 -> {
|
||||
if (vaoIds.size() == 1) break;
|
||||
final int vao = vaoIds.removeInt(1 + random.nextInt(vaoIds.size() - 1));
|
||||
for (VAOManager m : pair) m.onDeleteVertexArray(vao);
|
||||
if (vao == currentVao) {
|
||||
currentVao = NO_VAO;
|
||||
bindBoth(DEFAULT_VAO);
|
||||
}
|
||||
}
|
||||
}
|
||||
final int epoch = Reflect.get(pruned, "bufferEpoch");
|
||||
final int at = op;
|
||||
assertTrue(epoch < PRUNE_INTERVAL, () -> "op " + at + ": epoch " + epoch + " escaped the prune interval");
|
||||
if (epoch < epochBefore) prunes++;
|
||||
assertSameLiveness(op, sp);
|
||||
}
|
||||
assertTrue(prunes >= 800, "op stream pruned only " + prunes + " times");
|
||||
assertTrue(deadSlotChecks >= 400_000, "op stream compared only " + deadSlotChecks + " dead slots");
|
||||
assertTrue(reusedDeletes >= 1_000, "op stream deleted only " + reusedDeletes + " reused names");
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ lwjgl3 = "3.4.2"
|
||||
lwjgl3Release = "3.4.2"
|
||||
lwjgl3ify = "3.0.31"
|
||||
|
||||
celeritas = "2.5.13-GTNH"
|
||||
celeritas = "2.5.14-GTNH"
|
||||
gtnhlib = "0.11.36"
|
||||
eventbus = "7.0-beta.15"
|
||||
glslTransformLib = "0.2.0-33.g3ec381e"
|
||||
|
||||
@@ -14,9 +14,11 @@ PLOTS = [
|
||||
"items.bail.ineligible", "items.bail.material", "items.bail.isbrh", "items.bail.blockState", "items.bail.notAllowed", "items.bail.template", "items.bail.queue",
|
||||
"particles.direct", "particles.captured", "particles.spilled", "particles.undecodable", "particles.draws", "sdl.renderPasses", "sdl.submits", "sdl.gpuWaitUs",
|
||||
"sdl.acquireWaitUs",
|
||||
"glsm.bufferDeletes", "draw.batchRebuilds.translucent", "draw.batchRebuilds", "terrain.regionsDrawn",
|
||||
"sdl.bufferCreates", "sdl.bufferDeletes", "sdl.bufferPoolHits",
|
||||
"weather.strength", "weather.quads", "weather.drawCalls", "weather.micros",
|
||||
]
|
||||
ZONES = ["entityModelParts", "tesrBatch", "entityLayerLoop", "entityDeferred", "tesrOpaque", "particlePass", "cl:weather", "streamingEndFrame"]
|
||||
ZONES = ["entityModelParts", "tesrBatch", "entityLayerLoop", "entityDeferred", "tesrOpaque", "particlePass", "cl:weather", "streamingEndFrame", "chunkAssembleRegion"]
|
||||
CLOCK = "gl.drawCalls"
|
||||
|
||||
|
||||
|
||||
+5
-2
@@ -807,6 +807,7 @@ public final class ResourceManager {
|
||||
final ByteBuffer droppedPboStaging;
|
||||
final ByteBuffer droppedPlainStaging;
|
||||
final PersistentMapping droppedPm;
|
||||
final boolean hadEboShadow;
|
||||
wLock.lock();
|
||||
try {
|
||||
final long handle = bufferHandles.remove(glId);
|
||||
@@ -820,7 +821,7 @@ public final class ResourceManager {
|
||||
droppedPlainStaging = removePlainMappingLocked(glId);
|
||||
droppedPboStaging = pboStagingData.remove(glId);
|
||||
deleteUboShadow(glId);
|
||||
deleteEboShadow(glId);
|
||||
hadEboShadow = deleteEboShadow(glId);
|
||||
deleteArrayShadow(glId);
|
||||
if (handle != 0) {
|
||||
returnBufferToPool(handle, usage, size);
|
||||
@@ -829,6 +830,7 @@ public final class ResourceManager {
|
||||
} finally {
|
||||
wLock.unlock();
|
||||
}
|
||||
if (hadEboShadow) invalidateSplitCacheFor(glId);
|
||||
if (droppedPboStaging != null) {
|
||||
memFree(droppedPboStaging);
|
||||
}
|
||||
@@ -1277,11 +1279,12 @@ public final class ResourceManager {
|
||||
try { return eboShadowVersions.get(bufferGlId); } finally { rLock.unlock(); }
|
||||
}
|
||||
|
||||
private void deleteEboShadow(int bufferGlId) {
|
||||
private boolean deleteEboShadow(int bufferGlId) {
|
||||
final ByteBuffer existing = eboShadows.remove(bufferGlId);
|
||||
if (existing != null) memFree(existing);
|
||||
eboShadowVersions.remove(bufferGlId);
|
||||
eboShadowWanted.remove(bufferGlId);
|
||||
return existing != null;
|
||||
}
|
||||
|
||||
public boolean canShadowArrayBuffer(int size) {
|
||||
|
||||
@@ -514,7 +514,6 @@ public enum Mixins implements IMixins {
|
||||
, "celeritas.terrain.MixinShaderLoader"
|
||||
, "celeritas.terrain.MixinGlUniformMatrix4f"
|
||||
, "celeritas.terrain.MixinGlUniformMatrix3f"
|
||||
, "celeritas.terrain.MixinDefaultChunkRenderer"
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.glsm.CaptureGate;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMAttribStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMPrograms;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextureLifetime;
|
||||
@@ -40,6 +41,7 @@ public final class TracyFramePlots {
|
||||
private static final PlotDelta P_GL_TEX_BIND_MISSES = new PlotDelta("gl.texBindMisses");
|
||||
private static final PlotDelta P_GL_PROGRAM_SWITCHES = new PlotDelta("gl.programSwitches");
|
||||
private static final long P_GL_TEX_AWAITING_RELEASE = Tracy.plotHandle("gl.texAwaitingRelease");
|
||||
private static final PlotDelta P_GLSM_BUFFER_DELETES = new PlotDelta("glsm.bufferDeletes");
|
||||
private static final PlotDelta P_GLSM_BRACKETS = new PlotDelta("glsm.brackets");
|
||||
private static final PlotDelta P_GLSM_COW_SLOTS = new PlotDelta("glsm.cowSlots");
|
||||
private static final PlotDelta P_GLSM_POP_RESTORES = new PlotDelta("glsm.popRestores");
|
||||
@@ -62,6 +64,7 @@ public final class TracyFramePlots {
|
||||
private static final long P_VOX_RANGES = Tracy.plotHandle("voxelization.ranges");
|
||||
private static final long P_VOX_REGIONS = Tracy.plotHandle("voxelization.regions");
|
||||
private static final long P_DRAW_BATCH_REBUILDS = Tracy.plotHandle("draw.batchRebuilds");
|
||||
private static final long P_DRAW_BATCH_REBUILDS_TRANSLUCENT = Tracy.plotHandle("draw.batchRebuilds.translucent");
|
||||
private static final PlotDelta P_DRAW_INDEX_BUFFER_GROWTHS = new PlotDelta("draw.indexBufferGrowths");
|
||||
|
||||
private static final long P_CULL_COMMANDS_SUBMITTED = Tracy.plotHandle("cull.commandsSubmitted");
|
||||
@@ -197,6 +200,7 @@ public final class TracyFramePlots {
|
||||
P_GL_TEX_BIND_MISSES.plot(GLSMTextures.texBindMisses);
|
||||
P_GL_PROGRAM_SWITCHES.plot(GLSMPrograms.programSwitches);
|
||||
Tracy.plotInt(P_GL_TEX_AWAITING_RELEASE, GLSMTextureLifetime.deferredTexturesAwaitingRelease());
|
||||
P_GLSM_BUFFER_DELETES.plot(GLSMBuffers.bufferDeletes);
|
||||
P_GLSM_BRACKETS.plot(GLSMAttribStack.attribPushes);
|
||||
P_GLSM_COW_SLOTS.plot(GLSMAttribStack.attribSlotsSaved);
|
||||
P_GLSM_POP_RESTORES.plot(GLSMAttribStack.attribValueRestores);
|
||||
@@ -214,6 +218,7 @@ public final class TracyFramePlots {
|
||||
Tracy.plotInt(P_TERRAIN_DRAW_COMMANDS, TerrainDrawStats.takeCommandsSubmitted());
|
||||
Tracy.plotInt(P_TERRAIN_REGIONS_DRAWN, TerrainDrawStats.takeRegionsDrawn());
|
||||
Tracy.plotInt(P_DRAW_BATCH_REBUILDS, TerrainDrawStats.takeBatchRebuilds());
|
||||
Tracy.plotInt(P_DRAW_BATCH_REBUILDS_TRANSLUCENT, TerrainDrawStats.takeBatchRebuildsTranslucent());
|
||||
Tracy.plotInt(P_VOX_ENCODERS, SdlShadowVoxelizationSink.takeEncoders());
|
||||
Tracy.plotInt(P_VOX_RANGES, SdlShadowVoxelizationSink.takeRanges());
|
||||
Tracy.plotInt(P_VOX_REGIONS, SdlShadowVoxelizationSink.takeRegions());
|
||||
|
||||
+6
-1
@@ -305,6 +305,11 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SectionRenderDataStorage.BatchCacheParams batchCacheParams(boolean useBlockFaceCulling) {
|
||||
return SectionRenderDataStorage.BatchCacheParams.of(useBlockFaceCulling, ShadowRenderingState.areShadowsCurrentlyBeingRendered() ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable CachedBatch getRegionBatch(CommandList commandList, RenderRegion region,
|
||||
SectionRenderDataStorage storage, ChunkRenderList renderList,
|
||||
@@ -327,7 +332,7 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
try {
|
||||
final CachedBatch cached = super.getRegionBatch(commandList, region, storage, renderList, occlusionCamera,
|
||||
renderPass, useBlockFaceCulling, cacheParams);
|
||||
if (cached != before) TerrainDrawStats.recordRebuild();
|
||||
if (cached != before) TerrainDrawStats.recordRebuild(renderPass.isSorted());
|
||||
final MultiDrawBatch batch = cached != null ? cached.getBatch() : null;
|
||||
if (batch != null && !batch.isEmpty()) TerrainDrawStats.recordBatch(batch.size());
|
||||
return cached;
|
||||
|
||||
@@ -8,6 +8,7 @@ public final class TerrainDrawStats {
|
||||
private static int commandsSubmitted;
|
||||
private static int regionsDrawn;
|
||||
private static int batchRebuilds;
|
||||
private static int batchRebuildsTranslucent;
|
||||
|
||||
private static int cullCommandsSubmitted;
|
||||
private static int cullRegionsDrawn;
|
||||
@@ -35,8 +36,9 @@ public final class TerrainDrawStats {
|
||||
regionsDrawn++;
|
||||
}
|
||||
|
||||
public static void recordRebuild() {
|
||||
public static void recordRebuild(boolean translucent) {
|
||||
batchRebuilds++;
|
||||
if (translucent) batchRebuildsTranslucent++;
|
||||
}
|
||||
|
||||
public static void recordCullRegion(int commandCount) {
|
||||
@@ -76,6 +78,12 @@ public final class TerrainDrawStats {
|
||||
return n;
|
||||
}
|
||||
|
||||
public static int takeBatchRebuildsTranslucent() {
|
||||
final int n = batchRebuildsTranslucent;
|
||||
batchRebuildsTranslucent = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
public static int takeCommandsSubmitted() {
|
||||
final int n = commandsSubmitted;
|
||||
commandsSubmitted = 0;
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import org.embeddedt.embeddium.impl.render.chunk.DefaultChunkRenderer;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.data.SectionRenderDataStorage;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = DefaultChunkRenderer.class, remap = false)
|
||||
public abstract class MixinDefaultChunkRenderer {
|
||||
|
||||
@Unique private static final SectionRenderDataStorage.BatchCacheParams angelica$CULLED = new SectionRenderDataStorage.BatchCacheParams(true);
|
||||
@Unique private static final SectionRenderDataStorage.BatchCacheParams angelica$UNCULLED = new SectionRenderDataStorage.BatchCacheParams(false);
|
||||
|
||||
@Redirect(method = "render", at = @At(value = "NEW", target = "org/embeddedt/embeddium/impl/render/chunk/data/SectionRenderDataStorage$BatchCacheParams"))
|
||||
private SectionRenderDataStorage.BatchCacheParams angelica$sharedParams(boolean cull) {
|
||||
return cull ? angelica$CULLED : angelica$UNCULLED;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user