diff --git a/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotRenderer.java b/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotRenderer.java index cd7613f9..ecc3cd73 100644 --- a/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotRenderer.java +++ b/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotRenderer.java @@ -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 diff --git a/src/main/java/com/replaymod/render/hooks/ChunkLoadingRenderGlobal.java b/src/main/java/com/replaymod/render/hooks/ChunkLoadingRenderGlobal.java index 4e008f6d..25e6c3b4 100644 --- a/src/main/java/com/replaymod/render/hooks/ChunkLoadingRenderGlobal.java +++ b/src/main/java/com/replaymod/render/hooks/ChunkLoadingRenderGlobal.java @@ -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 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 diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java b/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java new file mode 100644 index 00000000..df5d5cd0 --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java @@ -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 threadBuffers; +//$$ +//$$ @Shadow public abstract boolean upload(); +//$$ +//$$ @Shadow @Final private TaskExecutor mailbox; +//$$ +//$$ @Shadow protected abstract void scheduleRunTasks(); +//$$ +//$$ @Shadow @Final private Queue uploadQueue; +//$$ private final Lock waitingForWorkLock = new ReentrantLock(); +//$$ private final Condition newWork = waitingForWorkLock.newCondition(); +//$$ private volatile boolean allDone; +//$$ +//$$ private int totalBufferCount; +//$$ +//$$ @Inject(method = "", 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> ci) { +//$$ this.waitingForWorkLock.lock(); +//$$ try { +//$$ this.newWork.signal(); +//$$ } finally { +//$$ this.waitingForWorkLock.unlock(); +//$$ } +//$$ } +//$$ +//$$ private boolean waitForMainThreadWork() { +//$$ boolean allDone = this.mailbox.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 diff --git a/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java b/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java similarity index 64% rename from src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java rename to src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java index a564e17f..16cc9214 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java +++ b/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java @@ -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 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 diff --git a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java index 38890ee9..7b2dcc8f 100644 --- a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java +++ b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java @@ -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 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(); } diff --git a/src/main/resources/mixins.render.replaymod.json b/src/main/resources/mixins.render.replaymod.json index d516fd4a..092ff79c 100644 --- a/src/main/resources/mixins.render.replaymod.json +++ b/src/main/resources/mixins.render.replaymod.json @@ -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",