Skip to content

Commit

Permalink
Fix some bugs / optimize MixinWorldTick + Make a spotlessApply
Browse files Browse the repository at this point in the history
  • Loading branch information
quentin452 committed May 6, 2023
1 parent 40a010c commit dc42e6b
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
import java.util.*;
import java.util.concurrent.*;

import cpw.mods.fml.common.FMLLog;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.entity.Entity;
Expand All @@ -20,18 +16,23 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import cpw.mods.fml.common.FMLLog;
import fr.iamacat.multithreading.config.MultithreadingandtweaksMultithreadingConfig;

@Mixin(EntityLivingBase.class)
public abstract class MixinEntitiesRendering {

private final ConcurrentLinkedQueue<Entity> entityQueue = new ConcurrentLinkedQueue<>();
private final ExecutorService executorService = Executors.newFixedThreadPool(
MultithreadingandtweaksMultithreadingConfig.numberofcpus,
new ThreadFactoryBuilder().setNameFormat("Entity-Rendering-%d").build());
new ThreadFactoryBuilder().setNameFormat("Entity-Rendering-%d")
.build());
private static final int BATCH_SIZE = MultithreadingandtweaksMultithreadingConfig.batchsize;
private long lastRenderTime = System.currentTimeMillis();

protected abstract void myBindEntityTexture(Entity entity);

private int tickCounter;

protected abstract void myRenderShadow(Entity entity, double x, double y, double z, float yaw, float partialTicks);
Expand All @@ -56,7 +57,8 @@ public void onDoRender(Entity entity, double x, double y, double z, float yaw, f
renderEntities(entityQueue);
} catch (Exception e) {
// Log exception and rethrow
FMLLog.getLogger().error("Error rendering entities", e);
FMLLog.getLogger()
.error("Error rendering entities", e);
throw e;
} finally {
entityQueue.clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
public abstract class MixinLiquidRendering {

private static final int BATCH_SIZE = MultithreadingandtweaksMultithreadingConfig.batchsize;
private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(MultithreadingandtweaksMultithreadingConfig.numberofcpus);
private static final ExecutorService THREAD_POOL = Executors
.newFixedThreadPool(MultithreadingandtweaksMultithreadingConfig.numberofcpus);

@Inject(method = "tesselate", at = @At("HEAD"), cancellable = true)
private void onTesselate(IBlockAccess world, BlockPos pos, Tessellator tessellator, int metadata, CallbackInfoReturnable<Boolean> ci) {
private void onTesselate(IBlockAccess world, BlockPos pos, Tessellator tessellator, int metadata,
CallbackInfoReturnable<Boolean> ci) {
if (MultithreadingandtweaksMultithreadingConfig.enableMixinLiquidRendering) {
ci.cancel();
tesselateFluid(world, pos, tessellator, metadata);
Expand All @@ -53,16 +55,25 @@ private void tesselateFluid(IBlockAccess world, BlockPos pos, Tessellator tessel
}
}

private void tesselateBatch(World world, List<BlockPos> positions, Tessellator tessellator, Set<BlockPos> visitedBlocks, int state, Queue<BlockPos> fluidBlocks) {
Block block = world.getBlock(positions.get(0).getX(), positions.get(0).getY(), positions.get(0).getZ());
private void tesselateBatch(World world, List<BlockPos> positions, Tessellator tessellator,
Set<BlockPos> visitedBlocks, int state, Queue<BlockPos> fluidBlocks) {
Block block = world.getBlock(
positions.get(0)
.getX(),
positions.get(0)
.getY(),
positions.get(0)
.getZ());
List<BlockPos> nextPositions = new ArrayList<>();

Minecraft minecraft = FMLClientHandler.instance().getClient();
Minecraft minecraft = FMLClientHandler.instance()
.getClient();
TextureMap textureMapBlocks = minecraft.getTextureMapBlocks();

for (BlockPos blockPos : positions) {
Block currentBlock = world.getBlock(blockPos.getX(), blockPos.getY(), blockPos.getZ());
if (currentBlock.getMaterial().isLiquid() && currentBlock == block) {
if (currentBlock.getMaterial()
.isLiquid() && currentBlock == block) {
for (EnumFacing direction : EnumFacing.values()) {
BlockPos offset = blockPos.offset(direction);
if (!visitedBlocks.add(offset)) {
Expand All @@ -71,11 +82,24 @@ private void tesselateBatch(World world, List<BlockPos> positions, Tessellator t

Block offsetBlock = world.getBlock(offset.getX(), offset.getY(), offset.getZ());
int offsetMetadata = world.getBlockMetadata(offset.getX(), offset.getY(), offset.getZ());
if (offsetBlock.getMaterial().isReplaceable() || (offsetBlock.getMaterial().isLiquid() && offsetBlock == block && offsetMetadata == state)) {
putFluidVertex(tessellator, blockPos.getX(), blockPos.getY(), blockPos.getZ(), textureMapBlocks.getTextureExtry(offsetBlock.getIcon(0, offsetMetadata).getIconName()), direction);
} else if (offsetBlock.getMaterial().isLiquid() && offsetBlock == block) {
nextPositions.add(offset);
}
if (offsetBlock.getMaterial()
.isReplaceable()
|| (offsetBlock.getMaterial()
.isLiquid() && offsetBlock == block
&& offsetMetadata == state)) {
putFluidVertex(
tessellator,
blockPos.getX(),
blockPos.getY(),
blockPos.getZ(),
textureMapBlocks.getTextureExtry(
offsetBlock.getIcon(0, offsetMetadata)
.getIconName()),
direction);
} else if (offsetBlock.getMaterial()
.isLiquid() && offsetBlock == block) {
nextPositions.add(offset);
}
}
}
}
Expand All @@ -84,7 +108,9 @@ private void tesselateBatch(World world, List<BlockPos> positions, Tessellator t
fluidBlocks.addAll(nextPositions);
}
}
private void putFluidVertex(Tessellator renderer, double x, double y, double z, TextureAtlasSprite sprite, EnumFacing facing) {

private void putFluidVertex(Tessellator renderer, double x, double y, double z, TextureAtlasSprite sprite,
EnumFacing facing) {
float minU, maxU, minV, maxV;
if (facing == EnumFacing.UP || facing == EnumFacing.DOWN) {
minU = sprite.getInterpolatedU(x * 8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.multiplayer.ChunkProviderClient;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;

import org.spongepowered.asm.mixin.Mixin;
Expand All @@ -24,6 +23,7 @@

@Mixin(WorldClient.class)
public abstract class MixinWorldgen {

private WorldClient world;

private final ExecutorService executorService = Executors.newFixedThreadPool(6);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

Expand All @@ -29,7 +28,8 @@ public abstract class MixinEntitySpawning {

private final ExecutorService executorService = Executors.newFixedThreadPool(
MultithreadingandtweaksMultithreadingConfig.numberofcpus,
new ThreadFactoryBuilder().setNameFormat("Entity-Spawning-%d").build());
new ThreadFactoryBuilder().setNameFormat("Entity-Spawning-%d")
.build());

private static final int BATCH_SIZE = MultithreadingandtweaksMultithreadingConfig.batchsize;
private final LinkedBlockingQueue<Entity> spawnQueue = new LinkedBlockingQueue<>();
Expand Down Expand Up @@ -87,11 +87,10 @@ private void spawnEntitiesInQueue(World world) {
value = "INVOKE",
target = "Lnet/minecraft/client/renderer/entity/Render;doRender(Lnet/minecraft/entity/Entity;DDDFF)V"))
private void redirectDoRenderEntities(Render render, Entity entity, double x, double y, double z, float yaw,
float partialTicks) {
float partialTicks) {
render.doRender(entity, x, y, z, yaw, partialTicks);
}


public void close() {
executorService.shutdown();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ private void onTick(CallbackInfo ci) {
if (MultithreadingandtweaksMultithreadingConfig.enableMixinEntityUpdate) {
AtomicInteger indexCounter = new AtomicInteger(0);
entitiesToUpdate.parallelStream()
.collect(Collectors.groupingByConcurrent(
entity -> (int) Math.floor(indexCounter.getAndIncrement() / (double) MAX_ENTITIES_PER_TICK)))
.collect(
Collectors.groupingByConcurrent(
entity -> (int) Math.floor(indexCounter.getAndIncrement() / (double) MAX_ENTITIES_PER_TICK)))
.values()
.parallelStream()
.forEach(batch -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import java.util.concurrent.*;
import java.util.stream.Collectors;

import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.world.Explosion;
import net.minecraft.world.World;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
public abstract class MixinWorldTick {

private final Object lock = new Object();
private static final int updatechunkatonce = 10;
private static final int UPDATE_CHUNK_AT_ONCE = 10;
private final ConcurrentLinkedQueue<Chunk> chunksToUpdate = new ConcurrentLinkedQueue<>();
private final Map<ChunkCoordIntPair, Chunk> loadedChunks = new ConcurrentHashMap<>();
private final Map<ChunkCoordIntPair, CompletableFuture<Chunk>> loadingChunks = new ConcurrentHashMap<>();

private final Object tickLock = new Object(); // Lock for accessing TickNextTick list

@Final
private ForkJoinPool executorService = new ForkJoinPool(
private volatile ForkJoinPool executorService = new ForkJoinPool(
MultithreadingandtweaksMultithreadingConfig.numberofcpus,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null,
Expand All @@ -47,67 +48,77 @@ private void onTick(CallbackInfo ci) {
}, executorService);
}
}

private void updateChunks(World world) throws Exception {
ChunkProviderServer chunkProvider = (ChunkProviderServer) world.getChunkProvider();
IChunkLoader chunkLoader = chunkProvider.currentChunkLoader;

// Add all chunks that need to be updated to the queue
for (Object chunk : chunkProvider.loadedChunks) {
Chunk loadedChunk = (Chunk) chunk;
ChunkCoordIntPair chunkCoord = loadedChunk.getChunkCoordIntPair();
loadedChunks.put(chunkCoord, loadedChunk);
chunksToUpdate.offer(loadedChunk);
List<Chunk> chunksToUpdateCopy;
synchronized (chunkProvider.loadedChunks) {
chunksToUpdateCopy = new ArrayList<>(chunkProvider.loadedChunks);
}

List<CompletableFuture<Chunk>> futures = new ArrayList<>();
Set<ChunkCoordIntPair> adjacentChunks = new HashSet<>();
Iterator<Chunk> iter = chunksToUpdateCopy.iterator();

ConcurrentHashMap<ChunkCoordIntPair, Chunk> loadedChunks = new ConcurrentHashMap<>((Map) chunkProvider.loadedChunks);
ConcurrentLinkedQueue<Chunk> chunksToUpdate = new ConcurrentLinkedQueue<>(chunksToUpdateCopy);
ConcurrentHashMap<ChunkCoordIntPair, CompletableFuture<Chunk>> loadingChunks = new ConcurrentHashMap<>();

while (iter.hasNext()) {
Chunk chunk = iter.next();
ChunkCoordIntPair chunkCoord = chunk.getChunkCoordIntPair();
loadedChunks.put(chunkCoord, chunk);
chunksToUpdate.offer(chunk);
adjacentChunks.addAll(getAdjacentChunks(chunkCoord));
iter.remove(); // remove chunk from the list while iterating
}

// Process the chunks in batches
while (!chunksToUpdate.isEmpty()) {
List<Chunk> batch = new ArrayList<>();
Set<ChunkCoordIntPair> adjacentChunks = new HashSet<>();
for (int i = 0; i < updatechunkatonce && !chunksToUpdate.isEmpty(); i++) {
Chunk chunk = chunksToUpdate.poll();
if (chunk != null) {
batch.add(chunk);
ChunkCoordIntPair chunkCoord = chunk.getChunkCoordIntPair();
adjacentChunks.addAll(getAdjacentChunks(chunkCoord));
}
for (int i = 0; i < UPDATE_CHUNK_AT_ONCE && !chunksToUpdate.isEmpty(); i++) {
batch.add(chunksToUpdate.poll());
}

// Load the adjacent chunks if they are not already loaded
for (ChunkCoordIntPair chunkCoord : adjacentChunks) {
if (!loadedChunks.containsKey(chunkCoord) && !loadingChunks.containsKey(chunkCoord)) {
loadingChunks.put(chunkCoord, CompletableFuture.supplyAsync(() -> {
CompletableFuture<Chunk> future = CompletableFuture.supplyAsync(() -> {
try {
Chunk adjacentChunk = chunkLoader
.loadChunk(world, chunkCoord.chunkXPos, chunkCoord.chunkZPos);
Chunk adjacentChunk;
synchronized (lock) {
adjacentChunk = chunkLoader.loadChunk(world, chunkCoord.chunkXPos, chunkCoord.chunkZPos);
chunkProvider.saveChunks(false, null);
}
loadedChunks.put(chunkCoord, adjacentChunk);
return adjacentChunk;
} catch (IOException e) {
throw new RuntimeException(e);
}
}, executorService));
}, executorService);
futures.add(future);
loadingChunks.put(chunkCoord, future);
}
}

// Wait for the adjacent chunks to finish loading
for (CompletableFuture<Chunk> future : loadingChunks.values()) {
for (CompletableFuture<Chunk> future : futures) {
Chunk adjacentChunk = future.join();
if (adjacentChunk != null) {
loadedChunks.put(adjacentChunk.getChunkCoordIntPair(), adjacentChunk);
}
}
loadingChunks.clear();
futures.clear();

// Process the batch of chunks
synchronized (tickLock) { // Synchronize access to TickNextTick list
processBatch(chunkProvider, batch);
synchronized (tickLock) {
processBatch(chunkProvider, new CopyOnWriteArrayList<>(batch));
}
}
}




private Set<ChunkCoordIntPair> getAdjacentChunks(ChunkCoordIntPair chunkCoord) {
Set<ChunkCoordIntPair> adjacentChunks = new HashSet<>();
Set<ChunkCoordIntPair> adjacentChunks = ConcurrentHashMap.newKeySet();
int chunkX = chunkCoord.chunkXPos;
int chunkZ = chunkCoord.chunkZPos;
int radius = 1;
Expand All @@ -124,17 +135,15 @@ private Set<ChunkCoordIntPair> getAdjacentChunks(ChunkCoordIntPair chunkCoord) {
private void processBatch(ChunkProviderServer chunkProvider, List<Chunk> batch) {
// Load the chunks from disk asynchronously and save them
CompletableFuture.runAsync(() -> {
batch.parallelStream()
.forEach(chunk -> {
synchronized (lock) {
chunkProvider.saveChunks(true, null);
}
});
// Save all loaded chunks
synchronized (lock) {
chunkProvider.saveChunks(true, null);
for (Chunk chunk : batch) {
chunkProvider.saveChunks(true, null);
}
// Save all loaded chunks
for (Chunk chunk : loadedChunks.values()) {
chunkProvider.saveChunks(true, null);
}
}
}, executorService);
}

}

0 comments on commit dc42e6b

Please sign in to comment.