diff --git a/build.gradle.kts b/build.gradle.kts index 40a1e631d6..af98d29a18 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,6 +42,7 @@ sourceSets { val main = getByName("main") val api = create("api") val desktop = create("desktop") + val workarounds = create("workarounds") api.apply { java { @@ -49,6 +50,12 @@ sourceSets { } } + workarounds.apply { + java { + compileClasspath += main.compileClasspath + } + } + desktop.apply { java { srcDir("src/desktop/java") @@ -58,7 +65,9 @@ sourceSets { main.apply { java { compileClasspath += api.output + compileClasspath += workarounds.output runtimeClasspath += api.output + runtimeClasspath += workarounds.output } } } @@ -89,8 +98,7 @@ tasks { } jar { - from("${rootProject.projectDir}/COPYING") - from("${rootProject.projectDir}/COPYING.LESSER") + from("${rootProject.projectDir}/LICENSE.md") val api = sourceSets.getByName("api") from(api.output.classesDirs) @@ -100,6 +108,10 @@ tasks { from(desktop.output.classesDirs) from(desktop.output.resourcesDir) + val workarounds = sourceSets.getByName("workarounds") + from(workarounds.output.classesDirs) + from(workarounds.output.resourcesDir) + manifest.attributes["Main-Class"] = "net.caffeinemc.mods.sodium.desktop.LaunchWarn" } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java index 23a0d2ae0c..ba8b48345e 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java @@ -1,16 +1,15 @@ package me.jellysquid.mods.sodium.client; -import me.jellysquid.mods.sodium.client.compatibility.checks.PreLaunchChecks; -import me.jellysquid.mods.sodium.client.compatibility.workarounds.Workarounds; -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; +import net.caffeinemc.mods.sodium.client.compatibility.checks.PreLaunchChecks; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; public class SodiumPreLaunch implements PreLaunchEntrypoint { @Override public void onPreLaunch() { - PreLaunchChecks.beforeLWJGLInit(); + PreLaunchChecks.checkEnvironment(); GraphicsAdapterProbe.findAdapters(); - PreLaunchChecks.onGameInit(); Workarounds.init(); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/BugChecks.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/BugChecks.java deleted file mode 100644 index 36db764224..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/BugChecks.java +++ /dev/null @@ -1,26 +0,0 @@ -package me.jellysquid.mods.sodium.client.compatibility.checks; - -/** - * "Checks" are used to determine whether the environment we are running within is actually reasonable. Most often, - * failing checks will crash the game and prompt the user for intervention. - */ -class BugChecks { - public static final boolean ISSUE_899 = configureCheck("issue899", true); - public static final boolean ISSUE_1486 = configureCheck("issue1486", true); - public static final boolean ISSUE_2048 = configureCheck("issue2048", true); - public static final boolean ISSUE_2561 = configureCheck("issue2561", true); - - private static boolean configureCheck(String name, boolean defaultValue) { - var propertyValue = System.getProperty(getPropertyKey(name), null); - - if (propertyValue == null) { - return defaultValue; - } - - return Boolean.parseBoolean(propertyValue); - } - - private static String getPropertyKey(String name) { - return "sodium.checks." + name; - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PreLaunchChecks.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PreLaunchChecks.java deleted file mode 100644 index 8327cc233b..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PreLaunchChecks.java +++ /dev/null @@ -1,169 +0,0 @@ -package me.jellysquid.mods.sodium.client.compatibility.checks; - -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; -import me.jellysquid.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaDriverVersion; -import me.jellysquid.mods.sodium.client.platform.MessageBox; -import me.jellysquid.mods.sodium.client.platform.windows.WindowsFileVersion; -import me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; -import me.jellysquid.mods.sodium.client.util.OsUtils; -import me.jellysquid.mods.sodium.client.util.OsUtils.OperatingSystem; -import org.jetbrains.annotations.Nullable; -import org.lwjgl.Version; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; - -/** - * Performs OpenGL driver validation before the game creates an OpenGL context. This runs during the earliest possible - * opportunity at game startup, and uses a custom hardware prober to search for problematic drivers. - */ -public class PreLaunchChecks { - private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-EarlyDriverScanner"); - - // This string should be determined at compile time, so it can be checked against the runtime version. - private static final String REQUIRED_LWJGL_VERSION = Version.VERSION_MAJOR + "." + Version.VERSION_MINOR + "." + Version.VERSION_REVISION; - - private static final String normalMessage = "You must change the LWJGL version in your launcher to continue. This is usually controlled by the settings for a profile or instance in your launcher."; - - private static final String prismMessage = "It appears you are using Prism Launcher to start the game. You can likely fix this problem by opening your instance settings and navigating to the Version section in the sidebar."; - - public static void beforeLWJGLInit() { - if (BugChecks.ISSUE_2561) { - if (!Version.getVersion().startsWith(REQUIRED_LWJGL_VERSION)) { - String message = normalMessage; - - if (System.getProperty("minecraft.launcher.brand", "unknown").equalsIgnoreCase("PrismLauncher")) { - message = prismMessage; - } - - showCriticalErrorAndClose("Sodium Renderer - Unsupported LWJGL", - (""" - The game failed to start because the currently active LWJGL version is not \ - compatible. - - Installed version: ###CURRENT_VERSION### - Required version: ###REQUIRED_VERSION### - - """ + message) - .replace("###CURRENT_VERSION###", org.lwjgl.Version.getVersion()) - .replace("###REQUIRED_VERSION###", REQUIRED_LWJGL_VERSION), - "https://github.com/CaffeineMC/sodium-fabric/wiki/LWJGL-Compatibility"); - - } - } - } - - public static void onGameInit() { - if (BugChecks.ISSUE_899) { - var installedVersion = findIntelDriverMatchingBug899(); - - if (installedVersion != null) { - showCriticalErrorAndClose("Sodium Renderer - Unsupported Driver", - """ - The game failed to start because the currently installed Intel Graphics Driver is not \ - compatible. - - Installed version: ###CURRENT_DRIVER### - Required version: 10.18.10.5161 (or newer) - - You must update your graphics card driver in order to continue.""" - .replace("###CURRENT_DRIVER###", NvidiaDriverVersion.parse(installedVersion).toString()), - "https://github.com/CaffeineMC/sodium-fabric/wiki/Driver-Compatibility#windows-intel-gen7"); - } - } - - if (BugChecks.ISSUE_1486) { - var installedVersion = findNvidiaDriverMatchingBug1486(); - - if (installedVersion != null) { - showCriticalErrorAndClose("Sodium Renderer - Unsupported Driver", - """ - The game failed to start because the currently installed NVIDIA Graphics Driver is not \ - compatible. - - Installed version: ###CURRENT_DRIVER### - Required version: 536.23 (or newer) - - You must update your graphics card driver in order to continue.""" - .replace("###CURRENT_DRIVER###", installedVersion.toString()), - "https://github.com/CaffeineMC/sodium-fabric/wiki/Driver-Compatibility#nvidia-gpus"); - - } - } - } - - private static void showCriticalErrorAndClose(String title, String message, String url) { - // Always print the information to the log file first, just in case we can't show the message box. - LOGGER.error(""" - ###ERROR_DESCRIPTION### - - For more information, please see: ###HELP_URL###""" - .replace("###ERROR_DESCRIPTION###", message) - .replace("###HELP_URL###", url == null ? "" : url)); - - // Try to show a graphical message box (if the platform supports it) and shut down the game. - MessageBox.showMessageBox(null, MessageBox.IconType.ERROR, title, message, url); - System.exit(1 /* failure code */); - } - - // https://github.com/CaffeineMC/sodium-fabric/issues/899 - private static @Nullable WindowsFileVersion findIntelDriverMatchingBug899() { - if (OsUtils.getOs() != OperatingSystem.WIN) { - return null; - } - - for (var adapter : GraphicsAdapterProbe.getAdapters()) { - if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { - var driverName = wddmAdapterInfo.getOpenGlIcdName(); - var driverVersion = wddmAdapterInfo.openglIcdVersion(); - - // Intel OpenGL ICD for Generation 7 GPUs - if (driverName.matches("ig7icd(32|64)")) { - // https://www.intel.com/content/www/us/en/support/articles/000005654/graphics.html - // Anything which matches the 15.33 driver scheme (WDDM x.y.10.w) should be checked - // Drivers before build 5161 are assumed to have bugs with synchronization primitives - if (driverVersion.z() == 10 && driverVersion.w() < 5161) { - return driverVersion; - } - } - } - } - - return null; - } - - - // https://github.com/CaffeineMC/sodium-fabric/issues/1486 - // The way which NVIDIA tries to detect the Minecraft process could not be circumvented until fairly recently - // So we require that an up-to-date graphics driver is installed so that our workarounds can disable the Threaded - // Optimizations driver hack. - private static @Nullable WindowsFileVersion findNvidiaDriverMatchingBug1486() { - // The Linux driver has two separate branches which have overlapping version numbers, despite also having - // different feature sets. As a result, we can't reliably determine which Linux drivers are broken... - if (OsUtils.getOs() != OperatingSystem.WIN) { - return null; - } - - for (var adapter : GraphicsAdapterProbe.getAdapters()) { - if (adapter.vendor() != GraphicsAdapterVendor.NVIDIA) { - continue; - } - - if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { - var driverVersion = wddmAdapterInfo.openglIcdVersion(); - - if (driverVersion.z() == 15) { // Only match 5XX.XX drivers - // Broken in x.y.15.2647 (526.47) - // Fixed in x.y.15.3623 (536.23) - if (driverVersion.w() >= 2647 && driverVersion.w() < 3623) { - return driverVersion; - } - } - } - } - - return null; - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/GLContextInfo.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/GLContextInfo.java deleted file mode 100644 index a4d6c36d41..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/GLContextInfo.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.jellysquid.mods.sodium.client.compatibility.environment; - -import org.jetbrains.annotations.Nullable; -import org.lwjgl.opengl.GL11C; - -public record GLContextInfo(String vendor, String renderer, String version) { - @Nullable - public static GLContextInfo create() { - String vendor = GL11C.glGetString(GL11C.GL_VENDOR); - String renderer = GL11C.glGetString(GL11C.GL_RENDERER); - String version = GL11C.glGetString(GL11C.GL_VERSION); - - if (vendor == null || renderer == null || version == null) { - return null; - } - - return new GLContextInfo(vendor, renderer, version); - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java deleted file mode 100644 index 7b890661b6..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java +++ /dev/null @@ -1,51 +0,0 @@ -package me.jellysquid.mods.sodium.client.compatibility.environment.probe; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.file.PathUtils; -import org.jetbrains.annotations.NotNull; - -public enum GraphicsAdapterVendor { - NVIDIA, - AMD, - INTEL, - UNKNOWN; - - @NotNull - static GraphicsAdapterVendor fromPciVendorId(String vendor) { - if (vendor.contains("0x1002")) { - return AMD; - } else if (vendor.contains("0x10de")) { - return NVIDIA; - } else if (vendor.contains("0x8086")) { - return INTEL; - } - - return UNKNOWN; - } - - public static GraphicsAdapterVendor fromIcdName(String name) { - // Intel Gen 4, 5, 6 - ig4icd - // Intel Gen 7 - ig7icd - // Intel Gen 7.5 - ig75icd - // Intel Gen 8 - ig8icd - // Intel Gen 9, 9.5 - ig9icd - // Intel Gen 11 - ig11icd - // Intel Gen 12 - ig12icd (UHD Graphics, with early drivers) - // igxelpicd (Xe-LP; integrated) - // igxehpicd (Xe-HP; dedicated) - if (name.matches("ig(4|7|75|8|9|11|12|xelp|xehp)icd(32|64)")) { - return INTEL; - } - - if (name.matches("nvoglv(32|64)")) { - return NVIDIA; - } - - if (name.matches("atiglpxx|atig6pxx")) { - return AMD; - } - - return UNKNOWN; - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java deleted file mode 100644 index 2dd882bb99..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java +++ /dev/null @@ -1,49 +0,0 @@ -package me.jellysquid.mods.sodium.client.compatibility.workarounds.nvidia; - -import me.jellysquid.mods.sodium.client.platform.unix.Libc; -import me.jellysquid.mods.sodium.client.platform.windows.api.Kernel32; -import me.jellysquid.mods.sodium.client.platform.windows.WindowsCommandLine; -import me.jellysquid.mods.sodium.client.util.OsUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class NvidiaWorkarounds { - private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-NvidiaWorkarounds"); - - public static void install() { - LOGGER.warn("Applying workaround: Prevent the NVIDIA OpenGL driver from using broken optimizations (NVIDIA_THREADED_OPTIMIZATIONS)"); - - try { - switch (OsUtils.getOs()) { - case WIN -> { - // The NVIDIA drivers rely on parsing the command line arguments to detect Minecraft. If we destroy those, - // then it shouldn't be able to detect us anymore. - WindowsCommandLine.setCommandLine("net.caffeinemc.sodium"); - - // Ensures that Minecraft will run on the dedicated GPU, since the drivers can no longer detect it - Kernel32.setEnvironmentVariable("SHIM_MCCOMPAT", "0x800000001"); - } - case LINUX -> { - // Unlike Windows, we don't need to hide ourselves from the driver. We can just request that - // it not use threaded optimizations instead. - Libc.setEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", "0"); - } - } - } catch (Throwable t) { - LOGGER.error("Failure while applying workarounds", t); - - LOGGER.error("READ ME! The workarounds for the NVIDIA Graphics Driver did not apply correctly!"); - LOGGER.error("READ ME! You are very likely going to run into unexplained crashes and severe performance issues!"); - LOGGER.error("READ ME! Please see this issue for more information: https://github.com/CaffeineMC/sodium-fabric/issues/1816"); - } - } - - public static void uninstall() { - switch (OsUtils.getOs()) { - case WIN -> { - WindowsCommandLine.resetCommandLine(); - } - case LINUX -> { } - } - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java index 8791f32a62..e7af5ef24a 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java @@ -11,7 +11,7 @@ import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl; import me.jellysquid.mods.sodium.client.gui.options.storage.MinecraftOptionsStorage; import me.jellysquid.mods.sodium.client.gui.options.storage.SodiumOptionsStorage; -import me.jellysquid.mods.sodium.client.compatibility.workarounds.Workarounds; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.option.*; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/QueryResult.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/QueryResult.java deleted file mode 100644 index ffe9ad84f9..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/QueryResult.java +++ /dev/null @@ -1,4 +0,0 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.version; - -public record QueryResult(long address, int length) { -} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/core/WindowMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/core/WindowMixin.java index d2cc7bcb3a..2be596314f 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/core/WindowMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/core/WindowMixin.java @@ -1,16 +1,22 @@ package me.jellysquid.mods.sodium.mixin.core; import me.jellysquid.mods.sodium.client.SodiumClientMod; -import me.jellysquid.mods.sodium.client.compatibility.workarounds.Workarounds; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; import net.minecraft.client.util.Window; import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWNativeWin32; +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; @Mixin(Window.class) -public class WindowMixin { +public class WindowMixin implements NativeWindowHandle { + @Shadow @Final private long handle; + @Inject(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwCreateWindow(IILjava/lang/CharSequence;JJ)J", shift = At.Shift.BEFORE)) public void setAdditionalWindowHints(CallbackInfo ci) { if (SodiumClientMod.options().performance.useNoErrorGLContext && @@ -18,4 +24,9 @@ public void setAdditionalWindowHints(CallbackInfo ci) { GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_NO_ERROR, GLFW.GLFW_TRUE); } } + + @Override + public long getWin32Handle() { + return GLFWNativeWin32.glfwGetWin32Window(this.handle); + } } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/workarounds/context_creation/WindowMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/workarounds/context_creation/WindowMixin.java index 1da80fc219..149e6d2340 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/workarounds/context_creation/WindowMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/workarounds/context_creation/WindowMixin.java @@ -1,9 +1,10 @@ package me.jellysquid.mods.sodium.mixin.workarounds.context_creation; -import me.jellysquid.mods.sodium.client.compatibility.checks.ModuleScanner; -import me.jellysquid.mods.sodium.client.compatibility.checks.PostLaunchChecks; -import me.jellysquid.mods.sodium.client.compatibility.workarounds.Workarounds; -import me.jellysquid.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds; +import net.caffeinemc.mods.sodium.client.compatibility.checks.ModuleScanner; +import net.caffeinemc.mods.sodium.client.compatibility.checks.PostLaunchChecks; +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; import net.minecraft.client.WindowEventHandler; import net.minecraft.client.WindowSettings; import net.minecraft.client.util.MonitorTracker; @@ -24,33 +25,30 @@ @Mixin(Window.class) -public class WindowMixin { - @Shadow - @Final - private static Logger LOGGER; - +public class WindowMixin { @Shadow +@Final +private static Logger LOGGER; @Unique private long wglPrevContext = MemoryUtil.NULL; - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwCreateWindow(IILjava/lang/CharSequence;JJ)J")) + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwCreateWindow(IILjava/lang/CharSequence;JJ)J"), expect = 0, require = 0) private long wrapGlfwCreateWindow(int width, int height, CharSequence title, long monitor, long share) { - final boolean applyNvidiaWorkarounds = Workarounds.isWorkaroundEnabled(Workarounds.Reference.NVIDIA_THREADED_OPTIMIZATIONS); - - if (applyNvidiaWorkarounds) { - NvidiaWorkarounds.install(); - } + NvidiaWorkarounds.applyEnvironmentChanges(); try { return GLFW.glfwCreateWindow(width, height, title, monitor, share); } finally { - if (applyNvidiaWorkarounds) { - NvidiaWorkarounds.uninstall(); - } + NvidiaWorkarounds.undoEnvironmentChanges(); } } @Inject(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL;createCapabilities()Lorg/lwjgl/opengl/GLCapabilities;", shift = At.Shift.AFTER)) private void postWindowCreated(WindowEventHandler eventHandler, MonitorTracker monitorTracker, WindowSettings settings, String videoMode, String title, CallbackInfo ci) { + GlContextInfo context = GlContextInfo.create(); + LOGGER.info("OpenGL Vendor: {}", context.vendor()); + LOGGER.info("OpenGL Renderer: {}", context.renderer()); + LOGGER.info("OpenGL Version: {}", context.version()); + // Capture the current WGL context so that we can detect it being replaced later. if (Util.getOperatingSystem() == Util.OperatingSystem.WINDOWS) { this.wglPrevContext = WGL.wglGetCurrentContext(); @@ -58,8 +56,8 @@ private void postWindowCreated(WindowEventHandler eventHandler, MonitorTracker m this.wglPrevContext = MemoryUtil.NULL; } - PostLaunchChecks.onContextInitialized(); - ModuleScanner.checkModules(); + PostLaunchChecks.onContextInitialized((NativeWindowHandle) this, context); + ModuleScanner.checkModules((NativeWindowHandle) this); } @Inject(method = "swapBuffers", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;flipFrame(J)V", shift = At.Shift.AFTER)) @@ -81,7 +79,7 @@ private void preSwapBuffers(CallbackInfo ci) { // Likely, this indicates a module was injected into the current process. We should check that // nothing problematic was just installed. - ModuleScanner.checkModules(); + ModuleScanner.checkModules((NativeWindowHandle) this); // If we didn't find anything problematic (which would have thrown an exception), then let's just record // the new context pointer and carry on. diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/BugChecks.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/BugChecks.java new file mode 100644 index 0000000000..e42457e2fb --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/BugChecks.java @@ -0,0 +1,75 @@ +package net.caffeinemc.mods.sodium.client.compatibility.checks; + +/** + * "Checks" are used to determine whether the environment we are running within is actually reasonable. Most often, + * failing checks will crash the game and prompt the user for intervention. + */ +class BugChecks { + /** + * Some older drivers for Intel Gen7 Graphics on Windows are defective and will never return from + * a call to
glClientWaitSync
. As there is no way to recover the main thread after the + * deadlock occurs, trying to work around this seems to be impossible. Updating the driver to version + * 15.33.53.5161 resolves the problem, and is the currently recommended solution. + * GitHub Issue + */ + public static final boolean ISSUE_899 = configureCheck("issue899", true); + + /** + * Since version 526.47 of the NVIDIA Graphics Driver, the OpenGL user-mode driver will attempt to detect + * Minecraft with hard-coded logic in the driver, and enable broken optimizations (referred to as "Threaded + * Optimizations"). This will cause crashes during framebuffer initialization (and some draw calls) for unclear + * reasons, especially on systems with hybrid graphics. Furthermore, performance is often severely degraded + * due to bubbles in the NVIDIA command submission thread, especially when other mods query context state. + *

+ * Sodium can prevent the detection of Minecraft and the enablement of these unstable optimizations, but the logic + * is extensive and depends heavily on the exact operating system and graphics driver version used. Some older + * graphics driver versions have no workaround available presently. + * GitHub Issue + */ + public static final boolean ISSUE_1486 = configureCheck("issue1486", true); + + /** + * Older versions of the RivaTuner Statistics Server will attempt to use legacy OpenGL functions in a Core + * profile, which causes either a crash or excessive log spam, depending on the OpenGL implementation. This is + * because RivaTuner relies on replacing the OpenGL context immediately after the application initializes it, + * but does so improperly when a No Error Context is used. This problem can't be avoided by simply configuring + * RivaTuner to not inject into Minecraft, as it *always* injects first to modify the context, and *then* disables + * itself. + * GitHub Issue + */ + public static final boolean ISSUE_2048 = configureCheck("issue2048", true); + + /** + * LWJGL does not provide API stability guarantees for other libraries using it to create their own C bindings. + * Because of this, the game will crash due to breaking method/type signature changes very early at startup. We + * should not be using these "internal" classes in LWJGL, but the amount of work involved doing everything ourselves + * is astronomical. When Minecraft ships a version of OpenJDK with the Foreign Function & Memory API, we can replace + * our dependency on LWJGL and remove this check. + * GitHub Issue + */ + public static final boolean ISSUE_2561 = configureCheck("issue2561", true); + + /** + * ASUS's GPU Tweak III does not correctly restore OpenGL context state after rendering its in-game overlay, + * which frequently causes graphical corruption, excessive log file spam, and crashes. These problems are + * actually reproducible without any mods installed, but it seems to be exacerbated with Sodium due to how different + * the rendering pipeline is. This problem can be avoided by configuring the application to not inject into + * Minecraft (or Java applications). + * GitHub Issue + */ + public static final boolean ISSUE_2637 = configureCheck("issue2637", true); + + private static boolean configureCheck(String name, boolean defaultValue) { + var propertyValue = System.getProperty(getPropertyKey(name), null); + + if (propertyValue == null) { + return defaultValue; + } + + return Boolean.parseBoolean(propertyValue); + } + + private static String getPropertyKey(String name) { + return "sodium.checks." + name; + } +} diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/GraphicsDriverChecks.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/GraphicsDriverChecks.java new file mode 100644 index 0000000000..d37572b0f4 --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/GraphicsDriverChecks.java @@ -0,0 +1,63 @@ +package net.caffeinemc.mods.sodium.client.compatibility.checks; + +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.intel.IntelWorkarounds; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaDriverVersion; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; +import net.caffeinemc.mods.sodium.client.platform.PlatformHelper; + +class GraphicsDriverChecks { + static void postContextInit(NativeWindowHandle window, GlContextInfo context) { + var vendor = GraphicsAdapterVendor.fromContext(context); + + if (vendor == GraphicsAdapterVendor.UNKNOWN) { + return; + } + + if (vendor == GraphicsAdapterVendor.INTEL && BugChecks.ISSUE_899) { + var installedVersion = IntelWorkarounds.findIntelDriverMatchingBug899(); + + if (installedVersion != null) { + var installedVersionString = installedVersion.toString(); + + PlatformHelper.showCriticalErrorAndClose(window, + "Sodium Renderer - Unsupported Driver", + """ + The game failed to start because the currently installed Intel Graphics Driver is not \ + compatible. + + Installed version: ###CURRENT_DRIVER### + Required version: 10.18.10.5161 (or newer) + + Please click the 'Help' button to read more about how to fix this problem.""" + .replace("###CURRENT_DRIVER###", installedVersionString), + "https://link.caffeinemc.net/help/sodium/graphics-driver/windows/intel/gh-899"); + } + } + + if (vendor == GraphicsAdapterVendor.NVIDIA && BugChecks.ISSUE_1486) { + var installedVersion = NvidiaWorkarounds.findNvidiaDriverMatchingBug1486(); + + if (installedVersion != null) { + var installedVersionString = NvidiaDriverVersion.parse(installedVersion) + .toString(); + + PlatformHelper.showCriticalErrorAndClose(window, + "Sodium Renderer - Unsupported Driver", + """ + The game failed to start because the currently installed NVIDIA Graphics Driver is not \ + compatible. + + Installed version: ###CURRENT_DRIVER### + Required version: 536.23 (or newer) + + Please click the 'Help' button to read more about how to fix this problem.""" + .replace("###CURRENT_DRIVER###", installedVersionString), + "https://link.caffeinemc.net/help/sodium/graphics-driver/windows/nvidia/gh-1486"); + + } + } + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ModuleScanner.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/ModuleScanner.java similarity index 51% rename from src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ModuleScanner.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/ModuleScanner.java index 4c01f88fd5..8af1a8d70f 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ModuleScanner.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/ModuleScanner.java @@ -1,21 +1,21 @@ -package me.jellysquid.mods.sodium.client.compatibility.checks; - -import me.jellysquid.mods.sodium.client.platform.MessageBox; -import me.jellysquid.mods.sodium.client.platform.windows.WindowsFileVersion; -import me.jellysquid.mods.sodium.client.platform.windows.api.Kernel32; -import me.jellysquid.mods.sodium.client.platform.windows.api.version.Version; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.util.Window; -import net.minecraft.util.WinNativeModuleUtil; -import org.jetbrains.annotations.NotNull; +package net.caffeinemc.mods.sodium.client.compatibility.checks; + +import com.sun.jna.Platform; +import com.sun.jna.platform.win32.Kernel32Util; +import net.caffeinemc.mods.sodium.client.platform.MessageBox; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; +import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; +import net.caffeinemc.mods.sodium.client.platform.windows.api.Kernel32; +import net.caffeinemc.mods.sodium.client.platform.windows.api.version.Version; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.regex.Pattern; /** * Utility class for determining whether the current process has been injected into or otherwise modified. This should @@ -25,30 +25,62 @@ public class ModuleScanner { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-Win32ModuleChecks"); - private static final String[] RTSS_HOOKS_MODULE_NAMES = { "RTSSHooks64.dll", "RTSSHooks.dll" }; + private static final String[] RTSS_HOOKS_MODULE_NAMES = { + "RTSSHooks64.dll", + "RTSSHooks.dll" + }; - public static void checkModules() { - List modules; + private static final String[] ASUS_GPU_TWEAK_MODULE_NAMES = { + "GTIII-OSD64-GL.dll", "GTIII-OSD-GL.dll", + "GTIII-OSD64-VK.dll", "GTIII-OSD-VK.dll", + "GTIII-OSD64.dll", "GTIII-OSD.dll" + }; + + public static void checkModules(NativeWindowHandle window) { + List modules; try { - modules = WinNativeModuleUtil.collectNativeModules(); + modules = listModules(); } catch (Throwable t) { LOGGER.warn("Failed to scan the currently loaded modules", t); return; } if (modules.isEmpty()) { - // Platforms other than Windows will not return anything. return; } - // RivaTuner hooks the wglCreateContext function, and leaves itself behind as a loaded module + // RivaTuner hooks the wglCreateContext() function to inject itself, and injects even if the process + // is blacklisted in the settings. The only way to stop it from injecting is to close the server process + // entirely. if (BugChecks.ISSUE_2048 && isModuleLoaded(modules, RTSS_HOOKS_MODULE_NAMES)) { - checkRTSSModules(); + checkRTSSModules(window); + } + + // ASUS GPU Tweak III hooks SwapBuffers() function to inject itself, and does so even if the On-Screen + // Display (OSD) is disabled. The only way to stop it from hooking the game is to add the Java process to + // the blacklist, or uninstall the application entirely. + if (BugChecks.ISSUE_2637 && isModuleLoaded(modules, ASUS_GPU_TWEAK_MODULE_NAMES)) { + checkASUSGpuTweakIII(window); } } - private static void checkRTSSModules() { + private static List listModules() { + if (!Platform.isWindows()) { + return List.of(); + } + + var pid = com.sun.jna.platform.win32.Kernel32.INSTANCE.GetCurrentProcessId(); + var modules = new ArrayList(); + + for (var module : Kernel32Util.getModules(pid)) { + modules.add(module.szModule()); + } + + return Collections.unmodifiableList(modules); + } + + private static void checkRTSSModules(NativeWindowHandle window) { LOGGER.warn("RivaTuner Statistics Server (RTSS) has injected into the process! Attempting to apply workarounds for compatibility..."); @Nullable WindowsFileVersion version = null; @@ -66,7 +98,6 @@ private static void checkRTSSModules() { } if (version == null || !isRTSSCompatible(version)) { - Window window = MinecraftClient.getInstance().getWindow(); MessageBox.showMessageBox(window, MessageBox.IconType.ERROR, "Sodium Renderer", """ You appear to be using an older version of RivaTuner Statistics Server (RTSS) which is not compatible with Sodium. @@ -74,10 +105,10 @@ You appear to be using an older version of RivaTuner Statistics Server (RTSS) wh You must either update to a newer version (7.3.4 and later) or close the RivaTuner Statistics Server application. For more information on how to solve this problem, click the 'Help' button.""", - "https://github.com/CaffeineMC/sodium-fabric/wiki/Known-Issues#rtss-incompatible"); - + "https://link.caffeinemc.net/help/sodium/incompatible-software/rivatuner-statistics-server/gh-2048"); + throw new RuntimeException("The installed version of RivaTuner Statistics Server (RTSS) is not compatible with Sodium, " + - "see here for more details: https://github.com/CaffeineMC/sodium-fabric/wiki/Known-Issues#rtss-incompatible"); + "see here for more details: https://link.caffeinemc.net/help/sodium/incompatible-software/rivatuner-statistics-server/gh-2048"); } } @@ -90,6 +121,24 @@ private static boolean isRTSSCompatible(WindowsFileVersion version) { return x > 7 || (x == 7 && y > 3) || (x == 7 && y == 3 && z >= 4); } + private static void checkASUSGpuTweakIII(NativeWindowHandle window) { + MessageBox.showMessageBox(window, MessageBox.IconType.ERROR, "Sodium Renderer", + """ + ASUS GPU Tweak III is not compatible with Minecraft, and causes extreme performance issues and severe graphical corruption when used with Minecraft. + + You *must* do one of the following things to continue: + + a) Open the settings of ASUS GPU Tweak III, enable the Blacklist option, click "Browse from file...", and select the Java runtime (javaw.exe) which is used by Minecraft. + + b) Completely uninstall the ASUS GPU Tweak III application. + + For more information on how to solve this problem, click the 'Help' button.""", + "https://link.caffeinemc.net/help/sodium/incompatible-software/asus-gtiii/gh-2637"); + + throw new RuntimeException("ASUS GPU Tweak III is not compatible with Minecraft, " + + "see here for more details: https://link.caffeinemc.net/help/sodium/incompatible-software/asus-gtiii/gh-2637"); + } + private static @Nullable WindowsFileVersion findRTSSModuleVersion() { long module; @@ -140,10 +189,10 @@ private static boolean isRTSSCompatible(WindowsFileVersion version) { return WindowsFileVersion.fromFileVersion(fileVersion); } - private static boolean isModuleLoaded(List modules, String[] names) { + private static boolean isModuleLoaded(List modules, String[] names) { for (var name : names) { for (var module : modules) { - if (module.path.equalsIgnoreCase(name)) { + if (module.equalsIgnoreCase(name)) { return true; } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PostLaunchChecks.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PostLaunchChecks.java similarity index 66% rename from src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PostLaunchChecks.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PostLaunchChecks.java index 37170c53c6..6180199456 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PostLaunchChecks.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PostLaunchChecks.java @@ -1,8 +1,10 @@ -package me.jellysquid.mods.sodium.client.compatibility.checks; +package net.caffeinemc.mods.sodium.client.compatibility.checks; -import me.jellysquid.mods.sodium.client.gui.console.Console; -import me.jellysquid.mods.sodium.client.gui.console.message.MessageLevel; -import net.minecraft.text.Text; +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds; +import net.caffeinemc.mods.sodium.client.console.Console; +import net.caffeinemc.mods.sodium.client.console.message.MessageLevel; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,17 +15,18 @@ public class PostLaunchChecks { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-PostlaunchChecks"); - public static void onContextInitialized() { + public static void onContextInitialized(NativeWindowHandle window, GlContextInfo context) { + GraphicsDriverChecks.postContextInit(window, context); + NvidiaWorkarounds.applyContextChanges(context); + // FIXME: This can be determined earlier, but we can't access the GUI classes in pre-launch if (isUsingPojavLauncher()) { - Console.instance().logMessage(MessageLevel.SEVERE, Text.translatable("sodium.console.pojav_launcher"), 30.0); - LOGGER.error("It appears that PojavLauncher is being used with an OpenGL compatibility layer. This will " + - "likely cause severe performance issues, graphical issues, and crashes when used with Sodium. This " + - "configuration is not supported -- you are on your own!"); + throw new RuntimeException("It appears that you are using PojavLauncher, which is not supported when " + + "using Sodium. Please check your mods list."); } } - // https://github.com/CaffeineMC/sodium-fabric/issues/1916 + // https://github.com/CaffeineMC/sodium/issues/1916 private static boolean isUsingPojavLauncher() { if (System.getenv("POJAV_RENDERER") != null) { LOGGER.warn("Detected presence of environment variable POJAV_LAUNCHER, which seems to indicate we are running on Android"); diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PreLaunchChecks.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PreLaunchChecks.java new file mode 100644 index 0000000000..5912c283d2 --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PreLaunchChecks.java @@ -0,0 +1,68 @@ +package net.caffeinemc.mods.sodium.client.compatibility.checks; + +import net.caffeinemc.mods.sodium.client.platform.PlatformHelper; +import org.lwjgl.Version; + +/** + * Performs OpenGL driver validation before the game creates an OpenGL context. This runs during the earliest possible + * opportunity at game startup, and uses a custom hardware prober to search for problematic drivers. + */ +public class PreLaunchChecks { + // These version constants are inlined at compile time. + private static final String REQUIRED_LWJGL_VERSION = + Version.VERSION_MAJOR + "." + Version.VERSION_MINOR + "." + Version.VERSION_REVISION; + + public static void checkEnvironment() { + if (BugChecks.ISSUE_2561) { + checkLwjglRuntimeVersion(); + } + } + + private static void checkLwjglRuntimeVersion() { + if (isUsingKnownCompatibleLwjglVersion()) { + return; + } + + String advice; + + if (isUsingPrismLauncher()) { + advice = """ + It appears you are using Prism Launcher to start the game. You can \ + likely fix this problem by opening your instance settings and navigating to the Version\ + section in the sidebar."""; + } else { + advice = """ + You must change the LWJGL version in your launcher to continue. \ + This is usually controlled by the settings for a profile or instance in your launcher."""; + } + + String message = """ + The game failed to start because the currently active LWJGL version is not \ + compatible. + + Installed version: ###CURRENT_VERSION### + Required version: ###REQUIRED_VERSION### + + ###ADVICE_STRING###""" + .replace("###CURRENT_VERSION###", Version.getVersion()) + .replace("###REQUIRED_VERSION###", REQUIRED_LWJGL_VERSION) + .replace("###ADVICE_STRING###", advice); + + PlatformHelper.showCriticalErrorAndClose(null, "Sodium Renderer - Unsupported LWJGL", message, + "https://link.caffeinemc.net/help/sodium/runtime-issue/lwjgl3/gh-2561"); + } + + private static boolean isUsingKnownCompatibleLwjglVersion() { + return Version.getVersion() + .startsWith(REQUIRED_LWJGL_VERSION); + } + + private static boolean isUsingPrismLauncher() { + return getLauncherBrand() + .equalsIgnoreCase("PrismLauncher"); + } + + private static String getLauncherBrand() { + return System.getProperty("minecraft.launcher.brand", "unknown"); + } +} diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GlContextInfo.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GlContextInfo.java new file mode 100644 index 0000000000..7e8f484385 --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GlContextInfo.java @@ -0,0 +1,19 @@ +package net.caffeinemc.mods.sodium.client.compatibility.environment; + +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL11C; + +import java.util.Objects; + +public record GlContextInfo(String vendor, String renderer, String version) { + public static GlContextInfo create() { + String vendor = Objects.requireNonNull(GL11C.glGetString(GL11C.GL_VENDOR), + "GL_VENDOR is NULL"); + String renderer = Objects.requireNonNull(GL11C.glGetString(GL11C.GL_RENDERER), + "GL_RENDERER is NULL"); + String version = Objects.requireNonNull(GL11C.glGetString(GL11C.GL_VERSION), + "GL_VERSION is NULL"); + + return new GlContextInfo(vendor, renderer, version); + } +} diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/OsUtils.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/OsUtils.java new file mode 100644 index 0000000000..c232c0faa9 --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/OsUtils.java @@ -0,0 +1,36 @@ +package net.caffeinemc.mods.sodium.client.compatibility.environment; + +import java.util.Locale; + +public class OsUtils { + private static final OperatingSystem OS = determineOs(); + + public static OperatingSystem determineOs() { + var name = System.getProperty("os.name"); + + if (name != null) { + var normalized = name.toLowerCase(Locale.ROOT); + + if (normalized.startsWith("windows")) { + return OperatingSystem.WIN; + } else if (normalized.startsWith("mac")) { + return OperatingSystem.MAC; + } else if (normalized.startsWith("linux")) { + return OperatingSystem.LINUX; + } + } + + return OperatingSystem.UNKNOWN; + } + + public static OperatingSystem getOs() { + return OS; + } + + public enum OperatingSystem { + WIN, + MAC, + LINUX, + UNKNOWN + } +} \ No newline at end of file diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java similarity index 83% rename from src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java index 0e757aa811..665671c4e2 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.compatibility.environment.probe; +package net.caffeinemc.mods.sodium.client.compatibility.environment.probe; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java similarity index 54% rename from src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java index 2a56bf6a8c..4bfb59179a 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java @@ -1,22 +1,29 @@ -package me.jellysquid.mods.sodium.client.compatibility.environment.probe; +package net.caffeinemc.mods.sodium.client.compatibility.environment.probe; -import me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; -import me.jellysquid.mods.sodium.client.util.OsUtils; +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; +import net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import oshi.util.ExecutingCommand; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; public class GraphicsAdapterProbe { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-GraphicsAdapterProbe"); + private static final Set LINUX_PCI_CLASSES = Set.of( + "0x030000", // PCI_CLASS_DISPLAY_VGA + "0x030001", // PCI_CLASS_DISPLAY_XGA + "0x030200", // PCI_CLASS_DISPLAY_3D + "0x038000" // PCI_CLASS_DISPLAY_OTHER + ); + private static List ADAPTERS = List.of(); public static void findAdapters() { @@ -65,30 +72,23 @@ public static void findAdapters() { Iterable devicesIter = devices::iterator; for (var devicePath : devicesIter) { - // 0x030000 = VGA compatible controller - // 0x030200 = 3D controller (GPUs with no inputs attached, e.g. hybrid graphics laptops) var deviceClass = Files.readString(devicePath.resolve("class")).trim(); - if (!deviceClass.equals("0x030000") && !deviceClass.equals("0x030200")) { + + if (!LINUX_PCI_CLASSES.contains(deviceClass)) { continue; } var pciVendorId = Files.readString(devicePath.resolve("vendor")).trim(); var pciDeviceId = Files.readString(devicePath.resolve("device")).trim(); + var adapterVendor = GraphicsAdapterVendor.fromPciVendorId(pciVendorId); + var adapterName = getPciDeviceName$Linux(pciVendorId, pciDeviceId); - // The Linux kernel doesn't provide a way to get the device name, so we need to use lspci, - // since it comes with a list of known device names mapped to device IDs. - var name = ExecutingCommand // See `man lspci` for more information - .runNative("lspci -vmm -d " + pciVendorId.substring(2) + ":" + pciDeviceId.substring(2)) - .stream() - .filter(line -> line.startsWith("Device:")) - .map(line -> line.substring("Device:".length()).trim()) - .findFirst() - .orElse("unknown"); - - var vendor = GraphicsAdapterVendor.fromPciVendorId(pciVendorId); + if (adapterName == null) { + adapterName = ""; + } - var info = new GraphicsAdapterInfo.LinuxPciAdapterInfo(vendor, name, pciVendorId, pciDeviceId); + var info = new GraphicsAdapterInfo.LinuxPciAdapterInfo(adapterVendor, adapterName, pciVendorId, pciDeviceId); results.add(info); } } catch (IOException ignored) {} @@ -96,6 +96,41 @@ public static void findAdapters() { return results; } + private static @Nullable String getPciDeviceName$Linux(String vendorId, String deviceId) { + // The Linux kernel doesn't provide a way to get the device name, so we need to use lspci, + // since it comes with a list of known device names mapped to device IDs. + // See `man lspci` for more information + + // []:[][:[:]] + var deviceFilter = vendorId.substring(2) + ":" + deviceId.substring(2); + + try { + var process = Runtime.getRuntime() + .exec(new String[] { "lspci", "-vmm", "-d", deviceFilter }); + var result = process.waitFor(); + + if (result != 0) { + throw new IOException("lspci exited with error code: %s".formatted(result)); + } + + try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + + while ((line = reader.readLine()) != null) { + if (line.startsWith("Device:")) { + return line.substring("Device:".length()).trim(); + } + } + } + + throw new IOException("lspci did not return a device name"); + } catch (Throwable e) { + LOGGER.warn("Failed to query PCI device name for %s:%s".formatted(vendorId, deviceId), e); + } + + return null; + } + public static Collection getAdapters() { if (ADAPTERS == null) { LOGGER.error("Graphics adapters not probed yet; returning an empty list."); diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java new file mode 100644 index 0000000000..436bee2ae7 --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java @@ -0,0 +1,78 @@ +package net.caffeinemc.mods.sodium.client.compatibility.environment.probe; + +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; +import java.util.regex.Pattern; + +public enum GraphicsAdapterVendor { + NVIDIA, + AMD, + INTEL, + UNKNOWN; + + // Intel Gen 4, 5, 6 - ig4icd + // Intel Gen 7 - ig7icd + // Intel Gen 7.5 - ig75icd + // Intel Gen 8 - ig8icd + // Intel Gen 9, 9.5 - ig9icd + // Intel Gen 11 - ig11icd + // Intel Xe-LP - ig12icd (early drivers) or igxelpicd (later drivers) + // Intel Xe-HP - igxehpicd + // Intel Xe-HPG - igxehpgicd + // Intel Xe2-LPG - igxe2lpgicd + // Intel Xe2-HPG - igxe2hpgicd + private static final Pattern INTEL_ICD_PATTERN = + Pattern.compile("ig(4|7|75|8|9|11|12|(xe2?(hpg?|lpg?)))icd(32|64)\\.dll", Pattern.CASE_INSENSITIVE); + + private static final Pattern NVIDIA_ICD_PATTERN = + Pattern.compile("nvoglv(32|64)\\.dll", Pattern.CASE_INSENSITIVE); + + private static final Pattern AMD_ICD_PATTERN = + Pattern.compile("(atiglpxx|atig6pxx)\\.dll", Pattern.CASE_INSENSITIVE); + + @NotNull + static GraphicsAdapterVendor fromPciVendorId(String vendor) { + if (vendor.contains("0x1002")) { + return AMD; + } else if (vendor.contains("0x10de")) { + return NVIDIA; + } else if (vendor.contains("0x8086")) { + return INTEL; + } + + return UNKNOWN; + } + + @NotNull + public static GraphicsAdapterVendor fromIcdName(String name) { + if (matchesPattern(INTEL_ICD_PATTERN, name)) { + return INTEL; + } else if (matchesPattern(NVIDIA_ICD_PATTERN, name)) { + return NVIDIA; + } else if (matchesPattern(AMD_ICD_PATTERN, name)) { + return AMD; + } else { + return UNKNOWN; + } + } + + @NotNull + public static GraphicsAdapterVendor fromContext(GlContextInfo context) { + var vendor = context.vendor(); + + return switch (vendor) { + case "NVIDIA Corporation" -> NVIDIA; + case "Intel", "Intel Open Source Technology Center" -> INTEL; + case "AMD", "ATI Technologies Inc." -> AMD; + default -> UNKNOWN; + }; + + } + + private static boolean matchesPattern(Pattern pattern, String name) { + return pattern.matcher(name) + .matches(); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/Workarounds.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/Workarounds.java similarity index 60% rename from src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/Workarounds.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/Workarounds.java index aeeb6d4c74..659d98b6b3 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/Workarounds.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/Workarounds.java @@ -1,13 +1,15 @@ -package me.jellysquid.mods.sodium.client.compatibility.workarounds; +package net.caffeinemc.mods.sodium.client.compatibility.workarounds; -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterInfo; -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; -import me.jellysquid.mods.sodium.client.util.OsUtils; +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.intel.IntelWorkarounds; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -35,10 +37,13 @@ private static Set findNecessaryWorkarounds() { var workarounds = EnumSet.noneOf(Reference.class); var operatingSystem = OsUtils.getOs(); - var graphicsAdapters = GraphicsAdapterProbe.getAdapters(); + if (NvidiaWorkarounds.isNvidiaGraphicsCardPresent()) { + workarounds.add(Reference.NVIDIA_THREADED_OPTIMIZATIONS_BROKEN); + } - if (isUsingNvidiaGraphicsCard(operatingSystem, graphicsAdapters)) { - workarounds.add(Reference.NVIDIA_THREADED_OPTIMIZATIONS); + if (IntelWorkarounds.isUsingIntelGen8OrOlder()) { + workarounds.add(Reference.INTEL_FRAMEBUFFER_BLIT_CRASH_WHEN_UNFOCUSED); + workarounds.add(Reference.INTEL_DEPTH_BUFFER_COMPARISON_UNRELIABLE); } if (operatingSystem == OsUtils.OperatingSystem.LINUX) { @@ -58,12 +63,6 @@ private static Set findNecessaryWorkarounds() { return Collections.unmodifiableSet(workarounds); } - private static boolean isUsingNvidiaGraphicsCard(OsUtils.OperatingSystem operatingSystem, Collection adapters) { - - return (operatingSystem == OsUtils.OperatingSystem.WIN || operatingSystem == OsUtils.OperatingSystem.LINUX) && - adapters.stream().anyMatch(adapter -> adapter.vendor() == GraphicsAdapterVendor.NVIDIA); - } - public static boolean isWorkaroundEnabled(Reference id) { return ACTIVE_WORKAROUNDS.get() .contains(id); @@ -73,14 +72,29 @@ public enum Reference { /** * The NVIDIA driver applies "Threaded Optimizations" when Minecraft is detected, causing severe * performance issues and crashes. - * GitHub Issue + * GitHub Issue */ - NVIDIA_THREADED_OPTIMIZATIONS, + NVIDIA_THREADED_OPTIMIZATIONS_BROKEN, /** * Requesting a No Error Context causes a crash at startup when using a Wayland session. - * GitHub Issue + * GitHub Issue */ NO_ERROR_CONTEXT_UNSUPPORTED, + + /** + * Intel's graphics driver for Gen8 and older seems to be faulty and causes a crash when calling + * glFramebufferBlit after the window loses focus. + * GitHub Issue + */ + INTEL_FRAMEBUFFER_BLIT_CRASH_WHEN_UNFOCUSED, + + /** + * Intel's graphics driver for Gen8 and older does not respect depth comparison rules per the OpenGL + * specification, causing block model overlays to Z-fight when the overlay is on a different render pass than + * the base model. + * GitHub Issue + */ + INTEL_DEPTH_BUFFER_COMPARISON_UNRELIABLE } } diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/intel/IntelWorkarounds.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/intel/IntelWorkarounds.java new file mode 100644 index 0000000000..390502d152 --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/intel/IntelWorkarounds.java @@ -0,0 +1,59 @@ +package net.caffeinemc.mods.sodium.client.compatibility.workarounds.intel; + +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; +import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; +import net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; +import org.jetbrains.annotations.Nullable; + +public class IntelWorkarounds { + // https://github.com/CaffeineMC/sodium/issues/899 + public static @Nullable WindowsFileVersion findIntelDriverMatchingBug899() { + if (OsUtils.getOs() != OsUtils.OperatingSystem.WIN) { + return null; + } + + for (var adapter : GraphicsAdapterProbe.getAdapters()) { + if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { + @Nullable var driverName = wddmAdapterInfo.getOpenGlIcdName(); + + if (driverName == null) { + continue; + } + + var driverVersion = wddmAdapterInfo.openglIcdVersion(); + + // Intel OpenGL ICD for Generation 7 GPUs + if (driverName.matches("ig7icd(32|64).dll")) { + // https://www.intel.com/content/www/us/en/support/articles/000005654/graphics.html + // Anything which matches the 15.33 driver scheme (WDDM x.y.10.w) should be checked + // Drivers before build 5161 are assumed to have bugs with synchronization primitives + if (driverVersion.z() == 10 && driverVersion.w() < 5161) { + return driverVersion; + } + } + } + } + + return null; + } + + public static boolean isUsingIntelGen8OrOlder() { + if (OsUtils.getOs() != OsUtils.OperatingSystem.WIN) { + return false; + } + + for (var adapter : GraphicsAdapterProbe.getAdapters()) { + if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { + @Nullable var driverName = wddmAdapterInfo.getOpenGlIcdName(); + + // Intel OpenGL ICD for legacy GPUs + if (driverName != null && driverName.matches("ig(7|75|8)icd(32|64)\\.dll")) { + return true; + } + } + } + + return false; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java similarity index 82% rename from src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java index d666e13a15..8e3f152c66 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java @@ -1,6 +1,6 @@ -package me.jellysquid.mods.sodium.client.compatibility.workarounds.nvidia; +package net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia; -import me.jellysquid.mods.sodium.client.platform.windows.WindowsFileVersion; +import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; public record NvidiaDriverVersion(int major, int minor) { public static NvidiaDriverVersion parse(WindowsFileVersion version) { diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java new file mode 100644 index 0000000000..d7c1be4da6 --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java @@ -0,0 +1,152 @@ +package net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia; + +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils.OperatingSystem; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; +import net.caffeinemc.mods.sodium.client.platform.unix.Libc; +import net.caffeinemc.mods.sodium.client.platform.windows.WindowsCommandLine; +import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; +import net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GL32C; +import org.lwjgl.opengl.KHRDebug; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NvidiaWorkarounds { + private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-NvidiaWorkarounds"); + + public static boolean isNvidiaGraphicsCardPresent() { + return GraphicsAdapterProbe.getAdapters() + .stream() + .anyMatch(adapter -> adapter.vendor() == GraphicsAdapterVendor.NVIDIA); + } + + // https://github.com/CaffeineMC/sodium/issues/1486 + // The way which NVIDIA tries to detect the Minecraft process could not be circumvented until fairly recently + // So we require that an up-to-date graphics driver is installed so that our workarounds can disable the Threaded + // Optimizations driver hack. + public static @Nullable WindowsFileVersion findNvidiaDriverMatchingBug1486() { + // The Linux driver has two separate branches which have overlapping version numbers, despite also having + // different feature sets. As a result, we can't reliably determine which Linux drivers are broken... + if (OsUtils.getOs() != OperatingSystem.WIN) { + return null; + } + + for (var adapter : GraphicsAdapterProbe.getAdapters()) { + if (adapter.vendor() != GraphicsAdapterVendor.NVIDIA) { + continue; + } + + if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { + var driverVersion = wddmAdapterInfo.openglIcdVersion(); + + if (driverVersion.z() == 15) { // Only match 5XX.XX drivers + // Broken in x.y.15.2647 (526.47) + // Fixed in x.y.15.3623 (536.23) + if (driverVersion.w() >= 2647 && driverVersion.w() < 3623) { + return driverVersion; + } + } + } + } + + return null; + } + + public static void applyEnvironmentChanges() { + // We can't know if the OpenGL context will actually be initialized using the NVIDIA ICD, but we need to + // modify the process environment *now* otherwise the driver will initialize with bad settings. For non-NVIDIA + // drivers, these workarounds are not likely to cause issues. + if (!isNvidiaGraphicsCardPresent()) { + return; + } + + LOGGER.info("Modifying process environment to apply workarounds for the NVIDIA graphics driver..."); + + try { + if (OsUtils.getOs() == OperatingSystem.WIN) { + applyEnvironmentChanges$Windows(); + } else if (OsUtils.getOs() == OperatingSystem.LINUX) { + applyEnvironmentChanges$Linux(); + } + } catch (Throwable t) { + LOGGER.error("Failed to modify the process environment", t); + logWarning(); + } + } + + + private static void applyEnvironmentChanges$Windows() { + // The NVIDIA drivers rely on parsing the command line arguments to detect Minecraft. We need to + // make sure that it detects the game so that *some* important optimizations are applied. Later, + // we will try to enable GL_DEBUG_OUTPUT_SYNCHRONOUS so that "Threaded Optimizations" cannot + // be enabled. + WindowsCommandLine.setCommandLine("net.caffeinemc.sodium / net.minecraft.client.main.Main /"); + } + + private static void applyEnvironmentChanges$Linux() { + // Unlike Windows, we can just request that it not use threaded optimizations instead. + Libc.setEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", "0"); + } + + public static void undoEnvironmentChanges() { + if (OsUtils.getOs() == OperatingSystem.WIN) { + undoEnvironmentChanges$Windows(); + } + } + + private static void undoEnvironmentChanges$Windows() { + WindowsCommandLine.resetCommandLine(); + } + + public static void applyContextChanges(GlContextInfo context) { + // The context may not have been initialized with the NVIDIA ICD, even if we think there is an NVIDIA + // graphics adapter in use. Because enabling these workarounds have the potential to severely hurt performance + // on other drivers, make sure we exit now. + if (GraphicsAdapterVendor.fromContext(context) != GraphicsAdapterVendor.NVIDIA) { + return; + } + + LOGGER.info("Modifying OpenGL context to apply workarounds for the NVIDIA graphics driver..."); + + if (Workarounds.isWorkaroundEnabled(Workarounds.Reference.NVIDIA_THREADED_OPTIMIZATIONS_BROKEN)) { + if (OsUtils.getOs() == OperatingSystem.WIN) { + applyContextChanges$Windows(); + } + } + } + + private static void applyContextChanges$Windows() { + // On Windows, the NVIDIA drivers do not have any environment variable to control whether + // "Threaded Optimizations" are enabled. But we can enable the "GL_DEBUG_OUTPUT_SYNCHRONOUS" option to + // achieve the same effect. + var capabilities = GL.getCapabilities(); + + if (capabilities.GL_KHR_debug) { + LOGGER.info("Enabling GL_DEBUG_OUTPUT_SYNCHRONOUS to force the NVIDIA driver to disable threaded" + + "command submission"); + GL32C.glEnable(KHRDebug.GL_DEBUG_OUTPUT_SYNCHRONOUS); + } else { + LOGGER.error("GL_KHR_debug does not appear to be supported, unable to disable threaded " + + "command submission!"); + logWarning(); + } + } + + private static void logWarning() { + LOGGER.error("READ ME!"); + LOGGER.error("READ ME! The workarounds for the NVIDIA Graphics Driver did not apply correctly!"); + LOGGER.error("READ ME! You are very likely going to run into unexplained crashes and severe performance issues."); + LOGGER.error("READ ME! More information about what went wrong can be found above this message."); + LOGGER.error("READ ME!"); + LOGGER.error("READ ME! Please help us understand why this problem occurred by opening a bug report on our issue tracker:"); + LOGGER.error("READ ME! https://github.com/CaffeineMC/sodium/issues"); + LOGGER.error("READ ME!"); + + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/MessageBox.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/MessageBox.java similarity index 67% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/MessageBox.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/MessageBox.java index 861e36a2cc..fe80d7a501 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/MessageBox.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/MessageBox.java @@ -1,25 +1,24 @@ -package me.jellysquid.mods.sodium.client.platform; - -import me.jellysquid.mods.sodium.client.platform.windows.api.msgbox.MsgBoxParamSw; -import me.jellysquid.mods.sodium.client.util.OsUtils; -import me.jellysquid.mods.sodium.client.platform.windows.api.msgbox.MsgBoxCallback; -import me.jellysquid.mods.sodium.client.platform.windows.api.User32; -import net.minecraft.client.util.Window; -import net.minecraft.util.Util; +package net.caffeinemc.mods.sodium.client.platform; + +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; +import net.caffeinemc.mods.sodium.client.platform.windows.api.Shell32; +import net.caffeinemc.mods.sodium.client.platform.windows.api.User32; +import net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox.MsgBoxCallback; +import net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox.MsgBoxParamSw; import org.jetbrains.annotations.Nullable; -import org.lwjgl.glfw.GLFWNativeWin32; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; -import org.lwjgl.util.tinyfd.TinyFileDialogs; +import java.awt.*; +import java.io.IOException; +import java.net.URI; import java.nio.ByteBuffer; -import java.util.Locale; import java.util.Objects; public class MessageBox { private static final @Nullable MessageBoxImpl IMPL = MessageBoxImpl.chooseImpl(); - public static void showMessageBox(@Nullable Window window, + public static void showMessageBox(NativeWindowHandle window, IconType icon, String title, String description, @Nullable String helpUrl) @@ -40,30 +39,15 @@ private interface MessageBoxImpl { } } - void showMessageBox(@Nullable Window window, + void showMessageBox(NativeWindowHandle window, IconType icon, String title, String description, @Nullable String helpUrl); } - private static class TFDMessageBoxImpl implements MessageBoxImpl { - // This adds information about how to open the help box, since we cannot change the buttons. - private static final String NOTICE = "\n\nFor more information, click OK; otherwise, click Cancel."; - - @Override - public void showMessageBox(@Nullable Window window, IconType icon, String title, String description, @Nullable String helpUrl) { - boolean clicked = TinyFileDialogs.tinyfd_messageBox(title, helpUrl == null ? description : description + NOTICE, helpUrl == null ? "ok" : "okcancel", icon.name().toLowerCase(Locale.ROOT), false); - - if (clicked && helpUrl != null) { - Util.getOperatingSystem() - .open(helpUrl); - } - } - } - private static class WindowsMessageBoxImpl implements MessageBoxImpl { @Override - public void showMessageBox(@Nullable Window window, + public void showMessageBox(NativeWindowHandle window, IconType icon, String title, String description, @Nullable String helpUrl) { @@ -75,8 +59,7 @@ public void showMessageBox(@Nullable Window window, if (helpUrl != null) { msgBoxCallback = MsgBoxCallback.create(lpHelpInfo -> { - Util.getOperatingSystem() - .open(helpUrl); + Shell32.browseUrl(window, helpUrl); }); } else { msgBoxCallback = null; @@ -85,7 +68,7 @@ public void showMessageBox(@Nullable Window window, final long hWndOwner; if (window != null) { - hWndOwner = GLFWNativeWin32.glfwGetWin32Window(window.getHandle()); + hWndOwner = window.getWin32Handle(); } else { hWndOwner = MemoryUtil.NULL; } diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/NativeWindowHandle.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/NativeWindowHandle.java new file mode 100644 index 0000000000..44707a274b --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/NativeWindowHandle.java @@ -0,0 +1,5 @@ +package net.caffeinemc.mods.sodium.client.platform; + +public interface NativeWindowHandle { + long getWin32Handle(); +} diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/PlatformHelper.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/PlatformHelper.java new file mode 100644 index 0000000000..dfc1f2b77f --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/PlatformHelper.java @@ -0,0 +1,29 @@ +package net.caffeinemc.mods.sodium.client.platform; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PlatformHelper { + private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-EarlyDriverScanner"); + + public static void showCriticalErrorAndClose( + @Nullable NativeWindowHandle window, + @NotNull String messageTitle, + @NotNull String messageBody, + @NotNull String helpUrl) + { + // Always print the information to the log file first, just in case we can't show the message box. + LOGGER.error(""" + ###ERROR_DESCRIPTION### + + For more information, please see: ###HELP_URL###""" + .replace("###ERROR_DESCRIPTION###", messageBody) + .replace("###HELP_URL###", helpUrl)); + + // Try to show a graphical message box (if the platform supports it) and shut down the game. + MessageBox.showMessageBox(window, MessageBox.IconType.ERROR, messageTitle, messageBody, helpUrl); + System.exit(1 /* failure code */); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/unix/Libc.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/unix/Libc.java similarity index 92% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/unix/Libc.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/unix/Libc.java index 96da6426a0..45c522e3e5 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/unix/Libc.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/unix/Libc.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.unix; +package net.caffeinemc.mods.sodium.client.platform.unix; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.*; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsCommandLine.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/WindowsCommandLine.java similarity index 96% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsCommandLine.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/WindowsCommandLine.java index 9d223b2e17..3165ad6d1e 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsCommandLine.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/WindowsCommandLine.java @@ -1,6 +1,6 @@ -package me.jellysquid.mods.sodium.client.platform.windows; +package net.caffeinemc.mods.sodium.client.platform.windows; -import me.jellysquid.mods.sodium.client.platform.windows.api.Kernel32; +import net.caffeinemc.mods.sodium.client.platform.windows.api.Kernel32; import org.lwjgl.system.MemoryUtil; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsFileVersion.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/WindowsFileVersion.java similarity index 82% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsFileVersion.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/WindowsFileVersion.java index 042a9f027d..991a1171ea 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsFileVersion.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/WindowsFileVersion.java @@ -1,6 +1,6 @@ -package me.jellysquid.mods.sodium.client.platform.windows; +package net.caffeinemc.mods.sodium.client.platform.windows; -import me.jellysquid.mods.sodium.client.platform.windows.api.version.VersionFixedFileInfoStruct; +import net.caffeinemc.mods.sodium.client.platform.windows.api.version.VersionFixedFileInfoStruct; import org.jetbrains.annotations.NotNull; public record WindowsFileVersion(int x, int y, int z, int w) { diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Gdi32.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Gdi32.java similarity index 71% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Gdi32.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Gdi32.java index 5adef78b90..628c483b9d 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Gdi32.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Gdi32.java @@ -1,17 +1,17 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api; +package net.caffeinemc.mods.sodium.client.platform.windows.api; -import org.apache.commons.lang3.Validate; import org.lwjgl.system.JNI; import org.lwjgl.system.SharedLibrary; import static org.lwjgl.system.APIUtil.apiCreateLibrary; +import static org.lwjgl.system.APIUtil.apiGetFunctionAddress; import static org.lwjgl.system.MemoryUtil.NULL; public class Gdi32 { private static final SharedLibrary LIBRARY = apiCreateLibrary("gdi32"); public static final long PFN_D3DKMTQueryAdapterInfo = LIBRARY.getFunctionAddress("D3DKMTQueryAdapterInfo"); - public static final long PFN_D3DKMTCloseAdapter = LIBRARY.getFunctionAddress( "D3DKMTCloseAdapter"); + public static final long PFN_D3DKMTCloseAdapter = LIBRARY.getFunctionAddress("D3DKMTCloseAdapter"); public static final long PFN_D3DKMTEnumAdapters = LIBRARY.getFunctionAddress("D3DKMTEnumAdapters"); public static boolean isD3DKMTSupported() { @@ -20,19 +20,24 @@ public static boolean isD3DKMTSupported() { // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/nf-d3dkmthk-d3dkmtqueryadapterinfo public static int /* NTSTATUS */ nd3dKmtQueryAdapterInfo(long ptr /* D3DKMT_QUERYADAPTERINFO */) { - Validate.isTrue(PFN_D3DKMTQueryAdapterInfo != NULL); - return JNI.callPI(ptr, PFN_D3DKMTQueryAdapterInfo); + return JNI.callPI(ptr, checkPfn(PFN_D3DKMTQueryAdapterInfo)); } // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/nf-d3dkmthk-d3dkmtenumadapters2 public static int /* NTSTATUS */ nD3DKMTEnumAdapters(long ptr /* D3DKMT_ENUMADAPTERS */) { - Validate.isTrue(PFN_D3DKMTEnumAdapters != NULL); - return JNI.callPI(ptr, PFN_D3DKMTEnumAdapters); + return JNI.callPI(ptr, checkPfn(PFN_D3DKMTEnumAdapters)); } // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/nf-d3dkmthk-d3dkmtcloseadapter public static int /* NTSTATUS */ nD3DKMTCloseAdapter(long ptr /* D3DKMT_CLOSEADAPTER */) { - Validate.isTrue(PFN_D3DKMTCloseAdapter != NULL); - return JNI.callPI(ptr, PFN_D3DKMTCloseAdapter); + return JNI.callPI(ptr, checkPfn(PFN_D3DKMTCloseAdapter)); + } + + private static long checkPfn(long pfn) { + if (pfn == NULL) { + throw new NullPointerException("Function pointer not available"); + } + + return pfn; } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Kernel32.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Kernel32.java similarity index 98% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Kernel32.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Kernel32.java index e417df9069..4b1c62ae3a 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Kernel32.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Kernel32.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api; +package net.caffeinemc.mods.sodium.client.platform.windows.api; import org.jetbrains.annotations.Nullable; import org.lwjgl.PointerBuffer; diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Shell32.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Shell32.java new file mode 100644 index 0000000000..3ba5629e69 --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Shell32.java @@ -0,0 +1,56 @@ +package net.caffeinemc.mods.sodium.client.platform.windows.api; + +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.JNI; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.SharedLibrary; + +import java.util.Objects; + +import static org.lwjgl.system.APIUtil.apiCreateLibrary; +import static org.lwjgl.system.MemoryUtil.NULL; + +public class Shell32 { + private static final SharedLibrary LIBRARY = apiCreateLibrary("shell32"); + + private static final long PFN_ShellExecuteW = LIBRARY.getFunctionAddress("ShellExecuteW"); + + public static void browseUrl(@Nullable NativeWindowHandle window, String url) { + Objects.requireNonNull(url, "URL parameter must be non-null"); + + try (var stack = MemoryStack.stackPush()) { + stack.nUTF16("open", true); + var lpOperation = stack.getPointerAddress(); + + stack.nUTF16(url, true); + var lpFile = stack.getPointerAddress(); + + nShellExecuteW(window != null ? window.getWin32Handle() : NULL, + lpOperation, + lpFile, + NULL, + NULL, + 0x1 /* SW_NORMAL */); + } + } + + public static long nShellExecuteW( + /* HWND */ long hwnd, + /* LPCWSTR */ long lpOperation, + /* LPCWSTR */ long lpFile, + /* LPCWSTR */ long lpParameters, + /* LPCWSTR */ long lpDirectory, + /* INT */ int nShowCmd + ) { + return JNI.invokePPPPPP(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd, checkPfn(PFN_ShellExecuteW)); + } + + private static long checkPfn(long pfn) { + if (pfn == NULL) { + throw new NullPointerException("Function pointer not available"); + } + + return pfn; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/User32.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/User32.java similarity index 82% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/User32.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/User32.java index 26ef8d593c..f4c2165237 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/User32.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/User32.java @@ -1,6 +1,6 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api; +package net.caffeinemc.mods.sodium.client.platform.windows.api; -import me.jellysquid.mods.sodium.client.platform.windows.api.msgbox.MsgBoxParamSw; +import net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox.MsgBoxParamSw; import org.lwjgl.system.APIUtil; import org.lwjgl.system.JNI; import org.lwjgl.system.SharedLibrary; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java similarity index 75% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java index cdceae2378..9ee88de593 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java @@ -1,36 +1,35 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; - -import com.sun.jna.platform.win32.VersionHelpers; -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterInfo; -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; -import me.jellysquid.mods.sodium.client.platform.windows.WindowsFileVersion; -import me.jellysquid.mods.sodium.client.platform.windows.api.Gdi32; -import me.jellysquid.mods.sodium.client.platform.windows.api.version.Version; -import org.apache.commons.io.FilenameUtils; +package net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt; + +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterInfo; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; +import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; +import net.caffeinemc.mods.sodium.client.platform.windows.api.Gdi32; +import net.caffeinemc.mods.sodium.client.platform.windows.api.version.Version; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.Struct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import static me.jellysquid.mods.sodium.client.platform.windows.api.Gdi32.*; -import static me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMTQueryAdapterInfoType.WDDM12.*; +import static net.caffeinemc.mods.sodium.client.platform.windows.api.Gdi32.*; +import static net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMTQueryAdapterInfoType.WDDM12.*; import static org.lwjgl.system.MemoryUtil.memAddress; import static org.lwjgl.system.MemoryUtil.memByteBuffer; public class D3DKMT { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-D3DKMT"); - private static final boolean SUPPORTS_D3DKMT = VersionHelpers.IsWindowsVistaOrGreater() && Gdi32.isD3DKMTSupported(); - private static final boolean SUPPORTS_QUERYING_ADAPTER_TYPE = VersionHelpers.IsWindows8OrGreater(); - public static List findGraphicsAdapters() { - if (!SUPPORTS_D3DKMT) { - LOGGER.warn("Unable to query graphics adapters when the operating system is older than Windows Vista."); + if (!Gdi32.isD3DKMTSupported()) { + // D3DKMT was introduced with Windows Vista, but it was not possible to enumerate adapters and their + // rendering capabilities until Windows 8.0. + LOGGER.warn("Unable to query graphics adapters when the operating system is older than Windows 8.0."); return List.of(); } @@ -74,14 +73,10 @@ private static void freeAdapters(@NotNull D3DKMTAdapterInfoStruct.Buffer adapter } private static @Nullable D3DKMT.WDDMAdapterInfo getAdapterInfo(int adapter) { - int adapterType = -1; + int adapterType = queryAdapterType(adapter); - if (SUPPORTS_QUERYING_ADAPTER_TYPE) { - adapterType = queryAdapterType(adapter); - - if (!isSupportedAdapterType(adapterType)) { - return null; - } + if (!isSupportedAdapterType(adapterType)) { + return null; } String adapterName = queryFriendlyName(adapter); @@ -117,7 +112,7 @@ private static boolean isSupportedAdapterType(int adapterType) { private static @Nullable String queryDriverFileName(int adapter) { try (MemoryStack stack = MemoryStack.stackPush()) { D3DKMTOpenGLInfoStruct info = D3DKMTOpenGLInfoStruct.calloc(stack); - d3dkmtQueryAdapterInfo(adapter, KMTQAITYPE_UMOPENGLINFO, memByteBuffer(info.address(), info.sizeof())); + d3dkmtQueryAdapterInfo(adapter, KMTQAITYPE_UMOPENGLINFO, info); return info.getUserModeDriverFileName(); } @@ -142,7 +137,7 @@ private static boolean isSupportedAdapterType(int adapterType) { private static @NotNull String queryFriendlyName(int adapter) { try (MemoryStack stack = MemoryStack.stackPush()) { D3DKMTAdapterRegistryInfoStruct registryInfo = D3DKMTAdapterRegistryInfoStruct.calloc(stack); - d3dkmtQueryAdapterInfo(adapter, KMTQAITYPE_ADAPTERREGISTRYINFO, memByteBuffer(registryInfo.address(), registryInfo.sizeof())); + d3dkmtQueryAdapterInfo(adapter, KMTQAITYPE_ADAPTERREGISTRYINFO, registryInfo); String name = registryInfo.getAdapterString(); @@ -157,19 +152,23 @@ private static boolean isSupportedAdapterType(int adapterType) { private static int queryAdapterType(int adapter) { try (MemoryStack stack = MemoryStack.stackPush()) { var info = stack.callocInt(1); - d3dkmtQueryAdapterInfo(adapter, KMTQAITYPE_ADAPTERTYPE, memByteBuffer(info)); + d3dkmtQueryAdapterInfo(adapter, KMTQAITYPE_ADAPTERTYPE, memAddress(info), Integer.BYTES); return info.get(0); } } - private static void d3dkmtQueryAdapterInfo(int adapter, int type, ByteBuffer holder) { + private static void d3dkmtQueryAdapterInfo(int adapter, int type, Struct struct) { + d3dkmtQueryAdapterInfo(adapter, type, struct.address(), struct.sizeof()); + } + + private static void d3dkmtQueryAdapterInfo(int adapter, int type, long ptr, int len) { try (MemoryStack stack = MemoryStack.stackPush()) { var info = D3DKMTQueryAdapterInfoStruct.malloc(stack); info.setAdapterHandle(adapter); info.setType(type); - info.setDataPointer(memAddress(holder)); - info.setDataLength(holder.remaining()); + info.setDataPointer(ptr); + info.setDataLength(len); apiCheckError("D3DKMTQueryAdapterInfo", nd3dKmtQueryAdapterInfo(info.address())); } @@ -192,10 +191,10 @@ public record WDDMAdapterInfo( @NotNull GraphicsAdapterVendor vendor, @NotNull String name, int adapterType, - String openglIcdFilePath, - WindowsFileVersion openglIcdVersion + @Nullable String openglIcdFilePath, + @Nullable WindowsFileVersion openglIcdVersion ) implements GraphicsAdapterInfo { - public String getOpenGlIcdName() { + public @Nullable String getOpenGlIcdName() { return D3DKMT.getOpenGlIcdName(this.openglIcdFilePath); } @@ -206,7 +205,18 @@ public String toString() { } } - private static String getOpenGlIcdName(String path) { - return FilenameUtils.removeExtension(FilenameUtils.getName(path)); + private static String getOpenGlIcdName(@Nullable String filePath) { + if (filePath == null) { + return null; + } + + var fileName = Paths.get(filePath) + .getFileName(); + + if (fileName == null) { + return null; + } + + return fileName.toString(); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterInfoStruct.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterInfoStruct.java similarity index 97% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterInfoStruct.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterInfoStruct.java index d0fb1c8a0a..d21f8712e4 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterInfoStruct.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterInfoStruct.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; +package net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt; import org.jetbrains.annotations.NotNull; import org.lwjgl.system.MemoryUtil; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterRegistryInfoStruct.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterRegistryInfoStruct.java similarity index 96% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterRegistryInfoStruct.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterRegistryInfoStruct.java index ee8c533d36..22ea3287ed 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterRegistryInfoStruct.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterRegistryInfoStruct.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; +package net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryStack; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTEnumAdaptersStruct.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTEnumAdaptersStruct.java similarity index 96% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTEnumAdaptersStruct.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTEnumAdaptersStruct.java index 9a34daf9c3..8b101e9866 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTEnumAdaptersStruct.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTEnumAdaptersStruct.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; +package net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTOpenGLInfoStruct.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTOpenGLInfoStruct.java similarity index 97% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTOpenGLInfoStruct.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTOpenGLInfoStruct.java index 1fa98035e5..f27c763781 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTOpenGLInfoStruct.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTOpenGLInfoStruct.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; +package net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoStruct.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoStruct.java similarity index 97% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoStruct.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoStruct.java index c5a139b721..338ce76165 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoStruct.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoStruct.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; +package net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoType.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoType.java similarity index 98% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoType.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoType.java index 9f9d6abc91..7d9cc24a63 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoType.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoType.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; +package net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt; // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/ne-d3dkmthk-_kmtqueryadapterinfotype public class D3DKMTQueryAdapterInfoType { diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallback.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallback.java similarity index 92% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallback.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallback.java index ab8388c0e0..c7634b3c7f 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallback.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallback.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.msgbox; +package net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox; import org.lwjgl.system.Callback; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallbackI.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallbackI.java similarity index 92% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallbackI.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallbackI.java index cebd949822..0994026e18 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallbackI.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/msgbox/MsgBoxCallbackI.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.msgbox; +package net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox; import org.jetbrains.annotations.NotNull; import org.lwjgl.system.CallbackI; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java similarity index 97% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java index 2b814795d9..09e7302750 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.msgbox; +package net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/LanguageCodePage.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/LanguageCodePage.java similarity index 85% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/LanguageCodePage.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/LanguageCodePage.java index 27fa38e395..9debe2adb9 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/LanguageCodePage.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/LanguageCodePage.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.version; +package net.caffeinemc.mods.sodium.client.platform.windows.api.version; import org.lwjgl.system.MemoryUtil; diff --git a/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/QueryResult.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/QueryResult.java new file mode 100644 index 0000000000..c9dbf18c78 --- /dev/null +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/QueryResult.java @@ -0,0 +1,4 @@ +package net.caffeinemc.mods.sodium.client.platform.windows.api.version; + +public record QueryResult(long address, int length) { +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/Version.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/Version.java similarity index 95% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/Version.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/Version.java index 1151edd12c..ea7c10b339 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/Version.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/Version.java @@ -1,6 +1,6 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.version; +package net.caffeinemc.mods.sodium.client.platform.windows.api.version; -import me.jellysquid.mods.sodium.client.platform.windows.api.Kernel32; +import net.caffeinemc.mods.sodium.client.platform.windows.api.Kernel32; import org.jetbrains.annotations.Nullable; import org.lwjgl.PointerBuffer; import org.lwjgl.system.*; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionFixedFileInfoStruct.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/VersionFixedFileInfoStruct.java similarity index 97% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionFixedFileInfoStruct.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/VersionFixedFileInfoStruct.java index 558177681b..660bdd0fc7 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionFixedFileInfoStruct.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/VersionFixedFileInfoStruct.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.version; +package net.caffeinemc.mods.sodium.client.platform.windows.api.version; import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.Struct; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionInfo.java b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/VersionInfo.java similarity index 96% rename from src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionInfo.java rename to src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/VersionInfo.java index 271c66b997..cdb95f2bda 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionInfo.java +++ b/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/version/VersionInfo.java @@ -1,4 +1,4 @@ -package me.jellysquid.mods.sodium.client.platform.windows.api.version; +package net.caffeinemc.mods.sodium.client.platform.windows.api.version; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryUtil;