From 8fed8669f323f0cc332aac7fdbdc9d357d469394 Mon Sep 17 00:00:00 2001 From: Administrator Date: Tue, 23 Jun 2026 16:05:07 +0200 Subject: [PATCH] fix(startup): disable HW acceleration on RDP/VM to kill the intermittent white screen + export filename (v3.3.106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (A) Intermittent pure-white startup window. On a Windows VM viewed over RemoteDesktop the app sometimes opened to a blank white window — no error banner, not even the static menu bar. A multi-agent investigation + adversarial review pinned it by one airtight deduction: the BrowserWindow is created with backgroundColor '#16181c' (dark), so "white" can never be an un-painted, loading, or failed state — all of those show dark. Pure white, with no banner and no static menu bar, and silent in the logs, eliminates every DOM/init/CSS/load failure mode (each would leave the dark styled shell, unstyled black-on-white menu text, or the red init().catch banner) and leaves exactly one: a GPU/compositor surface failure on the RDP virtual display adapter. The code ran hardware acceleration full-default with no fallback (no disableHardwareAcceleration, no GPU switch anywhere), and the GPU child-process-gone handler is log-only — matching the silent symptom. Fix (the reviewed safe subset): - app.disableHardwareAcceleration() at module top (before app.whenReady), gated on an RDP session (process.env.SESSIONNAME matches /^RDP/) OR a persisted gpu-disabled.flag in userData. The renderer has no WebGL/canvas/video, so software compositing costs effectively nothing here and does not undo the recent renderer perf work; local/console users keep hardware acceleration. - Auto-heal: when a GPU child-process-gone fires, write gpu-disabled.flag so the next launch disables acceleration even if the RDP gate didn't match (covers a VM reached via console or a flaky virtual GPU). Self-heals after at most one white screen. - The existing child-process-gone / render-process-gone / did-fail-load logging is kept so the affected server's next white-start log can confirm the cause (CHILD PROCESS GONE type=GPU). A webContents reload would not disrupt uploads (uploadManager lives in the main process and is torn down only on quit), but no watchdog is added because in this GPU mode init completes — an init-complete signal would not detect the blank surface. (B) Backup export default filename changed from multi-hoster-backup-YYYY-MM-DD.mhu to DD-MM-YYYY-multihoster-backup.mhu. 409 tests pass; clean boot (the guard is inert off-RDP). Co-Authored-By: Claude Opus 4.8 (1M context) --- main.js | 17 ++++++++++++++++- package.json | 2 +- tasks/todo.md | 29 +++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/main.js b/main.js index a0768fe..9d3c33d 100644 --- a/main.js +++ b/main.js @@ -27,6 +27,16 @@ const stats = require('./lib/stats'); const { createCollectors } = require('./lib/diagnostics-collectors'); const { createAgent } = require('./lib/diagnostics-agent'); +function _gpuDisableFlagPath() { + try { return path.join(app.getPath('userData'), 'gpu-disabled.flag'); } catch { return null; } +} +(function maybeDisableHardwareAcceleration() { + let disable = false; + try { if (/^RDP/i.test(process.env.SESSIONNAME || '')) disable = true; } catch {} + if (!disable) { try { const f = _gpuDisableFlagPath(); if (f && fs.existsSync(f)) disable = true; } catch {} } + if (disable) { try { app.disableHardwareAcceleration(); } catch {} } +})(); + const _eventLoopDelay = monitorEventLoopDelay({ resolution: 10 }); _eventLoopDelay.enable(); let _eldLastLog = 0; @@ -1278,6 +1288,9 @@ function createWindow() { app.on('child-process-gone', (_event, details) => { _writeCrashLog('CHILD PROCESS GONE', new Error(details.reason || 'unknown'), details); debugLog(`CHILD PROCESS GONE: type=${details.type} reason=${details.reason} exitCode=${details.exitCode}`); + if (details && details.type === 'GPU') { + try { const f = _gpuDisableFlagPath(); if (f) fs.writeFileSync(f, new Date().toISOString(), 'utf-8'); } catch {} + } }); mainWindow.loadFile(path.join(__dirname, 'renderer', 'index.html')); @@ -2203,9 +2216,11 @@ ipcMain.handle('clear-history', async () => { // --- Backup export / import --- ipcMain.handle('export-backup', async () => { + const _bd = new Date(); + const _bdate = `${String(_bd.getDate()).padStart(2, '0')}-${String(_bd.getMonth() + 1).padStart(2, '0')}-${_bd.getFullYear()}`; const { canceled, filePath } = await dialog.showSaveDialog(mainWindow, { title: 'Backup exportieren', - defaultPath: `multi-hoster-backup-${new Date().toISOString().slice(0, 10)}.mhu`, + defaultPath: `${_bdate}-multihoster-backup.mhu`, filters: [ { name: 'Multi-Hoster Backup (verschlüsselt)', extensions: ['mhu'] }, { name: 'Multi-Hoster Backup (Klartext JSON)', extensions: ['json'] } diff --git a/package.json b/package.json index 0c4f2f0..b05d620 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "multi-hoster-uploader", - "version": "3.3.105", + "version": "3.3.106", "description": "Upload files to doodstream, voe, vidmoly, byse simultaneously", "main": "main.js", "scripts": { diff --git a/tasks/todo.md b/tasks/todo.md index 72345bb..c503f8a 100644 --- a/tasks/todo.md +++ b/tasks/todo.md @@ -1,3 +1,32 @@ +# v3.3.106 — intermittent white-screen on startup (RDP/VM GPU) + export filename + +Two asks. (A) White screen: user sometimes gets a PURE-WHITE window on start (no error banner, NOT even the +static menu bar) on a 12-vCPU Windows VM over RemoteDesktop. Workflow wn2k04x5t (2 agents + adversarial verify) +nailed it by one airtight deduction: the BrowserWindow backgroundColor is DARK (#16181c, main.js:1228), so +"white" can NEVER be an un-painted/loading/failed state — those all show DARK. Pure-white + no menu bar + no +banner + SILENT eliminates every DOM/init/CSS/load mode (each leaves the dark styled shell, unstyled-black-on- +white menu text, or the red init().catch banner) → the ONLY match is a GPU/compositor surface failure on the +RDP virtual display adapter. Confirmed: NO disableHardwareAcceleration / disable-gpu / appendSwitch ANYWHERE, +and child-process-gone (GPU) is log-only (matches the silent symptom) while render-process-gone pops a dialog. +Renderer has ZERO WebGL/canvas/video (audited) → software compositing costs ~nothing and doesn't undo the perf +work; a webContents.reload() doesn't disrupt uploads (uploadManager lives in main, torn down only on quit). +Watchdog REJECTED: the GPU mode lets init complete, so an init-complete signal wouldn't detect the white screen. + +SHIPPED v3.3.106 (main.js — adversary's safe subset): +- app.disableHardwareAcceleration() at module top (before app.whenReady) GATED on RDP (process.env.SESSIONNAME + matches /^RDP/) OR a persisted gpu-disabled.flag (in userData). Zero-regression for local/console users. +- Auto-heal: on a GPU child-process-gone, write gpu-disabled.flag → next launch disables HW accel even if the + RDP gate missed (covers VM-via-console / bad virtual GPU). Self-healing after at most one white screen. +- Kept the child-process-gone/render-process-gone/did-fail-load instrumentation to CONFIRM on the server log + (caveat: root cause is the standard RDP-GPU bet, not yet confirmed on the affected machine — next white-start + log shows CHILD PROCESS GONE type=GPU = confirmed). +- (B) export-backup defaultPath: multi-hoster-backup-YYYY-MM-DD.mhu → DD-MM-YYYY-multihoster-backup.mhu. +409 tests pass, clean boot (guard inert on non-RDP dev machine). + +DEFERRED (perf polish, workflow w5o2rpffx design ready): history JSONL + batch-start residual. + +--- + # v3.3.105 — URGENT data-safety: config write fsync + account-wipe guard User report: after a server CRASHED during upload (NOT the v3.3.104 update — the other server updated fine and