Skip to content

refactor(rhi-webgl): rework canvas resolution API, follow display size by default#3037

Merged
GuoLei1990 merged 60 commits into
galacean:dev/2.0from
luo2430:dev/2.0
Jul 13, 2026
Merged

refactor(rhi-webgl): rework canvas resolution API, follow display size by default#3037
GuoLei1990 merged 60 commits into
galacean:dev/2.0from
luo2430:dev/2.0

Conversation

@luo2430

@luo2430 luo2430 commented Jun 17, 2026

Copy link
Copy Markdown

概述

一次 canvas 分辨率 API 的易用性增强:把 auto-resize 能力归位到 WebCanvas,让渲染分辨率默认自动跟随显示尺寸——大多数应用 create({ canvas }) 之后不用写任何 resize 代码。

原始需求是给 WebGLEngine 加 auto-resize opt-in(@luo2430)。经过讨论从第一性演进成这套重构,方向不变、形态更彻底。

主要改动

1. 归属:auto-resize 全部归 WebCanvas,pump 下沉到基类

observer 观察 canvas、算 canvas 尺寸、改 canvas 分辨率——概念上 100% 属于 canvas。原本放 engine 是因为白屏修复需要帧时序(实现依赖绑架了归属)。现在:

  • auto-resize 逻辑全在 WebCanvas
  • 每帧的 resize pump 下沉到基类 Engine.update()this._canvas._pumpPendingResize()),_pumpPendingResize 是基类 Canvas 的默认 no-op、WebCanvas override——WebGLEngine 不再需要 override update()

2. 默认自动跟随显示尺寸(零配置)

// 之前:用户要显式调 + 自己挂 window.resize 监听
engine.canvas.resizeByClientSize();

// 现在:create 后什么都不用写
WebGLEngine.create({ canvas });
  • ResizeObserver 监听 canvas 元素本身 → 接住窗口、CSS、flex/容器、侧边栏/面板等所有来源的尺寸变化(window.resize 只能接住窗口变化)
  • 0×0 安全:resize 延迟到帧循环,canvas 无布局尺寸时跳过——未挂载的 canvas 永不被设成 0×0 buffer
  • example 中的显式 resize 调用相应删除

3. 两个分辨率入口

engine.canvas.setAutoResolution(scale?)    // 自动:跟随显示尺寸
engine.canvas.setResolution(width, height) // 固定:锁死绝对分辨率(隐式退出自动)
  • scalebase 是物理分辨率1 = 满物理分辨率(原生清晰),0.7 = 降 30% 提性能,2 = 超采样。device pixel ratio 自动吸收,用户无需理解 DPR
  • 想固定分辨率:create({ canvas }) 后调 setResolution(w, h)(退出自动,无闪烁)

4. width / height 改只读

分辨率是原子的整体:单独设 width 不动 height 会产生畸变中间态 + 两次 buffer 重建,且裸写 canvas.width 会让引擎缓存尺寸与真实 buffer 分叉。改只读后,所有分辨率变更走 setResolution 一个原子入口。width/height 是"画布分辨率"的分量。

5. 分数 DPR 精度:devicePixelContentBoxSize

clientWidth * dpr 在分数 DPR(Windows 1.25/1.5)下有 ≤1px 舍入误差。改用 ResizeObserver entry 的 devicePixelContentBoxSize(精确物理像素)消除它,Safari 等不支持的浏览器自动降级回 clientWidth * dpr(特性检测,无人退化)。four 大 web3D 引擎(three.js/Babylon/Pixi/PlayCanvas)均未采用此特性。

6. 删 resizeByClientSize

被默认自动 + setAutoResolution 覆盖。

Breaking Changes

