Skip to content

fix(client-bake): 第一帧判黑后先重试,别把能自愈的状况变成整单报废 - #925

Open
johnnyzhang-eng wants to merge 1 commit into
1024XEngineer:mainfrom
johnnyzhang-eng:fix/924-retry-black-frame
Open

fix(client-bake): 第一帧判黑后先重试,别把能自愈的状况变成整单报废#925
johnnyzhang-eng wants to merge 1 commit into
1024XEngineer:mainfrom
johnnyzhang-eng:fix/924-retry-black-frame

Conversation

@johnnyzhang-eng

Copy link
Copy Markdown
Contributor

Closes #924

问题

#918 加的亮度闸开始拦真实任务。线上连续 4 单失败(886 / 888 / 890 / 891):

浏览器出帧失败:第 0 帧主体是纯黑(平均亮度 0.0 < 20)

闸拦对了,但用户从「悄悄拿到一张黑帧」变成「整单做不出来」——代价太大。

根因

#918compileAsync 预热保证的是着色器编译已解码贴图的上传,不等图片本身 decode。FBXLoader.loadAsync() resolve 之后内嵌贴图仍在异步解码,所以第 0 帧照样黑。

为什么用重试

这是个纯等待问题:再渲一次就好了。要精确等到贴图就绪,得逐个 texture 去 await decode,而 three 的贴图来源有 ImageBitmap / HTMLImageElement / CanvasTexture 好几种,覆盖不全仍会漏。

判黑后重渲对所有成因都有效(贴图未解码、着色器未编译、材质异步替换),不需要预测时序。

判黑后每 300ms 重渲、最多 10 次;3 秒没好才失败。compileAsync 保留——它仍消掉一部分情况。

验证

前端 format / lint / typecheck / 1382 passed / build 全过。

两个变异方向都红:不重试直接失败(现在线上的行为)、永远不判黑(加闸之前的行为)。

口径更正

#918 里我说「渲第一帧前先 compileAsync 就能解决」,被线上证伪。当时只在自己的探针里验过——探针能过是因为它的贴图更小、解码更快,我没有在真实 BakeStage 里验。

1024XEngineer#918 加的亮度闸把黑帧拦住了,但 compileAsync 那道预热在真实浏览器里不够 ——
它只保证着色器编译与**已解码**贴图的上传,不等图片本身解码。线上实测 4 单
(886/888/890/891)全部卡在第 0 帧,用户从「悄悄拿到一张黑帧」变成「整单失败」。

改成判黑后每 300ms 重渲一次、最多 10 次;3 秒内没好才失败。贴图解码是几百毫秒
的事,给到 3 秒是十倍余量。

Closes 1024XEngineer#924
@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
windup Ready Ready Preview Aug 29, 2026 12:51am

@codecov

codecov Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #925      +/-   ##
==========================================
+ Coverage   86.12%   91.27%   +5.14%     
==========================================
  Files         306      193     -113     
  Lines       24037    13204   -10833     
  Branches     3646        0    -3646     
==========================================
- Hits        20703    12052    -8651     
+ Misses       2360     1152    -1208     
+ Partials      974        0     -974     
Flag Coverage Δ
backend 91.27% <ø> (ø)
frontend ?

Flags with carried forward coverage won't be shown. Click here to find out more.
see 113 files with indirect coverage changes

🚀 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.

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The retry behavior addresses transient black frames, but the new awaited retry path needs to preserve the existing cancellation contract before it can be considered safe.

Verification: git diff --check passes. The focused Vitest command could not run in this workspace because frontend dependencies are not installed (vitest: Permission denied).

// 再渲一次就好了。直接失败等于把一个几百毫秒能自愈的状况变成整单报废。
let luma = stage.subjectLuma()
for (let k = 0; luma >= 0 && luma < MIN_SUBJECT_LUMA && k < BLACK_FRAME_RETRIES; k++) {
await sleep(BLACK_FRAME_WAIT_MS)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Recheck cancellation during black-frame waits

If signal is aborted while this sleep is pending, the loop resumes and calls subjectLuma() without invoking throwIfAborted(). A still-black frame then throws StageError, which the catch block reports through failBake; if the frame recovers, the code can upload a frame after cancellation. This breaks the existing BakeAborted contract (cancellation must not be reported as failure) for the new retry path. Recheck the signal after the await (or make the wait abort-aware) before sampling or continuing.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 亮度闸把出帧整单拦死了 —— 预热不够,第 0 帧仍会黑

1 participant