Skip to content

[bug] HDR 显示器上,不透明度 < 100% 时按键会导致模型瞬间变亮(含已验证修法) #1043

Description

@345-yu

描述 Bug

在 HDR 显示器上,当不透明度 < 100% 时,每次按键模型都会瞬间变亮一下。

三个条件缺一不可:

条件 去掉后
HDR 开启 不再闪
不透明度 < 100% 不再闪
有按键输入 不再闪

这大概率就是 #582 说的那个现象。那条 issue 里作者回复「你录的视频里我没看到透明度发生变化」,报告者自己怀疑显卡驱动就关掉了 —— 如果作者的显示器没开 HDR,是确实复现不出来的。

重现步骤

  1. Windows 上把显示器 HDR 打开(设置 → 系统 → 屏幕 → HDR)
  2. BongoCat 偏好设置 → 猫咪 → 不透明度,调到 100% 以下(我用的 68%)
  3. 正常打字,盯着模型看 —— 每次按键会闪一下变亮
  4. 把 HDR 关掉,重复第 3 步 —— 闪烁消失

预期行为

按键不应该影响模型亮度。

原因分析

主窗口用根节点的 CSS opacity 做半透明,而按键图是动态增删的 DOM 图层:

<div :style="{ opacity: catStore.window.opacity / 100 }">
  <img v-if="backgroundImagePath" />
  <canvas id="live2dCanvas" />
  <img v-for="path in modelStore.pressedKeys" :src="convertFileSrc(path)" />  <!-- 按键时增删 -->
</div>

按键导致 opacity 组内的合成层数量变化 → Chromium 重新评估合成策略。而在 HDR 下,
SDR 白点的映射在不同合成路径上不一致,于是表现为一帧的亮度跳变。

这属于 Chromium / WebView2 已知的 HDR 合成问题,不是本项目代码本身的缺陷:

但本项目可以规避它。

已验证的修法

Windows 下把不透明度交给系统合成器,而不是 CSS:WebView 内部始终保持全不透明,
Chromium 的合成路径就不再随图层增删变化。

窗口本来就带 WS_EX_LAYERED(Tauri 透明窗口的默认行为),只需补一个恒定 alpha:

#[command]
pub async fn set_window_opacity<R: Runtime>(window: WebviewWindow<R>, opacity: u8) {
    let Ok(hwnd) = window.hwnd() else { return };

    unsafe {
        let ex_style = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
        let layered = WS_EX_LAYERED.0 as isize;

        if ex_style & layered == 0 {
            SetWindowLongPtrW(hwnd, GWL_EXSTYLE, ex_style | layered);
        }

        let _ = SetLayeredWindowAttributes(hwnd, COLORREF(0), opacity, LWA_ALPHA);
    }
}

前端相应改为:

// Windows 走系统级窗口 alpha,其余平台保持原有 CSS 行为
if (isWindows) {
  watch(() => catStore.window.opacity, setWindowOpacity, { immediate: true })
}
  :style="{
-   opacity: catStore.window.opacity / 100,
+   opacity: isWindows ? 1 : catStore.window.opacity / 100,
    borderRadius: `${catStore.window.radius}%`,
  }"

windows crate 现有的 feature 就够,不需要加依赖。macOS / Linux 我没有环境验证,
保持原样未改。

实测结果

环境:RTX 4060 Laptop / 2560×1600 @ 240Hz / HDR 开启 / 不透明度 68%

  • 绝大部分闪烁消失
  • 验证补丁确实生效:GetLayeredWindowAttributes 读到 alpha=173, flags=0x2 (LWA_ALPHA),
    173 正好是 68% × 255

残留(如实说明):模型自身带加算混合的小面积高光仍有极轻微闪动 ——
实测到的是角尖的辉光和玻璃杯的透光。因为按键图层的增删本身并没有被消除,
只是窗口内部不再半透明,所以只剩下模型自己半透明的那部分像素还会受影响。

根治需要把按键图渲染进 Live2D 的 canvas(让整个窗口永远只有一个合成层,
按键时只有像素变化、没有图层变化),本次没做 —— 改动面大,且需要重新评估性能。

软件信息

{
  "appName": "BongoCat",
  "appVersion": "1.1.0",
  "platform": "windows",
  "platformArch": "x86_64",
  "platformVersion": "10.0.26200",
  "webview2Runtime": "153.0.4234.48",
  "display": "2560x1600 @ 240Hz, HDR on",
  "gpu": "NVIDIA GeForce RTX 4060 Laptop GPU"
}

对应源码为 master 分支当前状态(44f44bc,2026-04-28)。

附加信息

相关:#582「设置增加"关闭闪烁"功能」。那条 issue 描述的是「透明度 != 100 时窗口透明度会随机闪动」,
触发路径是鼠标在 Edge 顶部栏晃动;本 issue 的触发路径是按键。两者机制应该相同 ——
都是图层/合成状态变化在 HDR 下引起的亮度跳变。

建议把 HDR 作为这类问题的必要复现条件写进排查清单,否则没有 HDR 显示器的人永远复现不出来。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions