Skip to content

Commit

Permalink
Avoid showing the incompatible driver error in some cases
Browse files Browse the repository at this point in the history
For systems with hybrid graphics, it may be the case
that an incompatible graphics driver is installed, but that
it isn't used for the OpenGL context.

We can avoid showing errors in this situation by checking
the vendor string of the context immediately after
creation.

This is not the most robust check, but in practice, a single
system should not have multiple graphics drivers installed
from the same vendor, so checking the string should be
relatively safe.
  • Loading branch information
jellysquid3 committed Dec 4, 2024
1 parent 57e1ccb commit 58e837c
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ private long wrapGlfwCreateWindowForge(final IntSupplier width, final IntSupplie

@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL;createCapabilities()Lorg/lwjgl/opengl/GLCapabilities;", shift = At.Shift.AFTER))
private void postContextReady(WindowEventHandler eventHandler, ScreenManager monitorTracker, DisplayData settings, String videoMode, String title, CallbackInfo ci) {
GlContextInfo driver = GlContextInfo.create();
LOGGER.info("OpenGL Vendor: {}", driver.vendor());
LOGGER.info("OpenGL Renderer: {}", driver.renderer());
LOGGER.info("OpenGL Version: {}", driver.version());
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.getPlatform() == Util.OS.WINDOWS) {
Expand All @@ -85,8 +85,7 @@ private void postContextReady(WindowEventHandler eventHandler, ScreenManager mon
this.wglPrevContext = MemoryUtil.NULL;
}

NvidiaWorkarounds.applyContextChanges(driver);
PostLaunchChecks.onContextInitialized();
PostLaunchChecks.onContextInitialized((NativeWindowHandle) this, context);
ModuleScanner.checkModules((NativeWindowHandle) this);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
You must update your graphics card driver in order to continue."""
.replace("###CURRENT_DRIVER###", installedVersionString),
"https://github.com/CaffeineMC/sodium/wiki/Driver-Compatibility#windows-intel-gen7");
}
}

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)
You must update your graphics card driver in order to continue."""
.replace("###CURRENT_DRIVER###", installedVersionString),
"https://github.com/CaffeineMC/sodium/wiki/Driver-Compatibility#nvidia-gpus");

}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package net.caffeinemc.mods.sodium.client.compatibility.checks;

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;

Expand All @@ -12,7 +15,10 @@
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, "sodium.console.pojav_launcher", true, 30.0);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,112 +1,55 @@
package net.caffeinemc.mods.sodium.client.compatibility.checks;

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.MessageBox;
import net.caffeinemc.mods.sodium.client.platform.PlatformHelper;
import org.lwjgl.Version;
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");

// These version constants are inlined at compile time.
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() {
public static void checkEnvironment() {
if (BugChecks.ISSUE_2561) {
if (!isUsingKnownCompatibleLwjglVersion()) {
String message = normalMessage;

if (isUsingPrismLauncher()) {
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###", Version.getVersion())
.replace("###REQUIRED_VERSION###", REQUIRED_LWJGL_VERSION),
"https://github.com/CaffeineMC/sodium/wiki/LWJGL-Compatibility");
}
checkLwjglRuntimeVersion();
}
}

public static void onGameInit() {
if (BugChecks.ISSUE_899) {
var installedVersion = IntelWorkarounds.findIntelDriverMatchingBug899();

if (installedVersion != null) {
var installedVersionString = installedVersion.toString();

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###", installedVersionString),
"https://github.com/CaffeineMC/sodium/wiki/Driver-Compatibility#windows-intel-gen7");
}
private static void checkLwjglRuntimeVersion() {
if (isUsingKnownCompatibleLwjglVersion()) {
return;
}

if (BugChecks.ISSUE_1486) {
var installedVersion = NvidiaWorkarounds.findNvidiaDriverMatchingBug1486();

if (installedVersion != null) {
var installedVersionString = NvidiaDriverVersion.parse(installedVersion)
.toString();

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###", installedVersionString),
"https://github.com/CaffeineMC/sodium/wiki/Driver-Compatibility#nvidia-gpus");

}
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.""";
}
}

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 */);
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://github.com/CaffeineMC/sodium/wiki/LWJGL-Compatibility");
}

private static boolean isUsingKnownCompatibleLwjglVersion() {
Expand All @@ -115,7 +58,11 @@ private static boolean isUsingKnownCompatibleLwjglVersion() {
}

private static boolean isUsingPrismLauncher() {
return System.getProperty("minecraft.launcher.brand", "unknown")
return getLauncherBrand()
.equalsIgnoreCase("PrismLauncher");
}

private static String getLauncherBrand() {
return System.getProperty("minecraft.launcher.brand", "unknown");
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
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 {
Expand Down Expand Up @@ -41,6 +43,7 @@ static GraphicsAdapterVendor fromPciVendorId(String vendor) {
return UNKNOWN;
}

@NotNull
public static GraphicsAdapterVendor fromIcdName(String name) {
if (matchesPattern(INTEL_ICD_PATTERN, name)) {
return INTEL;
Expand All @@ -53,6 +56,19 @@ public static GraphicsAdapterVendor fromIcdName(String name) {
}
}

@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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private static Set<Reference> findNecessaryWorkarounds() {
var workarounds = EnumSet.noneOf(Reference.class);
var operatingSystem = OsUtils.getOs();

if (NvidiaWorkarounds.isUsingNvidiaGraphicsCard()) {
if (NvidiaWorkarounds.isNvidiaGraphicsCardPresent()) {
workarounds.add(Reference.NVIDIA_THREADED_OPTIMIZATIONS_BROKEN);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Locale;

public class NvidiaWorkarounds {
private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-NvidiaWorkarounds");

public static boolean isUsingNvidiaGraphicsCard() {
public static boolean isNvidiaGraphicsCardPresent() {
return GraphicsAdapterProbe.getAdapters()
.stream()
.anyMatch(adapter -> adapter.vendor() == GraphicsAdapterVendor.NVIDIA);
Expand Down Expand Up @@ -64,7 +62,7 @@ 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 (!isUsingNvidiaGraphicsCard()) {
if (!isNvidiaGraphicsCardPresent()) {
return;
}

Expand Down Expand Up @@ -106,14 +104,11 @@ public static void undoEnvironmentChanges() {
WindowsCommandLine.resetCommandLine();
}

public static void applyContextChanges(GlContextInfo driver) {
var normalizedVendorName = driver.vendor()
.toLowerCase(Locale.ROOT);

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 (!normalizedVendorName.startsWith("nvidia")) {
if (GraphicsAdapterVendor.fromContext(context) != GraphicsAdapterVendor.NVIDIA) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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 */);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
public class SodiumPreLaunch implements PreLaunchEntrypoint {
@Override
public void onPreLaunch() {
PreLaunchChecks.beforeLWJGLInit();
PreLaunchChecks.checkEnvironment();
GraphicsAdapterProbe.findAdapters();
PreLaunchChecks.onGameInit();
Workarounds.init();
}
}
Loading

0 comments on commit 58e837c

Please sign in to comment.