fix(camera): mark replacement shader fields as assignment clone#2988
fix(camera): mark replacement shader fields as assignment clone#2988cptbtptpbcptdtptp wants to merge 2 commits into
Conversation
Add @assignmentClone to _replacementShader and _replacementSubShaderTag so cloned cameras share the same shader/tag reference instead of deep cloning them (which would create unnecessary duplicates). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WalkthroughCamera's internal replacement-shader state ( ChangesReplacement Shader Clone Preservation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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/core/Camera.test.ts`:
- Around line 432-453: Replace the string assignment and string comparison with
a proper ShaderTagKey: create a ShaderTagKey via
ShaderTagKey.getByName("TestTag"), assign that to
camera._replacementSubShaderTag (instead of the raw string), and update the
assertion to compare the cloned camera's _replacementSubShaderTag to the same
ShaderTagKey (or compare their names via .getName()) so types and semantics
match; references: camera._replacementSubShaderTag, ShaderTagKey.getByName,
cloneCamera.
🪄 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: 1688ce5f-7744-4d70-9cf9-41db4bc2fbf8
📒 Files selected for processing (2)
packages/core/src/Camera.tstests/src/core/Camera.test.ts
| it("clone preserves replacement shader by reference (assignmentClone)", () => { | ||
| const shader = Shader.create("TestCloneReplaceShader", [ | ||
| new SubShader("Default", [new ShaderPass("Default", [], [], ShaderLanguage.GLSLES100)]) | ||
| ]); | ||
|
|
||
| // @ts-ignore | ||
| camera._replacementShader = shader; | ||
| // @ts-ignore | ||
| camera._replacementSubShaderTag = "TestTag"; | ||
|
|
||
| const cloneCamera = camera.entity.clone().getComponent(Camera); | ||
|
|
||
| // @ts-ignore - assignmentClone copies reference, not deep clone | ||
| expect(cloneCamera._replacementShader).to.eq(shader); | ||
| // @ts-ignore | ||
| expect(cloneCamera._replacementSubShaderTag).to.eq("TestTag"); | ||
|
|
||
| // @ts-ignore - cleanup | ||
| camera._replacementShader = null; | ||
| // @ts-ignore | ||
| camera._replacementSubShaderTag = null; | ||
| }); |
There was a problem hiding this comment.
Fix type violations in replacement shader tag assignment and assertion.
The test has two critical correctness issues:
-
Line 440: Assigns a string
"TestTag"directly to_replacementSubShaderTag, but this field is typed asShaderTagKey, notstring. While@ts-ignoresuppresses the type error, this violates the actual type contract. -
Line 447: Compares
_replacementSubShaderTagto the string"TestTag". If line 440 is corrected to use a properShaderTagKeyobject, this assertion will fail since you'd be comparing an object to a string.
The proper usage pattern is shown in Camera.ts line 719: ShaderTagKey.getByName(replacementTag).
🔧 Proposed fix using ShaderTagKey.getByName()
+ const testTag = ShaderTagKey.getByName("TestTag");
+
// `@ts-ignore`
camera._replacementShader = shader;
// `@ts-ignore`
- camera._replacementSubShaderTag = "TestTag";
+ camera._replacementSubShaderTag = testTag;
const cloneCamera = camera.entity.clone().getComponent(Camera);
// `@ts-ignore` - assignmentClone copies reference, not deep clone
expect(cloneCamera._replacementShader).to.eq(shader);
// `@ts-ignore`
- expect(cloneCamera._replacementSubShaderTag).to.eq("TestTag");
+ expect(cloneCamera._replacementSubShaderTag).to.eq(testTag);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it("clone preserves replacement shader by reference (assignmentClone)", () => { | |
| const shader = Shader.create("TestCloneReplaceShader", [ | |
| new SubShader("Default", [new ShaderPass("Default", [], [], ShaderLanguage.GLSLES100)]) | |
| ]); | |
| // @ts-ignore | |
| camera._replacementShader = shader; | |
| // @ts-ignore | |
| camera._replacementSubShaderTag = "TestTag"; | |
| const cloneCamera = camera.entity.clone().getComponent(Camera); | |
| // @ts-ignore - assignmentClone copies reference, not deep clone | |
| expect(cloneCamera._replacementShader).to.eq(shader); | |
| // @ts-ignore | |
| expect(cloneCamera._replacementSubShaderTag).to.eq("TestTag"); | |
| // @ts-ignore - cleanup | |
| camera._replacementShader = null; | |
| // @ts-ignore | |
| camera._replacementSubShaderTag = null; | |
| }); | |
| it("clone preserves replacement shader by reference (assignmentClone)", () => { | |
| const shader = Shader.create("TestCloneReplaceShader", [ | |
| new SubShader("Default", [new ShaderPass("Default", [], [], ShaderLanguage.GLSLES100)]) | |
| ]); | |
| const testTag = ShaderTagKey.getByName("TestTag"); | |
| // `@ts-ignore` | |
| camera._replacementShader = shader; | |
| // `@ts-ignore` | |
| camera._replacementSubShaderTag = testTag; | |
| const cloneCamera = camera.entity.clone().getComponent(Camera); | |
| // `@ts-ignore` - assignmentClone copies reference, not deep clone | |
| expect(cloneCamera._replacementShader).to.eq(shader); | |
| // `@ts-ignore` | |
| expect(cloneCamera._replacementSubShaderTag).to.eq(testTag); | |
| // `@ts-ignore` - cleanup | |
| camera._replacementShader = null; | |
| // `@ts-ignore` | |
| camera._replacementSubShaderTag = null; | |
| }); |
🤖 Prompt for 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.
In `@tests/src/core/Camera.test.ts` around lines 432 - 453, Replace the string
assignment and string comparison with a proper ShaderTagKey: create a
ShaderTagKey via ShaderTagKey.getByName("TestTag"), assign that to
camera._replacementSubShaderTag (instead of the raw string), and update the
assertion to compare the cloned camera's _replacementSubShaderTag to the same
ShaderTagKey (or compare their names via .getName()) so types and semantics
match; references: camera._replacementSubShaderTag, ShaderTagKey.getByName,
cloneCamera.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev/2.0 #2988 +/- ##
===========================================
+ Coverage 78.25% 78.30% +0.04%
===========================================
Files 900 900
Lines 99234 99236 +2
Branches 10172 10192 +20
===========================================
+ Hits 77657 77705 +48
+ Misses 21406 21360 -46
Partials 171 171
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
GuoLei1990
left a comment
There was a problem hiding this comment.
总结
给 Camera 的 _replacementShader / _replacementSubShaderTag 加 @assignmentClone,意图是 clone 时共享引用而非深拷贝。语义方向对(Shader / ShaderTagKey 是引擎全局共享资产),但本 PR 在当前 dev/2.0 模型下是运行时 no-op,且与已落地的 clone 重构(#3018 的 @property 模型)直接冲突,需要重新对齐落点。代码未变(HEAD 仍为 9845234),结论与上一轮一致,此处合并为一份。
问题
P1
- PR 处于 CONFLICTING / DIRTY,无法合并 — base
dev/2.0上 clone 系统已切到 opt-in@property模型(assignmentClone/deepClone/ignoreClone这套 per-field 装饰器已不存在,CloneManager 改为@property+ 类级@defaultCloneMode)。本 PR 仍在旧装饰器模型上打补丁,rebase 后会编译失败。必须基于新模型重做。
P2
-
Camera.ts:128-130— 在 dev/2.0 旧模型下,这两个字段加@assignmentClone是运行时 no-op,不是 correctness 修复CloneManager.cloneProperty(origin/dev/2.0)的直接赋值分支:
```ts
if (!(sourceProperty instanceof Object) || cloneMode === undefined || cloneMode === CloneMode.Assignment) {
target[k] = sourceProperty; // 引用赋值
return;
}
```
未装饰字段(cloneMode === undefined)和@assignmentClone(CloneMode.Assignment)走同一分支。Shader/ShaderTagKey均无_remap(已确认),不进 remap 分支,深拷贝只发生在@shallowClone/@deepClone。因此这两个字段没装饰本来就引用共享,与未装饰的_renderTarget一致。PR 描述「不加就会深拷贝 Shader」在 dev/2.0 不成立。建议把fix:+ 「修复深拷贝」措辞改为「显式标注共享语义」,避免误导 changelog / 回滚判断。 -
与 #3018 的关系:本 PR 应并入 #3018,而非独立合入 — #3018(
@property重构)已包含c2f588710 fix(clone): clone Camera replacement fields with managed shader ref count,正是对这两个字段在新模型下的正确处理(ShaderTagKey已用类级@defaultCloneMode(CloneMode.Assignment),且新模型对 ref-counted 资源做 +1 引用计数)。本 PR 的 per-field 装饰器在新模型里会被一并移除。建议直接关闭本 PR、由 #3018 覆盖,或在 #3018 落地前明确两者落点不再重叠。 -
Camera.test.ts:440— 测试给_replacementSubShaderTag(类型ShaderTagKey)赋裸字符串"TestTag",靠@ts-ignore压制类型错误(历史多轮提及,仍未改)只验证了字符串引用相等,没验证真实
ShaderTagKey对象的 clone 行为。ShaderTagKey在新模型里正是@defaultCloneMode(CloneMode.Assignment)的典型形态,应重点验证。建议改用ShaderTagKey.getByName("TestTag")构造真实对象再断言引用相等。
自检
- no-op 行为对照
origin/dev/2.0(真实 merge 目标)的cloneProperty源码确认:未装饰与@assignmentClone同一直接赋值分支;Shader/ShaderTagKey无_remap已 grep 确认。 - 新模型已落地 / 旧装饰器已删除:对照
pr3018_fresh分支 CloneManager(@property+@defaultCloneMode,旧assignmentClone等 export 已不存在)确认。 - 代码自上一轮 review(commit 9845234)未变,本次仅合并并刷新结论,不新增重复角度。
Summary
@assignmentCloneto_replacementShaderand_replacementSubShaderTagso cloned cameras share the same shader/tag reference instead of deep cloning them.Background
Extracted from
ca5252a9aon fix/shaderlab. Without this, cloning a camera that has a replacement shader set would deep-clone the Shader object, creating an unnecessary duplicate.Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit