Сделаны бескомпромиссные оптимизации
This commit is contained in:
@@ -5,7 +5,6 @@ import net.minecraft.client.MinecraftClient;
|
|||||||
import net.minecraft.client.gl.Framebuffer;
|
import net.minecraft.client.gl.Framebuffer;
|
||||||
import net.minecraft.client.render.Camera;
|
import net.minecraft.client.render.Camera;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.LightType;
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.lwjgl.BufferUtils;
|
import org.lwjgl.BufferUtils;
|
||||||
import org.lwjgl.opengl.*;
|
import org.lwjgl.opengl.*;
|
||||||
|
|||||||
@@ -60,6 +60,16 @@ public final class FogBlockRegistry {
|
|||||||
return new FogBlockProperties(solid, 1, 45).pack();
|
return new FogBlockProperties(solid, 1, 45).pack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (block == Blocks.SOUL_CAMPFIRE || block == Blocks.SOUL_FIRE) {
|
||||||
|
return new FogBlockProperties(solid, 1, 63).pack();
|
||||||
|
}
|
||||||
|
if (block == Blocks.SOUL_LANTERN) {
|
||||||
|
return new FogBlockProperties(solid, 1, 31).pack();
|
||||||
|
}
|
||||||
|
if (block == Blocks.SOUL_TORCH || block == Blocks.SOUL_WALL_TORCH) {
|
||||||
|
return new FogBlockProperties(solid, 1, 15).pack();
|
||||||
|
}
|
||||||
|
|
||||||
// Поглотители тумана (sign = -1)
|
// Поглотители тумана (sign = -1)
|
||||||
if (block == Blocks.LAVA) {
|
if (block == Blocks.LAVA) {
|
||||||
return new FogBlockProperties(solid, -1, 50).pack();
|
return new FogBlockProperties(solid, -1, 50).pack();
|
||||||
@@ -67,18 +77,17 @@ public final class FogBlockRegistry {
|
|||||||
if (block == Blocks.MAGMA_BLOCK) {
|
if (block == Blocks.MAGMA_BLOCK) {
|
||||||
return new FogBlockProperties(solid, -1, 40).pack();
|
return new FogBlockProperties(solid, -1, 40).pack();
|
||||||
}
|
}
|
||||||
if (block == Blocks.FIRE || block == Blocks.SOUL_FIRE) {
|
if (block == Blocks.FIRE) {
|
||||||
return new FogBlockProperties(solid, -1, 30).pack();
|
return new FogBlockProperties(solid, -1, 30).pack();
|
||||||
}
|
}
|
||||||
if (block == Blocks.CAMPFIRE || block == Blocks.SOUL_CAMPFIRE) {
|
if (block == Blocks.CAMPFIRE) {
|
||||||
return new FogBlockProperties(solid, -1, 25).pack();
|
return new FogBlockProperties(solid, -1, 63).pack();
|
||||||
}
|
}
|
||||||
if (block == Blocks.TORCH || block == Blocks.WALL_TORCH ||
|
if (block == Blocks.TORCH || block == Blocks.WALL_TORCH) {
|
||||||
block == Blocks.SOUL_TORCH || block == Blocks.SOUL_WALL_TORCH) {
|
|
||||||
return new FogBlockProperties(solid, -1, 15).pack();
|
return new FogBlockProperties(solid, -1, 15).pack();
|
||||||
}
|
}
|
||||||
if (block == Blocks.LANTERN || block == Blocks.SOUL_LANTERN) {
|
if (block == Blocks.LANTERN) {
|
||||||
return new FogBlockProperties(solid, -1, 10).pack();
|
return new FogBlockProperties(solid, -1, 25).pack();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Нейтральные блоки (sign = 0)
|
// Нейтральные блоки (sign = 0)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package su.divan2000.veila.client.render.fog;
|
package su.divan2000.veila.client.render.fog;
|
||||||
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.ChunkSectionPos;
|
import net.minecraft.util.math.ChunkSectionPos;
|
||||||
import net.minecraft.world.LightType;
|
|
||||||
import net.minecraft.world.chunk.ChunkNibbleArray;
|
import net.minecraft.world.chunk.ChunkNibbleArray;
|
||||||
import net.minecraft.world.chunk.light.ChunkLightProvider;
|
import net.minecraft.world.chunk.light.ChunkLightProvider;
|
||||||
import net.minecraft.world.chunk.light.LightingProvider;
|
import net.minecraft.world.chunk.light.LightingProvider;
|
||||||
@@ -13,6 +11,16 @@ public final class FogLightProvider {
|
|||||||
// Размер массива для одной секции: 16×16×16×2 = 8192 (×2 для двух каналов)
|
// Размер массива для одной секции: 16×16×16×2 = 8192 (×2 для двух каналов)
|
||||||
public static final int SECTION_DATA_SIZE = 16 * 16 * 16 * 2;
|
public static final int SECTION_DATA_SIZE = 16 * 16 * 16 * 2;
|
||||||
|
|
||||||
|
// Pre-allocated buffer для чтения одной секции из LightingProvider
|
||||||
|
// используется в fillSectionIntoArray для избежания аллокаций
|
||||||
|
private static final byte[] SECTION_BUFFER = new byte[SECTION_DATA_SIZE];
|
||||||
|
|
||||||
|
// Флаг для отслеживания используется ли буфер сейчас (защита от race condition)
|
||||||
|
// Так как fillSectionIntoArray может вызываться из разных потоков,
|
||||||
|
// нужен второй буфер для параллельной работы
|
||||||
|
private static final byte[] SECTION_BUFFER_ALT = new byte[SECTION_DATA_SIZE];
|
||||||
|
private static volatile boolean bufferInUse = false;
|
||||||
|
|
||||||
private FogLightProvider() {
|
private FogLightProvider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +46,15 @@ public final class FogLightProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] result = new byte[SECTION_DATA_SIZE];
|
// Используем один из двух pre-allocated буферов (double buffering)
|
||||||
|
byte[] result;
|
||||||
|
if (!bufferInUse) {
|
||||||
|
bufferInUse = true;
|
||||||
|
result = SECTION_BUFFER;
|
||||||
|
} else {
|
||||||
|
result = SECTION_BUFFER_ALT;
|
||||||
|
}
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
for (int localZ = 0; localZ < 16; localZ++) {
|
for (int localZ = 0; localZ < 16; localZ++) {
|
||||||
@@ -47,13 +63,17 @@ public final class FogLightProvider {
|
|||||||
int blockLight = (blockArray != null) ? blockArray.get(localX, localY, localZ) : 0;
|
int blockLight = (blockArray != null) ? blockArray.get(localX, localY, localZ) : 0;
|
||||||
int skyLight = (skyArray != null) ? skyArray.get(localX, localY, localZ) : 0;
|
int skyLight = (skyArray != null) ? skyArray.get(localX, localY, localZ) : 0;
|
||||||
|
|
||||||
// Упаковка в два байта: R=blockLight, G=skyLight
|
|
||||||
FogLightVolume.packLight(blockLight, skyLight, result, index);
|
FogLightVolume.packLight(blockLight, skyLight, result, index);
|
||||||
index += 2;
|
index += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Освобождаем буфер
|
||||||
|
if (result == SECTION_BUFFER) {
|
||||||
|
bufferInUse = false;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -286,34 +286,51 @@ public final class FogLightStreamer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void prepareInitialLoads() {
|
/**
|
||||||
|
* Подготавливает все секции для загрузки (client thread).
|
||||||
|
* Обрабатывает и initialLoadMask, и pendingSectionsMask.
|
||||||
|
* Вызывается из FogSystem.tick().
|
||||||
|
*/
|
||||||
|
public static void prepareAllSections() {
|
||||||
int processed = 0;
|
int processed = 0;
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
|
// Обрабатываем initial load
|
||||||
|
int index = 0;
|
||||||
while (processed < MAX_INITIAL_SECTIONS_PER_TICK) {
|
while (processed < MAX_INITIAL_SECTIONS_PER_TICK) {
|
||||||
index = nextSetBit(initialLoadMask, index);
|
index = nextSetBit(initialLoadMask, index);
|
||||||
if (index < 0) break;
|
if (index < 0) break;
|
||||||
|
|
||||||
// Конвертируем индекс обратно в координаты секции
|
if (prepareSectionByIndex(index)) {
|
||||||
int sectionX = currentOriginChunkX + (index % FogLightVolume.CHUNKS_X);
|
|
||||||
int sectionZ = currentOriginChunkZ + ((index / FogLightVolume.CHUNKS_X) % FogLightVolume.CHUNKS_Z);
|
|
||||||
int sectionY = (index / (FogLightVolume.CHUNKS_X * FogLightVolume.CHUNKS_Z)) + (FogWorldVolume.MIN_Y >> 4);
|
|
||||||
|
|
||||||
byte[] sectionData = FogLightProvider.fillSectionIntoArray(sectionX, sectionY, sectionZ);
|
|
||||||
if (sectionData != null) {
|
|
||||||
clearBit(initialLoadMask, index);
|
clearBit(initialLoadMask, index);
|
||||||
setBit(readySectionsMask, index);
|
processed++;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обрабатываем инкрементальные обновления
|
||||||
|
index = 0;
|
||||||
|
while (processed < MAX_INITIAL_SECTIONS_PER_TICK + MAX_SECTIONS_PER_FRAME) {
|
||||||
|
index = nextSetBit(pendingSectionsMask, index);
|
||||||
|
if (index < 0) break;
|
||||||
|
|
||||||
|
if (prepareSectionByIndex(index)) {
|
||||||
|
clearBit(pendingSectionsMask, index);
|
||||||
processed++;
|
processed++;
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Загружает готовые секции в текстуру (render thread).
|
||||||
|
* Только OpenGL вызовы, без чтения из LightingProvider.
|
||||||
|
* Вызывается из FogSystem.beginRender().
|
||||||
|
*/
|
||||||
public static void uploadReadySections() {
|
public static void uploadReadySections() {
|
||||||
int processed = 0;
|
int processed = 0;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
while (processed < MAX_SECTIONS_PER_FRAME) {
|
while (processed < MAX_SECTIONS_PER_FRAME + MAX_INITIAL_SECTIONS_PER_TICK) {
|
||||||
index = nextSetBit(readySectionsMask, index);
|
index = nextSetBit(readySectionsMask, index);
|
||||||
if (index < 0) break;
|
if (index < 0) break;
|
||||||
|
|
||||||
@@ -325,22 +342,29 @@ public final class FogLightStreamer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void processSectionUpdates() {
|
/**
|
||||||
int processed = 0;
|
* Читает данные секции из LightingProvider и помечает как готовую к загрузке.
|
||||||
int index = 0;
|
* Вызывается из prepareAllSections() в client thread.
|
||||||
|
*/
|
||||||
|
private static boolean prepareSectionByIndex(int index) {
|
||||||
|
int sectionX = currentOriginChunkX + (index % FogLightVolume.CHUNKS_X);
|
||||||
|
int sectionZ = currentOriginChunkZ + ((index / FogLightVolume.CHUNKS_X) % FogLightVolume.CHUNKS_Z);
|
||||||
|
int sectionY = (index / (FogLightVolume.CHUNKS_X * FogLightVolume.CHUNKS_Z)) + (FogWorldVolume.MIN_Y >> 4);
|
||||||
|
|
||||||
while (processed < MAX_SECTIONS_PER_FRAME) {
|
byte[] sectionData = FogLightProvider.fillSectionIntoArray(sectionX, sectionY, sectionZ);
|
||||||
index = nextSetBit(pendingSectionsMask, index);
|
if (sectionData == null) {
|
||||||
if (index < 0) break;
|
return false; // Секция не готова, пробуем позже
|
||||||
|
|
||||||
if (uploadSectionByIndex(index)) {
|
|
||||||
clearBit(pendingSectionsMask, index);
|
|
||||||
processed++;
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Помечаем как готовую к загрузке в render thread
|
||||||
|
setBit(readySectionsMask, index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Загружает готовую секцию в текстуру через OpenGL.
|
||||||
|
* Вызывается из uploadReadySections() в render thread.
|
||||||
|
*/
|
||||||
private static boolean uploadSectionByIndex(int index) {
|
private static boolean uploadSectionByIndex(int index) {
|
||||||
int physicalX = index % FogLightVolume.CHUNKS_X;
|
int physicalX = index % FogLightVolume.CHUNKS_X;
|
||||||
int physicalZ = (index / FogLightVolume.CHUNKS_X) % FogLightVolume.CHUNKS_Z;
|
int physicalZ = (index / FogLightVolume.CHUNKS_X) % FogLightVolume.CHUNKS_Z;
|
||||||
@@ -350,6 +374,7 @@ public final class FogLightStreamer {
|
|||||||
int sectionZ = currentOriginChunkZ + Math.floorMod(physicalZ - currentRingChunkOffsetZ, FogLightVolume.CHUNKS_Z);
|
int sectionZ = currentOriginChunkZ + Math.floorMod(physicalZ - currentRingChunkOffsetZ, FogLightVolume.CHUNKS_Z);
|
||||||
int sectionY = localSectionY + (FogWorldVolume.MIN_Y >> 4);
|
int sectionY = localSectionY + (FogWorldVolume.MIN_Y >> 4);
|
||||||
|
|
||||||
|
// Читаем данные повторно (они должны быть доступны быстро, так как уже в кэше)
|
||||||
byte[] sectionData = FogLightProvider.fillSectionIntoArray(sectionX, sectionY, sectionZ);
|
byte[] sectionData = FogLightProvider.fillSectionIntoArray(sectionX, sectionY, sectionZ);
|
||||||
if (sectionData == null) {
|
if (sectionData == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ public final class FogLightVolume {
|
|||||||
private static int clearPBO = -1;
|
private static int clearPBO = -1;
|
||||||
private static final int MAX_CLEAR_SIZE = SIZE_X * SIZE_Y * SIZE_Z * 2; // ×2 для двух каналов
|
private static final int MAX_CLEAR_SIZE = SIZE_X * SIZE_Y * SIZE_Z * 2; // ×2 для двух каналов
|
||||||
|
|
||||||
|
// Pre-allocated buffer для одной секции (4096 вокселей × 2 байта = 8192 байта)
|
||||||
|
// Используется для загрузки секций через uploadSection
|
||||||
|
private static final ByteBuffer SECTION_UPLOAD_BUFFER =
|
||||||
|
BufferUtils.createByteBuffer(FogLightStreamer.SECTION_SIZE
|
||||||
|
* FogLightStreamer.SECTION_SIZE
|
||||||
|
* FogLightStreamer.SECTION_SIZE * 2);
|
||||||
|
|
||||||
private FogLightVolume() {
|
private FogLightVolume() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,9 +167,10 @@ public final class FogLightVolume {
|
|||||||
int textureSectionZ,
|
int textureSectionZ,
|
||||||
byte[] data
|
byte[] data
|
||||||
) {
|
) {
|
||||||
ByteBuffer sectionBuffer = BufferUtils.createByteBuffer(data.length);
|
// Переиспользуем статический буфер вместо создания нового
|
||||||
sectionBuffer.put(data);
|
SECTION_UPLOAD_BUFFER.clear();
|
||||||
sectionBuffer.flip();
|
SECTION_UPLOAD_BUFFER.put(data);
|
||||||
|
SECTION_UPLOAD_BUFFER.flip();
|
||||||
|
|
||||||
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
|
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
|
||||||
|
|
||||||
@@ -182,7 +190,7 @@ public final class FogLightVolume {
|
|||||||
16,
|
16,
|
||||||
GL30.GL_RG,
|
GL30.GL_RG,
|
||||||
GL11.GL_UNSIGNED_BYTE,
|
GL11.GL_UNSIGNED_BYTE,
|
||||||
sectionBuffer
|
SECTION_UPLOAD_BUFFER
|
||||||
);
|
);
|
||||||
|
|
||||||
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ public final class FogSystem {
|
|||||||
|
|
||||||
// Освещение: загружаем готовые секции + инкрементальные обновления (render thread)
|
// Освещение: загружаем готовые секции + инкрементальные обновления (render thread)
|
||||||
FogLightStreamer.uploadReadySections();
|
FogLightStreamer.uploadReadySections();
|
||||||
FogLightStreamer.processSectionUpdates();
|
|
||||||
|
|
||||||
FogSimulationManager.update();
|
FogSimulationManager.update();
|
||||||
}
|
}
|
||||||
@@ -41,7 +40,7 @@ public final class FogSystem {
|
|||||||
FogChunkStreamer.processPending();
|
FogChunkStreamer.processPending();
|
||||||
|
|
||||||
// Освещение: читаем данные секций (client thread, без OpenGL)
|
// Освещение: читаем данные секций (client thread, без OpenGL)
|
||||||
FogLightStreamer.prepareInitialLoads();
|
FogLightStreamer.prepareAllSections();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initialize() {
|
private static void initialize() {
|
||||||
|
|||||||
@@ -210,7 +210,8 @@ void main()
|
|||||||
|
|
||||||
// Вклад ослабляется текущим transmittance
|
// Вклад ослабляется текущим transmittance
|
||||||
skyScattered += lightValues.x * baseScattering * transmittance;
|
skyScattered += lightValues.x * baseScattering * transmittance;
|
||||||
blockScattered += lightValues.y * baseScattering * transmittance;
|
//Свет от блоков не должен делать значимо ярче те воксели, которые и так подсвечены солнцем. Солнечный свет очень яркий, с ним сложно сравниться
|
||||||
|
blockScattered += lightValues.y * baseScattering * transmittance*((lightValues.y-lightValues.x)*0.9+0.1);
|
||||||
ambientScattered += AMBIENT_INTENSITY * baseScattering * transmittance;
|
ambientScattered += AMBIENT_INTENSITY * baseScattering * transmittance;
|
||||||
|
|
||||||
// Обновляем transmittance: Beer-Lambert law
|
// Обновляем transmittance: Beer-Lambert law
|
||||||
|
|||||||
Reference in New Issue
Block a user