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 05865a8399..fd5b30206a 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java @@ -1,6 +1,6 @@ package me.jellysquid.mods.sodium.client; -import me.jellysquid.mods.sodium.client.compatibility.checks.EarlyDriverScanner; +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.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; @@ -9,7 +9,7 @@ public class SodiumPreLaunch implements PreLaunchEntrypoint { @Override public void onPreLaunch() { GraphicsAdapterProbe.findAdapters(); - EarlyDriverScanner.scanDrivers(); + PreLaunchChecks.onGameInit(); Workarounds.init(); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/Configuration.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/BugChecks.java similarity index 72% rename from src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/Configuration.java rename to src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/BugChecks.java index 17dfcb9ff1..ccd0edf3f1 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/Configuration.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/BugChecks.java @@ -4,9 +4,10 @@ * "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 Configuration { - public static final boolean WIN32_RTSS_HOOKS = configureCheck("win32.rtss", true); - public static final boolean WIN32_DRIVER_INTEL_GEN7 = configureCheck("win32.intelGen7", true); +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); private static boolean configureCheck(String name, boolean defaultValue) { var propertyValue = System.getProperty(getPropertyKey(name), null); diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/EarlyDriverScanner.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/EarlyDriverScanner.java deleted file mode 100644 index 12e8011d6b..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/EarlyDriverScanner.java +++ /dev/null @@ -1,81 +0,0 @@ -package me.jellysquid.mods.sodium.client.compatibility.checks; - -import me.jellysquid.mods.sodium.client.platform.MessageBox; -import me.jellysquid.mods.sodium.client.platform.windows.WindowsDriverStoreVersion; -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; -import me.jellysquid.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; -import net.minecraft.util.Util; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 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 EarlyDriverScanner { - private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-EarlyDriverScanner"); - - private static final String CONSOLE_MESSAGE_TEMPLATE = """ - ###ERROR_DESCRIPTION### - - For more information, please see: ###HELP_URL### - """; - - private static final String INTEL_GEN7_DRIVER_MESSAGE = """ - The game failed to start because the currently installed Intel Graphics Driver is not compatible. - - Installed version: ###CURRENT_DRIVER### - Required version: 15.33.53.5161 (or newer) - - You must update your graphics card driver in order to continue."""; - - private static final String INTEL_GEN7_DRIVER_HELP_URL = "https://github.com/CaffeineMC/sodium-fabric/wiki/Driver-Compatibility#windows-intel-gen7"; - - public static void scanDrivers() { - if (Configuration.WIN32_DRIVER_INTEL_GEN7) { - var installedVersion = findBrokenIntelGen7GraphicsDriver(); - - if (installedVersion != null) { - showUnsupportedDriverMessageBox( - INTEL_GEN7_DRIVER_MESSAGE - .replace("###CURRENT_DRIVER###", installedVersion.getFriendlyString()), - INTEL_GEN7_DRIVER_HELP_URL); - } - } - } - - private static void showUnsupportedDriverMessageBox(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(CONSOLE_MESSAGE_TEMPLATE - .replace("###ERROR_DESCRIPTION###", message) - .replace("###HELP_URL###", url)); - - // Try to show a graphical message box (if the platform supports it) and shut down the game. - MessageBox.showMessageBox(null, MessageBox.IconType.ERROR, "Sodium Renderer - Unsupported Driver", message, url); - System.exit(1 /* failure code */); - } - - // https://github.com/CaffeineMC/sodium-fabric/issues/899 - private static @Nullable WindowsDriverStoreVersion findBrokenIntelGen7GraphicsDriver() { - if (Util.getOperatingSystem() != Util.OperatingSystem.WINDOWS) { - return null; - } - - for (var adapter : GraphicsAdapterProbe.getAdapters()) { - if (adapter.vendor() != GraphicsAdapterVendor.INTEL) { - continue; - } - - try { - var version = WindowsDriverStoreVersion.parse(adapter.version()); - - if (version.driverModel() == 10 && version.featureLevel() == 18 && version.major() == 10) { - return version; - } - } catch (WindowsDriverStoreVersion.ParseException ignored) { } - } - - return null; - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ModuleScanner.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ModuleScanner.java index af141f56e0..4c01f88fd5 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ModuleScanner.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ModuleScanner.java @@ -1,11 +1,14 @@ 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; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +43,7 @@ public static void checkModules() { } // RivaTuner hooks the wglCreateContext function, and leaves itself behind as a loaded module - if (Configuration.WIN32_RTSS_HOOKS && isModuleLoaded(modules, RTSS_HOOKS_MODULE_NAMES)) { + if (BugChecks.ISSUE_2048 && isModuleLoaded(modules, RTSS_HOOKS_MODULE_NAMES)) { checkRTSSModules(); } } @@ -48,7 +51,7 @@ public static void checkModules() { private static void checkRTSSModules() { LOGGER.warn("RivaTuner Statistics Server (RTSS) has injected into the process! Attempting to apply workarounds for compatibility..."); - String version = null; + @Nullable WindowsFileVersion version = null; try { version = findRTSSModuleVersion(); @@ -65,43 +68,29 @@ 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. " + - "You must either update to a newer version (7.3.4 and later) or close the RivaTuner Statistics Server application.\n\n" + - "For more information on how to solve this problem, click the 'Help' button.", + """ + You appear to be using an older version of RivaTuner Statistics Server (RTSS) which is not compatible with Sodium. + + 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"); - throw new RuntimeException("RivaTuner Statistics Server (RTSS) is not compatible with Sodium, " + + 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"); } } - // BUG: For some reason, the version string can either be in the format of "X.Y.Z.W" or "X, Y, Z, W"... - // This does not make sense, and probably points to our handling of code pages being wrong. But for the time being, - // the regex has been made to handle both formats, because looking at Win32 code any longer is going to break me. - private static final Pattern RTSS_VERSION_PATTERN = Pattern.compile("^(?\\d*)(, |\\.)(?\\d*)(, |\\.)(?\\d*)(, |\\.)(?\\d*)$"); - - private static boolean isRTSSCompatible(String version) { - var matcher = RTSS_VERSION_PATTERN.matcher(version); - - if (!matcher.matches()) { - return false; - } - - try { - int x = Integer.parseInt(matcher.group("x")); - int y = Integer.parseInt(matcher.group("y")); - int z = Integer.parseInt(matcher.group("z")); - - // >=7.3.4 - return x > 7 || (x == 7 && y > 3) || (x == 7 && y == 3 && z >= 4); - } catch (NumberFormatException e) { - LOGGER.warn("Invalid version string: {}", version); - } + private static boolean isRTSSCompatible(WindowsFileVersion version) { + int x = version.x(); + int y = version.y(); + int z = version.z(); - return false; + // >=7.3.4 + return x > 7 || (x == 7 && y > 3) || (x == 7 && y == 3 && z >= 4); } - private static String findRTSSModuleVersion() { + private static @Nullable WindowsFileVersion findRTSSModuleVersion() { long module; try { @@ -141,21 +130,14 @@ private static String findRTSSModuleVersion() { return null; } - var translation = version.queryEnglishTranslation(); - - if (translation == null) { - LOGGER.warn("Couldn't find suitable translation"); - return null; - } - - var fileVersion = version.queryValue("FileVersion", translation); + var fileVersion = version.queryFixedFileInfo(); if (fileVersion == null) { LOGGER.warn("Couldn't query file version"); return null; } - return fileVersion; + return WindowsFileVersion.fromFileVersion(fileVersion); } private static boolean isModuleLoaded(List modules, String[] names) { diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/LateDriverScanner.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PostLaunchChecks.java similarity index 50% rename from src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/LateDriverScanner.java rename to src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PostLaunchChecks.java index d6c05c312c..37170c53c6 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/LateDriverScanner.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PostLaunchChecks.java @@ -1,11 +1,8 @@ package me.jellysquid.mods.sodium.client.compatibility.checks; -import me.jellysquid.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaDriverVersion; import me.jellysquid.mods.sodium.client.gui.console.Console; import me.jellysquid.mods.sodium.client.gui.console.message.MessageLevel; -import me.jellysquid.mods.sodium.client.compatibility.environment.GLContextInfo; import net.minecraft.text.Text; -import net.minecraft.util.Util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,12 +10,11 @@ * Performs OpenGL driver validation after the game creates an OpenGL context. This runs immediately after OpenGL * context creation, and uses the implementation details of the OpenGL context to perform validation. */ -public class LateDriverScanner { +public class PostLaunchChecks { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-PostlaunchChecks"); public static void onContextInitialized() { - checkContextImplementation(); - + // 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 " + @@ -27,52 +23,6 @@ public static void onContextInitialized() { } } - private static void checkContextImplementation() { - GLContextInfo driver = GLContextInfo.create(); - - if (driver == null) { - LOGGER.warn("Could not retrieve identifying strings for OpenGL implementation"); - return; - } - - LOGGER.info("OpenGL Vendor: {}", driver.vendor()); - LOGGER.info("OpenGL Renderer: {}", driver.renderer()); - LOGGER.info("OpenGL Version: {}", driver.version()); - - if (!isSupportedNvidiaDriver(driver)) { - Console.instance() - .logMessage(MessageLevel.SEVERE, Text.translatable("sodium.console.broken_nvidia_driver"), 30.0); - - LOGGER.error("The NVIDIA graphics driver appears to be out of date. This will likely cause severe " + - "performance issues and crashes when used with Sodium. The graphics driver should be updated to " + - "the latest version (version 536.23 or newer)."); - } - } - - // 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 boolean isSupportedNvidiaDriver(GLContextInfo driver) { - // 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 (Util.getOperatingSystem() != Util.OperatingSystem.WINDOWS) { - return true; - } - - var version = NvidiaDriverVersion.tryParse(driver); - - if (version != null) { - return !version.isWithinRange( - new NvidiaDriverVersion(526, 47), // Broken in 526.47 - new NvidiaDriverVersion(536, 23) // Fixed in 536.23 - ); - } - - // If we couldn't determine the version, then it's supported either way. - return true; - } - // https://github.com/CaffeineMC/sodium-fabric/issues/1916 private static boolean isUsingPojavLauncher() { if (System.getenv("POJAV_RENDERER") != null) { 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 new file mode 100644 index 0000000000..9225e7d6f5 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/PreLaunchChecks.java @@ -0,0 +1,134 @@ +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 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"); + + public static void onGameInit() { + if (BugChecks.ISSUE_899) { + var installedVersion = findIntelDriverMatchingBug899(); + + if (installedVersion != null) { + showUnsupportedDriverMessageBox( + """ + 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) { + showUnsupportedDriverMessageBox( + """ + 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 showUnsupportedDriverMessageBox(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)); + + // Try to show a graphical message box (if the platform supports it) and shut down the game. + MessageBox.showMessageBox(null, MessageBox.IconType.ERROR, "Sodium Renderer - Unsupported Driver", 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/checks/ResourcePackScanner.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ResourcePackScanner.java index 35dbb36bae..48480335da 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ResourcePackScanner.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/ResourcePackScanner.java @@ -66,6 +66,7 @@ private static void printToasts(Collection resourcePacks) { var likelyIncompatibleResourcePacks = resourcePacks.stream() .filter((pack) -> !pack.shaderIncludes.isEmpty()) + .filter((pack) -> !incompatibleResourcePacks.contains(pack)) // filter out known-incompatible packs .toList(); boolean shown = false; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/SodiumResourcePackMetadata.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/SodiumResourcePackMetadata.java index cbce1f5a24..4c41c44b8b 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/SodiumResourcePackMetadata.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/checks/SodiumResourcePackMetadata.java @@ -2,10 +2,10 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.resource.metadata.ResourceMetadataSerializer; - import java.util.List; +import net.minecraft.resource.metadata.ResourceMetadataSerializer; + /** * Reads additional metadata for Sodium from a resource pack's `pack.mcmeta` file. This allows the * resource pack author to specify which shaders from their pack are not usable with Sodium, but that diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java index 6351fd349c..0e757aa811 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterInfo.java @@ -1,5 +1,19 @@ package me.jellysquid.mods.sodium.client.compatibility.environment.probe; -public record GraphicsAdapterInfo(GraphicsAdapterVendor vendor, String name, String version) { +import org.jetbrains.annotations.NotNull; -} +public interface GraphicsAdapterInfo { + @NotNull GraphicsAdapterVendor vendor(); + + @NotNull String name(); + + record LinuxPciAdapterInfo( + @NotNull GraphicsAdapterVendor vendor, + @NotNull String name, + + String pciVendorId, + String pciDeviceId + ) implements GraphicsAdapterInfo { + + } +} \ No newline at end of file diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java index 2aa35e34ba..fab52da236 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java @@ -1,9 +1,9 @@ package me.jellysquid.mods.sodium.client.compatibility.environment.probe; -import net.minecraft.util.Util; +import me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; +import me.jellysquid.mods.sodium.client.util.OsUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import oshi.SystemInfo; import oshi.util.ExecutingCommand; import java.io.IOException; @@ -16,46 +16,41 @@ public class GraphicsAdapterProbe { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-GraphicsAdapterProbe"); - private static List ADAPTERS; + private static List ADAPTERS = List.of(); public static void findAdapters() { LOGGER.info("Searching for graphics cards..."); - // We rely on separate detection logic for Linux because Oshi fails to find GPUs without - // display outputs, and we can also retrieve the driver version for NVIDIA GPUs this way. - var results = Util.getOperatingSystem() == Util.OperatingSystem.LINUX - ? findAdaptersLinux() - : findAdaptersCrossPlatform(); - - if (results.isEmpty()) { - LOGGER.warn("No graphics cards were found. Either you have no hardware devices supporting 3D acceleration, or " + - "something has gone terribly wrong!"); + List adapters = switch (OsUtils.getOs()) { + case WIN -> findAdapters$Windows(); + case LINUX -> findAdapters$Linux(); + default -> null; + }; + + if (adapters == null) { + // Not supported on this platform + return; + } else if (adapters.isEmpty()) { + // Tried to search for adapters, but didn't find anything + LOGGER.warn("Could not find any graphics adapters! Probably the device is not on a bus we can probe, or " + + "there are no devices supporting 3D acceleration."); + } else { + // Search returned some adapters + for (var adapter : adapters) { + LOGGER.info("Found graphics adapter: {}", adapter); + } } - ADAPTERS = results; + ADAPTERS = adapters; } - public static List findAdaptersCrossPlatform() { - var systemInfo = new SystemInfo(); - var hardwareInfo = systemInfo.getHardware(); - - var results = new ArrayList(); - - for (var graphicsCard : hardwareInfo.getGraphicsCards()) { - GraphicsAdapterVendor vendor = GraphicsAdapterVendor.identifyVendorFromString(graphicsCard.getVendor()); - String name = graphicsCard.getName(); - String versionInfo = graphicsCard.getVersionInfo(); - - var info = new GraphicsAdapterInfo(vendor, name, versionInfo); - results.add(info); - - LOGGER.info("Found graphics card: {}", info); - } - - return results; + private static List findAdapters$Windows() { + return D3DKMT.findGraphicsAdapters(); } - private static List findAdaptersLinux() { + // We rely on separate detection logic for Linux because Oshi fails to find GPUs without + // display outputs, and we can also retrieve the driver version for NVIDIA GPUs this way. + private static List findAdapters$Linux() { var results = new ArrayList(); try (var devices = Files.list(Path.of("/sys/bus/pci/devices/"))) { @@ -69,37 +64,31 @@ private static List findAdaptersLinux() { continue; } - var deviceVendor = Files.readString(devicePath.resolve("vendor")).trim(); - GraphicsAdapterVendor vendor = GraphicsAdapterVendor.identifyVendorFromString(deviceVendor); + var pciVendorId = Files.readString(devicePath.resolve("vendor")).trim(); + var pciDeviceId = Files.readString(devicePath.resolve("device")).trim(); + // 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 deviceId = Files.readString(devicePath.resolve("device")).trim(); var name = ExecutingCommand // See `man lspci` for more information - .runNative("lspci -vmm -d " + deviceVendor.substring(2) + ":" + deviceId.substring(2)) + .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"); - // This works for the NVIDIA driver, not for i915/amdgpu/etc. though (for obvious reasons). - var versionInfo = "unknown"; - try { - versionInfo = Files.readString(devicePath.resolve("driver/module/version")).trim(); - } catch (IOException ignored) {} + var vendor = GraphicsAdapterVendor.fromPciVendorId(pciVendorId); - var info = new GraphicsAdapterInfo(vendor, name, versionInfo); + var info = new GraphicsAdapterInfo.LinuxPciAdapterInfo(vendor, name, pciVendorId, pciDeviceId); results.add(info); - - LOGGER.info("Found graphics card: {}", info); } } catch (IOException ignored) {} return results; } - public static Collection getAdapters() { + public static Collection getAdapters() { if (ADAPTERS == null) { throw new RuntimeException("Graphics adapters not probed yet"); } 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 index 61a40fd68c..3d2cc60326 100644 --- 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 @@ -1,5 +1,8 @@ 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 { @@ -9,15 +12,38 @@ public enum GraphicsAdapterVendor { UNKNOWN; @NotNull - static GraphicsAdapterVendor identifyVendorFromString(String vendor) { - if (vendor.startsWith("Advanced Micro Devices, Inc.") || vendor.contains("0x1002")) { + static GraphicsAdapterVendor fromPciVendorId(String vendor) { + if (vendor.contains("0x1002")) { return AMD; - } else if (vendor.startsWith("NVIDIA") || vendor.contains("0x10de")) { + } else if (vendor.contains("0x10de")) { return NVIDIA; - } else if (vendor.startsWith("Intel") || vendor.contains("0x8086")) { + } 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 - igxelpicd (Xe-LP; integrated) and igxehpicd (Xe-LP; dedicated) + if (name.matches("ig(4|7|75|8|9|11|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/Workarounds.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/Workarounds.java index 9e14747383..aeeb6d4c74 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/Workarounds.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/Workarounds.java @@ -3,7 +3,7 @@ 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 net.minecraft.util.Util; +import me.jellysquid.mods.sodium.client.util.OsUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +33,7 @@ public static void init() { private static Set findNecessaryWorkarounds() { var workarounds = EnumSet.noneOf(Reference.class); - var operatingSystem = Util.getOperatingSystem(); + var operatingSystem = OsUtils.getOs(); var graphicsAdapters = GraphicsAdapterProbe.getAdapters(); @@ -41,7 +41,7 @@ private static Set findNecessaryWorkarounds() { workarounds.add(Reference.NVIDIA_THREADED_OPTIMIZATIONS); } - if (operatingSystem == Util.OperatingSystem.LINUX) { + if (operatingSystem == OsUtils.OperatingSystem.LINUX) { var session = System.getenv("XDG_SESSION_TYPE"); if (session == null) { @@ -58,8 +58,9 @@ private static Set findNecessaryWorkarounds() { return Collections.unmodifiableSet(workarounds); } - private static boolean isUsingNvidiaGraphicsCard(Util.OperatingSystem operatingSystem, Collection adapters) { - return (operatingSystem == Util.OperatingSystem.WINDOWS || operatingSystem == Util.OperatingSystem.LINUX) && + 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); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java index 1f9c52f56c..d666e13a15 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaDriverVersion.java @@ -1,52 +1,21 @@ package me.jellysquid.mods.sodium.client.compatibility.workarounds.nvidia; -import me.jellysquid.mods.sodium.client.compatibility.environment.GLContextInfo; -import org.apache.commons.lang3.Validate; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; -import java.util.regex.Pattern; +import me.jellysquid.mods.sodium.client.platform.windows.WindowsFileVersion; public record NvidiaDriverVersion(int major, int minor) { - private static final Pattern PATTERN = Pattern.compile("^.*NVIDIA (?\\d+)\\.(?\\d+)(?\\.\\d+)?$"); - - @Nullable - public static NvidiaDriverVersion tryParse(GLContextInfo driver) { - if (!Objects.equals(driver.vendor(), "NVIDIA Corporation")) { - return null; - } - - var matcher = PATTERN.matcher(driver.version()); - - if (!matcher.matches()) { - return null; - } - - int major, minor; - - try { - major = Integer.parseInt(matcher.group("major")); - minor = Integer.parseInt(matcher.group("minor")); - } catch (NumberFormatException e) { - return null; - } + public static NvidiaDriverVersion parse(WindowsFileVersion version) { + // NVIDIA drivers use a strange versioning format, where the major/minor are concatenated into + // the end of the file version. For example, driver 526.47 is represented as X.Y.15.2657, where + // the X and Y values are the usual for WDDM drivers. + int merged = (((version.z() - 10) * 10_000) + version.w()); + int major = merged / 100; + int minor = merged % 100; return new NvidiaDriverVersion(major, minor); } - /** - * @param oldest The oldest version (inclusive) to test against - * @param newest The newest version (exclusive) to test against - * @return True if this version is within the specified version range - */ - public boolean isWithinRange(NvidiaDriverVersion oldest, NvidiaDriverVersion newest) { - return this.asInteger() >= oldest.asInteger() && this.asInteger() < newest.asInteger(); - } - - private long asInteger() { - Validate.isTrue(this.major >= 0); - Validate.isTrue(this.minor >= 0); - - return (Integer.toUnsignedLong(this.major) << 32) | (Integer.toUnsignedLong(this.minor) << 0); + @Override + public String toString() { + return "%d.%d".formatted(this.major, this.minor); } } 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 index 7522124c52..2dd882bb99 100644 --- 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 @@ -3,7 +3,7 @@ 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 net.minecraft.util.Util; +import me.jellysquid.mods.sodium.client.util.OsUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,8 +14,8 @@ public static void install() { LOGGER.warn("Applying workaround: Prevent the NVIDIA OpenGL driver from using broken optimizations (NVIDIA_THREADED_OPTIMIZATIONS)"); try { - switch (Util.getOperatingSystem()) { - case WINDOWS -> { + 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"); @@ -39,8 +39,8 @@ public static void install() { } public static void uninstall() { - switch (Util.getOperatingSystem()) { - case WINDOWS -> { + switch (OsUtils.getOs()) { + case WIN -> { WindowsCommandLine.resetCommandLine(); } case LINUX -> { } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/MessageBox.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/MessageBox.java index 1ae15626ff..7e3e8be414 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/MessageBox.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/MessageBox.java @@ -1,6 +1,7 @@ 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; @@ -9,7 +10,6 @@ import org.lwjgl.glfw.GLFWNativeWin32; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; - import java.nio.ByteBuffer; import java.util.Objects; @@ -28,7 +28,7 @@ public static void showMessageBox(@Nullable Window window, private interface MessageBoxImpl { static @Nullable MessageBoxImpl chooseImpl() { - if (Util.getOperatingSystem() == Util.OperatingSystem.WINDOWS) { + if (OsUtils.getOs() == OsUtils.OperatingSystem.WIN) { return new WindowsMessageBoxImpl(); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/unix/Libc.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/unix/Libc.java index 43eb5930da..96da6426a0 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/unix/Libc.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/unix/Libc.java @@ -4,7 +4,7 @@ import org.lwjgl.system.*; public class Libc { - private static final SharedLibrary LIBRARY = Library.loadNative("me.jellyquid.mods.sodium", "libc.so.6"); + private static final SharedLibrary LIBRARY = APIUtil.apiCreateLibrary("libc.so.6"); private static final long PFN_setenv; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsCommandLine.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsCommandLine.java index 1a855ab1e8..9d223b2e17 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsCommandLine.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsCommandLine.java @@ -2,7 +2,6 @@ import me.jellysquid.mods.sodium.client.platform.windows.api.Kernel32; import org.lwjgl.system.MemoryUtil; - import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.util.Objects; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsDriverStoreVersion.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsDriverStoreVersion.java deleted file mode 100644 index 328888cd37..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsDriverStoreVersion.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.jellysquid.mods.sodium.client.platform.windows; - -import java.util.regex.Pattern; - -public record WindowsDriverStoreVersion(int driverModel, int featureLevel, int major, int minor) { - private static final Pattern PATTERN = Pattern.compile( - "^(?[0-9]{1,2})\\.(?[0-9]{1,2})\\.(?[0-9]{1,5})\\.(?[0-9]{1,5})$"); - - public static WindowsDriverStoreVersion parse(String version) throws ParseException { - var matcher = PATTERN.matcher(version); - - if (!matcher.matches()) { - throw new ParseException(version); - } - - var driverModel = Integer.parseInt(matcher.group("driverModel")); - var featureLevel = Integer.parseInt(matcher.group("featureLevel")); - var major = Integer.parseInt(matcher.group("major")); - var minor = Integer.parseInt(matcher.group("minor")); - - return new WindowsDriverStoreVersion(driverModel, featureLevel, major, minor); - } - - public String getFriendlyString() { - return "%s.%s.%s.%s".formatted(this.driverModel, this.featureLevel, this.major, this.minor); - } - - public static class ParseException extends Exception { - private ParseException(String version) { - super("Not a valid driver store version (%s)".formatted(version)); - } - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsFileVersion.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsFileVersion.java new file mode 100644 index 0000000000..042a9f027d --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/WindowsFileVersion.java @@ -0,0 +1,20 @@ +package me.jellysquid.mods.sodium.client.platform.windows; + +import me.jellysquid.mods.sodium.client.platform.windows.api.version.VersionFixedFileInfoStruct; +import org.jetbrains.annotations.NotNull; + +public record WindowsFileVersion(int x, int y, int z, int w) { + public static @NotNull WindowsFileVersion fromFileVersion(VersionFixedFileInfoStruct fileVersion) { + int x = (fileVersion.getFileVersionMostSignificantBits() >>> 16) & 0xffff; + int y = (fileVersion.getFileVersionMostSignificantBits() >>> 0) & 0xffff; + int z = (fileVersion.getFileVersionLeastSignificantBits() >>> 16) & 0xffff; + int w = (fileVersion.getFileVersionLeastSignificantBits() >>> 0) & 0xffff; + + return new WindowsFileVersion(x, y, z, w); + } + + @Override + public String toString() { + return "%s.%s.%s.%s".formatted(this.x, this.y, this.z, this.w); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Gdi32.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Gdi32.java new file mode 100644 index 0000000000..56b2b67b89 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Gdi32.java @@ -0,0 +1,39 @@ +package me.jellysquid.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.apiGetFunctionAddressOptional; +import static org.lwjgl.system.MemoryUtil.NULL; + +public class Gdi32 { + private static final SharedLibrary LIBRARY = apiCreateLibrary("gdi32"); + + public static final long PFN_D3DKMTQueryAdapterInfo = apiGetFunctionAddressOptional(LIBRARY, "D3DKMTQueryAdapterInfo"); + public static final long PFN_D3DKMTCloseAdapter = apiGetFunctionAddressOptional(LIBRARY, "D3DKMTCloseAdapter"); + public static final long PFN_D3DKMTEnumAdapters = apiGetFunctionAddressOptional(LIBRARY, "D3DKMTEnumAdapters"); + + public static boolean isD3DKMTSupported() { + return PFN_D3DKMTQueryAdapterInfo != NULL && PFN_D3DKMTCloseAdapter != NULL && PFN_D3DKMTEnumAdapters != NULL; + } + + // 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); + } + + // 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); + } + + // 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); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Kernel32.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Kernel32.java index d85c4852b8..e417df9069 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Kernel32.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/Kernel32.java @@ -3,9 +3,7 @@ import org.jetbrains.annotations.Nullable; import org.lwjgl.PointerBuffer; import org.lwjgl.system.*; - import java.nio.ByteBuffer; -import java.nio.IntBuffer; public class Kernel32 { private static final SharedLibrary LIBRARY = APIUtil.apiCreateLibrary("kernel32"); diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/User32.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/User32.java index d4892a5249..26ef8d593c 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/User32.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/User32.java @@ -3,17 +3,14 @@ import me.jellysquid.mods.sodium.client.platform.windows.api.msgbox.MsgBoxParamSw; import org.lwjgl.system.APIUtil; import org.lwjgl.system.JNI; -import org.lwjgl.system.Library; import org.lwjgl.system.SharedLibrary; +import static org.lwjgl.system.APIUtil.apiGetFunctionAddress; + public class User32 { private static final SharedLibrary LIBRARY = APIUtil.apiCreateLibrary("user32"); - private static final long PFN_MessageBoxIndirectW; - - static { - PFN_MessageBoxIndirectW = APIUtil.apiGetFunctionAddress(LIBRARY, "MessageBoxIndirectW"); - } + private static final long PFN_MessageBoxIndirectW = apiGetFunctionAddress(LIBRARY, "MessageBoxIndirectW"); /** * @see findGraphicsAdapters() { + if (!SUPPORTS_D3DKMT) { + LOGGER.warn("Unable to query graphics adapters when the operating system is older than Windows Vista."); + return List.of(); + } + + try (MemoryStack stack = MemoryStack.stackPush()) { + var adapters = D3DKMTEnumAdaptersStruct.calloc(stack); + apiCheckError("D3DKMTEnumAdapters", nD3DKMTEnumAdapters(adapters.address())); + + final D3DKMTAdapterInfoStruct.Buffer adapterInfoBuffer = adapters.getAdapters(); + + try { + return queryAdapters(adapterInfoBuffer); + } finally { + freeAdapters(adapterInfoBuffer); + } + } + } + + private static @NotNull ArrayList queryAdapters(@NotNull D3DKMTAdapterInfoStruct.Buffer adapterInfoBuffer) { + var results = new ArrayList(); + + for (int adapterIndex = adapterInfoBuffer.position(); adapterIndex < adapterInfoBuffer.limit(); adapterIndex++) { + var pAdapterInfo = adapterInfoBuffer.get(adapterIndex); + int pAdapter = pAdapterInfo.getAdapterHandle(); + + var parsed = getAdapterInfo(pAdapter); + + if (parsed != null) { + results.add(parsed); + } + } + + return results; + } + + private static void freeAdapters(@NotNull D3DKMTAdapterInfoStruct.Buffer adapterInfoBuffer) { + for (int adapterIndex = adapterInfoBuffer.position(); adapterIndex < adapterInfoBuffer.limit(); adapterIndex++) { + var adapterInfo = adapterInfoBuffer.get(adapterIndex); + apiCheckError("D3DKMTCloseAdapter", + d3dkmtCloseAdapter(adapterInfo.getAdapterHandle())); + } + } + + private static @Nullable D3DKMT.WDDMAdapterInfo getAdapterInfo(int adapter) { + int adapterType = -1; + + if (SUPPORTS_QUERYING_ADAPTER_TYPE) { + adapterType = queryAdapterType(adapter); + + if (!isSupportedAdapterType(adapterType)) { + return null; + } + } + + String adapterName = queryFriendlyName(adapter); + + @Nullable String driverFileName = queryDriverFileName(adapter); + @Nullable WindowsFileVersion driverVersion = null; + + GraphicsAdapterVendor driverVendor = GraphicsAdapterVendor.UNKNOWN; + + if (driverFileName != null) { + driverVersion = queryDriverVersion(driverFileName); + driverVendor = GraphicsAdapterVendor.fromIcdName(getOpenGlIcdName(driverFileName)); + } + + return new WDDMAdapterInfo(driverVendor, adapterName, adapterType, driverFileName, driverVersion); + + } + + private static boolean isSupportedAdapterType(int adapterType) { + // Adapter does not support rendering + if ((adapterType & 0x1) == 0) { + return false; + } + + // Adapter uses software rendering + if ((adapterType & 0x4) != 0) { + return false; + } + + return true; + } + + private static @Nullable String queryDriverFileName(int adapter) { + try (MemoryStack stack = MemoryStack.stackPush()) { + D3DKMTOpenGLInfoStruct info = D3DKMTOpenGLInfoStruct.calloc(stack); + d3dkmtQueryAdapterInfo(adapter, KMTQAITYPE_UMOPENGLINFO, memByteBuffer(info)); + + return info.getUserModeDriverFileName(); + } + } + + private static @Nullable WindowsFileVersion queryDriverVersion(String file) { + var version = Version.getModuleFileVersion(file); + + if (version == null) { + return null; + } + + var fileVersion = version.queryFixedFileInfo(); + + if (fileVersion == null) { + return null; + } + + return WindowsFileVersion.fromFileVersion(fileVersion); + } + + private static @NotNull String queryFriendlyName(int adapter) { + try (MemoryStack stack = MemoryStack.stackPush()) { + D3DKMTAdapterRegistryInfoStruct registryInfo = D3DKMTAdapterRegistryInfoStruct.calloc(stack); + d3dkmtQueryAdapterInfo(adapter, KMTQAITYPE_ADAPTERREGISTRYINFO, memByteBuffer(registryInfo)); + + String name = registryInfo.getAdapterString(); + + if (name == null) { + name = ""; + } + + return name; + } + } + + private static int queryAdapterType(int adapter) { + try (MemoryStack stack = MemoryStack.stackPush()) { + var info = stack.callocInt(1); + d3dkmtQueryAdapterInfo(adapter, KMTQAITYPE_ADAPTERTYPE, memByteBuffer(info)); + + return info.get(0); + } + } + + private static void d3dkmtQueryAdapterInfo(int adapter, int type, ByteBuffer holder) { + try (MemoryStack stack = MemoryStack.stackPush()) { + var info = D3DKMTQueryAdapterInfoStruct.malloc(stack); + info.setAdapterHandle(adapter); + info.setType(type); + info.setDataPointer(memAddress(holder)); + info.setDataLength(holder.remaining()); + + apiCheckError("D3DKMTQueryAdapterInfo", nd3dKmtQueryAdapterInfo(info.address())); + } + } + + private static int d3dkmtCloseAdapter(int handle) { + try (var stack = MemoryStack.stackPush()) { + var info = stack.ints(handle); + return nD3DKMTCloseAdapter(memAddress(info)); + } + } + + private static void apiCheckError(String name, int error) { + if (error != 0) { + throw new RuntimeException("%s returned non-zero result (error=%s)".formatted(name, Integer.toHexString(error))); + } + } + + public record WDDMAdapterInfo( + @NotNull GraphicsAdapterVendor vendor, + @NotNull String name, + int adapterType, + String openglIcdFilePath, + WindowsFileVersion openglIcdVersion + ) implements GraphicsAdapterInfo { + public String getOpenGlIcdName() { + return D3DKMT.getOpenGlIcdName(this.name); + } + + @Override + public String toString() { + return "AdapterInfo{vendor=%s, description='%s', adapterType=0x%08X, openglIcdFilePath='%s', openglIcdVersion=%s}" + .formatted(this.vendor, this.name, this.adapterType, this.openglIcdFilePath, this.openglIcdVersion); + } + } + + private static String getOpenGlIcdName(String path) { + return FilenameUtils.removeExtension(FilenameUtils.getName(path)); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterInfoStruct.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterInfoStruct.java new file mode 100644 index 0000000000..cf7d4a74c0 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterInfoStruct.java @@ -0,0 +1,87 @@ +package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; + +import org.jetbrains.annotations.NotNull; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.Struct; +import org.lwjgl.system.StructBuffer; + +import java.nio.ByteBuffer; + +import static org.lwjgl.system.MemoryUtil.memGetInt; + +// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/ns-d3dkmthk-_d3dkmt_adapterinfo +// typedef struct _D3DKMT_ADAPTERINFO { +// D3DKMT_HANDLE hAdapter; +// LUID AdapterLuid; +// ULONG NumOfSources; +// BOOL bPrecisePresentRegionsPreferred; +// } D3DKMT_ADAPTERINFO; +class D3DKMTAdapterInfoStruct extends Struct { + public static final int SIZEOF, ALIGNOF; + + private static final int OFFSET_HADAPTER; + private static final int OFFSET_ADAPTER_LUID; + private static final int OFFSET_NUM_OF_SOURCES; + private static final int OFFSET_PRECISE_PRESENT_REGIONS_PREFERRED; + + static { + var layout = __struct( + __member(Integer.BYTES), + __member(Long.BYTES, Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES) + ); + + SIZEOF = layout.getSize(); + ALIGNOF = layout.getAlignment(); + + OFFSET_HADAPTER = layout.offsetof(0); + OFFSET_ADAPTER_LUID = layout.offsetof(1); + OFFSET_NUM_OF_SOURCES = layout.offsetof(2); + OFFSET_PRECISE_PRESENT_REGIONS_PREFERRED = layout.offsetof(3); + } + + D3DKMTAdapterInfoStruct(long address, ByteBuffer container) { + super(address, container); + } + + @Override + protected D3DKMTAdapterInfoStruct create(long address, ByteBuffer container) { + return new D3DKMTAdapterInfoStruct(address, container); + } + + public static D3DKMTAdapterInfoStruct create(long address) { + return new D3DKMTAdapterInfoStruct(address, null); + } + + public static Buffer calloc(int count) { + return new Buffer(MemoryUtil.nmemCalloc(count, SIZEOF), count); + } + + public int getAdapterHandle() { + return memGetInt(this.address + OFFSET_HADAPTER); + } + + @Override + public int sizeof() { + return SIZEOF; + } + + static class Buffer extends StructBuffer { + private static final D3DKMTAdapterInfoStruct ELEMENT_FACTORY = D3DKMTAdapterInfoStruct.create(-1L); + + protected Buffer(long address, int capacity) { + super(address, null, -1, 0, capacity, capacity); + } + + @Override + protected @NotNull D3DKMTAdapterInfoStruct getElementFactory() { + return ELEMENT_FACTORY; + } + + @Override + protected @NotNull Buffer self() { + return this; + } + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterRegistryInfoStruct.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterRegistryInfoStruct.java new file mode 100644 index 0000000000..024a0b057e --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTAdapterRegistryInfoStruct.java @@ -0,0 +1,76 @@ +package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; + +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.Struct; + +import java.nio.ByteBuffer; + +import static org.lwjgl.system.MemoryUtil.*; + +// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/ns-d3dkmthk-_d3dkmt_adapterregistryinfo +// typedef struct _D3DKMT_ADAPTERREGISTRYINFO { +// WCHAR AdapterString[MAX_PATH]; +// WCHAR BiosString[MAX_PATH]; +// WCHAR DacType[MAX_PATH]; +// WCHAR ChipType[MAX_PATH]; +// } D3DKMT_ADAPTERREGISTRYINFO; +class D3DKMTAdapterRegistryInfoStruct extends Struct { + private static final int MAX_PATH = 260; + private static final int SIZEOF, ALIGNOF; + + private static final int OFFSET_ADAPTER_STRING; + private static final int OFFSET_BIOS_STRING; + private static final int OFFSET_DAC_TYPE; + private static final int OFFSET_CHIP_TYPE; + + static { + var layout = __struct( + __member(Short.BYTES * MAX_PATH, Short.BYTES), + __member(Short.BYTES * MAX_PATH, Short.BYTES), + __member(Short.BYTES * MAX_PATH, Short.BYTES), + __member(Short.BYTES * MAX_PATH, Short.BYTES) + ); + + SIZEOF = layout.getSize(); + ALIGNOF = layout.getAlignment(); + + OFFSET_ADAPTER_STRING = layout.offsetof(0); + OFFSET_BIOS_STRING = layout.offsetof(1); + OFFSET_DAC_TYPE = layout.offsetof(2); + OFFSET_CHIP_TYPE = layout.offsetof(3); + } + + private D3DKMTAdapterRegistryInfoStruct(long address, ByteBuffer container) { + super(address, container); + } + + @Override + protected D3DKMTAdapterRegistryInfoStruct create(long address, ByteBuffer container) { + return new D3DKMTAdapterRegistryInfoStruct(address, container); + } + + public static D3DKMTAdapterRegistryInfoStruct calloc(MemoryStack stack) { + return new D3DKMTAdapterRegistryInfoStruct(stack.ncalloc(ALIGNOF, 1, SIZEOF), null); + } + + public @Nullable String getAdapterString() { + return getString(this.address + OFFSET_ADAPTER_STRING); + } + + private static @Nullable String getString(long ptr) { + final var buf = memByteBuffer(ptr, Short.BYTES * MAX_PATH); + final var len = memLengthNT2(buf) >> 1; + + if (len == 0) { + return null; + } + + return memUTF16(buf, len); + } + + @Override + public int sizeof() { + return SIZEOF; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTEnumAdaptersStruct.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTEnumAdaptersStruct.java new file mode 100644 index 0000000000..7ba23b64aa --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTEnumAdaptersStruct.java @@ -0,0 +1,60 @@ +package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.Struct; + +import java.nio.ByteBuffer; + +import static org.lwjgl.system.MemoryUtil.*; + +// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/ns-d3dkmthk-_d3dkmt_enumadapters +// typedef struct _D3DKMT_ENUMADAPTERS { +// [in] ULONG NumAdapters; +// D3DKMT_ADAPTERINFO Adapters[MAX_ENUM_ADAPTERS]; +//} D3DKMT_ENUMADAPTERS; +class D3DKMTEnumAdaptersStruct extends Struct { + private static final int SIZEOF, ALIGNOF; + private static final int MAX_ENUM_ADAPTERS = 16; + + private static final int OFFSET_NUM_ADAPTERS; + private static final int OFFSET_ADAPTERS; + + static { + var layout = __struct( + __member(Integer.BYTES), + __member(D3DKMTAdapterInfoStruct.SIZEOF * MAX_ENUM_ADAPTERS, D3DKMTAdapterInfoStruct.ALIGNOF) + ); + + SIZEOF = layout.getSize(); + ALIGNOF = layout.getAlignment(); + + OFFSET_NUM_ADAPTERS = layout.offsetof(0); + OFFSET_ADAPTERS = layout.offsetof(1); + } + + private D3DKMTEnumAdaptersStruct(long address, @Nullable ByteBuffer container) { + super(address, container); + } + + @Override + protected @NotNull D3DKMTEnumAdaptersStruct create(long address, ByteBuffer container) { + return new D3DKMTEnumAdaptersStruct(address, container); + } + + public static D3DKMTEnumAdaptersStruct calloc(MemoryStack stack) { + return new D3DKMTEnumAdaptersStruct(stack.ncalloc(ALIGNOF, 1, SIZEOF), null); + } + + public D3DKMTAdapterInfoStruct.Buffer getAdapters() { + return new D3DKMTAdapterInfoStruct.Buffer(this.address + OFFSET_ADAPTERS, + MemoryUtil.memGetInt(this.address + OFFSET_NUM_ADAPTERS)); + } + + @Override + public int sizeof() { + return SIZEOF; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTOpenGLInfoStruct.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTOpenGLInfoStruct.java new file mode 100644 index 0000000000..44672a4795 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTOpenGLInfoStruct.java @@ -0,0 +1,87 @@ +package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.Struct; + +import java.nio.ByteBuffer; + +import static org.lwjgl.system.MemoryUtil.*; + +// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/ns-d3dkmthk-_d3dkmt_openglinfo +// typedef struct _D3DKMT_OPENGLINFO { +// WCHAR UmdOpenGlIcdFileName[MAX_PATH]; +// [out] ULONG Version; +// [in] ULONG Flags; +// } D3DKMT_OPENGLINFO; +public class D3DKMTOpenGLInfoStruct extends Struct { + private static final int MAX_PATH = 260; + private static final int SIZEOF, ALIGNOF; + + private static final int OFFSET_UMD_OPENGL_ICD_FILE_NAME; + private static final int OFFSET_VERSION; + private static final int OFFSET_FLAGS; + + static { + var layout = __struct( + __member(Short.BYTES * MAX_PATH, Short.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES) + ); + + SIZEOF = layout.getSize(); + ALIGNOF = layout.getAlignment(); + + OFFSET_UMD_OPENGL_ICD_FILE_NAME = layout.offsetof(0); + OFFSET_VERSION = layout.offsetof(1); + OFFSET_FLAGS = layout.offsetof(2); + } + + private D3DKMTOpenGLInfoStruct(long address, @Nullable ByteBuffer container) { + super(address, container); + } + + @Override + protected @NotNull D3DKMTOpenGLInfoStruct create(long address, ByteBuffer container) { + return new D3DKMTOpenGLInfoStruct(address, container); + } + + public static D3DKMTOpenGLInfoStruct calloc() { + return new D3DKMTOpenGLInfoStruct(MemoryUtil.nmemCalloc(1, SIZEOF), null); + } + + public static D3DKMTOpenGLInfoStruct calloc(MemoryStack stack) { + return new D3DKMTOpenGLInfoStruct(stack.ncalloc(ALIGNOF, 1, SIZEOF), null); + } + + public ByteBuffer getUserModeDriverFileNameBuffer() { + return MemoryUtil.memByteBuffer(this.address + OFFSET_UMD_OPENGL_ICD_FILE_NAME, + Short.BYTES * MAX_PATH); + } + + public @Nullable String getUserModeDriverFileName() { + var name = this.getUserModeDriverFileNameBuffer(); + var length = memLengthNT2(name); + + if (length == 0) { + return null; + } + + return memUTF16(memAddress(name), length >> 1); + } + + public int getVersion() { + return MemoryUtil.memGetInt(this.address + OFFSET_VERSION); + } + + public int getFlags() { + return MemoryUtil.memGetInt(this.address + OFFSET_FLAGS); + } + + @Override + public int sizeof() { + return SIZEOF; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoStruct.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoStruct.java new file mode 100644 index 0000000000..8c4a697e3f --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoStruct.java @@ -0,0 +1,79 @@ +package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.Pointer; +import org.lwjgl.system.Struct; + +import java.nio.ByteBuffer; + +import static org.lwjgl.system.MemoryUtil.memPutAddress; +import static org.lwjgl.system.MemoryUtil.memPutInt; + +// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/ns-d3dkmthk-_d3dkmt_queryadapterinfo +// typedef struct _D3DKMT_QUERYADAPTERINFO { +// D3DKMT_HANDLE hAdapter; +// KMTQUERYADAPTERINFOTYPE Type; +// VOID *pPrivateDriverData; +// UINT PrivateDriverDataSize; +// } D3DKMT_QUERYADAPTERINFO; +class D3DKMTQueryAdapterInfoStruct extends Struct { + private static final int SIZEOF, ALIGNOF; + + private static final int OFFSET_ADAPTER_HANDLE; + private static final int OFFSET_TYPE; + private static final int OFFSET_DATA_PTR; + private static final int OFFSET_DATA_SIZE; + + static { + var layout = __struct( + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Pointer.POINTER_SIZE), + __member(Integer.BYTES) + ); + + SIZEOF = layout.getSize(); + ALIGNOF = layout.getAlignment(); + + OFFSET_ADAPTER_HANDLE = layout.offsetof(0); + OFFSET_TYPE = layout.offsetof(1); + OFFSET_DATA_PTR = layout.offsetof(2); + OFFSET_DATA_SIZE = layout.offsetof(3); + } + + private D3DKMTQueryAdapterInfoStruct(long address, @Nullable ByteBuffer container) { + super(address, container); + } + + @Override + protected @NotNull D3DKMTAdapterInfoStruct create(long address, ByteBuffer container) { + return new D3DKMTAdapterInfoStruct(address, container); + } + + public static D3DKMTQueryAdapterInfoStruct malloc(MemoryStack stack) { + return new D3DKMTQueryAdapterInfoStruct(stack.nmalloc(ALIGNOF, SIZEOF), null); + } + + @Override + public int sizeof() { + return SIZEOF; + } + + public void setAdapterHandle(int adapter) { + memPutInt(this.address + OFFSET_ADAPTER_HANDLE, adapter); + } + + public void setType(int type) { + memPutInt(this.address + OFFSET_TYPE, type); + } + + public void setDataPointer(long address) { + memPutAddress(this.address + OFFSET_DATA_PTR, address); + } + + public void setDataLength(int length) { + memPutInt(this.address + OFFSET_DATA_SIZE, length); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoType.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoType.java new file mode 100644 index 0000000000..9f9d6abc91 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMTQueryAdapterInfoType.java @@ -0,0 +1,122 @@ +package me.jellysquid.mods.sodium.client.platform.windows.api.d3dkmt; + +// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/ne-d3dkmthk-_kmtqueryadapterinfotype +public class D3DKMTQueryAdapterInfoType { + // Windows Vista + public static class WDDM10 { + public static final int KMTQAITYPE_UMDRIVERPRIVATE = 0; + public static final int KMTQAITYPE_UMDRIVERNAME = 1; + public static final int KMTQAITYPE_UMOPENGLINFO = 2; + public static final int KMTQAITYPE_GETSEGMENTSIZE = 3; + public static final int KMTQAITYPE_ADAPTERGUID = 4; + public static final int KMTQAITYPE_FLIPQUEUEINFO = 5; + public static final int KMTQAITYPE_ADAPTERADDRESS = 6; + public static final int KMTQAITYPE_SETWORKINGSETINFO = 7; + public static final int KMTQAITYPE_ADAPTERREGISTRYINFO = 8; + public static final int KMTQAITYPE_CURRENTDISPLAYMODE = 9; + public static final int KMTQAITYPE_MODELIST = 10; + public static final int KMTQAITYPE_CHECKDRIVERUPDATESTATUS = 11; + public static final int KMTQAITYPE_VIRTUALADDRESSINFO = 12; + public static final int KMTQAITYPE_DRIVERVERSION = 13; + } + + // Windows 7 + public static class WDDM11 extends WDDM10 { + + } + + // Windows 8 + public static class WDDM12 extends WDDM11 { + public static final int KMTQAITYPE_ADAPTERTYPE = 15; + public static final int KMTQAITYPE_OUTPUTDUPLCONTEXTSCOUNT = 16; + public static final int KMTQAITYPE_WDDM_1_2_CAPS = 17; + public static final int KMTQAITYPE_UMD_DRIVER_VERSION = 18; + public static final int KMTQAITYPE_DIRECTFLIP_SUPPORT = 19; + } + + // Windows 8.1 + public static class WDDM13 extends WDDM12 { + public static final int KMTQAITYPE_MULTIPLANEOVERLAY_SUPPORT = 20; + public static final int KMTQAITYPE_DLIST_DRIVER_NAME = 21; + public static final int KMTQAITYPE_WDDM_1_3_CAPS = 22; + public static final int KMTQAITYPE_MULTIPLANEOVERLAY_HUD_SUPPORT = 23; + } + + // Windows 10 Version 1507 + public static class WDDM20 extends WDDM13 { + public static final int KMTQAITYPE_WDDM_2_0_CAPS = 24; + public static final int KMTQAITYPE_NODEMETADATA = 25; + public static final int KMTQAITYPE_CPDRIVERNAME = 26; + public static final int KMTQAITYPE_XBOX = 27; + public static final int KMTQAITYPE_INDEPENDENTFLIP_SUPPORT = 28; + public static final int KMTQAITYPE_MIRACASTCOMPANIONDRIVERNAME = 29; + public static final int KMTQAITYPE_PHYSICALADAPTERCOUNT = 30; + public static final int KMTQAITYPE_PHYSICALADAPTERDEVICEIDS = 31; + public static final int KMTQAITYPE_DRIVERCAPS_EXT = 32; + public static final int KMTQAITYPE_QUERY_MIRACAST_DRIVER_TYPE = 33; + public static final int KMTQAITYPE_QUERY_GPUMMU_CAPS = 34; + public static final int KMTQAITYPE_QUERY_MULTIPLANEOVERLAY_DECODE_SUPPORT = 35; + public static final int KMTQAITYPE_QUERY_HW_PROTECTION_TEARDOWN_COUNT = 36; + public static final int KMTQAITYPE_QUERY_ISBADDRIVERFORHWPROTECTIONDISABLED = 37; + public static final int KMTQAITYPE_MULTIPLANEOVERLAY_SECONDARY_SUPPORT = 38; + public static final int KMTQAITYPE_INDEPENDENTFLIP_SECONDARY_SUPPORT = 39; + } + + // Windows 10 Version 1607 + public static class WDDM21 extends WDDM20 { + public static final int KMTQAITYPE_PANELFITTER_SUPPORT = 40; + } + + // Windows 10 Version 1703 + public static class WDDM22 extends WDDM21 { + public static final int KMTQAITYPE_PHYSICALADAPTERPNPKEY = 41; + public static final int KMTQAITYPE_GETSEGMENTGROUPSIZE = 42; + public static final int KMTQAITYPE_MPO3DDI_SUPPORT = 43; + public static final int KMTQAITYPE_HWDRM_SUPPORT = 44; + public static final int KMTQAITYPE_MPOKERNELCAPS_SUPPORT = 45; + public static final int KMTQAITYPE_MULTIPLANEOVERLAY_STRETCH_SUPPORT = 46; + public static final int KMTQAITYPE_GET_DEVICE_VIDPN_OWNERSHIP_INFO = 47; + } + + // Windows 10 Version 1709 + public static class WDDM23 extends WDDM22 { + + } + + // Windows 10 Version 1803 + public static class WDDM24 extends WDDM23 { + public static final int KMTQAITYPE_QUERYREGISTRY = 48; + public static final int KMTQAITYPE_KMD_DRIVER_VERSION = 49; + public static final int KMTQAITYPE_BLOCKLIST_KERNEL = 50; + public static final int KMTQAITYPE_BLOCKLIST_RUNTIME = 51; + public static final int KMTQAITYPE_ADAPTERGUID_RENDER = 52; + public static final int KMTQAITYPE_ADAPTERADDRESS_RENDER = 53; + public static final int KMTQAITYPE_ADAPTERREGISTRYINFO_RENDER = 54; + public static final int KMTQAITYPE_CHECKDRIVERUPDATESTATUS_RENDER = 55; + public static final int KMTQAITYPE_DRIVERVERSION_RENDER = 56; + public static final int KMTQAITYPE_ADAPTERTYPE_RENDER = 57; + public static final int KMTQAITYPE_WDDM_1_2_CAPS_RENDER = 58; + public static final int KMTQAITYPE_WDDM_1_3_CAPS_RENDER = 59; + public static final int KMTQAITYPE_QUERY_ADAPTER_UNIQUE_GUID = 60; + public static final int KMTQAITYPE_NODEPERFDATA = 61; + public static final int KMTQAITYPE_ADAPTERPERFDATA = 62; + public static final int KMTQAITYPE_ADAPTERPERFDATA_CAPS = 63; + public static final int KMTQUITYPE_GPUVERSION = 64; + } + + // Windows 10 Version 1903 + public static class WDDM26 extends WDDM24 { + public static final int KMTQAITYPE_DRIVER_DESCRIPTION = 65; + public static final int KMTQAITYPE_DRIVER_DESCRIPTION_RENDER = 66; + public static final int KMTQAITYPE_SCANOUT_CAPS = 67; + public static final int KMTQAITYPE_DISPLAY_UMDRIVERNAME = 71; // Added in 19H2 + public static final int KMTQAITYPE_PARAVIRTUALIZATION_RENDER = 68; + } + + // Windows 10 Version 2004 + public static class WDDM27 extends WDDM26 { + public static final int KMTQAITYPE_SERVICENAME = 69; + public static final int KMTQAITYPE_WDDM_2_7_CAPS = 70; + public static final int KMTQAITYPE_TRACKEDWORKLOAD_SUPPORT = 72; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java index d5410ba98e..83b867234e 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/msgbox/MsgBoxParamSw.java @@ -1,5 +1,6 @@ package me.jellysquid.mods.sodium.client.platform.windows.api.msgbox; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; @@ -61,7 +62,7 @@ private MsgBoxParamSw(long address, @Nullable ByteBuffer container) { } @Override - protected MsgBoxParamSw create(long address, ByteBuffer container) { + protected @NotNull MsgBoxParamSw create(long address, ByteBuffer container) { return new MsgBoxParamSw(address, container); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionFixedFileInfoStruct.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionFixedFileInfoStruct.java new file mode 100644 index 0000000000..0c86600a81 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionFixedFileInfoStruct.java @@ -0,0 +1,100 @@ +package me.jellysquid.mods.sodium.client.platform.windows.api.version; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.Struct; + +import java.nio.ByteBuffer; + +// typedef struct tagVS_FIXEDFILEINFO { +// DWORD dwSignature; +// DWORD dwStrucVersion; +// DWORD dwFileVersionMS; +// DWORD dwFileVersionLS; +// DWORD dwProductVersionMS; +// DWORD dwProductVersionLS; +// DWORD dwFileFlagsMask; +// DWORD dwFileFlags; +// DWORD dwFileOS; +// DWORD dwFileType; +// DWORD dwFileSubtype; +// DWORD dwFileDateMS; +// DWORD dwFileDateLS; +// } VS_FIXEDFILEINFO; + +public class VersionFixedFileInfoStruct extends Struct { + private static final int SIZEOF, ALIGNOF; + + private static final int OFFSET_DW_SIGNATURE; + private static final int OFFSET_DW_STRUCTURE_VERSION; + private static final int OFFSET_DW_FILE_VERSION_MS; + private static final int OFFSET_DW_FILE_VERSION_LS; + private static final int OFFSET_DW_PRODUCT_VERSION_MS; + private static final int OFFSET_DW_PRODUCT_VERSION_LS; + private static final int OFFSET_DW_FILE_FLAGS_MASK; + private static final int OFFSET_DW_FILE_FLAGS; + private static final int OFFSET_DW_FILE_OS; + private static final int OFFSET_DW_FILE_TYPE; + private static final int OFFSET_DW_FILE_SUBTYPE; + private static final int OFFSET_DW_FILE_DATE_MS; + private static final int OFFSET_DW_FILE_DATE_LS; + + static { + var layout = __struct( + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES) + ); + + OFFSET_DW_SIGNATURE = layout.offsetof(0); + OFFSET_DW_STRUCTURE_VERSION = layout.offsetof(1); + OFFSET_DW_FILE_VERSION_MS = layout.offsetof(2); + OFFSET_DW_FILE_VERSION_LS = layout.offsetof(3); + OFFSET_DW_PRODUCT_VERSION_MS = layout.offsetof(4); + OFFSET_DW_PRODUCT_VERSION_LS = layout.offsetof(5); + OFFSET_DW_FILE_FLAGS_MASK = layout.offsetof(6); + OFFSET_DW_FILE_FLAGS = layout.offsetof(7); + OFFSET_DW_FILE_OS = layout.offsetof(8); + OFFSET_DW_FILE_TYPE = layout.offsetof(9); + OFFSET_DW_FILE_SUBTYPE = layout.offsetof(10); + OFFSET_DW_FILE_DATE_MS = layout.offsetof(11); + OFFSET_DW_FILE_DATE_LS = layout.offsetof(12); + + SIZEOF = layout.getSize(); + ALIGNOF = layout.getAlignment(); + } + private VersionFixedFileInfoStruct(long address, ByteBuffer container) { + super(address, container); + } + + public static VersionFixedFileInfoStruct from(long address) { + return new VersionFixedFileInfoStruct(address, null); + } + + @Override + protected VersionFixedFileInfoStruct create(long address, ByteBuffer container) { + return new VersionFixedFileInfoStruct(address, container); + } + + public int getFileVersionMostSignificantBits() { + return MemoryUtil.memGetInt(this.address + OFFSET_DW_FILE_VERSION_MS); + } + + public int getFileVersionLeastSignificantBits() { + return MemoryUtil.memGetInt(this.address + OFFSET_DW_FILE_VERSION_LS); + } + + @Override + public int sizeof() { + return SIZEOF; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionInfo.java b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionInfo.java index ea277b508d..271c66b997 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionInfo.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/platform/windows/api/version/VersionInfo.java @@ -28,6 +28,16 @@ public static VersionInfo allocate(int len) { return MemoryUtil.memUTF16(result.address()); } + public @Nullable VersionFixedFileInfoStruct queryFixedFileInfo() { + var result = Version.query(this.pBlock, "\\"); + + if (result == null) { + return null; + } + + return VersionFixedFileInfoStruct.from(result.address()); + } + public @Nullable LanguageCodePage queryEnglishTranslation() { var result = Version.query(this.pBlock, "\\VarFileInfo\\Translation"); diff --git a/src/main/java/me/jellysquid/mods/sodium/client/util/OsUtils.java b/src/main/java/me/jellysquid/mods/sodium/client/util/OsUtils.java new file mode 100644 index 0000000000..974a0da37f --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/util/OsUtils.java @@ -0,0 +1,25 @@ +package me.jellysquid.mods.sodium.client.util; + +import org.apache.commons.lang3.SystemUtils; + +public class OsUtils { + + public static OperatingSystem getOs() { + if (SystemUtils.IS_OS_WINDOWS) { + return OperatingSystem.WIN; + } else if (SystemUtils.IS_OS_MAC) { + return OperatingSystem.MAC; + } else if (SystemUtils.IS_OS_LINUX) { + return OperatingSystem.LINUX; + } + + return OperatingSystem.UNKNOWN; + } + + public enum OperatingSystem { + WIN, + MAC, + LINUX, + UNKNOWN + } +} \ No newline at end of file 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 614ea54230..1da80fc219 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,7 +1,7 @@ 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.LateDriverScanner; +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.minecraft.client.WindowEventHandler; @@ -58,7 +58,7 @@ private void postWindowCreated(WindowEventHandler eventHandler, MonitorTracker m this.wglPrevContext = MemoryUtil.NULL; } - LateDriverScanner.onContextInitialized(); + PostLaunchChecks.onContextInitialized(); ModuleScanner.checkModules(); }