refactor(rhi-webgl): rework canvas resolution API, follow display size by default#3037
refactor(rhi-webgl): rework canvas resolution API, follow display size by default#3037luo2430 wants to merge 38 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough
ChangesOffscreenCanvas detection and auto-resize
Sequence Diagram(s)sequenceDiagram
participant Caller
participant WebGLEngine
participant ResizeObserver
participant WebCanvas
Caller->>WebGLEngine: enableAutoResize(pixelRatio?)
WebGLEngine->>ResizeObserver: disconnect() (prior observer, if any)
WebGLEngine->>ResizeObserver: new ResizeObserver(callback)
WebGLEngine->>ResizeObserver: observe(canvas element)
WebGLEngine->>WebGLEngine: on(Shutdown, _cleanupResizeObserver)
Note over ResizeObserver,WebCanvas: canvas client dimensions change
ResizeObserver->>WebCanvas: resizeByClientSize(pixelRatio)
Caller->>WebGLEngine: disableAutoResize()
WebGLEngine->>WebGLEngine: off(Shutdown, _cleanupResizeObserver)
WebGLEngine->>ResizeObserver: disconnect()
Note over WebGLEngine: engine.destroy() triggers Shutdown
WebGLEngine->>WebGLEngine: _cleanupResizeObserver()
WebGLEngine->>ResizeObserver: disconnect()
Estimated code review effort🎯 2 (Simple) | ⏱️ ~15 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/src/rhi-webgl/WebGLEngine.test.ts`:
- Around line 140-145: The spy on the _cleanupResizeObserver method is created
after enableAutoResize() has already registered the Shutdown listener, meaning
the listener holds a reference to the original function rather than the spied
version. Move the vi.spyOn call for cleanupResizeObserverSpy to before the
enableAutoResize() call to ensure the registered Shutdown listener captures the
spied function reference, allowing the assertion on
cleanupResizeObserverSpy.toHaveBeenCalledTimes(1) to work correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: ca7819a5-d724-46f3-ad7d-879ae0800382
📒 Files selected for processing (3)
packages/core/src/input/InputManager.tspackages/rhi-webgl/src/WebGLEngine.tstests/src/rhi-webgl/WebGLEngine.test.ts
|
这里有一个时序问题需要提一下,根据 W3C 规范,浏览器每帧的渲染管线顺序是: 在发生 resize 那帧,引擎绘制 rAF 时使用的还是旧的画布尺寸,随后 ResizeObserver 回调修改画布的尺寸会清空 canvas buffer ,表现就是这一帧会白,但 window 监听 resize 也不可以直接用,因为 window.resize 仅监听浏览器窗口大小变化,所以其他引擎要么完全手动触发,要么轮询检测,避免本帧 resize 与渲染不同步。 |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev/2.0 #3037 +/- ##
===========================================
+ Coverage 79.37% 79.42% +0.04%
===========================================
Files 903 903
Lines 100632 100652 +20
Branches 11260 11273 +13
===========================================
+ Hits 79879 79942 +63
+ Misses 20569 20526 -43
Partials 184 184
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
代码处理完毕,vitest问题我会去开一个issue,有部分解决方案和提议,但需要验证。另外,isOffscreenCanvas中的 |
|
@cptbtptpbcptdtptp 这里有好康的 |
|
|
@cptbtptpbcptdtptp 这个示例能分享一下吗?我用于测试。 |
examples/src/device-restore.ts 这个,设置 enableAutoResize 就可以。 |
…ents OffscreenCanvas not following the display size is the correct, only sensible behavior, not a gap to warn about — so the no-op needs no TODO. Also drop trailing periods from single-line // comments for consistency.
…lback The callback body already reads as flag-only.
Keep only the non-obvious why: the 0x0 guard exists because an unmounted canvas has no layout size, and the rounding is a fractional-dpr TODO. Drop the parts that restate the code (the retained flag, the Math.round).
That an unmounted canvas reports clientWidth 0 is common web knowledge, and the guard reads clearly on its own.
…BoxSize TODO It is not Baseline (no Safari), so it can't be swapped in unconditionally — the reason it isn't used yet, invisible from the code.
…ed path It is a separate exact-pixel path gated by feature detection, with this clientWidth-based rounding kept as the permanent fallback — not a replacement (the value can't be polyfilled where the browser lacks it).
…sizing Capture the observer entry's devicePixelContentBoxSize (exact device pixels, so only the scale is applied) and fall back to clientWidth * dpr where it is unavailable (Safari). This removes the fractional-dpr rounding error on browsers that support it, without changing behavior elsewhere.
Group the @internal method with the other internal methods instead of placing it between the public scale accessors and setAutoResolution.
The field names and the callback that assigns them are self-explanatory.
The eager synchronous _pendingResize=true ran before the observer's first entry arrived, so the first resize always took the clientWidth fallback even where devicePixelContentBoxSize is supported. Drive the first resize from observe()'s initial callback instead; only reapply synchronously when already observing (a scale change), reusing the last measured size.
…size These were introduced earlier in this PR and never shipped, so keeping them as deprecated forwarders was contradictory (new + deprecated in the same change). The capability lives on the canvas now — use engine.canvas.setAutoResolution() / setResolution() directly.
Move the per-frame resize pump into Engine.update so WebGLEngine no longer overrides update. _pumpPendingResize becomes a base Canvas no-op that WebCanvas overrides, like _exitAutoResize.
Just state what it returns; the resolution API is documented on the WebCanvas/Canvas class it returns, not restated on the getter.
GuoLei1990
left a comment
There was a problem hiding this comment.
复审 @ f1dc48713(线性追加 3 个 commit:把 per-frame resize pump 从 WebGLEngine.update() override 下沉进基类 Engine.update(),WebGLEngine 彻底不再 override update + 两个 docs-only;机制经逐链核对等价、pump-before-render 顺序未回退、e2e×4 全绿实证,approve 维持 gate 清空)
自我上轮 review(5aacc8551)以来 HEAD 线性推进 3 个 commit(merge-base --is-ancestor 5aacc8551 f1dc48713 = 祖先,非 force-push):
4889464b0 refactor: pump pending canvas resize in base Engine.update24df15f5f docs(core): drop the pump comment in Engine.updatef1dc48713 docs(rhi-webgl): simplify the canvas getter doc
git diff 5aacc8551 f1dc48713 --stat = 仅 3 文件:Canvas.ts(+1/-1)、Engine.ts(+2)、WebGLEngine.ts(−8)。WebCanvas.ts / WebGLGraphicDevice.ts 与已 approve 的 5aacc8551 逐字节相同(git diff 空)——历史全部闭环项在这两文件里,未回退。注意:4889464b0 是 refactor 前缀、动了基类帧循环,我逐链 trace(非信 commit message)。
4889464b0 逐链核对:pump 下沉进基类 Engine.update() —— 是消除「override 只为注入一行」的净收敛,机制严格等价
改动三处,逐一核对:
-
Canvas._pumpPendingResize():abstract→ 基类 no-op{}(Canvas.ts:62)。baseCanvas提供无害空实现,WebCanvas._pumpPendingResize()(WebCanvas.ts:95,本轮逐字节未动)仍是真实现的override。commit body 称「像_exitAutoResize一样」——核实属实:Canvas.ts:66正是protected _exitAutoResize(): void {}同款「基类 no-op + WebCanvas override」既有惯例,_pumpPendingResize现逐字对齐它。非新增泛化,是复用本文件既有模式。 -
Engine.update()顶部新增this._canvas._pumpPendingResize()(Engine.ts:325)。逐链核对顺序与 null 安全:- pump 是
update()第一条语句——在time._update()、所有 script/physics/animation 更新、以及(更下方)this._render(scenes)之前。故 resize 落 render 前、同帧,白屏 fix 顺序未回退(与旧WebGLEngine.update()里「pump 再 super.update()」严格等价)。 - null 安全:
_canvas在构造函数:252作为必填首参赋值(早于任何update()可能被调),且从构造到_destroy从不置 null →this._canvas._pumpPendingResize()恒安全。 - 多态派发正确:
WebGLEngine唯一Engine子类(git grep "extends Engine"全仓仅WebGLEngine.ts:8),_animate循环(:147/:152)调this.update()→ 基类update()→ 顶部 pump 多态派发到WebCanvas._pumpPendingResize()。行为与旧 override 逐字等价。
- pump 是
-
删掉
WebGLEngine.update()override(WebGLEngine.ts−6 行)。旧 override 体只有pump + super.update()(无其它逻辑,diff 坐实),pump 已下沉到基类 → override 现在是纯冗余,删除后WebGLEngine零update()override,更干净。测试无回归:本 delta 零测试文件改动,WebGLEngine.test.ts不调.update()(resize 测试直戳_pumpPendingResize/ observer 生命周期,均未变)。
判例意义:这条把「resize→render 同帧」的所有权从 WebGL 特定 override 收口进基类帧循环——它本就是基类帧生命周期保证(非 WebGL 独有),归属更正确;且消除了「override 只为注入一行 cross-cutting 调用」这个反模式(对应 skill「隐式委托 / override 只为一行」)。是净架构改进。
24df15f5f + f1dc48713 逐条核对(docs-only)
24df15f5f删 pump 行的注释(// Apply any pending canvas resize before the frame so resize and render land together)——4889464b0加 pump 时带的这条注释被这个 commit 删掉,留下裸this._canvas._pumpPendingResize();。见下 [P3]。f1dc48713简化 getter doc(WebGLEngine.ts:31):从「The canvas the engine renders to; call setResolution()/setAutoResolution() ...」→「The web canvas the engine renders to.」——合理。resolution API 文档挂在返回类型WebCanvas/Canvas上,getter 上重述属冗余。多行/** */块保留、描述带句尾句号,JSDoc 合规。
问题
[P3] packages/core/src/Engine.ts:325 — pump 行「为何在 render 前」的 rationale 现已无处留存(非阻塞)
24df15f5f 删掉 pump 行注释后,this._canvas._pumpPendingResize() 变成裸调用,而「必须在 render 前跑,让 resize 和 render 同帧、避免白屏」这个 why 现在全仓无处留存(git grep "white flash\|same frame\|before render\|land together\|for why" 除无关命中外为空)。这个 why 是 load-bearing 的:pump 调用的位置(update() 首行、_render 之前)本身是承载性事实,但裸行不告诉未来读者「为什么必须在这个位置」——一个后续「整理」帧循环顺序的人可能无意把它移到 render 之后而重引白屏。
与我历史 [P3] 的区别:此前那条 [P3] 是「删白屏 why 后 WebGLEngine.ts 留下 see Canvas._pumpPendingResize for why 悬挂交叉引用」——本轮 WebGLEngine.update() override 连同那句 for why 尾巴一起被删了,悬挂引用已消除、那条 [P3] 闭环。本条是新形态(rationale 彻底消失、无悬挂指针),同源但非同一条。非阻塞,纯 doc-hygiene,建议在 pump 行上补一句 why(如 // Pump before render so resize and render land in the same frame),或维持现状——不阻塞合并。
CI
tip f1dc48713:build×3(win/mac/ubuntu)/ codecov / codecov/project / e2e×4 / lint / labeler 全部 SUCCESS。e2e×4 全绿是关键实证——它跑真机 _animate → update → render 循环,坐实「pump 下沉基类后仍在 render 前落 resize」(白屏 fix 未回退),非仅静态核对。唯一 codecov/patch fail —— 本 delta 是 refactor(挪一行)+ docs 删除,无新增产物覆盖行,patch coverage 天然显示「无新增覆盖」,是覆盖率比值 artifact 非测试失败(codecov job 本身 pass、真 suite 全 pass、e2e×4 全绿),同历轮判定,不作为 finding。
已闭环清单(逐条对 tip 核对,未回退)
- [P0] 83+ e2e
resizeByClientSize迁移 / 8 单测 suite 崩溃 → 全仓源码/e2e/tests/examples 零resizeByClientSize(仅 docs 8 文件),e2e×4 全绿。关闭。 - [P1]
_cleanupAutoResize/_releaseCanvasthis-loss;lint red(infra 抖动,tip lint SUCCESS)。关闭。 - [P2] observe 注释撒谎/白屏;isOffscreenCanvas 收成 internal;DPR 调用时固化;scale 校验 throw;
devicePixelContentBoxSize精确设尺;enable/disable deprecated forwarder 删除;[P3] pending 标志/字段位置/注释句号。均关闭、未回退(WebCanvas.ts/WebGLGraphicDevice.ts逐字节未变,Canvas.ts/Engine.ts除 pump 下沉外机制原样)。 - [P3]
WebGLEngine.ts→Canvas._pumpPendingResize for why悬挂交叉引用 → 本轮WebGLEngine.update()override 连同for why尾巴一并删除,悬挂引用消除、闭环(新形态见上 [P3],同源非同条)。
仍开放(非阻塞,硬去重不重提展开)
- [P2] docs/README 未随 breaking change 更新(8 文件仍教已删的
resizeByClientSize())+ [P2] 基类Canvas抽象契约 breaking 建议标 CHANGELOG:本轮未触,仍开放,建议合并前顺手清。 - [P3]
WebCanvas.ts:110fallback 注释举 Safari 为例略过时(Safari 16.4+ 已支持devicePixelContentBoxSize):本轮WebCanvas.ts逐字节未动,原样延续,仅作记录。
结论
approve:本轮 3 个 commit——① 4889464b0 把 per-frame resize pump 从 WebGLEngine.update() override 下沉进基类 Engine.update() 首行(_pumpPendingResize 从 abstract 变基类 no-op、WebCanvas 仍 override,逐字对齐既有 _exitAutoResize 惯例),经逐链核对 pump-before-render 顺序严格等价、_canvas 构造期赋值 null 安全、WebGLEngine 唯一 Engine 子类多态派发正确、旧 override 体只有 pump+super 纯冗余删除、测试零改动无回归——是把帧生命周期所有权收口进基类的净架构改进;② 24df15f5f 删 pump 行注释(顺带消除我历史 [P3] 悬挂交叉引用,但白屏 why 现无处留存 = 新 [P3] 非阻塞);③ f1dc48713 getter doc 简化(resolution API 归返回类型,getter 上重述冗余)。WebCanvas.ts/WebGLGraphicDevice.ts 逐字节未变、历史全部闭环项未回退,CI 全绿(唯 codecov/patch 是 refactor+docs 的比值 artifact),e2e×4 实证白屏 fix 未回退。无 P0/P1/P2/新阻塞项。因 HEAD 已推过我上轮 approve 的 commit(reviewDecision = REVIEW_REQUIRED),在新 tip f1dc48713 提交 APPROVE 以维持 gate 清空。剩两条 P2(docs / CHANGELOG)+ 两条 P3(pump why / Safari 注释)均非阻塞,建议合并前顺手清。
Auto-resize stays on by default (create always calls setAutoResolution); apps wanting a fixed resolution call setResolution afterwards, which exits auto mode without a flicker. Removes an init-only opt-out that runtime setResolution already covers.
|
+1 |
@GuoLei1990 我根据pr改动修改了文档,需要commit在这吗? |
|
嗯嗯,可以的,是我遗漏了!PR 拉到最新帮忙适配下吧🙏 |
- Updated canvas resizing logic in docs from resizeByClientSize to setAutoResolution and setResolution - Aligned English and Chinese documentation for physics and platform modules - Removed deprecated method calls from README and example snippets - Enhanced clarity on device pixel ratio (DPR) handling and resolution strategies
@GuoLei1990 提交了,但严格来说要设置lint-staged的,不然直接提交报错 |
👌 |
GuoLei1990
left a comment
There was a problem hiding this comment.
复审 @ 90a8755382(线性追加 2 个 commit:一个 refactor 删掉本 PR 自引入的 autoResize create 选项 + 一个 docs 把 8 个文档/README 从已删的 resizeByClientSize 迁到 setAutoResolution/setResolution;前者经 Git 历史透镜坐实非 breaking、无残留消费者、无闪烁,后者逐条打开代码验非撒谎——闭环我历史长期 [P2] docs finding,approve 维持 gate 清空)
自我上轮 review(f1dc48713)以来 HEAD 线性推进 2 个 commit(compare f1dc48713...90a8755382 = status: ahead / ahead_by: 2 / behind_by: 0,非 force-push;父链 90a8755382→724dd3cee→f1dc48713):
724dd3cee refactor(rhi-webgl): drop the autoResize create option90a8755382 chore(docs): migrate canvas resizing APIs and update guides
724dd3cee 只动 WebGLEngine.ts(+1/-5)+ WebGLEngine.test.ts(+1/-9);90a8755382 只动 8 个 docs/README(docs/{en,zh}/core/canvas.mdx 各 +56/-47、platform.mdx/raycast.mdx 各 ±1、2 个 README ±1)。核心产物 WebCanvas.ts/Canvas.ts/WebGLGraphicDevice.ts/Engine.ts 本轮零改动——历史全部闭环项在这些文件里,未回退。
724dd3cee 逐链核对:删 autoResize create 选项 —— 非 breaking、无残留消费者、无闪烁,是净简化
commit body 称「auto-resize 默认常开(create 恒调 setAutoResolution);要固定分辨率的 app 之后调 setResolution 退出 auto、无闪烁;删掉一个 runtime setResolution 已覆盖的 init-only opt-out」。逐条用 Git 历史透镜 + 代码 trace 核实:
- 非 breaking 删除(关键,同
9df47a601判例):拉 dev/2.0(PR base)的WebGLEngine.ts原文——WebGLEngineConfiguration上根本没有autoResize字段(连setAutoResolution/resizeByClientSize/_releaseCanvas都没有,整套 auto-resize machinery 全是本 PR 引入的)。删一个本 PR 自引入、从未发版的 create 选项,不是回退任何已发布 API,是消除本 PR 自己加的 init-only opt-out。非静默回退(commit body 诚实记录依据,能力由 runtimesetResolution覆盖)。 - 零残留消费者:
git grep autoResize HEAD(本地 worktree HEAD =724dd3cee)= 空;PR head tree 同(docs commit 不重引autoResize)。删除后无一处 dangling reference。 - 删的测试是配套 revert-safe 清理:删掉的正是
autoResize false disables the default follow用例——它测的是现已删除的{ canvas, autoResize: false }路径,随代码一并删除正确(为已删代码留测试才是债)。其余canvas auto resize全链路测试(默认建 observer / 重入复用 observer+更新 scale / 帧 pumpMath.roundresize / 0×0 skip 保留 pending /setResolution退出 auto+非法值 throw /setAutoResolution非法 scale throw / destroy 释放 observer)原样保留、反向证伪机制完整;注释autoResize defaults to true→create enters auto mode by default准确。 - 「无闪烁」claim 成立(trace
setAutoResolution→observer 初始回调→_pumpPendingResize→_exitAutoResize):新替代路径 =create({ canvas })建 observer +observe()(初始回调异步只置_pendingResize=true),app 随后同步调setResolution(w,h)→_exitAutoResize()在任何帧 pump 之前清_pendingResize=false+ 断 observer → 无 auto-resize 帧;即便某帧 pump 抢先,也是_pumpPendingResize与 render 同帧(本 PR 早已建立的机制),无白屏。唯一代价 = app 立即setResolution时一次多余的 observer attach+detach,可忽略。
判例意义:这条与 9df47a601(删 deprecated forwarder)同源——本 PR 在发版前继续收敛自己尚未发布的 API 表面,减一个 config 旋钮 = 少一份用户认知 + 少一条代码分支,是净简化。
90a8755382 逐条核对(docs-only):闭环我历史长期 [P2] 「docs 教已删的 resizeByClientSize」
canvas.mdx(en+zh 各 +56/-47)重写适配章节,逐条打开代码验证非 aspirational(撒谎):
- 「默认自动跟随显示尺寸」←
create恒调setAutoResolution()✓ setAutoResolution(scale=1)/(0.7)/(2)←override setAutoResolution(scale: number = 1)+ scale>0 守卫 ✓- 「
setAutoResolution覆盖之前的setResolution」← re-observe / 新建 observer 分支 ✓ setResolution(1920,1080)锁定固定分辨率、退出 auto ←Canvas.setResolution→_exitAutoResize()+_setSize✓- 「
width/height只读」← read-only getter ✓ - mermaid「
ResizeObserver→devicePixelContentBoxSize→×scale;fallbackclientWidth×devicePixelRatio」←_pumpPendingResizeexact-device-pixel 路径 + Safari fallback 逐字对齐 ✓ - 「resize deferred 到下一帧
engine.update()防撕裂」← pump-before-render 机制 ✓ - Fixed Width 例改用
setResolution(fixedWidth, Math.round(innerHeight/scale))+setScale,Math.round是好卫生(正值不 throw)✓ - zh 版是连贯中文翻译,非英文残留 copy-paste ✓
platform.mdx(en+zh)DPR Mode 表格 cell 从「via resizeByClientSize」改「via setAutoResolution」,Auto/Fixed 描述随新模型对齐;raycast.mdx/README/galacean-README 删掉已删的 engine.canvas.resizeByClientSize() 行、注释改「auto-resizes by default」,无 dangling 句子。
逐文件核对 PR head(90a8755382)8 个文件的 resizeByClientSize 残留计数 = 全 0。我历史长期 [P2]「docs/README 未随 breaking change 更新」闭环。
CI
tip 90a8755382:build×3(win/mac/ubuntu)/ codecov / codecov/project / e2e×4 / lint / labeler 全部 SUCCESS。e2e×4 全绿是关键实证——724dd3cee 改了 create 期 auto-resize 路径(现无条件 setAutoResolution()),e2e 跑真机 create → run → update → render 循环,坐实默认常开 auto-resize 不回归。唯一 codecov/patch fail = refactor 删行 + docs、无新增产物覆盖行的比值 artifact(codecov job 本身 pass、真 suite 全 pass、e2e×4 全绿),同历轮判定,不作为 finding。
已闭环清单(逐条对 tip 核对,未回退)
- [P0] 83+ e2e
resizeByClientSize迁移 / 8 单测 suite 崩溃 → e2e×4 全绿。关闭。 - [P1]
_cleanupAutoResize/_releaseCanvasthis-loss;lint red → lint SUCCESS。关闭。 - [P2] observe 注释撒谎/白屏;isOffscreenCanvas 收成 internal;DPR 调用时固化;scale 校验 throw;
devicePixelContentBoxSize精确设尺;enable/disable deprecated forwarder 删除;autoResizecreate 选项删除;[P3] pending 标志/字段位置/注释句号/_pumpPendingResize悬挂引用。均关闭、未回退(WebCanvas.ts/Canvas.ts/WebGLGraphicDevice.ts/Engine.ts本轮零改动)。 - [P2] docs/README 未随 breaking change 更新(8 文件教已删的
resizeByClientSize)→ 本轮90a8755382全部迁移,8 文件resizeByClientSize残留 = 0。闭环。
仍开放(非阻塞,硬去重不重提展开)
- [P2] 基类
Canvas抽象契约 breaking 建议标 CHANGELOG(BREAKING CHANGE已入 commit body):本轮未触,仍开放,建议合并前顺手补。 - [P3]
WebCanvas.ts:110fallback 注释举 Safari 为例略过时(Safari 16.4+ 已支持devicePixelContentBoxSize):本轮WebCanvas.ts逐字节未动,原样延续,仅作记录。
结论
approve:本轮 2 个 commit——① 724dd3cee 删掉本 PR 自引入、从未发版的 autoResize create 选项(Git 历史透镜坐实 dev/2.0 base 上 WebGLEngineConfiguration 零 autoResize = 非 breaking、非静默回退),全仓 git grep autoResize = 空零残留消费者,删的测试是测已删代码的配套清理(revert-safe),「无闪烁」claim 经 trace 成立(setResolution→_exitAutoResize 在帧 pump 前清 pending),是减一个 config 旋钮的净简化;② 90a8755382 把 8 个 docs/README 从已删的 resizeByClientSize 迁到 setAutoResolution/setResolution,逐条打开代码验所有 claim 非 aspirational、8 文件残留计数全 0、zh 版连贯——闭环我历史长期 [P2] docs finding。核心产物文件本轮零改动、历史全部闭环项未回退,CI 全绿(唯 codecov/patch 是比值 artifact),e2e×4 实证 auto-resize 默认常开不回归。无 P0/P1/P2/新阻塞项。因 HEAD 已推过我上轮 approve 的 commit(reviewDecision = REVIEW_REQUIRED),在新 tip 90a8755382 提交 APPROVE 以维持 gate 清空。剩一条 P2(CHANGELOG)+ 一条 P3(Safari 注释)均非阻塞,建议合并前顺手清。




概述
一次 canvas 分辨率 API 的易用性增强:把 auto-resize 能力归位到
WebCanvas,让渲染分辨率默认自动跟随显示尺寸——大多数应用create({ canvas })之后不用写任何 resize 代码。主要改动
1. 归属:auto-resize 全部归
WebCanvas,pump 下沉到基类observer 观察 canvas、算 canvas 尺寸、改 canvas 分辨率——概念上 100% 属于 canvas。原本放 engine 是因为白屏修复需要帧时序(实现依赖绑架了归属)。现在:
WebCanvasEngine.update()(this._canvas._pumpPendingResize()),_pumpPendingResize是基类Canvas的默认 no-op、WebCanvasoverride——WebGLEngine不再需要 overrideupdate()2. 默认自动跟随显示尺寸(零配置)
ResizeObserver监听 canvas 元素本身 → 接住窗口、CSS、flex/容器、侧边栏/面板等所有来源的尺寸变化(window.resize只能接住窗口变化)3. 两个分辨率入口
scale的 base 是物理分辨率:1= 满物理分辨率(原生清晰),0.7= 降 30% 提性能,2= 超采样。device pixel ratio 自动吸收,用户无需理解 DPRcreate({ canvas })后调setResolution(w, h)(退出自动,无闪烁)4.
width/height改只读分辨率是原子的整体:单独设
width不动height会产生畸变中间态 + 两次 buffer 重建,且裸写canvas.width会让引擎缓存尺寸与真实 buffer 分叉。改只读后,所有分辨率变更走setResolution一个原子入口。width/height是"画布分辨率"的分量。5. 分数 DPR 精度:
devicePixelContentBoxSizeclientWidth * dpr在分数 DPR(Windows 1.25/1.5)下有 ≤1px 舍入误差。改用ResizeObserverentry 的devicePixelContentBoxSize(精确物理像素)消除它,Safari 等不支持的浏览器自动降级回clientWidth * dpr(特性检测,无人退化)。four 大 web3D 引擎(three.js/Babylon/Pixi/PlayCanvas)均未采用此特性。6. 删
resizeByClientSize被默认自动 +
setAutoResolution覆盖。Breaking Changes
major 版本级:
canvas.width/canvas.height改为只读(用setResolution设分辨率)resizeByClientSizecreate后调setResolution)设计参照
create({...})配置模式对齐 three.js / Babylon(web 引擎标准)scale(物理分辨率之上的性能倍数)对齐 UE ScreenPercentagesetResolution命名对齐 Unity / UE(SetResolution/SetScreenResolution)验证
devicePixelContentBoxSize精确路径 + 首帧时序 jsdom 不忠实复现,靠规范 + 代码审查(建议真机验一次)供讨论。 @luo2430 有不同意见或更好想法请直接回复。