From ba7140ddece68bd433a67942e584287f426dae91 Mon Sep 17 00:00:00 2001 From: Abdo Date: Sat, 9 Sep 2023 01:59:49 +0300 Subject: [PATCH] Expose video driver options for Qt6 (#2643) * Expose video driver options for Qt6 * Default to d3d11/metal/vulkan on Qt6 * Remove `pass` * Add missing word to existing translation * Default to OpenGL on Linux * Exclude Vulkan from macOS * Label default drivers --- ftl/qt/preferences.ftl | 1 + ftl/qt/qt-misc.ftl | 2 +- qt/aqt/__init__.py | 23 ++++++++++++++++++++--- qt/aqt/preferences.py | 34 +++++++++++++++++++--------------- qt/aqt/profiles.py | 41 +++++++++++++++++++++++------------------ qt/aqt/qt/qt6.py | 1 + 6 files changed, 65 insertions(+), 37 deletions(-) diff --git a/ftl/qt/preferences.ftl b/ftl/qt/preferences.ftl index 9f022cb6e..bfb1ca680 100644 --- a/ftl/qt/preferences.ftl +++ b/ftl/qt/preferences.ftl @@ -6,3 +6,4 @@ preferences-video-driver-software-mac = Software (not recommended) preferences-video-driver-opengl-other = OpenGL (faster, may cause issues) preferences-video-driver-software-other = Software (slower) preferences-video-driver-angle = ANGLE (may work better than OpenGL) +preferences-video-driver-default = default diff --git a/ftl/qt/qt-misc.ftl b/ftl/qt/qt-misc.ftl index f79629831..192eb6d52 100644 --- a/ftl/qt/qt-misc.ftl +++ b/ftl/qt/qt-misc.ftl @@ -61,7 +61,7 @@ qt-misc-your-firewall-or-antivirus-program-is = Your firewall or antivirus progr qt-misc-error = Error qt-misc-no-temp-folder = No usable temporary folder found. Make sure C:\\temp exists or TEMP in your environment points to a valid, writable folder. qt-misc-incompatible-video-driver = Your video driver is incompatible. Please start Anki again, and Anki will switch to a slower, more compatible mode. -qt-misc-error-loading-graphics-driver = Error loading '{ $mode }' graphics driver. Please start Anki again to try next driver. { $context } +qt-misc-error-loading-graphics-driver = Error loading '{ $mode }' graphics driver. Please start Anki again to try the next driver. { $context } qt-misc-anki-is-running = Anki Already Running qt-misc-if-instance-is-not-responding = If the existing instance of Anki is not responding, please close it using your task manager, or restart your computer. qt-misc-second = diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 06fb7bc8c..706ff63d8 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -399,6 +399,8 @@ def parseArgs(argv: list[str]) -> tuple[argparse.Namespace, list[str]]: def setupGL(pm: aqt.profiles.ProfileManager) -> None: driver = pm.video_driver() + # RHI errors are emitted multiple times so make sure we only handle them once + driver_failed = False # work around pyqt loading wrong GL library if is_lin: @@ -431,11 +433,16 @@ def setupGL(pm: aqt.profiles.ProfileManager) -> None: context += f"{ctx.function}" if context: context = f"'{context}'" - if ( + + nonlocal driver_failed + if not driver_failed and ( "Failed to create OpenGL context" in msg # Based on the message Qt6 shows to the user; have not tested whether # we can actually capture this or not. or "Failed to initialize graphics backend" in msg + # RHI backend + or "Failed to create QRhi" in msg + or "Failed to get a QRhi" in msg ): QMessageBox.critical( None, @@ -446,6 +453,7 @@ def setupGL(pm: aqt.profiles.ProfileManager) -> None: ), ) pm.set_video_driver(driver.next()) + driver_failed = True return else: print(f"Qt {category}: {msg} {context}") @@ -455,8 +463,9 @@ def setupGL(pm: aqt.profiles.ProfileManager) -> None: if driver == VideoDriver.OpenGL: # Leaving QT_OPENGL unset appears to sometimes produce different results # to explicitly setting it to 'auto'; the former seems to be more compatible. - pass - else: + if qtmajor > 5: + QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.OpenGL) + elif driver in (VideoDriver.Software, VideoDriver.ANGLE): if is_win: # on Windows, this appears to be sufficient on Qt5/Qt6. # On Qt6, ANGLE is excluded by the enum. @@ -469,6 +478,14 @@ def setupGL(pm: aqt.profiles.ProfileManager) -> None: # Required on Qt6 if "QTWEBENGINE_CHROMIUM_FLAGS" not in os.environ: os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--disable-gpu" + if qtmajor > 5: + QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.Software) + elif driver == VideoDriver.Metal: + QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.Metal) + elif driver == VideoDriver.Vulkan: + QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.Vulkan) + elif driver == VideoDriver.Direct3D: + QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.Direct3D11) PROFILE_CODE = os.environ.get("ANKI_PROFILE_CODE") diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index e0eb089be..afcb44601 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -392,9 +392,6 @@ class Preferences(QDialog): self.form.video_driver.setCurrentIndex( self.video_drivers.index(self.mw.pm.video_driver()) ) - if qtmajor > 5: - self.form.video_driver_label.setVisible(False) - self.form.video_driver.setVisible(False) def update_video_driver(self) -> None: new_driver = self.video_drivers[self.form.video_driver.currentIndex()] @@ -404,15 +401,22 @@ class Preferences(QDialog): def video_driver_name_for_platform(driver: VideoDriver) -> str: - if driver == VideoDriver.ANGLE: - return tr.preferences_video_driver_angle() - elif driver == VideoDriver.Software: - if is_mac: - return tr.preferences_video_driver_software_mac() - else: - return tr.preferences_video_driver_software_other() - else: - if is_mac: - return tr.preferences_video_driver_opengl_mac() - else: - return tr.preferences_video_driver_opengl_other() + if qtmajor < 6: + if driver == VideoDriver.ANGLE: + return tr.preferences_video_driver_angle() + elif driver == VideoDriver.Software: + if is_mac: + return tr.preferences_video_driver_software_mac() + else: + return tr.preferences_video_driver_software_other() + elif driver == VideoDriver.OpenGL: + if is_mac: + return tr.preferences_video_driver_opengl_mac() + else: + return tr.preferences_video_driver_opengl_other() + + label = driver.name + if driver == VideoDriver.default_for_platform(): + label += f" ({tr.preferences_video_driver_default()})" + + return label diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index d1b752ee6..084dc0b4f 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -42,37 +42,42 @@ class VideoDriver(Enum): OpenGL = "auto" ANGLE = "angle" Software = "software" + Metal = "metal" + Vulkan = "vulkan" + Direct3D = "d3d11" @staticmethod def default_for_platform() -> VideoDriver: - if is_mac or qtmajor > 5: - return VideoDriver.OpenGL - else: - return VideoDriver.Software + return VideoDriver.all_for_platform()[0] def constrained_to_platform(self) -> VideoDriver: - if self == VideoDriver.ANGLE and not VideoDriver.supports_angle(): - return VideoDriver.Software + if self not in VideoDriver.all_for_platform(): + return VideoDriver.default_for_platform() return self def next(self) -> VideoDriver: - if self == VideoDriver.Software: - return VideoDriver.OpenGL - elif self == VideoDriver.OpenGL and VideoDriver.supports_angle(): - return VideoDriver.ANGLE - else: - return VideoDriver.Software - - @staticmethod - def supports_angle() -> bool: - return is_win and qtmajor < 6 + all = VideoDriver.all_for_platform() + try: + idx = (all.index(self) + 1) % len(all) + except ValueError: + idx = 0 + return all[idx] @staticmethod def all_for_platform() -> list[VideoDriver]: - all = [VideoDriver.OpenGL] - if VideoDriver.supports_angle(): + all = [] + if qtmajor > 5: + if is_win: + all.append(VideoDriver.Direct3D) + if is_mac: + all.append(VideoDriver.Metal) + all.append(VideoDriver.OpenGL) + if qtmajor > 5 and not is_mac: + all.append(VideoDriver.Vulkan) + if is_win and qtmajor < 6: all.append(VideoDriver.ANGLE) all.append(VideoDriver.Software) + return all diff --git a/qt/aqt/qt/qt6.py b/qt/aqt/qt/qt6.py index 4b4d220a9..df79d6b1a 100644 --- a/qt/aqt/qt/qt6.py +++ b/qt/aqt/qt/qt6.py @@ -14,6 +14,7 @@ from PyQt6.QtCore import * # conflicting Qt and qFuzzyCompare definitions require an ignore from PyQt6.QtGui import * # type: ignore[misc,assignment] from PyQt6.QtNetwork import QLocalServer, QLocalSocket, QNetworkProxy +from PyQt6.QtQuick import * from PyQt6.QtWebChannel import QWebChannel from PyQt6.QtWebEngineCore import * from PyQt6.QtWebEngineWidgets import *