From 67b8d4b953621445f6f1d8350edff76c9a8d5167 Mon Sep 17 00:00:00 2001 From: minggo Date: Fri, 23 Aug 2024 14:41:20 +0800 Subject: [PATCH 01/21] rename event name to fix event name conflict with NodeEventType.MOUSE_ENTER/MOUSE_LEAVE (#17576) --- cocos/input/types/event-enum.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocos/input/types/event-enum.ts b/cocos/input/types/event-enum.ts index 40c0ecb1e8f..e8b8d52e216 100644 --- a/cocos/input/types/event-enum.ts +++ b/cocos/input/types/event-enum.ts @@ -343,14 +343,14 @@ export enum InputEventType { * trigger this event. * @zh 当鼠标离开窗口或者 canvas 时发出该消息。只有 Windows、macOS 或者 PC web 会触发该事件。 */ - MOUSE_LEAVE = 'mouse-leave', + MOUSE_LEAVE = 'mouse-leave-window', /** * @en The event type indicates mouse enters the window or canvas. Only Windows, macOS or web PC can * trigger this event. * @zh 当鼠标进入窗口或者 canvas 时发出该消息。只有 Windows、macOS 或者 PC web 会触发该事件。 */ - MOUSE_ENTER = 'mouse-enter', + MOUSE_ENTER = 'mouse-enter-window', /** * @en From 582e03aaf1e830a61efb18f12325516655884145 Mon Sep 17 00:00:00 2001 From: bofeng-song Date: Tue, 27 Aug 2024 10:11:26 +0800 Subject: [PATCH 02/21] Fix the issue where the cached animationCache object is using a released skeleton object due to asset release. (#17581) --- cocos/spine/skeleton-cache.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cocos/spine/skeleton-cache.ts b/cocos/spine/skeleton-cache.ts index f3e539f8c24..f9ada2e52f9 100644 --- a/cocos/spine/skeleton-cache.ts +++ b/cocos/spine/skeleton-cache.ts @@ -358,6 +358,9 @@ class SkeletonCache { } } + const skeletonInfo = this._skeletonCache[assetUuid]; + if (!skeletonInfo) return; + const sharedOperate = (aniKey: string, animationCache: AnimationCache): void => { this._animationPool[`${assetUuid}#${aniKey}`] = animationCache; animationCache.clear(); @@ -366,9 +369,6 @@ class SkeletonCache { animationCache.destroy(); }; const operate = this._privateMode ? privateOperate : sharedOperate; - - const skeletonInfo = this._skeletonCache[assetUuid]; - if (!skeletonInfo) return; const animationsCache = skeletonInfo.animationsCache; for (const aniKey in animationsCache) { // Clear cache texture, and put cache into pool. @@ -466,6 +466,14 @@ class SkeletonCache { delete animationPool[key]; } } + let skeletonInfo = this._skeletonCache[uuid]; + const skeleton = skeletonInfo && skeletonInfo.skeleton; + if (skeleton) { + spine.wasmUtil.destroySpineSkeleton(skeleton); + } + if (skeletonInfo) { + delete this._skeletonCache[uuid]; + } } else { const animationPool = this._animationPool; for (const key in animationPool) { From 9a4693e8f2a114bc59bd79a17b0ed971b2211f27 Mon Sep 17 00:00:00 2001 From: GengineJS <476393671@qq.com> Date: Thu, 29 Aug 2024 11:29:55 +0800 Subject: [PATCH 03/21] Fix the issue where post-processing modifications after rendering are ineffective. (#17586) * Fix the issue where post-processing modifications after rendering are ineffective. * Removes the intensity edit slot for bloom effect --- cocos/rendering/custom/define.ts | 27 +++++-------------- cocos/rendering/custom/executor.ts | 2 +- .../builtin-pipeline-settings.ts | 6 ----- 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/cocos/rendering/custom/define.ts b/cocos/rendering/custom/define.ts index 57d6f8ce916..d1fab1136e6 100644 --- a/cocos/rendering/custom/define.ts +++ b/cocos/rendering/custom/define.ts @@ -448,16 +448,7 @@ export function getDescBindingFromName (bindingName: string): number { return -1; } -const uniformMap: Map = new Map(); -const buffHashMap: Map = new Map(); -function numsHash (arr: number[]): number { - let hash = 0; - for (let i = 0; i < arr.length; i++) { - hash = hashCombineNum(arr[i], hash); - } - return hash; -} - +const uniformMap: Map = new Map(); class DescBuffManager { private buffers: Buffer[] = []; private currBuffIdx: number = 0; @@ -484,20 +475,14 @@ class DescBuffManager { updateBuffer (bindId: number, vals: number[], layout: string, setData: DescriptorSetData): void { const descriptorSet = setData.descriptorSet!; const buffer = this.getCurrentBuffer(); - const uniformKey = `${layout}${bindId}${this.currBuffIdx}`; - let currUniform = uniformMap.get(uniformKey); - const currHash = numsHash(vals); + let currUniform = uniformMap.get(buffer); if (!currUniform) { currUniform = new Float32Array(vals); - uniformMap.set(uniformKey, currUniform); - } - const destHash = buffHashMap.get(buffer); - if (destHash !== currHash) { - currUniform.set(vals); - buffer.update(currUniform); - bindGlobalDesc(descriptorSet, bindId, buffer); - buffHashMap.set(buffer, currHash); + uniformMap.set(buffer, currUniform); } + currUniform.set(vals); + buffer.update(currUniform); + bindGlobalDesc(descriptorSet, bindId, buffer); } } diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index 2f0f5d06214..3ee34800fce 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -1995,7 +1995,7 @@ class PreRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor { if (!this.rg.getValid(this.sceneID)) return; const renderQueue = this.currQueue as DeviceRenderQueue; const graphScene = context.pools.addGraphScene(); - graphScene.init(null, value, -1); + graphScene.init(null, value, this.sceneID); const renderScene = renderQueue.addScene(graphScene); renderScene.preRecord(); } diff --git a/editor/assets/default_renderpipeline/builtin-pipeline-settings.ts b/editor/assets/default_renderpipeline/builtin-pipeline-settings.ts index 8b02a4affa5..2f3fd0e0e12 100644 --- a/editor/assets/default_renderpipeline/builtin-pipeline-settings.ts +++ b/editor/assets/default_renderpipeline/builtin-pipeline-settings.ts @@ -327,12 +327,6 @@ export class BuiltinPipelineSettings extends Component { return this._settings.bloom.threshold; } - @property({ - tooltip: 'i18n:bloom.intensity', - group: { id: 'Bloom', name: 'Bloom (PostProcessing)', style: 'section' }, - type: CCFloat, - min: 0, - }) set bloomIntensity(value: number) { this._settings.bloom.intensity = value; } From 6fe9ff7261bf2d3f61e6312d76c90cf07a32f156 Mon Sep 17 00:00:00 2001 From: hyde zhou Date: Thu, 29 Aug 2024 11:30:41 +0800 Subject: [PATCH 04/21] fix reflection probe bake and planar reflection error in editor (#17584) --- cocos/rendering/custom/executor.ts | 1 + cocos/rendering/custom/scene-culling.ts | 2 +- cocos/rendering/custom/web-pipeline-types.ts | 20 +++++++++++-------- .../builtin-pipeline.ts | 6 ++++++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index 3ee34800fce..8338a7985f4 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -1446,6 +1446,7 @@ class DeviceRenderScene implements RecordingInterface { const renderQueueDesc = sceneCulling.renderQueueIndex.get(this.graphScene.sceneID)!; const renderQueue = sceneCulling.renderQueues[renderQueueDesc.renderQueueTarget]; const graphSceneData = this.graphScene.scene!; + if (bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE)) renderQueue.probeQueue.applyMacro(); renderQueue.recordCommands(context.commandBuffer, this._renderPass); if (bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE)) renderQueue.probeQueue.removeMacro(); if (graphSceneData.flags & SceneFlags.GEOMETRY) { diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index 8a0ceeeaaf5..40354f01797 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -254,7 +254,7 @@ function addRenderObject ( ): void { const probeQueue = queue.probeQueue; if (isDrawProbe) { - probeQueue.applyMacro(model, phaseLayoutId); + probeQueue.addToProbeQueue(model, phaseLayoutId); } const subModels = model.subModels; const subModelCount = subModels.length; diff --git a/cocos/rendering/custom/web-pipeline-types.ts b/cocos/rendering/custom/web-pipeline-types.ts index 66a357cc1e3..7bbc9be648a 100644 --- a/cocos/rendering/custom/web-pipeline-types.ts +++ b/cocos/rendering/custom/web-pipeline-types.ts @@ -225,6 +225,17 @@ export class ProbeHelperQueue { this.probeMap.length = 0; } + applyMacro (): void { + for (const subModel of this.probeMap) { + let patches: IMacroPatch[] = [ + { name: CC_USE_RGBE_OUTPUT, value: true }, + ]; + if (subModel.patches) { + patches = patches.concat(subModel.patches); + } + subModel.onMacroPatchesStateChanged(patches); + } + } removeMacro (): void { for (const subModel of this.probeMap) { if (!subModel.patches) continue; @@ -238,7 +249,7 @@ export class ProbeHelperQueue { } } } - applyMacro (model: Model, probeLayoutId: number): void { + addToProbeQueue (model: Model, probeLayoutId: number): void { const subModels = model.subModels; for (let j = 0; j < subModels.length; j++) { const subModel: SubModel = subModels[j]; @@ -258,13 +269,6 @@ export class ProbeHelperQueue { } if (passIdx < 0) { continue; } if (!bUseReflectPass) { - let patches: IMacroPatch[] = []; - patches = patches.concat(subModel.patches!); - const useRGBEPatchs: IMacroPatch[] = [ - { name: CC_USE_RGBE_OUTPUT, value: true }, - ]; - patches = patches.concat(useRGBEPatchs); - subModel.onMacroPatchesStateChanged(patches); this.probeMap.push(subModel); } } diff --git a/editor/assets/default_renderpipeline/builtin-pipeline.ts b/editor/assets/default_renderpipeline/builtin-pipeline.ts index 25e82886d65..1ee29f85794 100644 --- a/editor/assets/default_renderpipeline/builtin-pipeline.ts +++ b/editor/assets/default_renderpipeline/builtin-pipeline.ts @@ -154,6 +154,7 @@ class CameraConfigs { enableFXAA = false; enableFSR = false; enableHDR = false; + enablePlanarReflectionProbe = false; useFullPipeline = false; singleForwardRadiancePass = false; radianceFormat = gfx.Format.RGBA8; @@ -212,6 +213,8 @@ function setupCameraConfigs( && !!camera.scene.mainLight && camera.scene.mainLight.shadowEnabled; + cameraConfigs.enablePlanarReflectionProbe = isMainGameWindow || camera.cameraUsage === CameraUsage.SCENE_VIEW; + cameraConfigs.enableProfiler = DEBUG && isMainGameWindow; cameraConfigs.settings = camera.pipelineSettings @@ -1506,6 +1509,9 @@ if (rendering) { const height = Math.max(Math.floor(area.y), 1); if (probe.probeType === renderer.scene.ProbeType.PLANAR) { + if (!this._cameraConfigs.enablePlanarReflectionProbe) { + continue; + } const window: renderer.RenderWindow = probe.realtimePlanarTexture!.window!; const colorName = `PlanarProbeRT${probeID}`; const depthStencilName = `PlanarProbeDS${probeID}`; From 3ad291ac10da1b2189687385176dd3ced065e823 Mon Sep 17 00:00:00 2001 From: GengineJS <476393671@qq.com> Date: Tue, 3 Sep 2024 14:01:38 +0800 Subject: [PATCH 05/21] [Optimized] upload buffer (#17595) * [Optimized] upload buffer * Dual buffer --- cocos/rendering/custom/define.ts | 65 +++++++++++++++++------------- cocos/rendering/custom/executor.ts | 11 ++++- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/cocos/rendering/custom/define.ts b/cocos/rendering/custom/define.ts index d1fab1136e6..119dd7eccdd 100644 --- a/cocos/rendering/custom/define.ts +++ b/cocos/rendering/custom/define.ts @@ -448,15 +448,17 @@ export function getDescBindingFromName (bindingName: string): number { return -1; } -const uniformMap: Map = new Map(); class DescBuffManager { private buffers: Buffer[] = []; private currBuffIdx: number = 0; private device: Device; + public currUniform: Float32Array; + private _root; constructor (bufferSize: number, numBuffers: number = 2) { - const root = cclegacy.director.root; + const root = this._root = cclegacy.director.root; const device = root.device; this.device = device; + this.currUniform = new Float32Array(bufferSize / 4); for (let i = 0; i < numBuffers; i++) { const bufferInfo: BufferInfo = new BufferInfo( BufferUsageBit.UNIFORM | BufferUsageBit.TRANSFER_DST, @@ -468,36 +470,33 @@ class DescBuffManager { } } getCurrentBuffer (): Buffer { - const root = cclegacy.director.root; - this.currBuffIdx = root.frameCount % this.buffers.length; + this.currBuffIdx = this._root.frameCount % this.buffers.length; return this.buffers[this.currBuffIdx]; } - updateBuffer (bindId: number, vals: number[], layout: string, setData: DescriptorSetData): void { + updateData (vals: number[]): void { + this.currUniform.set(vals); + } + updateBuffer (bindId: number, setData: DescriptorSetData): void { const descriptorSet = setData.descriptorSet!; const buffer = this.getCurrentBuffer(); - let currUniform = uniformMap.get(buffer); - if (!currUniform) { - currUniform = new Float32Array(vals); - uniformMap.set(buffer, currUniform); - } - currUniform.set(vals); - buffer.update(currUniform); + buffer.update(this.currUniform); bindGlobalDesc(descriptorSet, bindId, buffer); } } const buffsMap: Map = new Map(); - -function updateGlobalDescBuffer (blockId: number, sceneId: number, vals: number[], layout: string, setData: DescriptorSetData): void { +const currBindBuffs: Map = new Map(); +function updateGlobalDescBuffer (blockId: number, sceneId: number, idxRD: number, vals: number[], setData: DescriptorSetData): void { const bindId = getDescBinding(blockId, setData); if (bindId === -1) { return; } - const descKey = `${blockId}${bindId}${sceneId}`; + const descKey = `${blockId}${bindId}${idxRD}${sceneId}`; + currBindBuffs.set(descKey, bindId); let currDescBuff = buffsMap.get(descKey); if (!currDescBuff) { - buffsMap.set(descKey, new DescBuffManager(vals.length * 4)); + buffsMap.set(descKey, new DescBuffManager(vals.length * 4, 2)); currDescBuff = buffsMap.get(descKey); } - currDescBuff!.updateBuffer(bindId, vals, layout, setData); + currDescBuff!.updateData(vals); } const layouts: Map = new Map(); @@ -522,8 +521,8 @@ export function getDescriptorSetDataFromLayoutId (id: number): DescriptorSetData return layoutData; } -export function updateGlobalDescBinding (data: RenderData, sceneId: number, layoutName = 'default'): void { - updatePerPassUBO(layoutName, sceneId, data); +export function updateGlobalDescBinding (data: RenderData, sceneId: number, idxRD: number, layoutName = 'default'): void { + updatePerPassUBO(layoutName, sceneId, idxRD, data); } function getUniformBlock (block: string, layoutName: string): UniformBlock | undefined { @@ -557,15 +556,20 @@ class ConstantBlockInfo { blockId: number = -1; } const constantBlockMap: Map = new Map(); -function copyToConstantBuffer (target: number[], val: number[], offset: number): void { +function copyToConstantBuffer (target: number[], val: number[], offset: number): boolean { + let isImparity = false; if (offset < 0 || offset > target.length) { - throw new Error('Offset is out of the bounds of the target array.'); + return isImparity; } const length = Math.min(val.length, target.length - offset); for (let i = 0; i < length; i++) { - target[offset + i] = val[i]; + if (target[offset + i] !== val[i]) { + target[offset + i] = val[i]; + isImparity = true; + } } + return isImparity; } function addConstantBuffer (block: string, layout: string): number[] | null { @@ -587,7 +591,7 @@ function addConstantBuffer (block: string, layout: string): number[] | null { uniformBlockMap.set(block, buffers); return buffers; } -export function updatePerPassUBO (layout: string, sceneId: number, user: RenderData): void { +export function updatePerPassUBO (layout: string, sceneId: number, idxRD: number, user: RenderData): void { const constantMap = user.constants; const samplers = user.samplers; const textures = user.textures; @@ -595,6 +599,7 @@ export function updatePerPassUBO (layout: string, sceneId: number, user: RenderD const webPip = cclegacy.director.root.pipeline as WebPipeline; const lg = webPip.layoutGraph; const descriptorSetData = getDescriptorSetDataFromLayout(layout)!; + currBindBuffs.clear(); for (const [key, data] of constantMap) { let constantBlock = constantBlockMap.get(key); if (!constantBlock) { @@ -607,7 +612,7 @@ export function updatePerPassUBO (layout: string, sceneId: number, user: RenderD if (offset === -1) { // Although the current uniformMem does not belong to the current uniform block, // it does not mean that it should not be bound to the corresponding descriptor. - updateGlobalDescBuffer(blockId, sceneId, constantBuff, layout, descriptorSetData); + updateGlobalDescBuffer(blockId, sceneId, idxRD, constantBuff, descriptorSetData); continue; } constantBlockMap.set(key, new ConstantBlockInfo()); @@ -615,12 +620,12 @@ export function updatePerPassUBO (layout: string, sceneId: number, user: RenderD constantBlock.buffer = constantBuff; constantBlock.blockId = blockId; constantBlock.offset = offset; - copyToConstantBuffer(constantBuff, data, offset); - updateGlobalDescBuffer(blockId, sceneId, constantBuff, layout, descriptorSetData); + const isImparity = copyToConstantBuffer(constantBuff, data, offset); + if (isImparity) updateGlobalDescBuffer(blockId, sceneId, idxRD, constantBuff, descriptorSetData); } } else { - copyToConstantBuffer(constantBlock.buffer, data, constantBlock.offset); - updateGlobalDescBuffer(constantBlock.blockId, sceneId, constantBlock.buffer, layout, descriptorSetData); + const isImparity = copyToConstantBuffer(constantBlock.buffer, data, constantBlock.offset); + if (isImparity) updateGlobalDescBuffer(constantBlock.blockId, sceneId, idxRD, constantBlock.buffer, descriptorSetData); } } @@ -643,6 +648,10 @@ export function updatePerPassUBO (layout: string, sceneId: number, user: RenderD bindGlobalDesc(descriptorSet, bindId, value); } } + for (const [key, value] of currBindBuffs) { + const buffManager = buffsMap.get(key)!; + buffManager.updateBuffer(value, descriptorSetData); + } for (const [key, value] of buffers) { const bindId = getDescBinding(key, descriptorSetData); if (bindId === -1) { continue; } diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index 8338a7985f4..ce2fbf3efb2 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -808,6 +808,7 @@ class DeviceRenderPass implements RecordingInterface { protected _viewport: Viewport | null = null; private _rasterInfo: RasterPassInfo; private _layout: RenderPassLayoutInfo | null = null; + private _idxOfRenderData: number = 0; constructor (passInfo: RasterPassInfo) { this._rasterInfo = passInfo; const device = context.device; @@ -909,6 +910,7 @@ class DeviceRenderPass implements RecordingInterface { swapchain ? swapchain.depthStencilTexture : depthTex, )); } + get indexOfRD (): number { return this._idxOfRenderData; } get layoutName (): string { return this._layoutName; } get passID (): number { return this._passID; } get renderLayout (): RenderPassLayoutInfo | null { return this._layout; } @@ -920,6 +922,9 @@ class DeviceRenderPass implements RecordingInterface { get deviceQueues (): Map { return this._deviceQueues; } get rasterPassInfo (): RasterPassInfo { return this._rasterInfo; } get viewport (): Viewport | null { return this._viewport; } + addIdxOfRD (): void { + this._idxOfRenderData++; + } visitResource (resName: string): void { const resourceGraph = context.resourceGraph; const vertId = resourceGraph.vertex(resName); @@ -1040,6 +1045,7 @@ class DeviceRenderPass implements RecordingInterface { this._layoutName = context.renderGraph.getLayout(id); this._passID = cclegacy.rendering.getPassID(this._layoutName); this._deviceQueues.clear(); + this._idxOfRenderData = 0; let framebuffer: Framebuffer | null = null; const colTextures: Texture[] = []; const currFramebuffer = this._framebuffer; @@ -1234,7 +1240,7 @@ class DeviceComputePass implements RecordingInterface { queue.record(); } const renderData = context.renderGraph.getData(this._computeInfo.id); - updateGlobalDescBinding(renderData, -1, context.renderGraph.getLayout(this._computeInfo.id)); + updateGlobalDescBinding(renderData, -1, 0, context.renderGraph.getLayout(this._computeInfo.id)); } resetResource (id: number, pass: ComputePass): void { @@ -1392,7 +1398,8 @@ class DeviceRenderScene implements RecordingInterface { protected _updateGlobal (data: RenderData, sceneId: number): void { const devicePass = this._currentQueue.devicePass; - updateGlobalDescBinding(data, sceneId, context.renderGraph.getLayout(devicePass.rasterPassInfo.id)); + devicePass.addIdxOfRD(); + updateGlobalDescBinding(data, sceneId, devicePass.indexOfRD, context.renderGraph.getLayout(devicePass.rasterPassInfo.id)); } protected _updateRenderData (): void { From a900c7cb568d16fac8db93a007912257fbb0bf12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AA=9B=E5=AA=9B?= Date: Tue, 3 Sep 2024 14:03:04 +0800 Subject: [PATCH 06/21] update i18n for render pipeline (#17596) --- editor/i18n/en/localization.js | 12 ++++++------ editor/i18n/zh/localization.js | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/editor/i18n/en/localization.js b/editor/i18n/en/localization.js index 9d5c57fdf69..7dc087d9334 100755 --- a/editor/i18n/en/localization.js +++ b/editor/i18n/en/localization.js @@ -1105,16 +1105,16 @@ module.exports = link(mixin({ description: "Enable the XR function system", }, custom_pipeline: { - label: "Custom Render Pipeline (Beta)", - description: "Enable custom render pipeline", + label: "Render Pipeline (New)", + description: "The new render pipeline is based on data-oriented render graph. Users can write cross platform render pipelines and optimize for the specific platform.", }, custom_pipeline_post_process: { - label: "Custom Render Pipeline Post Process (Deprecated)", - description: "Enable custom render pipeline post process", + label: "Post Process Module (Deprecated)", + description: "The render pipeline used in previous versions. This pipeline will be deprecated in future versions.", }, legacy_pipeline: { - label: "Legacy Render Pipeline (Deprecated)", - description: "Enable legacy render pipeline", + label: "Render Pipeline (Legacy)", + description: "The render pipeline used in previous versions. This pipeline will be deprecated in future versions.", }, websocket: { label: "WebSocket", diff --git a/editor/i18n/zh/localization.js b/editor/i18n/zh/localization.js index 151b8869a65..c1e84f49f52 100755 --- a/editor/i18n/zh/localization.js +++ b/editor/i18n/zh/localization.js @@ -1080,16 +1080,16 @@ module.exports = link(mixin({ description: "启用 XR 功能系统。", }, custom_pipeline: { - label: "自定义渲染管线 (Beta)", - description: "启用自定义渲染管线。", + label: "新渲染管线", + description: "面向数据的Render Graph渲染管线,可以自由构建跨平台的渲染算法,并针对目标平台优化。", }, custom_pipeline_post_process: { - label: "自定义渲染管线的后处理模块 (Deprecated)", - description: "启用自定义渲染管线的后处理模块", + label: "后处理模块(已废弃)", + description: "此选项用于兼容旧项目(Custom管线),新项目请使用 Builtin 自带的后处理流程。", }, legacy_pipeline: { - label: "废弃渲染管线 (Deprecated)", - description: "启用废弃渲染管线。", + label: "原渲染管线", + description: "原有的渲染管线,在后续的版本中会被移除。", }, websocket: { label: "WebSocket", From 3a5898d48839f8ceb2d0d23b422f2113eff5207b Mon Sep 17 00:00:00 2001 From: hyde zhou Date: Tue, 3 Sep 2024 18:10:20 +0800 Subject: [PATCH 07/21] revert changes during pipeline replacing (#17597) --- .../post-process/color-grading.effect | 30 +++++++------------ .../effects/pipeline/post-process/fsr.effect | 10 +++++++ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/editor/assets/effects/pipeline/post-process/color-grading.effect b/editor/assets/effects/pipeline/post-process/color-grading.effect index 88eefd6ee33..38195b66a06 100644 --- a/editor/assets/effects/pipeline/post-process/color-grading.effect +++ b/editor/assets/effects/pipeline/post-process/color-grading.effect @@ -8,6 +8,11 @@ CCEffect %{ pass: color-grading-nx1 rasterizerState: &rasterizerState cullMode: none + blendState: &blendState + targets: + - blend: false + blendSrc: one + blendDst: zero depthStencilState: &depthStencilState depthTest: false depthWrite: false @@ -16,6 +21,7 @@ CCEffect %{ pass: color-grading-8x8 rasterizerState: *rasterizerState depthStencilState: *depthStencilState + blendState: *blendState }% CCProgram ubo %{ @@ -35,8 +41,6 @@ CCProgram color-grading-vs %{ CCProgram color-grading-nx1-fs %{ precision highp float; - #include - #include #include in vec2 v_uv; @@ -58,20 +62,13 @@ CCProgram color-grading-nx1-fs %{ layout(location = 0) out vec4 fragColor; void main () { vec4 sceneColor = texture(sceneColorMap, v_uv); - // (Tone mapping + gamma correction) + Color grading - #if CC_USE_FLOAT_OUTPUT - sceneColor.rgb = HDRToLDR(sceneColor.rgb); - sceneColor.rgb = LinearToSRGB(sceneColor.rgb); - #endif vec3 gradeColor = ColorGrading(colorGradingMap, sceneColor.rgb); - fragColor = mix(sceneColor, vec4(gradeColor, sceneColor.a), contribute); + fragColor = mix(sceneColor, vec4(gradeColor, 1.0), contribute); } }% CCProgram color-grading-8x8-fs %{ precision highp float; - #include - #include #include in vec2 v_uv; #define EPS 0.000001 @@ -79,23 +76,18 @@ CCProgram color-grading-8x8-fs %{ #define SIZE 512.0 layout(location = 0) out vec4 fragColor; void main () { - vec4 origColor = texture(sceneColorMap, v_uv); - // (Tone mapping + gamma correction) + Color grading - #if CC_USE_FLOAT_OUTPUT - origColor.rgb = HDRToLDR(origColor.rgb); - origColor.rgb = LinearToSRGB(origColor.rgb); - #endif - float bValue = (origColor.b * 255.0) / 4.0; + vec3 orgColor = texture(sceneColorMap, v_uv).rgb; + float bValue = (orgColor.b * 255.0) / 4.0; vec2 mulB = clamp(floor(bValue) + vec2(0.0, 1.0), 0.0, 63.0); vec2 row = floor(mulB / 8.0 + EPS); vec4 row_col = vec4(row, mulB - row * 8.0); - vec4 lookup = origColor.ggrr * (63.0 / SIZE) + row_col * (TOTAL / SIZE) + (0.5 / SIZE); + vec4 lookup = orgColor.ggrr * (63.0 / SIZE) + row_col * (TOTAL / SIZE) + (0.5 / SIZE); float b1w = bValue - mulB.x; vec3 sampled1 = texture(colorGradingMap, lookup.zx).rgb; vec3 sampled2 = texture(colorGradingMap, lookup.wy).rgb; - fragColor = vec4(mix(origColor.rgb, mix(sampled1, sampled2, b1w), contribute), origColor.a); + fragColor = vec4(mix(orgColor, mix(sampled1, sampled2, b1w), contribute), 1.0); } }% diff --git a/editor/assets/effects/pipeline/post-process/fsr.effect b/editor/assets/effects/pipeline/post-process/fsr.effect index 1b0e5766334..5a088ede8c5 100644 --- a/editor/assets/effects/pipeline/post-process/fsr.effect +++ b/editor/assets/effects/pipeline/post-process/fsr.effect @@ -11,6 +11,11 @@ CCEffect %{ depthStencilState: depthTest: false depthWrite: false + blendState: + targets: + - blend: true + blendSrc: one + blendDst: zero - vert: vs frag: fs-rcas pass: post-process @@ -19,6 +24,11 @@ CCEffect %{ depthStencilState: depthTest: false depthWrite: false + blendState: + targets: + - blend: true + blendSrc: one + blendDst: zero }% From 93f859f822278b42e8b0f9e91d6e6a595a089849 Mon Sep 17 00:00:00 2001 From: Knox Date: Wed, 4 Sep 2024 14:26:42 +0800 Subject: [PATCH 08/21] Fix custom-pipeline-post-process enable failures (#17601) Co-authored-by: cocos --- editor/engine-features/render-config.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/editor/engine-features/render-config.json b/editor/engine-features/render-config.json index 0f12efc36f1..cd974d1c154 100644 --- a/editor/engine-features/render-config.json +++ b/editor/engine-features/render-config.json @@ -275,6 +275,13 @@ "enginePlugin": false, "category": "animation" }, + "custom-pipeline-post-process": { + "default": false, + "label": "i18n:ENGINE.features.custom_pipeline_post_process.label", + "description": "i18n:ENGINE.features.custom_pipeline_post_process.description", + "enginePlugin": false, + "hidden": true + }, "render-pipeline": { "label": "i18n:ENGINE.features.render-pipeline.label", "description": "i18n:ENGINE.features.render-pipeline.description", From ef7616299fc7e834d4b32fc4ddbe1c88390c8887 Mon Sep 17 00:00:00 2001 From: GengineJS <476393671@qq.com> Date: Wed, 4 Sep 2024 14:28:02 +0800 Subject: [PATCH 09/21] Fix the issue where the "Custom" pipeline cannot use post-processing in WebGL 1.0. (#17600) --- cocos/rendering/custom/define.ts | 57 ++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/cocos/rendering/custom/define.ts b/cocos/rendering/custom/define.ts index 119dd7eccdd..193c1d1dce2 100644 --- a/cocos/rendering/custom/define.ts +++ b/cocos/rendering/custom/define.ts @@ -486,18 +486,6 @@ class DescBuffManager { const buffsMap: Map = new Map(); const currBindBuffs: Map = new Map(); -function updateGlobalDescBuffer (blockId: number, sceneId: number, idxRD: number, vals: number[], setData: DescriptorSetData): void { - const bindId = getDescBinding(blockId, setData); - if (bindId === -1) { return; } - const descKey = `${blockId}${bindId}${idxRD}${sceneId}`; - currBindBuffs.set(descKey, bindId); - let currDescBuff = buffsMap.get(descKey); - if (!currDescBuff) { - buffsMap.set(descKey, new DescBuffManager(vals.length * 4, 2)); - currDescBuff = buffsMap.get(descKey); - } - currDescBuff!.updateData(vals); -} const layouts: Map = new Map(); export function getDescriptorSetDataFromLayout (layoutName: string): DescriptorSetData | undefined { @@ -591,6 +579,43 @@ function addConstantBuffer (block: string, layout: string): number[] | null { uniformBlockMap.set(block, buffers); return buffers; } + +function updateGlobalDescBuffer (descKey: string, vals: number[]): void { + let currDescBuff = buffsMap.get(descKey); + if (!currDescBuff) { + buffsMap.set(descKey, new DescBuffManager(vals.length * 4, 2)); + currDescBuff = buffsMap.get(descKey); + } + currDescBuff!.updateData(vals); +} + +function updateConstantBlock ( + constantBuff: ConstantBlockInfo, + data: number[], + descriptorSetData: DescriptorSetData, + sceneId: number, + idxRD: number, +): void { + const blockId = constantBuff.blockId; + const buffer = constantBuff.buffer; + const isImparity = copyToConstantBuffer(buffer, data, constantBuff.offset); + const bindId = getDescBinding(blockId, descriptorSetData); + const desc = descriptorSetData.descriptorSet!; + if (isImparity || !desc.getBuffer(bindId) && bindId !== -1) { + const descKey = `${blockId}${bindId}${idxRD}${sceneId}`; + currBindBuffs.set(descKey, bindId); + updateGlobalDescBuffer(descKey, buffer); + } +} + +function updateDefaultConstantBlock (blockId: number, sceneId: number, idxRD: number, vals: number[], setData: DescriptorSetData): void { + const bindId = getDescBinding(blockId, setData); + if (bindId === -1) { return; } + const descKey = `${blockId}${bindId}${idxRD}${sceneId}`; + currBindBuffs.set(descKey, bindId); + updateGlobalDescBuffer(descKey, vals); +} + export function updatePerPassUBO (layout: string, sceneId: number, idxRD: number, user: RenderData): void { const constantMap = user.constants; const samplers = user.samplers; @@ -612,7 +637,7 @@ export function updatePerPassUBO (layout: string, sceneId: number, idxRD: number if (offset === -1) { // Although the current uniformMem does not belong to the current uniform block, // it does not mean that it should not be bound to the corresponding descriptor. - updateGlobalDescBuffer(blockId, sceneId, idxRD, constantBuff, descriptorSetData); + updateDefaultConstantBlock(blockId, sceneId, idxRD, constantBuff, descriptorSetData); continue; } constantBlockMap.set(key, new ConstantBlockInfo()); @@ -620,12 +645,10 @@ export function updatePerPassUBO (layout: string, sceneId: number, idxRD: number constantBlock.buffer = constantBuff; constantBlock.blockId = blockId; constantBlock.offset = offset; - const isImparity = copyToConstantBuffer(constantBuff, data, offset); - if (isImparity) updateGlobalDescBuffer(blockId, sceneId, idxRD, constantBuff, descriptorSetData); + updateConstantBlock(constantBlock, data, descriptorSetData, sceneId, idxRD); } } else { - const isImparity = copyToConstantBuffer(constantBlock.buffer, data, constantBlock.offset); - if (isImparity) updateGlobalDescBuffer(constantBlock.blockId, sceneId, idxRD, constantBlock.buffer, descriptorSetData); + updateConstantBlock(constantBlock, data, descriptorSetData, sceneId, idxRD); } } From b990d851cd0b88b70b6fa7f64d1c451f94a32aa2 Mon Sep 17 00:00:00 2001 From: Knox Date: Thu, 5 Sep 2024 16:19:50 +0800 Subject: [PATCH 10/21] Fix memory hike caused by repeatedly switching inspectors (#17605) * Fix memory hike caused by repeatedly switching inspectors * refine code --------- Co-authored-by: cocos --- editor/inspector/contributions/asset.js | 5 +++-- editor/inspector/contributions/node.js | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/editor/inspector/contributions/asset.js b/editor/inspector/contributions/asset.js index 14937097843..cff3bfb7a80 100644 --- a/editor/inspector/contributions/asset.js +++ b/editor/inspector/contributions/asset.js @@ -75,8 +75,8 @@ const Elements = { Editor.Message.addBroadcastListener('asset-db:asset-change', panel.__assetChanged__); - Elements.panel.i18nChangeBind = Elements.panel.i18nChange.bind(panel); - Editor.Message.addBroadcastListener('i18n:change', Elements.panel.i18nChangeBind); + panel.i18nChangeBind = Elements.panel.i18nChange.bind(panel); + Editor.Message.addBroadcastListener('i18n:change', panel.i18nChangeBind); panel.history = new History(); }, @@ -164,6 +164,7 @@ const Elements = { panel.__assetChangedHandle__ = undefined; } + Editor.Message.removeBroadcastListener('i18n:change', panel.i18nChangeBind); Editor.Message.removeBroadcastListener('asset-db:asset-change', panel.__assetChanged__); delete panel.history; diff --git a/editor/inspector/contributions/node.js b/editor/inspector/contributions/node.js index b1700383d65..ada2f489c1d 100644 --- a/editor/inspector/contributions/node.js +++ b/editor/inspector/contributions/node.js @@ -1160,8 +1160,8 @@ const Elements = { event.stopPropagation(); }); - Elements.node.i18nChangeBind = Elements.node.i18nChange.bind(panel); - Editor.Message.addBroadcastListener('i18n:change', Elements.node.i18nChangeBind); + panel.i18nChangeBind = Elements.node.i18nChange.bind(panel); + Editor.Message.addBroadcastListener('i18n:change', panel.i18nChangeBind); }, async update() { const panel = this; @@ -1404,7 +1404,9 @@ const Elements = { } }, close() { - Editor.Message.removeBroadcastListener('i18n:change', Elements.node.i18nChangeBind); + const panel = this; + + Editor.Message.removeBroadcastListener('i18n:change', panel.i18nChangeBind); }, i18nChange() { const panel = this; From 78a5f2feeecd55711a5a58a3501998cff4e4984a Mon Sep 17 00:00:00 2001 From: Elias Rad <146735585+nnsW3@users.noreply.github.com> Date: Fri, 6 Sep 2024 10:20:37 +0300 Subject: [PATCH 11/21] docs: fix spelling issues (#17564) * fix deprecated-api.md * fix modules.md * fix CPP_CODING_STYLE.md * fix CPP_CODING_STYLE.md --- docs/CPP_CODING_STYLE.md | 4 ++-- docs/contribution/deprecated-api.md | 4 ++-- docs/contribution/modules.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/CPP_CODING_STYLE.md b/docs/CPP_CODING_STYLE.md index 2b1fabc0ace..18d77dc00c5 100644 --- a/docs/CPP_CODING_STYLE.md +++ b/docs/CPP_CODING_STYLE.md @@ -20,7 +20,7 @@ _Forked from [Google's C++ coding style](https://google.github.io/styleguide/cpp - [Nonmember, Static Member, and Global Functions](#nonmember-static-member-and-global-functions) - [Local Variables](#local-variables) - [Static and Global Variables](#static-and-global-variables) - - [Decision on destrcution:](#decision-on-destrcution) + - [Decision on destruction:](#decision-on-destrcution) - [Decision on initialization](#decision-on-initialization) - [Common patterns](#common-patterns) - [thread_local variables](#thread_local-variables) @@ -381,7 +381,7 @@ Global and static variables that use dynamic initialization or have non-trivial **Decision:** -### Decision on destrcution: +### Decision on destruction: When destructors are trivial, their execution is not subject to ordering at all (they are effectively not "run"); otherwise we are exposed to the risk of accessing objects after the end of their lifetime. Therefore, we only allow objects with static storage duration if they are trivially destructible. Fundamental types (like pointers and int) are trivially destructible, as are arrays of trivially destructible types. Note that variables marked with constexpr are trivially destructible. diff --git a/docs/contribution/deprecated-api.md b/docs/contribution/deprecated-api.md index 54814a83811..ebcbadaed7c 100644 --- a/docs/contribution/deprecated-api.md +++ b/docs/contribution/deprecated-api.md @@ -46,7 +46,7 @@ interface IReplacement { customFunction?: Function; /** Setter of custom replacement properties */ customSetter?: (v: any) => void; - /** Getter for custom replacement propertys */ + /** Getter for custom replacement properties */ customGetter?: () => any; } @@ -148,4 +148,4 @@ or ```ts import * as cc from 'cc'; console.log(cc.ButtonComponent); -``` \ No newline at end of file +``` diff --git a/docs/contribution/modules.md b/docs/contribution/modules.md index d94e75af98c..578bcf98535 100644 --- a/docs/contribution/modules.md +++ b/docs/contribution/modules.md @@ -53,6 +53,6 @@ If your API need to be in a brand new public module, you need to create the new Creating a public module is simply adding a module file under `/exports`. Then, you need some extra steps to bring your public module to users: -- Add an new element into `features` array in [cc.config.json](../../cc.config.json), **feature** means a feature set which might be selected by user. `modules` field indicates which public modules(see above) should be considered included while this feature is enabled. Each element of `modules` should be the extension-less file name of a public module. That JSON configuration file should have been associated with a schema file. For more fields or controls over that file, see the schema file. +- Add a new element into `features` array in [cc.config.json](../../cc.config.json), **feature** means a feature set which might be selected by user. `modules` field indicates which public modules(see above) should be considered included while this feature is enabled. Each element of `modules` should be the extension-less file name of a public module. That JSON configuration file should have been associated with a schema file. For more fields or controls over that file, see the schema file. - Navigate to [render-config.json](../../editor/engine-features/render-config.json) to configure the feature's exterior in Editor. Also there's an associated schema file. From 3ecad7882a6a52aa82d9f14a07b4274a61920425 Mon Sep 17 00:00:00 2001 From: GengineJS <476393671@qq.com> Date: Tue, 10 Sep 2024 11:58:25 +0800 Subject: [PATCH 12/21] Streamline the new pipeline execution code (#17614) --- cocos/rendering/custom/compiler.ts | 57 ---- cocos/rendering/custom/define.ts | 10 +- cocos/rendering/custom/executor.ts | 261 +++++-------------- cocos/rendering/custom/scene-culling.ts | 210 +++++---------- cocos/rendering/custom/web-pipeline-types.ts | 5 - cocos/rendering/custom/web-pipeline.ts | 54 +--- 6 files changed, 133 insertions(+), 464 deletions(-) diff --git a/cocos/rendering/custom/compiler.ts b/cocos/rendering/custom/compiler.ts index 05a980a7df4..f81c57817c8 100644 --- a/cocos/rendering/custom/compiler.ts +++ b/cocos/rendering/custom/compiler.ts @@ -23,7 +23,6 @@ ****************************************************************************/ import { DEBUG } from 'internal:constants'; import { Buffer, Framebuffer, LoadOp, StoreOp, Texture, Viewport } from '../../gfx'; -import { assert } from '../../core'; import { VectorGraphColorMap } from './effect'; import { DefaultVisitor, depthFirstSearch, ReferenceGraphView } from './graph'; import { LayoutGraphData } from './layout-graph'; @@ -150,13 +149,6 @@ class PassVisitor implements RenderGraphVisitor { for (const [passId, currRaster] of rasters) { if (passId > this.passID) { isPreRaster = true; - // TODO: Shadow map is rather special, as it will be merged into one pass later, and then this determination can be removed. - if (!this._isShadowMap(this.sceneID)) { - assert( - currRaster.loadOp === LoadOp.LOAD, - `The resource with name ${input} is being used, and the pass that uses this resource must have loadOp set to 'load'`, - ); - } } } for (const [passId] of computes) { @@ -165,16 +157,12 @@ class PassVisitor implements RenderGraphVisitor { break; } } - if (isPreRaster) { - assert(raster.storeOp === StoreOp.STORE, `The resource ${input} is being used, so storeOp needs to be set to 'store'`); - } rasters.set(this.passID, raster); } else { const resId = resGraph.vertex(input); const trait = resGraph.getTraits(resId); switch (trait.residency) { case ResourceResidency.PERSISTENT: - assert(raster.storeOp === StoreOp.STORE, `Persistent resources must have storeOp set to 'store'.`); break; default: } @@ -462,51 +450,6 @@ export class Compiler { context.pipeline.resourceUses.length = 0; this._visitor.colorMap.colors.length = context.resourceGraph.nv(); depthFirstSearch(this._resourceGraph, this._visitor, this._visitor.colorMap); - - if (DEBUG) { - const useContext = context.resourceContext; - for (const [name, use] of useContext) { - const resId = this._resourceGraph.vertex(name); - const trait = this._resourceGraph.getTraits(resId); - const rasterArr: number[] = Array.from(use.rasters.keys()); - if (!rasterArr.length) { - continue; - } - - const min = rasterArr.reduce((prev, current): number => (prev < current ? prev : current)); - const firstRaster = use.rasters.get(min)!; - switch (trait.residency) { - case ResourceResidency.PERSISTENT: - assert( - firstRaster.loadOp !== LoadOp.DISCARD, - `The loadOp for persistent resources in the top-level pass cannot be set to 'discard'.`, - ); - break; - case ResourceResidency.MANAGED: - assert(firstRaster.loadOp === LoadOp.CLEAR, `The loadOp for Managed resources in the top-level pass can only be set to 'clear'.`); - break; - default: - break; - } - const computeArr: number[] = Array.from(use.computes.keys()); - const max = rasterArr.reduce((prev, current): number => (prev > current ? prev : current)); - let maxCompute = -1; - if (computeArr.length) { - maxCompute = computeArr.reduce((prev, current): number => (prev > current ? prev : current)); - } - if (max > maxCompute) { - const lastRaster = use.rasters.get(max)!; - switch (trait.residency) { - case ResourceResidency.MANAGED: - // TODO - // assert(lastRaster.storeOp === StoreOp.DISCARD, `MANAGED resources that are not being used must be set to 'discard'.`); - break; - default: - break; - } - } - } - } } } const context = new CompilerContext(); diff --git a/cocos/rendering/custom/define.ts b/cocos/rendering/custom/define.ts index 193c1d1dce2..acecc893e38 100644 --- a/cocos/rendering/custom/define.ts +++ b/cocos/rendering/custom/define.ts @@ -43,7 +43,7 @@ import { AttachmentType, LightInfo, QueueHint, ResourceResidency, SceneFlags, UpdateFrequency, } from './types'; -import { Vec4, geometry, toRadian, cclegacy, assert } from '../../core'; +import { Vec4, geometry, toRadian, cclegacy } from '../../core'; import { RenderWindow } from '../../render-scene/core/render-window'; import { RenderData, RenderGraph } from './render-graph'; import { WebPipeline } from './web-pipeline'; @@ -495,7 +495,6 @@ export function getDescriptorSetDataFromLayout (layoutName: string): DescriptorS } const webPip = cclegacy.director.root.pipeline as WebPipeline; const stageId = webPip.layoutGraph.locateChild(webPip.layoutGraph.N, layoutName); - assert(stageId !== 0xFFFFFFFF); const layout = webPip.layoutGraph.getLayout(stageId); const layoutData = layout.descriptorSets.get(UpdateFrequency.PER_PASS); layouts.set(layoutName, layoutData!); @@ -849,31 +848,24 @@ export function SetLightUBO ( export function getSubpassOrPassID (sceneId: number, rg: RenderGraph, lg: LayoutGraphData): number { const queueId = rg.getParent(sceneId); - assert(queueId !== 0xFFFFFFFF); const subpassOrPassID = rg.getParent(queueId); - assert(subpassOrPassID !== 0xFFFFFFFF); const passId = rg.getParent(subpassOrPassID); let layoutId = lg.N; // single render pass if (passId === rg.N) { const layoutName: string = rg.getLayout(subpassOrPassID); - assert(!!layoutName); layoutId = lg.locateChild(lg.N, layoutName); } else { const passLayoutName: string = rg.getLayout(passId); - assert(!!passLayoutName); const passLayoutId = lg.locateChild(lg.N, passLayoutName); - assert(passLayoutId !== lg.N); const subpassLayoutName: string = rg.getLayout(subpassOrPassID); if (subpassLayoutName.length === 0) { layoutId = passLayoutId; } else { const subpassLayoutId = lg.locateChild(passLayoutId, subpassLayoutName); - assert(subpassLayoutId !== lg.N); layoutId = subpassLayoutId; } } - assert(layoutId !== lg.N); return layoutId; } diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index ce2fbf3efb2..266f8eb1375 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -29,7 +29,7 @@ */ /* eslint-disable max-len */ import { getPhaseID, PipelineStateManager } from '..'; -import { assert, cclegacy, RecyclePool } from '../../core'; +import { cclegacy, RecyclePool } from '../../core'; import intersect from '../../core/geometry/intersect'; import { Sphere } from '../../core/geometry/sphere'; import { @@ -310,12 +310,12 @@ class DeviceTexture extends DeviceResource { } } -function isShadowMap (graphScene: GraphScene): boolean | null { +function isShadowMap (scene?: SceneData): boolean | undefined { const pSceneData: PipelineSceneData = cclegacy.director.root.pipeline.pipelineSceneData; return pSceneData.shadows.enabled && pSceneData.shadows.type === ShadowType.ShadowMap - && graphScene.scene - && (graphScene.scene.flags & SceneFlags.SHADOW_CASTER) !== 0; + && scene + && (scene.flags & SceneFlags.SHADOW_CASTER) !== 0; } class DeviceBuffer extends DeviceResource { @@ -577,7 +577,7 @@ class DeviceComputeQueue implements RecordingInterface { class DeviceRenderQueue implements RecordingInterface { private _renderScenes: DeviceRenderScene[] = []; - private _devicePass: DeviceRenderPass | undefined; + private _devicePass?: DeviceRenderPass; private _hint: QueueHint = QueueHint.NONE; private _graphQueue!: RenderQueue; private _phaseID: number = getPhaseID('default'); @@ -614,7 +614,7 @@ class DeviceRenderQueue implements RecordingInterface { get isUploadInstance (): boolean { return this._isUploadInstance; } set isUploadBatched (value: boolean) { this._isUploadBatched = value; } get isUploadBatched (): boolean { return this._isUploadBatched; } - init (devicePass: DeviceRenderPass, renderQueue: RenderQueue, id: number): void { + init (devicePass, renderQueue: RenderQueue, id: number): void { this.reset(); this._graphQueue = renderQueue; this.queueHint = renderQueue.hint; @@ -633,9 +633,10 @@ class DeviceRenderQueue implements RecordingInterface { this._blitDesc.createScreenQuad(); this._blitDesc.createStageDescriptor(); } - addScene (scene: GraphScene): DeviceRenderScene { + + setScene (sceneID: number, scene?: SceneData, blit?: Blit): DeviceRenderScene { const deviceScene = context.pools.addDeviceScene(); - deviceScene.init(this, scene); + deviceScene.init(this, sceneID, scene, blit); this._renderScenes.push(deviceScene); return deviceScene; } @@ -737,62 +738,6 @@ class RenderPassLayoutInfo { get layout (): PipelineLayoutData { return this._layout; } } -class RasterPassInfo { - protected _id!: number; - protected _pass!: RasterPass; - get id (): number { return this._id; } - get pass (): RasterPass { return this._pass; } - private _copyPass (pass: RasterPass): void { - const rasterPass = this._pass || new RasterPass(); - rasterPass.width = pass.width; - rasterPass.height = pass.height; - rasterPass.versionName = pass.versionName; - rasterPass.version = pass.version; - rasterPass.showStatistics = pass.showStatistics; - rasterPass.viewport.copy(pass.viewport); - for (const val of pass.rasterViews) { - const currRasterKey = val[0]; - const currRasterView = val[1]; - const rasterView = rasterPass.rasterViews.get(currRasterKey) || new RasterView(); - rasterView.slotName = currRasterView.slotName; - rasterView.accessType = currRasterView.accessType; - rasterView.attachmentType = currRasterView.attachmentType; - rasterView.loadOp = currRasterView.loadOp ? LoadOp.CLEAR : LoadOp.LOAD; - rasterView.storeOp = currRasterView.storeOp; - rasterView.clearFlags = currRasterView.clearFlags; - rasterView.slotID = currRasterView.slotID; - rasterView.clearColor.copy(currRasterView.clearColor); - rasterPass.rasterViews.set(currRasterKey, rasterView); - } - for (const val of pass.computeViews) { - const currComputeViews = val[1]; - const currComputeKey = val[0]; - const computeViews: ComputeView[] = rasterPass.computeViews.get(currComputeKey) || []; - if (computeViews.length) computeViews.length = currComputeViews.length; - let idx = 0; - for (const currComputeView of currComputeViews) { - const computeView = computeViews[idx] || new ComputeView(); - computeView.name = currComputeView.name; - computeView.accessType = currComputeView.accessType; - computeView.clearFlags = currComputeView.clearFlags; - computeView.clearValue.x = currComputeView.clearValue.x; - computeView.clearValue.y = currComputeView.clearValue.y; - computeView.clearValue.z = currComputeView.clearValue.z; - computeView.clearValue.w = currComputeView.clearValue.w; - computeView.clearValueType = currComputeView.clearValueType; - computeViews[idx] = computeView; - idx++; - } - rasterPass.computeViews.set(currComputeKey, computeViews); - } - this._pass = rasterPass; - } - applyInfo (id: number, pass: RasterPass): void { - this._id = id; - this._copyPass(pass); - } -} - const profilerViewport = new Viewport(); const renderPassArea = new Rect(); const resourceVisitor = new ResourceVisitor(); @@ -804,15 +749,17 @@ class DeviceRenderPass implements RecordingInterface { protected _clearDepth = 1; protected _clearStencil = 0; protected _passID: number; + protected _rasterID: number; + protected _rasterPass: RasterPass; protected _layoutName: string; protected _viewport: Viewport | null = null; - private _rasterInfo: RasterPassInfo; private _layout: RenderPassLayoutInfo | null = null; private _idxOfRenderData: number = 0; - constructor (passInfo: RasterPassInfo) { - this._rasterInfo = passInfo; + constructor (rasterID: number, rasterPass: RasterPass) { + this._rasterID = rasterID; + this._rasterPass = rasterPass; const device = context.device; - this._layoutName = context.renderGraph.getLayout(passInfo.id); + this._layoutName = context.renderGraph.getLayout(rasterID); this._passID = cclegacy.rendering.getPassID(this._layoutName); const depthStencilAttachment = new DepthStencilAttachment(); depthStencilAttachment.format = Format.DEPTH_STENCIL; @@ -821,14 +768,14 @@ class DeviceRenderPass implements RecordingInterface { let depthTex: Texture | null = null; let swapchain: Swapchain | null = null; let framebuffer: Framebuffer | null = null; - for (const cv of passInfo.pass.computeViews) { + for (const cv of rasterPass.computeViews) { this._applyRenderLayout(cv); } // update the layout descriptorSet if (this.renderLayout && this.renderLayout.descriptorSet) { this.renderLayout.descriptorSet.update(); } - for (const [resName, rasterV] of passInfo.pass.rasterViews) { + for (const [resName, rasterV] of rasterPass.rasterViews) { let resTex = context.deviceTextures.get(resName); if (!resTex) { this.visitResource(resName); @@ -911,6 +858,7 @@ class DeviceRenderPass implements RecordingInterface { )); } get indexOfRD (): number { return this._idxOfRenderData; } + get rasterID (): number { return this._rasterID; } get layoutName (): string { return this._layoutName; } get passID (): number { return this._passID; } get renderLayout (): RenderPassLayoutInfo | null { return this._layout; } @@ -920,7 +868,6 @@ class DeviceRenderPass implements RecordingInterface { get clearDepth (): number { return this._clearDepth; } get clearStencil (): number { return this._clearStencil; } get deviceQueues (): Map { return this._deviceQueues; } - get rasterPassInfo (): RasterPassInfo { return this._rasterInfo; } get viewport (): Viewport | null { return this._viewport; } addIdxOfRD (): void { this._idxOfRenderData++; @@ -938,18 +885,17 @@ class DeviceRenderPass implements RecordingInterface { context.passDescriptorSet = getDescriptorSetDataFromLayout(this.layoutName)!.descriptorSet; } protected _applyRenderLayout (input: [string, ComputeView[]]): void { - const stageName = context.renderGraph.getLayout(this.rasterPassInfo.id); + const stageName = context.renderGraph.getLayout(this._rasterID); if (stageName) { const layoutGraph = context.layoutGraph; const stageId = layoutGraph.locateChild(layoutGraph.N, stageName); if (stageId !== 0xFFFFFFFF) { - this._layout = new RenderPassLayoutInfo(stageId, this.rasterPassInfo.id, input); + this._layout = new RenderPassLayoutInfo(stageId, this._rasterID, input); } } } getGlobalDescData (): DescriptorSetData { const stageId = context.layoutGraph.locateChild(context.layoutGraph.N, 'default'); - assert(stageId !== 0xFFFFFFFF); const layout = context.layoutGraph.getLayout(stageId); const layoutData = layout.descriptorSets.get(UpdateFrequency.PER_PASS)!; return layoutData; @@ -957,7 +903,7 @@ class DeviceRenderPass implements RecordingInterface { protected _applyViewport (frameTex: Texture): void { this._viewport = null; - const viewport = this._rasterInfo.pass.viewport; + const viewport = this._rasterPass.viewport; if (viewport.left !== 0 || viewport.top !== 0 || viewport.width !== 0 @@ -1031,7 +977,7 @@ class DeviceRenderPass implements RecordingInterface { for (const queue of this._deviceQueues.values()) { queue.record(); } - if (this._rasterInfo.pass.showStatistics) { + if (this._rasterPass.showStatistics) { this._showProfiler(renderPassArea); } cmdBuff.endRenderPass(); @@ -1041,7 +987,8 @@ class DeviceRenderPass implements RecordingInterface { // nothing to do } resetResource (id: number, pass: RasterPass): void { - this._rasterInfo.applyInfo(id, pass); + this._rasterID = id; + this._rasterPass = pass; this._layoutName = context.renderGraph.getLayout(id); this._passID = cclegacy.rendering.getPassID(this._layoutName); this._deviceQueues.clear(); @@ -1051,7 +998,7 @@ class DeviceRenderPass implements RecordingInterface { const currFramebuffer = this._framebuffer; const currFBDepthTex = currFramebuffer.depthStencilTexture; let depTexture = currFramebuffer ? currFBDepthTex : null; - for (const cv of this._rasterInfo.pass.computeViews) { + for (const cv of pass.computeViews) { this._applyRenderLayout(cv); } // update the layout descriptorSet @@ -1065,7 +1012,7 @@ class DeviceRenderPass implements RecordingInterface { let width = 0; let height = 0; - for (const [resName, rasterV] of this._rasterInfo.pass.rasterViews) { + for (const [resName, rasterV] of pass.rasterViews) { if (rasterV.attachmentType === AttachmentType.SHADING_RATE) { continue; } @@ -1087,7 +1034,7 @@ class DeviceRenderPass implements RecordingInterface { isInsideTexDestroy = currFBDepthTex.getTextureHandle() === 0; } const needRebuild = (width !== currentWidth) || (height !== currentHeight) || currFramebuffer.needRebuild || isInsideTexDestroy; - for (const [resName, rasterV] of this._rasterInfo.pass.rasterViews) { + for (const [resName, rasterV] of pass.rasterViews) { let deviceTex = context.deviceTextures.get(resName)!; const currTex = deviceTex; if (!deviceTex) { @@ -1133,34 +1080,9 @@ class ComputePassInfo { protected _pass!: ComputePass; get id (): number { return this._id; } get pass (): ComputePass { return this._pass; } - private _copyPass (pass: ComputePass): void { - const computePass = this._pass || new ComputePass(); - for (const val of pass.computeViews) { - const currComputeViews = val[1]; - const currComputeKey = val[0]; - const computeViews: ComputeView[] = computePass.computeViews.get(currComputeKey) || []; - if (computeViews.length) computeViews.length = currComputeViews.length; - let idx = 0; - for (const currComputeView of currComputeViews) { - const computeView = computeViews[idx] || new ComputeView(); - computeView.name = currComputeView.name; - computeView.accessType = currComputeView.accessType; - computeView.clearFlags = currComputeView.clearFlags; - computeView.clearValue.x = currComputeView.clearValue.x; - computeView.clearValue.y = currComputeView.clearValue.y; - computeView.clearValue.z = currComputeView.clearValue.z; - computeView.clearValue.w = currComputeView.clearValue.w; - computeView.clearValueType = currComputeView.clearValueType; - computeViews[idx] = computeView; - idx++; - } - computePass.computeViews.set(currComputeKey, computeViews); - } - this._pass = computePass; - } applyInfo (id: number, pass: ComputePass): void { this._id = id; - this._copyPass(pass); + this._pass = pass; } } @@ -1220,7 +1142,6 @@ class DeviceComputePass implements RecordingInterface { } getGlobalDescData (): DescriptorSetData { const stageId = context.layoutGraph.locateChild(context.layoutGraph.N, 'default'); - assert(stageId !== 0xFFFFFFFF); const layout = context.layoutGraph.getLayout(stageId); const layoutData = layout.descriptorSets.get(UpdateFrequency.PER_PASS)!; return layoutData; @@ -1257,56 +1178,23 @@ class DeviceComputePass implements RecordingInterface { } } } -class GraphScene { - scene: SceneData | null = null; - blit: Blit | null = null; - dispatch: Dispatch | null = null; - sceneID = -1; - private _copyScene (scene: SceneData | null): void { - if (scene) { - if (!this.scene) { - this.scene = new SceneData(); - } - this.scene.scene = scene.scene; - this.scene.light.level = scene.light.level; - this.scene.light.light = scene.light.light; - this.scene.flags = scene.flags; - this.scene.camera = scene.camera; - this.scene.shadingLight = scene.shadingLight; - return; - } - this.scene = null; - } - private _copyBlit (blit: Blit | null): void { - if (blit) { - if (!this.blit) { - this.blit = new Blit(blit.material, blit.passID, blit.sceneFlags, blit.camera); - } - this.blit.material = blit.material; - this.blit.passID = blit.passID; - this.blit.sceneFlags = blit.sceneFlags; - this.blit.camera = blit.camera; - return; - } - this.blit = null; - } - init (scene: SceneData | null, blit: Blit | null, sceneID): void { - this._copyScene(scene); - this._copyBlit(blit); - this.sceneID = sceneID; - } -} + const sceneViewport = new Viewport(); class DeviceRenderScene implements RecordingInterface { protected _currentQueue!: DeviceRenderQueue; protected _renderPass!: RenderPass; - protected _graphScene!: GraphScene; protected _scene: RenderScene | null = null; protected _camera: Camera | null = null; + protected _sceneData?: SceneData; + protected _blit?: Blit; + protected _sceneID: number = -1; + get blit (): Blit | undefined { return this._blit; } + get sceneData (): SceneData | undefined { return this._sceneData; } + get sceneID (): number { return this._sceneID; } get camera (): Camera | null { return this._camera; } preRecord (): void { - if (this.graphScene.blit) { - this._currentQueue.createBlitDesc(this.graphScene.blit); + if (this._blit) { + this._currentQueue.createBlitDesc(this._blit); this._currentQueue.blitDesc!.update(); } context.lightResource.buildLightBuffer(context.commandBuffer); @@ -1315,28 +1203,29 @@ class DeviceRenderScene implements RecordingInterface { postRecord (): void { // nothing to do } - init (queue: DeviceRenderQueue, graphScene: GraphScene): void { + init (queue: DeviceRenderQueue, sceneID: number, scene?: SceneData, blit?: Blit): void { this._currentQueue = queue; - this._graphScene = graphScene; + this._sceneData = scene; + this._blit = blit; + this._sceneID = sceneID; this._renderPass = queue.devicePass.renderPass; - const camera = graphScene.scene && graphScene.scene.camera ? graphScene.scene.camera : null; + const camera = scene && scene.camera ? scene.camera : null; if (camera) { this._scene = camera.scene; this._camera = camera; } } - get graphScene (): GraphScene { return this._graphScene; } protected _recordUI (): void { const devicePass = this._currentQueue.devicePass; - const rasterId = devicePass.rasterPassInfo.id; + const rasterId = devicePass.rasterID; const passRenderData = context.renderGraph.getData(rasterId); // RasterPass first - this._updateGlobal(passRenderData, this.graphScene.sceneID); + this._updateGlobal(passRenderData, this.sceneID); // then Queue const queueId = this._currentQueue.queueId; const queueRenderData = context.renderGraph.getData(queueId)!; - this._updateGlobal(queueRenderData, this.graphScene.sceneID); + this._updateGlobal(queueRenderData, this.sceneID); this._currentQueue.isUpdateUBO = true; @@ -1368,11 +1257,11 @@ class DeviceRenderScene implements RecordingInterface { } private _recordBlit (): void { - if (!this.graphScene.blit) { return; } + if (!this.blit) { return; } - const blit = this.graphScene.blit; - const currMat = blit.material; - const pass = currMat!.passes[blit.passID]; + const blit = this.blit; + const currMat = blit.material!; + const pass = currMat.passes[blit.passID]; pass.update(); const shader = pass.getShaderVariant(); const devicePass = this._currentQueue.devicePass; @@ -1399,15 +1288,15 @@ class DeviceRenderScene implements RecordingInterface { protected _updateGlobal (data: RenderData, sceneId: number): void { const devicePass = this._currentQueue.devicePass; devicePass.addIdxOfRD(); - updateGlobalDescBinding(data, sceneId, devicePass.indexOfRD, context.renderGraph.getLayout(devicePass.rasterPassInfo.id)); + updateGlobalDescBinding(data, sceneId, devicePass.indexOfRD, context.renderGraph.getLayout(devicePass.rasterID)); } protected _updateRenderData (): void { if (this._currentQueue.isUpdateUBO) return; const devicePass = this._currentQueue.devicePass; - const rasterId = devicePass.rasterPassInfo.id; + const rasterId = devicePass.rasterID; const passRenderData = context.renderGraph.getData(rasterId); - const sceneId = this.graphScene.sceneID; + const sceneId = this.sceneID; // CCGlobal this._updateGlobal(passRenderData, sceneId); // CCCamera, CCShadow, CCCSM @@ -1426,9 +1315,8 @@ class DeviceRenderScene implements RecordingInterface { context.commandBuffer.setScissor(this._currentQueue.scissor!); } else if (!this._currentQueue.devicePass.viewport) { const texture = this._currentQueue.devicePass.framebuffer.colorTextures[0]!; - const graphScene = this.graphScene; - const lightInfo = graphScene.scene ? graphScene.scene.light : null; - const area = isShadowMap(this.graphScene) && graphScene.scene && lightInfo!.light + const lightInfo = this.sceneData ? this.sceneData.light : null; + const area = isShadowMap(this.sceneData) && this.sceneData && lightInfo!.light ? getRenderArea(this.camera!, texture.width, texture.height, lightInfo!.light, lightInfo!.level) : getRenderArea(this.camera!, texture.width, texture.height); sceneViewport.left = area.x; @@ -1446,13 +1334,13 @@ class DeviceRenderScene implements RecordingInterface { this._updateRenderData(); this._applyViewport(); // Currently processing blit and camera first - if (this.graphScene.blit) { + if (this.blit) { this._recordBlit(); return; } - const renderQueueDesc = sceneCulling.renderQueueIndex.get(this.graphScene.sceneID)!; + const renderQueueDesc = sceneCulling.renderQueueIndex.get(this.sceneID)!; const renderQueue = sceneCulling.renderQueues[renderQueueDesc.renderQueueTarget]; - const graphSceneData = this.graphScene.scene!; + const graphSceneData = this.sceneData!; if (bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE)) renderQueue.probeQueue.applyMacro(); renderQueue.recordCommands(context.commandBuffer, this._renderPass); if (bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE)) renderQueue.probeQueue.removeMacro(); @@ -1470,13 +1358,10 @@ class DeviceRenderScene implements RecordingInterface { } class ExecutorPools { - constructor (context: ExecutorContext) { + constructor () { this.deviceQueuePool = new RecyclePool((): DeviceRenderQueue => new DeviceRenderQueue(), 16); this.deviceScenePool = new RecyclePool((): DeviceRenderScene => new DeviceRenderScene(), 16); this.computeQueuePool = new RecyclePool((): DeviceComputeQueue => new DeviceComputeQueue(), 16); - this.graphScenePool = new RecyclePool((): GraphScene => new GraphScene(), 16); - this.rasterPassInfoPool = new RecyclePool((): RasterPassInfo => new RasterPassInfo(), 16); - this.computePassInfoPool = new RecyclePool((): ComputePassInfo => new ComputePassInfo(), 16); this.passPool = new RecyclePool((): { priority: number; hash: number; depth: number; shaderId: number; subModel: any; passIdx: number; } => ({ priority: 0, hash: 0, @@ -1492,31 +1377,17 @@ class ExecutorPools { addComputeQueue (): DeviceComputeQueue { return this.computeQueuePool.add(); } - addGraphScene (): GraphScene { - return this.graphScenePool.add(); - } addDeviceScene (): DeviceRenderScene { return this.deviceScenePool.add(); } - addRasterPassInfo (): RasterPassInfo { - return this.rasterPassInfoPool.add(); - } - addComputePassInfo (): ComputePassInfo { - return this.computePassInfoPool.add(); - } reset (): void { this.deviceQueuePool.reset(); this.computeQueuePool.reset(); - this.graphScenePool.reset(); - this.computePassInfoPool.reset(); this.deviceScenePool.reset(); } readonly deviceQueuePool: RecyclePool; readonly computeQueuePool: RecyclePool; - readonly graphScenePool: RecyclePool; readonly passPool: RecyclePool; - readonly rasterPassInfoPool: RecyclePool; - readonly computePassInfoPool: RecyclePool; readonly deviceScenePool: RecyclePool; } @@ -1720,7 +1591,7 @@ class ExecutorContext { this.layoutGraph = layoutGraph; this.width = width; this.height = height; - this.pools = new ExecutorPools(this); + this.pools = new ExecutorPools(); this.blit = new BlitInfo(this); this.culling = new SceneCulling(); this.passDescriptorSet = descriptorSet; @@ -1920,9 +1791,7 @@ class PreRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor { const passHash = pass.hashValue; this.currPass = devicePasses.get(passHash); if (!this.currPass) { - const rasterInfo = context.pools.addRasterPassInfo(); - rasterInfo.applyInfo(this.passID, pass); - this.currPass = new DeviceRenderPass(rasterInfo); + this.currPass = new DeviceRenderPass(this.passID, pass); devicePasses.set(passHash, this.currPass); } else { this.currPass.resetResource(this.passID, pass); @@ -1970,7 +1839,7 @@ class PreRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor { queue (value: RenderQueue): void { if (!this.rg.getValid(this.queueID)) return; let deviceQueue: DeviceComputeQueue | DeviceRenderQueue; - if ('rasterPassInfo' in this.currPass!) { + if (this.currPass instanceof DeviceRenderPass) { deviceQueue = context.pools.addDeviceQueue(); deviceQueue.init(this.currPass, value, this.queueID); this.currQueue = deviceQueue; @@ -1994,17 +1863,13 @@ class PreRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor { scene (value: SceneData): void { if (!this.rg.getValid(this.sceneID)) return; const renderQueue = this.currQueue as DeviceRenderQueue; - const graphScene = context.pools.addGraphScene(); - graphScene.init(value, null, this.sceneID); - const renderScene = renderQueue.addScene(graphScene); + const renderScene = renderQueue.setScene(this.sceneID, value); renderScene.preRecord(); } blit (value: Blit): void { if (!this.rg.getValid(this.sceneID)) return; const renderQueue = this.currQueue as DeviceRenderQueue; - const graphScene = context.pools.addGraphScene(); - graphScene.init(null, value, this.sceneID); - const renderScene = renderQueue.addScene(graphScene); + const renderScene = renderQueue.setScene(this.sceneID, undefined, value); renderScene.preRecord(); } dispatch (value: Dispatch): void { diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index 40354f01797..6ce519a873b 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -1,4 +1,4 @@ -import { Vec3, assert, RecyclePool } from '../../core'; +import { Vec3, RecyclePool } from '../../core'; import { Frustum, intersect, AABB } from '../../core/geometry'; import { CommandBuffer, Device, Buffer, BufferInfo, BufferViewInfo, MemoryUsageBit, BufferUsageBit } from '../../gfx'; import { BatchingSchemes, Pass, RenderScene } from '../../render-scene'; @@ -7,7 +7,7 @@ import { CSMLevel, Camera, DirectionalLight, Light, LightType, Model, PointLight ReflectionProbe, SKYBOX_FLAG, ShadowType, Shadows, SphereLight, SpotLight } from '../../render-scene/scene'; import { Layers, Node } from '../../scene-graph'; import { PipelineSceneData } from '../pipeline-scene-data'; -import { hashCombineStr, getSubpassOrPassID, bool, AlignUp, SetLightUBO } from './define'; +import { hashCombineStr, getSubpassOrPassID, bool, AlignUp, SetLightUBO, hashCombineNum } from './define'; import { LayoutGraphData } from './layout-graph'; import { CullingFlags, RenderGraph, RenderGraphValue, SceneData, RenderQueue as RenderQueue0 } from './render-graph'; import { SceneFlags } from './types'; @@ -29,64 +29,32 @@ class CullingPools { const REFLECTION_PROBE_DEFAULT_MASK = Layers.makeMaskExclude([Layers.BitMask.UI_2D, Layers.BitMask.UI_3D, Layers.BitMask.GIZMOS, Layers.BitMask.EDITOR, Layers.BitMask.SCENE_GIZMO, Layers.BitMask.PROFILER]); - +const objIdMap: WeakMap = new WeakMap(); +let cullingKeys: string = ''; +let objectCount: number = 0; +function objectID (claze: object): number { + if (!objIdMap.has(claze)) objIdMap.set(claze, ++objectCount); + return objIdMap.get(claze)!; +} function computeCullingKey ( sceneData: SceneData, castShadows: boolean, refId: number = -1, -): number { - let hashCode = 0; - const camera = sceneData.camera; - const light = sceneData.light.light; +): string { + cullingKeys = ''; + const camera = sceneData.camera!; + const light = sceneData.light.light!; const lightLevel = sceneData.light.level; - const culledByLight = sceneData.light.culledByLight; - const reflectProbe = sceneData.light.probe; + const reflectProbe = sceneData.light.probe!; const shadeLight = sceneData.shadingLight; - if (camera) { - // camera - hashCode = hashCombineStr(`u${camera.node.uuid}`, hashCode); - hashCode = hashCombineStr(`p${camera.priority}`, hashCode); - hashCode = hashCombineStr(`v${camera.visibility}`, hashCode); - hashCode = hashCombineStr(`f${camera.clearFlag}`, hashCode); - hashCode = hashCombineStr(`cx${camera.clearColor.x}cy${camera.clearColor.y}cz${camera.clearColor.z}cw${camera.clearColor.w}`, hashCode); - hashCode = hashCombineStr(`cd${camera.clearDepth}cs${camera.clearStencil}`, hashCode); - hashCode = hashCombineStr(`pj${camera.projectionType}`, hashCode); - hashCode = hashCombineStr(`fa${camera.fovAxis}`, hashCode); - hashCode = hashCombineStr(`fov${camera.fov}`, hashCode); - hashCode = hashCombineStr(`n${camera.nearClip}`, hashCode); - hashCode = hashCombineStr(`far${camera.farClip}`, hashCode); - hashCode = hashCombineStr(`apt${camera.aperture}`, hashCode); - hashCode = hashCombineStr(`sht${camera.shutter}`, hashCode); - hashCode = hashCombineStr(`iso${camera.iso}`, hashCode); - hashCode = hashCombineStr(`rx${camera.viewport.x}ry${camera.viewport.y}rw${camera.viewport.width}rh${camera.viewport.height}`, hashCode); - hashCode = hashCombineStr(`upp${camera.usePostProcess}`, hashCode); - } - // light - if (light) { - hashCode = hashCombineStr(`u${light.node!.uuid}`, hashCode); - // hashCode = hashCombineStr(`cx${light.finalColor.x}cy${light.finalColor.y}cz${light.finalColor.z}`, hashCode); - // hashCode = hashCombineStr(`ct${light.useColorTemperature}`, hashCode); - // hashCode = hashCombineStr(`ctv${light.colorTemperature}`, hashCode); - // hashCode = hashCombineStr(`vis${light.visibility}`, hashCode); - // hashCode = hashCombineStr(`tp${light.type}`, hashCode); - // switch (light.type) { - // case LightType.DIRECTIONAL: - // hashCode = hashCombineStr(`${(light as DirectionalLight).illuminance}`, hashCode); - // break; - // default: - // } - } - if (shadeLight) { - hashCode = hashCombineStr(`shadeLight${shadeLight.node!.uuid}`, hashCode); - } - hashCode = hashCombineStr(`culledByLight${culledByLight}`, hashCode); - hashCode = hashCombineStr(`cast${castShadows}`, hashCode); - hashCode = hashCombineStr(`level${lightLevel}`, hashCode); - if (reflectProbe) { - hashCode = hashCombineStr(`probe${reflectProbe.getProbeId()}`, hashCode); - } - hashCode = hashCombineStr(`refId${refId}`, hashCode); - return hashCode; + cullingKeys += `${camera ? objectID(camera) : 0}-`; + cullingKeys += `${reflectProbe ? objectID(reflectProbe) : 0}-`; + cullingKeys += `${(refId === -1 && light) ? objectID(light) : 0}-`; + cullingKeys += `${(refId !== -1 && shadeLight) ? objectID(shadeLight) : 0}-`; + cullingKeys += `${refId === -1 ? lightLevel : 0}-`; + cullingKeys += `${castShadows ? 1 : 0}-`; + cullingKeys += `${refId}`; + return cullingKeys; } class FrustumCullingKey { @@ -116,8 +84,8 @@ class LightBoundsCullingKey { } class LightBoundsCulling { - resultKeyIndex: Map = new Map(); - resultIndex: Map = new Map(); + resultKeyIndex: Map = new Map(); + resultIndex: Map = new Map(); update (): void { this.resultIndex.clear(); this.resultKeyIndex.clear(); @@ -141,8 +109,8 @@ let pSceneData: PipelineSceneData; class FrustumCulling { // key: hash val - resultIndex: Map = new Map(); - resultKeyIndex: Map = new Map(); + resultIndex: Map = new Map(); + resultKeyIndex: Map = new Map(); update (): void { this.resultIndex.clear(); this.resultKeyIndex.clear(); @@ -163,16 +131,13 @@ function isReflectProbeMask (model: Model): boolean { const transWorldBounds = new AABB(); function isFrustumVisible (model: Model, frustum: Readonly, castShadow: boolean): boolean { - const modelWorldBounds = model.worldBounds; - if (!modelWorldBounds) { - return false; - } - transWorldBounds.copy(modelWorldBounds); + const modelWorldBounds = model.worldBounds!; const shadows = pSceneData.shadows; - if (shadows.type === ShadowType.Planar && castShadow) { + if (castShadow && shadows.type === ShadowType.Planar) { AABB.transform(transWorldBounds, modelWorldBounds, shadows.matLight); + return !intersect.aabbFrustum(transWorldBounds, frustum); } - return !intersect.aabbFrustum(transWorldBounds, frustum); + return !intersect.aabbFrustum(modelWorldBounds, frustum); } function isIntersectAABB (lAABB: AABB, rAABB: AABB): boolean { @@ -196,11 +161,10 @@ function sceneCulling ( } for (const model of scene.models) { - assert(!!model); if (!model.enabled || !model.node || (castShadow && !model.castShadow)) { continue; } - if (scene && scene.isCulledByLod(camera, model)) { + if (scene.isCulledByLod(camera, model)) { continue; } if (!probe || (probe && probe.probeType === ProbeType.CUBE)) { @@ -230,15 +194,12 @@ function isBlend (pass: Pass): boolean { } return bBlend; } - +const _tempVec3 = new Vec3(); function computeSortingDepth (camera: Camera, model: Model): number { let depth = 0; if (model.node) { - const node = model.transform; - const tempVec3 = vec3Pool.acquire(); - const position = Vec3.subtract(tempVec3, node.worldPosition, camera.position); - depth = position.dot(camera.forward); - vec3Pool.release(tempVec3); + Vec3.subtract(_tempVec3, model.worldBounds ? model.worldBounds.center : model.node.worldPosition, camera.position); + depth = Vec3.dot(_tempVec3, camera.forward); } return depth; } @@ -259,6 +220,7 @@ function addRenderObject ( const subModels = model.subModels; const subModelCount = subModels.length; const skyboxModel = pSceneData.skybox.model; + const depth = computeSortingDepth(camera, model); for (let subModelIdx = 0; subModelIdx < subModelCount; ++subModelIdx) { const subModel = subModels[subModelIdx]; const passes = subModel.passes; @@ -267,7 +229,7 @@ function addRenderObject ( if (probePhase) phaseLayoutId = probeQueue.defaultId; for (let passIdx = 0; passIdx < passCount; ++passIdx) { if (model === skyboxModel && !subModelIdx && !passIdx && isDrawOpaqueOrMask) { - queue.opaqueQueue.add(model, computeSortingDepth(camera, model), subModelIdx, passIdx); + queue.opaqueQueue.add(model, depth, subModelIdx, passIdx); continue; } const pass = passes[passIdx]; @@ -277,7 +239,7 @@ function addRenderObject ( continue; } // check scene flags - const is_blend = isBlend(pass); + const is_blend = pass.blendState.targets[0].blend; const isOpaqueOrMask = !is_blend; if (!isDrawBlend && is_blend) { // skip transparent object @@ -295,13 +257,10 @@ function addRenderObject ( } else { queue.opaqueInstancingQueue.add(pass, subModel, passIdx); } + } else if (is_blend) { + queue.transparentQueue.add(model, depth, subModelIdx, passIdx); } else { - const depth = computeSortingDepth(camera, model); - if (is_blend) { - queue.transparentQueue.add(model, depth, subModelIdx, passIdx); - } else { - queue.opaqueQueue.add(model, depth, subModelIdx, passIdx); - } + queue.opaqueQueue.add(model, depth, subModelIdx, passIdx); } } } @@ -368,9 +327,7 @@ export class SceneCulling { if (!this.enableLightCulling) { return 0xFFFFFFFF; // Return an empty ID. } - assert(!!sceneData.shadingLight, 'shadingLight is expected but not found.'); - const scene = sceneData.scene; - assert(!!scene, 'scene is expected but not found.'); + const scene = sceneData.scene!; let queries = this.lightBoundsCullings.get(scene); if (!queries) { @@ -386,7 +343,6 @@ export class SceneCulling { } const lightBoundsCullingID: LightBoundsCullingID = this.numLightBoundsCulling++; if (this.numLightBoundsCulling > this.lightBoundsCullingResults.length) { - assert(this.numLightBoundsCulling === (this.lightBoundsCullingResults.length + 1)); this.lightBoundsCullingResults.push(this.cullingPools.lightBoundsCullingResultRecycle.add().update()); } queries.resultIndex.set(key, lightBoundsCullingID); @@ -417,7 +373,6 @@ export class SceneCulling { } const frustumCulledResultID: FrustumCullingID = this.numFrustumCulling++; if (this.numFrustumCulling > this.frustumCullingResults.length) { - assert(this.numFrustumCulling === (this.frustumCullingResults.length + 1)); this.frustumCullingResults.push([]); } queries.resultIndex.set(key, frustumCulledResultID); @@ -433,16 +388,11 @@ export class SceneCulling { private createRenderQueue (sceneFlags: SceneFlags, subpassOrPassLayoutID: number): number { const targetID = this.numRenderQueues++; if (this.numRenderQueues > this.renderQueues.length) { - assert(this.numRenderQueues === (this.renderQueues.length + 1)); const renderQueue = this.cullingPools.renderQueueRecycle.add(); renderQueue.update(); this.renderQueues.push(renderQueue); } - assert(targetID < this.renderQueues.length); const rq = this.renderQueues[targetID]; - assert(rq.empty()); - assert(rq.sceneFlags === SceneFlags.NONE); - assert(rq.subpassOrPassLayoutID === 0xFFFFFFFF); rq.sceneFlags = sceneFlags; rq.subpassOrPassLayoutID = subpassOrPassLayoutID; return targetID; @@ -455,7 +405,6 @@ export class SceneCulling { } const sceneData = rg.j(v); if (!sceneData.scene) { - assert(!!sceneData.scene); continue; } const frustumCulledResultID = this.getOrCreateFrustumCulling(v); @@ -473,7 +422,6 @@ export class SceneCulling { uploadInstancing (cmdBuffer: CommandBuffer): void { for (let queueID = 0; queueID !== this.numRenderQueues; ++queueID) { - assert(this.numRenderQueues <= this.renderQueues.length); const queue = this.renderQueues[queueID]; queue.opaqueInstancingQueue.uploadBuffers(cmdBuffer); queue.transparentInstancingQueue.uploadBuffers(cmdBuffer); @@ -483,7 +431,6 @@ export class SceneCulling { private _getPhaseIdFromScene (scene: number): number { const rg: RenderGraph = this.renderGraph; const renderQueueId = rg.getParent(scene); - assert(rg.h(RenderGraphValue.Queue, renderQueueId)); const graphRenderQueue = rg.j(renderQueueId); return graphRenderQueue.phaseID; } @@ -509,37 +456,33 @@ export class SceneCulling { private batchFrustumCulling (pplSceneData: PipelineSceneData): void { for (const [scene, queries] of this.frustumCullings) { - assert(!!scene); for (const [key, frustomCulledResultID] of queries.resultIndex) { const cullingKey = queries.resultKeyIndex.get(key)!; const sceneData = cullingKey.sceneData!; - assert(!!sceneData.camera); - assert(!sceneData.camera.scene || sceneData.camera.scene === scene); const light = sceneData.light.light; const level = sceneData.light.level; const castShadow = cullingKey.castShadows; const probe = sceneData.light.probe; const camera = probe ? probe.camera : sceneData.camera; - assert(frustomCulledResultID < this.frustumCullingResults.length); const models = this.frustumCullingResults[frustomCulledResultID]; if (probe) { - sceneCulling(scene, camera, camera.frustum, castShadow, probe, models); + sceneCulling(scene, camera!, camera!.frustum, castShadow, probe, models); continue; } if (light) { switch (light.type) { case LightType.SPOT: - sceneCulling(scene, camera, (light as SpotLight).frustum, castShadow, null, models); + sceneCulling(scene, camera!, (light as SpotLight).frustum, castShadow, null, models); break; case LightType.DIRECTIONAL: { - const frustum = this.getBuiltinShadowFrustum(pplSceneData, camera, light as DirectionalLight, level); - sceneCulling(scene, camera, frustum, castShadow, null, models); + const frustum = this.getBuiltinShadowFrustum(pplSceneData, camera!, light as DirectionalLight, level); + sceneCulling(scene, camera!, frustum, castShadow, null, models); } break; default: } } else { - sceneCulling(scene, camera, camera.frustum, castShadow, null, models); + sceneCulling(scene, camera!, camera!.frustum, castShadow, null, models); } } } @@ -548,7 +491,6 @@ export class SceneCulling { private executeSphereLightCulling (light: SphereLight, frustumCullingResult: Array, lightBoundsCullingResult: Array): void { const lightAABB = light.aabb; for (const model of frustumCullingResult) { - assert(!!model); const modelBounds = model.worldBounds; if (!modelBounds || intersect.aabbWithAABB(modelBounds, lightAABB)) { lightBoundsCullingResult.push(model); @@ -560,7 +502,6 @@ export class SceneCulling { const lightAABB = light.aabb; const lightFrustum: Frustum = light.frustum; for (const model of frustumCullingResult) { - assert(!!model); const modelBounds = model.worldBounds; if (!modelBounds || (intersect.aabbWithAABB(lightAABB, modelBounds) && intersect.aabbFrustum(modelBounds, lightFrustum))) { lightBoundsCullingResult.push(model); @@ -571,7 +512,6 @@ export class SceneCulling { private executePointLightCulling (light: PointLight, frustumCullingResult: Array, lightBoundsCullingResult: Array): void { const lightAABB = light.aabb; for (const model of frustumCullingResult) { - assert(!!model); const modelBounds = model.worldBounds; if (!modelBounds || intersect.aabbWithAABB(lightAABB, modelBounds)) { lightBoundsCullingResult.push(model); @@ -586,7 +526,6 @@ export class SceneCulling { ): void { rangedDirLightBoundingBox.transform(light.node!.worldMatrix, null, null, null, lightAABB); for (const model of frustumCullingResult) { - assert(!!model); const modelBounds = model.worldBounds; if (!modelBounds || intersect.aabbWithAABB(lightAABB, modelBounds)) { lightBoundsCullingResult.push(model); @@ -596,19 +535,13 @@ export class SceneCulling { private batchLightBoundsCulling (): void { for (const [scene, queries] of this.lightBoundsCullings) { - assert(!!scene); for (const [key, cullingID] of queries.resultIndex) { const cullingKey = queries.resultKeyIndex.get(key)!; const sceneData = cullingKey.sceneData!; const frustumCullingID = cullingKey.frustumCullingID; const frustumCullingResult = this.frustumCullingResults[frustumCullingID]; - assert(!!sceneData.camera); - assert(!!sceneData.shadingLight); - assert(!sceneData.camera.scene || sceneData.camera.scene === scene); - assert(cullingID < this.frustumCullingResults.length); const lightBoundsCullingResult = this.lightBoundsCullingResults[cullingID]; - assert(lightBoundsCullingResult.instances.length === 0); - switch (sceneData.shadingLight.type) { + switch (sceneData.shadingLight!.type) { case LightType.SPHERE: { const light = sceneData.shadingLight as SphereLight; @@ -641,9 +574,25 @@ export class SceneCulling { } } + private _getModelsByCullingResults (lightBoundsCullingID, frustomCulledResultID): Array { + // is culled by light bounds + if (lightBoundsCullingID !== 0xFFFFFFFF) { + if (lightBoundsCullingID < this.lightBoundsCullingResults.length) { + return this.lightBoundsCullingResults[lightBoundsCullingID].instances; + } else { + return []; + } + } + // not culled by light bounds + if (frustomCulledResultID < this.frustumCullingResults.length) { + return this.frustumCullingResults[frustomCulledResultID]; + } else { + return []; + } + } + private fillRenderQueues (rg: RenderGraph, pplSceneData: PipelineSceneData): void { for (const [sceneId, desc] of this.renderQueueIndex) { - assert(rg.h(RenderGraphValue.Scene, sceneId)); const frustomCulledResultID = desc.frustumCulledResultID; const lightBoundsCullingID = desc.lightBoundsCulledResultID; const targetId = desc.renderQueueTarget; @@ -657,38 +606,17 @@ export class SceneCulling { } // render queue info const renderQueueId = rg.getParent(sceneId); - assert(rg.h(RenderGraphValue.Queue, renderQueueId)); const graphRenderQueue = rg.j(renderQueueId); const phaseLayoutId = graphRenderQueue.phaseID; - assert(phaseLayoutId !== this.layoutGraph.N); // culling source - assert(frustomCulledResultID < this.frustumCullingResults.length); - const sourceModels = ((): Array => { - // is culled by light bounds - if (lightBoundsCullingID !== 0xFFFFFFFF) { - if (lightBoundsCullingID < this.lightBoundsCullingResults.length) { - return this.lightBoundsCullingResults[lightBoundsCullingID].instances; - } else { - return []; - } - } - // not culled by light bounds - if (frustomCulledResultID < this.frustumCullingResults.length) { - return this.frustumCullingResults[frustomCulledResultID]; - } else { - return []; - } - })(); + const sourceModels = this._getModelsByCullingResults(lightBoundsCullingID, frustomCulledResultID); // queue target - assert(targetId < this.renderQueues.length); const renderQueue = this.renderQueues[targetId]; - assert(renderQueue.empty()); // skybox const camera = sceneData.camera; - assert(!!camera); // fill render queue for (const model of sourceModels) { addRenderObject( @@ -696,7 +624,7 @@ export class SceneCulling { isDrawOpaqueOrMask, isDrawBlend, isDrawProbe, - camera, + camera!, model, renderQueue, ); @@ -721,8 +649,6 @@ export class LightResource { private readonly lightIndex = new Map(); init (programLib: WebProgramLibrary, deviceIn: Device, maxNumLights: number): void { - assert(!this.device); - this.device = deviceIn; this.programLibrary = programLib; @@ -752,8 +678,6 @@ export class LightResource { )); this.cpuBuffer = new Float32Array(bufferSize / Float32Array.BYTES_PER_ELEMENT); - - assert(!!(this.elementSize && this.maxNumLights)); this.resized = true; } @@ -847,8 +771,6 @@ export class LightResource { this.cpuBuffer.set(prevCpuBuffer); } - assert(this.lights.length < this.maxNumLights); - // Add light const lightID = this.lights.length; this.lights[lightID] = light; diff --git a/cocos/rendering/custom/web-pipeline-types.ts b/cocos/rendering/custom/web-pipeline-types.ts index 7bbc9be648a..0e991ee5646 100644 --- a/cocos/rendering/custom/web-pipeline-types.ts +++ b/cocos/rendering/custom/web-pipeline-types.ts @@ -916,19 +916,14 @@ export class RenderInstancingQueue { if (iter === undefined) { const instanceBufferID = this.passInstances.size; if (instanceBufferID >= this.instanceBuffers.length) { - assert(instanceBufferID === this.instanceBuffers.length); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument this.instanceBuffers.push(new InstancedBuffer(new Pass(cclegacy.director.root))); } this.passInstances.set(pass, instanceBufferID); - assert(instanceBufferID < this.instanceBuffers.length); const instanceBuffer = this.instanceBuffers[instanceBufferID]; instanceBuffer.pass = pass; const instances = instanceBuffer.instances; - for (const item of instances) { - assert(item.count === 0); - } } const instancedBuffer = this.instanceBuffers[this.passInstances.get(pass)!]; diff --git a/cocos/rendering/custom/web-pipeline.ts b/cocos/rendering/custom/web-pipeline.ts index 16c6a47a8f6..a2be1408239 100644 --- a/cocos/rendering/custom/web-pipeline.ts +++ b/cocos/rendering/custom/web-pipeline.ts @@ -24,9 +24,9 @@ /* eslint-disable max-len */ import { systemInfo } from 'pal/system-info'; -import { DEBUG, EDITOR } from 'internal:constants'; +import { EDITOR } from 'internal:constants'; import { DescriptorSetLayout, Device, Feature, Format, FormatFeatureBit, Sampler, Swapchain, Texture, ClearFlagBit, DescriptorSet, deviceManager, Viewport, API, CommandBuffer, Type, SamplerInfo, Filter, Address, DescriptorSetInfo, LoadOp, StoreOp, ShaderStageFlagBit, BufferInfo, TextureInfo, TextureType, ResolveMode, SampleCount, Color, ComparisonFunc } from '../../gfx'; -import { Vec4, assert, macro, cclegacy, RecyclePool } from '../../core'; +import { Vec4, macro, cclegacy, RecyclePool } from '../../core'; import { AccessType, AttachmentType, CopyPair, LightInfo, LightingMode, MovePair, QueueHint, RenderCommonObjectPool, ResolvePair, ResourceDimension, ResourceFlags, ResourceResidency, SceneFlags, UpdateFrequency, UploadPair } from './types'; import { ComputePass, CopyPass, MovePass, RasterPass, RasterSubpass, RenderData, RenderGraph, RenderGraphComponent, RenderGraphValue, RenderQueue, RenderSwapchain, ResourceDesc, ResourceGraph, ResourceGraphValue, ResourceStates, ResourceTraits, SceneData, Subpass, PersistentBuffer, RenderGraphObjectPool, CullingFlags, ManagedResource, ManagedBuffer } from './render-graph'; import { ComputePassBuilder, ComputeQueueBuilder, BasicPipeline, RenderQueueBuilder, RenderSubpassBuilder, PipelineType, BasicRenderPassBuilder, PipelineCapabilities, BasicMultisampleRenderPassBuilder, Setter, SceneBuilder } from './pipeline'; @@ -447,9 +447,6 @@ export class WebRenderSubpassBuilder extends WebSetter implements RenderSubpassB } addQueue (hint: QueueHint = QueueHint.RENDER_OPAQUE, layoutName = 'default', passName = ''): RenderQueueBuilder { const layoutId = this._lg.locateChild(this._layoutID, layoutName); - if (DEBUG) { - assert(layoutId !== 0xFFFFFFFF); - } const queue = renderGraphPool.createRenderQueue(hint, layoutId); const data = renderGraphPool.createRenderData(); const queueID = this._renderGraph.addVertex(RenderGraphValue.Queue, queue, '', layoutName, data, false, this._vertID); @@ -509,9 +506,6 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR this._renderGraph.setName(this._vertID, name); } addRenderTarget (name: string, loadOp = LoadOp.CLEAR, storeOp = StoreOp.STORE, clearColor = new Color()): void { - if (DEBUG) { - assert(Boolean(name && this._resourceGraph.contains(name))); - } let clearFlag = ClearFlagBit.COLOR; if (loadOp === LoadOp.LOAD) { clearFlag = ClearFlagBit.NONE; @@ -529,9 +523,6 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR this._pass.rasterViews.set(name, view); } addDepthStencil (name: string, loadOp = LoadOp.CLEAR, storeOp = StoreOp.STORE, depth = 1, stencil = 0, clearFlag = ClearFlagBit.DEPTH_STENCIL): void { - if (DEBUG) { - assert(Boolean(name && this._resourceGraph.contains(name))); - } const view = renderGraphPool.createRasterView( '', AccessType.WRITE, @@ -558,13 +549,6 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR private _addComputeResource (name: string, accessType: AccessType, slotName: string): void { const view = renderGraphPool.createComputeView(slotName); view.accessType = accessType; - if (DEBUG) { - assert(Boolean(view.name)); - assert(Boolean(name && this._resourceGraph.contains(name))); - const descriptorName = view.name; - const descriptorID = this._lg.attributeIndex.get(descriptorName); - assert(descriptorID !== undefined); - } if (this._pass.computeViews.has(name)) { this._pass.computeViews.get(name)?.push(view); } else { @@ -597,9 +581,6 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR } addQueue (hint: QueueHint = QueueHint.RENDER_OPAQUE, layoutName = 'default', passName = ''): WebRenderQueueBuilder { const layoutId = this._lg.locateChild(this._layoutID, layoutName); - if (DEBUG) { - assert(layoutId !== 0xFFFFFFFF); - } const queue = renderGraphPool.createRenderQueue(hint, layoutId); const data = renderGraphPool.createRenderData(); const queueID = this._renderGraph.addVertex(RenderGraphValue.Queue, queue, '', layoutName, data, false, this._vertID); @@ -769,9 +750,6 @@ export class WebComputePassBuilder extends WebSetter implements ComputePassBuild } addQueue (layoutName = 'default', passName = ''): WebComputeQueueBuilder { const layoutId = this._lg.locateChild(this._layoutID, layoutName); - if (DEBUG) { - assert(layoutId !== 0xFFFFFFFF); - } const queue = renderGraphPool.createRenderQueue(QueueHint.RENDER_OPAQUE, layoutId); const data = renderGraphPool.createRenderData(); const queueID = this._renderGraph.addVertex(RenderGraphValue.Queue, queue, '', layoutName, data, false, this._vertID); @@ -783,13 +761,6 @@ export class WebComputePassBuilder extends WebSetter implements ComputePassBuild private _addComputeResource (name: string, accessType: AccessType, slotName: string): void { const view = renderGraphPool.createComputeView(slotName); view.accessType = accessType; - if (DEBUG) { - assert(Boolean(view.name)); - assert(Boolean(name && this._resourceGraph.contains(name))); - const descriptorName = view.name; - const descriptorID = this._lg.attributeIndex.get(descriptorName); - assert(descriptorID !== undefined); - } if (this._pass.computeViews.has(name)) { this._pass.computeViews.get(name)?.push(view); } else { @@ -938,9 +909,7 @@ export class WebPipeline implements BasicPipeline { desc.flags = ResourceFlags.COLOR_ATTACHMENT; if (!renderWindow.swapchain) { - assert(renderWindow.framebuffer.colorTextures.length === 1 - && renderWindow.framebuffer.colorTextures[0] !== null); - desc.sampleCount = renderWindow.framebuffer.colorTextures[0].info.samples; + desc.sampleCount = renderWindow.framebuffer.colorTextures[0]!.info.samples; return this._resourceGraph.addVertex( ResourceGraphValue.Framebuffer, renderWindow.framebuffer, @@ -1136,11 +1105,6 @@ export class WebPipeline implements BasicPipeline { for (const pair of copyPairs) { const targetName = pair.target; const tarVerId = this.resourceGraph.find(targetName); - if (DEBUG) { - const srcVerId = this.resourceGraph.find(pair.source); - assert(srcVerId !== 0xFFFFFFFF, `The resource named ${pair.source} was not found in Resource Graph.`); - assert(tarVerId !== 0xFFFFFFFF, `The resource named ${targetName} was not found in Resource Graph.`); - } const resDesc = this.resourceGraph.getDesc(tarVerId); const currRaster = this.addRenderPass(resDesc.width, resDesc.height, 'copy-pass'); currRaster.addRenderTarget(targetName, LoadOp.CLEAR, StoreOp.STORE, pipelinePool.createColor()); @@ -1172,7 +1136,6 @@ export class WebPipeline implements BasicPipeline { public getGlobalDescriptorSetData (): DescriptorSetData | undefined { const stageId = this.layoutGraph.locateChild(this.layoutGraph.N, 'default'); - assert(stageId !== 0xFFFFFFFF); const layout = this.layoutGraph.getLayout(stageId); const layoutData = layout.descriptorSets.get(UpdateFrequency.PER_PASS); return layoutData; @@ -1448,9 +1411,7 @@ export class WebPipeline implements BasicPipeline { desc.width = width; desc.height = height; if (swapchain) { - assert(this.resourceGraph.w(resId) === ResourceGraphValue.Swapchain); const sc = this.resourceGraph.j(resId); - assert(!!sc.swapchain); sc.swapchain = swapchain; desc.format = sc.swapchain.depthStencilTexture.format; } else if (format !== Format.UNKNOWN) { @@ -1481,7 +1442,6 @@ export class WebPipeline implements BasicPipeline { desc.flags = ResourceFlags.DEPTH_STENCIL_ATTACHMENT | ResourceFlags.SAMPLED; if (swapchain) { - assert(residency === ResourceResidency.BACKBUFFER); return this._resourceGraph.addVertex( ResourceGraphValue.Swapchain, new RenderSwapchain(swapchain, true), @@ -1646,13 +1606,6 @@ export class WebPipeline implements BasicPipeline { } } addRenderPassImpl (width: number, height: number, layoutName: string, count = 1, quality = 0): BasicMultisampleRenderPassBuilder { - if (DEBUG) { - const stageId = this.layoutGraph.locateChild(this.layoutGraph.N, layoutName); - assert(stageId !== 0xFFFFFFFF); - const layout = this.layoutGraph.getLayout(stageId); - assert(Boolean(layout)); - assert(Boolean(layout.descriptorSets.get(UpdateFrequency.PER_PASS))); - } const name = 'Raster'; const pass = renderGraphPool.createRasterPass(); pass.viewport.width = width; @@ -1672,7 +1625,6 @@ export class WebPipeline implements BasicPipeline { return this.addRenderPassImpl(width, height, layoutName); } addMultisampleRenderPass (width: number, height: number, count: number, quality: number, layoutName = 'default'): BasicMultisampleRenderPassBuilder { - assert(count > 1); return this.addRenderPassImpl(width, height, layoutName, count, quality); } public getDescriptorSetLayout (shaderName: string, freq: UpdateFrequency): DescriptorSetLayout { From 09cada9f35179280c44fe148b12e912837063eae Mon Sep 17 00:00:00 2001 From: GengineJS <476393671@qq.com> Date: Tue, 10 Sep 2024 15:10:19 +0800 Subject: [PATCH 13/21] Optimize scene culling code (#17619) --- cocos/rendering/custom/scene-culling.ts | 41 +++++++++++++------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index 6ce519a873b..a2c1043816f 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -125,6 +125,10 @@ function isModelVisible (model: Model, visibility: number): boolean { return !!(visibility & model.visFlags); } +function isVisible (model: Model, visibility: number): boolean { + return isNodeVisible(model.node, visibility) || isModelVisible(model, visibility); +} + function isReflectProbeMask (model: Model): boolean { return bool((model.node.layer & REFLECTION_PROBE_DEFAULT_MASK) === model.node.layer || (REFLECTION_PROBE_DEFAULT_MASK & model.visFlags)); } @@ -167,33 +171,30 @@ function sceneCulling ( if (scene.isCulledByLod(camera, model)) { continue; } - if (!probe || (probe && probe.probeType === ProbeType.CUBE)) { - if (isNodeVisible(model.node, visibility) - || isModelVisible(model, visibility)) { - const wBounds = model.worldBounds; - // frustum culling - if (wBounds && ((!probe && isFrustumVisible(model, camOrLightFrustum, castShadow)) - || (probe && isIntersectAABB(wBounds, probe.boundingBox!)))) { - continue; - } - - models.push(model); + const wBounds = model.worldBounds; + if (!probe) { + if (!isVisible(model, visibility)) { + continue; + } + // frustum culling + if (wBounds && isFrustumVisible(model, camOrLightFrustum, castShadow)) { + continue; } + models.push(model); + } else if (probe.probeType === ProbeType.CUBE) { + if (!isVisible(model, visibility)) { + continue; + } + if (wBounds && isIntersectAABB(wBounds, probe.boundingBox!)) { + continue; + } + models.push(model); } else if (isReflectProbeMask(model)) { models.push(model); } } } -function isBlend (pass: Pass): boolean { - let bBlend = false; - for (const target of pass.blendState.targets) { - if (target.blend) { - bBlend = true; - } - } - return bBlend; -} const _tempVec3 = new Vec3(); function computeSortingDepth (camera: Camera, model: Model): number { let depth = 0; From d446e3f05a0b14c50eb71d80c3836bf8459db155 Mon Sep 17 00:00:00 2001 From: hyde zhou Date: Wed, 11 Sep 2024 10:09:33 +0800 Subject: [PATCH 14/21] Merge render queue (#17622) --- .../pipeline/custom/LayoutGraphTypes.h | 6 +- .../pipeline/custom/NativeExecutor.cpp | 4 +- .../pipeline/custom/NativePipelineFwd.h | 13 +- .../pipeline/custom/NativePipelineTypes.cpp | 17 +-- .../pipeline/custom/NativePipelineTypes.h | 56 ++++++-- .../pipeline/custom/NativeRenderQueue.cpp | 23 +-- .../pipeline/custom/NativeSceneCulling.cpp | 132 +++++++++++------- .../pipeline/custom/RenderGraphTypes.cpp | 6 +- .../pipeline/custom/RenderGraphTypes.h | 6 +- 9 files changed, 170 insertions(+), 93 deletions(-) diff --git a/native/cocos/renderer/pipeline/custom/LayoutGraphTypes.h b/native/cocos/renderer/pipeline/custom/LayoutGraphTypes.h index b966b47b176..ea518ded3e3 100644 --- a/native/cocos/renderer/pipeline/custom/LayoutGraphTypes.h +++ b/native/cocos/renderer/pipeline/custom/LayoutGraphTypes.h @@ -314,14 +314,14 @@ inline bool operator<(const NameLocalID& lhs, const NameLocalID& rhs) noexcept { struct DescriptorData { DescriptorData() = default; - DescriptorData(NameLocalID descriptorIDIn, gfx::Type typeIn, uint32_t countIn) noexcept + DescriptorData(const NameLocalID& descriptorIDIn, gfx::Type typeIn, uint32_t countIn) noexcept : descriptorID(descriptorIDIn), type(typeIn), count(countIn) {} - DescriptorData(NameLocalID descriptorIDIn, gfx::Type typeIn) noexcept + DescriptorData(const NameLocalID& descriptorIDIn, gfx::Type typeIn) noexcept : descriptorID(descriptorIDIn), type(typeIn) {} - DescriptorData(NameLocalID descriptorIDIn) noexcept // NOLINT + DescriptorData(const NameLocalID& descriptorIDIn) noexcept // NOLINT : descriptorID(descriptorIDIn) {} NameLocalID descriptorID; diff --git a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp index 0f43bc27342..df4f4e8069a 100644 --- a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp @@ -667,10 +667,10 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { tryBindLeafOverwritePerPassDescriptorSet(sceneID); } const auto* scene = camera->getScene(); - const auto& queueDesc = ctx.context.sceneCulling.renderQueueIndex.at(sceneID); + const auto& queueDesc = ctx.context.sceneCulling.renderQueueQueryIndex.at(sceneID); const auto& queue = ctx.context.sceneCulling.renderQueues[queueDesc.renderQueueTarget.value]; - queue.recordCommands(ctx.cmdBuff, ctx.currentPass, 0); + queue.recordCommands(ctx.cmdBuff, ctx.currentPass, 0, sceneData.flags); #if CC_USE_GEOMETRY_RENDERER if (any(sceneData.flags & SceneFlags::GEOMETRY) && diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h b/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h index 62729d3ae84..ffb1d497f3d 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h +++ b/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h @@ -93,7 +93,8 @@ struct LightBoundsCullingID; struct LightBoundsCullingKey; struct LightBoundsCulling; struct NativeRenderQueueID; -struct NativeRenderQueueDesc; +struct NativeRenderQueueKey; +struct NativeRenderQueueQuery; struct LightBoundsCullingResult; struct SceneCulling; struct LightResource; @@ -123,11 +124,21 @@ struct hash { hash_t operator()(const cc::render::FrustumCullingID& val) const noexcept; }; +template <> +struct hash { + hash_t operator()(const cc::render::LightBoundsCullingID& val) const noexcept; +}; + template <> struct hash { hash_t operator()(const cc::render::LightBoundsCullingKey& val) const noexcept; }; +template <> +struct hash { + hash_t operator()(const cc::render::NativeRenderQueueKey& val) const noexcept; +}; + } // namespace ccstd // clang-format on diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp index 1e75b7d1b8f..ad67cf7679a 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp +++ b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp @@ -74,23 +74,14 @@ NativeRenderQueue::NativeRenderQueue(const allocator_type& alloc) noexcept opaqueInstancingQueue(alloc), transparentInstancingQueue(alloc) {} -NativeRenderQueue::NativeRenderQueue(SceneFlags sceneFlagsIn, uint32_t subpassOrPassLayoutIDIn, const allocator_type& alloc) noexcept -: opaqueQueue(alloc), - transparentQueue(alloc), - probeQueue(alloc), - opaqueInstancingQueue(alloc), - transparentInstancingQueue(alloc), - sceneFlags(sceneFlagsIn), - subpassOrPassLayoutID(subpassOrPassLayoutIDIn) {} - NativeRenderQueue::NativeRenderQueue(NativeRenderQueue&& rhs, const allocator_type& alloc) : opaqueQueue(std::move(rhs.opaqueQueue), alloc), transparentQueue(std::move(rhs.transparentQueue), alloc), probeQueue(std::move(rhs.probeQueue), alloc), opaqueInstancingQueue(std::move(rhs.opaqueInstancingQueue), alloc), transparentInstancingQueue(std::move(rhs.transparentInstancingQueue), alloc), + camera(rhs.camera), sceneFlags(rhs.sceneFlags), - subpassOrPassLayoutID(rhs.subpassOrPassLayoutID), lightByteOffset(rhs.lightByteOffset) {} ResourceGroup::ResourceGroup(const allocator_type& alloc) noexcept @@ -195,16 +186,18 @@ SceneCulling::SceneCulling(const allocator_type& alloc) noexcept frustumCullingResults(alloc), lightBoundsCullings(alloc), lightBoundsCullingResults(alloc), + renderQueueIndex(alloc), renderQueues(alloc), - renderQueueIndex(alloc) {} + renderQueueQueryIndex(alloc) {} SceneCulling::SceneCulling(SceneCulling&& rhs, const allocator_type& alloc) : frustumCullings(std::move(rhs.frustumCullings), alloc), frustumCullingResults(std::move(rhs.frustumCullingResults), alloc), lightBoundsCullings(std::move(rhs.lightBoundsCullings), alloc), lightBoundsCullingResults(std::move(rhs.lightBoundsCullingResults), alloc), - renderQueues(std::move(rhs.renderQueues), alloc), renderQueueIndex(std::move(rhs.renderQueueIndex), alloc), + renderQueues(std::move(rhs.renderQueues), alloc), + renderQueueQueryIndex(std::move(rhs.renderQueueQueryIndex), alloc), numFrustumCulling(rhs.numFrustumCulling), numLightBoundsCulling(rhs.numLightBoundsCulling), numRenderQueues(rhs.numRenderQueues), diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h index bddd3632c1f..fbe1bbc7024 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h +++ b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h @@ -1083,7 +1083,6 @@ struct NativeRenderQueue { } NativeRenderQueue(const allocator_type& alloc) noexcept; // NOLINT - NativeRenderQueue(SceneFlags sceneFlagsIn, uint32_t subpassOrPassLayoutIDIn, const allocator_type& alloc) noexcept; NativeRenderQueue(NativeRenderQueue&& rhs, const allocator_type& alloc); NativeRenderQueue(NativeRenderQueue&& rhs) noexcept = default; @@ -1095,15 +1094,15 @@ struct NativeRenderQueue { void clear() noexcept; bool empty() const noexcept; void recordCommands( - gfx::CommandBuffer *cmdBuffer, gfx::RenderPass *renderPass, uint32_t subpassIndex) const; + gfx::CommandBuffer *cmdBuffer, gfx::RenderPass *renderPass, uint32_t subpassIndex, SceneFlags sceneFlags) const; RenderDrawQueue opaqueQueue; RenderDrawQueue transparentQueue; ProbeHelperQueue probeQueue; RenderInstancingQueue opaqueInstancingQueue; RenderInstancingQueue transparentInstancingQueue; + const scene::Camera* camera{nullptr}; SceneFlags sceneFlags{SceneFlags::NONE}; - uint32_t subpassOrPassLayoutID{0xFFFFFFFF}; uint32_t lightByteOffset{0xFFFFFFFF}; }; @@ -1332,6 +1331,15 @@ struct LightBoundsCullingID { uint32_t value{0xFFFFFFFF}; }; +inline bool operator==(const LightBoundsCullingID& lhs, const LightBoundsCullingID& rhs) noexcept { + return std::forward_as_tuple(lhs.value) == + std::forward_as_tuple(rhs.value); +} + +inline bool operator!=(const LightBoundsCullingID& lhs, const LightBoundsCullingID& rhs) noexcept { + return !(lhs == rhs); +} + struct LightBoundsCullingKey { FrustumCullingID frustumCullingID; const scene::Camera* camera{nullptr}; @@ -1374,11 +1382,25 @@ struct NativeRenderQueueID { uint32_t value{0xFFFFFFFF}; }; -struct NativeRenderQueueDesc { +struct NativeRenderQueueKey { + FrustumCullingID frustumCulledResultID; + LightBoundsCullingID lightBoundsCulledResultID; + uint32_t queueLayoutID{0xFFFFFFFF}; +}; + +inline bool operator==(const NativeRenderQueueKey& lhs, const NativeRenderQueueKey& rhs) noexcept { + return std::forward_as_tuple(lhs.frustumCulledResultID, lhs.lightBoundsCulledResultID, lhs.queueLayoutID) == + std::forward_as_tuple(rhs.frustumCulledResultID, rhs.lightBoundsCulledResultID, rhs.queueLayoutID); +} + +inline bool operator!=(const NativeRenderQueueKey& lhs, const NativeRenderQueueKey& rhs) noexcept { + return !(lhs == rhs); +} + +struct NativeRenderQueueQuery { FrustumCullingID frustumCulledResultID; LightBoundsCullingID lightBoundsCulledResultID; NativeRenderQueueID renderQueueTarget; - scene::LightType lightType{scene::LightType::UNKNOWN}; }; struct LightBoundsCullingResult { @@ -1405,18 +1427,20 @@ struct SceneCulling { private: FrustumCullingID getOrCreateFrustumCulling(const SceneData& sceneData); LightBoundsCullingID getOrCreateLightBoundsCulling(const SceneData& sceneData, FrustumCullingID frustumCullingID); - NativeRenderQueueID createRenderQueue(SceneFlags sceneFlags, LayoutGraphData::vertex_descriptor subpassOrPassLayoutID); - void collectCullingQueries(const RenderGraph& rg, const LayoutGraphData& lg); + NativeRenderQueueID getOrCreateRenderQueue( + const NativeRenderQueueKey& renderQueueKey, SceneFlags sceneFlags, const scene::Camera* camera); + void collectCullingQueries(const RenderGraph& rg); void batchFrustumCulling(const NativePipeline& ppl); void batchLightBoundsCulling(); - void fillRenderQueues(const RenderGraph& rg, const pipeline::PipelineSceneData& pplSceneData); + void fillRenderQueues(); public: ccstd::pmr::unordered_map frustumCullings; ccstd::pmr::vector> frustumCullingResults; ccstd::pmr::unordered_map lightBoundsCullings; ccstd::pmr::vector lightBoundsCullingResults; + ccstd::pmr::unordered_map renderQueueIndex; ccstd::pmr::vector renderQueues; - PmrFlatMap renderQueueIndex; + PmrFlatMap renderQueueQueryIndex; uint32_t numFrustumCulling{0}; uint32_t numLightBoundsCulling{0}; uint32_t numRenderQueues{0}; @@ -1754,6 +1778,12 @@ inline hash_t hash::operator()(const cc::render::F return seed; } +inline hash_t hash::operator()(const cc::render::LightBoundsCullingID& val) const noexcept { + hash_t seed = 0; + hash_combine(seed, val.value); + return seed; +} + inline hash_t hash::operator()(const cc::render::LightBoundsCullingKey& val) const noexcept { hash_t seed = 0; hash_combine(seed, val.frustumCullingID); @@ -1763,6 +1793,14 @@ inline hash_t hash::operator()(const cc::rend return seed; } +inline hash_t hash::operator()(const cc::render::NativeRenderQueueKey& val) const noexcept { + hash_t seed = 0; + hash_combine(seed, val.frustumCulledResultID); + hash_combine(seed, val.lightBoundsCulledResultID); + hash_combine(seed, val.queueLayoutID); + return seed; +} + } // namespace ccstd // clang-format on diff --git a/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp b/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp index 9dbf4a81641..effaae9952e 100644 --- a/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp @@ -254,15 +254,20 @@ void NativeRenderQueue::sort() { void NativeRenderQueue::recordCommands( gfx::CommandBuffer *cmdBuffer, gfx::RenderPass *renderPass, - uint32_t subpassIndex) const { - opaqueQueue.recordCommandBuffer( - renderPass, subpassIndex, cmdBuffer, lightByteOffset); - opaqueInstancingQueue.recordCommandBuffer( - renderPass, subpassIndex, cmdBuffer, lightByteOffset); - transparentQueue.recordCommandBuffer( - renderPass, subpassIndex, cmdBuffer, lightByteOffset); - transparentInstancingQueue.recordCommandBuffer( - renderPass, subpassIndex, cmdBuffer, lightByteOffset); + uint32_t subpassIndex, + SceneFlags sceneFlags) const { + if (any(sceneFlags & (SceneFlags::OPAQUE | SceneFlags::MASK))) { + opaqueQueue.recordCommandBuffer( + renderPass, subpassIndex, cmdBuffer, lightByteOffset); + opaqueInstancingQueue.recordCommandBuffer( + renderPass, subpassIndex, cmdBuffer, lightByteOffset); + } + if (any(sceneFlags & SceneFlags::BLEND)) { + transparentQueue.recordCommandBuffer( + renderPass, subpassIndex, cmdBuffer, lightByteOffset); + transparentInstancingQueue.recordCommandBuffer( + renderPass, subpassIndex, cmdBuffer, lightByteOffset); + } } } // namespace render diff --git a/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp b/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp index 0e4a7aacba0..ebb54197adb 100644 --- a/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp @@ -2,7 +2,7 @@ #include "cocos/renderer/pipeline/custom/LayoutGraphUtils.h" #include "cocos/renderer/pipeline/custom/NativeBuiltinUtils.h" #include "cocos/renderer/pipeline/custom/NativePipelineTypes.h" -#include "cocos/renderer/pipeline/custom/NativeRenderGraphUtils.h" +#include "cocos/renderer/pipeline/custom/RenderGraphGraphs.h" #include "cocos/renderer/pipeline/custom/details/GslUtils.h" #include "cocos/renderer/pipeline/custom/details/Range.h" #include "cocos/scene/Octree.h" @@ -31,8 +31,9 @@ void NativeRenderQueue::clear() noexcept { transparentQueue.instances.clear(); opaqueInstancingQueue.clear(); transparentInstancingQueue.clear(); + camera = nullptr; sceneFlags = SceneFlags::NONE; - subpassOrPassLayoutID = 0xFFFFFFFF; + lightByteOffset = 0xFFFFFFFF; } bool NativeRenderQueue::empty() const noexcept { @@ -127,25 +128,47 @@ LightBoundsCullingID SceneCulling::getOrCreateLightBoundsCulling( return iter->second; } -NativeRenderQueueID SceneCulling::createRenderQueue( - SceneFlags sceneFlags, LayoutGraphData::vertex_descriptor subpassOrPassLayoutID) { +NativeRenderQueueID SceneCulling::getOrCreateRenderQueue( + const NativeRenderQueueKey& renderQueueKey, SceneFlags sceneFlags, const scene::Camera* camera) { + constexpr auto categoryMask = SceneFlags::SHADOW_CASTER | SceneFlags::REFLECTION_PROBE; + constexpr auto drawMask = SceneFlags::OPAQUE | SceneFlags::MASK | SceneFlags::BLEND; + constexpr auto allMask = categoryMask | drawMask; + + auto iter = renderQueueIndex.find(renderQueueKey); + if (iter != renderQueueIndex.end()) { + auto& rq = renderQueues[iter->second.value]; + CC_EXPECTS(rq.camera == camera); + CC_EXPECTS((sceneFlags & categoryMask) == (rq.sceneFlags & categoryMask)); + // merge scene flags + rq.sceneFlags |= sceneFlags & drawMask; + return iter->second; + } + const auto targetID = numRenderQueues++; + + // renderQueues are not cleared, so we can reuse the space + // this->renderQueues.size() is more like a capacity if (numRenderQueues > renderQueues.size()) { CC_EXPECTS(numRenderQueues == renderQueues.size() + 1); renderQueues.emplace_back(); } + + // Update render queue index + auto res = renderQueueIndex.emplace(renderQueueKey, NativeRenderQueueID{targetID}); + CC_ENSURES(res.second); + CC_ENSURES(targetID < renderQueues.size()); auto& rq = renderQueues[targetID]; CC_EXPECTS(rq.empty()); + CC_EXPECTS(rq.camera == nullptr); CC_EXPECTS(rq.sceneFlags == SceneFlags::NONE); - CC_EXPECTS(rq.subpassOrPassLayoutID == 0xFFFFFFFF); - rq.sceneFlags = sceneFlags; - rq.subpassOrPassLayoutID = subpassOrPassLayoutID; + + rq.camera = camera; + rq.sceneFlags = sceneFlags & allMask; return NativeRenderQueueID{targetID}; } -void SceneCulling::collectCullingQueries( - const RenderGraph& rg, const LayoutGraphData& lg) { +void SceneCulling::collectCullingQueries(const RenderGraph& rg) { for (const auto vertID : makeRange(vertices(rg))) { if (!holds(vertID, rg)) { continue; @@ -157,21 +180,33 @@ void SceneCulling::collectCullingQueries( } const auto frustomCulledResultID = getOrCreateFrustumCulling(sceneData); const auto lightBoundsCullingID = getOrCreateLightBoundsCulling(sceneData, frustomCulledResultID); - const auto layoutID = getSubpassOrPassID(vertID, rg, lg); - const auto targetID = createRenderQueue(sceneData.flags, layoutID); - const auto lightType = sceneData.light.light - ? sceneData.light.light->getType() - : scene::LightType::UNKNOWN; - - // add render queue to query source - renderQueueIndex.emplace( + + // Get render queue phaseLayoutID + const auto queueID = parent(vertID, rg); + CC_ENSURES(queueID != RenderGraph::null_vertex()); + CC_EXPECTS(holds(queueID, rg)); + const auto& renderQueue = get(QueueTag{}, queueID, rg); + const auto phaseLayoutID = renderQueue.phaseID; + CC_EXPECTS(phaseLayoutID != LayoutGraphData::null_vertex()); + + // Make render queue key + NativeRenderQueueKey renderQueueKey{ + frustomCulledResultID, + lightBoundsCullingID, + phaseLayoutID, + }; + + // Get or create render queue + const auto renderQueueID = getOrCreateRenderQueue(renderQueueKey, sceneData.flags, sceneData.camera); + + // add render queue query + auto res = renderQueueQueryIndex.emplace( vertID, - NativeRenderQueueDesc{ + NativeRenderQueueQuery{ frustomCulledResultID, lightBoundsCullingID, - targetID, - lightType, - }); + renderQueueID}); + CC_ENSURES(res.second); } } @@ -582,31 +617,28 @@ void addRenderObject( } // namespace -void SceneCulling::fillRenderQueues( - const RenderGraph& rg, const pipeline::PipelineSceneData& pplSceneData) { - const auto* const skybox = pplSceneData.getSkybox(); - for (auto&& [sceneID, desc] : renderQueueIndex) { - CC_EXPECTS(holds(sceneID, rg)); - const auto frustomCulledResultID = desc.frustumCulledResultID; - const auto lightBoundsCullingID = desc.lightBoundsCulledResultID; - const auto targetID = desc.renderQueueTarget; - const auto& sceneData = get(SceneTag{}, sceneID, rg); +void SceneCulling::fillRenderQueues() { + for (const auto& [key, targetID] : renderQueueIndex) { + // native queue target + CC_EXPECTS(targetID.value < renderQueues.size()); + auto& nativeQueue = renderQueues[targetID.value]; + CC_EXPECTS(nativeQueue.empty()); + + const auto frustomCulledResultID = key.frustumCulledResultID; + const auto lightBoundsCullingID = key.lightBoundsCulledResultID; // check scene flags - const bool bDrawBlend = any(sceneData.flags & SceneFlags::TRANSPARENT_OBJECT); - const bool bDrawOpaqueOrMask = any(sceneData.flags & (SceneFlags::OPAQUE_OBJECT | SceneFlags::CUTOUT_OBJECT)); - const bool bDrawShadowCaster = any(sceneData.flags & SceneFlags::SHADOW_CASTER); - const bool bDrawProbe = any(sceneData.flags & SceneFlags::REFLECTION_PROBE); + const bool bDrawBlend = any(nativeQueue.sceneFlags & SceneFlags::BLEND); + const bool bDrawOpaqueOrMask = any(nativeQueue.sceneFlags & (SceneFlags::OPAQUE | SceneFlags::MASK)); + const bool bDrawShadowCaster = any(nativeQueue.sceneFlags & SceneFlags::SHADOW_CASTER); + const bool bDrawProbe = any(nativeQueue.sceneFlags & SceneFlags::REFLECTION_PROBE); if (!bDrawShadowCaster && !bDrawBlend && !bDrawOpaqueOrMask && !bDrawProbe) { // nothing to draw continue; } // render queue info - const auto renderQueueID = parent(sceneID, rg); - CC_EXPECTS(holds(renderQueueID, rg)); - const auto& renderQueue = get(QueueTag{}, renderQueueID, rg); - const auto phaseLayoutID = renderQueue.phaseID; + const auto phaseLayoutID = key.queueLayoutID; CC_EXPECTS(phaseLayoutID != LayoutGraphData::null_vertex()); // culling source @@ -621,20 +653,15 @@ void SceneCulling::fillRenderQueues( return frustumCullingResults.at(frustomCulledResultID.value); }(); - // native queue target - CC_EXPECTS(targetID.value < renderQueues.size()); - auto& nativeQueue = renderQueues[targetID.value]; - CC_EXPECTS(nativeQueue.empty()); - // skybox - const auto* camera = sceneData.camera; + const auto* camera = nativeQueue.camera; CC_EXPECTS(camera); // fill native queue for (const auto* const model : sourceModels) { addRenderObject( phaseLayoutID, bDrawOpaqueOrMask, bDrawBlend, - bDrawProbe, *sceneData.camera, *model, nativeQueue); + bDrawProbe, *camera, *model, nativeQueue); } // post-processing @@ -647,10 +674,10 @@ void SceneCulling::buildRenderQueues( const NativePipeline& ppl) { kPipelineSceneData = ppl.pipelineSceneData; kLayoutGraph = ≶ - collectCullingQueries(rg, lg); + collectCullingQueries(rg); batchFrustumCulling(ppl); batchLightBoundsCulling(); // cull frustum-culling's results by light bounds - fillRenderQueues(rg, *ppl.pipelineSceneData); + fillRenderQueues(); } void SceneCulling::clear() noexcept { @@ -670,10 +697,13 @@ void SceneCulling::clear() noexcept { q.clear(); } - // clear render graph scene vertex query index + // clear render queue index renderQueueIndex.clear(); // do not clear this->renderQueues, it is reused to avoid memory allocation + // clear render graph scene vertex query index + renderQueueQueryIndex.clear(); + // reset all counters numFrustumCulling = 0; numLightBoundsCulling = 0; @@ -780,12 +810,12 @@ void LightResource::buildLights( } // assign light byte offset to each queue - for (const auto& [sceneID, desc] : sceneCulling.renderQueueIndex) { + for (const auto& [sceneID, desc] : sceneCulling.renderQueueQueryIndex) { if (desc.lightBoundsCulledResultID.value == 0xFFFFFFFF) { continue; } - const auto lightByteOffset = sceneCulling.lightBoundsCullingResults.at( - desc.lightBoundsCulledResultID.value) + const auto lightByteOffset = sceneCulling.lightBoundsCullingResults + .at(desc.lightBoundsCulledResultID.value) .lightByteOffset; sceneCulling.renderQueues.at(desc.renderQueueTarget.value).lightByteOffset = lightByteOffset; diff --git a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.cpp b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.cpp index fccfe23a326..b080af58e3a 100644 --- a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.cpp +++ b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.cpp @@ -38,7 +38,7 @@ RasterView::RasterView(const allocator_type& alloc) noexcept : slotName(alloc), slotName1(alloc) {} -RasterView::RasterView(ccstd::pmr::string slotNameIn, AccessType accessTypeIn, AttachmentType attachmentTypeIn, gfx::LoadOp loadOpIn, gfx::StoreOp storeOpIn, gfx::ClearFlagBit clearFlagsIn, gfx::Color clearColorIn, gfx::ShaderStageFlagBit shaderStageFlagsIn, const allocator_type& alloc) noexcept // NOLINT +RasterView::RasterView(ccstd::pmr::string slotNameIn, AccessType accessTypeIn, AttachmentType attachmentTypeIn, gfx::LoadOp loadOpIn, gfx::StoreOp storeOpIn, gfx::ClearFlagBit clearFlagsIn, const gfx::Color& clearColorIn, gfx::ShaderStageFlagBit shaderStageFlagsIn, const allocator_type& alloc) noexcept // NOLINT : slotName(std::move(slotNameIn), alloc), slotName1(alloc), accessType(accessTypeIn), @@ -49,7 +49,7 @@ RasterView::RasterView(ccstd::pmr::string slotNameIn, AccessType accessTypeIn, A clearColor(clearColorIn), shaderStageFlags(shaderStageFlagsIn) {} -RasterView::RasterView(ccstd::pmr::string slotNameIn, ccstd::pmr::string slotName1In, AccessType accessTypeIn, AttachmentType attachmentTypeIn, gfx::LoadOp loadOpIn, gfx::StoreOp storeOpIn, gfx::ClearFlagBit clearFlagsIn, gfx::Color clearColorIn, gfx::ShaderStageFlagBit shaderStageFlagsIn, const allocator_type& alloc) noexcept // NOLINT +RasterView::RasterView(ccstd::pmr::string slotNameIn, ccstd::pmr::string slotName1In, AccessType accessTypeIn, AttachmentType attachmentTypeIn, gfx::LoadOp loadOpIn, gfx::StoreOp storeOpIn, gfx::ClearFlagBit clearFlagsIn, const gfx::Color& clearColorIn, gfx::ShaderStageFlagBit shaderStageFlagsIn, const allocator_type& alloc) noexcept // NOLINT : slotName(std::move(slotNameIn), alloc), slotName1(std::move(slotName1In), alloc), accessType(accessTypeIn), @@ -378,7 +378,7 @@ RaytracePass::RaytracePass(RaytracePass const& rhs, const allocator_type& alloc) ClearView::ClearView(const allocator_type& alloc) noexcept : slotName(alloc) {} -ClearView::ClearView(ccstd::pmr::string slotNameIn, gfx::ClearFlagBit clearFlagsIn, gfx::Color clearColorIn, const allocator_type& alloc) noexcept +ClearView::ClearView(ccstd::pmr::string slotNameIn, gfx::ClearFlagBit clearFlagsIn, const gfx::Color& clearColorIn, const allocator_type& alloc) noexcept : slotName(std::move(slotNameIn), alloc), clearFlags(clearFlagsIn), clearColor(clearColorIn) {} diff --git a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h index 7f7ee8197d3..524b5ad0367 100644 --- a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h +++ b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h @@ -85,8 +85,8 @@ struct RasterView { } RasterView(const allocator_type& alloc = boost::container::pmr::get_default_resource()) noexcept; // NOLINT - RasterView(ccstd::pmr::string slotNameIn, AccessType accessTypeIn, AttachmentType attachmentTypeIn, gfx::LoadOp loadOpIn, gfx::StoreOp storeOpIn, gfx::ClearFlagBit clearFlagsIn, gfx::Color clearColorIn, gfx::ShaderStageFlagBit shaderStageFlagsIn, const allocator_type& alloc = boost::container::pmr::get_default_resource()) noexcept; - RasterView(ccstd::pmr::string slotNameIn, ccstd::pmr::string slotName1In, AccessType accessTypeIn, AttachmentType attachmentTypeIn, gfx::LoadOp loadOpIn, gfx::StoreOp storeOpIn, gfx::ClearFlagBit clearFlagsIn, gfx::Color clearColorIn, gfx::ShaderStageFlagBit shaderStageFlagsIn, const allocator_type& alloc = boost::container::pmr::get_default_resource()) noexcept; + RasterView(ccstd::pmr::string slotNameIn, AccessType accessTypeIn, AttachmentType attachmentTypeIn, gfx::LoadOp loadOpIn, gfx::StoreOp storeOpIn, gfx::ClearFlagBit clearFlagsIn, const gfx::Color& clearColorIn, gfx::ShaderStageFlagBit shaderStageFlagsIn, const allocator_type& alloc = boost::container::pmr::get_default_resource()) noexcept; + RasterView(ccstd::pmr::string slotNameIn, ccstd::pmr::string slotName1In, AccessType accessTypeIn, AttachmentType attachmentTypeIn, gfx::LoadOp loadOpIn, gfx::StoreOp storeOpIn, gfx::ClearFlagBit clearFlagsIn, const gfx::Color& clearColorIn, gfx::ShaderStageFlagBit shaderStageFlagsIn, const allocator_type& alloc = boost::container::pmr::get_default_resource()) noexcept; RasterView(RasterView&& rhs, const allocator_type& alloc); RasterView(RasterView const& rhs, const allocator_type& alloc); @@ -843,7 +843,7 @@ struct ClearView { } ClearView(const allocator_type& alloc = boost::container::pmr::get_default_resource()) noexcept; // NOLINT - ClearView(ccstd::pmr::string slotNameIn, gfx::ClearFlagBit clearFlagsIn, gfx::Color clearColorIn, const allocator_type& alloc = boost::container::pmr::get_default_resource()) noexcept; + ClearView(ccstd::pmr::string slotNameIn, gfx::ClearFlagBit clearFlagsIn, const gfx::Color& clearColorIn, const allocator_type& alloc = boost::container::pmr::get_default_resource()) noexcept; ClearView(ClearView&& rhs, const allocator_type& alloc); ClearView(ClearView const& rhs, const allocator_type& alloc); From 6434f14fe4e0d035282ad711fb84bf929b220006 Mon Sep 17 00:00:00 2001 From: GengineJS <476393671@qq.com> Date: Thu, 12 Sep 2024 18:45:35 +0800 Subject: [PATCH 15/21] Fix the issue where the custom pipeline's RenderTexture output is incorrect. (#17632) --- cocos/rendering/custom/compiler.ts | 76 ++++++++++++------------- cocos/rendering/custom/define.ts | 18 +++--- cocos/rendering/custom/executor.ts | 7 ++- cocos/rendering/custom/scene-culling.ts | 16 +++--- 4 files changed, 56 insertions(+), 61 deletions(-) diff --git a/cocos/rendering/custom/compiler.ts b/cocos/rendering/custom/compiler.ts index f81c57817c8..24fcd50f4f9 100644 --- a/cocos/rendering/custom/compiler.ts +++ b/cocos/rendering/custom/compiler.ts @@ -34,53 +34,51 @@ import { RenderGraphValue, } from './render-graph'; import { AccessType, ResourceResidency, SceneFlags } from './types'; -import { hashCombineNum, hashCombineStr } from './define'; +import { hashCombineKey, hashCombineStr } from './define'; function genHashValue (pass: RasterPass): void { - let hashCode = 0; + let hashCode = ''; for (const [name, raster] of pass.rasterViews) { - hashCode = hashCombineStr('raster', hashCode); - hashCode = hashCombineStr(name, hashCode); - hashCode = hashCombineStr(raster.slotName, hashCode); - hashCode = hashCombineNum(raster.accessType, hashCode); - hashCode = hashCombineNum(raster.attachmentType, hashCode); - hashCode = hashCombineNum(raster.loadOp, hashCode); - hashCode = hashCombineNum(raster.storeOp, hashCode); - hashCode = hashCombineNum(raster.clearFlags, hashCode); - hashCode = hashCombineNum(raster.clearColor.x, hashCode); - hashCode = hashCombineNum(raster.clearColor.y, hashCode); - hashCode = hashCombineNum(raster.clearColor.z, hashCode); - hashCode = hashCombineNum(raster.clearColor.w, hashCode); - hashCode = hashCombineNum(raster.slotID, hashCode); - hashCode = hashCombineNum(raster.shaderStageFlags, hashCode); + hashCode += hashCombineKey(name); + hashCode += hashCombineKey(raster.slotName); + hashCode += hashCombineKey(raster.accessType); + hashCode += hashCombineKey(raster.attachmentType); + hashCode += hashCombineKey(raster.loadOp); + hashCode += hashCombineKey(raster.storeOp); + hashCode += hashCombineKey(raster.clearFlags); + hashCode += hashCombineKey(raster.clearColor.x); + hashCode += hashCombineKey(raster.clearColor.y); + hashCode += hashCombineKey(raster.clearColor.z); + hashCode += hashCombineKey(raster.clearColor.w); + hashCode += hashCombineKey(raster.slotID); + hashCode += hashCombineKey(raster.shaderStageFlags); } for (const [name, computes] of pass.computeViews) { - hashCode = hashCombineStr(name, hashCode); + hashCode += hashCombineKey(name); for (const compute of computes) { - hashCode = hashCombineStr('compute', hashCode); - hashCode = hashCombineStr(compute.name, hashCode); - hashCode = hashCombineNum(compute.accessType, hashCode); - hashCode = hashCombineNum(compute.clearFlags, hashCode); - hashCode = hashCombineNum(compute.clearValueType, hashCode); - hashCode = hashCombineNum(compute.clearValue.x, hashCode); - hashCode = hashCombineNum(compute.clearValue.y, hashCode); - hashCode = hashCombineNum(compute.clearValue.z, hashCode); - hashCode = hashCombineNum(compute.clearValue.w, hashCode); - hashCode = hashCombineNum(compute.shaderStageFlags, hashCode); + hashCode += hashCombineKey(compute.name); + hashCode += hashCombineKey(compute.accessType); + hashCode += hashCombineKey(compute.clearFlags); + hashCode += hashCombineKey(compute.clearValueType); + hashCode += hashCombineKey(compute.clearValue.x); + hashCode += hashCombineKey(compute.clearValue.y); + hashCode += hashCombineKey(compute.clearValue.z); + hashCode += hashCombineKey(compute.clearValue.w); + hashCode += hashCombineKey(compute.shaderStageFlags); } } - hashCode = hashCombineNum(pass.width, hashCode); - hashCode = hashCombineNum(pass.height, hashCode); - hashCode = hashCombineNum(pass.viewport.left, hashCode); - hashCode = hashCombineNum(pass.viewport.top, hashCode); - hashCode = hashCombineNum(pass.viewport.width, hashCode); - hashCode = hashCombineNum(pass.viewport.height, hashCode); - hashCode = hashCombineNum(pass.viewport.minDepth, hashCode); - hashCode = hashCombineNum(pass.viewport.maxDepth, hashCode); - hashCode = hashCombineNum(pass.showStatistics ? 1 : 0, hashCode); - pass.hashValue = hashCode; + hashCode += hashCombineKey(pass.width); + hashCode += hashCombineKey(pass.height); + hashCode += hashCombineKey(pass.viewport.left); + hashCode += hashCombineKey(pass.viewport.top); + hashCode += hashCombineKey(pass.viewport.width); + hashCode += hashCombineKey(pass.viewport.height); + hashCode += hashCombineKey(pass.viewport.minDepth); + hashCode += hashCombineKey(pass.viewport.maxDepth); + hashCode += hashCombineKey(pass.showStatistics ? 1 : 0); + pass.hashValue = hashCombineStr(hashCode); } - +const readViews: Map = new Map(); class PassVisitor implements RenderGraphVisitor { public queueID = 0xFFFFFFFF; public sceneID = 0xFFFFFFFF; @@ -182,7 +180,7 @@ class PassVisitor implements RenderGraphVisitor { } const outputId = this.resID; const outputName = this.context.resourceGraph.vertexName(outputId); - const readViews: Map = new Map(); + readViews.clear(); const pass = this._currPass! as RasterPass; const validPass = rg.getValid(this.passID); for (const [readName, raster] of pass.rasterViews) { diff --git a/cocos/rendering/custom/define.ts b/cocos/rendering/custom/define.ts index acecc893e38..1ea2e8e5dd8 100644 --- a/cocos/rendering/custom/define.ts +++ b/cocos/rendering/custom/define.ts @@ -685,22 +685,18 @@ export function updatePerPassUBO (layout: string, sceneId: number, idxRD: number descriptorSet.update(); } -function hashCombine (hash, currHash: number): number { - return currHash ^= (hash >>> 0) + 0x9e3779b9 + (currHash << 6) + (currHash >> 2); +export function hashCombineKey (val): string { + return `${val}-`; } -export function hashCombineNum (val: number, currHash: number): number { - const hash = 5381; - return hashCombine((hash * 33) ^ val, currHash); -} - -export function hashCombineStr (str: string, currHash: number): number { +export function hashCombineStr (str: string): number { // DJB2 HASH - let hash = 5381; + let hash = 0; for (let i = 0; i < str.length; i++) { - hash = (hash * 33) ^ str.charCodeAt(i); + hash = ((hash << 5) - hash) + str.charCodeAt(i); + hash |= 0;// Convert to 32bit integer } - return hashCombine(hash, currHash); + return hash; } export function bool (val): boolean { diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index 266f8eb1375..926e218984e 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -795,7 +795,6 @@ class DeviceRenderPass implements RecordingInterface { } if (!swapchain) swapchain = resTex.swapchain; if (!framebuffer) framebuffer = resTex.framebuffer; - const clearFlag = rasterV.clearFlags & 0xffffffff; switch (rasterV.attachmentType) { case AttachmentType.RENDER_TARGET: { @@ -809,7 +808,9 @@ class DeviceRenderPass implements RecordingInterface { rasterV.loadOp === LoadOp.LOAD ? AccessFlagBit.COLOR_ATTACHMENT_WRITE : AccessFlagBit.NONE, rasterV.storeOp === StoreOp.STORE ? AccessFlagBit.COLOR_ATTACHMENT_WRITE : AccessFlagBit.NONE, )); - this._clearColor.push(rasterV.clearColor); + const currCol = new Color(); + currCol.copy(rasterV.clearColor); + this._clearColor.push(currCol); colors.push(colorAttachment); } break; @@ -1033,7 +1034,7 @@ class DeviceRenderPass implements RecordingInterface { if (currFBDepthTex && !isInsideTexDestroy) { isInsideTexDestroy = currFBDepthTex.getTextureHandle() === 0; } - const needRebuild = (width !== currentWidth) || (height !== currentHeight) || currFramebuffer.needRebuild || isInsideTexDestroy; + const needRebuild = width !== currentWidth || height !== currentHeight || currFramebuffer.needRebuild || isInsideTexDestroy; for (const [resName, rasterV] of pass.rasterViews) { let deviceTex = context.deviceTextures.get(resName)!; const currTex = deviceTex; diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index a2c1043816f..66ce44f628a 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -7,7 +7,7 @@ import { CSMLevel, Camera, DirectionalLight, Light, LightType, Model, PointLight ReflectionProbe, SKYBOX_FLAG, ShadowType, Shadows, SphereLight, SpotLight } from '../../render-scene/scene'; import { Layers, Node } from '../../scene-graph'; import { PipelineSceneData } from '../pipeline-scene-data'; -import { hashCombineStr, getSubpassOrPassID, bool, AlignUp, SetLightUBO, hashCombineNum } from './define'; +import { getSubpassOrPassID, bool, AlignUp, SetLightUBO, hashCombineKey } from './define'; import { LayoutGraphData } from './layout-graph'; import { CullingFlags, RenderGraph, RenderGraphValue, SceneData, RenderQueue as RenderQueue0 } from './render-graph'; import { SceneFlags } from './types'; @@ -47,13 +47,13 @@ function computeCullingKey ( const lightLevel = sceneData.light.level; const reflectProbe = sceneData.light.probe!; const shadeLight = sceneData.shadingLight; - cullingKeys += `${camera ? objectID(camera) : 0}-`; - cullingKeys += `${reflectProbe ? objectID(reflectProbe) : 0}-`; - cullingKeys += `${(refId === -1 && light) ? objectID(light) : 0}-`; - cullingKeys += `${(refId !== -1 && shadeLight) ? objectID(shadeLight) : 0}-`; - cullingKeys += `${refId === -1 ? lightLevel : 0}-`; - cullingKeys += `${castShadows ? 1 : 0}-`; - cullingKeys += `${refId}`; + cullingKeys += hashCombineKey(camera ? objectID(camera) : 0); + cullingKeys += hashCombineKey(reflectProbe ? objectID(reflectProbe) : 0); + cullingKeys += hashCombineKey((refId === -1 && light) ? objectID(light) : 0); + cullingKeys += hashCombineKey((refId !== -1 && shadeLight) ? objectID(shadeLight) : 0); + cullingKeys += hashCombineKey(refId === -1 ? lightLevel : 0); + cullingKeys += hashCombineKey(castShadows ? 1 : 0); + cullingKeys += hashCombineKey(refId); return cullingKeys; } From 90ff207db09aa93d2dcab269683ee50b54a220de Mon Sep 17 00:00:00 2001 From: hyde zhou Date: Fri, 13 Sep 2024 10:25:10 +0800 Subject: [PATCH 16/21] Merge render queue (Web). (#17628) --- cocos/rendering/custom/executor.ts | 6 +- cocos/rendering/custom/scene-culling.ts | 157 +++++++++++++------ cocos/rendering/custom/web-pipeline-types.ts | 27 ++-- 3 files changed, 129 insertions(+), 61 deletions(-) diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index 926e218984e..f3b4d222282 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -1339,11 +1339,11 @@ class DeviceRenderScene implements RecordingInterface { this._recordBlit(); return; } - const renderQueueDesc = sceneCulling.renderQueueIndex.get(this.sceneID)!; - const renderQueue = sceneCulling.renderQueues[renderQueueDesc.renderQueueTarget]; + const renderQueueQuery = sceneCulling.renderQueueQueryIndex.get(this.sceneID)!; + const renderQueue = sceneCulling.renderQueues[renderQueueQuery.renderQueueTarget]; const graphSceneData = this.sceneData!; if (bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE)) renderQueue.probeQueue.applyMacro(); - renderQueue.recordCommands(context.commandBuffer, this._renderPass); + renderQueue.recordCommands(context.commandBuffer, this._renderPass, graphSceneData.flags); if (bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE)) renderQueue.probeQueue.removeMacro(); if (graphSceneData.flags & SceneFlags.GEOMETRY) { this.camera!.geometryRenderer?.render( diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index 66ce44f628a..e7e333d4e4c 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -1,17 +1,18 @@ -import { Vec3, RecyclePool } from '../../core'; +import { DEBUG } from 'internal:constants'; +import { Vec3, RecyclePool, assert } from '../../core'; import { Frustum, intersect, AABB } from '../../core/geometry'; import { CommandBuffer, Device, Buffer, BufferInfo, BufferViewInfo, MemoryUsageBit, BufferUsageBit } from '../../gfx'; -import { BatchingSchemes, Pass, RenderScene } from '../../render-scene'; +import { BatchingSchemes, RenderScene } from '../../render-scene'; import { CSMLevel, Camera, DirectionalLight, Light, LightType, Model, PointLight, ProbeType, RangedDirectionalLight, ReflectionProbe, SKYBOX_FLAG, ShadowType, Shadows, SphereLight, SpotLight } from '../../render-scene/scene'; import { Layers, Node } from '../../scene-graph'; import { PipelineSceneData } from '../pipeline-scene-data'; -import { getSubpassOrPassID, bool, AlignUp, SetLightUBO, hashCombineKey } from './define'; +import { bool, AlignUp, SetLightUBO, hashCombineKey } from './define'; import { LayoutGraphData } from './layout-graph'; import { CullingFlags, RenderGraph, RenderGraphValue, SceneData, RenderQueue as RenderQueue0 } from './render-graph'; import { SceneFlags } from './types'; -import { RenderQueue, RenderQueueDesc, instancePool } from './web-pipeline-types'; +import { RenderQueue, RenderQueueQuery, instancePool } from './web-pipeline-types'; import { ObjectPool } from './utils'; import { getUniformBlockSize } from './layout-graph-utils'; import { WebProgramLibrary } from './web-program-library'; @@ -24,7 +25,7 @@ class CullingPools { lightBoundsCullingResultRecycle = new RecyclePool(() => new LightBoundsCullingResult(), 8); lightBoundsCullingKeyRecycle = new RecyclePool(() => new LightBoundsCullingKey(), 8); renderQueueRecycle = new RecyclePool(() => new RenderQueue(), 8); - renderQueueDescRecycle = new RecyclePool(() => new RenderQueueDesc(), 8); + renderQueueQueryRecycle = new RecyclePool(() => new RenderQueueQuery(), 8); } const REFLECTION_PROBE_DEFAULT_MASK = Layers.makeMaskExclude([Layers.BitMask.UI_2D, Layers.BitMask.UI_3D, Layers.BitMask.GIZMOS, Layers.BitMask.EDITOR, @@ -104,6 +105,20 @@ class LightBoundsCullingResult { type FrustumCullingID = number; type LightBoundsCullingID = number; +type RenderQueueID = number; + +function makeRenderQueueKey ( + frustumCulledResultID: FrustumCullingID, + lightBoundsCulledResultID: LightBoundsCullingID, + queueLayoutID: RenderQueueID, +): string { + return `${frustumCulledResultID}-${lightBoundsCulledResultID}-${queueLayoutID}`; +} + +function extractRenderQueueKey (key: string): number[] { + const keys = key.split('-'); + return [parseInt(keys[0]), parseInt(keys[1]), parseInt(keys[2])]; +} let pSceneData: PipelineSceneData; @@ -273,8 +288,9 @@ export class SceneCulling { frustumCullingResults: Array> = new Array>(); lightBoundsCullings: Map = new Map(); lightBoundsCullingResults: Array = new Array(); + renderQueueIndex: Map = new Map(); renderQueues: Array = new Array(); - renderQueueIndex: Map = new Map(); + renderQueueQueryIndex: Map = new Map(); cullingPools = new CullingPools(); // source id numFrustumCulling = 0; @@ -284,6 +300,11 @@ export class SceneCulling { layoutGraph; renderGraph!: RenderGraph; enableLightCulling = true; + + readonly kFilterMask = SceneFlags.SHADOW_CASTER | SceneFlags.REFLECTION_PROBE; + readonly kDrawMask = SceneFlags.OPAQUE | SceneFlags.MASK | SceneFlags.BLEND; + readonly kAllMask = this.kFilterMask | this.kDrawMask; + resetPool (): void { const cullingPools = this.cullingPools; cullingPools.frustumCullingKeyRecycle.reset(); @@ -292,7 +313,7 @@ export class SceneCulling { cullingPools.lightBoundsCullingResultRecycle.reset(); cullingPools.lightBoundsCullingKeyRecycle.reset(); cullingPools.renderQueueRecycle.reset(); - cullingPools.renderQueueDescRecycle.reset(); + cullingPools.renderQueueQueryRecycle.reset(); instancePool.reset(); } clear (): void { @@ -301,8 +322,9 @@ export class SceneCulling { this.frustumCullingResults.length = 0; this.lightBoundsCullings.clear(); this.lightBoundsCullingResults.length = 0; - this.renderQueues.length = 0; this.renderQueueIndex.clear(); + this.renderQueues.length = 0; + this.renderQueueQueryIndex.clear(); this.numLightBoundsCulling = 0; this.numFrustumCulling = 0; this.numRenderQueues = 0; @@ -312,10 +334,10 @@ export class SceneCulling { this.layoutGraph = lg; this.renderGraph = rg; pSceneData = pplSceneData; - this.collectCullingQueries(rg, lg); + this.collectCullingQueries(rg); this.batchFrustumCulling(pplSceneData); this.batchLightBoundsCulling(); - this.fillRenderQueues(rg, pplSceneData); + this.fillRenderQueues(); } private getOrCreateLightBoundsCulling (sceneData: SceneData, frustumCullingID: FrustumCullingID): LightBoundsCullingID { @@ -386,20 +408,47 @@ export class SceneCulling { return frustumCulledResultID; } - private createRenderQueue (sceneFlags: SceneFlags, subpassOrPassLayoutID: number): number { + private getOrCreateRenderQueue (renderQueueKey: string, sceneFlags: SceneFlags, camera: Camera | null): number { + const renderQueueID = this.renderQueueIndex.get(renderQueueKey); + if (renderQueueID !== undefined) { + const rq = this.renderQueues[renderQueueID]; + if (DEBUG) { + assert(rq.camera === camera); + assert((rq.sceneFlags & this.kFilterMask) === (sceneFlags & this.kFilterMask)); + } + rq.sceneFlags |= sceneFlags & this.kDrawMask; + return renderQueueID; + } + const targetID = this.numRenderQueues++; + + // renderQueues are not cleared, so we can reuse the space + // this->renderQueues.size() is more like a capacity if (this.numRenderQueues > this.renderQueues.length) { const renderQueue = this.cullingPools.renderQueueRecycle.add(); renderQueue.update(); this.renderQueues.push(renderQueue); } const rq = this.renderQueues[targetID]; - rq.sceneFlags = sceneFlags; - rq.subpassOrPassLayoutID = subpassOrPassLayoutID; + + // Update render queue index + this.renderQueueIndex.set(renderQueueKey, targetID); + + // Update render queue + if (DEBUG) { + assert(rq.empty()); + assert(rq.camera === null); + assert(rq.sceneFlags === SceneFlags.NONE); + assert(camera !== null); + assert(this.renderQueueIndex.size === this.numRenderQueues); + } + rq.camera = camera; + rq.sceneFlags = sceneFlags & this.kAllMask; + return targetID; } - private collectCullingQueries (rg: RenderGraph, lg: LayoutGraphData): void { + private collectCullingQueries (rg: RenderGraph): void { for (const v of rg.v()) { if (!rg.h(RenderGraphValue.Scene, v) || !rg.getValid(v)) { continue; @@ -410,14 +459,32 @@ export class SceneCulling { } const frustumCulledResultID = this.getOrCreateFrustumCulling(v); const lightBoundsCullingID = this.getOrCreateLightBoundsCulling(sceneData, frustumCulledResultID); - const layoutID: number = getSubpassOrPassID(v, rg, lg); - const targetID = this.createRenderQueue(sceneData.flags, layoutID); - const lightType = sceneData.light.light ? sceneData.light.light.type : LightType.UNKNOWN; - const renderQueueDesc = this.cullingPools.renderQueueDescRecycle.add(); - renderQueueDesc.update(frustumCulledResultID, lightBoundsCullingID, targetID, lightType); + // Get render queue phaseLayoutID + const queueID = rg.getParent(v); + if (DEBUG) { + assert(queueID !== 0xFFFFFFFF); + assert(rg.h(RenderGraphValue.Queue, queueID)); + } + const renderQueue = rg.j(queueID); + const phaseLayoutID = renderQueue.phaseID; + + // Make render queue key + const renderQueueKey = makeRenderQueueKey( + frustumCulledResultID, + lightBoundsCullingID, + phaseLayoutID, + ); + + // Get or create render queue + const renderQueueID = this.getOrCreateRenderQueue(renderQueueKey, sceneData.flags, sceneData.camera); + + // add render queue query + const renderQueueQuery = this.cullingPools.renderQueueQueryRecycle.add(); + renderQueueQuery.update(frustumCulledResultID, lightBoundsCullingID, renderQueueID); + // add render queue to query source - this.renderQueueIndex.set(v, renderQueueDesc); + this.renderQueueQueryIndex.set(v, renderQueueQuery); } } @@ -592,40 +659,42 @@ export class SceneCulling { } } - private fillRenderQueues (rg: RenderGraph, pplSceneData: PipelineSceneData): void { - for (const [sceneId, desc] of this.renderQueueIndex) { - const frustomCulledResultID = desc.frustumCulledResultID; - const lightBoundsCullingID = desc.lightBoundsCulledResultID; - const targetId = desc.renderQueueTarget; - const sceneData = rg.j(sceneId); - const isDrawBlend: boolean = bool(sceneData.flags & SceneFlags.TRANSPARENT_OBJECT); - const isDrawOpaqueOrMask: boolean = bool(sceneData.flags & (SceneFlags.OPAQUE_OBJECT | SceneFlags.CUTOUT_OBJECT)); - const isDrawShadowCaster: boolean = bool(sceneData.flags & SceneFlags.SHADOW_CASTER); - const isDrawProbe: boolean = bool(sceneData.flags & SceneFlags.REFLECTION_PROBE); + private fillRenderQueues (): void { + for (const [key, targetID] of this.renderQueueIndex) { + // render queue target + const renderQueue = this.renderQueues[targetID]; + if (DEBUG) { + assert(targetID < this.renderQueues.length); + assert(renderQueue.empty()); + } + + const [frustomCulledResultID, lightBoundsCullingID, phaseLayoutID] = extractRenderQueueKey(key); + + // check scene flags + const isDrawBlend: boolean = bool(renderQueue.sceneFlags & SceneFlags.BLEND); + const isDrawOpaqueOrMask: boolean = bool(renderQueue.sceneFlags & (SceneFlags.OPAQUE | SceneFlags.MASK)); + const isDrawShadowCaster: boolean = bool(renderQueue.sceneFlags & SceneFlags.SHADOW_CASTER); + const isDrawProbe: boolean = bool(renderQueue.sceneFlags & SceneFlags.REFLECTION_PROBE); + if (!isDrawShadowCaster && !isDrawBlend && !isDrawOpaqueOrMask && !isDrawProbe) { + // nothing to draw continue; } - // render queue info - const renderQueueId = rg.getParent(sceneId); - const graphRenderQueue = rg.j(renderQueueId); - const phaseLayoutId = graphRenderQueue.phaseID; // culling source const sourceModels = this._getModelsByCullingResults(lightBoundsCullingID, frustomCulledResultID); - // queue target - const renderQueue = this.renderQueues[targetId]; - // skybox - const camera = sceneData.camera; + const camera = renderQueue.camera!; + // fill render queue for (const model of sourceModels) { addRenderObject( - phaseLayoutId, + phaseLayoutID, isDrawOpaqueOrMask, isDrawBlend, isDrawProbe, - camera!, + camera, model, renderQueue, ); @@ -710,13 +779,13 @@ export class LightResource { } // Assign light byte offset to each queue - for (const [sceneID, desc] of sceneCulling.renderQueueIndex) { - if (desc.lightBoundsCulledResultID === 0xFFFFFFFF) { + for (const [sceneID, query] of sceneCulling.renderQueueQueryIndex) { + if (query.lightBoundsCulledResultID === 0xFFFFFFFF) { continue; } - const lightByteOffset: number = sceneCulling.lightBoundsCullingResults[desc.lightBoundsCulledResultID].lightByteOffset; + const lightByteOffset: number = sceneCulling.lightBoundsCullingResults[query.lightBoundsCulledResultID].lightByteOffset; - sceneCulling.renderQueues[desc.renderQueueTarget].lightByteOffset = lightByteOffset; + sceneCulling.renderQueues[query.renderQueueTarget].lightByteOffset = lightByteOffset; } } diff --git a/cocos/rendering/custom/web-pipeline-types.ts b/cocos/rendering/custom/web-pipeline-types.ts index 0e991ee5646..416470a4e6c 100644 --- a/cocos/rendering/custom/web-pipeline-types.ts +++ b/cocos/rendering/custom/web-pipeline-types.ts @@ -1006,33 +1006,28 @@ export class RenderInstancingQueue { } } -export class RenderQueueDesc { +export class RenderQueueQuery { frustumCulledResultID: number; lightBoundsCulledResultID: number; renderQueueTarget: number; - lightType: LightType; constructor ( frustumCulledResultID = 0xFFFFFFFF, lightBoundsCulledResultID = 0xFFFFFFFF, renderQueueTargetIn = 0xFFFFFFFF, - lightTypeIn: LightType = LightType.UNKNOWN, ) { this.frustumCulledResultID = frustumCulledResultID; this.lightBoundsCulledResultID = lightBoundsCulledResultID; this.renderQueueTarget = renderQueueTargetIn; - this.lightType = lightTypeIn; } update ( culledSourceIn = 0xFFFFFFFF, lightBoundsCulledResultID = 0xFFFFFFFF, renderQueueTargetIn = 0xFFFFFFFF, - lightTypeIn: LightType = LightType.UNKNOWN, ): void { this.frustumCulledResultID = culledSourceIn; this.lightBoundsCulledResultID = lightBoundsCulledResultID; this.renderQueueTarget = renderQueueTargetIn; - this.lightType = lightTypeIn; } } @@ -1042,8 +1037,8 @@ export class RenderQueue { transparentQueue: RenderDrawQueue = new RenderDrawQueue(); opaqueInstancingQueue: RenderInstancingQueue = new RenderInstancingQueue(); transparentInstancingQueue: RenderInstancingQueue = new RenderInstancingQueue(); + camera: Camera | null = null; sceneFlags: SceneFlags = SceneFlags.NONE; - subpassOrPassLayoutID = 0xFFFFFFFF; lightByteOffset = 0xFFFFFFFF; sort (): void { this.opaqueQueue.sortOpaqueOrCutout(); @@ -1058,8 +1053,9 @@ export class RenderQueue { this.transparentQueue.clear(); this.opaqueInstancingQueue.clear(); this.transparentInstancingQueue.clear(); + this.camera = null; this.sceneFlags = SceneFlags.NONE; - this.subpassOrPassLayoutID = 0xFFFFFFFF; + this.lightByteOffset = 0xFFFFFFFF; } empty (): boolean { @@ -1069,12 +1065,15 @@ export class RenderQueue { && this.transparentInstancingQueue.empty(); } - recordCommands (cmdBuffer: CommandBuffer, renderPass: RenderPass): void { + recordCommands (cmdBuffer: CommandBuffer, renderPass: RenderPass, sceneFlags: SceneFlags): void { const offsets = this.lightByteOffset === 0xFFFFFFFF ? null : [this.lightByteOffset]; - this.opaqueQueue.recordCommandBuffer(deviceManager.gfxDevice, renderPass, cmdBuffer, null, 0, offsets); - this.opaqueInstancingQueue.recordCommandBuffer(renderPass, cmdBuffer, null, 0, offsets); - - this.transparentInstancingQueue.recordCommandBuffer(renderPass, cmdBuffer, null, 0, offsets); - this.transparentQueue.recordCommandBuffer(deviceManager.gfxDevice, renderPass, cmdBuffer, null, 0, offsets); + if (sceneFlags & (SceneFlags.OPAQUE | SceneFlags.MASK)) { + this.opaqueQueue.recordCommandBuffer(deviceManager.gfxDevice, renderPass, cmdBuffer, null, 0, offsets); + this.opaqueInstancingQueue.recordCommandBuffer(renderPass, cmdBuffer, null, 0, offsets); + } + if (sceneFlags & SceneFlags.BLEND) { + this.transparentInstancingQueue.recordCommandBuffer(renderPass, cmdBuffer, null, 0, offsets); + this.transparentQueue.recordCommandBuffer(deviceManager.gfxDevice, renderPass, cmdBuffer, null, 0, offsets); + } } } From fe616ca55fc61a9ee0716f15b3f956568dcf86ea Mon Sep 17 00:00:00 2001 From: James Chen Date: Wed, 18 Sep 2024 15:30:06 +0800 Subject: [PATCH 17/21] Fix a compilation error on xcode 16.0 (#17645) * Fix a compilation error on xcode 16.0 --- native/cocos/renderer/GFXDeviceManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/cocos/renderer/GFXDeviceManager.h b/native/cocos/renderer/GFXDeviceManager.h index 15d5d1538dd..0804ceb9ff5 100644 --- a/native/cocos/renderer/GFXDeviceManager.h +++ b/native/cocos/renderer/GFXDeviceManager.h @@ -150,7 +150,7 @@ class CC_DLL DeviceManager final { } #if !defined(CC_SERVER_MODE) - if (CC_DEBUG > 0 && !FORCE_DISABLE_VALIDATION || FORCE_ENABLE_VALIDATION) { + if ((CC_DEBUG > 0 && !FORCE_DISABLE_VALIDATION) || FORCE_ENABLE_VALIDATION) { device = ccnew gfx::DeviceValidator(device); } #endif From 82594e9f454dd4db933b790d99b9161be90a21f9 Mon Sep 17 00:00:00 2001 From: hyde zhou Date: Thu, 19 Sep 2024 10:23:39 +0800 Subject: [PATCH 18/21] fix iOS14 batcher2d performance issue (#17646) --- cocos/gfx/webgl/webgl-commands.ts | 10 +++++++++- cocos/gfx/webgl2/webgl2-commands.ts | 26 +++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/cocos/gfx/webgl/webgl-commands.ts b/cocos/gfx/webgl/webgl-commands.ts index a67933cab09..79d1cf1cbf0 100644 --- a/cocos/gfx/webgl/webgl-commands.ts +++ b/cocos/gfx/webgl/webgl-commands.ts @@ -23,6 +23,7 @@ */ import { BYTEDANCE } from 'internal:constants'; +import { systemInfo } from 'pal/system-info'; import { debugID, error, errorID, CachedArray, cclegacy, assertID } from '../../core'; import { WebGLCommandAllocator } from './webgl-command-allocator'; import { WebGLEXT } from './webgl-define'; @@ -39,6 +40,7 @@ import { TextureBlit, Filter, } from '../base/define'; import { WebGLStateCache } from './webgl-state-cache'; +import { OS } from '../../../pal/system-info/enum-type'; export function GFXFormatToWebGLType (format: Format, gl: WebGLRenderingContext): GLenum { switch (format) { @@ -830,7 +832,13 @@ export function WebGLCmdFuncUpdateBuffer ( } } - if (size === buff.byteLength) { + if (systemInfo.os === OS.IOS && (gpuBuffer.memUsage & MemoryUsageBit.HOST) && offset === 0 && size === buff.byteLength) { + // Fix performance issue on iOS. + // TODO(zhouzhenglong): glBufferSubData is faster than glBufferData in most cases. + // We should use multiple buffers to avoid stall (cpu write conflicts with gpu read). + // Before that, we will use glBufferData instead of glBufferSubData. + gl.bufferData(gpuBuffer.glTarget, buff, gl.DYNAMIC_DRAW); + } else if (size === buff.byteLength) { gl.bufferSubData(gpuBuffer.glTarget, offset, buff); } else { gl.bufferSubData(gpuBuffer.glTarget, offset, buff.slice(0, size)); diff --git a/cocos/gfx/webgl2/webgl2-commands.ts b/cocos/gfx/webgl2/webgl2-commands.ts index f5354b13589..3eee9709462 100644 --- a/cocos/gfx/webgl2/webgl2-commands.ts +++ b/cocos/gfx/webgl2/webgl2-commands.ts @@ -22,6 +22,7 @@ THE SOFTWARE. */ +import { systemInfo } from 'pal/system-info'; import { BufferUsageBit, ColorMask, CullMode, DynamicStateFlagBit, Filter, Format, TextureType, Type, FormatInfo, FormatInfos, FormatSize, LoadOp, MemoryUsageBit, SampleCount, ShaderStageFlagBit, TextureFlagBit, @@ -47,6 +48,7 @@ import { IWebGL2GPURenderPass, } from './webgl2-gpu-objects'; import { CachedArray, error, errorID, cclegacy, assertID, debugID } from '../../core'; +import { OS } from '../../../pal/system-info/enum-type'; const WebGLWraps: GLenum[] = [ 0x2901, // WebGLRenderingContext.REPEAT @@ -1001,7 +1003,13 @@ export function WebGL2CmdFuncUpdateBuffer ( cache.glArrayBuffer = gpuBuffer.glBuffer; } - if (size === buff.byteLength) { + if (systemInfo.os === OS.IOS && (gpuBuffer.memUsage & MemoryUsageBit.HOST) && offset === 0 && size === buff.byteLength) { + // Fix performance issue on iOS. + // TODO(zhouzhenglong): glBufferSubData is faster than glBufferData in most cases. + // We should use multiple buffers to avoid stall (cpu write conflicts with gpu read). + // Before that, we will use glBufferData instead of glBufferSubData. + gl.bufferData(gpuBuffer.glTarget, buff, gl.DYNAMIC_DRAW); + } else if (size === buff.byteLength) { gl.bufferSubData(gpuBuffer.glTarget, offset, buff); } else { gl.bufferSubData(gpuBuffer.glTarget, offset, buff.slice(0, size)); @@ -1022,7 +1030,13 @@ export function WebGL2CmdFuncUpdateBuffer ( cache.glElementArrayBuffer = gpuBuffer.glBuffer; } - if (size === buff.byteLength) { + if (systemInfo.os === OS.IOS && (gpuBuffer.memUsage & MemoryUsageBit.HOST) && offset === 0 && size === buff.byteLength) { + // Fix performance issue on iOS. + // TODO(zhouzhenglong): glBufferSubData is faster than glBufferData in most cases. + // We should use multiple buffers to avoid stall (cpu write conflicts with gpu read). + // Before that, we will use glBufferData instead of glBufferSubData. + gl.bufferData(gpuBuffer.glTarget, buff, gl.DYNAMIC_DRAW); + } else if (size === buff.byteLength) { gl.bufferSubData(gpuBuffer.glTarget, offset, buff); } else { gl.bufferSubData(gpuBuffer.glTarget, offset, buff.slice(0, size)); @@ -1035,7 +1049,13 @@ export function WebGL2CmdFuncUpdateBuffer ( cache.glUniformBuffer = gpuBuffer.glBuffer; } - if (size === buff.byteLength) { + if (systemInfo.os === OS.IOS && (gpuBuffer.memUsage & MemoryUsageBit.HOST) && offset === 0 && size === buff.byteLength) { + // Fix performance issue on iOS. + // TODO(zhouzhenglong): glBufferSubData is faster than glBufferData in most cases. + // We should use multiple buffers to avoid stall (cpu write conflicts with gpu read). + // Before that, we will use glBufferData instead of glBufferSubData. + gl.bufferData(gpuBuffer.glTarget, buff, gl.DYNAMIC_DRAW); + } else if (size === buff.byteLength) { gl.bufferSubData(gpuBuffer.glTarget, offset, buff); } else { gl.bufferSubData(gpuBuffer.glTarget, offset, new Float32Array(buff, 0, size / 4)); From 38e303e2803b68eb84317e90caad69e629817a91 Mon Sep 17 00:00:00 2001 From: GengineJS <476393671@qq.com> Date: Thu, 19 Sep 2024 10:48:03 +0800 Subject: [PATCH 19/21] optimized code (#17651) --- cocos/rendering/custom/define.ts | 3 ++- cocos/rendering/custom/executor.ts | 15 +++++++++++---- cocos/rendering/custom/web-pipeline-types.ts | 14 +++----------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cocos/rendering/custom/define.ts b/cocos/rendering/custom/define.ts index 1ea2e8e5dd8..7b0db79fcdd 100644 --- a/cocos/rendering/custom/define.ts +++ b/cocos/rendering/custom/define.ts @@ -470,7 +470,8 @@ class DescBuffManager { } } getCurrentBuffer (): Buffer { - this.currBuffIdx = this._root.frameCount % this.buffers.length; + const { director } = cclegacy; + this.currBuffIdx = director.getTotalFrames() % this.buffers.length; return this.buffers[this.currBuffIdx]; } updateData (vals: number[]): void { diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index f3b4d222282..4c17d8ba0fe 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -945,8 +945,7 @@ class DeviceRenderPass implements RecordingInterface { cmdBuff.draw(ia); } - // record common buffer - record (): void { + beginPass (): void { const tex = this.framebuffer.colorTextures[0]!; this._applyViewport(tex); const cmdBuff = context.commandBuffer; @@ -974,14 +973,22 @@ class DeviceRenderPass implements RecordingInterface { context.passDescriptorSet, ); } + } + endPass (): void { + const cmdBuff = context.commandBuffer; + cmdBuff.endRenderPass(); + } + // record common buffer + record (): void { + this.beginPass(); for (const queue of this._deviceQueues.values()) { queue.record(); } if (this._rasterPass.showStatistics) { this._showProfiler(renderPassArea); } - cmdBuff.endRenderPass(); + this.endPass(); } postRecord (): void { @@ -1707,9 +1714,9 @@ export class Executor { const culling = context.culling; culling.buildRenderQueues(rg, context.layoutGraph, context.pipelineSceneData); context.lightResource.buildLights(culling, context.pipelineSceneData.isHDR, context.pipelineSceneData.shadows); - culling.uploadInstancing(cmdBuff); this._removeDeviceResource(); cmdBuff.begin(); + culling.uploadInstancing(cmdBuff); if (!this._visitor) this._visitor = new RenderVisitor(); depthFirstSearch(this._visitor.graphView, this._visitor, this._visitor.colorMap); cmdBuff.end(); diff --git a/cocos/rendering/custom/web-pipeline-types.ts b/cocos/rendering/custom/web-pipeline-types.ts index 416470a4e6c..c0ef55beec3 100644 --- a/cocos/rendering/custom/web-pipeline-types.ts +++ b/cocos/rendering/custom/web-pipeline-types.ts @@ -905,7 +905,6 @@ export class RenderDrawQueue { export class RenderInstancingQueue { passInstances: Map = new Map(); instanceBuffers: Array = new Array(); - sortedBatches: Array = new Array(); empty (): boolean { return this.passInstances.size === 0; @@ -917,7 +916,7 @@ export class RenderInstancingQueue { const instanceBufferID = this.passInstances.size; if (instanceBufferID >= this.instanceBuffers.length) { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - this.instanceBuffers.push(new InstancedBuffer(new Pass(cclegacy.director.root))); + this.instanceBuffers.push(new InstancedBuffer(pass)); } this.passInstances.set(pass, instanceBufferID); @@ -931,7 +930,6 @@ export class RenderInstancingQueue { } clear (): void { - this.sortedBatches.length = 0; this.passInstances.clear(); const instanceBuffers = this.instanceBuffers; instanceBuffers.forEach((instance) => { @@ -939,13 +937,7 @@ export class RenderInstancingQueue { }); } - sort (): void { - this.sortedBatches.length = this.passInstances.size; - let index = 0; - for (const [pass, bufferID] of this.passInstances.entries()) { - this.sortedBatches[index++] = this.instanceBuffers[bufferID]; - } - } + sort (): void {} uploadBuffers (cmdBuffer: CommandBuffer): void { for (const [pass, bufferID] of this.passInstances.entries()) { @@ -963,7 +955,7 @@ export class RenderInstancingQueue { offset = 0, dynamicOffsets: number[] | null = null, ): void { - const renderQueue = this.sortedBatches; + const renderQueue = this.instanceBuffers; for (const instanceBuffer of renderQueue) { if (!instanceBuffer.hasPendingModels) { continue; From 684974dcf93602c0b8e3e3f2eba23077ad9c3450 Mon Sep 17 00:00:00 2001 From: bofeng-song Date: Thu, 19 Sep 2024 13:53:19 +0800 Subject: [PATCH 20/21] Fix the incorrect usage of the closure variable 'this' (#17650) * Fix the incorrect usage of the closure variable 'this' --- .../platforms/taobao-mini-game/wrapper/builtin/WebSocket.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platforms/minigame/platforms/taobao-mini-game/wrapper/builtin/WebSocket.js b/platforms/minigame/platforms/taobao-mini-game/wrapper/builtin/WebSocket.js index 2c09b88cf39..d7bb82d3f85 100644 --- a/platforms/minigame/platforms/taobao-mini-game/wrapper/builtin/WebSocket.js +++ b/platforms/minigame/platforms/taobao-mini-game/wrapper/builtin/WebSocket.js @@ -43,7 +43,7 @@ export default class WebSocket { my.connectSocket({ url, - fail: function fail(res) { + fail: (res) => { this._triggerEvent('error', res) } }) @@ -113,7 +113,7 @@ export default class WebSocket { my.sendSocketMessage({ data, isBuffer, - fail: function (res) { + fail: (res) => { this._triggerEvent('error', res) } }); From 5ea58256f85792b131ee56b3b771190ecd4cbd84 Mon Sep 17 00:00:00 2001 From: hyde zhou Date: Tue, 24 Sep 2024 09:40:34 +0800 Subject: [PATCH 21/21] fix ui per-pass descriptor set (#17663) --- .../pipeline/custom/NativeExecutor.cpp | 17 +++++++++++++++ .../custom/NativeExecutorDescriptor.cpp | 21 ++++++++++++++++--- .../custom/NativeExecutorRenderGraph.h | 2 ++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp index df4f4e8069a..d99e91df292 100644 --- a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp @@ -412,6 +412,15 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { get<0>(iter->second)); } } + void tryBindUIOverwritePerPassDescriptorSet(RenderGraph::vertex_descriptor sceneID) const { + auto iter = ctx.uiDescriptorSet.find(sceneID); + if (iter != ctx.uiDescriptorSet.end()) { + CC_EXPECTS(iter->second); + ctx.cmdBuff->bindDescriptorSet( + static_cast(pipeline::SetIndex::GLOBAL), + iter->second); + } + } void begin(const RasterPass& pass, RenderGraph::vertex_descriptor vertID) const { const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); if (!renderData.custom.empty()) { @@ -692,6 +701,7 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { if (queueData.passLayoutID != LayoutGraphData::null_vertex()) { const auto phaseLayoutID = locate(queueData.passLayoutID, "default", ctx.lg); if (phaseLayoutID != LayoutGraphData::null_vertex()) { + tryBindUIOverwritePerPassDescriptorSet(sceneID); submitUICommands(ctx.currentPass, phaseLayoutID, camera, ctx.cmdBuff); } } else { @@ -704,6 +714,7 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { CC_ENSURES(passLayoutID != LayoutGraphData::null_vertex()); const auto phaseLayoutID = locate(passLayoutID, "default", ctx.lg); if (phaseLayoutID != LayoutGraphData::null_vertex()) { + tryBindUIOverwritePerPassDescriptorSet(sceneID); submitUICommands(ctx.currentPass, phaseLayoutID, camera, ctx.cmdBuff); } } else { // Subpass @@ -721,6 +732,7 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { CC_ENSURES(subpassLayoutID != LayoutGraphData::null_vertex()); const auto phaseLayoutID = locate(subpassLayoutID, "default", ctx.lg); if (phaseLayoutID != LayoutGraphData::null_vertex()) { + tryBindUIOverwritePerPassDescriptorSet(sceneID); submitUICommands(ctx.currentPass, phaseLayoutID, camera, ctx.cmdBuff); } } @@ -1320,6 +1332,10 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { std::tuple> renderGraphDescriptorSet(scratch); + ccstd::pmr::unordered_map< + RenderGraph::vertex_descriptor, gfx::DescriptorSet*> + uiDescriptorSet(scratch); + ccstd::pmr::unordered_map< RenderGraph::vertex_descriptor, gfx::DescriptorSet*> @@ -1340,6 +1356,7 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { &ppl, perPassResourceIndex, renderGraphDescriptorSet, + uiDescriptorSet, profilerPerPassDescriptorSets, perInstanceDescriptorSets, programLibrary, diff --git a/native/cocos/renderer/pipeline/custom/NativeExecutorDescriptor.cpp b/native/cocos/renderer/pipeline/custom/NativeExecutorDescriptor.cpp index e7825d37228..76cb194fe85 100644 --- a/native/cocos/renderer/pipeline/custom/NativeExecutorDescriptor.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeExecutorDescriptor.cpp @@ -79,7 +79,7 @@ void updateCpuUniformBuffer( CC_EXPECTS(sizeof(Mat4) == typeSize); const Mat4 id{}; for (uint32_t i = 0; i != value.count; ++i) { - memcpy(buffer.data() + offset + i * typeSize, id.m, typeSize); + memcpy(buffer.data() + offset + (i * typeSize), id.m, typeSize); } } } @@ -418,7 +418,6 @@ gfx::DescriptorSet* updatePerPassDescriptorSet( CC_ENSURES(prevBuffer); newSet->bindBuffer(bindID, prevBuffer); } - auto name = lg.valueNames[d.descriptorID.value]; bindID += d.count; } break; @@ -486,7 +485,7 @@ struct RenderGraphUploadVisitor : boost::dfs_visitor<> { const auto queueID = parent(leafNodeID, ctx.g); const auto passOrSubpassID = parent(queueID, ctx.g); const auto passID = parent(passOrSubpassID, ctx.g); - + LayoutGraphData::vertex_descriptor parentPassLayoutID = LayoutGraphData::null_vertex(); if (passID == RenderGraph::null_vertex()) { const auto& passLayoutName = get(RenderGraph::LayoutTag{}, ctx.g, passOrSubpassID); const auto passLayoutID = locate( @@ -495,6 +494,7 @@ struct RenderGraphUploadVisitor : boost::dfs_visitor<> { passLayoutID, ctx, leafNodeID); if (perPassSet) { get<0>(ctx.renderGraphDescriptorSet[leafNodeID]) = perPassSet; + parentPassLayoutID = passLayoutID; } } else { const auto subpassID = passOrSubpassID; @@ -512,6 +512,21 @@ struct RenderGraphUploadVisitor : boost::dfs_visitor<> { subpassLayoutID, ctx, leafNodeID); if (perPassSet) { get<0>(ctx.renderGraphDescriptorSet[leafNodeID]) = perPassSet; + parentPassLayoutID = subpassLayoutID; + } + } + if (holds(leafNodeID, ctx.g)) { + const auto& sceneData = get(SceneTag{}, leafNodeID, ctx.g); + if (any(sceneData.flags & SceneFlags::UI)) { + const auto passLayoutID = locate(LayoutGraphData::null_vertex(), "default", ctx.lg); + CC_EXPECTS(passLayoutID != LayoutGraphData::null_vertex()); + if (passLayoutID != parentPassLayoutID) { + auto* perPassSet = updateCameraUniformBufferAndDescriptorSet( + passLayoutID, ctx, leafNodeID); + if (perPassSet) { + ctx.uiDescriptorSet[leafNodeID] = perPassSet; + } + } } } } diff --git a/native/cocos/renderer/pipeline/custom/NativeExecutorRenderGraph.h b/native/cocos/renderer/pipeline/custom/NativeExecutorRenderGraph.h index d9cc17e0b0b..da083bfd540 100644 --- a/native/cocos/renderer/pipeline/custom/NativeExecutorRenderGraph.h +++ b/native/cocos/renderer/pipeline/custom/NativeExecutorRenderGraph.h @@ -63,6 +63,8 @@ struct RenderGraphVisitorContext { ccstd::pmr::unordered_map< RenderGraph::vertex_descriptor, std::tuple>& renderGraphDescriptorSet; + ccstd::pmr::unordered_map< + RenderGraph::vertex_descriptor, gfx::DescriptorSet*>& uiDescriptorSet; ccstd::pmr::unordered_map< RenderGraph::vertex_descriptor, gfx::DescriptorSet*>& profilerPerPassDescriptorSets;