Skip to content

fix(camera): mark replacement shader fields as assignment clone#2988

Draft
cptbtptpbcptdtptp wants to merge 2 commits into
galacean:dev/2.0from
cptbtptpbcptdtptp:pr/camera-replacement-clone-mode
Draft

fix(camera): mark replacement shader fields as assignment clone#2988
cptbtptpbcptdtptp wants to merge 2 commits into
galacean:dev/2.0from
cptbtptpbcptdtptp:pr/camera-replacement-clone-mode

Conversation

@cptbtptpbcptdtptp

@cptbtptpbcptdtptp cptbtptpbcptdtptp commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add @assignmentClone to _replacementShader and _replacementSubShaderTag so cloned cameras share the same shader/tag reference instead of deep cloning them.

Background

Extracted from ca5252a9a on 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

  • New test case: clone preserves replacement shader by reference
  • CI green

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed camera component duplication to properly maintain shader state during cloning, ensuring consistent behavior when duplicating camera instances.

Review Change Stack

cptbtptpbcptdtptp and others added 2 commits May 11, 2026 18:22
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>
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Walkthrough

Camera's internal replacement-shader state (_replacementShader and _replacementSubShaderTag) is now decorated with @assignmentClone, ensuring these fields preserve their object references during component cloning. Import statements and test coverage are updated accordingly.

Changes

Replacement Shader Clone Preservation

Layer / File(s) Summary
Import decorator
packages/core/src/Camera.ts
CloneManager imports include assignmentClone function alongside existing clone utilities.
Apply decorator to fields
packages/core/src/Camera.ts
@assignmentClone decorator applied to _replacementShader and _replacementSubShaderTag, changing clone semantics for these internal fields.
Test coverage
tests/src/core/Camera.test.ts
Existing clone test assertions reformatted for consistency; new test validates cloned camera retains shader object reference and tag value when replacement shaders are set.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A shader shines so bright,
Clone it right, preserve the light!
With decorators, references stay true,
Two fields now know just what to do. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding @assignmentClone decorator to camera replacement shader fields to fix cloning behavior.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

@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/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

📥 Commits

Reviewing files that changed from the base of the PR and between 1bc2b10 and 9845234.

📒 Files selected for processing (2)
  • packages/core/src/Camera.ts
  • tests/src/core/Camera.test.ts

Comment on lines +432 to +453
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;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix type violations in replacement shader tag assignment and assertion.

The test has two critical correctness issues:

  1. Line 440: Assigns a string "TestTag" directly to _replacementSubShaderTag, but this field is typed as ShaderTagKey, not string. While @ts-ignore suppresses the type error, this violates the actual type contract.

  2. Line 447: Compares _replacementSubShaderTag to the string "TestTag". If line 440 is corrected to use a proper ShaderTagKey object, 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.

Suggested change
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

codecov Bot commented May 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.30%. Comparing base (1bc2b10) to head (9845234).

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              
Flag Coverage Δ
unittests 78.30% <100.00%> (+0.04%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

@GuoLei1990 GuoLei1990 mentioned this pull request May 11, 2026
3 tasks
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

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

总结

给 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)和 @assignmentCloneCloneMode.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)未变,本次仅合并并刷新结论,不新增重复角度。

@GuoLei1990 GuoLei1990 marked this pull request as draft June 15, 2026 02:46
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.

2 participants