Implement chunk forcing during rendering for 1.15

This commit is contained in:
Jonas Herzig
2020-04-02 14:15:05 +02:00
parent 4ea13051fd
commit c119392c92
6 changed files with 230 additions and 37 deletions

View File

@@ -16,7 +16,7 @@ import com.replaymod.render.mixin.MainWindowAccessor;
import static com.replaymod.core.versions.MCVer.getWindow;
//#endif
//#if MC>=10800 && MC<11500
//#if MC>=10800
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
//#endif
@@ -44,7 +44,7 @@ public class ScreenshotRenderer implements RenderInfo {
boolean hideGUIBefore = mc.options.hudHidden;
mc.options.hudHidden = true;
//#if MC>=10800 && MC<11500
//#if MC>=10800
ChunkLoadingRenderGlobal clrg = new ChunkLoadingRenderGlobal(mc.worldRenderer);
//#endif
@@ -56,7 +56,7 @@ public class ScreenshotRenderer implements RenderInfo {
new ScreenshotWriter(settings.getOutputFile())).run();
}
//#if MC>=10800 && MC<11500
//#if MC>=10800
clrg.uninstall();
//#endif

View File

@@ -1,19 +1,23 @@
// FIXME 1.15 chunk rendering has changed significantly, ignoring this feature for now
//#if MC>=10800 && MC<11500
//#if MC>=10800
package com.replaymod.render.hooks;
import net.minecraft.client.render.WorldRenderer;
import java.lang.reflect.Field;
//#if MC>=11500
//#else
import com.replaymod.render.mixin.ChunkRenderDispatcherAccessor;
import com.replaymod.render.mixin.WorldRendererAccessor;
import com.replaymod.render.utils.JailingQueue;
import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.render.chunk.ChunkBuilder;
import net.minecraft.client.render.chunk.ChunkRenderTask;
import net.minecraft.client.render.chunk.ChunkRenderWorker;
import net.minecraft.client.render.chunk.ChunkRenderer;
import java.lang.reflect.Field;
import java.util.Iterator;
import static com.replaymod.core.versions.MCVer.*;
//#endif
//#if MC>=10904
import java.util.concurrent.PriorityBlockingQueue;
@@ -21,8 +25,6 @@ import java.util.concurrent.PriorityBlockingQueue;
//$$ import java.util.concurrent.BlockingQueue;
//#endif
import static com.replaymod.core.versions.MCVer.*;
public class ChunkLoadingRenderGlobal {
//#if MC>=11400
@@ -30,6 +32,9 @@ public class ChunkLoadingRenderGlobal {
//#else
//$$ private final RenderGlobal hooked;
//#endif
//#if MC>=11500
//#else
private ChunkBuilder renderDispatcher;
//#if MC>=11400
private JailingQueue<ChunkRenderTask> workerJailingQueue;
@@ -38,8 +43,8 @@ public class ChunkLoadingRenderGlobal {
//#endif
private ChunkRenderWorkerAccessor renderWorker;
private int frame;
//#endif
@SuppressWarnings("unchecked")
public ChunkLoadingRenderGlobal(
//#if MC>=11400
WorldRenderer renderGlobal
@@ -49,9 +54,29 @@ public class ChunkLoadingRenderGlobal {
) {
this.hooked = renderGlobal;
//#if MC>=11500
//$$ install();
//#else
setup(((WorldRendererAccessor) renderGlobal).getRenderDispatcher());
install();
//#endif
}
private void install() {
try {
//#if MC>=11400
Field hookField = WorldRenderer.class.getField("replayModRender_hook");
//#else
//$$ Field hookField = RenderGlobal.class.getField("replayModRender_hook");
//#endif
hookField.set(hooked, this);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
//#if MC>=11500
//#else
public void updateRenderDispatcher(ChunkBuilder renderDispatcher) {
if (this.renderDispatcher != null) {
workerJailingQueue.freeAll();
@@ -98,17 +123,6 @@ public class ChunkLoadingRenderGlobal {
workerJailingQueue.jail(workerThreads);
renderDispatcherAcc.setQueueChunkUpdates(queueChunkUpdates);
try {
//#if MC>=11400
Field hookField = WorldRenderer.class.getField("replayModRender_hook");
//#else
//$$ Field hookField = RenderGlobal.class.getField("replayModRender_hook");
//#endif
hookField.set(hooked, this);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
public void updateChunks() {
@@ -143,8 +157,15 @@ public class ChunkLoadingRenderGlobal {
}
}
public int nextFrameId() {
return frame++;
}
//#endif
public void uninstall() {
//#if MC<11500
workerJailingQueue.freeAll();
//#endif
try {
//#if MC>=11400
@@ -158,8 +179,10 @@ public class ChunkLoadingRenderGlobal {
}
}
public int nextFrameId() {
return frame++;
}
//#if MC>=11500
//$$ public interface IBlockOnChunkRebuilds {
//$$ boolean uploadEverythingBlocking();
//$$ }
//#endif
}
//#endif

View File

@@ -0,0 +1,111 @@
package com.replaymod.render.mixin;
//#if MC>=11500
//$$ import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
//$$ import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
//$$ import net.minecraft.client.render.chunk.ChunkBuilder;
//$$ import net.minecraft.util.thread.TaskExecutor;
//$$ import org.spongepowered.asm.mixin.Final;
//$$ import org.spongepowered.asm.mixin.Mixin;
//$$ import org.spongepowered.asm.mixin.Shadow;
//$$ import org.spongepowered.asm.mixin.injection.At;
//$$ import org.spongepowered.asm.mixin.injection.Inject;
//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
//$$
//$$ import java.util.Queue;
//$$ import java.util.concurrent.CompletableFuture;
//$$ import java.util.concurrent.locks.Condition;
//$$ import java.util.concurrent.locks.Lock;
//$$ import java.util.concurrent.locks.ReentrantLock;
//$$
//$$ @Mixin(ChunkBuilder.class)
//$$ public abstract class Mixin_BlockOnChunkRebuilds implements ChunkLoadingRenderGlobal.IBlockOnChunkRebuilds {
//$$ @Shadow @Final private Queue<BlockBufferBuilderStorage> threadBuffers;
//$$
//$$ @Shadow public abstract boolean upload();
//$$
//$$ @Shadow @Final private TaskExecutor<Runnable> mailbox;
//$$
//$$ @Shadow protected abstract void scheduleRunTasks();
//$$
//$$ @Shadow @Final private Queue<Runnable> uploadQueue;
//$$ private final Lock waitingForWorkLock = new ReentrantLock();
//$$ private final Condition newWork = waitingForWorkLock.newCondition();
//$$ private volatile boolean allDone;
//$$
//$$ private int totalBufferCount;
//$$
//$$ @Inject(method = "<init>", at = @At("RETURN"))
//$$ private void rememberTotalThreads(CallbackInfo ci) {
//$$ this.totalBufferCount = this.threadBuffers.size();
//$$ }
//$$
//$$ @Inject(method = "scheduleRunTasks", at = @At("RETURN"))
//$$ private void notifyMainThreadIfEverythingIsDone(CallbackInfo ci) {
//$$ if (this.threadBuffers.size() == this.totalBufferCount) {
//$$ // Looks like we're done, better notify the main thread in case the previous task didn't generate an upload
//$$ this.waitingForWorkLock.lock();
//$$ try {
//$$ this.allDone = true;
//$$ this.newWork.signalAll();
//$$ } finally {
//$$ this.waitingForWorkLock.unlock();
//$$ }
//$$ } else {
//$$ this.allDone = false;
//$$ }
//$$ }
//$$
//$$ @Inject(method = "scheduleUpload", at = @At("RETURN"))
//$$ private void notifyMainThreadOfNewUpload(CallbackInfoReturnable<CompletableFuture<Void>> ci) {
//$$ this.waitingForWorkLock.lock();
//$$ try {
//$$ this.newWork.signal();
//$$ } finally {
//$$ this.waitingForWorkLock.unlock();
//$$ }
//$$ }
//$$
//$$ private boolean waitForMainThreadWork() {
//$$ boolean allDone = this.mailbox.<Boolean>ask(reply -> () -> {
//$$ scheduleRunTasks();
//$$ reply.send(this.threadBuffers.size() == this.totalBufferCount);
//$$ }).join();
//$$
//$$ if (allDone) {
//$$ return true;
//$$ } else {
//$$ this.waitingForWorkLock.lock();
//$$ try {
//$$ while (true) {
//$$ if (this.allDone) {
//$$ return true;
//$$ } else if (!this.uploadQueue.isEmpty()) {
//$$ return false;
//$$ } else {
//$$ this.newWork.awaitUninterruptibly();
//$$ }
//$$ }
//$$ } finally {
//$$ this.waitingForWorkLock.unlock();
//$$ }
//$$ }
//$$ }
//$$
//$$ @Override
//$$ public boolean uploadEverythingBlocking() {
//$$ boolean anything = false;
//$$
//$$ boolean allChunksBuilt;
//$$ do {
//$$ allChunksBuilt = waitForMainThreadWork();
//$$ while (upload()) {
//$$ anything = true;
//$$ }
//$$ } while (!allChunksBuilt);
//$$
//$$ return anything;
//$$ }
//$$ }
//#endif

View File

@@ -1,17 +1,24 @@
//#if MC>=10800 && MC<11500
package com.replaymod.render.mixin;
//#if MC>=10800
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Frustum;
import net.minecraft.client.render.chunk.ChunkBuilder;
import net.minecraft.client.render.VisibleRegion;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11400
//#if MC>=11500
//$$ import java.util.Set;
//#else
import net.minecraft.client.render.VisibleRegion;
import net.minecraft.entity.Entity;
//#endif
//#if MC>=11400 && MC<11500
import net.minecraft.client.render.Camera;
//#endif
@@ -32,8 +39,57 @@ import net.minecraft.client.render.WorldRenderer;
//#else
//$$ @Mixin(RenderGlobal.class)
//#endif
public abstract class MixinRenderGlobal {
public abstract class Mixin_ForceChunkLoading {
public ChunkLoadingRenderGlobal replayModRender_hook;
//#if MC>=11500
//$$ @Shadow private Set<ChunkBuilder.BuiltChunk> chunksToRebuild;
//$$
//$$ @Shadow private ChunkBuilder chunkBuilder;
//$$
//$$ @Shadow private boolean needsTerrainUpdate;
//$$
//$$ @Shadow public abstract void scheduleTerrainUpdate();
//$$
//$$ @Shadow protected abstract void setupTerrain(Camera camera_1, Frustum frustum_1, boolean boolean_1, int int_1, boolean boolean_2);
//$$
//$$ @Shadow private int frame;
//$$
//$$ private boolean passThrough;
//$$ @Inject(method = "setupTerrain", at = @At("HEAD"), cancellable = true)
//$$ private void forceAllChunks(Camera camera_1, Frustum frustum_1, boolean boolean_1, int int_1, boolean boolean_2, CallbackInfo ci) {
//$$ if (replayModRender_hook == null) {
//$$ return;
//$$ }
//$$ if (passThrough) {
//$$ return;
//$$ }
//$$ ci.cancel();
//$$
//$$ passThrough = true;
//$$ try {
//$$ do {
//$$ // Determine which chunks shall be visible
//$$ setupTerrain(camera_1, frustum_1, boolean_1, frame++, boolean_2);
//$$
//$$ // Schedule all chunks which need rebuilding (we schedule even important rebuilds because we wait for
//$$ // all of them anyway and this way we can take advantage of threading)
//$$ for (ChunkBuilder.BuiltChunk builtChunk : this.chunksToRebuild) {
//$$ builtChunk.scheduleRebuild(this.chunkBuilder);
//$$ builtChunk.cancelRebuild();
//$$ }
//$$ this.chunksToRebuild.clear();
//$$
//$$ // Upload all chunks
//$$ this.needsTerrainUpdate |= ((ChunkLoadingRenderGlobal.IBlockOnChunkRebuilds) this.chunkBuilder).uploadEverythingBlocking();
//$$
//$$ // Repeat until no more updates are needed
//$$ } while (this.needsTerrainUpdate);
//$$ } finally {
//$$ passThrough = false;
//$$ }
//$$ }
//#else
private boolean replayModRender_passThroughSetupTerrain;
@Shadow
@@ -132,5 +188,6 @@ public abstract class MixinRenderGlobal {
replayModRender_hook.updateRenderDispatcher(this.chunkBuilder);
}
}
//#endif
}
//#endif

View File

@@ -49,9 +49,7 @@ import org.lwjgl.glfw.GLFW;
//#endif
//#if MC>=10800
//#if MC<11500
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
//#endif
import static com.mojang.blaze3d.platform.GlStateManager.*;
//#else
//$$ import com.replaymod.replay.gui.screen.GuiOpeningReplay;
@@ -88,7 +86,7 @@ public class VideoRenderer implements RenderInfo {
private TimelinePlayer timelinePlayer;
private Future<Void> timelinePlayerFuture;
//#if MC>=10800 && MC<11500
//#if MC>=10800
private ChunkLoadingRenderGlobal chunkLoadingRenderGlobal;
//#endif
//#if MC<10800
@@ -314,7 +312,7 @@ public class VideoRenderer implements RenderInfo {
//$$ gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight());
//#endif
//#if MC>=10800 && MC<11500
//#if MC>=10800
chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.worldRenderer);
//#endif
@@ -351,7 +349,7 @@ public class VideoRenderer implements RenderInfo {
mc.options.setSoundVolume(entry.getKey(), entry.getValue());
}
mc.openScreen(null);
//#if MC>=10800 && MC<11500
//#if MC>=10800
if (chunkLoadingRenderGlobal != null) {
chunkLoadingRenderGlobal.uninstall();
}

View File

@@ -8,9 +8,13 @@
"Mixin_ChromaKeyForceSky",
"Mixin_CubicRotation",
"Mixin_SkipBlockOutlinesDuringRender",
//#if MC>=10800 && MC<11500
//#if MC>=10800
//#if MC>=11500
//$$ "Mixin_BlockOnChunkRebuilds",
//#else
"ChunkRenderDispatcherAccessor",
"MixinChunkRenderWorker",
//#endif
"Mixin_ForceChunkLoading",
//#endif
//#if MC>=11400
"MainWindowAccessor",