mirror of
https://github.com/TriliumNext/Trilium.git
synced 2026-10-12 02:09:59 +00:00
feat(desktop): add option to disable GPU hardware acceleration
Adds a 'Hardware acceleration (GPU)' toggle in Appearance > Performance settings so users hitting GPU driver incompatibilities (blank window, display glitches) can turn it off without manually passing --disable-gpu. The option is read before Electron's ready event straight from the DB provider (core options aren't wired up that early, cf. #10559) and calls app.disableHardwareAcceleration(). Local-only (not synced) since GPU compatibility is per-machine; requires an app restart. Closes #10572 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
eacdeea3ed
commit
fa41709d63
@@ -1579,6 +1579,8 @@
|
||||
"enable-shadows": "Shadows",
|
||||
"enable-backdrop-effects": "Background effects for menus, popups and panels",
|
||||
"enable-smooth-scroll": "Smooth scrolling",
|
||||
"enable-hardware-acceleration": "Hardware acceleration (GPU)",
|
||||
"enable-hardware-acceleration-description": "Disable this if the app shows a blank window or has display glitches caused by your GPU drivers. Requires an app restart.",
|
||||
"app-restart-required": "Requires app restart"
|
||||
},
|
||||
"zoom_factor": {
|
||||
|
||||
@@ -658,6 +658,8 @@ function Performance() {
|
||||
|
||||
{isElectron() && <SmoothScrollEnabledOption />}
|
||||
|
||||
{isElectron() && <HardwareAccelerationOption />}
|
||||
|
||||
</OptionsSection>;
|
||||
}
|
||||
|
||||
@@ -673,6 +675,18 @@ function SmoothScrollEnabledOption() {
|
||||
/>;
|
||||
}
|
||||
|
||||
function HardwareAccelerationOption() {
|
||||
const [ hardwareAccelerationEnabled, setHardwareAccelerationEnabled ] = useTriliumOptionBool("hardwareAccelerationEnabled");
|
||||
|
||||
return <OptionsRowWithToggle
|
||||
name="hardware-acceleration-enabled"
|
||||
label={t("ui-performance.enable-hardware-acceleration")}
|
||||
description={t("ui-performance.enable-hardware-acceleration-description")}
|
||||
currentValue={hardwareAccelerationEnabled}
|
||||
onChange={setHardwareAccelerationEnabled}
|
||||
/>;
|
||||
}
|
||||
|
||||
function MaxContentWidth() {
|
||||
const [maxContentWidth, setMaxContentWidth] = useTriliumOption("maxContentWidth");
|
||||
const [centerContent, setCenterContent] = useTriliumOptionBool("centerContent");
|
||||
|
||||
@@ -53,6 +53,8 @@ const h = vi.hoisted(() => ({
|
||||
isPrimaryInstance: true as boolean,
|
||||
allWindows: [] as unknown[],
|
||||
smoothScroll: "true" as string | null,
|
||||
hardwareAcceleration: "true" as string | null,
|
||||
disableHardwareAcceleration: vi.fn(),
|
||||
// When true, reading an option throws (simulates first run before the schema exists).
|
||||
dbUninitialized: false as boolean,
|
||||
isDbInitialized: true as boolean,
|
||||
@@ -91,6 +93,7 @@ vi.mock("electron", () => {
|
||||
setName: (...a: unknown[]) => h.setName(...a),
|
||||
setUserTasks: (...a: unknown[]) => h.setUserTasks(...a),
|
||||
commandLine: { appendSwitch: (...a: unknown[]) => h.appendSwitch(...a) },
|
||||
disableHardwareAcceleration: (...a: unknown[]) => h.disableHardwareAcceleration(...a),
|
||||
on: (event: string, cb: Handler) => h.appOn.set(event, cb),
|
||||
quit: (...a: unknown[]) => h.quit(...a),
|
||||
exit: (...a: unknown[]) => h.exit(...a),
|
||||
@@ -183,6 +186,7 @@ vi.mock("@triliumnext/server/src/sql_provider.js", () => ({
|
||||
if (h.dbUninitialized) throw new Error("no such table: options");
|
||||
const values: Record<string, string | null> = {
|
||||
smoothScrollEnabled: h.smoothScroll,
|
||||
hardwareAccelerationEnabled: h.hardwareAcceleration,
|
||||
locale: h.locale,
|
||||
formattingLocale: h.formattingLocale
|
||||
};
|
||||
@@ -264,6 +268,8 @@ function resetState() {
|
||||
h.isPrimaryInstance = true;
|
||||
h.allWindows = [];
|
||||
h.smoothScroll = "true";
|
||||
h.hardwareAcceleration = "true";
|
||||
h.disableHardwareAcceleration.mockClear();
|
||||
h.dbUninitialized = false;
|
||||
h.isDbInitialized = true;
|
||||
h.securitySettings = {};
|
||||
@@ -406,6 +412,28 @@ describe("main() bootstrap", () => {
|
||||
expect(switches).not.toContain("disable-smooth-scrolling");
|
||||
});
|
||||
|
||||
// #10572: hardware acceleration is disabled before `ready` from the shared provider
|
||||
// (same reason as smooth-scroll — core options aren't wired up yet).
|
||||
it("disables hardware acceleration when the option is off", async () => {
|
||||
setPlatform("darwin");
|
||||
h.hardwareAcceleration = "false";
|
||||
const { main } = await importMain();
|
||||
await main();
|
||||
expect(h.disableHardwareAcceleration).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps hardware acceleration when the option is on, absent, or the DB has no schema yet", async () => {
|
||||
for (const scenario of [{ value: "true" as string | null }, { value: null }, { value: "false" as string | null, uninitialized: true }]) {
|
||||
resetState();
|
||||
setPlatform("darwin");
|
||||
h.hardwareAcceleration = scenario.value;
|
||||
if (scenario.uninitialized) h.dbUninitialized = true;
|
||||
const { main } = await importMain();
|
||||
await main();
|
||||
expect(h.disableHardwareAcceleration).not.toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
|
||||
// Regression for #10559 (sibling of the smooth-scroll bug): the --lang switch is set
|
||||
// before core init from the same provider, so it must reflect the configured locale
|
||||
// instead of always falling back to "en".
|
||||
|
||||
@@ -191,6 +191,12 @@ export async function main() {
|
||||
if (readDbOption(dbProvider, "smoothScrollEnabled") === "false") {
|
||||
app.commandLine.appendSwitch("disable-smooth-scrolling");
|
||||
}
|
||||
// Lets users work around GPU driver incompatibilities that render a blank window
|
||||
// (see #10572) without having to pass --disable-gpu on the command line. Like the
|
||||
// switch above, this must run before `ready`.
|
||||
if (readDbOption(dbProvider, "hardwareAccelerationEnabled") === "false") {
|
||||
app.disableHardwareAcceleration();
|
||||
}
|
||||
|
||||
// The IPC provider just registers an `ipcMain.on` listener; no TCP socket
|
||||
// or session parser needed, so we can init it here (before startTriliumServer).
|
||||
|
||||
@@ -138,6 +138,8 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
|
||||
shadowsEnabled: boolean;
|
||||
backdropEffectsEnabled: boolean;
|
||||
smoothScrollEnabled: boolean;
|
||||
/** Desktop only: when disabled, Electron starts with GPU hardware acceleration turned off (workaround for GPU driver incompatibilities that cause a blank window). Requires an app restart. */
|
||||
hardwareAccelerationEnabled: boolean;
|
||||
codeNoteTheme: string;
|
||||
codeNoteThemeMatchesApp: boolean;
|
||||
codeNoteThemeLight: string;
|
||||
|
||||
@@ -84,6 +84,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
|
||||
"motionEnabled",
|
||||
"shadowsEnabled",
|
||||
"smoothScrollEnabled",
|
||||
"hardwareAccelerationEnabled",
|
||||
"backdropEffectsEnabled",
|
||||
"maxContentWidth",
|
||||
"centerContent",
|
||||
|
||||
@@ -244,6 +244,7 @@ const defaultOptions: DefaultOption[] = [
|
||||
{ name: "shadowsEnabled", value: "true", isSynced: false },
|
||||
{ name: "backdropEffectsEnabled", value: "true", isSynced: false },
|
||||
{ name: "smoothScrollEnabled", value: "true", isSynced: false },
|
||||
{ name: "hardwareAccelerationEnabled", value: "true", isSynced: false },
|
||||
{ name: "newLayout", value: "true", isSynced: true },
|
||||
|
||||
// PDF
|
||||
|
||||
Reference in New Issue
Block a user