diff --git a/cc.config.json b/cc.config.json index db1368c1396..ef888d91ef0 100644 --- a/cc.config.json +++ b/cc.config.json @@ -252,6 +252,7 @@ "cocos/render-scene/scene/submodel.ts": "cocos/render-scene/scene/submodel.jsb.ts", "cocos/render-scene/scene/index.ts": "cocos/render-scene/scene/index.jsb.ts", "cocos/render-scene/scene/reflection-probe.ts": "cocos/render-scene/scene/reflection-probe.jsb.ts", + "cocos/2d/renderer/batcher-2d.ts": "cocos/2d/renderer/batcher-2d.jsb.ts", "cocos/2d/renderer/native-2d.ts": "cocos/2d/renderer/native-2d.jsb.ts", "cocos/gfx/base/pipeline-state.ts": "cocos/gfx/base/pipeline-state.jsb.ts", "cocos/gfx/base/pipeline-sub-state.ts": "cocos/gfx/base/pipeline-sub-state.jsb.ts", diff --git a/cocos/2d/components/label.ts b/cocos/2d/components/label.ts index 7f1c3c8ff29..cf6f0799c48 100644 --- a/cocos/2d/components/label.ts +++ b/cocos/2d/components/label.ts @@ -40,6 +40,8 @@ import { BlendFactor } from '../../gfx'; import { TextStyle } from '../assembler/label/text-style'; import { TextLayout } from '../assembler/label/text-layout'; import { TextOutputLayoutData, TextOutputRenderData } from '../assembler/label/text-output-data'; +import { TransformBit } from '../../scene-graph'; +import { uiLayoutManager } from '../framework/ui-layout-manager'; const tempColor = Color.WHITE.clone(); /** @@ -230,6 +232,7 @@ export class Label extends UIRenderer { } this._string = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -252,6 +255,7 @@ export class Label extends UIRenderer { } this._horizontalAlign = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -274,6 +278,7 @@ export class Label extends UIRenderer { } this._verticalAlign = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -309,6 +314,7 @@ export class Label extends UIRenderer { } this._fontSize = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -330,6 +336,7 @@ export class Label extends UIRenderer { } this._lineHeight = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -354,6 +361,7 @@ export class Label extends UIRenderer { } this._spacingX = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -376,6 +384,7 @@ export class Label extends UIRenderer { } this._overflow = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -397,6 +406,7 @@ export class Label extends UIRenderer { } this._enableWrapText = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -432,6 +442,7 @@ export class Label extends UIRenderer { this.font = null; } this._flushAssembler(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -454,6 +465,7 @@ export class Label extends UIRenderer { } this._fontFamily = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -492,6 +504,7 @@ export class Label extends UIRenderer { this.destroyRenderData(); this._fontAtlas = null; + this._markLayoutDirty(); this.updateRenderData(true); } @@ -521,6 +534,7 @@ export class Label extends UIRenderer { } this._cacheMode = value; + this._markLayoutDirty(); this.updateRenderData(true); } @@ -542,6 +556,7 @@ export class Label extends UIRenderer { } this._isBold = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -563,6 +578,7 @@ export class Label extends UIRenderer { } this._isItalic = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -621,6 +637,7 @@ export class Label extends UIRenderer { set enableOutline (value) { if (this._enableOutline === value) return; this._enableOutline = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -641,6 +658,7 @@ export class Label extends UIRenderer { set outlineColor (value) { if (this._outlineColor === value) return; this._outlineColor.set(value); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -661,6 +679,7 @@ export class Label extends UIRenderer { set outlineWidth (value) { if (this._outlineWidth === value) return; this._outlineWidth = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -678,6 +697,7 @@ export class Label extends UIRenderer { set enableShadow (value) { if (this._enableShadow === value) return; this._enableShadow = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -698,6 +718,7 @@ export class Label extends UIRenderer { set shadowColor (value) { if (this._shadowColor === value) return; this._shadowColor.set(value); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -718,6 +739,7 @@ export class Label extends UIRenderer { set shadowOffset (value) { if (this._shadowOffset === value) return; this._shadowOffset.set(value); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -738,6 +760,7 @@ export class Label extends UIRenderer { set shadowBlur (value) { if (this._shadowBlur === value) return; this._shadowBlur = value; + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -808,6 +831,12 @@ export class Label extends UIRenderer { get textLayoutData (): TextOutputLayoutData { return this._textLayoutData!; } + /** + * @engineInternal + */ + get layoutDirty (): boolean { + return this._layoutDirty; + } @serializable protected _string = 'label'; @@ -875,6 +904,8 @@ export class Label extends UIRenderer { protected _textRenderData: TextOutputRenderData | null = null; protected _textLayoutData: TextOutputLayoutData | null = null; + protected _layoutDirty = false; + /** * @engineInternal */ @@ -904,6 +935,7 @@ export class Label extends UIRenderer { public onEnable (): void { super.onEnable(); + this._markLayoutDirty(); // TODO: Hack for barbarians if (!this._font && !this._isSystemFontUsed) { @@ -963,6 +995,7 @@ export class Label extends UIRenderer { this._applyFontTexture(); } if (this._assembler) { + // this._assembler.updateLayoutData(this); // Todo this._assembler.updateRenderData(this); } } @@ -1041,6 +1074,7 @@ export class Label extends UIRenderer { } this.changeMaterialForDefine(); if (this._assembler) { + // this._assembler.updateLayoutData(this);// Todo this._assembler.updateRenderData(this); } } @@ -1104,6 +1138,46 @@ export class Label extends UIRenderer { } super._updateBlendFunc(); } + + /** + * @engineInternal + */ + public _markLayoutDirty (): void { + if (this._layoutDirty) return; + this._layoutDirty = true; + if (this.enabled) { + uiLayoutManager.markLayoutDirty(this); + } + } + + /** + * @engineInternal + */ + public _resetLayoutDirty (): void { + this._layoutDirty = false; + } + + /** + * @engineInternal + */ + public _updateLayout (): void { + // Todo + // if (this._assembler) { + // this._assembler.updateLayoutData(this); + // } + } + + protected _nodeStateChange (transformType: TransformBit): void { + super._nodeStateChange(transformType); + this._markLayoutDirty(); + for (let i = 0; i < this.node.children.length; ++i) { + const child = this.node.children[i]; + const renderComp = child.getComponent(Label); + if (renderComp) { + renderComp._markLayoutDirty(); + } + } + } } cclegacy.Label = Label; diff --git a/cocos/2d/framework/render-root-2d.ts b/cocos/2d/framework/render-root-2d.ts index 9f10e6080e9..55508cbce89 100644 --- a/cocos/2d/framework/render-root-2d.ts +++ b/cocos/2d/framework/render-root-2d.ts @@ -22,10 +22,11 @@ THE SOFTWARE. */ -import { ccclass, disallowMultiple, executeInEditMode, executionOrder, help, menu, requireComponent } from 'cc.decorator'; -import { cclegacy } from '@base/global'; +import { ccclass, disallowMultiple, executeInEditMode, + executionOrder, help, menu, requireComponent } from 'cc.decorator'; import { Component } from '../../scene-graph/component'; import { UITransform } from './ui-transform'; +import { uiSystem } from './ui-system'; /** * @en The entry node for 2D object data collection, all 2D rendering objects need to be rendered under the RenderRoot node. @@ -40,14 +41,14 @@ import { UITransform } from './ui-transform'; @executeInEditMode export class RenderRoot2D extends Component { public onEnable (): void { - cclegacy.director.root!.batcher2D.addScreen(this); + uiSystem.batcher2D.addScreen(this.node); } public onDisable (): void { - cclegacy.director.root!.batcher2D.removeScreen(this); + uiSystem.batcher2D.removeScreen(this.node); } public onDestroy (): void { - cclegacy.director.root!.batcher2D.removeScreen(this); + uiSystem.batcher2D.removeScreen(this.node); } } diff --git a/cocos/2d/framework/ui-layout-manager.ts b/cocos/2d/framework/ui-layout-manager.ts new file mode 100644 index 00000000000..9d345280134 --- /dev/null +++ b/cocos/2d/framework/ui-layout-manager.ts @@ -0,0 +1,43 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import { UIRenderer } from './ui-renderer'; + +export class UILayoutManager { + private _dirtyUIs: (UIRenderer)[] = []; + + public markLayoutDirty (uiRenderer: UIRenderer): void { + this._dirtyUIs.push(uiRenderer); + } + + public updateAllDirtyLayout (): void { + const length = this._dirtyUIs.length; + const dirtyRenderers = this._dirtyUIs; + for (let i = 0; i < length; i++) { + dirtyRenderers[i]._updateLayout(); + } + this._dirtyUIs.length = 0; + } +} +export const uiLayoutManager = new UILayoutManager(); \ No newline at end of file diff --git a/cocos/2d/framework/ui-renderer.ts b/cocos/2d/framework/ui-renderer.ts index 061f9b25596..929be299d13 100644 --- a/cocos/2d/framework/ui-renderer.ts +++ b/cocos/2d/framework/ui-renderer.ts @@ -42,8 +42,8 @@ import { Renderer } from '../../misc/renderer'; import { RenderEntity, RenderEntityType } from '../renderer/render-entity'; import { uiRendererManager } from './ui-renderer-manager'; import { RenderDrawInfoType } from '../renderer/render-draw-info'; -import { director } from '../../game'; import type { Batcher2D } from '../renderer/batcher-2d'; +import { uiSystem } from './ui-system'; // hack ccenum(BlendFactor); @@ -133,7 +133,7 @@ export class UIRenderer extends Renderer { * @zh 后置渲染数据组装器 * @internal */ - public static PostAssembler: IAssemblerManager | null = null; + public static PostAssembler: IAssemblerManager | null = null; // Todo: Remove it constructor () { super(); @@ -256,7 +256,7 @@ export class UIRenderer extends Renderer { protected _stencilStage: Stage = Stage.DISABLED; protected _assembler: IAssembler | null = null; - protected _postAssembler: IAssembler | null = null; + protected _postAssembler: IAssembler | null = null; // Todo: Remove it // RenderEntity //protected renderData: RenderData | null = null; @@ -286,7 +286,7 @@ export class UIRenderer extends Renderer { * @deprecated Since v3.7.0, this is an engine private interface that will be removed in the future. */ get batcher (): Batcher2D { - return director.root!.batcher2D; + return uiSystem.batcher2D; } /** @@ -426,12 +426,19 @@ export class UIRenderer extends Renderer { * 它可能会组装额外的渲染数据到顶点数据缓冲区,也可能只是重置一些渲染状态。 * 注意:不要手动调用该函数,除非你理解整个流程。 */ - public postUpdateAssembler (render: IBatcher): void { + public postUpdateAssembler (render: IBatcher): void { // Todo:Remove it if (this._postAssembler && this._renderFlag) { this._postRender(render); } } + /** + * @engineInternal + */ + public _updateLayout (): void { + // Implemented by subclasses + } + protected _render (render: IBatcher): void { // Implemented by subclasses } diff --git a/cocos/2d/framework/ui-system.ts b/cocos/2d/framework/ui-system.ts new file mode 100644 index 00000000000..1dffb3440bf --- /dev/null +++ b/cocos/2d/framework/ui-system.ts @@ -0,0 +1,104 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + https://www.cocos.com/ + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import { cclegacy } from '@base/global'; +import { System } from '../../core'; +import { Director, director } from '../../game/director'; +import { uiRendererManager } from './ui-renderer-manager'; +import { uiLayoutManager } from './ui-layout-manager'; +import { Batcher2D } from '../renderer/batcher-2d'; + +export class UISystem extends System { + private _batcher: Batcher2D | null = null; + + /** + * @en The draw batch manager for 2D UI, for engine internal usage, user do not need to use this. + * @zh 2D UI 渲染合批管理器,引擎内部使用,用户无需使用此接口 + * @engineInternal + */ + public get batcher2D (): Batcher2D { + return this._batcher as Batcher2D; + } + + public init (): void { + if (!this._batcher) { + this._batcher = Batcher2D.getInstance(); + } + director.on(Director.EVENT_AFTER_SCENE_LAUNCH, this.afterSceneLaunch, this); + director.on(Director.EVENT_BEFORE_UPDATE, this.beforeUpdate, this); + director.on(Director.EVENT_AFTER_UPDATE, this.afterUpdate, this); + director.on(Director.EVENT_UPDATE_UI, this.tick, this); + director.on(Director.EVENT_BEFORE_DRAW, this.beforeDraw, this); + director.on(Director.EVENT_AFTER_DRAW, this.afterDraw, this); + director.on(Director.EVENT_BEFORE_COMMIT, this.render, this); + } + + public destroy (): void { + director.off(Director.EVENT_AFTER_SCENE_LAUNCH, this.afterSceneLaunch, this); + director.off(Director.EVENT_BEFORE_UPDATE, this.beforeUpdate, this); + director.off(Director.EVENT_AFTER_UPDATE, this.afterUpdate, this); + director.off(Director.EVENT_UPDATE_UI, this.tick, this); + director.off(Director.EVENT_BEFORE_DRAW, this.beforeDraw, this); + director.off(Director.EVENT_AFTER_DRAW, this.afterDraw, this); + director.off(Director.EVENT_BEFORE_COMMIT, this.render, this); + if (this._batcher) { + this._batcher.destroy(); + this._batcher = null; + } + } + + public afterSceneLaunch (): void { + cclegacy._widgetManager.refreshScene(); + } + + public beforeUpdate (): void { + cclegacy._widgetManager.refreshScene(); + } + + public afterUpdate (): void { + // useless + } + + public tick (): void { + uiLayoutManager.updateAllDirtyLayout(); + uiRendererManager.updateAllDirtyRenderers(); + } + + public render (): void { + if (this._batcher) { + this._batcher.update(); + this._batcher.uploadBuffers(); + } + } + + public afterDraw (): void { + if (this._batcher) { + this._batcher.reset(); + } + } + + private beforeDraw (): void { + // useless + } +} + +export const uiSystem = new UISystem(); +director.registerSystem('ui-system', uiSystem, 0); +cclegacy.internal.uiSystem = uiSystem; diff --git a/cocos/2d/framework/ui-transform.ts b/cocos/2d/framework/ui-transform.ts index 21e3ad196c8..835f6e03f8e 100644 --- a/cocos/2d/framework/ui-transform.ts +++ b/cocos/2d/framework/ui-transform.ts @@ -31,6 +31,7 @@ import { Director, director } from '../../game/director'; import { NodeEventType } from '../../scene-graph/node-event'; import { IMask } from '../../scene-graph/node-event-processor'; import { Mask } from '../components/mask'; +import { Batcher2D } from '../renderer/batcher-2d'; const _vec2a = new Vec2(); const _vec2b = new Vec2(); @@ -237,7 +238,7 @@ export class UITransform extends Component { * @deprecated since v3.0 */ get visibility (): number { - const camera = director.root!.batcher2D.getFirstRenderCamera(this.node); + const camera = Batcher2D.getFirstRenderCamera(this.node); return camera ? camera.visibility : 0; } @@ -246,7 +247,7 @@ export class UITransform extends Component { * @zh 查找被渲染相机的渲染优先级。 */ get cameraPriority (): number { - const camera = director.root!.batcher2D.getFirstRenderCamera(this.node); + const camera = Batcher2D.getFirstRenderCamera(this.node); return camera ? camera.priority : 0; } diff --git a/cocos/2d/renderer/batcher-2d.jsb.ts b/cocos/2d/renderer/batcher-2d.jsb.ts new file mode 100644 index 00000000000..f9206caa215 --- /dev/null +++ b/cocos/2d/renderer/batcher-2d.jsb.ts @@ -0,0 +1,32 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + + https://www.cocos.com/ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +import { cclegacy } from '@base/global'; +import type { Batcher2D as n2dBatcher2D } from "./batcher-2d"; + +declare const n2d: any; + +export const Batcher2D: typeof n2dBatcher2D = n2d.Batcher2D; +export type Batcher2D = n2dBatcher2D; +const batcher2DProto: any = n2d.Batcher2D.prototype; +cclegacy.internal.Batcher2D = Batcher2D; diff --git a/cocos/2d/renderer/batcher-2d.ts b/cocos/2d/renderer/batcher-2d.ts index cd146c400ae..845ad5c9d11 100644 --- a/cocos/2d/renderer/batcher-2d.ts +++ b/cocos/2d/renderer/batcher-2d.ts @@ -29,7 +29,7 @@ import { assertIsTrue } from '@base/debug/internal'; import { memop } from '@base/utils'; import { Camera, Model } from '../../render-scene/scene'; import { Material } from '../../asset/assets/material'; -import { RenderRoot2D, UIRenderer } from '../framework'; +import { UIRenderer } from '../framework'; import { Texture, Device, Attribute, Sampler, DescriptorSetInfo, Buffer, BufferInfo, BufferUsageBit, MemoryUsageBit, DescriptorSet, InputAssembler, deviceManager, PrimitiveMode } from '../../gfx'; import { Mat4, approx, EPSILON } from '../../core'; import { Root } from '../../root'; @@ -40,16 +40,16 @@ import { ModelLocalBindings, UBOLocal } from '../../rendering/define'; import { SpriteFrame } from '../assets'; import { TextureBase } from '../../asset/assets/texture-base'; import { IBatcher } from './i-batcher'; -import { StaticVBAccessor } from './static-vb-accessor'; -import { getAttributeStride, vfmt, vfmtPosUvColor } from './vertex-format'; +import { getAttributeStride, vfmt } from './vertex-format'; import { updateOpacity } from '../assembler/utils'; import { BaseRenderData, MeshRenderData } from './render-data'; import { UIMeshRenderer } from '../components/ui-mesh-renderer'; -import { NativeBatcher2d, NativeUIMeshBuffer } from './native-2d'; +import type { NativeUIMeshBuffer } from './native-2d'; import { MeshBuffer } from './mesh-buffer'; import { scene } from '../../render-scene'; import { builtinResMgr } from '../../asset/asset-manager'; import { RenderingSubMesh } from '../../asset/assets'; +import { BufferAccessorManager } from './buffer-accessor-manager'; const _dsInfo = new DescriptorSetInfo(null!); const m4_1 = new Mat4(); @@ -59,16 +59,25 @@ const m4_1 = new Mat4(); * @zh UI 渲染流程 */ export class Batcher2D implements IBatcher { - protected declare _nativeObj: NativeBatcher2d; - public get nativeObj (): NativeBatcher2d { - return this._nativeObj; + public static getInstance (): Batcher2D { + if (this.instance === null) { + this.instance = new Batcher2D(cclegacy.director.root as Root); + } + return this.instance; } + private static instance: Batcher2D | null = null; - get currBufferAccessor (): StaticVBAccessor { - if (this._staticVBBuffer) return this._staticVBBuffer; - // create if not set - this._staticVBBuffer = this.switchBufferAccessor(); - return this._staticVBBuffer; + public static getFirstRenderCamera (node: Node): Camera | null { + if (node.scene && node.scene.renderScene) { + const cameras = node.scene.renderScene.cameras; + for (let i = 0; i < cameras.length; i++) { + const camera = cameras[i]; + if (camera.visibility & node.layer) { + return camera; + } + } + } + return null; } get batches (): memop.CachedArray { @@ -76,13 +85,11 @@ export class Batcher2D implements IBatcher { } public device: Device; - private _screens: RenderRoot2D[] = []; - private _staticVBBuffer: StaticVBAccessor | null = null; - private _bufferAccessors: Map = new Map(); + private _screens: Node[] = []; private _drawBatchPool: memop.Pool; private _batches: memop.CachedArray; - private _currBID = -1; + private _indexStart = 0; private _emptyMaterial = new Material(); @@ -136,10 +143,7 @@ export class Batcher2D implements IBatcher { } this._batches.destroy(); - for (const accessor of this._bufferAccessors.values()) { - accessor.destroy(); - } - this._bufferAccessors.clear(); + BufferAccessorManager.instance.destroy(); if (this._drawBatchPool) { this._drawBatchPool.destroy(); @@ -158,16 +162,6 @@ export class Batcher2D implements IBatcher { } } - private syncRootNodesToNative (): void { - if (JSB) { - const rootNodes: Node[] = []; - for (const screen of this._screens) { - rootNodes.push(screen.node); - } - this._nativeObj.syncRootNodesToNative(rootNodes); - } - } - /** * @en * Add the managed Canvas. @@ -178,12 +172,9 @@ export class Batcher2D implements IBatcher { * @param comp @en The render root of 2d. * @zh 2d 渲染入口组件。 */ - public addScreen (comp: RenderRoot2D): void { - this._screens.push(comp); + public addScreen (node: Node): void { + this._screens.push(node); this._screens.sort(this._screenSort); - if (JSB) { - this.syncRootNodesToNative(); - } } /** @@ -193,54 +184,29 @@ export class Batcher2D implements IBatcher { * @param comp @en The target to removed. * @zh 被移除的屏幕。 */ - public removeScreen (comp: RenderRoot2D): void { - const idx = this._screens.indexOf(comp); + public removeScreen (node: Node): void { + const idx = this._screens.indexOf(node); if (idx === -1) { return; } this._screens.splice(idx, 1); - if (JSB) { - this.syncRootNodesToNative(); - } } public sortScreens (): void { this._screens.sort(this._screenSort); - if (JSB) { - this.syncRootNodesToNative(); - } - } - - public getFirstRenderCamera (node: Node): Camera | null { - if (node.scene && node.scene.renderScene) { - const cameras = node.scene.renderScene.cameras; - for (let i = 0; i < cameras.length; i++) { - const camera = cameras[i]; - if (camera.visibility & node.layer) { - return camera; - } - } - } - return null; } public update (): void { - if (JSB) { - return; - } const screens = this._screens; let offset = 0; for (let i = 0; i < screens.length; ++i) { const screen = screens[i]; - const scene = screen._getRenderScene(); - if (!screen.enabledInHierarchy || !scene) { - continue; - } + const scene = screen.scene.renderScene!; // Reset state and walk this._opacityDirty = 0; this._pOpacity = 1; - this.walk(screen.node); + this.walk(screen); this.autoMergeBatches(this._currComponent!); this.resetRenderStates(); @@ -265,88 +231,53 @@ export class Batcher2D implements IBatcher { } public uploadBuffers (): void { - if (JSB) { - this._nativeObj.uploadBuffers(); - } else if (this._batches.length > 0) { + if (this._batches.length > 0) { const length = this._meshDataArray.length; for (let i = 0; i < length; i++) { this._meshDataArray[i].uploadBuffers(); } - for (const accessor of this._bufferAccessors.values()) { - accessor.uploadBuffers(); - accessor.reset(); - } - + BufferAccessorManager.instance.upload(); this._descriptorSetCache.update(); } } public reset (): void { - if (JSB) { - this._nativeObj.reset(); - } else { - for (let i = 0; i < this._batches.length; ++i) { - const batch = this._batches.array[i]; - batch.clear(); - this._drawBatchPool.free(batch); - } - // Reset buffer accessors - for (const accessor of this._bufferAccessors.values()) { - accessor.reset(); - } - const length = this._meshDataArray.length; - for (let i = 0; i < length; i++) { - this._meshDataArray[i].freeIAPool(); - } - this._meshDataArray.length = 0; - this._staticVBBuffer = null; - - this._currBID = -1; - this._indexStart = 0; - this._currHash = 0; - this._currLayer = 0; - this._currRenderData = null; - this._currMaterial = this._emptyMaterial; - this._currTexture = null; - this._currSampler = null; - this._currComponent = null; - this._currTransform = null; - this._batches.clear(); - StencilManager.sharedManager!.reset(); + for (let i = 0; i < this._batches.length; ++i) { + const batch = this._batches.array[i]; + batch.clear(); + this._drawBatchPool.free(batch); } - } - - /** - * @zh 如果有必要,为相应的顶点布局切换网格缓冲区。 - * @en Switch the mesh buffer for corresponding vertex layout if necessary. - * @param attributes use VertexFormat.vfmtPosUvColor by default - */ - public switchBufferAccessor (attributes: Attribute[] = vfmtPosUvColor): StaticVBAccessor { - const strideBytes = attributes === vfmtPosUvColor ? 36 /* 9x4 */ : getAttributeStride(attributes); - // If current accessor not compatible with the requested attributes - if (!this._staticVBBuffer || (this._staticVBBuffer.vertexFormatBytes) !== strideBytes) { - let accessor = this._bufferAccessors.get(strideBytes); - if (!accessor) { - accessor = new StaticVBAccessor(this.device, attributes); - this._bufferAccessors.set(strideBytes, accessor); - } - this._staticVBBuffer = accessor; - this._currBID = -1; + BufferAccessorManager.instance.reset(); + const length = this._meshDataArray.length; + for (let i = 0; i < length; i++) { + this._meshDataArray[i].freeIAPool(); } - return this._staticVBBuffer; + this._meshDataArray.length = 0; + + this._indexStart = 0; + this._currHash = 0; + this._currLayer = 0; + this._currRenderData = null; + this._currMaterial = this._emptyMaterial; + this._currTexture = null; + this._currSampler = null; + this._currComponent = null; + this._currTransform = null; + this._batches.clear(); + StencilManager.sharedManager!.reset(); } - public registerBufferAccessor (key: number, accessor: StaticVBAccessor): void { - this._bufferAccessors.set(key, accessor); + public syncMeshBuffersToNative (accId: number, buffers: NativeUIMeshBuffer[]): void { + //sync mesh buffer to naive // Only native need this } public updateBuffer (attributes: Attribute[], bid: number): void { - const accessor = this.switchBufferAccessor(attributes); + const accessor = BufferAccessorManager.instance.switchBufferAccessor(attributes); // If accessor changed, then current bid will be reset to -1, this check will pass too - if (this._currBID !== bid) { - this._currBID = bid; + if (BufferAccessorManager.instance.currBID !== bid) { + BufferAccessorManager.instance.currBID = bid; this._indexStart = accessor.getMeshBuffer(bid).indexOffset; } } @@ -551,7 +482,7 @@ export class Batcher2D implements IBatcher { dssHash = StencilManager.sharedManager!.getStencilHash(comp.stencilStage); } - const stamp = cclegacy.director.getTotalFrames(); + const stamp = cclegacy.director.getTotalFrames() as number; if (model) { model.updateTransform(stamp); model.updateUBOs(stamp); @@ -592,7 +523,7 @@ export class Batcher2D implements IBatcher { } let ia; const rd = this._currRenderData as MeshRenderData; - const accessor = this._staticVBBuffer; + const accessor = BufferAccessorManager.instance.currBufferAccessor; // Previous batch using mesh buffer if (rd && rd._isMeshBuffer) { ia = rd.requestIA(this.device); @@ -601,7 +532,7 @@ export class Batcher2D implements IBatcher { } } else if (accessor) { // Previous batch using static vb buffer - const bid = this._currBID; + const bid = BufferAccessorManager.instance.currBID; const buf = accessor.getMeshBuffer(bid); if (!buf) { return; @@ -617,7 +548,7 @@ export class Batcher2D implements IBatcher { // Update index tracker and bid this._indexStart = buf.indexOffset; } - this._currBID = -1; + BufferAccessorManager.instance.currBID = -1; // Request ia failed if (!ia || !this._currTexture) { @@ -810,17 +741,12 @@ export class Batcher2D implements IBatcher { level += 1; } - private _screenSort (a: RenderRoot2D, b: RenderRoot2D): number { - return a.node.getSiblingIndex() - b.node.getSiblingIndex(); + private _screenSort (a: Node, b: Node): number { + return a.getSiblingIndex() - b.getSiblingIndex(); } - // TODO: Not a good way to do the job - private _releaseDescriptorSetCache (textureHash, sampler = null!): void { - if (JSB) { - this._nativeObj.releaseDescriptorSetCache(textureHash, sampler); - } else { - this._descriptorSetCache.releaseDescriptorSetCache(textureHash); - } + public releaseDescriptorSetCache (texture: TextureBase): void { + this._descriptorSetCache.releaseDescriptorSetCache(texture.getHash()); } // Mask use @@ -874,7 +800,7 @@ export class Batcher2D implements IBatcher { } const model = this._maskClearModel!; - const stamp = cclegacy.director.getTotalFrames(); + const stamp = cclegacy.director.getTotalFrames() as number; if (model) { model.updateTransform(stamp); model.updateUBOs(stamp); @@ -897,14 +823,6 @@ export class Batcher2D implements IBatcher { } _stencilManager.enableMask(); } - - //sync mesh buffer to naive - public syncMeshBuffersToNative (accId: number, buffers: MeshBuffer[]): void { - if (JSB) { - const nativeBuffers = buffers.map((buf) => buf.nativeObj); - this._nativeObj.syncMeshBuffersToNative(accId, nativeBuffers); - } - } } class LocalDescriptorSet { @@ -994,13 +912,11 @@ class LocalDescriptorSet { Mat4.invert(m4_1, worldMatrix); Mat4.transpose(m4_1, m4_1); - if (!JSB) { - // fix precision lost of webGL on android device - // scale worldIT mat to around 1.0 by product its sqrt of determinant. - const det = Mat4.determinant(m4_1); - const factor = 1.0 / Math.sqrt(det); - Mat4.multiplyScalar(m4_1, m4_1, factor); - } + // fix precision lost of webGL on android device + // scale worldIT mat to around 1.0 by product its sqrt of determinant. + const det = Mat4.determinant(m4_1); + const factor = 1.0 / Math.sqrt(det); + Mat4.multiplyScalar(m4_1, m4_1, factor); Mat4.toArray(this._localData, m4_1, UBOLocal.MAT_WORLD_IT_OFFSET); this._localBuffer!.update(this._localData); this._transformUpdate = false; diff --git a/cocos/2d/renderer/buffer-accessor-manager.ts b/cocos/2d/renderer/buffer-accessor-manager.ts new file mode 100644 index 00000000000..7e00bff039e --- /dev/null +++ b/cocos/2d/renderer/buffer-accessor-manager.ts @@ -0,0 +1,97 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + + https://www.cocos.com/ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import { director } from '../../game/director'; +import { Attribute } from '../../gfx/base/define'; +import { StaticVBAccessor } from './static-vb-accessor'; +import { getAttributeStride, vfmtPosUvColor } from './vertex-format'; + +export class BufferAccessorManager { + public static instance: BufferAccessorManager; + + get currBufferAccessor (): StaticVBAccessor | null { + return this._staticVBBuffer; + } + + get currBID (): number { + return this._currBID; + } + set currBID (val) { + this._currBID = val; + } + + private _bufferAccessors: Map = new Map(); + private _staticVBBuffer: StaticVBAccessor | null = null; + private _currBID = -1; + + /** + * @zh 如果有必要,为相应的顶点布局切换网格缓冲区。 + * @en Switch the mesh buffer for corresponding vertex layout if necessary. + * @param attributes use VertexFormat.vfmtPosUvColor by default + */ + public switchBufferAccessor (attributes: Attribute[] = vfmtPosUvColor): StaticVBAccessor { + const strideBytes = attributes === vfmtPosUvColor ? 36 /* 9x4 */ : getAttributeStride(attributes); + // If current accessor not compatible with the requested attributes + if (!this._staticVBBuffer || (this._staticVBBuffer.vertexFormatBytes) !== strideBytes) { + let accessor = this._bufferAccessors.get(strideBytes); + if (!accessor) { + accessor = new StaticVBAccessor(director.root!.device, attributes); + this._bufferAccessors.set(strideBytes, accessor); + } + + this._staticVBBuffer = accessor; + this._currBID = -1; + } + return this._staticVBBuffer; + } + + // Only Web need this + public registerBufferAccessor (key: number, accessor: StaticVBAccessor): void { + this._bufferAccessors.set(key, accessor); + } + + public destroy (): void { + for (const accessor of this._bufferAccessors.values()) { + accessor.destroy(); + } + this._bufferAccessors.clear(); + } + + public upload (): void { + for (const accessor of this._bufferAccessors.values()) { + accessor.uploadBuffers(); + accessor.reset(); + } + } + + public reset (): void { + for (const accessor of this._bufferAccessors.values()) { + accessor.reset(); + } + this._staticVBBuffer = null; + this._currBID = -1; + } +} + +BufferAccessorManager.instance = new BufferAccessorManager(); \ No newline at end of file diff --git a/cocos/2d/renderer/i-batcher.ts b/cocos/2d/renderer/i-batcher.ts index 9d9d53328e1..e036921c887 100644 --- a/cocos/2d/renderer/i-batcher.ts +++ b/cocos/2d/renderer/i-batcher.ts @@ -28,8 +28,7 @@ import { Device, Attribute } from '../../gfx'; import { Camera } from '../../render-scene/scene/camera'; import { Model } from '../../render-scene/scene/model'; import { SpriteFrame } from '../assets/sprite-frame'; -import { UIRenderer, RenderRoot2D } from '../framework'; -import { StaticVBAccessor } from './static-vb-accessor'; +import { UIRenderer } from '../framework'; import { DrawBatch2D } from './draw-batch'; import { BaseRenderData } from './render-data'; import { UIMeshRenderer } from '../components/ui-mesh-renderer'; @@ -37,7 +36,6 @@ import { Material } from '../../asset/assets'; import { Node } from '../../scene-graph'; export interface IBatcher { - currBufferAccessor: StaticVBAccessor; readonly batches: memop.CachedArray; // registerCustomBuffer (attributes: MeshBuffer | Attribute[], callback: ((...args: number[]) => void) | null) : MeshBuffer; // unRegisterCustomBuffer (buffer: MeshBuffer); @@ -47,9 +45,8 @@ export interface IBatcher { initialize(): boolean; destroy(); - addScreen (comp: RenderRoot2D); - getFirstRenderCamera (node: Node): Camera | null; - removeScreen (comp: RenderRoot2D); + addScreen (node: Node); + removeScreen (node: Node); sortScreens (); @@ -57,8 +54,6 @@ export interface IBatcher { uploadBuffers (); reset (); - switchBufferAccessor (attributes?: Attribute[]): StaticVBAccessor; - commitComp (comp: UIRenderer, renderData: BaseRenderData|null, frame: TextureBase | SpriteFrame | null, assembler: any, transform: Node | null); commitModel (comp: UIMeshRenderer | UIRenderer, model: Model | null, mat: Material | null); diff --git a/cocos/2d/renderer/native-2d-empty.ts b/cocos/2d/renderer/native-2d-empty.ts index 7a0f0dd81d0..ea3618c0ab8 100644 --- a/cocos/2d/renderer/native-2d-empty.ts +++ b/cocos/2d/renderer/native-2d-empty.ts @@ -24,7 +24,6 @@ export const NativeRenderDrawInfo = undefined; -export const NativeBatcher2d = undefined; export const NativeUIMeshBuffer = undefined; export const NativeRenderEntity = undefined; export const NativeUIModelProxy = undefined; diff --git a/cocos/2d/renderer/native-2d.jsb.ts b/cocos/2d/renderer/native-2d.jsb.ts index 62385341d3f..1c5e590e1f6 100644 --- a/cocos/2d/renderer/native-2d.jsb.ts +++ b/cocos/2d/renderer/native-2d.jsb.ts @@ -23,7 +23,6 @@ */ import type { NativeRenderDrawInfo as N2dNativeRenderDrawInfo, - NativeBatcher2d as N2dNativeBatcher2d, NativeUIMeshBuffer as N2dNativeUIMeshBuffer, NativeRenderEntity as N2dNativeRenderEntity, NativeUIModelProxy as N2dNativeUIModelProxy, @@ -35,8 +34,6 @@ declare const n2d: any; export const NativeRenderDrawInfo: typeof N2dNativeRenderDrawInfo = n2d.RenderDrawInfo; export type NativeRenderDrawInfo = N2dNativeRenderDrawInfo; -export const NativeBatcher2d: typeof N2dNativeBatcher2d = n2d.Batcher2d; -export type NativeBatcher2d = N2dNativeBatcher2d; export const NativeUIMeshBuffer: typeof N2dNativeUIMeshBuffer = n2d.UIMeshBuffer; export type NativeUIMeshBuffer = N2dNativeUIMeshBuffer; export const NativeRenderEntity: typeof N2dNativeRenderEntity = n2d.RenderEntity; diff --git a/cocos/2d/renderer/native-2d.ts b/cocos/2d/renderer/native-2d.ts index c6cabde2724..29c76b6ac10 100644 --- a/cocos/2d/renderer/native-2d.ts +++ b/cocos/2d/renderer/native-2d.ts @@ -104,15 +104,6 @@ export declare class NativeUIMeshBuffer { uploadBuffers(); } -export declare class NativeBatcher2d { - syncMeshBuffersToNative(accId: number, buffers: NativeUIMeshBuffer[]); - update(); - uploadBuffers(); - reset(); - syncRootNodesToNative(nodes: Node[]); - releaseDescriptorSetCache(texture: Texture, sampler: Sampler); -} - export declare class NativeUIModelProxy { initModel(node); activeSubModels(); diff --git a/cocos/2d/renderer/render-data.ts b/cocos/2d/renderer/render-data.ts index c1e3ef7d258..2f56b992c18 100644 --- a/cocos/2d/renderer/render-data.ts +++ b/cocos/2d/renderer/render-data.ts @@ -36,9 +36,10 @@ import { StaticVBAccessor, StaticVBChunk } from './static-vb-accessor'; import { getAttributeStride, vfmtPosUvColor } from './vertex-format'; import { Buffer, BufferInfo, BufferUsageBit, Device, Attribute, InputAssembler, InputAssemblerInfo, MemoryUsageBit } from '../../gfx'; import { RenderDrawInfo, RenderDrawInfoType } from './render-draw-info'; -import { Batcher2D } from './batcher-2d'; import { RenderEntity, RenderEntityType } from './render-entity'; import type { MeshBuffer } from './mesh-buffer'; +import { uiSystem } from '../framework/ui-system'; +import { BufferAccessorManager } from './buffer-accessor-manager'; /** * @deprecated since v3.5.0, this is an engine private interface that will be removed in the future. @@ -127,14 +128,6 @@ export class BaseRenderData { this._multiOwner = val; } - protected _batcher: Batcher2D | null = null; - get batcher (): Batcher2D { - if (!this._batcher) { - this._batcher = director.root!.batcher2D; - } - return this._batcher; - } - constructor (vertexFormat = vfmtPosUvColor) { this._floatStride = vertexFormat === vfmtPosUvColor ? DEFAULT_STRIDE : (getAttributeStride(vertexFormat) >> 2); this._vertexFormat = vertexFormat; @@ -216,11 +209,6 @@ export class BaseRenderData { export class RenderData extends BaseRenderData { public static add (vertexFormat = vfmtPosUvColor, accessor?: StaticVBAccessor): RenderData { const rd = new RenderData(vertexFormat, accessor); - if (!accessor) { - const batcher = director.root!.batcher2D; - accessor = batcher.switchBufferAccessor(rd._vertexFormat); - } - rd._accessor = accessor; return rd; } @@ -317,7 +305,7 @@ export class RenderData extends BaseRenderData { public constructor (vertexFormat = vfmtPosUvColor, accessor?: StaticVBAccessor) { super(vertexFormat); if (!accessor) { - accessor = this.batcher.switchBufferAccessor(this._vertexFormat); + accessor = BufferAccessorManager.instance.switchBufferAccessor(this._vertexFormat); } this._accessor = accessor; } diff --git a/cocos/2d/renderer/static-vb-accessor.ts b/cocos/2d/renderer/static-vb-accessor.ts index 68bd35b0aa2..4153fab2930 100644 --- a/cocos/2d/renderer/static-vb-accessor.ts +++ b/cocos/2d/renderer/static-vb-accessor.ts @@ -29,8 +29,8 @@ import { memop } from '@base/utils'; import { Device, Attribute } from '../../gfx'; import { MeshBuffer } from './mesh-buffer'; import { BufferAccessor } from './buffer-accessor'; +import { uiSystem } from '../framework/ui-system'; import { macro } from '../../core'; -import { director } from '../../game'; interface IFreeEntry { offset: number; @@ -307,8 +307,10 @@ export class StaticVBAccessor extends BufferAccessor { //sync to native // temporarily batcher transports buffers // It is better to put accessor to native - const batcher = director.root!.batcher2D; - batcher.syncMeshBuffersToNative(this.id, this._buffers); + if (JSB) { + const nativeBuffers = this._buffers.map((buf) => buf.nativeObj); + uiSystem.batcher2D.syncMeshBuffersToNative(this.id, nativeBuffers); + } return this._buffers.length - 1; } diff --git a/cocos/asset/assets/texture-base.jsb.ts b/cocos/asset/assets/texture-base.jsb.ts index bc88071b720..d466e223fbf 100644 --- a/cocos/asset/assets/texture-base.jsb.ts +++ b/cocos/asset/assets/texture-base.jsb.ts @@ -124,10 +124,6 @@ textureBaseProto.getSamplerInfo = function () { const oldDestroy = textureBaseProto.destroy; textureBaseProto.destroy = function () { - if (cclegacy.director.root?.batcher2D) { - // cclegacy.director.root.batcher2D._releaseDescriptorSetCache(this.getHash()); - cclegacy.director.root.batcher2D._releaseDescriptorSetCache(this.getGFXTexture(), this.getGFXSampler()); - } // dispatch into C++ virtual function CCObject::destroy return oldDestroy.call(this); }; diff --git a/cocos/asset/assets/texture-base.ts b/cocos/asset/assets/texture-base.ts index d618c91ea4e..e8593ccfd10 100644 --- a/cocos/asset/assets/texture-base.ts +++ b/cocos/asset/assets/texture-base.ts @@ -260,8 +260,8 @@ export class TextureBase extends Asset { */ public destroy (): boolean { const destroyed = super.destroy(); - if (destroyed && cclegacy.director.root?.batcher2D) { - cclegacy.director.root.batcher2D._releaseDescriptorSetCache(this._textureHash); + if (destroyed && cclegacy.internal.uiSystem.batcher2D) { + cclegacy.internal.uiSystem.batcher2D.releaseDescriptorSetCache(this); } return destroyed; } diff --git a/cocos/dragon-bones/assembler/simple.ts b/cocos/dragon-bones/assembler/simple.ts index a350202f563..c9bd574252a 100644 --- a/cocos/dragon-bones/assembler/simple.ts +++ b/cocos/dragon-bones/assembler/simple.ts @@ -24,6 +24,7 @@ import { Armature, BlendMode } from '@cocos/dragonbones-js'; import { cclegacy } from '@base/global'; +import { JSB } from 'internal:constants'; import { Color, Mat4, Vec3 } from '../../core'; import { BlendFactor } from '../../gfx'; import { vfmtPosUvColor } from '../../2d/renderer/vertex-format'; @@ -39,6 +40,8 @@ import { Texture2D } from '../../asset/assets'; import { TextureBase } from '../../asset/assets/texture-base'; import { Node } from '../../scene-graph'; import { director } from '../../game'; +import { uiSystem } from '../../2d/framework/ui-system'; +import { BufferAccessorManager } from '../../2d/renderer/buffer-accessor-manager'; const NEED_COLOR = 0x01; const NEED_BATCH = 0x10; @@ -135,11 +138,12 @@ export const simple: IAssembler = { ensureAccessor (): StaticVBAccessor { if (!_accessor) { const device = director.root!.device; - const batcher = director.root!.batcher2D; const attributes = vfmtPosUvColor; this.accessor = _accessor = new StaticVBAccessor(device, attributes, this.vCount); - // Register to batcher so that batcher can upload buffers after batching process - batcher.registerBufferAccessor(Number.parseInt('DRAGONBONES', 36), _accessor); + if (!JSB) { + // Register to batcher so that batcher can upload buffers after batching process + BufferAccessorManager.instance.registerBufferAccessor(Number.parseInt('DRAGONBONES', 36), _accessor); + } } return this.accessor as StaticVBAccessor; }, diff --git a/cocos/game/director.ts b/cocos/game/director.ts index 5f43630cfd0..675a5e9ae1d 100644 --- a/cocos/game/director.ts +++ b/cocos/game/director.ts @@ -39,7 +39,6 @@ import { Root } from '../root'; import { Node, Scene } from '../scene-graph'; import { ComponentScheduler } from '../scene-graph/component-scheduler'; import NodeActivator from '../scene-graph/node-activator'; -import { uiRendererManager } from '../2d/framework/ui-renderer-manager'; import { assetManager } from '../asset/asset-manager'; import { deviceManager } from '../gfx'; import { releaseManager } from '../asset/asset-manager/release-manager'; @@ -178,6 +177,13 @@ export class Director extends EventTarget { */ public static readonly EVENT_END_FRAME = 'director_end_frame'; + /** + * @en The event which will be triggered at the time that UI system update. + * @zh UI系统开始更新时的事件。 + * @event Director.EVENT_UPDATE_UI + */ + public static readonly EVENT_UPDATE_UI = 'director_update_ui'; + public static instance: Director; /** @@ -512,8 +518,12 @@ export class Director extends EventTarget { const bundle = assetManager.bundles.find((bundle): boolean => !!bundle.getSceneInfo(sceneName)); if (bundle) { // NOTE: the similar function signatures but defined as deferent function types. - bundle.preloadScene(sceneName, null, onProgress as (finished: number, total: number, item: any) => void, - onLoaded as ((err?: Error | null) => void) | null); + bundle.preloadScene( + sceneName, + null, + onProgress as (finished: number, total: number, item: any) => void, + onLoaded as ((err?: Error | null) => void) | null, + ); } else { const err = `Can not preload the scene "${sceneName}" because it is not in the build settings.`; if (onLoaded) { @@ -736,8 +746,9 @@ export class Director extends EventTarget { } } + this.emit(Director.EVENT_UPDATE_UI); + this.emit(Director.EVENT_BEFORE_DRAW); - uiRendererManager.updateAllDirtyRenderers(); this._root!.frameMove(dt); this.emit(Director.EVENT_AFTER_DRAW); diff --git a/cocos/root.jsb.ts b/cocos/root.jsb.ts index e6308543a0b..a469c09d8eb 100644 --- a/cocos/root.jsb.ts +++ b/cocos/root.jsb.ts @@ -56,26 +56,6 @@ export interface IRootInfo { const rootProto: any = Root.prototype; -rootProto._createBatcher2D = function () { - if (!this._batcher && cclegacy.internal.Batcher2D) { - this._batcher = new cclegacy.internal.Batcher2D(this); - if (!this._batcher!.initialize()) { - this._batcher = null; - this.destroy(); - return; - } - this._batcher._nativeObj = this.getBatcher2D(); - } -} - -Object.defineProperty(rootProto, 'batcher2D', { - configurable: true, - enumerable: true, - get() { - return this._batcher; - }, -}); - Object.defineProperty(rootProto, 'dataPoolManager', { configurable: true, enumerable: true, @@ -107,7 +87,6 @@ rootProto._ctor = function (device: Device) { this._dataPoolMgr = cclegacy.internal.DataPoolManager && new cclegacy.internal.DataPoolManager(device) as DataPoolManager; this._modelPools = new Map(); this._lightPools = new Map(); - this._batcher = null; this._pipelineEvent = new DummyPipelineEvent(); this._registerListeners(); }; @@ -256,7 +235,6 @@ rootProto.setRenderPipeline = function (pipeline) { } ppl = oldSetPipeline.call(this, pipeline); } - this._createBatcher2D(); return ppl; } diff --git a/cocos/root.ts b/cocos/root.ts index eecacd79cc3..16d900ae68d 100644 --- a/cocos/root.ts +++ b/cocos/root.ts @@ -42,7 +42,7 @@ import { RangedDirectionalLight } from './render-scene/scene/ranged-directional- import { RenderWindow, IRenderWindowInfo } from './render-scene/core/render-window'; import { ColorAttachment, DepthStencilAttachment, RenderPassInfo, StoreOp, Device, Swapchain, Feature, deviceManager, LegacyRenderMode } from './gfx'; import { BasicPipeline, PipelineRuntime } from './rendering/custom/pipeline'; -import { Batcher2D } from './2d/renderer/batcher-2d'; +import type { Batcher2D } from './2d/renderer/batcher-2d'; import { IPipelineEvent } from './rendering/pipeline-event'; import { localDescriptorSetLayout_ResizeMaxJoints, UBOCamera, UBOGlobal, UBOLocal, UBOShadow, UBOWorldBound } from './rendering/define'; import { XREye, XRPoseType } from './xr/xr-enums'; @@ -153,9 +153,10 @@ export class Root { /** * @en The draw batch manager for 2D UI, for engine internal usage, user do not need to use this. * @zh 2D UI 渲染合批管理器,引擎内部使用,用户无需使用此接口 + * @deprecated Since v3.9.0, this is an engine private interface that will be removed in the future. */ public get batcher2D (): Batcher2D { - return this._batcher as Batcher2D; + return cclegacy.internal.uiSystem.batcher2D as Batcher2D; } /** @@ -261,7 +262,6 @@ export class Root { private _pipelineEvent: IPipelineEvent | null = null; private _classicPipeline: RenderPipeline | null = null; private _customPipeline: BasicPipeline | null = null; - private _batcher: Batcher2D | null = null; private _dataPoolMgr: DataPoolManager; private _scenes: RenderScene[] = []; private _modelPools = new Map, memop.Pool>(); @@ -338,11 +338,6 @@ export class Root { this._pipelineEvent = null; } - if (this._batcher) { - this._batcher.destroy(); - this._batcher = null; - } - this._curWindow = null; this._mainWindow = null; this.dataPoolManager.clear(); @@ -434,13 +429,6 @@ export class Root { } this.onGlobalPipelineStateChanged(); - if (!this._batcher && internal.Batcher2D) { - this._batcher = new internal.Batcher2D(this); - if (!this._batcher!.initialize()) { - this.destroy(); - return false; - } - } return true; } @@ -804,11 +792,6 @@ export class Root { const scenes = this._scenes; const stamp = director.getTotalFrames() as number; - if (this._batcher) { - this._batcher.update(); - this._batcher.uploadBuffers(); - } - for (let i = 0; i < scenes.length; i++) { scenes[i].update(stamp); } @@ -830,8 +813,6 @@ export class Root { director.emit(Director.EVENT_AFTER_RENDER); this._device.present(); } - - if (this._batcher) this._batcher.reset(); } private _resizeMaxJointForDS (): void { diff --git a/cocos/spine/assembler/simple.ts b/cocos/spine/assembler/simple.ts index 799ea133090..3d5e9bf442b 100644 --- a/cocos/spine/assembler/simple.ts +++ b/cocos/spine/assembler/simple.ts @@ -23,6 +23,7 @@ */ import { cclegacy } from '@base/global'; +import { JSB } from 'internal:constants'; import { UIRenderable } from '../../2d'; import { IAssembler } from '../../2d/renderer/base'; @@ -36,6 +37,7 @@ import { RenderData } from '../../2d/renderer/render-data'; import { director } from '../../game'; import spine from '../lib/spine-core.js'; import { MaterialInstance } from '../../render-scene'; +import { BufferAccessorManager } from '../../2d/renderer/buffer-accessor-manager'; const _slotColor = new Color(0, 0, 255, 255); const _boneColor = new Color(255, 0, 0, 255); @@ -95,16 +97,19 @@ export const simple: IAssembler = { let accessor = useTint ? _tintAccessor : _accessor; if (!accessor) { const device = director.root!.device; - const batcher = director.root!.batcher2D; const attributes = useTint ? vfmtPosUvTwoColor4B : vfmtPosUvColor4B; if (useTint) { - accessor = _tintAccessor = new StaticVBAccessor(device, attributes, this.vCount as number); - // Register to batcher so that batcher can upload buffers after batching process - batcher.registerBufferAccessor(Number.parseInt('SPINETINT', 36), _tintAccessor); + accessor = _tintAccessor = new StaticVBAccessor(device, attributes, this.vCount); + if (!JSB) { + // Register to batcher so that batcher can upload buffers after batching process + BufferAccessorManager.instance.registerBufferAccessor(Number.parseInt('SPINETINT', 36), _tintAccessor); + } } else { - accessor = _accessor = new StaticVBAccessor(device, attributes, this.vCount as number); + accessor = _accessor = new StaticVBAccessor(device, attributes, this.vCount); + if (!JSB) { // Register to batcher so that batcher can upload buffers after batching process - batcher.registerBufferAccessor(Number.parseInt('SPINE', 36), _accessor); + BufferAccessorManager.instance.registerBufferAccessor(Number.parseInt('SPINE', 36), _accessor); + } } } return accessor; diff --git a/cocos/tiledmap/assembler/simple.ts b/cocos/tiledmap/assembler/simple.ts index ab5269a6900..e0ed4948781 100644 --- a/cocos/tiledmap/assembler/simple.ts +++ b/cocos/tiledmap/assembler/simple.ts @@ -56,8 +56,8 @@ let _moveX = 0; let _moveY = 0; let _fillCount = 0; -let _curTexture : Texture2D | null = null; -let _tempBuffers : Float32Array; +let _curTexture: Texture2D | null = null; +let _tempBuffers: Float32Array; let _curLayer: TiledLayer; let flipTexture: (grid: TiledGrid, gid: MixedGID) => void; @@ -71,10 +71,9 @@ export const simple: IAssembler = { ensureAccessor () { if (!_accessor) { const device = director.root!.device; - const batcher = director.root!.batcher2D; _accessor = new StaticVBAccessor(device, vfmtPosUvColor, this.vCount); //batcher.registerBufferAccessor(Number.parseInt('TILED-MAP', 36), _accessor); - director.on(Director.EVENT_BEFORE_DRAW, () => { + director.on(Director.EVENT_AFTER_UPDATE, () => { _accessor.reset(); }); } @@ -325,8 +324,13 @@ function packRenderData (): void { // rowMoveDir is -1 or 1, -1 means decrease, 1 means increase // colMoveDir is -1 or 1, -1 means decrease, 1 means increase -function traverseGrids (leftDown: { col: number, row: number }, rightTop: { col: number, row: number }, - rowMoveDir: number, colMoveDir: number, comp: TiledLayer): void { +function traverseGrids ( + leftDown: { col: number, row: number }, + rightTop: { col: number, row: number }, + rowMoveDir: number, + colMoveDir: number, + comp: TiledLayer, +): void { // show nothing if (rightTop.row < 0 || rightTop.col < 0) return; @@ -535,8 +539,16 @@ function traverseGrids (leftDown: { col: number, row: number }, rightTop: { col: packRenderData(); } -function fillByTiledNode (tiledNode: Node, color: Float32Array, vbuf: Float32Array, - left: number, right: number, top: number, bottom: number, diamondTile: boolean): void { +function fillByTiledNode ( + tiledNode: Node, + color: Float32Array, + vbuf: Float32Array, + left: number, + right: number, + top: number, + bottom: number, + diamondTile: boolean, +): void { const vertStep = 9; const vertStep2 = vertStep * 2; const vertStep3 = vertStep * 3; diff --git a/cocos/tiledmap/tiled-layer.ts b/cocos/tiledmap/tiled-layer.ts index 3c50618d2c8..7cb181c01b8 100644 --- a/cocos/tiledmap/tiled-layer.ts +++ b/cocos/tiledmap/tiled-layer.ts @@ -42,8 +42,8 @@ import { NodeEventType } from '../scene-graph/node-event'; import { RenderEntity, RenderEntityType } from '../2d/renderer/render-entity'; import { RenderDrawInfo, RenderDrawInfoType } from '../2d/renderer/render-draw-info'; import { Texture2D } from '../asset/assets'; -import { director } from '../game'; import { Camera } from '../render-scene/scene'; +import { Batcher2D } from '../2d/renderer/batcher-2d'; const _mat4_temp = new Mat4(); const _vec2_temp = new Vec2(); @@ -108,14 +108,14 @@ export class TiledLayer extends UIRenderer { row: number; col: number; }; - } { return this._cullingRect; } + } { return this._cullingRect; } protected _cullingDirty = true; protected _rightTop = { row: -1, col: -1 }; get rightTop (): { row: number; col: number; - } { return this._rightTop; } + } { return this._rightTop; } protected _layerInfo: TMXLayerInfo | null = null; protected _mapInfo: TMXMapInfo | null = null; @@ -368,7 +368,7 @@ export class TiledLayer extends UIRenderer { const rowData = this._userNodeGrid[row]; const colData = rowData && rowData[col]; if (colData) { - rowData!.count--; + rowData.count--; colData.count--; colData.list[index] = null; if (colData.count <= 0) { @@ -416,7 +416,7 @@ export class TiledLayer extends UIRenderer { } protected _reinstallCamera (): Camera | null { - const camera = director.root!.batcher2D.getFirstRenderCamera(this.node); + const camera = Batcher2D.getFirstRenderCamera(this.node); const cameraNode = camera?.node; if (this._cameraNode !== cameraNode) { this._uninstallCamera(); diff --git a/cocos/ui/editbox/edit-box-impl.ts b/cocos/ui/editbox/edit-box-impl.ts index 2e0815b9fde..978bd98720b 100644 --- a/cocos/ui/editbox/edit-box-impl.ts +++ b/cocos/ui/editbox/edit-box-impl.ts @@ -28,7 +28,6 @@ import { screenAdapter } from 'pal/screen-adapter'; import { ccwindow } from '@base/global'; import { BitmapFont } from '../../2d/assets'; -import { director } from '../../game/director'; import { game } from '../../game'; import { Mat4, Vec3, visibleRect, sys } from '../../core'; import { view } from '../view'; @@ -40,6 +39,7 @@ import { tabIndexUtil } from './tabIndexUtil'; import { InputFlag, InputMode, KeyboardReturnType } from './types'; import { EditBoxImplBase } from './edit-box-impl-base'; import { BrowserType, OS } from '../../../pal/system-info/enum-type'; +import { Batcher2D } from '../../2d/renderer/batcher-2d'; const ccdocument = ccwindow.document; @@ -287,7 +287,7 @@ export class EditBoxImpl extends EditBoxImplBase { return; } - const camera = director.root!.batcher2D.getFirstRenderCamera(node); + const camera = Batcher2D.getFirstRenderCamera(node); if (!camera) return; camera.node.getWorldRT(_matrix_temp); diff --git a/cocos/ui/scroll-view.ts b/cocos/ui/scroll-view.ts index b32782c453c..9cfcd0cf354 100644 --- a/cocos/ui/scroll-view.ts +++ b/cocos/ui/scroll-view.ts @@ -973,7 +973,7 @@ export class ScrollView extends ViewGroup { // Because widget component will adjust content position and scrollView position is correct after visit // So this event could make sure the content is on the correct position after loading. if (this._content) { - director.once(Director.EVENT_BEFORE_DRAW, this._adjustContentOutOfBoundary, this); + director.once(Director.EVENT_AFTER_UPDATE, this._adjustContentOutOfBoundary, this); } } diff --git a/cocos/video/video-player-impl.ts b/cocos/video/video-player-impl.ts index 6aeef0b4661..941894a0a8f 100644 --- a/cocos/video/video-player-impl.ts +++ b/cocos/video/video-player-impl.ts @@ -27,9 +27,9 @@ import { error } from '@base/debug'; import { UITransform } from '../2d/framework'; import { VideoPlayer } from './video-player'; import { EventType } from './video-player-enums'; -import { director } from '../game/director'; import { Node } from '../scene-graph'; import type { Camera } from '../render-scene/scene'; +import { Batcher2D } from '../2d/renderer/batcher-2d'; export abstract class VideoPlayerImpl { protected _componentEventList: Map void> = new Map(); @@ -127,7 +127,7 @@ export abstract class VideoPlayerImpl { public get state (): EventType { return this._state; } public get isPlaying (): boolean { return this._playing; } get UICamera (): Camera | null { - return director.root!.batcher2D.getFirstRenderCamera(this._node!); + return Batcher2D.getFirstRenderCamera(this._node!); } // video player event diff --git a/cocos/web-view/web-view-impl.ts b/cocos/web-view/web-view-impl.ts index 85e9886601b..ef130580dfb 100644 --- a/cocos/web-view/web-view-impl.ts +++ b/cocos/web-view/web-view-impl.ts @@ -26,9 +26,9 @@ import { cclegacy } from '@base/global'; import { WebView } from './web-view'; import { EventType } from './web-view-enums'; import { UITransform } from '../2d/framework'; -import { director } from '../game/director'; import { Node } from '../scene-graph'; import type { Camera } from '../render-scene/scene'; +import { Batcher2D } from '../2d/renderer/batcher-2d'; export abstract class WebViewImpl { protected _componentEventList: Map void> = new Map(); @@ -92,7 +92,7 @@ export abstract class WebViewImpl { get webview (): HTMLIFrameElement | null { return this._webview; } get state (): EventType { return this._state; } get UICamera (): Camera | null { - return director.root!.batcher2D.getFirstRenderCamera(this._node!); + return Batcher2D.getFirstRenderCamera(this._node!); } protected dispatchEvent (key: EventType, ...args: any[any]): void { diff --git a/native/cocos/2d/renderer/Batcher2d.cpp b/native/cocos/2d/renderer/Batcher2d.cpp index 8e05a432868..f115edd506c 100644 --- a/native/cocos/2d/renderer/Batcher2d.cpp +++ b/native/cocos/2d/renderer/Batcher2d.cpp @@ -29,10 +29,22 @@ #include "core/scene-graph/Scene.h" #include "editor-support/MiddlewareManager.h" #include "renderer/pipeline/Define.h" +#include "scene/Camera.h" #include "scene/Pass.h" namespace cc { +namespace { +Batcher2d* instance = nullptr; +} + +Batcher2d* Batcher2d::getInstance() { + if (instance == nullptr) { + instance = new Batcher2d(); + } + return instance; +} + Batcher2d::Batcher2d() : Batcher2d(nullptr) { } @@ -47,27 +59,7 @@ Batcher2d::Batcher2d(Root* root) } Batcher2d::~Batcher2d() { // NOLINT - _drawBatchPool.destroy(); - - for (auto iter : _descriptorSetCache) { - delete iter.second; - } - - for (auto* drawBatch : _batches) { - delete drawBatch; - } - _attributes.clear(); - - if (_maskClearModel != nullptr) { - Root::getInstance()->destroyModel(_maskClearModel); - _maskClearModel = nullptr; - } - if (_maskModelMesh != nullptr) { - _maskModelMesh->destroy(); - _maskModelMesh = nullptr; - } - _maskClearMtl = nullptr; - _maskAttributes.clear(); + destroy(); } void Batcher2d::syncMeshBuffersToNative(uint16_t accId, ccstd::vector&& buffers) { @@ -464,15 +456,18 @@ gfx::DescriptorSet* Batcher2d::getDescriptorSet(gfx::Texture* texture, gfx::Samp return ds; } -void Batcher2d::releaseDescriptorSetCache(gfx::Texture* texture, gfx::Sampler* sampler) { +void Batcher2d::releaseDescriptorSetCache(TextureBase* texture) { + CC_ASSERT(texture); + auto *const textureGFX = texture->getGFXTexture(); + auto *const samplerGFX = texture->getGFXSampler(); ccstd::hash_t hash = 2; size_t textureHash; - if (texture != nullptr) { - textureHash = boost::hash_value(texture); + if (textureGFX != nullptr) { + textureHash = boost::hash_value(textureGFX); ccstd::hash_combine(hash, textureHash); } - if (sampler != nullptr) { - ccstd::hash_combine(hash, sampler->getHash()); + if (samplerGFX != nullptr) { + ccstd::hash_combine(hash, samplerGFX->getHash()); } auto iter = _descriptorSetCache.find(hash); if (iter != _descriptorSetCache.end()) { @@ -542,6 +537,61 @@ void Batcher2d::reset() { // stencilManager } +void Batcher2d::destroy() { + _drawBatchPool.destroy(); + + for (auto iter : _descriptorSetCache) { + delete iter.second; + } + + for (auto* drawBatch : _batches) { + delete drawBatch; + } + _attributes.clear(); + + if (_maskClearModel != nullptr) { + Root::getInstance()->destroyModel(_maskClearModel); + _maskClearModel = nullptr; + } + if (_maskModelMesh != nullptr) { + _maskModelMesh->destroy(); + _maskModelMesh = nullptr; + } + _maskClearMtl = nullptr; + _maskAttributes.clear(); +} + +scene::Camera* Batcher2d::getFirstRenderCamera(const Node* node) { + if (node->getScene()!= nullptr && node->getScene()->getRenderScene()!= nullptr) { + const auto cameras = node->getScene()->getRenderScene()->getCameras(); + for (size_t i = 0; i < cameras.size(); i++) { + const auto camera = cameras[i]; + if (camera->getVisibility() & node->getLayer()) { + return camera; + } + } + } + return nullptr; +} + + +void Batcher2d::addScreen(Node* node) { + _rootNodeArr.push_back(node); + std::sort(_rootNodeArr.begin(), _rootNodeArr.end(), Batcher2d::screenSort); +} + +void Batcher2d::removeScreen(Node* node) { + auto iter = std::find(_rootNodeArr.begin(), _rootNodeArr.end(), node); + if (iter != _rootNodeArr.end()) { + _rootNodeArr.erase(iter); + } + std::sort(_rootNodeArr.begin(), _rootNodeArr.end(), Batcher2d::screenSort); +} + +void Batcher2d::sortScreen() { + std::sort(_rootNodeArr.begin(), _rootNodeArr.end(), Batcher2d::screenSort); +} + void Batcher2d::insertMaskBatch(RenderEntity* entity) { generateBatch(_currEntity, _currDrawInfo); resetRenderStates(); diff --git a/native/cocos/2d/renderer/Batcher2d.h b/native/cocos/2d/renderer/Batcher2d.h index 18acf6f0506..65159a518c2 100644 --- a/native/cocos/2d/renderer/Batcher2d.h +++ b/native/cocos/2d/renderer/Batcher2d.h @@ -36,12 +36,16 @@ #include "scene/DrawBatch2D.h" namespace cc { +namespace scene { +class Camera; +} class Root; using UIMeshBufferArray = ccstd::vector; using UIMeshBufferMap = ccstd::unordered_map; class Batcher2d final { public: +static Batcher2d *getInstance(); Batcher2d(); explicit Batcher2d(Root* root); ~Batcher2d(); @@ -52,9 +56,16 @@ class Batcher2d final { void update(); void uploadBuffers(); void reset(); + void destroy(); + + static scene::Camera* getFirstRenderCamera(const Node* node); + + void addScreen (Node* node); + void removeScreen (Node* node); + void sortScreen (); void syncRootNodesToNative(ccstd::vector&& rootNodes); - void releaseDescriptorSetCache(gfx::Texture* texture, gfx::Sampler* sampler); + void releaseDescriptorSetCache(TextureBase* texture); UIMeshBuffer* getMeshBuffer(uint16_t accId, uint16_t bufferId); gfx::Device* getDevice(); @@ -134,6 +145,10 @@ class Batcher2d final { } } + static inline size_t screenSort (Node* nodeA, Node* nodeB) { + return nodeA->getSiblingIndex() > nodeB->getSiblingIndex(); + } + void insertMaskBatch(RenderEntity* entity); void createClearModel(); diff --git a/native/cocos/2d/renderer/RenderDrawInfo.cpp b/native/cocos/2d/renderer/RenderDrawInfo.cpp index 94397e3c382..dc219cb6803 100644 --- a/native/cocos/2d/renderer/RenderDrawInfo.cpp +++ b/native/cocos/2d/renderer/RenderDrawInfo.cpp @@ -45,8 +45,8 @@ RenderDrawInfo::~RenderDrawInfo() { } void RenderDrawInfo::changeMeshBuffer() { - CC_ASSERT(Root::getInstance()->getBatcher2D()); - _meshBuffer = Root::getInstance()->getBatcher2D()->getMeshBuffer(_drawInfoAttrs._accId, _drawInfoAttrs._bufferId); + CC_ASSERT(Batcher2d::getInstance()); + _meshBuffer = Batcher2d::getInstance()->getMeshBuffer(_drawInfoAttrs._accId, _drawInfoAttrs._bufferId); } gfx::InputAssembler* RenderDrawInfo::requestIA(gfx::Device* device) { @@ -100,7 +100,7 @@ gfx::InputAssembler* RenderDrawInfo::initIAInfo(gfx::Device* device) { ibStride, }); - iaInfo.attributes = *(Root::getInstance()->getBatcher2D()->getDefaultAttribute()); + iaInfo.attributes = *(Batcher2d::getInstance()->getDefaultAttribute()); iaInfo.vertexBuffers.emplace_back(_vb); iaInfo.indexBuffer = _ib; diff --git a/native/cocos/core/Root.cpp b/native/cocos/core/Root.cpp index b8d84d399c4..a4035d4bf2a 100644 --- a/native/cocos/core/Root.cpp +++ b/native/cocos/core/Root.cpp @@ -1,4 +1,4 @@ -/**************************************************************************** + /**************************************************************************** Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd. http://www.cocos.com @@ -23,7 +23,6 @@ ****************************************************************************/ #include "core/Root.h" -#include "2d/renderer/Batcher2d.h" #include "application/ApplicationManager.h" #include "bindings/event/EventDispatcher.h" #include "pipeline/custom/RenderingModule.h" @@ -158,8 +157,6 @@ void Root::destroy() { CC_SAFE_DESTROY_NULL(_pipeline); - CC_SAFE_DELETE(_batcher); - for (auto *swapchain : _swapchains) { CC_SAFE_DELETE(swapchain); } @@ -343,14 +340,6 @@ bool Root::setRenderPipeline(pipeline::RenderPipeline *rppl /* = nullptr*/) { onGlobalPipelineStateChanged(); - if (_batcher == nullptr) { - _batcher = ccnew Batcher2d(this); - if (!_batcher->initialize()) { - destroy(); - return false; - } - } - return true; } @@ -386,10 +375,6 @@ void Root::frameMoveBegin() { scene->removeBatches(); } - if (_batcher != nullptr) { - _batcher->update(); - } - // _cameraList.clear(); } @@ -405,10 +390,6 @@ void Root::frameMoveProcess(bool isNeedUpdateScene, int32_t totalFrames) { // NOTE: c++ doesn't have a Director, so totalFrames need to be set from JS uint32_t stamp = totalFrames; - if (_batcher != nullptr) { - _batcher->uploadBuffers(); - } - if (isNeedUpdateScene) { for (const auto &scene : _scenes) { scene->update(stamp); @@ -444,10 +425,6 @@ void Root::frameMoveEnd() { #endif _device->present(); } - - if (_batcher != nullptr) { - _batcher->reset(); - } } void Root::frameMove(float deltaTime, int32_t totalFrames) { // NOLINT diff --git a/native/cocos/core/Root.h b/native/cocos/core/Root.h index 5020f11e2d9..e2fd919205d 100644 --- a/native/cocos/core/Root.h +++ b/native/cocos/core/Root.h @@ -53,7 +53,6 @@ namespace render { class PipelineRuntime; class Pipeline; } // namespace render -class Batcher2d; struct ISystemWindowInfo; class ISystemWindow; @@ -233,13 +232,6 @@ class Root final { */ render::Pipeline *getCustomPipeline() const; - /** - * @zh - * UI实例 - * 引擎内部使用,用户无需调用此接口 - */ - inline Batcher2d *getBatcher2D() const { return _batcher; } - /** * @zh * 场景列表 @@ -307,7 +299,6 @@ class Root final { gfx::Device *_device{nullptr}; gfx::Swapchain *_swapchain{nullptr}; - Batcher2d *_batcher{nullptr}; IntrusivePtr _mainRenderWindow; IntrusivePtr _curRenderWindow; IntrusivePtr _tempWindow; diff --git a/native/cocos/core/assets/TextureBase.cpp b/native/cocos/core/assets/TextureBase.cpp index 0c549c2733a..4c57c77b48b 100644 --- a/native/cocos/core/assets/TextureBase.cpp +++ b/native/cocos/core/assets/TextureBase.cpp @@ -31,6 +31,7 @@ #include "renderer/pipeline/Define.h" #include "base/std/hash/hash.h" +#include "2d/renderer/Batcher2d.h" namespace cc { @@ -107,9 +108,9 @@ void TextureBase::setAnisotropy(uint32_t anisotropy) { bool TextureBase::destroy() { const bool destroyed = Super::destroy(); - //cjh TODO: if (destroyed && cclegacy.director.root?.batcher2D) { - // cclegacy.director.root.batcher2D._releaseDescriptorSetCache(this._textureHash); - // } + if (destroyed && Batcher2d::getInstance() != nullptr) { + Batcher2d::getInstance()->releaseDescriptorSetCache(this); + } return destroyed; } diff --git a/native/cocos/editor-support/MiddlewareManager.cpp b/native/cocos/editor-support/MiddlewareManager.cpp index cad1376e50e..ad0a2aeef7b 100644 --- a/native/cocos/editor-support/MiddlewareManager.cpp +++ b/native/cocos/editor-support/MiddlewareManager.cpp @@ -121,7 +121,7 @@ void MiddlewareManager::render(float dt) { } uint16_t accID = 65534; - auto *batch2d = Root::getInstance()->getBatcher2D(); + auto *batch2d = Batcher2d::getInstance(); if (it.first == VF_XYZUVCC) { accID = 65535; } diff --git a/native/tools/swig-config/2d.i b/native/tools/swig-config/2d.i index 81bb62dec2f..37ac250d2b3 100644 --- a/native/tools/swig-config/2d.i +++ b/native/tools/swig-config/2d.i @@ -105,6 +105,7 @@ // 1. 'Rename Section' should be placed before attribute definition and %import/%include // 2. namespace is needed +%rename(Batcher2D) cc::Batcher2d; // ----- Module Macro Section ------ // Brief: Generated code should be wrapped inside a macro @@ -189,6 +190,8 @@ %import "2d/renderer/StencilManager.h" %import "math/Color.h" +%import "core/assets/TextureBase.h" + // ----- Include Section ------ // Brief: Include header files in which classes and methods will be bound %include "2d/renderer/UIMeshBuffer.h" diff --git a/tests/ui/sprite.test.ts b/tests/ui/sprite.test.ts index b05a02d2ee8..5fb5083afeb 100644 --- a/tests/ui/sprite.test.ts +++ b/tests/ui/sprite.test.ts @@ -2,13 +2,17 @@ import { Canvas, SpriteFrame, UITransform } from "../../cocos/2d"; import { Sprite, UIOpacity } from "../../cocos/2d/components"; import { Texture2D } from "../../cocos/asset/assets"; import { Node } from "../../cocos/scene-graph/node"; -import { Camera, Scene, Size, Vec3, director, game } from "../../exports/base"; +import { Camera, Director, Scene, Size, Vec3, director, game } from "../../exports/base"; import { Batcher2D } from "../../cocos/2d/renderer/batcher-2d"; +import { uiSystem } from "../../cocos/2d/framework/ui-system"; test('sprite.updateWorldMatrix', () => { // @ts-expect-error - director.root!._batcher = new Batcher2D(director.root!); + uiSystem._batcher = new Batcher2D(director.root!); + director.on(Director.EVENT_UPDATE_UI, uiSystem.tick, uiSystem); + director.on(Director.EVENT_AFTER_DRAW, uiSystem.afterDraw, uiSystem); + director.on(Director.EVENT_BEFORE_COMMIT, uiSystem.render, uiSystem); const scene = new Scene('test'); director.runSceneImmediate(scene);