Хорошие оптимизации PBO

This commit is contained in:
2026-08-08 05:02:55 +04:00
parent e39c3d716d
commit a4cdfb0c9c
5 changed files with 224 additions and 33 deletions
@@ -17,6 +17,8 @@ public final class FogChunkStreamer {
FogWorldVolume.CHUNK_SIZE; FogWorldVolume.CHUNK_SIZE;
private static final int MAX_CHUNKS_PER_TICK = 4; private static final int MAX_CHUNKS_PER_TICK = 4;
private static final int MAX_READY_CHUNK_UPLOADS_PER_TICK = 4;
private static volatile int burstFrames = 0;
// Битовые маски вместо массивов статусов // Битовые маски вместо массивов статусов
private static final AtomicLongArray pendingMask = new AtomicLongArray(TOTAL_WORDS); private static final AtomicLongArray pendingMask = new AtomicLongArray(TOTAL_WORDS);
@@ -143,14 +145,12 @@ public final class FogChunkStreamer {
int dz = newOriginChunkZ - oldOriginChunkZ; int dz = newOriginChunkZ - oldOriginChunkZ;
if (dx == 0 && dz == 0) { if (dx == 0 && dz == 0) {
uploadReadyData();
return; return;
} }
if (Math.abs(dx) >= FogWorldVolume.CHUNKS_X || if (Math.abs(dx) >= FogWorldVolume.CHUNKS_X ||
Math.abs(dz) >= FogWorldVolume.CHUNKS_Z) { Math.abs(dz) >= FogWorldVolume.CHUNKS_Z) {
markAllPending(); markAllPending();
uploadReadyData();
return; return;
} }
@@ -313,7 +313,9 @@ public final class FogChunkStreamer {
int processedCount = 0; int processedCount = 0;
int index = 0; int index = 0;
while (processedCount < MAX_CHUNKS_PER_TICK) { int limit = MAX_CHUNKS_PER_TICK + (burstFrames > 0 ? MAX_CHUNKS_PER_TICK : 0);
while (processedCount < limit) {
index = nextSetBit(pendingMask, index); index = nextSetBit(pendingMask, index);
if (index < 0) break; if (index < 0) break;
@@ -332,6 +334,7 @@ public final class FogChunkStreamer {
} }
index++; index++;
} }
if (burstFrames > 0) burstFrames--;
} }
public static void uploadReadyData() { public static void uploadReadyData() {
@@ -340,8 +343,13 @@ public final class FogChunkStreamer {
int ringOffsetX = currentRingChunkOffsetX; int ringOffsetX = currentRingChunkOffsetX;
int ringOffsetZ = currentRingChunkOffsetZ; int ringOffsetZ = currentRingChunkOffsetZ;
int processed = 0;
int index = 0; int index = 0;
while (true) {
int limit = MAX_READY_CHUNK_UPLOADS_PER_TICK + (burstFrames > 0 ? MAX_READY_CHUNK_UPLOADS_PER_TICK : 0);
FogWorldVolume.beginUpload();
while (processed < limit) {
index = nextSetBit(readyMask, index); index = nextSetBit(readyMask, index);
if (index < 0) break; if (index < 0) break;
@@ -354,8 +362,9 @@ public final class FogChunkStreamer {
if (preparedWorldChunkX[index] == currentWorldX && if (preparedWorldChunkX[index] == currentWorldX &&
preparedWorldChunkZ[index] == currentWorldZ) { preparedWorldChunkZ[index] == currentWorldZ) {
FogWorldVolume.uploadChunkFromArray(physicalX, physicalZ, preparedData[index]); FogWorldVolume.uploadChunkFromArrayNoBind(physicalX, physicalZ, preparedData[index]);
clearBit(readyMask, index); clearBit(readyMask, index);
processed++;
} else { } else {
// Координаты изменились — помечаем как pending заново // Координаты изменились — помечаем как pending заново
clearBit(readyMask, index); clearBit(readyMask, index);
@@ -363,5 +372,11 @@ public final class FogChunkStreamer {
} }
index++; index++;
} }
FogWorldVolume.endUpload();
if (burstFrames > 0) burstFrames--;
}
public static void setBurst(int frames) {
burstFrames = Math.max(burstFrames, frames);
} }
} }
@@ -29,8 +29,11 @@ public final class FogLightStreamer {
private static final int[] preparedSkyWorldZ = new int[TOTAL_SECTIONS]; private static final int[] preparedSkyWorldZ = new int[TOTAL_SECTIONS];
// Более равномерное распределение: меньше initial за раз, больше incremental // Более равномерное распределение: меньше initial за раз, больше incremental
private static final int MAX_INITIAL_SECTIONS_PER_TICK = 32; // Было 96 private static final int MAX_INITIAL_SECTIONS_PER_TICK = 16;
private static final int MAX_PENDING_SECTIONS_PER_TICK = 64; // Новый параметр private static final int MAX_PENDING_SECTIONS_PER_TICK = 16;
private static final int MAX_READY_BLOCK_SECTIONS_PER_TICK = 8;
private static final int MAX_READY_SKY_SECTIONS_PER_TICK = 8;
private static volatile int burstFrames = 0;
private static volatile int currentOriginChunkX; private static volatile int currentOriginChunkX;
private static volatile int currentOriginChunkZ; private static volatile int currentOriginChunkZ;
@@ -293,7 +296,8 @@ public final class FogLightStreamer {
// Чередование: немного initial block, немного pending block, // Чередование: немного initial block, немного pending block,
// немного initial sky, немного pending sky // немного initial sky, немного pending sky
int index = 0; int index = 0;
while (processedBlock < MAX_INITIAL_SECTIONS_PER_TICK) { int initialLimit = MAX_INITIAL_SECTIONS_PER_TICK + (burstFrames > 0 ? MAX_INITIAL_SECTIONS_PER_TICK : 0);
while (processedBlock < initialLimit) {
index = nextSetBit(initialBlockMask, index); index = nextSetBit(initialBlockMask, index);
if (index < 0) break; if (index < 0) break;
if (prepareBlockLight(index)) { if (prepareBlockLight(index)) {
@@ -304,7 +308,8 @@ public final class FogLightStreamer {
} }
index = 0; index = 0;
while (processedBlock < MAX_INITIAL_SECTIONS_PER_TICK + MAX_PENDING_SECTIONS_PER_TICK) { int pendingLimit = MAX_INITIAL_SECTIONS_PER_TICK + MAX_PENDING_SECTIONS_PER_TICK + (burstFrames > 0 ? MAX_PENDING_SECTIONS_PER_TICK : 0);
while (processedBlock < pendingLimit) {
index = nextSetBit(pendingBlockMask, index); index = nextSetBit(pendingBlockMask, index);
if (index < 0) break; if (index < 0) break;
if (prepareBlockLight(index)) { if (prepareBlockLight(index)) {
@@ -315,7 +320,8 @@ public final class FogLightStreamer {
} }
index = 0; index = 0;
while (processedSky < MAX_INITIAL_SECTIONS_PER_TICK) { int initialSkyLimit = MAX_INITIAL_SECTIONS_PER_TICK + (burstFrames > 0 ? MAX_INITIAL_SECTIONS_PER_TICK : 0);
while (processedSky < initialSkyLimit) {
index = nextSetBit(initialSkyMask, index); index = nextSetBit(initialSkyMask, index);
if (index < 0) break; if (index < 0) break;
if (prepareSkyLight(index)) { if (prepareSkyLight(index)) {
@@ -326,7 +332,8 @@ public final class FogLightStreamer {
} }
index = 0; index = 0;
while (processedSky < MAX_INITIAL_SECTIONS_PER_TICK + MAX_PENDING_SECTIONS_PER_TICK) { int pendingSkyLimit = MAX_INITIAL_SECTIONS_PER_TICK + MAX_PENDING_SECTIONS_PER_TICK + (burstFrames > 0 ? MAX_PENDING_SECTIONS_PER_TICK : 0);
while (processedSky < pendingSkyLimit) {
index = nextSetBit(pendingSkyMask, index); index = nextSetBit(pendingSkyMask, index);
if (index < 0) break; if (index < 0) break;
if (prepareSkyLight(index)) { if (prepareSkyLight(index)) {
@@ -335,30 +342,42 @@ public final class FogLightStreamer {
} }
index++; index++;
} }
if (burstFrames > 0) burstFrames--;
} }
public static void uploadReadySections() { public static void uploadReadySections() {
int processed = 0; int processedBlock = 0;
int processedSky = 0;
int index = 0; int index = 0;
FogLightVolume.beginUpload(FogLightVolume.getBlockLightTexture());
while (processed < MAX_PENDING_SECTIONS_PER_TICK) { int blockLimit = MAX_READY_BLOCK_SECTIONS_PER_TICK + (burstFrames > 0 ? MAX_READY_BLOCK_SECTIONS_PER_TICK : 0);
while (processedBlock < blockLimit) {
index = nextSetBit(readyBlockMask, index); index = nextSetBit(readyBlockMask, index);
if (index < 0) break; if (index < 0) break;
if (uploadBlockLight(index)) { if (uploadBlockLight(index)) {
processed++; processedBlock++;
} }
index++; index++;
} }
FogLightVolume.endUpload();
index = 0; index = 0;
while (processed < MAX_PENDING_SECTIONS_PER_TICK * 2) { FogLightVolume.beginUpload(FogLightVolume.getSkyLightTexture());
int skyLimit = MAX_READY_SKY_SECTIONS_PER_TICK + (burstFrames > 0 ? MAX_READY_SKY_SECTIONS_PER_TICK : 0);
while (processedSky < skyLimit) {
index = nextSetBit(readySkyMask, index); index = nextSetBit(readySkyMask, index);
if (index < 0) break; if (index < 0) break;
if (uploadSkyLight(index)) { if (uploadSkyLight(index)) {
processed++; processedSky++;
} }
index++; index++;
} }
FogLightVolume.endUpload();
if (burstFrames > 0) burstFrames--;
}
public static void setBurst(int frames) {
burstFrames = Math.max(burstFrames, frames);
} }
private static boolean prepareBlockLight(int index) { private static boolean prepareBlockLight(int index) {
@@ -420,7 +439,7 @@ public final class FogLightStreamer {
return false; return false;
} }
FogLightVolume.uploadBlockLightSection(physicalX, localSectionY, physicalZ, FogLightVolume.uploadBlockLightSectionNoBind(physicalX, localSectionY, physicalZ,
FogLightProvider.getBlockLightData(index)); FogLightProvider.getBlockLightData(index));
clearBit(readyBlockMask, index); clearBit(readyBlockMask, index);
return true; return true;
@@ -443,7 +462,7 @@ public final class FogLightStreamer {
return false; return false;
} }
FogLightVolume.uploadSkyLightSection(physicalX, localSectionY, physicalZ, FogLightVolume.uploadSkyLightSectionNoBind(physicalX, localSectionY, physicalZ,
FogLightProvider.getSkyLightData(index)); FogLightProvider.getSkyLightData(index));
clearBit(readySkyMask, index); clearBit(readySkyMask, index);
return true; return true;
@@ -6,6 +6,7 @@ import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31; import org.lwjgl.opengl.GL31;
// No GL43 usage: keep compatibility with OpenGL 3.2
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -43,6 +44,8 @@ public final class FogLightVolume {
// PBO для быстрой очистки // PBO для быстрой очистки
private static int clearBlockPBO = -1; private static int clearBlockPBO = -1;
private static int clearSkyPBO = -1; private static int clearSkyPBO = -1;
// PBO для загрузки секций
private static int uploadPBO = -1;
// Буферы для загрузки секций // Буферы для загрузки секций
private static final ByteBuffer BLOCK_SECTION_UPLOAD_BUFFER = private static final ByteBuffer BLOCK_SECTION_UPLOAD_BUFFER =
@@ -92,6 +95,7 @@ public final class FogLightVolume {
if (blockLightTexture != -1) return; if (blockLightTexture != -1) return;
initClearPBOs(); initClearPBOs();
initUploadPBO();
// Block light texture // Block light texture
blockLightTexture = GL11.glGenTextures(); blockLightTexture = GL11.glGenTextures();
@@ -177,20 +181,14 @@ public final class FogLightVolume {
BLOCK_SECTION_UPLOAD_BUFFER.put(data); BLOCK_SECTION_UPLOAD_BUFFER.put(data);
BLOCK_SECTION_UPLOAD_BUFFER.flip(); BLOCK_SECTION_UPLOAD_BUFFER.flip();
GL11.glBindTexture(GL12.GL_TEXTURE_3D, blockLightTexture); beginUpload(blockLightTexture);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
GL30.glTexSubImage3D( GL30.glTexSubImage3D(
GL12.GL_TEXTURE_3D, 0, GL12.GL_TEXTURE_3D, 0,
textureSectionX * CHUNK_SIZE, textureSectionY * 16, textureSectionZ * CHUNK_SIZE, textureSectionX * CHUNK_SIZE, textureSectionY * 16, textureSectionZ * CHUNK_SIZE,
16, 16, 16, 16, 16, 16,
GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, BLOCK_SECTION_UPLOAD_BUFFER GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, BLOCK_SECTION_UPLOAD_BUFFER
); );
endUpload();
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
} }
public static void uploadSkyLightSection( public static void uploadSkyLightSection(
@@ -200,11 +198,72 @@ public final class FogLightVolume {
SKY_SECTION_UPLOAD_BUFFER.put(data); SKY_SECTION_UPLOAD_BUFFER.put(data);
SKY_SECTION_UPLOAD_BUFFER.flip(); SKY_SECTION_UPLOAD_BUFFER.flip();
GL11.glBindTexture(GL12.GL_TEXTURE_3D, skyLightTexture); beginUpload(skyLightTexture);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); GL30.glTexSubImage3D(
GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0); GL12.GL_TEXTURE_3D, 0,
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0); textureSectionX * CHUNK_SIZE, textureSectionY * 16, textureSectionZ * CHUNK_SIZE,
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0); 16, 16, 16,
GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, SKY_SECTION_UPLOAD_BUFFER
);
endUpload();
}
public static void uploadBlockLightSectionNoBind(
int textureSectionX, int textureSectionY, int textureSectionZ, byte[] data
) {
// Try to upload via PBO to avoid CPU->GPU stalls
if (uploadPBO != -1) {
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, uploadPBO);
GL15.glBufferData(GL31.GL_PIXEL_UNPACK_BUFFER, BLOCK_SECTION_UPLOAD_BUFFER.capacity(), GL15.GL_STREAM_DRAW);
ByteBuffer mapped = GL15.glMapBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, GL15.GL_WRITE_ONLY);
if (mapped != null) {
mapped.put(data);
GL15.glUnmapBuffer(GL31.GL_PIXEL_UNPACK_BUFFER);
GL30.glTexSubImage3D(
GL12.GL_TEXTURE_3D, 0,
textureSectionX * CHUNK_SIZE, textureSectionY * 16, textureSectionZ * CHUNK_SIZE,
16, 16, 16,
GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, 0L
);
}
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, 0);
} else {
BLOCK_SECTION_UPLOAD_BUFFER.clear();
BLOCK_SECTION_UPLOAD_BUFFER.put(data);
BLOCK_SECTION_UPLOAD_BUFFER.flip();
GL30.glTexSubImage3D(
GL12.GL_TEXTURE_3D, 0,
textureSectionX * CHUNK_SIZE, textureSectionY * 16, textureSectionZ * CHUNK_SIZE,
16, 16, 16,
GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, BLOCK_SECTION_UPLOAD_BUFFER
);
}
}
public static void uploadSkyLightSectionNoBind(
int textureSectionX, int textureSectionY, int textureSectionZ, byte[] data
) {
if (uploadPBO != -1) {
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, uploadPBO);
GL15.glBufferData(GL31.GL_PIXEL_UNPACK_BUFFER, SKY_SECTION_UPLOAD_BUFFER.capacity(), GL15.GL_STREAM_DRAW);
ByteBuffer mapped = GL15.glMapBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, GL15.GL_WRITE_ONLY);
if (mapped != null) {
mapped.put(data);
GL15.glUnmapBuffer(GL31.GL_PIXEL_UNPACK_BUFFER);
GL30.glTexSubImage3D(
GL12.GL_TEXTURE_3D, 0,
textureSectionX * CHUNK_SIZE, textureSectionY * 16, textureSectionZ * CHUNK_SIZE,
16, 16, 16,
GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, 0L
);
}
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, 0);
} else {
SKY_SECTION_UPLOAD_BUFFER.clear();
SKY_SECTION_UPLOAD_BUFFER.put(data);
SKY_SECTION_UPLOAD_BUFFER.flip();
GL30.glTexSubImage3D( GL30.glTexSubImage3D(
GL12.GL_TEXTURE_3D, 0, GL12.GL_TEXTURE_3D, 0,
@@ -212,7 +271,18 @@ public final class FogLightVolume {
16, 16, 16, 16, 16, 16,
GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, SKY_SECTION_UPLOAD_BUFFER GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, SKY_SECTION_UPLOAD_BUFFER
); );
}
}
public static void beginUpload(int texture) {
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
}
public static void endUpload() {
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0); GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
} }
@@ -229,6 +299,7 @@ public final class FogLightVolume {
int[] widthChunks, int[] depthChunks, int[] widthChunks, int[] depthChunks,
int count int count
) { ) {
// Use PBO-based clears for OpenGL 3.2 compatibility
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0); GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0); GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);
@@ -268,4 +339,12 @@ public final class FogLightVolume {
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, 0); GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, 0);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0); GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
} }
private static void initUploadPBO() {
if (uploadPBO != -1) return;
uploadPBO = GL15.glGenBuffers();
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, uploadPBO);
GL15.glBufferData(GL31.GL_PIXEL_UNPACK_BUFFER, BLOCK_SECTION_UPLOAD_BUFFER.capacity(), GL15.GL_STREAM_DRAW);
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, 0);
}
} }
@@ -103,6 +103,9 @@ public final class FogSystem {
ringChunkOffsetX, ringChunkOffsetX,
ringChunkOffsetZ ringChunkOffsetZ
); );
// Boost uploads for a few frames to catch up after a window shift
FogChunkStreamer.setBurst(8);
FogLightStreamer.setBurst(8);
} }
public static int originChunkX() { public static int originChunkX() {
@@ -6,6 +6,7 @@ import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31; import org.lwjgl.opengl.GL31;
// No GL43 usage: keep compatibility with OpenGL 3.2
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -41,6 +42,8 @@ public final class FogWorldVolume {
// PBO для быстрой очистки регионов // PBO для быстрой очистки регионов
private static int clearPBO = -1; private static int clearPBO = -1;
// PBO для загрузки чанков
private static int uploadPBO = -1;
private static final int MAX_CLEAR_SIZE = SIZE_X * SIZE_Y * SIZE_Z; private static final int MAX_CLEAR_SIZE = SIZE_X * SIZE_Y * SIZE_Z;
private FogWorldVolume() { private FogWorldVolume() {
@@ -72,6 +75,7 @@ public final class FogWorldVolume {
// Инициализируем PBO ДО создания текстуры // Инициализируем PBO ДО создания текстуры
initClearPBO(); initClearPBO();
initUploadPBO();
texture = GL11.glGenTextures(); texture = GL11.glGenTextures();
@@ -196,6 +200,76 @@ public final class FogWorldVolume {
uploadChunk(textureChunkX, textureChunkZ, buffer); uploadChunk(textureChunkX, textureChunkZ, buffer);
} }
public static void beginUpload() {
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
}
public static void uploadChunkFromArrayNoBind(
int textureChunkX,
int textureChunkZ,
byte[] data
) {
// Use PBO to upload without stalling the driver
if (uploadPBO != -1) {
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, uploadPBO);
// Orphan and allocate
GL15.glBufferData(GL31.GL_PIXEL_UNPACK_BUFFER, CHUNK_SIZE * SIZE_Y * CHUNK_SIZE, GL15.GL_STREAM_DRAW);
ByteBuffer mapped = GL15.glMapBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, GL15.GL_WRITE_ONLY);
if (mapped != null) {
mapped.put(data);
GL15.glUnmapBuffer(GL31.GL_PIXEL_UNPACK_BUFFER);
GL30.glTexSubImage3D(
GL12.GL_TEXTURE_3D,
0,
textureChunkX * CHUNK_SIZE,
0,
textureChunkZ * CHUNK_SIZE,
CHUNK_SIZE,
SIZE_Y,
CHUNK_SIZE,
GL30.GL_RED_INTEGER,
GL11.GL_UNSIGNED_BYTE,
0L
);
}
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, 0);
} else {
ByteBuffer buffer = chunkBuffer();
buffer.put(data);
buffer.flip();
GL30.glTexSubImage3D(
GL12.GL_TEXTURE_3D,
0,
textureChunkX * CHUNK_SIZE,
0,
textureChunkZ * CHUNK_SIZE,
CHUNK_SIZE,
SIZE_Y,
CHUNK_SIZE,
GL30.GL_RED_INTEGER,
GL11.GL_UNSIGNED_BYTE,
buffer
);
}
}
public static void endUpload() {
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
}
private static void initUploadPBO() {
if (uploadPBO != -1) return;
uploadPBO = GL15.glGenBuffers();
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, uploadPBO);
GL15.glBufferData(GL31.GL_PIXEL_UNPACK_BUFFER, CHUNK_SIZE * SIZE_Y * CHUNK_SIZE, GL15.GL_STREAM_DRAW);
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, 0);
}
public static ByteBuffer chunkBuffer() { public static ByteBuffer chunkBuffer() {
CHUNK_BUFFER.clear(); CHUNK_BUFFER.clear();
return CHUNK_BUFFER; return CHUNK_BUFFER;
@@ -214,6 +288,7 @@ public final class FogWorldVolume {
int[] widthChunks, int[] depthChunks, int[] widthChunks, int[] depthChunks,
int count int count
) { ) {
// Use PBO-based clears for OpenGL 3.2 compatibility
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture); GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, clearPBO); GL15.glBindBuffer(GL31.GL_PIXEL_UNPACK_BUFFER, clearPBO);