Skip to content

Commit

Permalink
Improved client light processing performance by using a `Deduplicated…
Browse files Browse the repository at this point in the history
…LongQueue` instead of a `Set` of `BlockPos` objects
  • Loading branch information
Desoroxxx committed Dec 18, 2024
1 parent 6c01254 commit 503ad52
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 13 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project follows the [Ragnarök Versioning Convention](https://github.co

- `DeduplicatedLongQueue` now creates a new deduplication set instead of clearing it
- Client light processing now uses a queue which improves performance of light updates
- Improved client light processing performance by using a `DeduplicatedLongQueue` instead of a `Set` of `BlockPos` objects

### Fixed

Expand Down
6 changes: 6 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ java {
}

tasks {
listOf(deobfuscateMergedJarToSrg, srgifyBinpatchedJar).forEach {
it.configure {
accessTransformerFiles.from(project.files("src/main/resources/META-INF/${id}_at.cfg"))
}
}

processResources {
val expandProperties = mapOf(
"version" to project.version,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,50 @@
package dev.redstudio.alfheim.mixin.client;

import dev.redstudio.alfheim.api.ILightUpdatesProcessor;
import dev.redstudio.alfheim.utils.DeduplicatedLongQueue;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import java.util.*;
import java.util.Set;

import static dev.redstudio.alfheim.Alfheim.IS_NOTHIRIUM_LOADED;
import static dev.redstudio.alfheim.Alfheim.IS_VINTAGIUM_LOADED;
import static net.minecraft.util.math.BlockPos.*;

/**
* @author Luna Lage (Desoroxxx)
* @version 2024-12-18
* @since 1.0
*/
@SideOnly(Side.CLIENT)
@Mixin(RenderGlobal.class)
public abstract class RenderGlobalMixin implements ILightUpdatesProcessor {

@Unique private final DeduplicatedLongQueue alfheim$lightUpdatesQueue = new DeduplicatedLongQueue(8192);

@Shadow private ChunkRenderDispatcher renderDispatcher;
@Shadow @Final private Set<BlockPos> setLightUpdates;

@Shadow protected abstract void markBlocksForUpdate(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, boolean updateImmediately);

/**
* @author Luna Lage (Desoroxxx)
* @reason Use a deduplicated long queue instead of a set
* @since 1.5
*/
@Overwrite
public void notifyLightSet(final BlockPos blockPos) {
alfheim$lightUpdatesQueue.enqueue(blockPos.toLong());
}

/**
* Disable vanilla code to replace it with {@link #alfheim$processLightUpdates}
*
Expand All @@ -43,27 +58,29 @@ private boolean disableVanillaLightUpdates(final Set<BlockPos> instance) {
/**
* Fixes <a href="https://bugs.mojang.com/browse/MC-80966">MC-80966</a> by not checking if the chunk is empty or not.
* <p>
* Also improves performance by updating only the blocks in the set instead of every block in a 3x3 radius around each block in the set.
* Another performance improvement is using || instead of && allowing to skip earlier when there is nothing to update.
* It also improves performance by using a {@link DeduplicatedLongQueue} instead of a set.
* This removes the need to use an expensive iterator.
* It also reduces memory usage and GC pressure by using long primitives instead of a {@link BlockPos} object.
* <p>
* This also limits how many light updates are processed at once.
* Another performance improvement is using || instead of && allowing to skip earlier when there is nothing to update.
*
* @since 1.0
*/
@Override
public void alfheim$processLightUpdates() {
if (setLightUpdates.isEmpty() || (!IS_NOTHIRIUM_LOADED && !IS_VINTAGIUM_LOADED && renderDispatcher.hasNoFreeRenderBuilders()))
if (alfheim$lightUpdatesQueue.isEmpty() || (!IS_NOTHIRIUM_LOADED && !IS_VINTAGIUM_LOADED && renderDispatcher.hasNoFreeRenderBuilders()))
return;

final Queue<BlockPos> queue = new ArrayDeque<>(setLightUpdates);
BlockPos blockpos;
while (!alfheim$lightUpdatesQueue.isEmpty()) {
final long longPos = alfheim$lightUpdatesQueue.dequeue();

while ((blockpos = queue.poll()) != null) {
final int x = blockpos.getX();
final int y = blockpos.getY();
final int z = blockpos.getZ();
final int x = (int) (longPos << 64 - X_SHIFT - NUM_X_BITS >> 64 - NUM_X_BITS);
final int y = (int) (longPos << 64 - Y_SHIFT - NUM_Y_BITS >> 64 - NUM_Y_BITS);
final int z = (int) (longPos << 64 - NUM_Z_BITS >> 64 - NUM_Z_BITS);

markBlocksForUpdate(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1, false);
}

alfheim$lightUpdatesQueue.newDeduplicationSet();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
*/
public final class DeduplicatedLongQueue {

// TODO: Fully Implement my own implementation to get rid of the downsides of reduce etc...

private final LongArrayFIFOQueue queue;
private LongOpenHashSet set;

Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/META-INF/alfheim_at.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public net.minecraft.util.math.BlockPos field_177990_b # NUM_X_BITS
public net.minecraft.util.math.BlockPos field_177991_c # NUM_Z_BITS
public net.minecraft.util.math.BlockPos field_177989_d # NUM_Y_BITS
public net.minecraft.util.math.BlockPos field_177987_f # Y_SHIFT
public net.minecraft.util.math.BlockPos field_177988_g # X_SHIFT

0 comments on commit 503ad52

Please sign in to comment.