From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Mon, 24 Feb 2025 21:33:24 +0100 Subject: [PATCH] Rewrite ClientboundLightUpdatePacketData MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On writing operations: At 256 sections with 0.75 fill ratio: 18x faster (333.489 μs → 18.606 μs) At 256 sections with 0.5 fill ratio: 13.5x faster (147.804 μs → 10.962 μs) diff --git a/net/minecraft/network/protocol/game/ClientboundLightUpdatePacketData.java b/net/minecraft/network/protocol/game/ClientboundLightUpdatePacketData.java index a0b54f3a3d11e0f0f1cb806406a870ba36da8f07..234280499fe1bc495bcdd4c3e144d1f99b7e6975 100644 --- a/net/minecraft/network/protocol/game/ClientboundLightUpdatePacketData.java +++ b/net/minecraft/network/protocol/game/ClientboundLightUpdatePacketData.java @@ -16,30 +16,113 @@ import net.minecraft.world.level.lighting.LevelLightEngine; public class ClientboundLightUpdatePacketData { private static final StreamCodec DATA_LAYER_STREAM_CODEC = ByteBufCodecs.byteArray(2048); + + // Leaf start - Rewrite ClientboundLightUpdatePacketData + // Static constants to avoid allocations + private static final byte[][] EMPTY_ARRAY = new byte[0][]; + + // Pre-sized arrays to avoid dynamic resizing + private static final ThreadLocal SKY_BUFFER = ThreadLocal.withInitial(() -> new byte[256][]); + private static final ThreadLocal BLOCK_BUFFER = ThreadLocal.withInitial(() -> new byte[256][]); + + // Pre-cached BitSets with fixed size private final BitSet skyYMask; private final BitSet blockYMask; private final BitSet emptySkyYMask; private final BitSet emptyBlockYMask; - private final List skyUpdates; - private final List blockUpdates; + + // Fixed arrays with exact counts + private final byte[][] skyUpdates; + private final byte[][] blockUpdates; + private final int skyUpdateCount; + private final int blockUpdateCount; + // Leaf end - Rewrite ClientboundLightUpdatePacketData public ClientboundLightUpdatePacketData(ChunkPos chunkPos, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight) { - this.skyYMask = new BitSet(); - this.blockYMask = new BitSet(); - this.emptySkyYMask = new BitSet(); - this.emptyBlockYMask = new BitSet(); - this.skyUpdates = Lists.newArrayList(); - this.blockUpdates = Lists.newArrayList(); - - for (int i = 0; i < lightEngine.getLightSectionCount(); i++) { + // Leaf start - Rewrite ClientboundLightUpdatePacketData + int sectionCount = lightEngine.getLightSectionCount(); + + // Round up to nearest long boundary (64 bits) to prevent BitSet expansion + int longWords = (sectionCount + 63) >>> 6; + int bitSetSize = longWords << 6; + + // Pre-size all BitSets to exact size needed + this.skyYMask = new BitSet(bitSetSize); + this.blockYMask = new BitSet(bitSetSize); + this.emptySkyYMask = new BitSet(bitSetSize); + this.emptyBlockYMask = new BitSet(bitSetSize); + + // Get buffer arrays from thread local storage to avoid allocations + byte[][] skyBuffer = SKY_BUFFER.get(); + byte[][] blockBuffer = BLOCK_BUFFER.get(); + + // Process all sections in a single pass + int skyCount = 0; + int blockCount = 0; + int minLightSection = lightEngine.getMinLightSection(); + + // Cache layer listeners to avoid repeated method calls + var skyLayerListener = lightEngine.getLayerListener(LightLayer.SKY); + var blockLayerListener = lightEngine.getLayerListener(LightLayer.BLOCK); + + // Single pass through all sections + for (int i = 0; i < sectionCount; i++) { + int sectionY = minLightSection + i; + SectionPos sectionPos = SectionPos.of(chunkPos.x, sectionY, chunkPos.z); + + // Process sky light if (skyLight == null || skyLight.get(i)) { - this.prepareSectionData(chunkPos, lightEngine, LightLayer.SKY, i, this.skyYMask, this.emptySkyYMask, this.skyUpdates); + DataLayer skyData = skyLayerListener.getDataLayerData(sectionPos); + if (skyData != null) { + if (skyData.isEmpty()) { + emptySkyYMask.set(i); + } else { + skyYMask.set(i); + // Store in buffer temporarily - only clone at the end + skyBuffer[skyCount++] = skyData.getData(); + } + } } + // Process block light if (blockLight == null || blockLight.get(i)) { - this.prepareSectionData(chunkPos, lightEngine, LightLayer.BLOCK, i, this.blockYMask, this.emptyBlockYMask, this.blockUpdates); + DataLayer blockData = blockLayerListener.getDataLayerData(sectionPos); + if (blockData != null) { + if (blockData.isEmpty()) { + emptyBlockYMask.set(i); + } else { + blockYMask.set(i); + // Store in buffer temporarily - only clone at the end + blockBuffer[blockCount++] = blockData.getData(); + } + } + } + } + + // Create final arrays with exact sizes + if (skyCount > 0) { + this.skyUpdates = new byte[skyCount][]; + // Clone only at the end to minimize work + for (int i = 0; i < skyCount; i++) { + this.skyUpdates[i] = skyBuffer[i].clone(); + } + } else { + this.skyUpdates = EMPTY_ARRAY; + } + + if (blockCount > 0) { + this.blockUpdates = new byte[blockCount][]; + // Clone only at the end to minimize work + for (int i = 0; i < blockCount; i++) { + this.blockUpdates[i] = blockBuffer[i].clone(); } + } else { + this.blockUpdates = EMPTY_ARRAY; } + + this.skyUpdateCount = skyCount; + this.blockUpdateCount = blockCount; + // Leaf end - Rewrite ClientboundLightUpdatePacketData } public ClientboundLightUpdatePacketData(FriendlyByteBuf buffer, int x, int z) { @@ -47,8 +130,30 @@ public class ClientboundLightUpdatePacketData { this.blockYMask = buffer.readBitSet(); this.emptySkyYMask = buffer.readBitSet(); this.emptyBlockYMask = buffer.readBitSet(); - this.skyUpdates = buffer.readList(DATA_LAYER_STREAM_CODEC); - this.blockUpdates = buffer.readList(DATA_LAYER_STREAM_CODEC); + + // Leaf start - Rewrite ClientboundLightUpdatePacketData + // Read lists directly as arrays to avoid intermediate collections + List skyList = buffer.readList(DATA_LAYER_STREAM_CODEC); + List blockList = buffer.readList(DATA_LAYER_STREAM_CODEC); + + int skySize = skyList.size(); + int blockSize = blockList.size(); + + if (skySize > 0) { + this.skyUpdates = skyList.toArray(new byte[skySize][]); + } else { + this.skyUpdates = EMPTY_ARRAY; + } + + if (blockSize > 0) { + this.blockUpdates = blockList.toArray(new byte[blockSize][]); + } else { + this.blockUpdates = EMPTY_ARRAY; + } + + this.skyUpdateCount = skySize; + this.blockUpdateCount = blockSize; + // Leaf end - Rewrite ClientboundLightUpdatePacketData } public void write(FriendlyByteBuf buffer) { @@ -56,25 +161,33 @@ public class ClientboundLightUpdatePacketData { buffer.writeBitSet(this.blockYMask); buffer.writeBitSet(this.emptySkyYMask); buffer.writeBitSet(this.emptyBlockYMask); - buffer.writeCollection(this.skyUpdates, DATA_LAYER_STREAM_CODEC); - buffer.writeCollection(this.blockUpdates, DATA_LAYER_STREAM_CODEC); - } - private void prepareSectionData( - ChunkPos chunkPos, LevelLightEngine levelLightEngine, LightLayer lightLayer, int index, BitSet skyLight, BitSet blockLight, List updates - ) { - DataLayer dataLayerData = levelLightEngine.getLayerListener(lightLayer) - .getDataLayerData(SectionPos.of(chunkPos, levelLightEngine.getMinLightSection() + index)); - if (dataLayerData != null) { - if (dataLayerData.isEmpty()) { - blockLight.set(index); - } else { - skyLight.set(index); - updates.add(dataLayerData.copy().getData()); + // Leaf start - Rewrite ClientboundLightUpdatePacketData + // Avoid creating unnecessary objects when writing + if (this.skyUpdateCount > 0) { + // Use direct array access for efficiency + buffer.writeVarInt(this.skyUpdateCount); + for (int i = 0; i < this.skyUpdateCount; i++) { + DATA_LAYER_STREAM_CODEC.encode(buffer, this.skyUpdates[i]); } + } else { + buffer.writeVarInt(0); } + + if (this.blockUpdateCount > 0) { + // Use direct array access for efficiency + buffer.writeVarInt(this.blockUpdateCount); + for (int i = 0; i < this.blockUpdateCount; i++) { + DATA_LAYER_STREAM_CODEC.encode(buffer, this.blockUpdates[i]); + } + } else { + buffer.writeVarInt(0); + } + // Leaf end - Rewrite ClientboundLightUpdatePacketData } + // Getter methods + public BitSet getSkyYMask() { return this.skyYMask; } @@ -84,7 +197,7 @@ public class ClientboundLightUpdatePacketData { } public List getSkyUpdates() { - return this.skyUpdates; + return this.skyUpdateCount > 0 ? java.util.Arrays.asList(this.skyUpdates) : List.of(); // Leaf - Rewrite ClientboundLightUpdatePacketData } public BitSet getBlockYMask() { @@ -96,6 +209,6 @@ public class ClientboundLightUpdatePacketData { } public List getBlockUpdates() { - return this.blockUpdates; + return this.blockUpdateCount > 0 ? java.util.Arrays.asList(this.blockUpdates) : List.of(); // Leaf - Rewrite ClientboundLightUpdatePacketData } }