From a19d519b4b609908166a5c790aacc64d289a2537 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 20 Aug 2023 20:01:36 -0400 Subject: [PATCH 1/5] Temporarily shift splash screen control to background thread during registry events --- .../loading_screen_freeze/GameDataMixin.java | 20 +++++++ .../forge/util/AsyncLoadingScreen.java | 53 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java create mode 100644 forge/src/main/java/org/embeddedt/modernfix/forge/util/AsyncLoadingScreen.java diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java new file mode 100644 index 00000000..b42ef336 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java @@ -0,0 +1,20 @@ +package org.embeddedt.modernfix.forge.mixin.bugfix.loading_screen_freeze; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.ModLoader; +import net.minecraftforge.registries.GameData; +import org.embeddedt.modernfix.forge.util.AsyncLoadingScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(value = GameData.class, remap = false) +public class GameDataMixin { + @WrapOperation(method = "postRegisterEvents", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModLoader;postEventWrapContainerInModOrder(Lnet/minecraftforge/eventbus/api/Event;)V")) + private static void swapThreadAndPost(ModLoader loader, Event event, Operation operation) { + try(AsyncLoadingScreen ignored = new AsyncLoadingScreen()) { + operation.call(loader, event); + } + } +} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/util/AsyncLoadingScreen.java b/forge/src/main/java/org/embeddedt/modernfix/forge/util/AsyncLoadingScreen.java new file mode 100644 index 00000000..bfa8f62d --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/util/AsyncLoadingScreen.java @@ -0,0 +1,53 @@ +package org.embeddedt.modernfix.forge.util; + +import net.minecraftforge.fml.loading.ImmediateWindowHandler; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.opengl.GL; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.LockSupport; + +public class AsyncLoadingScreen extends Thread implements AutoCloseable { + private final long theWindow; + private final AtomicBoolean keepRunning; + + private static int splashThreadNum = 1; + + public AsyncLoadingScreen() { + this.setName("ModernFix splash thread " + splashThreadNum++); + this.theWindow = GLFW.glfwGetCurrentContext(); + if(this.theWindow == 0) + throw new IllegalStateException("No context found but async loading screen was requested"); + this.keepRunning = new AtomicBoolean(true); + this.start(); + } + + @Override + public synchronized void start() { + GLFW.glfwMakeContextCurrent(0); + super.start(); + } + + @Override + public void run() { + GLFW.glfwMakeContextCurrent(theWindow); + GL.createCapabilities(); // seems to be needed, otherwise we get a "function not valid for context" error + while(keepRunning.get()) { + LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(50)); + ImmediateWindowHandler.renderTick(); + } + GLFW.glfwMakeContextCurrent(0); + } + + @Override + public void close() { + keepRunning.set(false); + try { + this.join(); + } catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + GLFW.glfwMakeContextCurrent(theWindow); + } +} From ae59c2496eda1680fee770f482682345122f9343 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 20 Aug 2023 20:26:26 -0400 Subject: [PATCH 2/5] Show mod names and progress during registry events --- .../loading_screen_freeze/GameDataMixin.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java index b42ef336..070c68e4 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java @@ -1,20 +1,34 @@ package org.embeddedt.modernfix.forge.mixin.bugfix.loading_screen_freeze; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModLoader; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.StartupMessageManager; +import net.minecraftforge.fml.event.IModBusEvent; import net.minecraftforge.registries.GameData; +import net.minecraftforge.registries.RegisterEvent; import org.embeddedt.modernfix.forge.util.AsyncLoadingScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(value = GameData.class, remap = false) public class GameDataMixin { - @WrapOperation(method = "postRegisterEvents", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModLoader;postEventWrapContainerInModOrder(Lnet/minecraftforge/eventbus/api/Event;)V")) - private static void swapThreadAndPost(ModLoader loader, Event event, Operation operation) { + @Redirect(method = "postRegisterEvents", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModLoader;postEventWrapContainerInModOrder(Lnet/minecraftforge/eventbus/api/Event;)V")) + private static void swapThreadAndPost(ModLoader loader, T event) { + RegisterEvent registryEvent = (RegisterEvent)event; + var pb = StartupMessageManager.addProgressBar(registryEvent.getRegistryKey().location().toString(), ModList.get().size()); try(AsyncLoadingScreen ignored = new AsyncLoadingScreen()) { - operation.call(loader, event); + loader.postEventWithWrapInModOrder(event, (mc, e) -> { + ModLoadingContext.get().setActiveContainer(mc); + pb.label(pb.name() + " - " + mc.getModInfo().getDisplayName()); + pb.increment(); + }, (mc, e) -> { + ModLoadingContext.get().setActiveContainer(null); + }); + } finally { + pb.complete(); } } } From fe8d0434c59d307438305adf1a70edee96a58327 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 20 Aug 2023 20:27:10 -0400 Subject: [PATCH 3/5] Rename feature --- .../registry_event_progress}/GameDataMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename forge/src/main/java/org/embeddedt/modernfix/forge/mixin/{bugfix/loading_screen_freeze => feature/registry_event_progress}/GameDataMixin.java (95%) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java similarity index 95% rename from forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java rename to forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java index 070c68e4..a6a86c13 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/loading_screen_freeze/GameDataMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.forge.mixin.bugfix.loading_screen_freeze; +package org.embeddedt.modernfix.forge.mixin.feature.registry_event_progress; import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.fml.ModList; From 8d766a8cc8c5c0f5dac1524f49c32a36ac37d2d2 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 20 Aug 2023 20:35:20 -0400 Subject: [PATCH 4/5] Avoid calling createCapabilities many times, mark mixin as client-only --- .../feature/registry_event_progress/GameDataMixin.java | 2 ++ .../embeddedt/modernfix/forge/util/AsyncLoadingScreen.java | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java index a6a86c13..5daba9fb 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java @@ -8,12 +8,14 @@ import net.minecraftforge.fml.StartupMessageManager; import net.minecraftforge.fml.event.IModBusEvent; import net.minecraftforge.registries.GameData; import net.minecraftforge.registries.RegisterEvent; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; import org.embeddedt.modernfix.forge.util.AsyncLoadingScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(value = GameData.class, remap = false) +@ClientOnlyMixin public class GameDataMixin { @Redirect(method = "postRegisterEvents", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModLoader;postEventWrapContainerInModOrder(Lnet/minecraftforge/eventbus/api/Event;)V")) private static void swapThreadAndPost(ModLoader loader, T event) { diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/util/AsyncLoadingScreen.java b/forge/src/main/java/org/embeddedt/modernfix/forge/util/AsyncLoadingScreen.java index bfa8f62d..3cb765b8 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/util/AsyncLoadingScreen.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/util/AsyncLoadingScreen.java @@ -3,6 +3,7 @@ package org.embeddedt.modernfix.forge.util; import net.minecraftforge.fml.loading.ImmediateWindowHandler; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GLCapabilities; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -14,9 +15,13 @@ public class AsyncLoadingScreen extends Thread implements AutoCloseable { private static int splashThreadNum = 1; + private static GLCapabilities caps; + public AsyncLoadingScreen() { this.setName("ModernFix splash thread " + splashThreadNum++); this.theWindow = GLFW.glfwGetCurrentContext(); + if(caps == null) + caps = GL.createCapabilities(); if(this.theWindow == 0) throw new IllegalStateException("No context found but async loading screen was requested"); this.keepRunning = new AtomicBoolean(true); @@ -32,7 +37,7 @@ public class AsyncLoadingScreen extends Thread implements AutoCloseable { @Override public void run() { GLFW.glfwMakeContextCurrent(theWindow); - GL.createCapabilities(); // seems to be needed, otherwise we get a "function not valid for context" error + GL.setCapabilities(caps); while(keepRunning.get()) { LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(50)); ImmediateWindowHandler.renderTick(); From 73d2a4405ddacd23f59d5ebccc460f4df812bd12 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 20 Aug 2023 20:51:46 -0400 Subject: [PATCH 5/5] Improve performance of async handoff --- .../registry_event_progress/GameDataMixin.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java index 5daba9fb..4b0b568e 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/feature/registry_event_progress/GameDataMixin.java @@ -12,16 +12,32 @@ import org.embeddedt.modernfix.annotation.ClientOnlyMixin; import org.embeddedt.modernfix.forge.util.AsyncLoadingScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(value = GameData.class, remap = false) @ClientOnlyMixin public class GameDataMixin { + + private static AsyncLoadingScreen mfix$asyncScreen; + + @Inject(method = "postRegisterEvents", at = @At(value = "INVOKE", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;", ordinal = 0)) + private static void createAsyncScreen(CallbackInfo ci) { + mfix$asyncScreen = new AsyncLoadingScreen(); + } + + @Inject(method = "postRegisterEvents", at = @At(value = "INVOKE", target = "Ljava/lang/RuntimeException;getSuppressed()[Ljava/lang/Throwable;", ordinal = 0)) + private static void closeAsyncScreen(CallbackInfo ci) { + mfix$asyncScreen.close(); + mfix$asyncScreen = null; + } + @Redirect(method = "postRegisterEvents", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModLoader;postEventWrapContainerInModOrder(Lnet/minecraftforge/eventbus/api/Event;)V")) private static void swapThreadAndPost(ModLoader loader, T event) { RegisterEvent registryEvent = (RegisterEvent)event; var pb = StartupMessageManager.addProgressBar(registryEvent.getRegistryKey().location().toString(), ModList.get().size()); - try(AsyncLoadingScreen ignored = new AsyncLoadingScreen()) { + try { loader.postEventWithWrapInModOrder(event, (mc, e) -> { ModLoadingContext.get().setActiveContainer(mc); pb.label(pb.name() + " - " + mc.getModInfo().getDisplayName());