From c5d015e60adb9f0b4a925a6cce360e6a47c1c7da Mon Sep 17 00:00:00 2001 From: LexManos Date: Tue, 30 Jun 2026 15:57:35 -0700 Subject: [PATCH] Add mechanic for ImmediateWindowProviders to react to Graphics backed settings Disable early loading screen when using Vulkan. Fixes #10829 --- .../fml/earlydisplay/DisplayWindow.java | 10 +++- .../fml/loading/ImmediateWindowHandler.java | 53 ++++++++++++++++++- .../fml/loading/ImmediateWindowProvider.java | 25 +++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/fmlearlydisplay/src/main/java/net/minecraftforge/fml/earlydisplay/DisplayWindow.java b/fmlearlydisplay/src/main/java/net/minecraftforge/fml/earlydisplay/DisplayWindow.java index 5c6c8dc6c7..780f80357c 100644 --- a/fmlearlydisplay/src/main/java/net/minecraftforge/fml/earlydisplay/DisplayWindow.java +++ b/fmlearlydisplay/src/main/java/net/minecraftforge/fml/earlydisplay/DisplayWindow.java @@ -113,6 +113,14 @@ public class DisplayWindow implements ImmediateWindowProvider { return "fmlearlywindow"; } + @Override + public ImmediateWindowProvider selectBackend(String backend) { + // We only support opengl + if ("default".equals(backend) || "opengl".equals(backend)) + return this; + return ImmediateWindowProvider.getFallbackHandler(); + } + @Override public Runnable initialize(String[] arguments) { String mcVersion = FMLLoader.versionInfo().mcVersion(); @@ -137,7 +145,7 @@ public class DisplayWindow implements ImmediateWindowProvider { this.colourScheme = ColourScheme.BLACK; } else { try { - // check the options file for the colour scheme + // check the options file for the color scheme var optionLines = Files.readAllLines(FMLPaths.GAMEDIR.get().resolve(Path.of("options.txt"))); var keyName = "darkMojangStudiosBackground:"; for (String line : optionLines) { diff --git a/fmlloader/src/main/java/net/minecraftforge/fml/loading/ImmediateWindowHandler.java b/fmlloader/src/main/java/net/minecraftforge/fml/loading/ImmediateWindowHandler.java index d6a8bff4e1..c704a3fd7d 100644 --- a/fmlloader/src/main/java/net/minecraftforge/fml/loading/ImmediateWindowHandler.java +++ b/fmlloader/src/main/java/net/minecraftforge/fml/loading/ImmediateWindowHandler.java @@ -10,8 +10,13 @@ import net.minecraftforge.fml.loading.progress.StartupNotificationManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import joptsimple.OptionParser; + +import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; import java.util.function.*; import java.util.stream.Collectors; @@ -41,16 +46,62 @@ public class ImmediateWindowHandler { if (provider == null) { LOGGER.info("Failed to find ImmediateWindowProvider {}, disabling", providername); provider = new DummyProvider(); + } else { + var backend = findBackend(arguments); + var newProvider = provider.selectBackend(backend); + if (provider != newProvider) { + if (newProvider == null) + newProvider = new DummyProvider(); + + LOGGER.info("ImmediateWindowProvider {} does not support {}, switching to {}", provider.name(), backend, newProvider.name()); + provider = newProvider; + } } } + // Only update config if the provider isn't the dummy provider if (!Objects.equals(provider.name(), "dummyprovider")) FMLConfig.updateConfig(FMLConfig.ConfigValue.EARLY_WINDOW_PROVIDER, provider.name()); + FMLLoader.progressWindowTick = provider.initialize(arguments); earlyProgress = StartupNotificationManager.addProgressBar("EARLY", 0); earlyProgress.label("Bootstrapping Minecraft"); } + private static String findBackend(String[] arguments) { + // Try and parse from the command line arguments + var parser = new OptionParser(); + var backendOption = parser.accepts("graphicsBackend").withRequiredArg(); + parser.allowsUnrecognizedOptions(); + var parsed = parser.parse(arguments); + + if (parsed.has(backendOption)) + return parsed.valueOf(backendOption).toLowerCase(Locale.ENGLISH); + + + // Read the options.txt if it exists. + var optionsFile = FMLPaths.GAMEDIR.get().resolve(Path.of("options.txt")); + if (!Files.exists(optionsFile)) // Default is OpenGL first + return "default"; + + List lines = null; + try { + lines = Files.readAllLines(optionsFile); + } catch (IOException e) { + return "default"; // We failed to read for some reason, assume we're using the default. + } + + final String key = "preferredGraphicsBackend:"; + for (var line : lines) { + if (line.startsWith(key)) { + var backend = line.substring(key.length() + 1, line.length() - 1); + return backend.toLowerCase(Locale.ENGLISH); + } + } + + return "default"; + } + public static long setupMinecraftWindow(final int width, final int height, final String title, final long monitor, final Supplier backend) { return provider.setupMinecraftWindow(width, height, title, monitor, backend); } @@ -83,7 +134,7 @@ public class ImmediateWindowHandler { earlyProgress.label(message); } - private record DummyProvider() implements ImmediateWindowProvider { + record DummyProvider() implements ImmediateWindowProvider { private static Method NV_HANDOFF; private static Method NV_POSITION; private static Method NV_OVERLAY; diff --git a/fmlloader/src/main/java/net/minecraftforge/fml/loading/ImmediateWindowProvider.java b/fmlloader/src/main/java/net/minecraftforge/fml/loading/ImmediateWindowProvider.java index 59a9c744e9..d15c3629da 100644 --- a/fmlloader/src/main/java/net/minecraftforge/fml/loading/ImmediateWindowProvider.java +++ b/fmlloader/src/main/java/net/minecraftforge/fml/loading/ImmediateWindowProvider.java @@ -24,11 +24,36 @@ import java.util.function.Supplier; * No doubt many more things can be said here. */ public interface ImmediateWindowProvider { + /** + * Returns a new instance of ImmediateWindowProvider which just bounces to the vanilla code. + * This can be useful when you want to disable your provider and defer to vanilla behavior for some reason. + */ + public static ImmediateWindowProvider getFallbackHandler() { + return new ImmediateWindowHandler.DummyProvider(); + } + /** * @return The name of this window provider. Do NOT use fmlearlywindow. */ String name(); + /** + * This is called before initialize, but after reading the preferred graphics backend config value from the user's + * options.txt or command line. + * + * If you do not support the requested backend, you can return a new ImmediateWindowProvider that does. + * {@link #getFallbackHandler()} can be used to get an instance that falls back to Vanilla's code effectively + * disabling the early loading screen. + * + * @param backend - The backend the user has selected, known values: "default", "opengl", and "vulkan". However this + * is read from the config file, or command line arguments so could be anything. + * Default and OpenGL are treated the same, attempting to load OpenGL first, then Vulkan. + * Vulkan attempts to load Vulkan first then OpenGL + */ + default ImmediateWindowProvider selectBackend(String backend) { + return this; + } + /** * This is called very early on to initialize ourselves. Use this to initialize the window and other GL core resources. *