Skip to content
Open
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
79e9105
refactor(clone): add @defaultCloneMode and type-driven clone for cont…
Jun 17, 2026
a22e83c
refactor(clone): rewrite clone system — opt-out + default Assignment …
Jun 17, 2026
afce33d
refactor(clone): mark value-semantic classes with @defaultCloneMode(D…
Jun 17, 2026
94e6a03
fix(clone): remap nested Entity/Component refs via two-pass clone
Jun 22, 2026
a0e5fa9
refactor(clone): three-tier mode resolution + container default deep
Jun 22, 2026
297508f
refactor(clone): drop 171 field clone-mode decorators, rely on type d…
Jun 22, 2026
c5d6257
Merge remote-tracking branch 'upstream/dev/2.0' into fix/clone-opt-ou…
Jun 23, 2026
00498a1
ci: shard codecov unit tests (4 shards) to avoid browser crash on ful…
Jun 23, 2026
1561b72
docs(clone): drop CLONE_OPT_IN_AUDIT.md; design moved to PR description
Jun 23, 2026
d196914
ci: split codecov into 8 shards (vitest browser WebGL resource ceiling)
Jun 23, 2026
3cdd18c
test(clone): regression for texture refCount balance on clone/destroy
Jun 23, 2026
b8e754c
ci: revert codecov sharding experiment (let GitHub run the original f…
Jun 23, 2026
2139275
refactor(clone): decouple ref counting from the clone gate and unify …
Jul 2, 2026
13f3d95
Merge branch 'dev/2.0' into fix/clone-opt-out-assignment
Jul 2, 2026
9c5469c
refactor(clone): slot-ownership ref counting — gate acquires, owner r…
Jul 3, 2026
c2790dd
test(clone): refCount lifecycle suites + fix particle MeshShape mesh …
Jul 3, 2026
60135e9
refactor(clone)!: remove the deprecated shallowClone decorator
Jul 3, 2026
2a4309e
style(clone): order CloneManager members by visibility
Jul 3, 2026
88625db
refactor(clone): exempt user scripts from gate ref counting
Jul 3, 2026
2239fbd
refactor(clone): one uniform slot contract — no script special-casing
Jul 3, 2026
c7df2af
refactor(clone): move slot-ownership acquisition out of the gate
Jul 3, 2026
84e1b76
docs(clone): clarify the @deepClone-on-Remap branch is error recovery
Jul 3, 2026
3c26c2a
docs(clone): condense the CloneManager class comment
Jul 3, 2026
5b8da5a
test(clone): pin aliasing topology — shared instance clones into one …
Jul 3, 2026
e5894ae
refactor(clone): drop _cloneTo logic the type-driven gate already covers
Jul 3, 2026
8ef2a64
fix(mesh): type _onSkinUpdated's value as the union it actually carries
Jul 3, 2026
158fbdc
refactor(math): add a MathValue base class carrying the family clone …
Jul 3, 2026
1be50b0
Revert "refactor(math): add a MathValue base class carrying the famil…
Jul 3, 2026
5e3738d
test(clone): guard math value-type registration completeness
Jul 3, 2026
7dba7c1
feat(math): implement IClone/ICopy on Ray and register it Deep
Jul 3, 2026
de460e8
fix(clone): treat null-prototype objects as data containers
Jul 3, 2026
6c4cbb7
fix(clone): @deepClone on a registered asset recovers to sharing
Jul 3, 2026
56fb595
docs(clone): tighten comments in CloneManager and ComponentCloner
Jul 3, 2026
6a8688e
feat(clone): register runtime containers @defaultCloneMode(Ignore)
Jul 3, 2026
a3510d4
refactor(clone): drop @ignoreClone on runtime-container typed fields
Jul 3, 2026
887bfcc
docs(clone): define Deep as structure-fresh, member-semantic cloning
Jul 3, 2026
83e8328
docs(clone): correct stale or vacuous comments in gate and cloner
Jul 3, 2026
bdcc635
refactor(ui): centralize transition state ref counting in the base class
Jul 3, 2026
81c7991
test(math): cover Ray clone and copyFrom
Jul 3, 2026
e696681
fix(clone): release an owned counted preset displaced by an uncounted…
Jul 3, 2026
b0064ab
fix(clone): register typed-array clones in the identity map and harde…
Jul 3, 2026
d095812
fix(clone): route copyFrom dispatch after containers and close review…
Jul 3, 2026
6d0c7fe
test(clone): cover the rewritten Skin, PostProcess and TrailRenderer …
Jul 3, 2026
78ec199
docs(clone): align clone guides with the type-driven mechanism
Jul 3, 2026
9fa3cc2
refactor(clone): settle slot ownership unconditionally and fold dupli…
Jul 4, 2026
23f0459
test(clone): rename the suite after CloneManager and fix stale attrib…
Jul 4, 2026
2a91917
perf(clone): iterate Map/Set with for-of to avoid per-clone closure a…
Jul 4, 2026
c13dd08
docs(clone): drop a change-narrating comment on the Map branch
Jul 4, 2026
6d724c7
docs(clone): drop tense-relative wording from a lifecycle test comment
Jul 4, 2026
828008f
fix(particle): allow bare construction of ParticleCompositeGradient
Jul 4, 2026
16d95d6
test(clone): enforce the bare-construction contract for Deep-register…
Jul 4, 2026
f58d380
feat(clone): name the bare-construction contract when a deep clone ca…
Jul 4, 2026
843ab78
test(clone): pin the host-bound-in-container rejection as intended se…
Jul 4, 2026
9db6c66
test(clone): pin both host-bound-in-container behaviors by component …
Jul 4, 2026
f8ad0d7
fix(shader): cascade texture-array entries through the ref-count cont…
Jul 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions packages/core/src/2d/sprite/SpriteMask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { RenderContext } from "../../RenderPipeline/RenderContext";
import { SubPrimitiveChunk } from "../../RenderPipeline/SubPrimitiveChunk";
import { RenderElement } from "../../RenderPipeline/RenderElement";
import { Renderer, RendererUpdateFlags } from "../../Renderer";
import { assignmentClone, ignoreClone } from "../../clone/CloneManager";
import { ignoreClone } from "../../clone/CloneManager";
import { SpriteMaskLayer } from "../../enums/SpriteMaskLayer";
import { ShaderProperty } from "../../shader/ShaderProperty";
import { ISpriteRenderer } from "../assembler/ISpriteRenderer";
Expand All @@ -24,7 +24,6 @@ export class SpriteMask extends Renderer implements ISpriteRenderer {
static _alphaCutoffProperty: ShaderProperty = ShaderProperty.getByName("renderer_MaskAlphaCutoff");

/** The mask layers the sprite mask influence to. */
@assignmentClone
influenceLayers: SpriteMaskLayer = SpriteMaskLayer.Everything;
/** @internal */
@ignoreClone
Expand All @@ -44,16 +43,11 @@ export class SpriteMask extends Renderer implements ISpriteRenderer {
private _automaticWidth: number = 0;
@ignoreClone
private _automaticHeight: number = 0;
@assignmentClone
private _customWidth: number = undefined;
@assignmentClone
private _customHeight: number = undefined;
@assignmentClone
private _flipX: boolean = false;
@assignmentClone
private _flipY: boolean = false;

@assignmentClone
private _alphaCutoff: number = 0.5;

/**
Expand Down
9 changes: 1 addition & 8 deletions packages/core/src/2d/sprite/SpriteRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { RenderContext } from "../../RenderPipeline/RenderContext";
import { SubPrimitiveChunk } from "../../RenderPipeline/SubPrimitiveChunk";
import { RenderElement } from "../../RenderPipeline/RenderElement";
import { Renderer, RendererUpdateFlags } from "../../Renderer";
import { assignmentClone, deepClone, ignoreClone } from "../../clone/CloneManager";
import { ignoreClone } from "../../clone/CloneManager";
import { ShaderProperty } from "../../shader/ShaderProperty";
import { ISpriteAssembler } from "../assembler/ISpriteAssembler";
import { ISpriteRenderer } from "../assembler/ISpriteRenderer";
Expand Down Expand Up @@ -35,12 +35,9 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer {
private _drawMode: SpriteDrawMode;
@ignoreClone
private _assembler: ISpriteAssembler;
@assignmentClone
private _tileMode: SpriteTileMode = SpriteTileMode.Continuous;
@assignmentClone
private _tiledAdaptiveThreshold: number = 0.5;

@deepClone
private _color: Color = new Color(1, 1, 1, 1);
@ignoreClone
private _sprite: Sprite = null;
Expand All @@ -49,13 +46,9 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer {
private _automaticWidth: number = 0;
@ignoreClone
private _automaticHeight: number = 0;
@assignmentClone
private _customWidth: number = undefined;
@assignmentClone
private _customHeight: number = undefined;
@assignmentClone
private _flipX: boolean = false;
@assignmentClone
private _flipY: boolean = false;

/**
Expand Down
25 changes: 1 addition & 24 deletions packages/core/src/2d/text/TextRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { SubPrimitiveChunk } from "../../RenderPipeline/SubPrimitiveChunk";
import { RenderElement } from "../../RenderPipeline/RenderElement";
import { Renderer } from "../../Renderer";
import { TransformModifyFlags } from "../../Transform";
import { assignmentClone, deepClone, ignoreClone } from "../../clone/CloneManager";
import { ignoreClone } from "../../clone/CloneManager";
import { ShaderData, ShaderProperty } from "../../shader";
import { ShaderDataGroup } from "../../shader/enums/ShaderDataGroup";
import { Texture2D } from "../../texture";
Expand Down Expand Up @@ -36,38 +36,24 @@ export class TextRenderer extends Renderer implements ITextRenderer {
@ignoreClone
private _textChunks = Array<TextChunk>();
/** @internal */
@assignmentClone
_subFont: SubFont = null;
/** @internal */
@ignoreClone
_dirtyFlag = DirtyFlag.Font;
@deepClone
private _color = new Color(1, 1, 1, 1);
@assignmentClone
private _text = "";
@assignmentClone
private _width = 0;
@assignmentClone
private _height = 0;
@ignoreClone
private _localBounds = new BoundingBox();
@assignmentClone
private _font: Font = null;
@assignmentClone
private _fontSize = 24;
@assignmentClone
private _fontStyle = FontStyle.None;
@assignmentClone
private _lineSpacing = 0;
@assignmentClone
private _characterSpacing = 0;
@assignmentClone
private _horizontalAlignment = TextHorizontalAlignment.Center;
@assignmentClone
private _verticalAlignment = TextVerticalAlignment.Center;
@assignmentClone
private _enableWrapping = false;
@assignmentClone
private _overflowMode = OverflowMode.Overflow;

/**
Expand Down Expand Up @@ -330,15 +316,6 @@ export class TextRenderer extends Renderer implements ITextRenderer {
this._subFont && (this._subFont = null);
}

/**
* @internal
*/
override _cloneTo(target: TextRenderer): void {
super._cloneTo(target);
target.font = this._font;
target._subFont = this._subFont;
}

/**
* @internal
*/
Expand Down
20 changes: 1 addition & 19 deletions packages/core/src/Camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Transform } from "./Transform";
import { UpdateFlagManager } from "./UpdateFlagManager";
import { VirtualCamera } from "./VirtualCamera";
import { GLCapabilityType, Logger } from "./base";
import { deepClone, ignoreClone } from "./clone/CloneManager";
import { ignoreClone } from "./clone/CloneManager";
import { AntiAliasing } from "./enums/AntiAliasing";
import { CameraClearFlags } from "./enums/CameraClearFlags";
import { CameraModifyFlags } from "./enums/CameraModifyFlags";
Expand Down Expand Up @@ -116,13 +116,11 @@ export class Camera extends Component {
@ignoreClone
_globalShaderMacro: ShaderMacroCollection = new ShaderMacroCollection();
/** @internal */
@deepClone
_frustum: BoundingFrustum = new BoundingFrustum();
/** @internal */
@ignoreClone
_renderPipeline: BasicRenderPipeline;
/** @internal */
@deepClone
_virtualCamera: VirtualCamera = new VirtualCamera();
/** @internal */
_replacementShader: Shader = null;
Expand All @@ -148,25 +146,16 @@ export class Camera extends Component {
private _msaaSamples: MSAASamples;

private _renderTarget: RenderTarget = null;
@ignoreClone
private _updateFlagManager: UpdateFlagManager;
@ignoreClone
private _frustumChangeFlag: BoolUpdateFlag;
@ignoreClone
private _isViewMatrixDirty: BoolUpdateFlag;
@ignoreClone
private _isInvViewProjDirty: BoolUpdateFlag;
@deepClone
private _shaderData: ShaderData = new ShaderData(ShaderDataGroup.Camera);
@ignoreClone
private _depthBufferParams: Vector4 = new Vector4();
@deepClone
private _viewport: Vector4 = new Vector4(0, 0, 1, 1);
@deepClone
private _pixelViewport: Rect = new Rect(0, 0, 0, 0);
@deepClone
private _inverseProjectionMatrix: Matrix = new Matrix();
@deepClone
private _invViewProjMat: Matrix = new Matrix();

/**
Expand Down Expand Up @@ -826,13 +815,6 @@ export class Camera extends Component {
this._updateFlagManager?.removeListener(onChange);
}

/**
* @internal
*/
_cloneTo(target: Camera): void {
this._renderTarget?._addReferCount(1);
}

/**
* @internal
* @inheritdoc
Expand Down
13 changes: 3 additions & 10 deletions packages/core/src/Component.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { IReferable } from "./asset/IReferable";
import { EngineObject } from "./base";
import { assignmentClone, ignoreClone } from "./clone/CloneManager";
import { CloneUtils } from "./clone/CloneUtils";
import { defaultCloneMode, ignoreClone } from "./clone/CloneManager";
import { CloneMode } from "./clone/enums/CloneMode";
import { Entity } from "./Entity";
import { ActiveChangeFlag } from "./enums/ActiveChangeFlag";
import { Scene } from "./Scene";

/**
* The base class of the components.
*/
@defaultCloneMode(CloneMode.Remap)
export class Component extends EngineObject {
/** @internal */
_entity: Entity;
Expand All @@ -23,7 +24,6 @@ export class Component extends EngineObject {
@ignoreClone
private _phasedActive: boolean = false;

@assignmentClone
private _enabled: boolean = true;

/**
Expand Down Expand Up @@ -154,13 +154,6 @@ export class Component extends EngineObject {
}
}

/**
* @internal
*/
_remap<T extends Component>(srcRoot: Entity, targetRoot: Entity): T {
return CloneUtils.remapComponent(srcRoot, targetRoot, this) as unknown as T;
}

protected _addResourceReferCount(resource: IReferable, count: number): void {
this._entity._isTemplate || resource._addReferCount(count);
}
Expand Down
44 changes: 23 additions & 21 deletions packages/core/src/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ import { Transform } from "./Transform";
import { UpdateFlagManager } from "./UpdateFlagManager";
import { ReferResource } from "./asset/ReferResource";
import { EngineObject } from "./base";
import { CloneUtils } from "./clone/CloneUtils";
import { defaultCloneMode } from "./clone/CloneManager";
import { ComponentCloner } from "./clone/ComponentCloner";
import { CloneMode } from "./clone/enums/CloneMode";
import { ActiveChangeFlag } from "./enums/ActiveChangeFlag";
import { EntityModifyFlags } from "./enums/EntityModifyFlags";
import { DisorderedArray } from "./utils/DisorderedArray";

/**
* Entity, be used as components container.
*/
@defaultCloneMode(CloneMode.Remap)
export class Entity extends EngineObject {
/** @internal */
static _tempComponentConstructors: ComponentConstructor[] = [];
Expand Down Expand Up @@ -421,8 +423,9 @@ export class Entity extends EngineObject {
* @returns Cloned entity
*/
clone(): Entity {
const cloneEntity = this._createCloneEntity();
this._parseCloneEntity(this, cloneEntity, this, cloneEntity, new Map<Object, Object>());
const cloneMap = new Map<Object, Object>();
const cloneEntity = this._createCloneEntity(cloneMap);
this._parseCloneEntity(this, cloneEntity, cloneMap);
return cloneEntity;
}

Expand All @@ -434,13 +437,6 @@ export class Entity extends EngineObject {
return this._updateFlagManager.createFlag(BoolUpdateFlag);
}

/**
* @internal
*/
_remap(srcRoot: Entity, targetRoot: Entity): Entity {
return CloneUtils.remapEntity(srcRoot, targetRoot, this);
}

/**
* @internal
*/
Expand All @@ -449,14 +445,26 @@ export class Entity extends EngineObject {
this._templateResource = templateResource;
}

private _createCloneEntity(): Entity {
/**
* Build the clone's entity/component tree and register every source entity/component to its
* clone in the identity map, so the value-copy pass (`_parseCloneEntity`) can remap references —
* including ones nested in arrays / maps / objects — through the clone gate. Registration
* completes for the whole subtree before any value is copied, because a component anywhere in
* the tree may reference an entity anywhere else in it.
*/
private _createCloneEntity(cloneMap: Map<Object, Object>): Entity {
const componentConstructors = Entity._tempComponentConstructors;
const components = this._components;
for (let i = 0, n = components.length; i < n; i++) {
componentConstructors[i] = components[i].constructor as ComponentConstructor;
}
const cloneEntity = new Entity(this.engine, this.name, ...componentConstructors);
componentConstructors.length = 0;
cloneMap.set(this, cloneEntity);
const targetComponents = cloneEntity._components;
for (let i = 0, n = components.length; i < n; i++) {
cloneMap.set(components[i], targetComponents[i]);
}
const templateResource = this._templateResource;
if (templateResource) {
cloneEntity._templateResource = templateResource;
Expand All @@ -467,27 +475,21 @@ export class Entity extends EngineObject {
cloneEntity._isActive = this._isActive;
const srcChildren = this._children;
for (let i = 0, n = srcChildren.length; i < n; i++) {
cloneEntity.addChild(srcChildren[i]._createCloneEntity());
cloneEntity.addChild(srcChildren[i]._createCloneEntity(cloneMap));
}
return cloneEntity;
}

private _parseCloneEntity(
src: Entity,
target: Entity,
srcRoot: Entity,
targetRoot: Entity,
deepInstanceMap: Map<Object, Object>
): void {
private _parseCloneEntity(src: Entity, target: Entity, cloneMap: Map<Object, Object>): void {
const srcChildren = src._children;
const targetChildren = target._children;
for (let i = 0, n = srcChildren.length; i < n; i++) {
this._parseCloneEntity(srcChildren[i], targetChildren[i], srcRoot, targetRoot, deepInstanceMap);
this._parseCloneEntity(srcChildren[i], targetChildren[i], cloneMap);
}

const components = src._components;
for (let i = 0, n = components.length; i < n; i++) {
ComponentCloner.cloneComponent(components[i], target._components[i], srcRoot, targetRoot, deepInstanceMap);
ComponentCloner.cloneComponent(components[i], target._components[i], cloneMap);
}
}

Expand Down
7 changes: 1 addition & 6 deletions packages/core/src/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Entity } from "./Entity";
import { RenderContext } from "./RenderPipeline/RenderContext";
import { RenderElement } from "./RenderPipeline/RenderElement";
import { Transform, TransformModifyFlags } from "./Transform";
import { assignmentClone, deepClone, ignoreClone } from "./clone/CloneManager";
import { ignoreClone } from "./clone/CloneManager";
import { SpriteMaskLayer } from "./enums/SpriteMaskLayer";
import { Material } from "./material";
import { ShaderMacro, ShaderProperty } from "./shader";
Expand Down Expand Up @@ -48,9 +48,7 @@ export class Renderer extends Component {
@ignoreClone
_renderFrameCount: number;
/** @internal */
@assignmentClone
_maskInteraction: SpriteMaskInteraction = SpriteMaskInteraction.None;
@assignmentClone
_maskLayer: SpriteMaskLayer = SpriteMaskLayer.Layer0;

@ignoreClone
Expand All @@ -65,7 +63,6 @@ export class Renderer extends Component {
protected _bounds: BoundingBox = new BoundingBox();
protected _transformEntity: Entity;

@deepClone
private _shaderData: ShaderData = new ShaderData(ShaderDataGroup.Renderer);
@ignoreClone
private _mvMatrix: Matrix = new Matrix();
Expand All @@ -75,9 +72,7 @@ export class Renderer extends Component {
private _normalMatrix: Matrix = new Matrix();
@ignoreClone
private _materialsInstanced: boolean[] = [];
@assignmentClone
private _priority: number = 0;
@assignmentClone
private _receiveShadows: boolean = true;

/**
Expand Down
Loading
Loading