major 版本级

  • canvas.width / canvas.height 改为只读(用 setResolution 设分辨率)
  • 移除 resizeByClientSize
  • auto-resize 默认开启(想要固定分辨率:create 后调 setResolution

设计参照

  • create({...}) 配置模式对齐 three.js / Babylon(web 引擎标准)
  • 默认自动跟随对齐 Unity / UE 的"默认就跟随"心智
  • scale(物理分辨率之上的性能倍数)对齐 UE ScreenPercentage
  • setResolution 命名对齐 Unity / UE(SetResolution / SetScreenResolution

验证

  • tsc 全绿(core + rhi-webgl)
  • 全量单测全过、e2e ×4 全绿
  • devicePixelContentBoxSize 精确路径 + 首帧时序 jsdom 不忠实复现,靠规范 + 代码审查(建议真机验一次)

供讨论。 @luo2430 有不同意见或更好想法请直接回复。

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

WebCanvas gains an isOffscreenCanvas() method that centralizes the repeated inline OffscreenCanvas type checks; three existing guards in WebCanvas and two in WebGLGraphicDevice are updated to use it. WebGLEngine receives enableAutoResize(pixelRatio?) and disableAutoResize() methods backed by a ResizeObserver with EngineEventType.Shutdown cleanup. New tests cover both isOffscreenCanvas() and the full auto-resize lifecycle.

Changes

OffscreenCanvas detection and auto-resize

Layer / File(s) Summary
WebCanvas.isOffscreenCanvas() method and usage refactor
packages/rhi-webgl/src/WebCanvas.ts, tests/src/rhi-webgl/WebCanvas.test.ts
Adds isOffscreenCanvas(): boolean to WebCanvas and replaces three inline typeof OffscreenCanvas/instanceof guards (in scale getter, scale setter, and resizeByClientSize) with calls to the new method. A new test suite verifies the return value for HTMLCanvasElement, OffscreenCanvas, and post-construction field replacements.
WebGLGraphicDevice context fallback updated
packages/rhi-webgl/src/WebGLGraphicDevice.ts
Updates WebGLGraphicDevice.init to allow the WebGL2 gl variable to be undefined and replaces both experimental-webgl2 and experimental-webgl fallback gating with (canvas as WebCanvas).isOffscreenCanvas().
WebGLEngine enableAutoResize / disableAutoResize
packages/rhi-webgl/src/WebGLEngine.ts, tests/src/rhi-webgl/WebGLEngine.test.ts, tests/vitest.config.ts
Adds EngineEventType import, a private _resizeObserver field and static cleanup helper, and public enableAutoResize(pixelRatio?) / disableAutoResize() methods. enableAutoResize skips OffscreenCanvas, disconnects any prior observer, registers a new ResizeObserver calling canvas.resizeByClientSize, and registers a Shutdown listener. Tests verify listener counts, observer lifecycle and replacement, resizeByClientSize invocation after style mutation, and cleanup on engine.destroy(). screenshotFailures is set to false in the Vitest Playwright config.

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()
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Poem

🐰 A canvas that knows what it is — how grand!
No more typeof checks scattered by hand.
isOffscreenCanvas() speaks for the whole,
ResizeObserver watches with vigilant soul.
When shutdown arrives, it tidies the nest,
A well-behaved bunny — clean-up's the best! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately reflects the main change: canvas auto-resizing and display-size-based resolution behavior.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@luo2430 luo2430 marked this pull request as draft June 17, 2026 05:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5d74c1d and 887ac8c.

📒 Files selected for processing (3)
  • packages/core/src/input/InputManager.ts
  • packages/rhi-webgl/src/WebGLEngine.ts
  • tests/src/rhi-webgl/WebGLEngine.test.ts

Comment thread tests/src/rhi-webgl/WebGLEngine.test.ts Outdated
@luo2430 luo2430 marked this pull request as ready for review June 17, 2026 05:59
GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

@luo2430

luo2430 commented Jun 17, 2026

Copy link
Copy Markdown
Author

@GuoLei1990

this指针问题我考虑到了,但不知为何代码经过修改后我的本地测试可以跑通。

image

我会再去处理这个问题,原本以为已经修复了。

GuoLei1990

This comment was marked as outdated.

@cptbtptpbcptdtptp

Copy link
Copy Markdown
Collaborator

这里有一个时序问题需要提一下,根据 W3C 规范,浏览器每帧的渲染管线顺序是:

 Input Events (包括 window resize event) → rAF callbacks(引擎逻辑) → Style/Layout → ResizeObserver callbacks(本次 PR 添加) → Paint

在发生 resize 那帧,引擎绘制 rAF 时使用的还是旧的画布尺寸,随后 ResizeObserver 回调修改画布的尺寸会清空 canvas buffer ,表现就是这一帧会白,但 window 监听 resize 也不可以直接用,因为 window.resize 仅监听浏览器窗口大小变化,所以其他引擎要么完全手动触发,要么轮询检测,避免本帧 resize 与渲染不同步。

@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.28244% with 70 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.81%. Comparing base (75af66f) to head (4290fa6).
⚠️ Report is 1 commits behind head on dev/2.0.

Files with missing lines Patch % Lines
e2e/case/text-typed.ts 0.00% 11 Missing ⚠️
...cleRenderer-emit-mesh-cone-scale-rotation-world.ts 0.00% 6 Missing ⚠️
.../particleRenderer-emit-mesh-cone-scale-rotation.ts 0.00% 5 Missing ⚠️
...rer-emit-mesh-cone-scale-rotation-life-seperate.ts 0.00% 3 Missing ⚠️
...icleRenderer-emit-mesh-cone-scale-rotation-life.ts 0.00% 3 Missing ⚠️
examples/src/paricle-emit-mesh.ts 0.00% 3 Missing ⚠️
.../case/particleRenderer-emit-billboard-stretched.ts 0.00% 2 Missing ⚠️
packages/rhi-webgl/src/WebCanvas.ts 98.27% 2 Missing ⚠️
e2e/case/animator-additive.ts 0.00% 1 Missing ⚠️
e2e/case/animator-blendShape.ts 0.00% 1 Missing ⚠️
... and 33 more
Additional details and impacted files
@@             Coverage Diff             @@
##           dev/2.0    #3037      +/-   ##
===========================================
+ Coverage    79.52%   79.81%   +0.28%     
===========================================
  Files          904      904              
  Lines       101168   101285     +117     
  Branches     11377    11405      +28     
===========================================
+ Hits         80456    80838     +382     
+ Misses       20528    20265     -263     
+ Partials       184      182       -2     
Flag Coverage Δ
unittests 79.81% <73.28%> (+0.28%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@luo2430 luo2430 changed the title feat(rhi-webgl): add auto-resize support via ResizeObserver and refactor input canvas naming refactor(rhi-webgl): extract isOffscreenCanvas helper | feat(rhi-webgl): add WebGLEngine auto-resize support via ResizeObserver | chore(vitest): disable screenshotFailures to avoid taking blank screenshots Jun 18, 2026
@luo2430

luo2430 commented Jun 18, 2026

Copy link
Copy Markdown
Author

@GuoLei1990

代码处理完毕,vitest问题我会去开一个issue,有部分解决方案和提议,但需要验证。另外,isOffscreenCanvas中的typeof OffscreenCanvas !== "undefined"是不是可以直接提成一个全局常量或者静态常量,但项目中似乎没有这种先例,这符合项目规范吗?

GuoLei1990

This comment was marked as outdated.

@luo2430

luo2430 commented Jun 18, 2026

Copy link
Copy Markdown
Author

@GuoLei1990

注释已修改。
可以确定的是isOffscreenCanvas会被设定为一个public api,因为有可以跨包使用的地方。但为了这一处使用而添加一个新的工作区依赖我觉得不值得。

image

GuoLei1990

This comment was marked as outdated.

@luo2430

luo2430 commented Jun 18, 2026

Copy link
Copy Markdown
Author

@cptbtptpbcptdtptp

GuoLei1990

This comment was marked as outdated.

@luo2430

luo2430 commented Jun 21, 2026

Copy link
Copy Markdown
Author

@cptbtptpbcptdtptp 这里有好康的

@cptbtptpbcptdtptp

Copy link
Copy Markdown
Collaborator

@cptbtptpbcptdtptp 这里有好康的

这个是当前 PR 在真实 resize 时的表现
resize

期望应该是这样的(时序导致)
resize1

@luo2430

luo2430 commented Jun 21, 2026

Copy link
Copy Markdown
Author

@cptbtptpbcptdtptp 这个示例能分享一下吗?我用于测试。

@cptbtptpbcptdtptp

Copy link
Copy Markdown
Collaborator

@cptbtptpbcptdtptp 这个示例能分享一下吗?我用于测试。

examples/src/device-restore.ts 这个,设置 enableAutoResize 就可以。

@luo2430

luo2430 commented Jun 21, 2026

Copy link
Copy Markdown
Author

@cptbtptpbcptdtptp 收到

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jun 22, 2026
GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

@GuoLei1990

Copy link
Copy Markdown
Member

本轮改动总结(e014976dfe25286149

围绕 auto-resolution 的收尾优化,含一处 P0 崩溃修复。CI:build ×3 / e2e ×4 全绿(覆盖率补丁门槛除外)。

🔴 P0 修复:Safari 上 create() 崩溃 — e25286149

@GuoLei1990 你的 P0 结论正确observe({ box: "device-pixel-content-box" }) 缺保护确实会让引擎在部分浏览器 create() 崩溃白屏。经查证(WebIDL 规范 §3.2.18 + MDN browser-compat-data)补充一点:真实崩溃窗口是 Safari 15.4+(当前所有 Safari)和 Firefox 69–92,而非 Chrome 64–83——后者的 box 选项参数与 device-pixel-content-box 枚举值是 Chrome 84 同批落地的,之前没有 box 参数,传入属"未知 dictionary 成员"被静默忽略(不抛)。真正会抛的是"已把 box 声明为 ResizeObserverBoxOptions 枚举、但枚举缺 device-pixel-content-box 值"的浏览器:Safari(有 content-box/border-box,至今无 dpcb)、Firefox 69–92。

修法(特性检测,非 try/catch):观察前先 "devicePixelContentBoxSize" in ResizeObserverEntry.prototype 检测(web.dev 官方锚点),支持才传 box 选项,否则观察普通 content-box。从源头避免抛异常,而非抛出后 catch。已在真实 chromium 验证:合法值不抛、非法枚举值确实抛(坐实 Safari 失败模式)。

  • 支持 dpcb(Chrome):行为不变,精确设备像素
  • Safari 15.4+ / FF 69–92:不再崩溃,降级为 clientWidth × dpr sizing(整数 dpr 零损失,小数 dpr ≤1px 误差),ResizeObserver 仍在,持续跟随显示尺寸
  • 无 ResizeObserver(小程序 / iOS Safari <13.4):一次性 sizing

🧹 API 清理:删除 WebCanvas.scale / setScale85ee362f1

scale 访问器(Vector2)语义与 setAutoResolution(scale) 冲突(三处 scale 含义互斥:显示/buffer 比值、CSS transform 拉伸、dpr 倍率),且 getter/setter 不对称。全仓零消费者。对比 Unity / Unreal / Godot:三家都只有单标量 render-scale(≈ setAutoResolution)+ 显式分辨率(≈ setResolution),均无二维显示层拉伸的引擎 API(那属 CSS / OS 窗口)。删除使 Galacean 与业界对齐。文档同步更新。

🔤 内部命名对齐 resolution — b128f70c6

概念已统一到 resolution,同步内部方法名(仅 Canvas / WebCanvas,不触碰物理/UI 同名方法):

  • _setSize_setResolution
  • _onSizeChanged_onResolutionChanged
  • _setSizeByClientSizeFallback_setResolutionByClientSizeFallback

✅ 测试 & 其他

  • 77f5571b0 新增 5 个用例覆盖 device-pixel / 反馈环退出 / 无 RO 降级 / 0×0 skip / Offscreen no-op
  • f6d503234 精简反馈环注释与 warning 文案
  • ee3a9187f merge 最新 dev/2.0;8383c3b4f 修其中 orbital 用例调用的已删除 API resizeByClientSize(e2e 转绿)

@GuoLei1990 GuoLei1990 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审 @ e25286149fa5(HEAD 在我上一条 review 提交瞬间又线性推进 1 个 commit:e2528614 fix: feature-detect device-pixel-content-box before observing to avoid throwing on Safari——这个 commit 正是修复我历轮 flag 的 [P0]〔删掉的 dpcb try/catch → 改用 feature-detection 门控,比 try/catch 更干净〕,P0 闭环。但我上一轮同时 flag 的 [P1]〔rename 漏改测试文件〕仍未修WebGLEngine.test.ts:186/233 仍引用旧名 _setSize/_setSizeByClientSizeFallback,2 个单测确定性抛 TypeError,codecov〔vitest〕job 仍 FAILURE。gate 保持,--request-changes 重锚到 e25286149fa5。)

时序校准(我上一条 CHANGES_REQUESTED 落到了这个我尚未审的 commit 上)

我上一条 review 审的是 b128f70c〔rename commit,flag P1 测试漏改 + 重申 P0〕。提交 review 的瞬间 HEAD 已推进到 e25286149fa5——compare b128f70c...e2528614 = ahead_by: 1

  • e25286149fa5 fix: feature-detect device-pixel-content-box before observing to avoid throwing on Safari〔仅 WebCanvas.ts,+15/-3〕

GitHub 把我的 CHANGES_REQUESTED 记到了 e2528614——但我尚未审它。本轮从 b128f70c 审这一 delta〔且因它触碰我 flag 的 P0 边界,我逐链 trace + 拉 CI annotation〕。上一条已 minimize。

我上轮 [P0] 直接闭环——feature-detection 门控替代 try/catch,更优

e2528614 逐字核对,正是修复我历轮反复 flag 的 P0〔WebCanvas.ts:45 删掉 observe({box:"device-pixel-content-box"}) 外 try/catch,不支持 dpcb 的浏览器上 WebIDL enum 抛 TypeError → create() 崩溃〕:

+  private static _devicePixelContentBoxSupported?: boolean;
+  private static _supportsDevicePixelContentBox(): boolean {
+    // Passing { box: "device-pixel-content-box" } to observe() throws on browsers whose
+    // ResizeObserverBoxOptions enum lacks that value (Safari, Firefox < 93), so callers must gate on this
+    return (this._devicePixelContentBoxSupported ??=
+      typeof ResizeObserverEntry !== "undefined" && "devicePixelContentBoxSize" in ResizeObserverEntry.prototype);
+  }
   ...
-      // Browsers without device-pixel-content-box ignore the box option and fall back to content-box, ...
-      this._resizeObserver.observe(this._webCanvas, { box: "device-pixel-content-box" });
+      // Observe exact device pixels where supported, otherwise the content-box and size from the client size
+      if (WebCanvas._supportsDevicePixelContentBox()) {
+        this._resizeObserver.observe(this._webCanvas, { box: "device-pixel-content-box" });
+      } else {
+        this._resizeObserver.observe(this._webCanvas);
+      }

逐点核对,正确且优于我建议的 try/catch

  • feature-detection 是标准 dpcb 检测惯例"devicePixelContentBoxSize" in ResizeObserverEntry.prototype——支持 device-pixel-content-box box 值的浏览器与在 entry 上暴露 devicePixelContentBoxSize同批次 ship 的〔同一规范增量〕,用后者探测前者是社区公认写法。typeof ResizeObserverEntry !== "undefined" 独立守卫引用〔虽已在 ResizeObserver 存在分支内,仍单独 guard,稳健〕。??= 记忆化避免每次 setAutoResolution 重复探测。
  • 不再抛异常:门控命中 → observe({box:"dpcb"});不支持 → observe(this._webCanvas)〔纯 content-box〕。根本消除了 TypeError 抛出路径,比我建议的 try/catch 更干净〔无异常成本、无 catch〕。pump 的 device-pixel 分支读 devicePixelContentBoxSize?.[0]→undefined→client-size 在纯 content-box 下正确降级。
  • 注释改如实:新注释「throws on browsers whose ResizeObserverBoxOptions enum lacks that value (Safari, Firefox < 93)」——与 WebIDL 规范一致,替换了旧的 aspirational lie「browsers ignore the box option」。
  • build×3 + e2e 4/4 全绿坐实。

[P0] 闭环。 我此前坚持的诉求〔别让不支持 dpcb 的浏览器崩溃 + 注释改如实〕两条都满足,且落地方式更优。

我上轮 [P1] 未闭环——本 commit 只碰 WebCanvas.ts,测试文件旧名仍在

[P1](重申,e2528614 未触及)tests/src/rhi-webgl/WebGLEngine.test.ts:186,233 — rename 漏改测试文件,2 个单测确定性抛 TypeError,codecov〔vitest〕job FAILURE

b128f70c rename _setSize/_setSizeByClientSizeFallback_setResolution* 时漏改测试文件,e2528614 只修 P0〔仅 WebCanvas.ts〕,未碰测试。逐字核对 tip e25286149fa5WebGLEngine.test.ts〔fetch 独立复核〕:

// :181  注释仍写 _setSize
// :186  const originalSetSize = (webCanvas as any)._setSize.bind(webCanvas);  ← _setSize 已 undefined
// :233  (webCanvas as any)._setSizeByClientSizeFallback(1);                    ← 已 undefined

CI annotation 逐字坐实e25286149fa5codecov check-run id 86780018739,仍 FAILURE〕:

TypeError: Cannot read properties of undefined (reading 'bind')
 ❯ src/rhi-webgl/WebGLEngine.test.ts:186:56
TypeError: webCanvas._setSizeByClientSizeFallback is not a function
 ❯ src/rhi-webgl/WebGLEngine.test.ts:233:23
  • :186 — 测试「disables itself on a layout feedback loop」〔我 77f5571b 轮 approve 的测试 ②〕:_setSize 已改名 _setResolutionundefined.bind()TypeError
  • :233 — 测试「client-size fallback skips a canvas with no layout size」〔测试 ④〕:_setSizeByClientSizeFallback 已改名 _setResolutionByClientSizeFallbackis not a function

回归网确认:b128f70c 的父 commit 8383c3b4 codecov = SUCCESS——回归唯一由 rename 引入。codecov job 跑 vitest,本体 FAILURE + annotation 含 TypeError = 真单测失败,非「覆盖率比值 artifact」〔后者仅 codecov/patch〕。e2e 全绿因这两个是白盒 vitest 单测、只在 codecov job 跑。

修复:测试文件三处旧名同步改名〔:186/:187 _setSize_setResolution、:233 _setSizeByClientSizeFallback_setResolutionByClientSizeFallback、:181 注释同步〕:

    const originalSetResolution = (webCanvas as any)._setResolution.bind(webCanvas);
    (webCanvas as any)._setResolution = (w: number, h: number) => { ... };
    ...
    (webCanvas as any)._setResolutionByClientSizeFallback(1);

改完 codecov〔vitest〕转绿即闭环。审查动作:rename PR 每次必须全仓 grep 旧符号名〔含 tests/〕——production 改名后编译器抓不到测试里的 as any cast 引用,运行时才崩。这是本 PR 第二次同型 miss〔前一次 7dc5112〕。

已闭环清单(历史全部项,逐条对 tip e25286149fa5 核对,未回退)

  • [P0 我历轮] 删掉 dpcb observe 外 try/catch → 不支持 dpcb 的浏览器崩溃e2528614 改用 _supportsDevicePixelContentBox() feature-detection 门控 + 注释改如实。本轮闭环,且落地优于 try/catch。
  • [P0 我更早轮] _pumpPendingResize 漏改名28f639171 修复,_pumpPendingResolution 在,未回退。闭环。
  • [P0 更早轮] TS4113 跨包 build 破坏 → build×3 SUCCESS。闭环。
  • [P1 by cptbtptpbcptdtptp] 小程序 ResizeObserver ReferenceErrortypeof ResizeObserver 守卫在。闭环。
  • [P1 by cptbtptpbcptdtptp] 无 CSS 反馈环 → 检测 + 测试 ②〔但被 rename 打穿=本轮 P1〕。
  • [P1 by cptbtptpbcptdtptp] content-box 接不住 DPR-onlydevice-pixel-content-box 门控在〔现由 feature-detection 保护,不再崩〕。闭环。
  • [P2 by cptbtptpbcptdtptp] setResolution 不整数化 / @internal d.ts 泄漏Math.round / /** @internal */ 在。闭环。
  • [API] scale deprecation85ee362f 删除。闭环。
  • [P3 我更早轮] 反馈环 + RO 降级无 CI 测试 → 测试 ②③ 补上——但被 rename 漏改把 ②④ 打穿=本轮 P1
  • [naming] _setSize*_setResolution* productionb128f70c 落地正确〔_onResolutionChanged override 保留正确、build×3/e2e 坐实〕,唯测试文件漏改=本轮 P1

CI 说明

tip e25286149fa5build×3 SUCCESS、e2e 1,2,3,4/4 全 SUCCESS、lint SUCCESScodecov〔vitest〕= FAILURE〔annotation 含两处 TypeError,见 P1,非 codecov/patch 比值 artifact〕。

结论

request-changes:HEAD 在我上条 review 提交瞬间线性推进到 e25286149fa5fix: feature-detect device-pixel-content-box〕。这个 commit 正确闭环了我历轮反复 flag 的 [P0]——用 _supportsDevicePixelContentBox()"devicePixelContentBoxSize" in ResizeObserverEntry.prototype,标准 dpcb 探测惯例,??= 记忆化〕门控 observe 分支,不支持 dpcb 的浏览器走纯 content-box observe(webCanvas)根本消除 TypeError 抛出路径,比我建议的 try/catch 更干净;注释也改成如实描述「throws on browsers whose enum lacks that value」,替换了 aspirational lie。P0 闭环,落地优于 try/catch。但我上一轮同时 flag 的 [P1] 仍未修b128f70c rename _setSize*_setResolution* 时漏改测试文件 WebGLEngine.test.tse2528614 只碰 WebCanvas.ts 未碰测试 → :186 _setSize.bindundefined.bind → TypeError、:233 _setSizeByClientSizeFallback → is not a function,2 个单测〔恰是我 77f5571b 轮 approve 的测试 ②④〕确定性崩,codecov〔vitest〕job 仍 FAILURE〔annotation 逐字坐实,父 commit 8383c3b4 SUCCESS = 回归唯一由 rename 引入〕。修复=测试文件三处旧名同步改名〔:186/:187/:233 + :181 注释〕,改完 codecov 转绿。因当前 tip 单测确定性红、不可合并,P1 未闭环,提交 REQUEST_CHANGES 保持门控。⚠️ 我上一条落在 8383c3b4〔P0 仍开版本〕和 e25286149fa5〔HEAD 竞态、P0 已在该 commit 修好但我尚未审〕的两条 review 均已 minimize,本轮重锚到 e25286149fa5

@GuoLei1990 GuoLei1990 merged commit b7bf6f7 into galacean:dev/2.0 Jul 13, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation engine

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants