To delete or not to delete

- Fix AtomicBitset#nextClearBit
- Add test for AtomicBitset
- Remove unnecessary gl state stuff from IndirectEngine#renderCrumbling.
- Fix instance removal on IndirectEngine.
  - Deleting an instance not at the end of the instancers list would
    cause an exception to be thrown during upload caused by
    out-of-bounds indexing.
  - Fixed by unsetting changed for the tail of the instancers list.
This commit is contained in:
Jozufozu 2023-12-20 11:21:18 -08:00
parent 7c0959be9a
commit 948750f296
5 changed files with 67 additions and 34 deletions

View file

@ -40,11 +40,6 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
return instances.size();
}
protected boolean moreThanTwoThirdsChanged() {
return (changed.cardinality() * 3) > (instances.size() * 2);
}
public void notifyDirty(int index) {
if (index < 0 || index >= getInstanceCount()) {
return;
@ -68,21 +63,34 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
final int oldSize = this.instances.size();
int removeCount = deleted.cardinality();
if (oldSize == removeCount) {
clear();
return;
}
final int newSize = oldSize - removeCount;
// shift surviving elements left over the spaces left by removed elements
for (int i = 0, j = 0; (i < oldSize) && (j < newSize); i++, j++) {
i = deleted.nextClearBit(i);
// Punch out the deleted instances, shifting over surviving instances to fill their place.
for (int scanPos = 0, writePos = 0; (scanPos < oldSize) && (writePos < newSize); scanPos++, writePos++) {
// Find next non-deleted element.
scanPos = deleted.nextClearBit(scanPos);
if (i != j) {
var handle = handles.get(i);
I instance = instances.get(i);
if (scanPos != writePos) {
// Grab the old instance/handle from scanPos...
var handle = handles.get(scanPos);
I instance = instances.get(scanPos);
handles.set(j, handle);
instances.set(j, instance);
// ... and move it to writePos.
handles.set(writePos, handle);
instances.set(writePos, instance);
handle.index = j;
changed.set(j);
// Make sure the handle knows it's been moved...
handle.index = writePos;
// ...and set it changed to force an upload.
changed.set(writePos);
// Clear the old slot. There's nothing meaningful there that can be considered "changed".
changed.clear(scanPos);
}
}

View file

@ -19,6 +19,8 @@ public class InstanceHandleImpl implements InstanceHandle {
@Override
public void setDeleted() {
instancer.notifyRemoval(index);
// invalidate ourselves
clear();
}
public void clear() {

View file

@ -76,26 +76,9 @@ public class IndirectEngine extends AbstractEngine {
executor.syncUntil(flushFlag::isRaised);
try (var restoreState = GlStateTracker.getRestoreState()) {
int prevActiveTexture = GlStateManager._getActiveTexture();
Minecraft.getInstance().gameRenderer.overlayTexture()
.setupOverlayColor();
Minecraft.getInstance().gameRenderer.lightTexture()
.turnOnLightLayer();
GlTextureUnit.T1.makeActive();
RenderSystem.bindTexture(RenderSystem.getShaderTexture(1));
GlTextureUnit.T2.makeActive();
RenderSystem.bindTexture(RenderSystem.getShaderTexture(2));
drawManager.renderCrumbling(crumblingBlocks);
MaterialRenderState.reset();
Minecraft.getInstance().gameRenderer.overlayTexture()
.teardownOverlayColor();
Minecraft.getInstance().gameRenderer.lightTexture()
.turnOffLightLayer();
GlStateManager._activeTexture(prevActiveTexture);
}
}

View file

@ -168,13 +168,13 @@ public class AtomicBitset {
if (++longPosition > segmentMask) {
segmentPosition++;
if (segmentPosition >= segments.numSegments()) {
return segments.numSegments() << log2SegmentSize;
return segments.numSegments() << log2SegmentSize + (longPosition << 6);
}
segment = segments.getSegment(segmentPosition);
longPosition = 0;
}
word = segment.get(longPosition);
word = ~segment.get(longPosition);
}
}

View file

@ -0,0 +1,40 @@
package com.jozufozu.flywheel.lib.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class TestAtomicBitset {
@Test
void testNextClearBit() {
var segmentLength = 1 << AtomicBitset.DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS;
var bitLength = 2 << AtomicBitset.DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS;
var bs = new AtomicBitset(AtomicBitset.DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS, bitLength);
Assertions.assertEquals(0, bs.nextClearBit(0));
Assertions.assertEquals(1, bs.nextClearBit(1));
Assertions.assertEquals(5000, bs.nextClearBit(5000));
bs.set(16);
Assertions.assertEquals(0, bs.nextClearBit(0));
Assertions.assertEquals(17, bs.nextClearBit(16));
bs.set(segmentLength + 1);
Assertions.assertEquals(0, bs.nextClearBit(0));
Assertions.assertEquals(segmentLength + 2, bs.nextClearBit(segmentLength + 1));
bs.set(bitLength);
Assertions.assertEquals(0, bs.nextClearBit(0));
Assertions.assertEquals(bitLength + 1, bs.nextClearBit(bitLength));
for (int i = 0; i < bitLength; i++) {
bs.set(i);
}
Assertions.assertEquals(bitLength + 1, bs.nextClearBit(0));
}
}