公司动态
Flutter Windows 开始支持 Impeller ,还修复了多窗口 bug
因为这段时间一直在使用 Flutter 多窗口的原因有一个 windows 多窗口的 bug 一直挺难受的就是在切换到项目子窗口的时候会默认激化主窗口之后才能再次点击回到子窗口更准确说是就是激活一个 childRegularWindow后主窗口会重新回到前台并盖住它。这个问题的诱因很多但是实际上主要就是代码写的太草台。原因之一是在 Engine 里Engine 收到 Windows 的WM_ACTIVATE消息时不管窗口是激活还是失活都会执行FocusRootViewOf(this);也就是让当前窗口里的 Flutter view 获得焦点但是问题在于当主窗口正在失去焦点时它也会执行这句然后就导致主窗口本来应该退到后面然后又主动SetFocus自己然后 Windows 又把主窗口重新激活并拉到最前面。这个 Bug 就算了主要是 Flutter framework 还把多个窗口的 focus tree 挂错了层级Flutter 的多窗口里每个窗口都有自己的View但 Framework 里子窗口的FocusScope居然还会有挂在父窗口的FocusScope下面的情况这样会造成一个新问题子窗口获得焦点时主窗口的 focus scope 也被认为“有焦点”然后主窗口又又又通知 engine“我有焦点了把我的 native window 激活”然后又又又又会把主窗口拉到前面····而且整个过程也没有判断是否已经有另一个窗口拿到焦点这就导致这个 Bug 是综合好几个地方导致之前好久一直没能根本修复问题。一直只能写兜底代码来强切。不过针对这三个问题最近终于修复了如果收到的是WA_INACTIVE也就是窗口正在失活就直接返回不再抢焦点每个View的 focus subtree 直接挂到全局 root scope 下只有在没有其他窗口申请焦点时才恢复旧焦点然后在我合并完这部分代码之后马上又躺了另外一个坑Flutter windows 开始启用 Impeller 了是的Flutter 开始在 Windows 上用 OpenGLESSDF 适配 Impeller 了然后问题又出现了在绘制 canvas 的时候出现了细线看起来比 Skia 粗约 1.3–1.6 个物理像素的情况而且更暗如果叠加 alpha 还会直接失效这些以前在我项目里若隐若现的细线在 Impeller 出来后直接就变成又大又粗根据 issue 的计算目前不同的粗细在 Impeller 上变化倍率也不一样笔画宽度 (logical→physical)Skia 覆盖度Impeller 覆盖度倍数0.5 → 0.75 px0.752.002.7×1.0 → 1.5 px1.502.781.9×2.0 → 3.0 px3.004.591.5×4.0 → 6.0 px6.007.501.25×而且粗细还不是主要问题主要问题是在 canvas transform 缩放下AA 边缘也跟着被放大导致缩放比例越大线条越糊zoom4 时 0.5px 线变成 12px 宽模糊带Alpha 值被完全忽略0xCC和0x66渲染结果像素完全一致极细线在某些情况下整条消失这个问题看起来像是 Impeller 的SDFAlpha实现有问题smoothstep范围是固定的物理像素宽度比如smoothstep(-fade_size, fade_size, sdf)会在笔画边缘两侧分别产生约半个pixel_size * aa_pixels的渐变过渡带而这个过渡带是固定宽度取决于aa_pixels参数代码中设置为常量。然后对于 Skia 这样的解析式 AA覆盖度 笔画宽度但是对于 SDF 的 smoothstep覆盖度 笔画宽度 固定边缘过渡带所以当笔画只有 0.75 物理像素宽时这个固定的 ~1.2px 过渡带就变得很明显导致渲染出来的线是实心宽线 两侧晕染。另外float half_stroke max(stroke_width, base_pixel_size) * 0.5的最小宽度钳制没有补偿 alpha 当stroke_width 1像素时这里会把宽度强制钳制到至少 1 个像素base_pixel_size但没有按比例降低 alpha 来补偿。而Skia 的做法是亚像素线宽 把线变成 1 像素宽但按比例降低 alpha0.75px 线 ≈ 38% 不透明的 1px 线Impeller 的 SDF 实现直接给了满 alpha 的 1px 宽线所以看起来更暗更粗。也就是SDFStroke根本没有把 alpha 纳入计算alpha 值到 fragment shader 时已经在别处处理没有和宽度钳制联动。目前暂时缓解的办法有用drawRect替代drawLine用高度等于期望线宽的 filled rect 代替 stroked linefill 路径的 UberSDF 覆盖度是正确的强制使用 Skia--no-enable-impeller1px 整像素线宽用 1.0 物理像素宽的线目前看来Impeller 在 Windows 上估计还是要经历一次 Android 的问题不得不说自渲染的维护成本确实高不过多窗口在 Windows 上确实也可以用了自从 bug 解决之后基本没什么大问题了。链接[Windows][windowing] Activating a child RegularWindow from the taskbar briefly focuses it, then the main window is brought to front · Issue #187436 · flutter/flutter · GitHub[Impeller] Windows OpenGLESSDF renders thin strokes ~2x thicker and darker than Skia (constant ~1.4px extra coverage at all widths) · Issue #188911 · flutter/flutter · GitHub