diff --git a/cocos/asset/assets/effect-asset.ts b/cocos/asset/assets/effect-asset.ts index ab330a8145d..f23861f1423 100644 --- a/cocos/asset/assets/effect-asset.ts +++ b/cocos/asset/assets/effect-asset.ts @@ -332,7 +332,9 @@ export class EffectAsset extends Asset { public onLoaded (): void { if (cclegacy.rendering && cclegacy.rendering.enableEffectImport) { addEffectDefaultProperties(this); - (cclegacy.rendering.programLib as ProgramLibrary).addEffect(this); + const programLib = cclegacy.rendering.programLib; + programLib.addEffect(this); + programLib.init(deviceManager.gfxDevice); } else { programLib.register(this); } diff --git a/cocos/gfx/base/define.ts b/cocos/gfx/base/define.ts index 627c192b7b9..176b91f85e2 100644 --- a/cocos/gfx/base/define.ts +++ b/cocos/gfx/base/define.ts @@ -980,6 +980,15 @@ export class Viewport { this.maxDepth = info.maxDepth; return this; } + + public reset (): void { + this.left = 0; + this.top = 0; + this.width = 0; + this.height = 0; + this.minDepth = 0; + this.maxDepth = 1; + } } export class Color { @@ -1007,6 +1016,13 @@ export class Color { this.w = w; return this; } + + public reset (): void { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + } } export class BindingMappingInfo { @@ -1757,6 +1773,10 @@ export class DescriptorSetLayoutInfo { deepCopy(this.bindings, info.bindings, DescriptorSetLayoutBinding); return this; } + + reset (): void { + this.bindings.length = 0; + } } export class DescriptorSetInfo { diff --git a/cocos/gfx/base/shader.ts b/cocos/gfx/base/shader.ts index 64b99ec041f..23554d40d22 100644 --- a/cocos/gfx/base/shader.ts +++ b/cocos/gfx/base/shader.ts @@ -45,6 +45,10 @@ export abstract class Shader extends GFXObject { return this._samplers; } + get stages (): ShaderStage[] { + return this._stages; + } + protected _name = ''; protected _stages: ShaderStage[] = []; protected _attributes: Attribute[] = []; diff --git a/cocos/gfx/webgl/webgl-command-buffer.ts b/cocos/gfx/webgl/webgl-command-buffer.ts index f4868493ec3..c41205649d3 100644 --- a/cocos/gfx/webgl/webgl-command-buffer.ts +++ b/cocos/gfx/webgl/webgl-command-buffer.ts @@ -405,10 +405,13 @@ export class WebGLCommandBuffer extends CommandBuffer { } } - public pipelineBarrier (GeneralBarrier: Readonly, bufferBarriers?: Readonly, + public pipelineBarrier ( + GeneralBarrier: Readonly, + bufferBarriers?: Readonly, buffers?: Readonly, textureBarriers?: Readonly, - textures?: Readonly): void {} + textures?: Readonly, + ): void {} protected bindStates (): void { const bindStatesCmd = this._cmdAllocator.bindStatesCmdPool.alloc(WebGLCmdBindStates); diff --git a/cocos/rendering/custom/define.ts b/cocos/rendering/custom/define.ts index c4c63796adc..0f03b0472ac 100644 --- a/cocos/rendering/custom/define.ts +++ b/cocos/rendering/custom/define.ts @@ -24,17 +24,17 @@ import { EDITOR } from 'internal:constants'; import { BufferInfo, Buffer, BufferUsageBit, ClearFlagBit, Color, DescriptorSet, LoadOp, - Format, Rect, Sampler, StoreOp, Texture, Viewport, MemoryUsageBit, Filter, Address } from '../../gfx'; + Format, Rect, Sampler, StoreOp, Texture, Viewport, MemoryUsageBit, Filter, Address, DescriptorSetLayoutInfo, DescriptorSetLayoutBinding } from '../../gfx'; import { ProbeType, ReflectionProbe } from '../../render-scene/scene/reflection-probe'; import { Camera, SKYBOX_FLAG } from '../../render-scene/scene/camera'; -import { CSMLevel, ShadowType } from '../../render-scene/scene/shadows'; +import { CSMLevel, ShadowType, Shadows } from '../../render-scene/scene/shadows'; import { Light, LightType } from '../../render-scene/scene/light'; import { DirectionalLight } from '../../render-scene/scene/directional-light'; import { RangedDirectionalLight } from '../../render-scene/scene/ranged-directional-light'; import { PointLight } from '../../render-scene/scene/point-light'; import { SphereLight } from '../../render-scene/scene/sphere-light'; import { SpotLight } from '../../render-scene/scene/spot-light'; -import { supportsR32FloatTexture, supportsRGBA16HalfFloatTexture } from '../define'; +import { UBOForwardLight, supportsR32FloatTexture, supportsRGBA16HalfFloatTexture } from '../define'; import { BasicPipeline, Pipeline } from './pipeline'; import { AccessType, AttachmentType, CopyPair, LightInfo, @@ -46,7 +46,7 @@ import { getProfilerCamera, SRGBToLinear } from '../pipeline-funcs'; import { RenderWindow } from '../../render-scene/core/render-window'; import { RenderData, RenderGraph } from './render-graph'; import { WebComputePassBuilder, WebPipeline } from './web-pipeline'; -import { DescriptorSetData, LayoutGraph, LayoutGraphData } from './layout-graph'; +import { DescriptorSetData, DescriptorSetLayoutData, LayoutGraph, LayoutGraphData } from './layout-graph'; import { AABB } from '../../core/geometry'; import { DebugViewCompositeType, DebugViewSingleType } from '../debug-view'; import { ReflectionProbeManager } from '../../3d/reflection-probe/reflection-probe-manager'; @@ -682,11 +682,10 @@ export function buildShadowPass ( ); } const queue = shadowPass.addQueue(QueueHint.RENDER_OPAQUE, 'shadow-caster'); - queue.addSceneOfCamera( + queue.addScene( camera, - new LightInfo(light, level), - SceneFlags.SHADOW_CASTER | SceneFlags.OPAQUE_OBJECT | SceneFlags.TRANSPARENT_OBJECT, - ); + SceneFlags.SHADOW_CASTER | SceneFlags.OPAQUE_OBJECT | SceneFlags.MASK, + ).useLightFrustum(light, light.type !== LightType.DIRECTIONAL ? 0 : level); queue.setViewport(new Viewport(area.x, area.y, area.width, area.height)); } @@ -755,8 +754,10 @@ export function buildReflectionProbePass ( probeCamera.clearStencil, probeCamera.clearFlag, ); - const passBuilder = probePass.addQueue(QueueHint.RENDER_OPAQUE); - passBuilder.addSceneOfCamera(camera, new LightInfo(), SceneFlags.REFLECTION_PROBE); + const passBuilder = probePass.addQueue(QueueHint.RENDER_OPAQUE, 'reflect-map'); + const lightInfo = new LightInfo(); + lightInfo.probe = probe; + passBuilder.addSceneOfCamera(camera, lightInfo, SceneFlags.REFLECTION_PROBE | SceneFlags.OPAQUE_OBJECT); updateCameraUBO(passBuilder as unknown as any, probeCamera, ppl); } @@ -2481,6 +2482,140 @@ export function bool (val): boolean { return !!val; } +export function AlignUp (value: number, alignment: number): number { + return (value + (alignment - 1)) & ~(alignment - 1); +} +const kLightMeterScale = 10000; +export function SetLightUBO ( + light: Light | null, + bHDR: boolean, + exposure: number, + shadowInfo: Shadows | null, + buffer: Float32Array, + offset: number, + elemSize: number, +): void { + const vec4Array = new Float32Array(4); + let size = 0.0; + let range = 0.0; + let luminanceHDR = 0.0; + let luminanceLDR = 0.0; + + if (light && light.type === LightType.SPHERE) { + const sphereLight = light as SphereLight; + vec4Array[0] = sphereLight.position.x; + vec4Array[1] = sphereLight.position.y; + vec4Array[2] = sphereLight.position.z; + vec4Array[3] = LightType.SPHERE; + size = sphereLight.size; + range = sphereLight.range; + luminanceHDR = sphereLight.luminanceHDR; + luminanceLDR = sphereLight.luminanceLDR; + } else if (light && light.type === LightType.SPOT) { + const spotLight = light as SpotLight; + vec4Array[0] = spotLight.position.x; + vec4Array[1] = spotLight.position.y; + vec4Array[2] = spotLight.position.z; + vec4Array[3] = LightType.SPOT; + size = spotLight.size; + range = spotLight.range; + luminanceHDR = spotLight.luminanceHDR; + luminanceLDR = spotLight.luminanceLDR; + } else if (light && light.type === LightType.POINT) { + const pointLight = light as PointLight; + vec4Array[0] = pointLight.position.x; + vec4Array[1] = pointLight.position.y; + vec4Array[2] = pointLight.position.z; + vec4Array[3] = LightType.POINT; + size = 0.0; + range = pointLight.range; + luminanceHDR = pointLight.luminanceHDR; + luminanceLDR = pointLight.luminanceLDR; + } else if (light && light.type === LightType.RANGED_DIRECTIONAL) { + const rangedDirLight = light as RangedDirectionalLight; + vec4Array[0] = rangedDirLight.position.x; + vec4Array[1] = rangedDirLight.position.y; + vec4Array[2] = rangedDirLight.position.z; + vec4Array[3] = LightType.RANGED_DIRECTIONAL; + size = 0.0; + range = 0.0; + luminanceHDR = rangedDirLight.illuminanceHDR; + luminanceLDR = rangedDirLight.illuminanceLDR; + } + + let index = offset + UBOForwardLight.LIGHT_POS_OFFSET; + buffer.set(vec4Array, index); + + index = offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET; + vec4Array.set([size, range, 0, 0]); + buffer.set(vec4Array, index); + + index = offset + UBOForwardLight.LIGHT_COLOR_OFFSET; + const color = light ? light.color : new Color(); + if (light && light.useColorTemperature) { + const tempRGB = light.colorTemperatureRGB; + buffer[index++] = color.x * tempRGB.x; + buffer[index++] = color.y * tempRGB.y; + buffer[index++] = color.z * tempRGB.z; + } else { + buffer[index++] = color.x; + buffer[index++] = color.y; + buffer[index++] = color.z; + } + + if (bHDR) { + buffer[index] = luminanceHDR * exposure * kLightMeterScale; + } else { + buffer[index] = luminanceLDR; + } + + switch (light ? light.type : LightType.UNKNOWN) { + case LightType.SPHERE: + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = 0; + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = 0; + break; + case LightType.SPOT: { + const spotLight = light as SpotLight; + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = spotLight.spotAngle; + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = (shadowInfo && shadowInfo.enabled + && spotLight.shadowEnabled + && shadowInfo.type === ShadowType.ShadowMap) ? 1.0 : 0.0; + + index = offset + UBOForwardLight.LIGHT_DIR_OFFSET; + const direction = spotLight.direction; + buffer[index++] = direction.x; + buffer[index++] = direction.y; + buffer[index] = direction.z; + } break; + case LightType.POINT: + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = 0; + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = 0; + break; + case LightType.RANGED_DIRECTIONAL: { + const rangedDirLight = light as RangedDirectionalLight; + const right = rangedDirLight.right; + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 0] = right.x; + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 1] = right.y; + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = right.z; + buffer[offset + UBOForwardLight.LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = 0; + + const direction = rangedDirLight.direction; + buffer[offset + UBOForwardLight.LIGHT_DIR_OFFSET + 0] = direction.x; + buffer[offset + UBOForwardLight.LIGHT_DIR_OFFSET + 1] = direction.y; + buffer[offset + UBOForwardLight.LIGHT_DIR_OFFSET + 2] = direction.z; + buffer[offset + UBOForwardLight.LIGHT_DIR_OFFSET + 3] = 0; + + const scale = rangedDirLight.scale; + buffer[offset + UBOForwardLight.LIGHT_BOUNDING_SIZE_VS_OFFSET + 0] = scale.x * 0.5; + buffer[offset + UBOForwardLight.LIGHT_BOUNDING_SIZE_VS_OFFSET + 1] = scale.y * 0.5; + buffer[offset + UBOForwardLight.LIGHT_BOUNDING_SIZE_VS_OFFSET + 2] = scale.z * 0.5; + buffer[offset + UBOForwardLight.LIGHT_BOUNDING_SIZE_VS_OFFSET + 3] = 0; + } break; + default: + break; + } +} + export function getSubpassOrPassID (sceneId: number, rg: RenderGraph, lg: LayoutGraphData): number { const queueId = rg.getParent(sceneId); assert(queueId !== 0xFFFFFFFF); @@ -2500,10 +2635,13 @@ export function getSubpassOrPassID (sceneId: number, rg: RenderGraph, lg: Layout assert(passLayoutId !== lg.nullVertex()); const subpassLayoutName: string = rg.getLayout(subpassOrPassID); - assert(!!subpassLayoutName); - const subpassLayoutId = lg.locateChild(passLayoutId, subpassLayoutName); - assert(subpassLayoutId !== lg.nullVertex()); - layoutId = subpassLayoutId; + if (subpassLayoutName.length === 0) { + layoutId = passLayoutId; + } else { + const subpassLayoutId = lg.locateChild(passLayoutId, subpassLayoutName); + assert(subpassLayoutId !== lg.nullVertex()); + layoutId = subpassLayoutId; + } } assert(layoutId !== lg.nullVertex()); return layoutId; diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index 04f11e9ff66..5e6f45aa2d5 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -28,7 +28,7 @@ * ========================= !DO NOT CHANGE THE FOLLOWING SECTION MANUALLY! ========================= */ /* eslint-disable max-len */ -import { getPhaseID, InstancedBuffer, PipelineStateManager } from '..'; +import { getPhaseID, PipelineStateManager } from '..'; import { assert, cclegacy, RecyclePool } from '../../core'; import intersect from '../../core/geometry/intersect'; import { Sphere } from '../../core/geometry/sphere'; @@ -36,7 +36,6 @@ import { AccessFlagBit, Attribute, Buffer, - BufferFlagBit, BufferInfo, BufferUsageBit, BufferViewInfo, @@ -63,7 +62,6 @@ import { Rect, RenderPass, RenderPassInfo, - Shader, StoreOp, SurfaceTransform, Swapchain, @@ -76,11 +74,10 @@ import { import { legacyCC } from '../../core/global-exports'; import { Vec3 } from '../../core/math/vec3'; import { Vec4 } from '../../core/math/vec4'; -import { Pass } from '../../render-scene'; import { Camera } from '../../render-scene/scene/camera'; import { ShadowType } from '../../render-scene/scene/shadows'; import { Root } from '../../root'; -import { IRenderPass, isEnableEffect, SetIndex, UBODeferredLight, UBOForwardLight, UBOLocal } from '../define'; +import { IRenderPass, SetIndex, UBODeferredLight, UBOForwardLight, UBOLocal } from '../define'; import { PipelineSceneData } from '../pipeline-scene-data'; import { PipelineInputAssemblerData } from '../render-pipeline'; import { DescriptorSetData, LayoutGraphData, PipelineLayoutData, RenderPhaseData, RenderStageData } from './layout-graph'; @@ -135,6 +132,7 @@ import { RenderAdditiveLightQueue } from '../render-additive-light-queue'; import { DefaultVisitor, depthFirstSearch, ReferenceGraphView } from './graph'; import { VectorGraphColorMap } from './effect'; import { + bool, getDescriptorSetDataFromLayout, getDescriptorSetDataFromLayoutId, getRenderArea, @@ -143,8 +141,9 @@ import { validPunctualLightsCulling, } from './define'; import { RenderReflectionProbeQueue } from '../render-reflection-probe-queue'; -import { SceneCulling } from './scene-culling'; -import { WebPipeline } from './web-pipeline'; +import { LightResource, SceneCulling } from './scene-culling'; +import { Pass } from '../../render-scene'; +import { WebProgramLibrary } from './web-program-library'; class ResourceVisitor implements ResourceGraphVisitor { name: string; @@ -221,10 +220,10 @@ class ResourceVisitor implements ResourceGraphVisitor { this.createDeviceTex(value); } formatView (value: FormatView): void { - // noop + // do nothing } subresourceView (value: SubresourceView): void { - // noop + // do nothing } } @@ -248,26 +247,11 @@ class DeviceTexture extends DeviceResource { get description (): ResourceDesc | null { return this._desc; } get trait (): ResourceTraits | null { return this._trait; } get swapchain (): Swapchain | null { return this._swapchain; } - private _setDesc (desc: ResourceDesc): void { - if (!this._desc) { - this._desc = new ResourceDesc(); - } - this._desc.alignment = desc.alignment; - this._desc.depthOrArraySize = desc.depthOrArraySize; - this._desc.dimension = desc.dimension; - this._desc.flags = desc.flags; - this._desc.format = desc.format; - this._desc.height = desc.height; - this._desc.mipLevels = desc.mipLevels; - this._desc.sampleCount = desc.sampleCount; - this._desc.textureFlags = desc.textureFlags; - this._desc.width = desc.width; - } constructor (name: string, tex: PersistentTexture | Framebuffer | RenderSwapchain | ManagedResource) { super(name); const resGraph = context.resourceGraph; const verID = resGraph.vertex(name); - this._setDesc(resGraph.getDesc(verID)); + this._desc = resGraph.getDesc(verID); this._trait = resGraph.getTraits(verID); if (tex instanceof Texture) { this._texture = tex; @@ -282,7 +266,7 @@ class DeviceTexture extends DeviceResource { return; } - const info = this._desc!; + const info = this._desc; let type = TextureType.TEX2D; switch (info.dimension) { @@ -523,7 +507,6 @@ class BlitDesc { const deferredLitsBufView = context.blit.deferredLitsBufView; this._lightBufferData = context.blit.lightBufferData; this._lightBufferData.fill(0); - // const binding = isEnableEffect() ? getDescBindingFromName('CCForwardLight') : UBOForwardLight.BINDING; this._stageDesc.bindBuffer(UBOForwardLight.BINDING, deferredLitsBufView); } this._stageDesc.bindBuffer(UBOLocal.BINDING, context.blit.emptyLocalUBO); @@ -728,51 +711,45 @@ class RenderPassLayoutInfo { this._stage = lg.getRenderStage(layoutId); this._layout = lg.getLayout(layoutId); const layoutData = this._layout.descriptorSets.get(UpdateFrequency.PER_PASS); - // const globalDesc = context.descriptorSet; - if (layoutData) { - const layoutDesc = layoutData.descriptorSet!; - // find resource - const deviceTex = context.deviceTextures.get(this._inputName); - const gfxTex = deviceTex?.texture; - - const deviceBuf = context.deviceBuffers.get(this._inputName); - const gfxBuf = deviceBuf?.buffer; - if (!gfxTex && !gfxBuf) { - throw Error(`Could not find texture with resource name ${this._inputName}`); - } - const resId = context.resourceGraph.vertex(this._inputName); - const samplerInfo = context.resourceGraph.getSampler(resId); - // bind descriptors - for (const descriptor of input[1]) { - const descriptorName = descriptor.name; - const descriptorID = lg.attributeIndex.get(descriptorName); - // find descriptor binding - for (const block of layoutData.descriptorSetLayoutData.descriptorBlocks) { - for (let i = 0; i !== block.descriptors.length; ++i) { - // const buffer = layoutDesc.getBuffer(block.offset + i); - // const texture = layoutDesc.getTexture(block.offset + i); - if (descriptorID === block.descriptors[i].descriptorID) { - if (gfxTex) { - layoutDesc.bindTexture(block.offset + i, gfxTex); - layoutDesc.bindSampler(block.offset + i, context.device.getSampler(samplerInfo)); + if (!layoutData) { + return; + } + const layoutDesc = layoutData.descriptorSet!; + // find resource + const deviceTex = context.deviceTextures.get(this._inputName); + const gfxTex = deviceTex?.texture; + + const deviceBuf = context.deviceBuffers.get(this._inputName); + const gfxBuf = deviceBuf?.buffer; + if (!gfxTex && !gfxBuf) { + throw Error(`Could not find texture with resource name ${this._inputName}`); + } + const resId = context.resourceGraph.vertex(this._inputName); + const samplerInfo = context.resourceGraph.getSampler(resId); + // bind descriptors + for (const descriptor of input[1]) { + const descriptorName = descriptor.name; + const descriptorID = lg.attributeIndex.get(descriptorName); + // find descriptor binding + for (const block of layoutData.descriptorSetLayoutData.descriptorBlocks) { + for (let i = 0; i !== block.descriptors.length; ++i) { + if (descriptorID === block.descriptors[i].descriptorID) { + if (gfxTex) { + layoutDesc.bindTexture(block.offset + i, gfxTex); + const renderData = context.renderGraph.getData(this._vertID); + layoutDesc.bindSampler(block.offset + i, renderData.samplers.get(descriptorID) || context.device.getSampler(samplerInfo)); + } else { + const desc = context.resourceGraph.getDesc(resId); + if (desc.flags & ResourceFlags.STORAGE) { + const access = input[1][0].accessType !== AccessType.READ ? AccessFlagBit.COMPUTE_SHADER_WRITE + : AccessFlagBit.COMPUTE_SHADER_READ_OTHER; + (layoutDesc as any).bindBuffer(block.offset + i, gfxBuf!, 0, access); } else { - const desc = context.resourceGraph.getDesc(resId); - if (desc.flags & ResourceFlags.STORAGE) { - const access = input[1][0].accessType !== AccessType.READ ? AccessFlagBit.COMPUTE_SHADER_WRITE - : AccessFlagBit.COMPUTE_SHADER_READ_OTHER; - (layoutDesc as any).bindBuffer(block.offset + i, gfxBuf!, 0, access); - } else { - layoutDesc.bindBuffer(block.offset + i, gfxBuf!); - } + layoutDesc.bindBuffer(block.offset + i, gfxBuf!); } - if (!this._descriptorSet) this._descriptorSet = layoutDesc; - continue; } - // if (!buffer && !texture) { - // layoutDesc.bindBuffer(block.offset + i, globalDesc.getBuffer(block.offset + i)); - // layoutDesc.bindTexture(block.offset + i, globalDesc.getTexture(block.offset + i)); - // layoutDesc.bindSampler(block.offset + i, globalDesc.getSampler(block.offset + i)); - // } + if (!this._descriptorSet) this._descriptorSet = layoutDesc; + continue; } } } @@ -945,15 +922,6 @@ class DeviceRenderPass { const currTex = device.createTexture(new TextureInfo()); colorTexs.push(currTex); } - // if (!depthTex && !swapchain && !framebuffer) { - // depthTex = device.createTexture(new TextureInfo( - // TextureType.TEX2D, - // TextureUsageBit.DEPTH_STENCIL_ATTACHMENT | TextureUsageBit.SAMPLED, - // Format.DEPTH_STENCIL, - // colorTexs[0].width, - // colorTexs[0].height, - // )); - // } const depth = swapchain ? swapchain.depthStencilTexture : depthTex; if (!depth) { depthStencilAttachment.format = Format.UNKNOWN; @@ -1089,7 +1057,6 @@ class DeviceRenderPass { } postPass (): void { - // this.submitMap.clear(); for (const queue of this._deviceQueues) { queue.postRecord(); } @@ -1320,23 +1287,15 @@ class DeviceSceneTransversal extends WebSceneTransversal { } get graphScene (): GraphScene { return this._graphScene; } public preRenderPass (visitor: WebSceneVisitor): DevicePreSceneTask { - if (!this._preSceneTask) { - this._preSceneTask = new DevicePreSceneTask(this._currentQueue, this._graphScene, visitor); - } - this._preSceneTask.apply(this._currentQueue, this.graphScene); + this._preSceneTask = new DevicePreSceneTask(this._currentQueue, this._graphScene, visitor); return this._preSceneTask; } public transverse (visitor: WebSceneVisitor): DeviceSceneTask { - if (!this._sceneTask) { - this._sceneTask = new DeviceSceneTask(this._currentQueue, this._graphScene, visitor); - } - this._sceneTask.apply(this._currentQueue, this.graphScene); + this._sceneTask = new DeviceSceneTask(this._currentQueue, this._graphScene, visitor); return this._sceneTask; } public postRenderPass (visitor: WebSceneVisitor): DevicePostSceneTask { - if (!this._postSceneTask) { - this._postSceneTask = new DevicePostSceneTask(this._sceneData, context.ubo, this._camera, visitor); - } + this._postSceneTask = new DevicePostSceneTask(this._sceneData, context.ubo, this._camera, visitor); return this._postSceneTask; } } @@ -1355,6 +1314,7 @@ class GraphScene { 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; @@ -1412,30 +1372,17 @@ class DevicePreSceneTask extends WebSceneTask { public start (): void { if (this.graphScene.blit) { this._currentQueue.createBlitDesc(this.graphScene.blit); - return; - } - if (!this.camera) { - return; - } - const sceneFlag = this._graphScene.scene!.flags; - if (sceneFlag & SceneFlags.DEFAULT_LIGHTING) { - this._sceneCulling(); - validPunctualLightsCulling(context.pipeline, this.camera); - context.additiveLight.gatherLightPasses(this.camera, this._cmdBuff, this._currentQueue.devicePass.layoutName); } + context.lightResource.buildLightBuffer(context.commandBuffer); + context.lightResource.tryUpdateRenderSceneLocalDescriptorSet(context.culling); } public submit (): void { if (this.graphScene.blit) { this._currentQueue.blitDesc!.update(); } - // if (isShadowMap(this.graphScene)) { - - // } - // this._uploadInstanceBuffers(); } } - const sceneViewport = new Viewport(); class DeviceSceneTask extends WebSceneTask { protected _currentQueue: DeviceRenderQueue; @@ -1464,7 +1411,7 @@ class DeviceSceneTask extends WebSceneTask { } get graphScene (): GraphScene { return this._graphScene; } public start (): void { - // noop + // do nothing } protected _recordUI (): void { @@ -1512,17 +1459,6 @@ class DeviceSceneTask extends WebSceneTask { } } - protected _recordReflectionProbe (): void { - const submitMap = context.submitMap; - const currSubmitInfo = submitMap.get(this.camera!)!.get(this._currentQueue.phaseID)!; - currSubmitInfo.reflectionProbe?.recordCommandBuffer( - context.device, - this._renderPass, - - context.commandBuffer, - ); - } - private _clearExtBlitDesc (desc, extResId: number[]): void { const toGpuDesc = desc.gpuDescriptorSet; for (let i = 0; i < extResId.length; i++) { @@ -1590,7 +1526,9 @@ class DeviceSceneTask extends WebSceneTask { const queueId = this._currentQueue.queueId; const queueRenderData = context.renderGraph.getData(queueId)!; this._updateGlobal(queueRenderData); - + const sceneId = this.graphScene.sceneID; + const sceneRenderData = context.renderGraph.getData(sceneId)!; + if (sceneRenderData) this._updateGlobal(sceneRenderData); const layoutName = context.renderGraph.getLayout(rasterId); const descSetData = getDescriptorSetDataFromLayout(layoutName); mergeSrcToTargetDesc(descSetData!.descriptorSet, context.descriptorSet, true); @@ -1618,14 +1556,6 @@ class DeviceSceneTask extends WebSceneTask { } } - private _recordAdditiveLights (): void { - context.additiveLight?.recordCommandBuffer( - context.device, - this._renderPass, - context.commandBuffer, - ); - } - public submit (): void { const devicePass = this._currentQueue.devicePass; const sceneCulling = context.culling; @@ -1636,21 +1566,11 @@ class DeviceSceneTask extends WebSceneTask { this._recordBlit(); return; } - const renderQueueDesc = sceneCulling.sceneQueryIndex.get(this.graphScene.sceneID)!; + const renderQueueDesc = sceneCulling.renderQueueIndex.get(this.graphScene.sceneID)!; const renderQueue = sceneCulling.renderQueues[renderQueueDesc.renderQueueTarget]; const graphSceneData = this.graphScene.scene!; - renderQueue.opaqueQueue.recordCommandBuffer(deviceManager.gfxDevice, this._renderPass, context.commandBuffer); - renderQueue.opaqueInstancingQueue.recordCommandBuffer(this._renderPass, context.commandBuffer); - if (graphSceneData.flags & SceneFlags.DEFAULT_LIGHTING) { - this._recordAdditiveLights(); - this.visitor.bindDescriptorSet( - SetIndex.GLOBAL, - context.descriptorSet!, - ); - } - - renderQueue.transparentInstancingQueue.recordCommandBuffer(this._renderPass, context.commandBuffer); - renderQueue.transparentQueue.recordCommandBuffer(deviceManager.gfxDevice, this._renderPass, context.commandBuffer); + renderQueue.recordCommands(context.commandBuffer, this._renderPass); + if (bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE)) renderQueue.probeQueue.removeMacro(); if (graphSceneData.flags & SceneFlags.GEOMETRY) { this.camera!.geometryRenderer?.render( devicePass.renderPass, @@ -1661,9 +1581,6 @@ class DeviceSceneTask extends WebSceneTask { if (graphSceneData.flags & SceneFlags.UI) { this._recordUI(); } - if (graphSceneData.flags & SceneFlags.REFLECTION_PROBE) { - this._recordReflectionProbe(); - } } } @@ -1686,12 +1603,6 @@ class ExecutorPools { passIdx: 0, }), 64); } - addPassInfo (): IRenderPass { - return this.passPool.add(); - } - resetPassInfo (): void { - this.passPool.reset(); - } addDeviceQueue (): DeviceRenderQueue { return this.deviceQueuePool.add(); } @@ -1715,7 +1626,6 @@ class ExecutorPools { this.computeQueuePool.reset(); this.graphScenePool.reset(); this.reflectionProbe.reset(); - this.resetPassInfo(); this.computePassInfoPool.reset(); } readonly deviceQueuePool: RecyclePool; @@ -1942,6 +1852,7 @@ class ExecutorContext { for (const infoMap of this.submitMap) { for (const info of infoMap[1]) info[1].reset(); } + this.lightResource.clear(); } resize (width: number, height: number): void { this.width = width; @@ -1964,6 +1875,7 @@ class ExecutorContext { readonly pools: ExecutorPools; readonly blit: BlitInfo; readonly culling: SceneCulling; + lightResource: LightResource = new LightResource(); renderGraph: RenderGraph; width: number; height: number; @@ -1991,6 +1903,8 @@ export class Executor { width, height, ); + const programLib: WebProgramLibrary = cclegacy.rendering.programLib; + context.lightResource.init(programLib, device, 16); } resize (width: number, height: number): void { @@ -2044,8 +1958,10 @@ export class Executor { context.renderGraph = rg; context.reset(); const cmdBuff = context.commandBuffer; - context.culling.buildRenderQueues(rg, context.layoutGraph, context.pipelineSceneData); - context.culling.uploadInstancing(cmdBuff); + 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(); if (!this._visitor) this._visitor = new RenderVisitor(); @@ -2139,19 +2055,19 @@ class PreRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor { } } rasterSubpass (value: RasterSubpass): void { - // noop + // do nothing } computeSubpass (value: ComputeSubpass): void { - // noop + // do nothing } resolve (value: ResolvePass): void { - // noop + // do nothing } move (value: MovePass): void { - // noop + // do nothing } raytrace (value: RaytracePass): void { - // noop + // do nothing } compute (pass: ComputePass): void { if (!this.rg.getValid(this.passID)) return; @@ -2267,25 +2183,25 @@ class PostRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor this.currPass.postPass(); } rasterSubpass (value: RasterSubpass): void { - // noop + // do nothing } computeSubpass (value: ComputeSubpass): void { - // noop + // do nothing } resolve (value: ResolvePass): void { - // noop + // do nothing } compute (value: ComputePass): void { - // noop + // do nothing } copy (value: CopyPass): void { - // noop + // do nothing } move (value: MovePass): void { - // noop + // do nothing } raytrace (value: RaytracePass): void { - // noop + // do nothing } queue (value: RenderQueue): void { // collect scene results @@ -2294,10 +2210,10 @@ class PostRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor // scene command list finished } blit (value: Blit): void { - // noop + // do nothing } dispatch (value: Dispatch): void { - // noop + // do nothing } } diff --git a/cocos/rendering/custom/layout-graph-utils.ts b/cocos/rendering/custom/layout-graph-utils.ts index 6787df02880..99fa5da936c 100644 --- a/cocos/rendering/custom/layout-graph-utils.ts +++ b/cocos/rendering/custom/layout-graph-utils.ts @@ -24,8 +24,9 @@ /* eslint-disable max-len */ import { EffectAsset } from '../../asset/assets'; -import { assert } from '../../core'; -import { DescriptorSetInfo, DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutInfo, DescriptorType, Device, PipelineLayout, PipelineLayoutInfo, ShaderStageFlagBit, Type, Uniform, UniformBlock } from '../../gfx'; +import { assert, error, log, warn } from '../../core'; +import { DescriptorSetInfo, DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutInfo, DescriptorType, Device, Feature, Format, FormatFeatureBit, GetTypeSize, PipelineLayout, PipelineLayoutInfo, ShaderStageFlagBit, Type, Uniform, UniformBlock } from '../../gfx'; +import { UBOForwardLight, UBOSkinning } from '../define'; import { DefaultVisitor, depthFirstSearch, GraphColor, MutableVertexPropertyMap } from './graph'; import { DescriptorBlockData, DescriptorData, DescriptorDB, DescriptorSetData, DescriptorSetLayoutData, LayoutGraph, LayoutGraphData, LayoutGraphDataValue, LayoutGraphValue, PipelineLayoutData, RenderPassType, RenderPhase, RenderPhaseData, RenderStageData, ShaderProgramData } from './layout-graph'; import { UpdateFrequency, getUpdateFrequencyName, getDescriptorTypeOrderName, Descriptor, DescriptorBlock, DescriptorBlockFlattened, DescriptorBlockIndex, DescriptorTypeOrder, ParameterType } from './types'; @@ -109,7 +110,7 @@ export function getGfxDescriptorType (type: DescriptorTypeOrder): DescriptorType case DescriptorTypeOrder.INPUT_ATTACHMENT: return DescriptorType.INPUT_ATTACHMENT; default: - console.error('DescriptorType not found'); + error('DescriptorType not found'); return DescriptorType.INPUT_ATTACHMENT; } } @@ -137,7 +138,7 @@ export function getDescriptorTypeOrder (type: DescriptorType): DescriptorTypeOrd return DescriptorTypeOrder.INPUT_ATTACHMENT; case DescriptorType.UNKNOWN: default: - console.error('DescriptorTypeOrder not found'); + error('DescriptorTypeOrder not found'); return DescriptorTypeOrder.INPUT_ATTACHMENT; } } @@ -460,7 +461,7 @@ export class VisibilityBlock { public getVisibility (name: string): ShaderStageFlagBit { const v = this.descriptors.get(name); if (v === undefined) { - console.error(`Can't find visibility for descriptor: ${name}`); + error(`Can't find visibility for descriptor: ${name}`); return ShaderStageFlagBit.NONE; } return v; @@ -496,6 +497,50 @@ export class VisibilityPass { phases = new Map(); } +export const DEFAULT_UNIFORM_COUNTS: Map = new Map([ + ['cc_lightPos', UBOForwardLight.LIGHTS_PER_PASS], + ['cc_lightColor', UBOForwardLight.LIGHTS_PER_PASS], + ['cc_lightSizeRangeAngle', UBOForwardLight.LIGHTS_PER_PASS], + ['cc_lightDir', UBOForwardLight.LIGHTS_PER_PASS], + ['cc_lightBoundingSizeVS', UBOForwardLight.LIGHTS_PER_PASS], +]); + +export const DYNAMIC_UNIFORM_BLOCK: Set = new Set([ + 'CCCamera', + 'CCForwardLight', + 'CCUILocal', +]); + +export function getUniformBlockSize (blockMembers: Array): number { + let prevSize: number = 0; + + for (const m of blockMembers) { + if (m.count) { + prevSize += GetTypeSize(m.type) * m.count; + continue; + } + + const iter = DEFAULT_UNIFORM_COUNTS.get(m.name); + if (iter !== undefined) { + prevSize += GetTypeSize(m.type) * iter; + continue; + } + + if (m.name === 'cc_joints') { + const sz = GetTypeSize(m.type) * UBOSkinning.LAYOUT.members[0].count; + assert(sz !== UBOSkinning.SIZE); + prevSize += sz; + continue; + } + + error(`Invalid uniform count: ${m.name}`); + } + + assert(!!prevSize); + + return prevSize; +} + export class VisibilityGraph { public getPass (passName: string): VisibilityPass { const pass = this.passes.get(passName); @@ -542,7 +587,7 @@ export class VisibilityGraph { continue; } if (shader.descriptors === undefined) { - console.warn(`No descriptors in shader: ${programName}, please reimport ALL effects`); + warn(`No descriptors in shader: ${programName}, please reimport ALL effects`); continue; } const passName = getPassName(pass); @@ -595,8 +640,10 @@ export class LayoutGraphInfo { let passID = lg.locateChild(lg.nullVertex(), passName); if (passID === lg.nullVertex()) { passID = lg.addVertex( - LayoutGraphValue.RenderStage, type, - passName, new DescriptorDB(), + LayoutGraphValue.RenderStage, + type, + passName, + new DescriptorDB(), lg.nullVertex(), ); } @@ -608,8 +655,10 @@ export class LayoutGraphInfo { let subpassID = lg.locateChild(passID, subpassName); if (subpassID === lg.nullVertex()) { subpassID = lg.addVertex( - LayoutGraphValue.RenderStage, RenderPassType.RENDER_SUBPASS, - subpassName, new DescriptorDB(), + LayoutGraphValue.RenderStage, + RenderPassType.RENDER_SUBPASS, + subpassName, + new DescriptorDB(), passID, ); } @@ -621,8 +670,10 @@ export class LayoutGraphInfo { let phaseID = lg.locateChild(subpassOrPassID, phaseName); if (phaseID === lg.nullVertex()) { phaseID = lg.addVertex( - LayoutGraphValue.RenderPhase, new RenderPhase(), - phaseName, new DescriptorDB(), + LayoutGraphValue.RenderPhase, + new RenderPhase(), + phaseName, + new DescriptorDB(), subpassOrPassID, ); } @@ -675,18 +726,21 @@ export class LayoutGraphInfo { return; } if (value.type !== type) { - console.warn(`Type mismatch for descriptor ${name}`); + warn(`Type mismatch for descriptor ${name}`); } } - private addUniformBlock (block: DescriptorBlock, - name: string, gfxBlock: UniformBlock): void { + private addUniformBlock ( + block: DescriptorBlock, + name: string, + gfxBlock: UniformBlock, + ): void { const value = block.uniformBlocks.get(name); if (value === undefined) { block.uniformBlocks.set(name, gfxBlock); return; } if (!this.checkConsistency(value, gfxBlock)) { - console.warn(`Uniform block ${name} is inconsistent in the same block`); + warn(`Uniform block ${name} is inconsistent in the same block`); } } private buildBlocks (visDB: VisibilityDB, rate: UpdateFrequency, blocks: EffectAsset.IBlockInfo[], db: DescriptorDB, counter: DescriptorCounter): void { @@ -716,7 +770,9 @@ export class LayoutGraphInfo { visDB: VisibilityDB, rate: UpdateFrequency, infoArray: EffectAsset.IBufferInfo[], - type: Type, db: DescriptorDB, counter: DescriptorCounter, + type: Type, + db: DescriptorDB, + counter: DescriptorCounter, ): void { const visBlock = visDB.getBlock({ updateFrequency: rate, @@ -744,7 +800,9 @@ export class LayoutGraphInfo { rate: UpdateFrequency, order: DescriptorTypeOrder, infoArray: EffectAsset.ISamplerInfo[] | EffectAsset.IInputAttachmentInfo[], - type: Type, db: DescriptorDB, counter: DescriptorCounter, + type: Type, + db: DescriptorDB, + counter: DescriptorCounter, ): void { const visBlock = visDB.getBlock({ updateFrequency: rate, @@ -772,7 +830,8 @@ export class LayoutGraphInfo { rate: UpdateFrequency, order: DescriptorTypeOrder, infoArray: EffectAsset.IImageInfo[] | EffectAsset.ISamplerTextureInfo[] | EffectAsset.ITextureInfo[], - db: DescriptorDB, counter: DescriptorCounter, + db: DescriptorDB, + counter: DescriptorCounter, ): void { const visBlock = visDB.getBlock({ updateFrequency: rate, @@ -807,11 +866,11 @@ export class LayoutGraphInfo { } } if (!shader) { - console.warn(`program: ${programName} not found`); + warn(`program: ${programName} not found`); continue; } if (shader.descriptors === undefined) { - console.warn(`No descriptors in shader: ${programName}, please reimport ALL effects`); + warn(`No descriptors in shader: ${programName}, please reimport ALL effects`); continue; } // get database @@ -872,13 +931,13 @@ export class LayoutGraphInfo { const phaseID = v; const parentID = lg.getParent(phaseID); if (lg.id(parentID) !== LayoutGraphValue.RenderStage) { - console.error(`phase: ${lg.getName(phaseID)} has no parent stage`); + error(`phase: ${lg.getName(phaseID)} has no parent stage`); return 1; } const phaseDB = lg.getDescriptors(phaseID); const passVisDB = visMap.get(parentID); if (!passVisDB) { - console.error(`pass: ${lg.getName(parentID)} has no visibility database`); + error(`pass: ${lg.getName(parentID)} has no visibility database`); return 1; } // merge phase visibility to pass visibility @@ -902,14 +961,14 @@ export class LayoutGraphInfo { const phaseID = v; const parentID = lg.getParent(phaseID); if (lg.id(parentID) !== LayoutGraphValue.RenderStage) { - console.error(`phase: ${lg.getName(phaseID)} has no parent stage`); + error(`phase: ${lg.getName(phaseID)} has no parent stage`); return 1; } const passDB = lg.getDescriptors(parentID); const phaseDB = lg.getDescriptors(phaseID); const passVisDB = visMap.get(parentID); if (passVisDB === undefined) { - console.error(`pass: ${lg.getName(parentID)} has no visibility database`); + error(`pass: ${lg.getName(parentID)} has no visibility database`); return 1; } for (const [key0, block] of phaseDB.blocks) { @@ -917,9 +976,7 @@ export class LayoutGraphInfo { if (index0.updateFrequency <= UpdateFrequency.PER_PHASE) { continue; } - const visIndex = new VisibilityIndex( - index0.updateFrequency, index0.parameterType, index0.descriptorType, - ); + const visIndex = new VisibilityIndex(index0.updateFrequency, index0.parameterType, index0.descriptorType); const passVisBlock = passVisDB.getBlock(visIndex); for (const [name, d] of block.descriptors) { @@ -943,7 +1000,7 @@ export class LayoutGraphInfo { } const b = block.uniformBlocks.get(name); if (!b) { - console.error(`uniform block: ${name} not found`); + error(`uniform block: ${name} not found`); return 1; } this.addUniformBlock(passBlock, name, b); @@ -968,22 +1025,22 @@ export class LayoutGraphInfo { for (const e of lg.children(passID)) { const phaseID = lg.child(e); if (lg.id(phaseID) !== LayoutGraphValue.RenderPhase) { - console.error(`pass: ${lg.getName(passID)} is not single_render_pass or render_subpass`); + error(`pass: ${lg.getName(passID)} is not single_render_pass or render_subpass`); return 1; } const phaseDB = lg.getDescriptors(phaseID); for (const [key, passBlock] of passDB.blocks) { const index: DescriptorBlockIndex = JSON.parse(key); if (index.updateFrequency !== UpdateFrequency.PER_PASS) { - console.error(`phase: ${lg.getName(phaseID)} update frequency is not PER_PASS`); + error(`phase: ${lg.getName(phaseID)} update frequency is not PER_PASS`); return 1; } if (passBlock.count === 0) { - console.error(`pass: ${lg.getName(passID)} count is 0`); + error(`pass: ${lg.getName(passID)} count is 0`); return 1; } if (passBlock.capacity !== passBlock.count) { - console.error(`pass: ${lg.getName(passID)} capacity does not equal count`); + error(`pass: ${lg.getName(passID)} capacity does not equal count`); return 1; } const phaseBlock = this.getDescriptorBlock(key, phaseDB); @@ -1047,7 +1104,7 @@ function buildLayoutGraphDataImpl (graph: LayoutGraph, builder: LayoutGraphBuild } const vertID = builder.addRenderStage(graph.getName(v), parentID); if (vertID !== v) { - console.error('vertex id mismatch'); + error('vertex id mismatch'); } minLevel = UpdateFrequency.PER_PASS; maxLevel = UpdateFrequency.PER_PASS; @@ -1059,7 +1116,7 @@ function buildLayoutGraphDataImpl (graph: LayoutGraph, builder: LayoutGraphBuild assert(parentType === RenderPassType.RENDER_SUBPASS || parentType === RenderPassType.SINGLE_RENDER_PASS); const vertID = builder.addRenderPhase(graph.getName(v), parentID); if (vertID !== v) { - console.error('vertex id mismatch'); + error('vertex id mismatch'); } const phase = graph.getRenderPhase(v); for (const shaderName of phase.shaders) { @@ -1070,7 +1127,7 @@ function buildLayoutGraphDataImpl (graph: LayoutGraph, builder: LayoutGraphBuild break; } default: - console.error('unknown vertex type'); + error('unknown vertex type'); minLevel = UpdateFrequency.PER_INSTANCE; minLevel = UpdateFrequency.PER_PASS; break; @@ -1092,7 +1149,7 @@ function buildLayoutGraphDataImpl (graph: LayoutGraph, builder: LayoutGraphBuild } const flattened = convertDescriptorBlock(block); if (block.capacity === 0) { - console.error('block capacity is 0'); + error('block capacity is 0'); return; } if (index.updateFrequency > UpdateFrequency.PER_BATCH) { @@ -1141,16 +1198,24 @@ class LayoutGraphBuilder2 { addRenderStage (name: string, parentID: number): number { return this.lg.addVertex( LayoutGraphDataValue.RenderStage, - new RenderStageData(), name, - UpdateFrequency.PER_PASS, new PipelineLayoutData(), + new RenderStageData(), + + name, + UpdateFrequency.PER_PASS, + + new PipelineLayoutData(), parentID, ); } addRenderPhase (name: string, parentID: number): number { return this.lg.addVertex( LayoutGraphDataValue.RenderPhase, - new RenderPhaseData(), name, - UpdateFrequency.PER_PHASE, new PipelineLayoutData(), + new RenderPhaseData(), + + name, + UpdateFrequency.PER_PHASE, + + new PipelineLayoutData(), parentID, ); } @@ -1176,20 +1241,20 @@ class LayoutGraphBuilder2 { } addDescriptorBlock (nodeID: number, index: DescriptorBlockIndex, block: Readonly): void { if (block.capacity <= 0) { - console.error('empty block'); + error('empty block'); return; } if (block.descriptorNames.length !== block.descriptors.length) { - console.error('error descriptor'); + error('error descriptor'); return; } if (block.uniformBlockNames.length !== block.uniformBlocks.length) { - console.error('error uniform'); + error('error uniform'); return; } if (!(index.updateFrequency >= UpdateFrequency.PER_INSTANCE && index.updateFrequency <= UpdateFrequency.PER_PASS)) { - console.error('invalid update frequency'); + error('invalid update frequency'); return; } @@ -1230,7 +1295,7 @@ class LayoutGraphBuilder2 { } reserveDescriptorBlock (nodeID: number, index: DescriptorBlockIndex, block: DescriptorBlockFlattened): void { if (block.capacity <= 0) { - console.error('empty block'); + error('empty block'); return; } const g: LayoutGraphData = this.lg; @@ -1337,9 +1402,12 @@ function getDescriptorBlockData (map: Map, index: D } // make DescriptorSetLayoutData from effect directly -export function makeDescriptorSetLayoutData (lg: LayoutGraphData, - rate: UpdateFrequency, set: number, - descriptors: EffectAsset.IDescriptorInfo): DescriptorSetLayoutData { +export function makeDescriptorSetLayoutData ( + lg: LayoutGraphData, + rate: UpdateFrequency, + set: number, + descriptors: EffectAsset.IDescriptorInfo, +): DescriptorSetLayoutData { const map = new Map(); const uniformBlocks: Map = new Map(); for (let i = 0; i < descriptors.blocks.length; i++) { @@ -1435,7 +1503,7 @@ export function makeDescriptorSetLayoutData (lg: LayoutGraphData, // update uniform buffer binding const ub = uniformBlocks.get(d.descriptorID); if (!ub) { - console.error(`Uniform block not found for ${d.descriptorID}`); + error(`Uniform block not found for ${d.descriptorID}`); continue; } assert(ub.binding === 0xFFFFFFFF); @@ -1446,7 +1514,7 @@ export function makeDescriptorSetLayoutData (lg: LayoutGraphData, // update block capacity const binding = data.bindingMap.get(d.descriptorID); if (binding !== undefined) { - console.error(`Duplicated descriptor ${d.descriptorID}`); + error(`Duplicated descriptor ${d.descriptorID}`); } data.bindingMap.set(d.descriptorID, block.offset + block.capacity); block.capacity += d.count; @@ -1466,8 +1534,10 @@ export function makeDescriptorSetLayoutData (lg: LayoutGraphData, } // fill DescriptorSetLayoutInfo from DescriptorSetLayoutData -export function initializeDescriptorSetLayoutInfo (layoutData: DescriptorSetLayoutData, - info: DescriptorSetLayoutInfo): void { +export function initializeDescriptorSetLayoutInfo ( + layoutData: DescriptorSetLayoutData, + info: DescriptorSetLayoutInfo, +): void { for (let i = 0; i < layoutData.descriptorBlocks.length; ++i) { const block = layoutData.descriptorBlocks[i]; let slot = block.offset; @@ -1488,8 +1558,11 @@ export function initializeDescriptorSetLayoutInfo (layoutData: DescriptorSetLayo let _emptyDescriptorSetLayout: DescriptorSetLayout; let _emptyPipelineLayout: PipelineLayout; -function populatePipelineLayoutInfo (layout: PipelineLayoutData, - rate: UpdateFrequency, info: PipelineLayoutInfo): void { +export function populatePipelineLayoutInfo ( + layout: PipelineLayoutData, + rate: UpdateFrequency, + info: PipelineLayoutInfo, +): void { const set = layout.descriptorSets.get(rate); if (set && set.descriptorSetLayout) { info.setLayouts.push(set.descriptorSetLayout); @@ -1498,6 +1571,19 @@ function populatePipelineLayoutInfo (layout: PipelineLayoutData, } } +export function generateConstantMacros (device: Device, constantMacros: string): void { + constantMacros = ` + #define CC_DEVICE_SUPPORT_FLOAT_TEXTURE ${device.getFormatFeatures(Format.RGBA32F) & ( + FormatFeatureBit.RENDER_TARGET | FormatFeatureBit.SAMPLED_TEXTURE + ) ? '1' : '0'} + #define CC_DEVICE_MAX_VERTEX_UNIFORM_VECTORS ${device.capabilities.maxVertexUniformVectors} + #define CC_DEVICE_MAX_FRAGMENT_UNIFORM_VECTORS ${device.capabilities.maxFragmentUniformVectors} + #define CC_DEVICE_CAN_BENEFIT_FROM_INPUT_ATTACHMENT ${device.hasFeature(Feature.INPUT_ATTACHMENT_BENEFIT) ? '1' : '0'} + #define CC_PLATFORM_ANDROID_AND_WEBGL 0 + #define CC_ENABLE_WEBGL_HIGHP_STRUCT_VALUES 0 + #define CC_JOINT_UNIFORM_CAPACITY ${UBOSkinning.JOINT_UNIFORM_CAPACITY}`; +} + // initialize layout graph module export function initializeLayoutGraphData (device: Device, lg: LayoutGraphData): void { // create descriptor sets @@ -1507,10 +1593,12 @@ export function initializeLayoutGraphData (device: Device, lg: LayoutGraphData): const layoutData = lg.getLayout(v); for (const [_, set] of layoutData.descriptorSets) { if (set.descriptorSetLayout !== null) { - console.warn('descriptor set layout already initialized. It will be overwritten'); + warn('descriptor set layout already initialized. It will be overwritten'); } - initializeDescriptorSetLayoutInfo(set.descriptorSetLayoutData, - set.descriptorSetLayoutInfo); + initializeDescriptorSetLayoutInfo( + set.descriptorSetLayoutData, + set.descriptorSetLayoutInfo, + ); set.descriptorSetLayout = device.createDescriptorSetLayout(set.descriptorSetLayoutInfo); } } @@ -1558,14 +1646,18 @@ export function getEmptyPipelineLayout (): PipelineLayout { } // get descriptor set from LayoutGraphData (not from ProgramData) -export function getOrCreateDescriptorSetLayout (lg: LayoutGraphData, - subpassOrPassID: number, phaseID: number, rate: UpdateFrequency): DescriptorSetLayout { +export function getOrCreateDescriptorSetLayout ( + lg: LayoutGraphData, + subpassOrPassID: number, + phaseID: number, + rate: UpdateFrequency, +): DescriptorSetLayout { if (rate < UpdateFrequency.PER_PASS) { const phaseData = lg.getLayout(phaseID); const data = phaseData.descriptorSets.get(rate); if (data) { if (!data.descriptorSetLayout) { - console.error('descriptor set layout not initialized'); + error('descriptor set layout not initialized'); return _emptyDescriptorSetLayout; } return data.descriptorSetLayout; @@ -1580,7 +1672,7 @@ export function getOrCreateDescriptorSetLayout (lg: LayoutGraphData, const data = passData.descriptorSets.get(rate); if (data) { if (!data.descriptorSetLayout) { - console.error('descriptor set layout not initialized'); + error('descriptor set layout not initialized'); return _emptyDescriptorSetLayout; } return data.descriptorSetLayout; @@ -1589,14 +1681,18 @@ export function getOrCreateDescriptorSetLayout (lg: LayoutGraphData, } // getDescriptorSetLayout from LayoutGraphData -export function getDescriptorSetLayout (lg: LayoutGraphData, - subpassOrPassID: number, phaseID: number, rate: UpdateFrequency): DescriptorSetLayout | null { +export function getDescriptorSetLayout ( + lg: LayoutGraphData, + subpassOrPassID: number, + phaseID: number, + rate: UpdateFrequency, +): DescriptorSetLayout | null { if (rate < UpdateFrequency.PER_PASS) { const phaseData = lg.getLayout(phaseID); const data = phaseData.descriptorSets.get(rate); if (data) { if (!data.descriptorSetLayout) { - console.error('descriptor set layout not initialized'); + error('descriptor set layout not initialized'); return null; } return data.descriptorSetLayout; @@ -1611,7 +1707,7 @@ export function getDescriptorSetLayout (lg: LayoutGraphData, const data = passData.descriptorSets.get(rate); if (data) { if (!data.descriptorSetLayout) { - console.error('descriptor set layout not initialized'); + error('descriptor set layout not initialized'); return null; } return data.descriptorSetLayout; @@ -1620,8 +1716,11 @@ export function getDescriptorSetLayout (lg: LayoutGraphData, } // get or create DescriptorBlockData from DescriptorSetLayoutData -export function getOrCreateDescriptorBlockData (data: DescriptorSetLayoutData, - type: DescriptorType, vis: ShaderStageFlagBit): DescriptorBlockData { +export function getOrCreateDescriptorBlockData ( + data: DescriptorSetLayoutData, + type: DescriptorType, + vis: ShaderStageFlagBit, +): DescriptorBlockData { const order = getDescriptorTypeOrder(type); for (const block of data.descriptorBlocks) { if (block.type === order && block.visibility === vis) { @@ -1658,8 +1757,10 @@ export function getDescriptorName (lg: LayoutGraphData, nameID: number): string return lg.valueNames[nameID]; } -export function getPerPassDescriptorSetLayoutData (lg: LayoutGraphData, - subpassOrPassID: number): DescriptorSetLayoutData | null { +export function getPerPassDescriptorSetLayoutData ( + lg: LayoutGraphData, + subpassOrPassID: number, +): DescriptorSetLayoutData | null { assert(subpassOrPassID !== lg.nullVertex()); const node = lg.getLayout(subpassOrPassID); const set = node.descriptorSets.get(UpdateFrequency.PER_PASS); @@ -1669,8 +1770,10 @@ export function getPerPassDescriptorSetLayoutData (lg: LayoutGraphData, return set.descriptorSetLayoutData; } -export function getPerPhaseDescriptorSetLayoutData (lg: LayoutGraphData, - phaseID: number): DescriptorSetLayoutData | null { +export function getPerPhaseDescriptorSetLayoutData ( + lg: LayoutGraphData, + phaseID: number, +): DescriptorSetLayoutData | null { assert(phaseID !== lg.nullVertex()); const node = lg.getLayout(phaseID); const set = node.descriptorSets.get(UpdateFrequency.PER_PHASE); @@ -1680,8 +1783,11 @@ export function getPerPhaseDescriptorSetLayoutData (lg: LayoutGraphData, return set.descriptorSetLayoutData; } -export function getPerBatchDescriptorSetLayoutData (lg: LayoutGraphData, - phaseID: number, programID): DescriptorSetLayoutData | null { +export function getPerBatchDescriptorSetLayoutData ( + lg: LayoutGraphData, + phaseID: number, + programID, +): DescriptorSetLayoutData | null { assert(phaseID !== lg.nullVertex()); const phase = lg.getRenderPhase(phaseID); assert(programID < phase.shaderPrograms.length); @@ -1693,8 +1799,11 @@ export function getPerBatchDescriptorSetLayoutData (lg: LayoutGraphData, return set.descriptorSetLayoutData; } -export function getPerInstanceDescriptorSetLayoutData (lg: LayoutGraphData, - phaseID: number, programID): DescriptorSetLayoutData | null { +export function getPerInstanceDescriptorSetLayoutData ( + lg: LayoutGraphData, + phaseID: number, + programID, +): DescriptorSetLayoutData | null { assert(phaseID !== lg.nullVertex()); const phase = lg.getRenderPhase(phaseID); assert(programID < phase.shaderPrograms.length); diff --git a/cocos/rendering/custom/layout-graph.ts b/cocos/rendering/custom/layout-graph.ts index 9ec1346ce68..4e8d25991dd 100644 --- a/cocos/rendering/custom/layout-graph.ts +++ b/cocos/rendering/custom/layout-graph.ts @@ -30,15 +30,22 @@ /* eslint-disable max-len */ import { AddressableGraph, AdjI, AdjacencyGraph, BidirectionalGraph, ComponentGraph, ED, InEI, MutableGraph, MutableReferenceGraph, NamedGraph, OutE, OutEI, PolymorphicGraph, PropertyGraph, PropertyMap, ReferenceGraph, VertexListGraph, directional, findRelative, getPath, parallel, reindexEdgeList, traversal } from './graph'; import { DescriptorSet, DescriptorSetLayout, DescriptorSetLayoutInfo, PipelineLayout, ShaderStageFlagBit, Type, UniformBlock } from '../../gfx'; -import { DescriptorBlock, saveDescriptorBlock, loadDescriptorBlock, DescriptorBlockIndex, saveDescriptorBlockIndex, loadDescriptorBlockIndex, DescriptorTypeOrder, UpdateFrequency } from './types'; +import { DescriptorBlock, saveDescriptorBlock, loadDescriptorBlock, DescriptorBlockIndex, saveDescriptorBlockIndex, loadDescriptorBlockIndex, DescriptorTypeOrder, UpdateFrequency, RenderCommonObjectPool } from './types'; import { OutputArchive, InputArchive } from './archive'; import { saveUniformBlock, loadUniformBlock, saveDescriptorSetLayoutInfo, loadDescriptorSetLayoutInfo } from './serialization'; +import { RecyclePool } from '../../core/memop'; export class DescriptorDB { + reset (): void { + this.blocks.clear(); + } readonly blocks: Map = new Map(); } export class RenderPhase { + reset (): void { + this.shaders.clear(); + } readonly shaders: Set = new Set(); } @@ -587,6 +594,12 @@ export class UniformData { this.uniformType = uniformType; this.offset = offset; } + reset (uniformID = 0xFFFFFFFF, uniformType: Type = Type.UNKNOWN, offset = 0): void { + this.uniformID = uniformID; + this.uniformType = uniformType; + this.offset = offset; + this.size = 0; + } uniformID: number; uniformType: Type; offset: number; @@ -594,6 +607,10 @@ export class UniformData { } export class UniformBlockData { + reset (): void { + this.bufferSize = 0; + this.uniforms.length = 0; + } bufferSize = 0; readonly uniforms: UniformData[] = []; } @@ -604,6 +621,11 @@ export class DescriptorData { this.type = type; this.count = count; } + reset (descriptorID = 0, type: Type = Type.UNKNOWN, count = 1): void { + this.descriptorID = descriptorID; + this.type = type; + this.count = count; + } descriptorID: number; type: Type; count: number; @@ -615,6 +637,13 @@ export class DescriptorBlockData { this.visibility = visibility; this.capacity = capacity; } + reset (type: DescriptorTypeOrder = DescriptorTypeOrder.UNIFORM_BUFFER, visibility: ShaderStageFlagBit = ShaderStageFlagBit.NONE, capacity = 0): void { + this.type = type; + this.visibility = visibility; + this.offset = 0; + this.capacity = capacity; + this.descriptors.length = 0; + } type: DescriptorTypeOrder; visibility: ShaderStageFlagBit; offset = 0; @@ -636,6 +665,18 @@ export class DescriptorSetLayoutData { this.uniformBlocks = uniformBlocks; this.bindingMap = bindingMap; } + reset ( + slot = 0xFFFFFFFF, + capacity = 0, + ): void { + this.slot = slot; + this.capacity = capacity; + this.uniformBlockCapacity = 0; + this.samplerTextureCapacity = 0; + this.descriptorBlocks.length = 0; + this.uniformBlocks.clear(); + this.bindingMap.clear(); + } slot: number; capacity: number; uniformBlockCapacity = 0; @@ -651,6 +692,12 @@ export class DescriptorSetData { this.descriptorSetLayout = descriptorSetLayout; this.descriptorSet = descriptorSet; } + reset (descriptorSetLayout: DescriptorSetLayout | null = null, descriptorSet: DescriptorSet | null = null): void { + this.descriptorSetLayoutData.reset(); + this.descriptorSetLayoutInfo.reset(); + this.descriptorSetLayout = descriptorSetLayout; + this.descriptorSet = descriptorSet; + } readonly descriptorSetLayoutData: DescriptorSetLayoutData; readonly descriptorSetLayoutInfo: DescriptorSetLayoutInfo = new DescriptorSetLayoutInfo(); /*refcount*/ descriptorSetLayout: DescriptorSetLayout | null; @@ -658,36 +705,65 @@ export class DescriptorSetData { } export class PipelineLayoutData { + reset (): void { + this.descriptorSets.clear(); + } readonly descriptorSets: Map = new Map(); } export class ShaderBindingData { + reset (): void { + this.descriptorBindings.clear(); + } readonly descriptorBindings: Map = new Map(); } export class ShaderLayoutData { + reset (): void { + this.layoutData.clear(); + this.bindingData.clear(); + } readonly layoutData: Map = new Map(); readonly bindingData: Map = new Map(); } export class TechniqueData { + reset (): void { + this.passes.length = 0; + } readonly passes: ShaderLayoutData[] = []; } export class EffectData { + reset (): void { + this.techniques.clear(); + } readonly techniques: Map = new Map(); } export class ShaderProgramData { + reset (): void { + this.layout.reset(); + this.pipelineLayout = null; + } readonly layout: PipelineLayoutData = new PipelineLayoutData(); /*refcount*/ pipelineLayout: PipelineLayout | null = null; } export class RenderStageData { + reset (): void { + this.descriptorVisibility.clear(); + } readonly descriptorVisibility: Map = new Map(); } export class RenderPhaseData { + reset (): void { + this.rootSignature = ''; + this.shaderPrograms.length = 0; + this.shaderIndex.clear(); + this.pipelineLayout = null; + } rootSignature = ''; readonly shaderPrograms: ShaderProgramData[] = []; readonly shaderIndex: Map = new Map(); @@ -1260,10 +1336,222 @@ export class LayoutGraphData implements BidirectionalGraph constantMacros = ''; } +export class LayoutGraphObjectPoolSettings { + constructor (batchSize: number) { + this.descriptorDBBatchSize = batchSize; + this.renderPhaseBatchSize = batchSize; + this.layoutGraphBatchSize = batchSize; + this.uniformDataBatchSize = batchSize; + this.uniformBlockDataBatchSize = batchSize; + this.descriptorDataBatchSize = batchSize; + this.descriptorBlockDataBatchSize = batchSize; + this.descriptorSetLayoutDataBatchSize = batchSize; + this.descriptorSetDataBatchSize = batchSize; + this.pipelineLayoutDataBatchSize = batchSize; + this.shaderBindingDataBatchSize = batchSize; + this.shaderLayoutDataBatchSize = batchSize; + this.techniqueDataBatchSize = batchSize; + this.effectDataBatchSize = batchSize; + this.shaderProgramDataBatchSize = batchSize; + this.renderStageDataBatchSize = batchSize; + this.renderPhaseDataBatchSize = batchSize; + this.layoutGraphDataBatchSize = batchSize; + } + descriptorDBBatchSize = 16; + renderPhaseBatchSize = 16; + layoutGraphBatchSize = 16; + uniformDataBatchSize = 16; + uniformBlockDataBatchSize = 16; + descriptorDataBatchSize = 16; + descriptorBlockDataBatchSize = 16; + descriptorSetLayoutDataBatchSize = 16; + descriptorSetDataBatchSize = 16; + pipelineLayoutDataBatchSize = 16; + shaderBindingDataBatchSize = 16; + shaderLayoutDataBatchSize = 16; + techniqueDataBatchSize = 16; + effectDataBatchSize = 16; + shaderProgramDataBatchSize = 16; + renderStageDataBatchSize = 16; + renderPhaseDataBatchSize = 16; + layoutGraphDataBatchSize = 16; +} + +export class LayoutGraphObjectPool { + constructor (settings: LayoutGraphObjectPoolSettings, renderCommon: RenderCommonObjectPool) { + this.renderCommon = renderCommon; + this._descriptorDB = new RecyclePool(() => new DescriptorDB(), settings.descriptorDBBatchSize); + this._renderPhase = new RecyclePool(() => new RenderPhase(), settings.renderPhaseBatchSize); + this._layoutGraph = new RecyclePool(() => new LayoutGraph(), settings.layoutGraphBatchSize); + this._uniformData = new RecyclePool(() => new UniformData(), settings.uniformDataBatchSize); + this._uniformBlockData = new RecyclePool(() => new UniformBlockData(), settings.uniformBlockDataBatchSize); + this._descriptorData = new RecyclePool(() => new DescriptorData(), settings.descriptorDataBatchSize); + this._descriptorBlockData = new RecyclePool(() => new DescriptorBlockData(), settings.descriptorBlockDataBatchSize); + this._descriptorSetLayoutData = new RecyclePool(() => new DescriptorSetLayoutData(), settings.descriptorSetLayoutDataBatchSize); + this._descriptorSetData = new RecyclePool(() => new DescriptorSetData(), settings.descriptorSetDataBatchSize); + this._pipelineLayoutData = new RecyclePool(() => new PipelineLayoutData(), settings.pipelineLayoutDataBatchSize); + this._shaderBindingData = new RecyclePool(() => new ShaderBindingData(), settings.shaderBindingDataBatchSize); + this._shaderLayoutData = new RecyclePool(() => new ShaderLayoutData(), settings.shaderLayoutDataBatchSize); + this._techniqueData = new RecyclePool(() => new TechniqueData(), settings.techniqueDataBatchSize); + this._effectData = new RecyclePool(() => new EffectData(), settings.effectDataBatchSize); + this._shaderProgramData = new RecyclePool(() => new ShaderProgramData(), settings.shaderProgramDataBatchSize); + this._renderStageData = new RecyclePool(() => new RenderStageData(), settings.renderStageDataBatchSize); + this._renderPhaseData = new RecyclePool(() => new RenderPhaseData(), settings.renderPhaseDataBatchSize); + this._layoutGraphData = new RecyclePool(() => new LayoutGraphData(), settings.layoutGraphDataBatchSize); + } + reset (): void { + this._descriptorDB.reset(); + this._renderPhase.reset(); + this._layoutGraph.reset(); + this._uniformData.reset(); + this._uniformBlockData.reset(); + this._descriptorData.reset(); + this._descriptorBlockData.reset(); + this._descriptorSetLayoutData.reset(); + this._descriptorSetData.reset(); + this._pipelineLayoutData.reset(); + this._shaderBindingData.reset(); + this._shaderLayoutData.reset(); + this._techniqueData.reset(); + this._effectData.reset(); + this._shaderProgramData.reset(); + this._renderStageData.reset(); + this._renderPhaseData.reset(); + this._layoutGraphData.reset(); + } + createDescriptorDB (): DescriptorDB { + const v = this._descriptorDB.add(); + v.reset(); + return v; + } + createRenderPhase (): RenderPhase { + const v = this._renderPhase.add(); + v.reset(); + return v; + } + createLayoutGraph (): LayoutGraph { + const v = this._layoutGraph.add(); + v.clear(); + return v; + } + createUniformData ( + uniformID = 0xFFFFFFFF, + uniformType: Type = Type.UNKNOWN, + offset = 0, + ): UniformData { + const v = this._uniformData.add(); + v.reset(uniformID, uniformType, offset); + return v; + } + createUniformBlockData (): UniformBlockData { + const v = this._uniformBlockData.add(); + v.reset(); + return v; + } + createDescriptorData ( + descriptorID = 0, + type: Type = Type.UNKNOWN, + count = 1, + ): DescriptorData { + const v = this._descriptorData.add(); + v.reset(descriptorID, type, count); + return v; + } + createDescriptorBlockData ( + type: DescriptorTypeOrder = DescriptorTypeOrder.UNIFORM_BUFFER, + visibility: ShaderStageFlagBit = ShaderStageFlagBit.NONE, + capacity = 0, + ): DescriptorBlockData { + const v = this._descriptorBlockData.add(); + v.reset(type, visibility, capacity); + return v; + } + createDescriptorSetLayoutData ( + slot = 0xFFFFFFFF, + capacity = 0, + ): DescriptorSetLayoutData { + const v = this._descriptorSetLayoutData.add(); + v.reset(slot, capacity); + return v; + } + createDescriptorSetData ( + descriptorSetLayout: DescriptorSetLayout | null = null, + descriptorSet: DescriptorSet | null = null, + ): DescriptorSetData { + const v = this._descriptorSetData.add(); + v.reset(descriptorSetLayout, descriptorSet); + return v; + } + createPipelineLayoutData (): PipelineLayoutData { + const v = this._pipelineLayoutData.add(); + v.reset(); + return v; + } + createShaderBindingData (): ShaderBindingData { + const v = this._shaderBindingData.add(); + v.reset(); + return v; + } + createShaderLayoutData (): ShaderLayoutData { + const v = this._shaderLayoutData.add(); + v.reset(); + return v; + } + createTechniqueData (): TechniqueData { + const v = this._techniqueData.add(); + v.reset(); + return v; + } + createEffectData (): EffectData { + const v = this._effectData.add(); + v.reset(); + return v; + } + createShaderProgramData (): ShaderProgramData { + const v = this._shaderProgramData.add(); + v.reset(); + return v; + } + createRenderStageData (): RenderStageData { + const v = this._renderStageData.add(); + v.reset(); + return v; + } + createRenderPhaseData (): RenderPhaseData { + const v = this._renderPhaseData.add(); + v.reset(); + return v; + } + createLayoutGraphData (): LayoutGraphData { + const v = this._layoutGraphData.add(); + v.clear(); + return v; + } + public readonly renderCommon: RenderCommonObjectPool; + private readonly _descriptorDB: RecyclePool; + private readonly _renderPhase: RecyclePool; + private readonly _layoutGraph: RecyclePool; + private readonly _uniformData: RecyclePool; + private readonly _uniformBlockData: RecyclePool; + private readonly _descriptorData: RecyclePool; + private readonly _descriptorBlockData: RecyclePool; + private readonly _descriptorSetLayoutData: RecyclePool; + private readonly _descriptorSetData: RecyclePool; + private readonly _pipelineLayoutData: RecyclePool; + private readonly _shaderBindingData: RecyclePool; + private readonly _shaderLayoutData: RecyclePool; + private readonly _techniqueData: RecyclePool; + private readonly _effectData: RecyclePool; + private readonly _shaderProgramData: RecyclePool; + private readonly _renderStageData: RecyclePool; + private readonly _renderPhaseData: RecyclePool; + private readonly _layoutGraphData: RecyclePool; +} + export function saveDescriptorDB (ar: OutputArchive, v: DescriptorDB): void { ar.writeNumber(v.blocks.size); // Map for (const [k1, v1] of v.blocks) { - saveDescriptorBlockIndex(ar, JSON.parse(k1)); + saveDescriptorBlockIndex(ar, JSON.parse(k1) as DescriptorBlockIndex); saveDescriptorBlock(ar, v1); } } diff --git a/cocos/rendering/custom/pipeline.ts b/cocos/rendering/custom/pipeline.ts index 87172d5f715..e97d787ff1f 100644 --- a/cocos/rendering/custom/pipeline.ts +++ b/cocos/rendering/custom/pipeline.ts @@ -385,8 +385,18 @@ export interface Setter extends RenderNode { setBuiltinSpotLightConstants (light: SpotLight, camera: Camera): void; setBuiltinPointLightConstants (light: PointLight, camera: Camera): void; setBuiltinRangedDirectionalLightConstants (light: RangedDirectionalLight, camera: Camera): void; - setBuiltinDirectionalLightViewConstants (light: DirectionalLight, level?: number): void; - setBuiltinSpotLightViewConstants (light: SpotLight): void; + setBuiltinDirectionalLightFrustumConstants ( + camera: Camera, + light: DirectionalLight, + csmLevel?: number): void; + setBuiltinSpotLightFrustumConstants (light: SpotLight): void; +} + +export interface SceneBuilder extends Setter { + useLightFrustum ( + light: Light, + csmLevel?: number, + optCamera?: Camera): void; } /** @@ -413,16 +423,7 @@ export interface RenderQueueBuilder extends Setter { addScene ( camera: Camera, sceneFlags: SceneFlags, - light?: Light | null): void; - addSceneCulledByDirectionalLight ( - camera: Camera, - sceneFlags: SceneFlags, - light: DirectionalLight, - level: number): void; - addSceneCulledBySpotLight ( - camera: Camera, - sceneFlags: SceneFlags, - light: SpotLight): void; + light?: Light): SceneBuilder; /** * @en Render a full-screen quad. * @zh 渲染全屏四边形 @@ -515,7 +516,7 @@ export interface BasicRenderPassBuilder extends Setter { addTexture ( name: string, slotName: string, - sampler?: Sampler | null, + sampler?: Sampler, plane?: number): void; /** * @en Add render queue. @@ -689,14 +690,15 @@ export interface BasicPipeline extends PipelineRuntime { size: number, flags: ResourceFlags, residency: ResourceResidency): number; - updateBuffer ( + updateBuffer (name: string, size: number): void; + addExternalTexture ( name: string, - size: number): void; - addExternalTexture (name: string, texture: Texture, flags: ResourceFlags): number; + texture: Texture, + flags: ResourceFlags): number; updateExternalTexture (name: string, texture: Texture): void; addTexture ( name: string, - textureType: TextureType, + type: TextureType, format: Format, width: number, height: number, @@ -809,10 +811,11 @@ export interface BasicPipeline extends PipelineRuntime { * @param copyPairs @en Array of copy source and target @zh 拷贝来源与目标的数组 */ addCopyPass (copyPairs: CopyPair[]): void; + addBuiltinReflectionProbePass (camera: Camera): void; /** * @engineInternal */ - getDescriptorSetLayout (shaderName: string, freq: UpdateFrequency): DescriptorSetLayout | null; + getDescriptorSetLayout (shaderName: string, freq: UpdateFrequency): DescriptorSetLayout | undefined; } /** @@ -875,7 +878,7 @@ export interface RenderSubpassBuilder extends Setter { addTexture ( name: string, slotName: string, - sampler?: Sampler | null, + sampler?: Sampler, plane?: number): void; /** * @en Add storage buffer. @@ -1019,7 +1022,7 @@ export interface ComputeSubpassBuilder extends Setter { addTexture ( name: string, slotName: string, - sampler?: Sampler | null, + sampler?: Sampler, plane?: number): void; /** * @en Add storage buffer. @@ -1175,7 +1178,7 @@ export interface ComputePassBuilder extends Setter { addTexture ( name: string, slotName: string, - sampler?: Sampler | null, + sampler?: Sampler, plane?: number): void; /** * @en Add storage buffer. @@ -1389,7 +1392,7 @@ export interface Pipeline extends BasicPipeline { addBuiltinGpuCullingPass ( camera: Camera, hzbName?: string, - light?: Light | null): void; + light?: Light): void; addBuiltinHzbGenerationPass (sourceDepthStencilName: string, targetHzbName: string): void; /** * @experimental @@ -1423,8 +1426,7 @@ export interface PipelineBuilder { * @param pipeline @en Current render pipeline @zh 当前管线 */ setup (cameras: Camera[], pipeline: BasicPipeline): void; - - onGlobalPipelineStateChanged?(): void; + onGlobalPipelineStateChanged? (): void; } /** diff --git a/cocos/rendering/custom/private.ts b/cocos/rendering/custom/private.ts index 62e2393c627..b4cae1f5d17 100644 --- a/cocos/rendering/custom/private.ts +++ b/cocos/rendering/custom/private.ts @@ -64,7 +64,7 @@ export interface ProgramLibrary { phaseID: number, name: string, defines: MacroRecord, - key?: string | null): ProgramProxy | null; + key?: string): ProgramProxy | null; getBlockSizes (phaseID: number, programName: string): number[]; getHandleMap (phaseID: number, programName: string): Record; getProgramID (phaseID: number, programName: string): number; diff --git a/cocos/rendering/custom/render-graph.ts b/cocos/rendering/custom/render-graph.ts index e2b609289da..135d22cd640 100644 --- a/cocos/rendering/custom/render-graph.ts +++ b/cocos/rendering/custom/render-graph.ts @@ -31,10 +31,12 @@ import { AdjI, AdjacencyGraph, BidirectionalGraph, ComponentGraph, ED, InEI, MutableGraph, MutableReferenceGraph, NamedGraph, OutE, OutEI, PolymorphicGraph, PropertyGraph, PropertyMap, ReferenceGraph, UuidGraph, VertexListGraph, directional, parallel, reindexEdgeList, traversal } from './graph'; import { Material } from '../../asset/assets'; import { Camera } from '../../render-scene/scene/camera'; -import { AccessFlagBit, Buffer, ClearFlagBit, Color, Format, Framebuffer, LoadOp, RenderPass, SampleCount, Sampler, SamplerInfo, ShaderStageFlagBit, StoreOp, Swapchain, Texture, TextureFlagBit, Viewport, TextureType } from '../../gfx'; -import { AccessType, AttachmentType, ClearValueType, CopyPair, LightInfo, MovePair, QueueHint, ResolvePair, ResourceDimension, ResourceFlags, ResourceResidency, SceneFlags, UploadPair } from './types'; +import { AccessFlagBit, Buffer, ClearFlagBit, Color, Format, Framebuffer, LoadOp, RenderPass, SampleCount, Sampler, SamplerInfo, ShaderStageFlagBit, StoreOp, Swapchain, Texture, TextureFlagBit, TextureType, Viewport } from '../../gfx'; +import { AccessType, AttachmentType, ClearValueType, CopyPair, LightInfo, MovePair, QueueHint, ResolvePair, ResourceDimension, ResourceFlags, ResourceResidency, SceneFlags, UploadPair, RenderCommonObjectPool } from './types'; import { RenderScene } from '../../render-scene/core/render-scene'; import { RenderWindow } from '../../render-scene/core/render-window'; +import { Light } from '../../render-scene/scene'; +import { RecyclePool } from '../../core/memop'; export class ClearValue { constructor (x = 0, y = 0, z = 0, w = 0) { @@ -43,6 +45,12 @@ export class ClearValue { this.z = z; this.w = w; } + reset (x = 0, y = 0, z = 0, w = 0): void { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } x: number; y: number; z: number; @@ -69,6 +77,26 @@ export class RasterView { this.clearColor = clearColor; this.shaderStageFlags = shaderStageFlags; } + reset ( + slotName = '', + accessType: AccessType = AccessType.WRITE, + attachmentType: AttachmentType = AttachmentType.RENDER_TARGET, + loadOp: LoadOp = LoadOp.LOAD, + storeOp: StoreOp = StoreOp.STORE, + clearFlags: ClearFlagBit = ClearFlagBit.ALL, + shaderStageFlags: ShaderStageFlagBit = ShaderStageFlagBit.NONE, + ): void { + this.slotName = slotName; + this.slotName1 = ''; + this.accessType = accessType; + this.attachmentType = attachmentType; + this.loadOp = loadOp; + this.storeOp = storeOp; + this.clearFlags = clearFlags; + this.clearColor.reset(); + this.slotID = 0; + this.shaderStageFlags = shaderStageFlags; + } slotName: string; slotName1 = ''; accessType: AccessType; @@ -97,6 +125,21 @@ export class ComputeView { this.clearValue = clearValue; this.shaderStageFlags = shaderStageFlags; } + reset ( + name = '', + accessType: AccessType = AccessType.READ, + clearFlags: ClearFlagBit = ClearFlagBit.NONE, + clearValueType: ClearValueType = ClearValueType.NONE, + shaderStageFlags: ShaderStageFlagBit = ShaderStageFlagBit.NONE, + ): void { + this.name = name; + this.accessType = accessType; + this.plane = 0; + this.clearFlags = clearFlags; + this.clearValueType = clearValueType; + this.clearValue.reset(); + this.shaderStageFlags = shaderStageFlags; + } name: string; accessType: AccessType; plane = 0; @@ -107,6 +150,19 @@ export class ComputeView { } export class ResourceDesc { + reset (): void { + this.dimension = ResourceDimension.BUFFER; + this.alignment = 0; + this.width = 0; + this.height = 0; + this.depthOrArraySize = 0; + this.mipLevels = 0; + this.format = Format.UNKNOWN; + this.sampleCount = SampleCount.X1; + this.textureFlags = TextureFlagBit.NONE; + this.flags = ResourceFlags.NONE; + this.viewType = TextureType.TEX2D; + } dimension: ResourceDimension = ResourceDimension.BUFFER; alignment = 0; width = 0; @@ -124,6 +180,9 @@ export class ResourceTraits { constructor (residency: ResourceResidency = ResourceResidency.MANAGED) { this.residency = residency; } + reset (residency: ResourceResidency = ResourceResidency.MANAGED): void { + this.residency = residency; + } residency: ResourceResidency; } @@ -131,6 +190,13 @@ export class RenderSwapchain { constructor (swapchain: Swapchain | null = null) { this.swapchain = swapchain; } + reset (swapchain: Swapchain | null = null): void { + this.swapchain = swapchain; + this.renderWindow = null; + this.currentID = 0; + this.numBackBuffers = 0; + this.generation = 0xFFFFFFFF; + } /*pointer*/ swapchain: Swapchain | null; /*pointer*/ renderWindow: RenderWindow | null = null; currentID = 0; @@ -139,6 +205,9 @@ export class RenderSwapchain { } export class ResourceStates { + reset (): void { + this.states = AccessFlagBit.NONE; + } states: AccessFlagBit = AccessFlagBit.NONE; } @@ -146,6 +215,10 @@ export class ManagedBuffer { constructor (buffer: Buffer | null = null) { this.buffer = buffer; } + reset (buffer: Buffer | null = null): void { + this.buffer = buffer; + this.fenceValue = 0; + } /*refcount*/ buffer: Buffer | null; fenceValue = 0; } @@ -154,6 +227,10 @@ export class PersistentBuffer { constructor (buffer: Buffer | null = null) { this.buffer = buffer; } + reset (buffer: Buffer | null = null): void { + this.buffer = buffer; + this.fenceValue = 0; + } /*refcount*/ buffer: Buffer | null; fenceValue = 0; } @@ -162,6 +239,10 @@ export class ManagedTexture { constructor (texture: Texture | null = null) { this.texture = texture; } + reset (texture: Texture | null = null): void { + this.texture = texture; + this.fenceValue = 0; + } /*refcount*/ texture: Texture | null; fenceValue = 0; } @@ -170,15 +251,27 @@ export class PersistentTexture { constructor (texture: Texture | null = null) { this.texture = texture; } + reset (texture: Texture | null = null): void { + this.texture = texture; + this.fenceValue = 0; + } /*refcount*/ texture: Texture | null; fenceValue = 0; } export class ManagedResource { + reset (): void { + this.unused = 0; + } unused = 0; } export class Subpass { + reset (): void { + this.rasterViews.clear(); + this.computeViews.clear(); + this.resolvePairs.length = 0; + } readonly rasterViews: Map = new Map(); readonly computeViews: Map = new Map(); readonly resolvePairs: ResolvePair[] = []; @@ -486,11 +579,21 @@ export class SubpassGraph implements BidirectionalGraph } export class RasterSubpass { - constructor (subpassID: number, count: number, quality: number) { + constructor (subpassID = 0xFFFFFFFF, count = 1, quality = 0) { this.subpassID = subpassID; this.count = count; this.quality = quality; } + reset (subpassID = 0xFFFFFFFF, count = 1, quality = 0): void { + this.rasterViews.clear(); + this.computeViews.clear(); + this.resolvePairs.length = 0; + this.viewport.reset(); + this.subpassID = subpassID; + this.count = count; + this.quality = quality; + this.showStatistics = false; + } readonly rasterViews: Map = new Map(); readonly computeViews: Map = new Map(); readonly resolvePairs: ResolvePair[] = []; @@ -502,7 +605,12 @@ export class RasterSubpass { } export class ComputeSubpass { - constructor (subpassID: number) { + constructor (subpassID = 0xFFFFFFFF) { + this.subpassID = subpassID; + } + reset (subpassID = 0xFFFFFFFF): void { + this.rasterViews.clear(); + this.computeViews.clear(); this.subpassID = subpassID; } readonly rasterViews: Map = new Map(); @@ -511,6 +619,22 @@ export class ComputeSubpass { } export class RasterPass { + reset (): void { + this.rasterViews.clear(); + this.computeViews.clear(); + this.attachmentIndexMap.clear(); + this.textures.clear(); + this.subpassGraph.clear(); + this.width = 0; + this.height = 0; + this.count = 1; + this.quality = 0; + this.viewport.reset(); + this.versionName = ''; + this.version = 0; + this.hashValue = 0; + this.showStatistics = false; + } readonly rasterViews: Map = new Map(); readonly computeViews: Map = new Map(); readonly attachmentIndexMap: Map = new Map(); @@ -528,22 +652,42 @@ export class RasterPass { } export class PersistentRenderPassAndFramebuffer { - constructor (renderPass: RenderPass, framebuffer: Framebuffer) { + constructor (renderPass: RenderPass | null = null, framebuffer: Framebuffer | null = null) { this.renderPass = renderPass; this.framebuffer = framebuffer; } - /*refcount*/ renderPass: RenderPass; - /*refcount*/ framebuffer: Framebuffer; + reset (renderPass: RenderPass | null = null, framebuffer: Framebuffer | null = null): void { + this.renderPass = renderPass; + this.framebuffer = framebuffer; + this.clearColors.length = 0; + this.clearDepth = 0; + this.clearStencil = 0; + } + /*refcount*/ renderPass: RenderPass | null; + /*refcount*/ framebuffer: Framebuffer | null; readonly clearColors: Color[] = []; clearDepth = 0; clearStencil = 0; } export class FormatView { + reset (): void { + this.format = Format.UNKNOWN; + } format: Format = Format.UNKNOWN; } export class SubresourceView { + reset (): void { + this.textureView = null; + this.format = Format.UNKNOWN; + this.indexOrFirstMipLevel = 0; + this.numMipLevels = 0; + this.firstArraySlice = 0; + this.numArraySlices = 0; + this.firstPlane = 0; + this.numPlanes = 0; + } /*refcount*/ textureView: Texture | null = null; format: Format = Format.UNKNOWN; indexOrFirstMipLevel = 0; @@ -612,14 +756,12 @@ export interface ResourceGraphVisitor { export type ResourceGraphObject = ManagedResource | ManagedBuffer | ManagedTexture -| Buffer -| Texture +| PersistentBuffer +| PersistentTexture | Framebuffer | RenderSwapchain | FormatView -| SubresourceView -| PersistentBuffer -| PersistentTexture; +| SubresourceView; //----------------------------------------------------------------- // Graph Concept @@ -1308,24 +1450,41 @@ export class ResourceGraph implements BidirectionalGraph } export class ComputePass { + reset (): void { + this.computeViews.clear(); + this.textures.clear(); + } readonly computeViews: Map = new Map(); readonly textures: Map = new Map(); } export class ResolvePass { + reset (): void { + this.resolvePairs.length = 0; + } readonly resolvePairs: ResolvePair[] = []; } export class CopyPass { + reset (): void { + this.copyPairs.length = 0; + this.uploadPairs.length = 0; + } readonly copyPairs: CopyPair[] = []; readonly uploadPairs: UploadPair[] = []; } export class MovePass { + reset (): void { + this.movePairs.length = 0; + } readonly movePairs: MovePair[] = []; } export class RaytracePass { + reset (): void { + this.computeViews.clear(); + } readonly computeViews: Map = new Map(); } @@ -1335,6 +1494,11 @@ export class ClearView { this.clearFlags = clearFlags; this.clearColor = clearColor; } + reset (slotName = '', clearFlags: ClearFlagBit = ClearFlagBit.ALL): void { + this.slotName = slotName; + this.clearFlags = clearFlags; + this.clearColor.reset(); + } slotName: string; clearFlags: ClearFlagBit; readonly clearColor: Color; @@ -1345,31 +1509,68 @@ export class RenderQueue { this.hint = hint; this.phaseID = phaseID; } + reset (hint: QueueHint = QueueHint.RENDER_OPAQUE, phaseID = 0xFFFFFFFF): void { + this.hint = hint; + this.phaseID = phaseID; + this.viewport = null; + } hint: QueueHint; phaseID: number; viewport: Viewport | null = null; } +export enum CullingFlags { + NONE = 0, + CAMERA_FRUSTUM = 0x1, + LIGHT_FRUSTUM = 0x2, + LIGHT_BOUNDS = 0x4, +} + export class SceneData { - constructor (scene: RenderScene | null = null, camera: Camera | null = null, flags: SceneFlags = SceneFlags.NONE, light: LightInfo = new LightInfo()) { + constructor ( + scene: RenderScene | null = null, + camera: Camera | null = null, + flags: SceneFlags = SceneFlags.NONE, + light: LightInfo = new LightInfo(), + cullingFlags: CullingFlags = CullingFlags.CAMERA_FRUSTUM, + shadingLight: Light | null = null, + ) { this.scene = scene; this.camera = camera; this.light = light; this.flags = flags; + this.cullingFlags = cullingFlags; + this.shadingLight = shadingLight; + } + reset ( + scene: RenderScene | null = null, + camera: Camera | null = null, + flags: SceneFlags = SceneFlags.NONE, + cullingFlags: CullingFlags = CullingFlags.CAMERA_FRUSTUM, + shadingLight: Light | null = null, + ): void { + this.scene = scene; + this.camera = camera; + this.light.reset(); + this.flags = flags; + this.cullingFlags = cullingFlags; + this.shadingLight = shadingLight; } /*pointer*/ scene: RenderScene | null; /*pointer*/ camera: Camera | null; readonly light: LightInfo; flags: SceneFlags; + cullingFlags: CullingFlags; + /*refcount*/ shadingLight: Light | null; } export class Dispatch { constructor ( - material: Material | null, - passID: number, - threadGroupCountX: number, - threadGroupCountY: number, - threadGroupCountZ: number, + material: Material | null = null, + passID = 0, + threadGroupCountX = 0, + threadGroupCountY = 0, + threadGroupCountZ = 0, ) { this.material = material; this.passID = passID; @@ -1377,6 +1578,19 @@ export class Dispatch { this.threadGroupCountY = threadGroupCountY; this.threadGroupCountZ = threadGroupCountZ; } + reset ( + material: Material | null = null, + passID = 0, + threadGroupCountX = 0, + threadGroupCountY = 0, + threadGroupCountZ = 0, + ): void { + this.material = material; + this.passID = passID; + this.threadGroupCountX = threadGroupCountX; + this.threadGroupCountY = threadGroupCountY; + this.threadGroupCountZ = threadGroupCountZ; + } /*refcount*/ material: Material | null; passID: number; threadGroupCountX: number; @@ -1385,7 +1599,13 @@ export class Dispatch { } export class Blit { - constructor (material: Material | null, passID: number, sceneFlags: SceneFlags, camera: Camera | null) { + constructor (material: Material | null = null, passID = 0, sceneFlags: SceneFlags = SceneFlags.NONE, camera: Camera | null = null) { + this.material = material; + this.passID = passID; + this.sceneFlags = sceneFlags; + this.camera = camera; + } + reset (material: Material | null = null, passID = 0, sceneFlags: SceneFlags = SceneFlags.NONE, camera: Camera | null = null): void { this.material = material; this.passID = passID; this.sceneFlags = sceneFlags; @@ -1398,6 +1618,13 @@ export class Blit { } export class RenderData { + reset (): void { + this.constants.clear(); + this.buffers.clear(); + this.textures.clear(); + this.samplers.clear(); + this.custom = ''; + } readonly constants: Map = new Map(); readonly buffers: Map = new Map(); readonly textures: Map = new Map(); @@ -2288,3 +2515,410 @@ export class RenderGraph implements BidirectionalGraph readonly index: Map = new Map(); readonly sortedVertices: number[] = []; } + +export class RenderGraphObjectPoolSettings { + constructor (batchSize: number) { + this.clearValueBatchSize = batchSize; + this.rasterViewBatchSize = batchSize; + this.computeViewBatchSize = batchSize; + this.resourceDescBatchSize = batchSize; + this.resourceTraitsBatchSize = batchSize; + this.renderSwapchainBatchSize = batchSize; + this.resourceStatesBatchSize = batchSize; + this.managedBufferBatchSize = batchSize; + this.persistentBufferBatchSize = batchSize; + this.managedTextureBatchSize = batchSize; + this.persistentTextureBatchSize = batchSize; + this.managedResourceBatchSize = batchSize; + this.subpassBatchSize = batchSize; + this.subpassGraphBatchSize = batchSize; + this.rasterSubpassBatchSize = batchSize; + this.computeSubpassBatchSize = batchSize; + this.rasterPassBatchSize = batchSize; + this.persistentRenderPassAndFramebufferBatchSize = batchSize; + this.formatViewBatchSize = batchSize; + this.subresourceViewBatchSize = batchSize; + this.resourceGraphBatchSize = batchSize; + this.computePassBatchSize = batchSize; + this.resolvePassBatchSize = batchSize; + this.copyPassBatchSize = batchSize; + this.movePassBatchSize = batchSize; + this.raytracePassBatchSize = batchSize; + this.clearViewBatchSize = batchSize; + this.renderQueueBatchSize = batchSize; + this.sceneDataBatchSize = batchSize; + this.dispatchBatchSize = batchSize; + this.blitBatchSize = batchSize; + this.renderDataBatchSize = batchSize; + this.renderGraphBatchSize = batchSize; + } + clearValueBatchSize = 16; + rasterViewBatchSize = 16; + computeViewBatchSize = 16; + resourceDescBatchSize = 16; + resourceTraitsBatchSize = 16; + renderSwapchainBatchSize = 16; + resourceStatesBatchSize = 16; + managedBufferBatchSize = 16; + persistentBufferBatchSize = 16; + managedTextureBatchSize = 16; + persistentTextureBatchSize = 16; + managedResourceBatchSize = 16; + subpassBatchSize = 16; + subpassGraphBatchSize = 16; + rasterSubpassBatchSize = 16; + computeSubpassBatchSize = 16; + rasterPassBatchSize = 16; + persistentRenderPassAndFramebufferBatchSize = 16; + formatViewBatchSize = 16; + subresourceViewBatchSize = 16; + resourceGraphBatchSize = 16; + computePassBatchSize = 16; + resolvePassBatchSize = 16; + copyPassBatchSize = 16; + movePassBatchSize = 16; + raytracePassBatchSize = 16; + clearViewBatchSize = 16; + renderQueueBatchSize = 16; + sceneDataBatchSize = 16; + dispatchBatchSize = 16; + blitBatchSize = 16; + renderDataBatchSize = 16; + renderGraphBatchSize = 16; +} + +export class RenderGraphObjectPool { + constructor (settings: RenderGraphObjectPoolSettings, renderCommon: RenderCommonObjectPool) { + this.renderCommon = renderCommon; + this._clearValue = new RecyclePool(() => new ClearValue(), settings.clearValueBatchSize); + this._rasterView = new RecyclePool(() => new RasterView(), settings.rasterViewBatchSize); + this._computeView = new RecyclePool(() => new ComputeView(), settings.computeViewBatchSize); + this._resourceDesc = new RecyclePool(() => new ResourceDesc(), settings.resourceDescBatchSize); + this._resourceTraits = new RecyclePool(() => new ResourceTraits(), settings.resourceTraitsBatchSize); + this._renderSwapchain = new RecyclePool(() => new RenderSwapchain(), settings.renderSwapchainBatchSize); + this._resourceStates = new RecyclePool(() => new ResourceStates(), settings.resourceStatesBatchSize); + this._managedBuffer = new RecyclePool(() => new ManagedBuffer(), settings.managedBufferBatchSize); + this._persistentBuffer = new RecyclePool(() => new PersistentBuffer(), settings.persistentBufferBatchSize); + this._managedTexture = new RecyclePool(() => new ManagedTexture(), settings.managedTextureBatchSize); + this._persistentTexture = new RecyclePool(() => new PersistentTexture(), settings.persistentTextureBatchSize); + this._managedResource = new RecyclePool(() => new ManagedResource(), settings.managedResourceBatchSize); + this._subpass = new RecyclePool(() => new Subpass(), settings.subpassBatchSize); + this._subpassGraph = new RecyclePool(() => new SubpassGraph(), settings.subpassGraphBatchSize); + this._rasterSubpass = new RecyclePool(() => new RasterSubpass(), settings.rasterSubpassBatchSize); + this._computeSubpass = new RecyclePool(() => new ComputeSubpass(), settings.computeSubpassBatchSize); + this._rasterPass = new RecyclePool(() => new RasterPass(), settings.rasterPassBatchSize); + this._persistentRenderPassAndFramebuffer = new RecyclePool(() => new PersistentRenderPassAndFramebuffer(), settings.persistentRenderPassAndFramebufferBatchSize); + this._formatView = new RecyclePool(() => new FormatView(), settings.formatViewBatchSize); + this._subresourceView = new RecyclePool(() => new SubresourceView(), settings.subresourceViewBatchSize); + this._resourceGraph = new RecyclePool(() => new ResourceGraph(), settings.resourceGraphBatchSize); + this._computePass = new RecyclePool(() => new ComputePass(), settings.computePassBatchSize); + this._resolvePass = new RecyclePool(() => new ResolvePass(), settings.resolvePassBatchSize); + this._copyPass = new RecyclePool(() => new CopyPass(), settings.copyPassBatchSize); + this._movePass = new RecyclePool(() => new MovePass(), settings.movePassBatchSize); + this._raytracePass = new RecyclePool(() => new RaytracePass(), settings.raytracePassBatchSize); + this._clearView = new RecyclePool(() => new ClearView(), settings.clearViewBatchSize); + this._renderQueue = new RecyclePool(() => new RenderQueue(), settings.renderQueueBatchSize); + this._sceneData = new RecyclePool(() => new SceneData(), settings.sceneDataBatchSize); + this._dispatch = new RecyclePool(() => new Dispatch(), settings.dispatchBatchSize); + this._blit = new RecyclePool(() => new Blit(), settings.blitBatchSize); + this._renderData = new RecyclePool(() => new RenderData(), settings.renderDataBatchSize); + this._renderGraph = new RecyclePool(() => new RenderGraph(), settings.renderGraphBatchSize); + } + reset (): void { + this._clearValue.reset(); + this._rasterView.reset(); + this._computeView.reset(); + this._resourceDesc.reset(); + this._resourceTraits.reset(); + this._renderSwapchain.reset(); + this._resourceStates.reset(); + this._managedBuffer.reset(); + this._persistentBuffer.reset(); + this._managedTexture.reset(); + this._persistentTexture.reset(); + this._managedResource.reset(); + this._subpass.reset(); + this._subpassGraph.reset(); + this._rasterSubpass.reset(); + this._computeSubpass.reset(); + this._rasterPass.reset(); + this._persistentRenderPassAndFramebuffer.reset(); + this._formatView.reset(); + this._subresourceView.reset(); + this._resourceGraph.reset(); + this._computePass.reset(); + this._resolvePass.reset(); + this._copyPass.reset(); + this._movePass.reset(); + this._raytracePass.reset(); + this._clearView.reset(); + this._renderQueue.reset(); + this._sceneData.reset(); + this._dispatch.reset(); + this._blit.reset(); + this._renderData.reset(); + this._renderGraph.reset(); + } + createClearValue ( + x = 0, + y = 0, + z = 0, + w = 0, + ): ClearValue { + const v = this._clearValue.add(); + v.reset(x, y, z, w); + return v; + } + createRasterView ( + slotName = '', + accessType: AccessType = AccessType.WRITE, + attachmentType: AttachmentType = AttachmentType.RENDER_TARGET, + loadOp: LoadOp = LoadOp.LOAD, + storeOp: StoreOp = StoreOp.STORE, + clearFlags: ClearFlagBit = ClearFlagBit.ALL, + shaderStageFlags: ShaderStageFlagBit = ShaderStageFlagBit.NONE, + ): RasterView { + const v = this._rasterView.add(); + v.reset(slotName, accessType, attachmentType, loadOp, storeOp, clearFlags, shaderStageFlags); + return v; + } + createComputeView ( + name = '', + accessType: AccessType = AccessType.READ, + clearFlags: ClearFlagBit = ClearFlagBit.NONE, + clearValueType: ClearValueType = ClearValueType.NONE, + shaderStageFlags: ShaderStageFlagBit = ShaderStageFlagBit.NONE, + ): ComputeView { + const v = this._computeView.add(); + v.reset(name, accessType, clearFlags, clearValueType, shaderStageFlags); + return v; + } + createResourceDesc (): ResourceDesc { + const v = this._resourceDesc.add(); + v.reset(); + return v; + } + createResourceTraits ( + residency: ResourceResidency = ResourceResidency.MANAGED, + ): ResourceTraits { + const v = this._resourceTraits.add(); + v.reset(residency); + return v; + } + createRenderSwapchain ( + swapchain: Swapchain | null = null, + ): RenderSwapchain { + const v = this._renderSwapchain.add(); + v.reset(swapchain); + return v; + } + createResourceStates (): ResourceStates { + const v = this._resourceStates.add(); + v.reset(); + return v; + } + createManagedBuffer ( + buffer: Buffer | null = null, + ): ManagedBuffer { + const v = this._managedBuffer.add(); + v.reset(buffer); + return v; + } + createPersistentBuffer ( + buffer: Buffer | null = null, + ): PersistentBuffer { + const v = this._persistentBuffer.add(); + v.reset(buffer); + return v; + } + createManagedTexture ( + texture: Texture | null = null, + ): ManagedTexture { + const v = this._managedTexture.add(); + v.reset(texture); + return v; + } + createPersistentTexture ( + texture: Texture | null = null, + ): PersistentTexture { + const v = this._persistentTexture.add(); + v.reset(texture); + return v; + } + createManagedResource (): ManagedResource { + const v = this._managedResource.add(); + v.reset(); + return v; + } + createSubpass (): Subpass { + const v = this._subpass.add(); + v.reset(); + return v; + } + createSubpassGraph (): SubpassGraph { + const v = this._subpassGraph.add(); + v.clear(); + return v; + } + createRasterSubpass ( + subpassID = 0xFFFFFFFF, + count = 1, + quality = 0, + ): RasterSubpass { + const v = this._rasterSubpass.add(); + v.reset(subpassID, count, quality); + return v; + } + createComputeSubpass ( + subpassID = 0xFFFFFFFF, + ): ComputeSubpass { + const v = this._computeSubpass.add(); + v.reset(subpassID); + return v; + } + createRasterPass (): RasterPass { + const v = this._rasterPass.add(); + v.reset(); + return v; + } + createPersistentRenderPassAndFramebuffer ( + renderPass: RenderPass | null = null, + framebuffer: Framebuffer | null = null, + ): PersistentRenderPassAndFramebuffer { + const v = this._persistentRenderPassAndFramebuffer.add(); + v.reset(renderPass, framebuffer); + return v; + } + createFormatView (): FormatView { + const v = this._formatView.add(); + v.reset(); + return v; + } + createSubresourceView (): SubresourceView { + const v = this._subresourceView.add(); + v.reset(); + return v; + } + createResourceGraph (): ResourceGraph { + const v = this._resourceGraph.add(); + v.clear(); + return v; + } + createComputePass (): ComputePass { + const v = this._computePass.add(); + v.reset(); + return v; + } + createResolvePass (): ResolvePass { + const v = this._resolvePass.add(); + v.reset(); + return v; + } + createCopyPass (): CopyPass { + const v = this._copyPass.add(); + v.reset(); + return v; + } + createMovePass (): MovePass { + const v = this._movePass.add(); + v.reset(); + return v; + } + createRaytracePass (): RaytracePass { + const v = this._raytracePass.add(); + v.reset(); + return v; + } + createClearView ( + slotName = '', + clearFlags: ClearFlagBit = ClearFlagBit.ALL, + ): ClearView { + const v = this._clearView.add(); + v.reset(slotName, clearFlags); + return v; + } + createRenderQueue ( + hint: QueueHint = QueueHint.RENDER_OPAQUE, + phaseID = 0xFFFFFFFF, + ): RenderQueue { + const v = this._renderQueue.add(); + v.reset(hint, phaseID); + return v; + } + createSceneData ( + scene: RenderScene | null = null, + camera: Camera | null = null, + flags: SceneFlags = SceneFlags.NONE, + cullingFlags: CullingFlags = CullingFlags.CAMERA_FRUSTUM, + shadingLight: Light | null = null, + ): SceneData { + const v = this._sceneData.add(); + v.reset(scene, camera, flags, cullingFlags, shadingLight); + return v; + } + createDispatch ( + material: Material | null = null, + passID = 0, + threadGroupCountX = 0, + threadGroupCountY = 0, + threadGroupCountZ = 0, + ): Dispatch { + const v = this._dispatch.add(); + v.reset(material, passID, threadGroupCountX, threadGroupCountY, threadGroupCountZ); + return v; + } + createBlit ( + material: Material | null = null, + passID = 0, + sceneFlags: SceneFlags = SceneFlags.NONE, + camera: Camera | null = null, + ): Blit { + const v = this._blit.add(); + v.reset(material, passID, sceneFlags, camera); + return v; + } + createRenderData (): RenderData { + const v = this._renderData.add(); + v.reset(); + return v; + } + createRenderGraph (): RenderGraph { + const v = this._renderGraph.add(); + v.clear(); + return v; + } + public readonly renderCommon: RenderCommonObjectPool; + private readonly _clearValue: RecyclePool; + private readonly _rasterView: RecyclePool; + private readonly _computeView: RecyclePool; + private readonly _resourceDesc: RecyclePool; + private readonly _resourceTraits: RecyclePool; + private readonly _renderSwapchain: RecyclePool; + private readonly _resourceStates: RecyclePool; + private readonly _managedBuffer: RecyclePool; + private readonly _persistentBuffer: RecyclePool; + private readonly _managedTexture: RecyclePool; + private readonly _persistentTexture: RecyclePool; + private readonly _managedResource: RecyclePool; + private readonly _subpass: RecyclePool; + private readonly _subpassGraph: RecyclePool; + private readonly _rasterSubpass: RecyclePool; + private readonly _computeSubpass: RecyclePool; + private readonly _rasterPass: RecyclePool; + private readonly _persistentRenderPassAndFramebuffer: RecyclePool; + private readonly _formatView: RecyclePool; + private readonly _subresourceView: RecyclePool; + private readonly _resourceGraph: RecyclePool; + private readonly _computePass: RecyclePool; + private readonly _resolvePass: RecyclePool; + private readonly _copyPass: RecyclePool; + private readonly _movePass: RecyclePool; + private readonly _raytracePass: RecyclePool; + private readonly _clearView: RecyclePool; + private readonly _renderQueue: RecyclePool; + private readonly _sceneData: RecyclePool; + private readonly _dispatch: RecyclePool; + private readonly _blit: RecyclePool; + private readonly _renderData: RecyclePool; + private readonly _renderGraph: RecyclePool; +} diff --git a/cocos/rendering/custom/scene-culling.ts b/cocos/rendering/custom/scene-culling.ts index b337a00f6de..c2b74a07516 100644 --- a/cocos/rendering/custom/scene-culling.ts +++ b/cocos/rendering/custom/scene-culling.ts @@ -1,18 +1,47 @@ -import { Vec3, assert } from '../../core'; +import { Vec3, assert, RecyclePool } from '../../core'; import { Frustum, intersect, AABB } from '../../core/geometry'; -import { CommandBuffer } from '../../gfx'; +import { CommandBuffer, Device, Buffer, BufferInfo, BufferViewInfo, MemoryUsageBit, BufferUsageBit } from '../../gfx'; import { BatchingSchemes, Pass, RenderScene } from '../../render-scene'; -import { CSMLevel, Camera, DirectionalLight, Light, LightType, Model, SKYBOX_FLAG, ShadowType, SpotLight } from '../../render-scene/scene'; -import { Node } from '../../scene-graph'; +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 { hashCombineStr, getSubpassOrPassID, bool } from './define'; +import { hashCombineStr, getSubpassOrPassID, bool, AlignUp, SetLightUBO } from './define'; import { LayoutGraphData } from './layout-graph'; -import { RenderGraph, RenderGraphValue, SceneData } from './render-graph'; +import { CullingFlags, RenderGraph, RenderGraphValue, SceneData } from './render-graph'; import { SceneFlags } from './types'; -import { RenderQueue, RenderQueueDesc } from './web-pipeline-types'; +import { RenderQueue, RenderQueueDesc, instancePool } from './web-pipeline-types'; +import { ObjectPool } from './utils'; +import { getUniformBlockSize } from './layout-graph-utils'; +import { WebProgramLibrary } from './web-program-library'; -function computeCullingKey (camera: Camera | null, light: Light | null, castShadows: boolean, lightLevel: number): number { +const vec3Pool = new ObjectPool(() => new Vec3()); +class CullingPools { + frustumCullingKeyRecycle = new RecyclePool(() => new FrustumCullingKey(), 8); + frustumCullingsRecycle = new RecyclePool(() => new FrustumCulling(), 8); + lightBoundsCullingRecycle = new RecyclePool(() => new LightBoundsCulling(), 8); + lightBoundsCullingResultRecycle = new RecyclePool(() => new LightBoundsCullingResult(), 8); + lightBoundsCullingKeyRecycle = new RecyclePool(() => new LightBoundsCullingKey(), 8); + renderQueueRecycle = new RecyclePool(() => new RenderQueue(), 8); + renderQueueDescRecycle = new RecyclePool(() => new RenderQueueDesc(), 8); +} +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]); + +function computeCullingKey ( + sceneData: SceneData, + castShadows: boolean, + refId: number = -1, +): number { let hashCode = 0; + 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 shadeLight = sceneData.shadingLight; if (camera) { // camera hashCode = hashCombineStr(`u${camera.node.uuid}`, hashCode); @@ -47,30 +76,77 @@ function computeCullingKey (camera: Camera | null, light: Light | null, castShad // 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; } -class CullingKey { - camera: Camera | null; - light: Light | null; +class FrustumCullingKey { + sceneData: SceneData | null = null; castShadows = false; - lightLevel = 0xffffffff; - constructor (camera: Camera | null, light: Light | null, castShadows: boolean, lightLevel: number) { - this.camera = camera; - this.light = light; + constructor (sceneData: SceneData | null = null, castShadows: boolean = false) { + this.sceneData = sceneData; + this.castShadows = castShadows; + } + update (sceneData: SceneData, castShadows: boolean): void { + this.sceneData = sceneData; this.castShadows = castShadows; - this.lightLevel = lightLevel; } } +class LightBoundsCullingKey { + sceneData: SceneData | null = null; + frustumCullingID: FrustumCullingID = -1; + constructor (sceneData: SceneData | null = null, frustumCullingID: FrustumCullingID = -1) { + this.sceneData = sceneData; + this.frustumCullingID = frustumCullingID; + } + update (sceneData: SceneData | null = null, frustumCullingID: FrustumCullingID = -1): void { + this.sceneData = sceneData; + this.frustumCullingID = frustumCullingID; + } +} + +class LightBoundsCulling { + resultKeyIndex: Map = new Map(); + resultIndex: Map = new Map(); + update (): void { + this.resultIndex.clear(); + this.resultKeyIndex.clear(); + } +} + +class LightBoundsCullingResult { + instances: Array = new Array(); + lightByteOffset: number = 0xFFFFFFFF; + update (): LightBoundsCullingResult { + this.instances.length = 0; + this.lightByteOffset = 0xFFFFFFFF; + return this; + } +} + +type FrustumCullingID = number; +type LightBoundsCullingID = number; + let pSceneData: PipelineSceneData; -class CullingQueries { +class FrustumCulling { // key: hash val - culledResultIndex: Map = new Map(); - cullingKeyResult: Map = new Map(); + resultIndex: Map = new Map(); + resultKeyIndex: Map = new Map(); + update (): void { + this.resultIndex.clear(); + this.resultKeyIndex.clear(); + } } function isNodeVisible (node: Node, visibility: number): boolean { @@ -80,6 +156,11 @@ function isNodeVisible (node: Node, visibility: number): boolean { function isModelVisible (model: Model, visibility: number): boolean { return !!(visibility & model.visFlags); } + +function isReflectProbeMask (model: Model): boolean { + return bool((model.node.layer & REFLECTION_PROBE_DEFAULT_MASK) === model.node.layer || (REFLECTION_PROBE_DEFAULT_MASK & model.visFlags)); +} + const transWorldBounds = new AABB(); function isFrustumVisible (model: Model, frustum: Readonly, castShadow: boolean): boolean { const modelWorldBounds = model.worldBounds; @@ -94,31 +175,47 @@ function isFrustumVisible (model: Model, frustum: Readonly, castShadow: return !intersect.aabbFrustum(transWorldBounds, frustum); } +function isIntersectAABB (lAABB: AABB, rAABB: AABB): boolean { + return !intersect.aabbWithAABB(lAABB, rAABB); +} + function sceneCulling ( - skyboxModelToSkip: Model | null, scene: RenderScene, camera: Camera, camOrLightFrustum: Readonly, castShadow: boolean, + probe: ReflectionProbe | null, models: Array, ): void { + const skybox = pSceneData.skybox; + const skyboxModel = skybox.model; const visibility = camera.visibility; + const camSkyboxFlag = camera.clearFlag & SKYBOX_FLAG; + if (!castShadow && skybox && skybox.enabled && skyboxModel && camSkyboxFlag) { + models.push(skyboxModel); + } + for (const model of scene.models) { assert(!!model); - if (!model.enabled || model === skyboxModelToSkip || (castShadow && !model.castShadow)) { + if (!model.enabled || !model.node || (castShadow && !model.castShadow)) { continue; } if (scene && 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; + } - if (isNodeVisible(model.node, visibility) - || isModelVisible(model, visibility)) { - // frustum culling - if (isFrustumVisible(model, camOrLightFrustum, castShadow)) { - continue; + models.push(model); } - + } else if (isReflectProbeMask(model)) { models.push(model); } } @@ -138,9 +235,10 @@ function computeSortingDepth (camera: Camera, model: Model): number { let depth = 0; if (model.node) { const node = model.transform; - const tempVec3 = new Vec3(); + const tempVec3 = vec3Pool.acquire(); const position = Vec3.subtract(tempVec3, node.worldPosition, camera.position); depth = position.dot(camera.forward); + vec3Pool.release(tempVec3); } return depth; } @@ -149,17 +247,29 @@ function addRenderObject ( phaseLayoutId: number, isDrawOpaqueOrMask: boolean, isDrawBlend: boolean, + isDrawProbe: boolean, camera: Camera, model: Model, queue: RenderQueue, ): void { + const probeQueue = queue.probeQueue; + if (isDrawProbe) { + probeQueue.applyMacro(model, phaseLayoutId); + } const subModels = model.subModels; const subModelCount = subModels.length; + const skyboxModel = pSceneData.skybox.model; for (let subModelIdx = 0; subModelIdx < subModelCount; ++subModelIdx) { const subModel = subModels[subModelIdx]; const passes = subModel.passes; const passCount = passes.length; + const probePhase = probeQueue.probeMap.includes(subModel); + 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); + continue; + } const pass = passes[passIdx]; // check phase const phaseAllowed = phaseLayoutId === pass.phaseID; @@ -180,12 +290,10 @@ function addRenderObject ( // add object to queue if (pass.batchingScheme === BatchingSchemes.INSTANCING) { - const instancedBuffer = pass.getInstancedBuffer(); - instancedBuffer.merge(subModel, passIdx); if (is_blend) { - queue.transparentInstancingQueue.add(instancedBuffer); + queue.transparentInstancingQueue.add(pass, subModel, passIdx); } else { - queue.opaqueInstancingQueue.add(instancedBuffer); + queue.opaqueInstancingQueue.add(pass, subModel, passIdx); } } else { const depth = computeSortingDepth(camera, model); @@ -198,30 +306,44 @@ function addRenderObject ( } } } - +const rangedDirLightBoundingBox = new AABB(0, 0, 0, 0.5, 0.5, 0.5); +const lightAABB = new AABB(); export class SceneCulling { - sceneQueries: Map = new Map(); - culledResults: Array> = new Array>(); + frustumCullings: Map = new Map(); + frustumCullingResults: Array> = new Array>(); + lightBoundsCullings: Map = new Map(); + lightBoundsCullingResults: Array = new Array(); renderQueues: Array = new Array(); - sceneQueryIndex: Map = new Map(); + renderQueueIndex: Map = new Map(); + cullingPools = new CullingPools(); // source id - numCullingQueries = 0; + numFrustumCulling = 0; + numLightBoundsCulling = 0; // target id numRenderQueues = 0; layoutGraph; renderGraph; + resetPool (): void { + const cullingPools = this.cullingPools; + cullingPools.frustumCullingKeyRecycle.reset(); + cullingPools.frustumCullingsRecycle.reset(); + cullingPools.lightBoundsCullingRecycle.reset(); + cullingPools.lightBoundsCullingResultRecycle.reset(); + cullingPools.lightBoundsCullingKeyRecycle.reset(); + cullingPools.renderQueueRecycle.reset(); + cullingPools.renderQueueDescRecycle.reset(); + instancePool.reset(); + } clear (): void { - this.sceneQueries.clear(); - for (const c of this.culledResults) { - c.length = 0; - } - this.culledResults.length = 0; - for (const q of this.renderQueues) { - q.clear(); - } + this.resetPool(); + this.frustumCullings.clear(); + this.frustumCullingResults.length = 0; + this.lightBoundsCullings.clear(); + this.lightBoundsCullingResults.length = 0; this.renderQueues.length = 0; - this.sceneQueryIndex.clear(); - this.numCullingQueries = 0; + this.renderQueueIndex.clear(); + this.numLightBoundsCulling = 0; + this.numFrustumCulling = 0; this.numRenderQueues = 0; } @@ -230,41 +352,90 @@ export class SceneCulling { this.renderGraph = rg; pSceneData = pplSceneData; this.collectCullingQueries(rg, lg); - this.batchCulling(pplSceneData); + this.batchFrustumCulling(pplSceneData); + this.batchLightBoundsCulling(); this.fillRenderQueues(rg, pplSceneData); } - private getOrCreateSceneCullingQuery (sceneData: SceneData): number { + private getOrCreateLightBoundsCulling (sceneData: SceneData, frustumCullingID: FrustumCullingID): LightBoundsCullingID { + if (!(sceneData.cullingFlags & CullingFlags.LIGHT_BOUNDS)) { + 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.'); + + let queries = this.lightBoundsCullings.get(scene); + if (!queries) { + const cullingQuery = this.cullingPools.lightBoundsCullingRecycle.add(); + cullingQuery.update(); + this.lightBoundsCullings.set(scene, cullingQuery); + queries = this.lightBoundsCullings.get(scene)!; + } + const key = computeCullingKey(sceneData, false, frustumCullingID); + const cullNum = queries.resultIndex.get(key); + if (cullNum !== undefined) { + return cullNum; + } + 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); + const cullingKey = this.cullingPools.lightBoundsCullingKeyRecycle.add(); + cullingKey.update( + sceneData, + frustumCullingID, + ); + queries.resultKeyIndex.set(key, cullingKey); + return lightBoundsCullingID; + } + + private getOrCreateFrustumCulling (sceneId: number): number { + const sceneData: SceneData = this.renderGraph.getScene(sceneId); const scene = sceneData.scene!; - let queries = this.sceneQueries.get(scene); + let queries = this.frustumCullings.get(scene); if (!queries) { - this.sceneQueries.set(scene, new CullingQueries()); - queries = this.sceneQueries.get(scene); + const cullingQuery = this.cullingPools.frustumCullingsRecycle.add(); + cullingQuery.update(); + this.frustumCullings.set(scene, cullingQuery); + queries = this.frustumCullings.get(scene)!; } - const castShadow = bool(sceneData.flags & SceneFlags.SHADOW_CASTER); - const key = computeCullingKey(sceneData.camera, sceneData.light.light, castShadow, sceneData.light.level); - const cullNum = queries!.culledResultIndex.get(key); + const castShadow: boolean = bool(sceneData.flags & SceneFlags.SHADOW_CASTER); + const key = computeCullingKey(sceneData, castShadow); + const cullNum = queries.resultIndex.get(key); if (cullNum !== undefined) { return cullNum; } - const soureceID = this.numCullingQueries++; - if (this.numCullingQueries > this.culledResults.length) { - assert(this.numCullingQueries === (this.culledResults.length + 1)); - this.culledResults.push([]); + const frustumCulledResultID: FrustumCullingID = this.numFrustumCulling++; + if (this.numFrustumCulling > this.frustumCullingResults.length) { + assert(this.numFrustumCulling === (this.frustumCullingResults.length + 1)); + this.frustumCullingResults.push([]); } - queries!.culledResultIndex.set(key, soureceID); - queries!.cullingKeyResult.set(key, new CullingKey(sceneData.camera, sceneData.light.light, castShadow, sceneData.light.level)); - return soureceID; + queries.resultIndex.set(key, frustumCulledResultID); + const cullingKey = this.cullingPools.frustumCullingKeyRecycle.add(); + cullingKey.update( + sceneData, + castShadow, + ); + queries.resultKeyIndex.set(key, cullingKey); + return frustumCulledResultID; } private createRenderQueue (sceneFlags: SceneFlags, subpassOrPassLayoutID: number): number { const targetID = this.numRenderQueues++; if (this.numRenderQueues > this.renderQueues.length) { assert(this.numRenderQueues === (this.renderQueues.length + 1)); - this.renderQueues.push(new RenderQueue()); + 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; @@ -280,13 +451,16 @@ export class SceneCulling { assert(!!sceneData.scene); continue; } - const sourceID = this.getOrCreateSceneCullingQuery(sceneData); - const layoutID = getSubpassOrPassID(v, rg, lg); + 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); // add render queue to query source - this.sceneQueryIndex.set(v, new RenderQueueDesc(sourceID, targetID, lightType)); + this.renderQueueIndex.set(v, renderQueueDesc); } } @@ -299,69 +473,179 @@ export class SceneCulling { } } - private batchCulling (pplSceneData: PipelineSceneData): void { - const skybox = pplSceneData.skybox; - const skyboxModelToSkip = skybox ? skybox.model : null; - for (const [scene, queries] of this.sceneQueries) { + private _getPhaseIdFromScene (scene: number): number { + const rg: RenderGraph = this.renderGraph; + const renderQueueId = rg.getParent(scene); + assert(rg.holds(RenderGraphValue.Queue, renderQueueId)); + const graphRenderQueue = rg.getQueue(renderQueueId); + return graphRenderQueue.phaseID; + } + + private getBuiltinShadowFrustum (pplSceneData: PipelineSceneData, camera: Camera, mainLight: DirectionalLight, level: number): Readonly { + const csmLayers = pplSceneData.csmLayers; + const csmLevel = mainLight.csmLevel; + let frustum: Readonly; + const shadows = pplSceneData.shadows; + if (shadows.type === ShadowType.Planar) { + return camera.frustum; + } + if (shadows.enabled && shadows.type === ShadowType.ShadowMap && mainLight && mainLight.node) { + // pplSceneData.updateShadowUBORange(UBOShadow.SHADOW_COLOR_OFFSET, shadows.shadowColor); + csmLayers.update(pplSceneData, camera); + } + + if (mainLight.shadowFixedArea || csmLevel === CSMLevel.LEVEL_1) { + return csmLayers.specialLayer.validFrustum; + } + return csmLayers.layers[level].validFrustum; + } + + private batchFrustumCulling (pplSceneData: PipelineSceneData): void { + for (const [scene, queries] of this.frustumCullings) { assert(!!scene); - for (const [key, sourceID] of queries.culledResultIndex) { - const cullingKey = queries.cullingKeyResult.get(key)!; - assert(!!cullingKey.camera); - assert(cullingKey.camera.scene === scene); - const camera = cullingKey.camera; - const light = cullingKey.light; - const level = cullingKey.lightLevel; + for (const [key, frustomCulledResultID] of queries.resultIndex) { + const cullingKey = queries.resultKeyIndex.get(key)!; + const sceneData = cullingKey.sceneData!; + assert(!!sceneData.camera); + assert(sceneData.camera.scene === scene); + const light = sceneData.light.light; + const level = sceneData.light.level; const castShadow = cullingKey.castShadows; - assert(sourceID < this.culledResults.length); - const models = this.culledResults[sourceID]; + 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); + continue; + } if (light) { switch (light.type) { case LightType.SPOT: - sceneCulling(skyboxModelToSkip, scene, camera, (light as SpotLight).frustum, castShadow, models); + sceneCulling(scene, camera, (light as SpotLight).frustum, castShadow, null, models); break; case LightType.DIRECTIONAL: { - const csmLayers = pplSceneData.csmLayers; - const mainLight: DirectionalLight = light as DirectionalLight; - const csmLevel = mainLight.csmLevel; - let frustum: Readonly; - const shadows = pplSceneData.shadows; - if (shadows.type === ShadowType.Planar) { - frustum = camera.frustum; - } else { - if (shadows.enabled && shadows.type === ShadowType.ShadowMap && mainLight && mainLight.node) { - // pplSceneData.updateShadowUBORange(UBOShadow.SHADOW_COLOR_OFFSET, shadows.shadowColor); - csmLayers.update(pplSceneData, camera); - } - - if (mainLight.shadowFixedArea || csmLevel === CSMLevel.LEVEL_1) { - frustum = csmLayers.specialLayer.validFrustum; - } else { - frustum = csmLayers.layers[level].validFrustum; - } - } - sceneCulling(skyboxModelToSkip, scene, camera, frustum, castShadow, models); + const frustum = this.getBuiltinShadowFrustum(pplSceneData, camera, light as DirectionalLight, level); + sceneCulling(scene, camera, frustum, castShadow, null, models); } break; default: } } else { - sceneCulling(skyboxModelToSkip, scene, camera, camera.frustum, castShadow, models); + sceneCulling(scene, camera, camera.frustum, castShadow, null, models); + } + } + } + } + + 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); + } + } + } + + private executeSpotLightCulling (light: SpotLight, frustumCullingResult: Array, lightBoundsCullingResult: Array): void { + 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); + } + } + } + + 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); + } + } + } + + private executeRangedDirectionalLightCulling ( + light: RangedDirectionalLight, + frustumCullingResult: Array, + lightBoundsCullingResult: Array, + ): 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); + } + } + } + + 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 === scene); + assert(cullingID < this.frustumCullingResults.length); + const lightBoundsCullingResult = this.lightBoundsCullingResults[cullingID]; + assert(lightBoundsCullingResult.instances.length === 0); + switch (sceneData.shadingLight.type) { + case LightType.SPHERE: + { + const light = sceneData.shadingLight as SphereLight; + this.executeSphereLightCulling(light, frustumCullingResult, lightBoundsCullingResult.instances); + } + break; + case LightType.SPOT: + { + const light = sceneData.shadingLight as SpotLight; + this.executeSpotLightCulling(light, frustumCullingResult, lightBoundsCullingResult.instances); + } + break; + case LightType.POINT: + { + const light = sceneData.shadingLight as PointLight; + this.executePointLightCulling(light, frustumCullingResult, lightBoundsCullingResult.instances); + } + break; + case LightType.RANGED_DIRECTIONAL: + { + const light = sceneData.shadingLight as RangedDirectionalLight; + this.executeRangedDirectionalLightCulling(light, frustumCullingResult, lightBoundsCullingResult.instances); + } + break; + case LightType.DIRECTIONAL: + case LightType.UNKNOWN: + default: } } } } private fillRenderQueues (rg: RenderGraph, pplSceneData: PipelineSceneData): void { - const skybox = pplSceneData.skybox; - for (const [sceneId, desc] of this.sceneQueryIndex) { + for (const [sceneId, desc] of this.renderQueueIndex) { assert(rg.holds(RenderGraphValue.Scene, sceneId)); - const sourceId = desc.culledSource; + const frustomCulledResultID = desc.frustumCulledResultID; + const lightBoundsCullingID = desc.lightBoundsCulledResultID; const targetId = desc.renderQueueTarget; const sceneData = rg.getScene(sceneId); - const isDrawBlend = bool(sceneData.flags & SceneFlags.TRANSPARENT_OBJECT); - const isDrawOpaqueOrMask = bool(sceneData.flags & (SceneFlags.OPAQUE_OBJECT | SceneFlags.CUTOUT_OBJECT)); - const isDrawShadowCaster = bool(sceneData.flags & SceneFlags.SHADOW_CASTER); - if (!isDrawShadowCaster && !isDrawBlend && !isDrawOpaqueOrMask) { + 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); + if (!isDrawShadowCaster && !isDrawBlend && !isDrawOpaqueOrMask && !isDrawProbe) { continue; } // render queue info @@ -372,8 +656,23 @@ export class SceneCulling { assert(phaseLayoutId !== this.layoutGraph.nullVertex()); // culling source - assert(sourceId < this.culledResults.length); - const sourceModels = this.culledResults[sourceId]; + 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 []; + } + })(); // queue target assert(targetId < this.renderQueues.length); @@ -383,27 +682,13 @@ export class SceneCulling { // skybox const camera = sceneData.camera; assert(!!camera); - if (!bool(sceneData.flags & SceneFlags.SHADOW_CASTER) - && skybox && skybox.enabled - && (camera.clearFlag & SKYBOX_FLAG)) { - assert(!!skybox.model); - const model = skybox.model; - const node = model.node; - let depth = 0; - if (node) { - const tempVec3 = new Vec3(); - Vec3.subtract(tempVec3, node.worldPosition, camera.position); - depth = tempVec3.dot(camera.forward); - } - renderQueue.opaqueQueue.add(model, depth, 0, 0); - } - // fill render queue for (const model of sourceModels) { addRenderObject( phaseLayoutId, isDrawOpaqueOrMask, isDrawBlend, + isDrawProbe, camera, model, renderQueue, @@ -414,3 +699,165 @@ export class SceneCulling { } } } + +export class LightResource { + private cpuBuffer!: Float32Array; + private programLibrary?: WebProgramLibrary; + private device: Device | null = null; + private elementSize: number = 0; + private maxNumLights: number = 16; + private binding: number = 0xFFFFFFFF; + private resized: boolean = false; + private lightBuffer?: Buffer; + private firstLightBufferView: Buffer | null = null; + private lights: Array = []; + private lightIndex: Map = new Map(); + + init (programLib: WebProgramLibrary, deviceIn: Device, maxNumLights: number): void { + assert(!this.device); + + this.device = deviceIn; + this.programLibrary = programLib; + + const instanceLayout = this.programLibrary.localLayoutData; + const attrID: number = programLib.layoutGraph.attributeIndex.get('CCForwardLight')!; + const uniformBlock = instanceLayout.uniformBlocks.get(attrID); + + this.elementSize = AlignUp( + getUniformBlockSize(uniformBlock!.members), + this.device.capabilities.uboOffsetAlignment, + ); + this.maxNumLights = maxNumLights; + this.binding = programLib.localLayoutData.bindingMap.get(attrID)!; + + const bufferSize = this.elementSize * this.maxNumLights; + + this.lightBuffer = this.device.createBuffer(new BufferInfo( + BufferUsageBit.UNIFORM | BufferUsageBit.TRANSFER_DST, + MemoryUsageBit.HOST | MemoryUsageBit.DEVICE, + bufferSize, + this.elementSize, + )); + this.firstLightBufferView = this.device.createBuffer(new BufferViewInfo( + this.lightBuffer, + 0, + this.elementSize, + )); + + this.cpuBuffer = new Float32Array(bufferSize / Float32Array.BYTES_PER_ELEMENT); + this.lights = new Array(this.maxNumLights); + this.lightIndex = new Map(); + + assert(!!(this.elementSize && this.maxNumLights)); + this.resized = true; + } + + buildLights (sceneCulling: SceneCulling, bHDR: boolean, shadowInfo: Shadows | null): void { + // Build light buffer + for (const [scene, lightBoundsCullings] of sceneCulling.lightBoundsCullings) { + for (const [key, lightBoundsCullingID] of lightBoundsCullings.resultIndex) { + const lightBoundsCulling = lightBoundsCullings.resultKeyIndex.get(key)!; + const sceneData = lightBoundsCulling.sceneData!; + let exposure: number = 1.0; + if (sceneData.camera) { + exposure = sceneData.camera.exposure; + } else if (sceneData.light.probe && sceneData.light.probe.camera) { + exposure = sceneData.light.probe.camera.exposure; + } else { + throw new Error('Unexpected situation: No camera or probe found.'); + } + const lightByteOffset: number = this.addLight( + sceneData.shadingLight!, + bHDR, + exposure, + shadowInfo, + ); + + // Save light byte offset for each light bounds culling + const result: LightBoundsCullingResult = sceneCulling.lightBoundsCullingResults[lightBoundsCullingID]; + result.lightByteOffset = lightByteOffset; + } + } + + // Assign light byte offset to each queue + for (const [sceneID, desc] of sceneCulling.renderQueueIndex) { + if (desc.lightBoundsCulledResultID === 0xFFFFFFFF) { + continue; + } + const lightByteOffset: number = sceneCulling.lightBoundsCullingResults[desc.lightBoundsCulledResultID].lightByteOffset; + + sceneCulling.renderQueues[desc.renderQueueTarget].lightByteOffset = lightByteOffset; + } + } + + tryUpdateRenderSceneLocalDescriptorSet (sceneCulling: SceneCulling): void { + if (!this.resized || !sceneCulling.lightBoundsCullings.size) { + return; + } + + for (const [scene, culling] of sceneCulling.frustumCullings) { + for (const model of scene.models) { + if (!model) { + throw new Error('Unexpected null model.'); + } + for (const submodel of model.subModels) { + const set = submodel.descriptorSet; + set.bindBuffer(this.binding, this.firstLightBufferView!); + set.update(); + } + } + } + this.resized = false; + } + + clear (): void { + this.cpuBuffer.fill(0); + this.lights.length = 0; + this.lightIndex.clear(); + } + + addLight (light: Light, bHDR: boolean, exposure: number, shadowInfo: Shadows | null): number { + // Already added + const existingLightID = this.lightIndex.get(light); + if (existingLightID !== undefined) { + return existingLightID; + } + + // Resize buffer if needed + if (this.lights.length === this.maxNumLights) { + this.resized = true; + this.maxNumLights *= 2; + const bufferSize = this.elementSize * this.maxNumLights; + this.lightBuffer!.resize(bufferSize); + this.firstLightBufferView = this.device!.createBuffer(new BufferViewInfo( + this.lightBuffer, + 0, + this.elementSize, + )); + this.cpuBuffer = new Float32Array(bufferSize / Float32Array.BYTES_PER_ELEMENT); + this.lights = new Array(this.maxNumLights); + this.lightIndex = new Map(); + } + + assert(this.lights.length < this.maxNumLights); + + // Add light + const lightID = this.lights.length; + this.lights[lightID] = light; + this.lightIndex.set(light, lightID); + + // Update buffer + const offset = this.elementSize / Float32Array.BYTES_PER_ELEMENT * lightID; + SetLightUBO(light, bHDR, exposure, shadowInfo, this.cpuBuffer, offset, this.elementSize); + + return lightID * this.elementSize; + } + + buildLightBuffer (cmdBuffer: CommandBuffer): void { + cmdBuffer.updateBuffer( + this.lightBuffer!, + this.cpuBuffer, + (this.lights.length * this.elementSize) / Float32Array.BYTES_PER_ELEMENT, + ); + } +} diff --git a/cocos/rendering/custom/types.ts b/cocos/rendering/custom/types.ts index bde9ca132f7..577955e8017 100644 --- a/cocos/rendering/custom/types.ts +++ b/cocos/rendering/custom/types.ts @@ -29,9 +29,11 @@ */ /* eslint-disable max-len */ import { ResolveMode, ShaderStageFlagBit, Type, UniformBlock } from '../../gfx'; +import { ReflectionProbe } from '../../render-scene/scene/reflection-probe'; import { Light } from '../../render-scene/scene'; import { OutputArchive, InputArchive } from './archive'; import { saveUniformBlock, loadUniformBlock } from './serialization'; +import { RecyclePool } from '../../core/memop'; export enum UpdateFrequency { PER_INSTANCE, @@ -208,6 +210,7 @@ export enum SceneFlags { DRAW_NON_INSTANCING = 0x1000, REFLECTION_PROBE = 0x2000, GPU_DRIVEN = 0x4000, + NON_BUILTIN = 0x8000, ALL = 0xFFFFFFFF, } @@ -288,12 +291,20 @@ export function getClearValueTypeName (e: ClearValueType): string { } export class LightInfo { - constructor (light: Light | null = null, level = 0, culledByLight = false) { + constructor (light: Light | null = null, level = 0, culledByLight = false, probe: ReflectionProbe | null = null) { this.light = light; + this.probe = probe; + this.level = level; + this.culledByLight = culledByLight; + } + reset (light: Light | null = null, level = 0, culledByLight = false, probe: ReflectionProbe | null = null): void { + this.light = light; + this.probe = probe; this.level = level; this.culledByLight = culledByLight; } /*refcount*/ light: Light | null; + /*pointer*/ probe: ReflectionProbe | null; level: number; culledByLight: boolean; } @@ -339,11 +350,21 @@ export class Descriptor { constructor (type: Type = Type.UNKNOWN) { this.type = type; } + reset (type: Type = Type.UNKNOWN): void { + this.type = type; + this.count = 1; + } type: Type; count = 1; } export class DescriptorBlock { + reset (): void { + this.descriptors.clear(); + this.uniformBlocks.clear(); + this.capacity = 0; + this.count = 0; + } readonly descriptors: Map = new Map(); readonly uniformBlocks: Map = new Map(); capacity = 0; @@ -351,6 +372,14 @@ export class DescriptorBlock { } export class DescriptorBlockFlattened { + reset (): void { + this.descriptorNames.length = 0; + this.uniformBlockNames.length = 0; + this.descriptors.length = 0; + this.uniformBlocks.length = 0; + this.capacity = 0; + this.count = 0; + } readonly descriptorNames: string[] = []; readonly uniformBlockNames: string[] = []; readonly descriptors: Descriptor[] = []; @@ -393,6 +422,19 @@ export class ResolvePair { this.mode = mode; this.mode1 = mode1; } + reset ( + source = '', + target = '', + resolveFlags: ResolveFlags = ResolveFlags.NONE, + mode: ResolveMode = ResolveMode.SAMPLE_ZERO, + mode1: ResolveMode = ResolveMode.SAMPLE_ZERO, + ): void { + this.source = source; + this.target = target; + this.resolveFlags = resolveFlags; + this.mode = mode; + this.mode1 = mode1; + } source: string; target: string; resolveFlags: ResolveFlags; @@ -424,6 +466,29 @@ export class CopyPair { this.targetFirstSlice = targetFirstSlice; this.targetPlaneSlice = targetPlaneSlice; } + reset ( + source = '', + target = '', + mipLevels = 0xFFFFFFFF, + numSlices = 0xFFFFFFFF, + sourceMostDetailedMip = 0, + sourceFirstSlice = 0, + sourcePlaneSlice = 0, + targetMostDetailedMip = 0, + targetFirstSlice = 0, + targetPlaneSlice = 0, + ): void { + this.source = source; + this.target = target; + this.mipLevels = mipLevels; + this.numSlices = numSlices; + this.sourceMostDetailedMip = sourceMostDetailedMip; + this.sourceFirstSlice = sourceFirstSlice; + this.sourcePlaneSlice = sourcePlaneSlice; + this.targetMostDetailedMip = targetMostDetailedMip; + this.targetFirstSlice = targetFirstSlice; + this.targetPlaneSlice = targetPlaneSlice; + } source: string; target: string; mipLevels: number; @@ -454,6 +519,22 @@ export class UploadPair { this.targetFirstSlice = targetFirstSlice; this.targetPlaneSlice = targetPlaneSlice; } + reset ( + target = '', + mipLevels = 0xFFFFFFFF, + numSlices = 0xFFFFFFFF, + targetMostDetailedMip = 0, + targetFirstSlice = 0, + targetPlaneSlice = 0, + ): void { + // source: Uint8Array size unchanged + this.target = target; + this.mipLevels = mipLevels; + this.numSlices = numSlices; + this.targetMostDetailedMip = targetMostDetailedMip; + this.targetFirstSlice = targetFirstSlice; + this.targetPlaneSlice = targetPlaneSlice; + } readonly source: Uint8Array; target: string; mipLevels: number; @@ -481,6 +562,23 @@ export class MovePair { this.targetFirstSlice = targetFirstSlice; this.targetPlaneSlice = targetPlaneSlice; } + reset ( + source = '', + target = '', + mipLevels = 0xFFFFFFFF, + numSlices = 0xFFFFFFFF, + targetMostDetailedMip = 0, + targetFirstSlice = 0, + targetPlaneSlice = 0, + ): void { + this.source = source; + this.target = target; + this.mipLevels = mipLevels; + this.numSlices = numSlices; + this.targetMostDetailedMip = targetMostDetailedMip; + this.targetFirstSlice = targetFirstSlice; + this.targetPlaneSlice = targetPlaneSlice; + } source: string; target: string; mipLevels: number; @@ -491,6 +589,19 @@ export class MovePair { } export class PipelineStatistics { + reset (): void { + this.numRenderPasses = 0; + this.numManagedTextures = 0; + this.totalManagedTextures = 0; + this.numUploadBuffers = 0; + this.numUploadBufferViews = 0; + this.numFreeUploadBuffers = 0; + this.numFreeUploadBufferViews = 0; + this.numDescriptorSets = 0; + this.numFreeDescriptorSets = 0; + this.numInstancingBuffers = 0; + this.numInstancingUniformBlocks = 0; + } numRenderPasses = 0; numManagedTextures = 0; totalManagedTextures = 0; @@ -504,14 +615,175 @@ export class PipelineStatistics { numInstancingUniformBlocks = 0; } +export class RenderCommonObjectPoolSettings { + constructor (batchSize: number) { + this.lightInfoBatchSize = batchSize; + this.descriptorBatchSize = batchSize; + this.descriptorBlockBatchSize = batchSize; + this.descriptorBlockFlattenedBatchSize = batchSize; + this.descriptorBlockIndexBatchSize = batchSize; + this.resolvePairBatchSize = batchSize; + this.copyPairBatchSize = batchSize; + this.uploadPairBatchSize = batchSize; + this.movePairBatchSize = batchSize; + this.pipelineStatisticsBatchSize = batchSize; + } + lightInfoBatchSize = 16; + descriptorBatchSize = 16; + descriptorBlockBatchSize = 16; + descriptorBlockFlattenedBatchSize = 16; + descriptorBlockIndexBatchSize = 16; + resolvePairBatchSize = 16; + copyPairBatchSize = 16; + uploadPairBatchSize = 16; + movePairBatchSize = 16; + pipelineStatisticsBatchSize = 16; +} + +export class RenderCommonObjectPool { + constructor (settings: RenderCommonObjectPoolSettings) { + this._lightInfo = new RecyclePool(() => new LightInfo(), settings.lightInfoBatchSize); + this._descriptor = new RecyclePool(() => new Descriptor(), settings.descriptorBatchSize); + this._descriptorBlock = new RecyclePool(() => new DescriptorBlock(), settings.descriptorBlockBatchSize); + this._descriptorBlockFlattened = new RecyclePool(() => new DescriptorBlockFlattened(), settings.descriptorBlockFlattenedBatchSize); + this._descriptorBlockIndex = new RecyclePool(() => new DescriptorBlockIndex(), settings.descriptorBlockIndexBatchSize); + this._resolvePair = new RecyclePool(() => new ResolvePair(), settings.resolvePairBatchSize); + this._copyPair = new RecyclePool(() => new CopyPair(), settings.copyPairBatchSize); + this._uploadPair = new RecyclePool(() => new UploadPair(), settings.uploadPairBatchSize); + this._movePair = new RecyclePool(() => new MovePair(), settings.movePairBatchSize); + this._pipelineStatistics = new RecyclePool(() => new PipelineStatistics(), settings.pipelineStatisticsBatchSize); + } + reset (): void { + this._lightInfo.reset(); + this._descriptor.reset(); + this._descriptorBlock.reset(); + this._descriptorBlockFlattened.reset(); + this._descriptorBlockIndex.reset(); + this._resolvePair.reset(); + this._copyPair.reset(); + this._uploadPair.reset(); + this._movePair.reset(); + this._pipelineStatistics.reset(); + } + createLightInfo ( + light: Light | null = null, + level = 0, + culledByLight = false, + probe: ReflectionProbe | null = null, + ): LightInfo { + const v = this._lightInfo.add(); + v.reset(light, level, culledByLight, probe); + return v; + } + createDescriptor ( + type: Type = Type.UNKNOWN, + ): Descriptor { + const v = this._descriptor.add(); + v.reset(type); + return v; + } + createDescriptorBlock (): DescriptorBlock { + const v = this._descriptorBlock.add(); + v.reset(); + return v; + } + createDescriptorBlockFlattened (): DescriptorBlockFlattened { + const v = this._descriptorBlockFlattened.add(); + v.reset(); + return v; + } + createDescriptorBlockIndex ( + updateFrequency: UpdateFrequency = UpdateFrequency.PER_INSTANCE, + parameterType: ParameterType = ParameterType.CONSTANTS, + descriptorType: DescriptorTypeOrder = DescriptorTypeOrder.UNIFORM_BUFFER, + visibility: ShaderStageFlagBit = ShaderStageFlagBit.NONE, + ): DescriptorBlockIndex { + const v = this._descriptorBlockIndex.add(); + v.updateFrequency = updateFrequency; + v.parameterType = parameterType; + v.descriptorType = descriptorType; + v.visibility = visibility; + return v; + } + createResolvePair ( + source = '', + target = '', + resolveFlags: ResolveFlags = ResolveFlags.NONE, + mode: ResolveMode = ResolveMode.SAMPLE_ZERO, + mode1: ResolveMode = ResolveMode.SAMPLE_ZERO, + ): ResolvePair { + const v = this._resolvePair.add(); + v.reset(source, target, resolveFlags, mode, mode1); + return v; + } + createCopyPair ( + source = '', + target = '', + mipLevels = 0xFFFFFFFF, + numSlices = 0xFFFFFFFF, + sourceMostDetailedMip = 0, + sourceFirstSlice = 0, + sourcePlaneSlice = 0, + targetMostDetailedMip = 0, + targetFirstSlice = 0, + targetPlaneSlice = 0, + ): CopyPair { + const v = this._copyPair.add(); + v.reset(source, target, mipLevels, numSlices, sourceMostDetailedMip, sourceFirstSlice, sourcePlaneSlice, targetMostDetailedMip, targetFirstSlice, targetPlaneSlice); + return v; + } + createUploadPair ( + target = '', + mipLevels = 0xFFFFFFFF, + numSlices = 0xFFFFFFFF, + targetMostDetailedMip = 0, + targetFirstSlice = 0, + targetPlaneSlice = 0, + ): UploadPair { + const v = this._uploadPair.add(); + v.reset(target, mipLevels, numSlices, targetMostDetailedMip, targetFirstSlice, targetPlaneSlice); + return v; + } + createMovePair ( + source = '', + target = '', + mipLevels = 0xFFFFFFFF, + numSlices = 0xFFFFFFFF, + targetMostDetailedMip = 0, + targetFirstSlice = 0, + targetPlaneSlice = 0, + ): MovePair { + const v = this._movePair.add(); + v.reset(source, target, mipLevels, numSlices, targetMostDetailedMip, targetFirstSlice, targetPlaneSlice); + return v; + } + createPipelineStatistics (): PipelineStatistics { + const v = this._pipelineStatistics.add(); + v.reset(); + return v; + } + private readonly _lightInfo: RecyclePool; + private readonly _descriptor: RecyclePool; + private readonly _descriptorBlock: RecyclePool; + private readonly _descriptorBlockFlattened: RecyclePool; + private readonly _descriptorBlockIndex: RecyclePool; + private readonly _resolvePair: RecyclePool; + private readonly _copyPair: RecyclePool; + private readonly _uploadPair: RecyclePool; + private readonly _movePair: RecyclePool; + private readonly _pipelineStatistics: RecyclePool; +} + export function saveLightInfo (ar: OutputArchive, v: LightInfo): void { // skip, v.light: Light + // skip, v.probe: ReflectionProbe ar.writeNumber(v.level); ar.writeBool(v.culledByLight); } export function loadLightInfo (ar: InputArchive, v: LightInfo): void { // skip, v.light: Light + // skip, v.probe: ReflectionProbe v.level = ar.readNumber(); v.culledByLight = ar.readBool(); } diff --git a/cocos/rendering/custom/utils.ts b/cocos/rendering/custom/utils.ts index 92e985d9731..7654640d022 100644 --- a/cocos/rendering/custom/utils.ts +++ b/cocos/rendering/custom/utils.ts @@ -118,3 +118,33 @@ export function getUBOTypeCount (type: Type): number { return 0; } } + +export class ObjectPool { + // Array to store objects in the pool + private pool: T[] = []; + // Function to create new objects + private createFunction: (...args: U) => T; + + // Constructor, takes a function to create objects as parameter + constructor (createFunction: (...args: U) => T) { + this.createFunction = createFunction; + } + // Get object from the pool, either take from the pool if available or create a new one + acquire (...args: U): T { + if (this.pool.length > 0) { + return this.pool.pop()!; + } + return this.createFunction(...args); + } + // Put the object back into the pool for later reuse + release (obj: T): void { + // Push the object to the end of the pool + if (!this.pool.includes(obj)) { + this.pool.push(obj); + } + } + + create (...args: U): T { + return this.createFunction(...args); + } +} diff --git a/cocos/rendering/custom/web-pipeline-types.ts b/cocos/rendering/custom/web-pipeline-types.ts index c41128cefd4..28024fe8e46 100644 --- a/cocos/rendering/custom/web-pipeline-types.ts +++ b/cocos/rendering/custom/web-pipeline-types.ts @@ -1,6 +1,8 @@ +import { RecyclePool, assert, cclegacy } from '../../core'; import { CommandBuffer, DescriptorSet, Device, PipelineState, RenderPass, deviceManager } from '../../gfx'; -import { RenderScene } from '../../render-scene'; -import { Camera, Light, LightType, Model, SubModel } from '../../render-scene/scene'; +import { IMacroPatch, Pass } from '../../render-scene'; + +import { LightType, Model, SubModel } from '../../render-scene/scene'; import { SetIndex } from '../define'; import { InstancedBuffer } from '../instanced-buffer'; import { PipelineStateManager } from '../pipeline-state-manager'; @@ -29,11 +31,108 @@ export class DrawInstance { this.shaderID = shaderID; this.passIndex = passIndex; } + update ( + subModel: SubModel | null = null, + priority = 0, + hash = 0, + depth = 0, + shaderID = 0, + passIndex = 0, + ): void { + this.subModel = subModel; + this.priority = priority; + this.hash = hash; + this.depth = depth; + this.shaderID = shaderID; + this.passIndex = passIndex; + } +} + +export const instancePool = new RecyclePool(() => new DrawInstance(), 8); + +const CC_USE_RGBE_OUTPUT = 'CC_USE_RGBE_OUTPUT'; +function getLayoutId (passLayout: string, phaseLayout: string): number { + const r = cclegacy.rendering; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return r.getPhaseID(r.getPassID(passLayout), phaseLayout); +} +function getPassIndexFromLayout (subModel: SubModel, phaseLayoutId: number): number { + const passes = subModel.passes; + for (let k = 0; k < passes.length; k++) { + if ((passes[k].phaseID === phaseLayoutId)) { + return k; + } + } + return -1; +} + +export class ProbeHelperQueue { + probeMap: Array = new Array(); + defaultId: number = getLayoutId('default', 'default'); + + clear (): void { + this.probeMap.length = 0; + } + + removeMacro (): void { + for (const subModel of this.probeMap) { + let patches: IMacroPatch[] = []; + patches = patches.concat(subModel.patches!); + if (!patches.length) continue; + for (let j = 0; j < patches.length; j++) { + const patch = patches[j]; + if (patch.name === CC_USE_RGBE_OUTPUT) { + patches.splice(j, 1); + break; + } + } + subModel.onMacroPatchesStateChanged(patches); + } + } + applyMacro (model: Model, probeLayoutId: number): void { + const subModels = model.subModels; + for (let j = 0; j < subModels.length; j++) { + const subModel: SubModel = subModels[j]; + + //Filter transparent objects + const isTransparent = subModel.passes[0].blendState.targets[0].blend; + if (isTransparent) { + continue; + } + + let passIdx = getPassIndexFromLayout(subModel, probeLayoutId); + let bUseReflectPass = true; + if (passIdx < 0) { + probeLayoutId = this.defaultId; + passIdx = getPassIndexFromLayout(subModel, probeLayoutId); + bUseReflectPass = false; + } + 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); + } + } + } } export class RenderDrawQueue { instances: Array = new Array(); + empty (): boolean { + return this.instances.length === 0; + } + + clear (): void { + this.instances.length = 0; + } + add (model: Model, depth: number, subModelIdx: number, passIdx: number): void { const subModel = model.subModels[subModelIdx]; const pass = subModel.passes[passIdx]; @@ -42,8 +141,9 @@ export class RenderDrawQueue { const shaderId = subModel.shaders[passIdx].typedID; const hash = (0 << 30) | (passPriority as number << 16) | (modelPriority as number << 8) | passIdx; const priority = model.priority; - - this.instances.push(new DrawInstance(subModel, priority, hash, depth, shaderId, passIdx)); + const instance = instancePool.add(); + instance.update(subModel, priority, hash, depth, shaderId, passIdx); + this.instances.push(instance); } /** * @en Comparison sorting function. Opaque objects are sorted by priority -> depth front to back -> shader ID. @@ -83,6 +183,9 @@ export class RenderDrawQueue { device: Device, renderPass: RenderPass, cmdBuffer: CommandBuffer, + ds: DescriptorSet | null = null, + offset = 0, + dynamicOffsets: number[] | null = null, ): void { for (const instance of this.instances) { const subModel = instance.subModel!; @@ -95,7 +198,17 @@ export class RenderDrawQueue { cmdBuffer.bindPipelineState(pso); cmdBuffer.bindDescriptorSet(SetIndex.MATERIAL, pass.descriptorSet); - cmdBuffer.bindDescriptorSet(SetIndex.LOCAL, subModel.descriptorSet); + if (ds) { + cmdBuffer.bindDescriptorSet(SetIndex.GLOBAL, ds, [offset]); + } + if (dynamicOffsets) { + cmdBuffer.bindDescriptorSet(SetIndex.LOCAL, subModel.descriptorSet, dynamicOffsets); + } else { + cmdBuffer.bindDescriptorSet( + SetIndex.LOCAL, + subModel.descriptorSet, + ); + } cmdBuffer.bindInputAssembler(inputAssembler); cmdBuffer.draw(inputAssembler); } @@ -103,19 +216,59 @@ export class RenderDrawQueue { } export class RenderInstancingQueue { - batches: Set = new Set(); + passInstances: Map = new Map(); + instanceBuffers: Array = new Array(); sortedBatches: Array = new Array(); - add (instancedBuffer: InstancedBuffer): void { - this.batches.add(instancedBuffer); + empty (): boolean { + return this.passInstances.size === 0; + } + + add (pass: Pass, subModel: SubModel, passID: number): void { + const iter = this.passInstances.get(pass); + 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)!]; + instancedBuffer.merge(subModel, passID); + } + + clear (): void { + this.sortedBatches.length = 0; + this.passInstances.clear(); + const instanceBuffers = this.instanceBuffers; + instanceBuffers.forEach((instance) => { + instance.clear(); + }); + this.instanceBuffers.length = 0; } sort (): void { - this.sortedBatches = Array.from(this.batches); + this.sortedBatches.length = this.passInstances.size; + let index = 0; + for (const [pass, bufferID] of this.passInstances.entries()) { + this.sortedBatches[index++] = this.instanceBuffers[bufferID]; + } } uploadBuffers (cmdBuffer: CommandBuffer): void { - for (const instanceBuffer of this.batches) { + for (const [pass, bufferID] of this.passInstances.entries()) { + const instanceBuffer = this.instanceBuffers[bufferID]; if (instanceBuffer.hasPendingModels) { instanceBuffer.uploadBuffers(cmdBuffer); } @@ -173,29 +326,44 @@ export class RenderInstancingQueue { } export class RenderQueueDesc { - culledSource: number; + frustumCulledResultID: number; + lightBoundsCulledResultID: number; renderQueueTarget: number; lightType: LightType; constructor ( - culledSourceIn = 0xFFFFFFFF, + frustumCulledResultID = 0xFFFFFFFF, + lightBoundsCulledResultID = 0xFFFFFFFF, renderQueueTargetIn = 0xFFFFFFFF, lightTypeIn: LightType = LightType.UNKNOWN, ) { - this.culledSource = culledSourceIn; + 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; } } export class RenderQueue { + probeQueue: ProbeHelperQueue = new ProbeHelperQueue(); opaqueQueue: RenderDrawQueue = new RenderDrawQueue(); transparentQueue: RenderDrawQueue = new RenderDrawQueue(); opaqueInstancingQueue: RenderInstancingQueue = new RenderInstancingQueue(); transparentInstancingQueue: RenderInstancingQueue = new RenderInstancingQueue(); sceneFlags: SceneFlags = SceneFlags.NONE; - subpassOrPassLayoutID = 0xffffffff; - + subpassOrPassLayoutID = 0xFFFFFFFF; + lightByteOffset = 0xFFFFFFFF; sort (): void { this.opaqueQueue.sortOpaqueOrCutout(); this.transparentQueue.sortTransparent(); @@ -203,32 +371,29 @@ export class RenderQueue { this.transparentInstancingQueue.sort(); } - private _clearInstances (instances: Set): void { - const it = instances.values(); let res = it.next(); - while (!res.done) { - res.value.clear(); - res = it.next(); - } - instances.clear(); - } - - clear (): void { - this.opaqueQueue.instances.length = 0; - this.transparentQueue.instances.length = 0; - this._clearInstances(this.opaqueInstancingQueue.batches); - this.opaqueInstancingQueue.sortedBatches.length = 0; - this._clearInstances(this.transparentInstancingQueue.batches); - this.transparentInstancingQueue.sortedBatches.length = 0; + update (): void { + this.probeQueue.clear(); + this.opaqueQueue.clear(); + this.transparentQueue.clear(); + this.opaqueInstancingQueue.clear(); + this.transparentInstancingQueue.clear(); this.sceneFlags = SceneFlags.NONE; this.subpassOrPassLayoutID = 0xFFFFFFFF; } empty (): boolean { - return this.opaqueQueue.instances.length === 0 - && this.transparentQueue.instances.length === 0 - && this.opaqueInstancingQueue.batches.size === 0 - && this.opaqueInstancingQueue.sortedBatches.length === 0 - && this.transparentInstancingQueue.batches.size === 0 - && this.transparentInstancingQueue.sortedBatches.length === 0; + return this.opaqueQueue.empty() + && this.transparentQueue.empty() + && this.opaqueInstancingQueue.empty() + && this.transparentInstancingQueue.empty(); + } + + recordCommands (cmdBuffer: CommandBuffer, renderPass: RenderPass): 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); } } diff --git a/cocos/rendering/custom/web-pipeline.ts b/cocos/rendering/custom/web-pipeline.ts index d383c1e100d..0a76a1f7506 100644 --- a/cocos/rendering/custom/web-pipeline.ts +++ b/cocos/rendering/custom/web-pipeline.ts @@ -24,21 +24,21 @@ /* eslint-disable max-len */ import { systemInfo } from 'pal/system-info'; -import { DEBUG } from 'internal:constants'; -import { Buffer, 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, UniformBlock, ResolveMode, SampleCount, Color } from '../../gfx'; -import { Mat4, Quat, toRadian, Vec2, Vec3, Vec4, assert, macro, cclegacy, IVec4Like, IMat4Like, IVec2Like, Color as CoreColor } from '../../core'; -import { AccessType, AttachmentType, CopyPair, LightInfo, LightingMode, MovePair, QueueHint, ResolvePair, ResourceDimension, ResourceFlags, ResourceResidency, SceneFlags, UpdateFrequency, UploadPair } from './types'; -import { ComputeView, RasterView, Blit, ClearView, ComputePass, CopyPass, Dispatch, ManagedBuffer, ManagedResource, MovePass, RasterPass, RasterSubpass, RenderData, RenderGraph, RenderGraphComponent, RenderGraphValue, RenderQueue, RenderSwapchain, ResourceDesc, ResourceGraph, ResourceGraphValue, ResourceStates, ResourceTraits, SceneData, Subpass, PersistentBuffer } from './render-graph'; -import { ComputePassBuilder, ComputeQueueBuilder, ComputeSubpassBuilder, BasicPipeline, PipelineBuilder, RenderPassBuilder, RenderQueueBuilder, RenderSubpassBuilder, PipelineType, BasicRenderPassBuilder, PipelineCapabilities, BasicMultisampleRenderPassBuilder } from './pipeline'; +import { DEBUG, EDITOR } from 'internal:constants'; +import { Buffer, 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, UniformBlock, ResolveMode, SampleCount, Color, ComparisonFunc } from '../../gfx'; +import { Mat4, Quat, toRadian, Vec2, Vec3, Vec4, assert, macro, cclegacy, IVec4Like, IMat4Like, IVec2Like, Color as CoreColor, RecyclePool } from '../../core'; +import { AccessType, AttachmentType, CopyPair, LightInfo, LightingMode, MovePair, QueueHint, RenderCommonObjectPool, RenderCommonObjectPoolSettings, 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, RenderGraphObjectPoolSettings, CullingFlags, ManagedResource, ManagedBuffer } from './render-graph'; +import { ComputePassBuilder, ComputeQueueBuilder, BasicPipeline, PipelineBuilder, RenderQueueBuilder, RenderSubpassBuilder, PipelineType, BasicRenderPassBuilder, PipelineCapabilities, BasicMultisampleRenderPassBuilder, Setter, SceneBuilder } from './pipeline'; import { PipelineSceneData } from '../pipeline-scene-data'; -import { Model, Camera, ShadowType, CSMLevel, DirectionalLight, SpotLight, PCFType, Shadows, SphereLight, PointLight, RangedDirectionalLight } from '../../render-scene/scene'; +import { Model, Camera, ShadowType, CSMLevel, DirectionalLight, SpotLight, PCFType, Shadows, SphereLight, PointLight, RangedDirectionalLight, ProbeType } from '../../render-scene/scene'; import { Light, LightType } from '../../render-scene/scene/light'; import { DescriptorSetData, DescriptorSetLayoutData, LayoutGraphData } from './layout-graph'; import { Executor } from './executor'; import { RenderWindow } from '../../render-scene/core/render-window'; import { MacroRecord, RenderScene } from '../../render-scene'; import { GlobalDSManager } from '../global-descriptor-set-manager'; -import { isEnableEffect, supportsR32FloatTexture, supportsRGBA16HalfFloatTexture, UBOSkinning } from '../define'; +import { supportsR32FloatTexture, supportsRGBA16HalfFloatTexture, UBOSkinning } from '../define'; import { OS } from '../../../pal/system-info/enum-type'; import { Compiler } from './compiler'; import { PipelineUBO } from '../pipeline-ubo'; @@ -51,12 +51,15 @@ import { CustomPipelineBuilder } from './custom-pipeline'; import { decideProfilerCamera } from '../pipeline-funcs'; import { DebugViewCompositeType } from '../debug-view'; import { getUBOTypeCount } from './utils'; -import { initGlobalDescBinding } from './define'; +import { buildReflectionProbePass, initGlobalDescBinding } from './define'; import { createGfxDescriptorSetsAndPipelines } from './layout-graph-utils'; import { Root } from '../../root'; import { CSMLayers, CSMShadowLayer } from '../shadow/csm-layers'; import { Scene } from '../../scene-graph'; import { Director } from '../../game'; +import { ReflectionProbeManager } from '../../3d'; +import { legacyCC } from '../../core/global-exports'; +import { WebProgramLibrary } from './web-program-library'; const _uboVec = new Vec4(); const _uboVec3 = new Vec3(); @@ -72,12 +75,94 @@ const _samplerPointInfo = new SamplerInfo( Address.CLAMP, Address.CLAMP, ); -export class WebSetter { + +const renderCommonObjectSetting = new RenderCommonObjectPoolSettings(16); +const renderGraphPoolSetting: RenderGraphObjectPoolSettings = new RenderGraphObjectPoolSettings(16); +class PipelinePool { + renderData = new RenderData(); + layoutGraph = new LayoutGraphData(); + rg = new RenderGraph(); + vertId = -1; + sceneData = new SceneData(); + resourceGraph = new ResourceGraph(); + computePass = new ComputePass(); + rasterPass = new RasterPass(); + rasterSubpass = new RasterSubpass(); + renderQueue = new RenderQueue(); + sceneBuilder = new RecyclePool(() => new WebSceneBuilder(this.renderData, this.layoutGraph, this.rg, this.vertId, this.sceneData), 16); + renderPassBuilder = new RecyclePool(() => new WebRenderPassBuilder(this.renderData, this.rg, this.layoutGraph, this.resourceGraph, this.vertId, this.rasterPass, this.getPipelineSceneData()), 16); + computeQueueBuilder = new RecyclePool(() => new WebComputeQueueBuilder(this.renderData, this.rg, this.layoutGraph, this.vertId, this.renderQueue, this.getPipelineSceneData()), 16); + renderQueueBuilder = new RecyclePool(() => new WebRenderQueueBuilder(this.renderData, this.rg, this.layoutGraph, this.vertId, this.renderQueue, this.getPipelineSceneData()), 16); + renderSubpassBuilder = new RecyclePool(() => new WebRenderSubpassBuilder(this.renderData, this.rg, this.layoutGraph, this.vertId, this.rasterSubpass, this.getPipelineSceneData()), 16); + computePassBuilder = new RecyclePool(() => new WebComputePassBuilder(this.renderData, this.rg, this.layoutGraph, this.resourceGraph, this.vertId, this.computePass, this.getPipelineSceneData()), 16); + samplerInfo = new RecyclePool(() => new SamplerInfo(), 16); + color = new RecyclePool(() => new Color(), 16); + renderCommonObjectPool = new RenderCommonObjectPool(renderCommonObjectSetting); + renderGraphPool = new RenderGraphObjectPool(renderGraphPoolSetting, this.renderCommonObjectPool); + viewport = new RecyclePool(() => new Viewport(), 16); + + getPipelineSceneData (): PipelineSceneData { + return (legacyCC.director.root as Root).pipeline.pipelineSceneData; + } + + createColor ( + x: number = 0, + y: number = 0, + z: number = 0, + w: number = 0, + ): Color { + const color = this.color.add(); + color.set(x, y, z, w); + return color; + } + createSamplerInfo ( + minFilter: Filter = Filter.LINEAR, + magFilter: Filter = Filter.LINEAR, + mipFilter: Filter = Filter.NONE, + addressU: Address = Address.WRAP, + addressV: Address = Address.WRAP, + addressW: Address = Address.WRAP, + maxAnisotropy: number = 0, + cmpFunc: ComparisonFunc = ComparisonFunc.ALWAYS, + ): SamplerInfo { + const samplerInfo = this.samplerInfo.add(); + samplerInfo.minFilter = minFilter; + samplerInfo.magFilter = magFilter; + samplerInfo.mipFilter = mipFilter; + samplerInfo.addressU = addressU; + samplerInfo.addressV = addressV; + samplerInfo.addressW = addressW; + samplerInfo.maxAnisotropy = maxAnisotropy; + samplerInfo.cmpFunc = cmpFunc; + return samplerInfo; + } + reset (): void { + this.sceneBuilder.reset(); + this.renderPassBuilder.reset(); + this.computePassBuilder.reset(); + this.computeQueueBuilder.reset(); + this.renderCommonObjectPool.reset(); + this.renderGraphPool.reset(); + this.viewport.reset(); + this.samplerInfo.reset(); + this.color.reset(); + this.renderQueueBuilder.reset(); + this.renderSubpassBuilder.reset(); + } +} +let pipelinePool: PipelinePool; +let renderGraphPool: RenderGraphObjectPool; +export class WebSetter implements Setter { constructor (data: RenderData, lg: LayoutGraphData) { this._data = data; this._lg = lg; } - + get name (): string { + return ''; + } + set name (name: string) { + // noop + } protected _copyToBuffer (target: IVec4Like | Quat | IVec2Like | IMat4Like | number, offset: number, type: Type): void { assert(offset !== -1); const arr = this.getCurrConstant(); @@ -137,10 +222,10 @@ export class WebSetter { } protected _getCurrUniformBlock (): UniformBlock | undefined { - const block: string = this._currBlock; + const block: string = this._currBlock; const nodeId = this._lg.locateChild(0xFFFFFFFF, this._currStage); const ppl = this._lg.getLayout(nodeId); - const layout = ppl.descriptorSets.get(UpdateFrequency.PER_PASS)!.descriptorSetLayoutData; + const layout = ppl.descriptorSets.get(this._currFrequency)!.descriptorSetLayoutData; const nameID: number = this._lg.attributeIndex.get(block)!; return layout.uniformBlocks.get(nameID); } @@ -148,7 +233,7 @@ export class WebSetter { protected _getCurrDescSetLayoutData (): DescriptorSetLayoutData { const nodeId = this._lg.locateChild(0xFFFFFFFF, this._currStage); const ppl = this._lg.getLayout(nodeId); - const layout = ppl.descriptorSets.get(UpdateFrequency.PER_PASS)!.descriptorSetLayoutData; + const layout = ppl.descriptorSets.get(this._currFrequency)!.descriptorSetLayoutData; return layout; } @@ -165,9 +250,10 @@ export class WebSetter { return -1; } - setCurrConstant (block: string, stage = 'default'): boolean { + setCurrConstant (block: string, stage = 'default', frequency: UpdateFrequency = UpdateFrequency.PER_PASS): boolean { this._currBlock = block; this._currStage = stage; + this._currFrequency = frequency; const nameID: number = this._lg.attributeIndex.get(block)!; this._currCount = 0; const currBlock = this._getCurrUniformBlock(); @@ -183,9 +269,10 @@ export class WebSetter { return this._currConstant; } - public addConstant (block: string, stage = 'default'): boolean { + public addConstant (block: string, stage = 'default', frequency: UpdateFrequency = UpdateFrequency.PER_PASS): boolean { this._currBlock = block; this._currStage = stage; + this._currFrequency = frequency; const num = this._lg.attributeIndex.get(block)!; this._currCount = 0; const currBlock = this._getCurrUniformBlock(); @@ -234,6 +321,9 @@ export class WebSetter { public setFloat (name: string, v: number, idx = 0): void { this._applyCurrConstantBuffer(name, v, Type.FLOAT, idx); } + public setArrayBuffer (name: string, arrayBuffer: ArrayBuffer): void { + throw new Error('Method not implemented.'); + } public offsetFloat (v: number, offset: number): void { this._copyToBuffer(v, offset, Type.FLOAT); } @@ -264,32 +354,195 @@ export class WebSetter { const num = this._lg.attributeIndex.get(name)!; this._data.samplers.set(num, sampler); } + + public getParentLayout (): string { + const director = cclegacy.director; + const root = director.root; + const pipeline = root.pipeline as WebPipeline; + const parId = pipeline.renderGraph!.getParent(this._vertID); + const layoutName = pipeline.renderGraph!.getLayout(parId); + return layoutName; + } + + public getCurrentLayout (): string { + const director = cclegacy.director; + const root = director.root; + const pipeline = root.pipeline as WebPipeline; + const layoutName = pipeline.renderGraph!.getLayout(this._vertID); + return layoutName; + } + public setBuiltinCameraConstants (camera: Camera): void { - // TODO + const director = cclegacy.director; + const root = director.root; + const pipeline = root.pipeline as WebPipeline; + const layoutName = this.getParentLayout(); + setCameraUBOValues(this, camera, pipeline.pipelineSceneData, camera.scene, layoutName); } public setBuiltinShadowMapConstants (light: Light, numLevels?: number): void { - // TODO + setShadowUBOView(this, null, this.getParentLayout()); } - public setBuiltinDirectionalLightViewConstants (light: DirectionalLight): void { - // TODO + public setBuiltinDirectionalLightFrustumConstants (camera: Camera, light: DirectionalLight, csmLevel = 0): void { + setShadowUBOLightView(this, camera, light, csmLevel); } - public setBuiltinSpotLightViewConstants (light: SpotLight): void { - // TODO + public setBuiltinSpotLightFrustumConstants (light: SpotLight): void { + setShadowUBOLightView(this, null, light, 0); } public setBuiltinDirectionalLightConstants (light: DirectionalLight, camera: Camera): void { // TODO } public setBuiltinSphereLightConstants (light: SphereLight, camera: Camera): void { - // TODO + const director = cclegacy.director; + const pipeline = (director.root as Root).pipeline; + const sceneData = pipeline.pipelineSceneData; + if (!this.addConstant('CCForwardLight', this.getParentLayout(), UpdateFrequency.PER_BATCH)) return; + uniformOffset = this.getUniformOffset('cc_lightPos', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.position.x, light.position.y, light.position.z, LightType.SPHERE); + this.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = this.getUniformOffset('cc_lightSizeRangeAngle', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.size, light.range, 0.0, 0.0); + this.offsetVec4(_uboVec, uniformOffset); + } + const isHDR = sceneData.isHDR; + const lightMeterScale = 10000.0; + uniformOffset = this.getUniformOffset('cc_lightColor', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.color.x, light.color.y, light.color.z, 0); + if (light.useColorTemperature) { + const finalColor = light.finalColor; + _uboVec.x = finalColor.x; + _uboVec.y = finalColor.y; + _uboVec.z = finalColor.z; + } + if (isHDR) { + _uboVec.w = (light).luminance * camera.exposure * lightMeterScale; + } else { + _uboVec.w = (light).luminance; + } + this.offsetVec4(_uboVec, uniformOffset); + } } public setBuiltinSpotLightConstants (light: SpotLight, camera: Camera): void { - // TODO + const director = cclegacy.director; + const pipeline = (director.root as Root).pipeline; + const sceneData = pipeline.pipelineSceneData; + + const shadowInfo = sceneData.shadows; + if (!this.addConstant('CCForwardLight', this.getParentLayout(), UpdateFrequency.PER_BATCH)) return; + uniformOffset = this.getUniformOffset('cc_lightPos', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.position.x, light.position.y, light.position.z, LightType.SPOT); + this.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = this.getUniformOffset('cc_lightSizeRangeAngle', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.size, light.range, light.spotAngle, (shadowInfo.enabled && light.shadowEnabled && shadowInfo.type === ShadowType.ShadowMap) ? 1 : 0); + this.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = this.getUniformOffset('cc_lightDir', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.direction.x, light.direction.y, light.direction.z, 0); + this.offsetVec4(_uboVec, uniformOffset); + } + const isHDR = sceneData.isHDR; + const lightMeterScale = 10000.0; + uniformOffset = this.getUniformOffset('cc_lightColor', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.color.x, light.color.y, light.color.z, 0); + if (light.useColorTemperature) { + const finalColor = light.finalColor; + _uboVec.x = finalColor.x; + _uboVec.y = finalColor.y; + _uboVec.z = finalColor.z; + } + if (isHDR) { + _uboVec.w = (light).luminance * camera.exposure * lightMeterScale; + } else { + _uboVec.w = (light).luminance; + } + this.offsetVec4(_uboVec, uniformOffset); + } } public setBuiltinPointLightConstants (light: PointLight, camera: Camera): void { - // TODO + const director = cclegacy.director; + const pipeline = (director.root as Root).pipeline; + const sceneData = pipeline.pipelineSceneData; + if (!this.addConstant('CCForwardLight', this.getParentLayout(), UpdateFrequency.PER_BATCH)) return; + uniformOffset = this.getUniformOffset('cc_lightPos', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.position.x, light.position.y, light.position.z, LightType.POINT); + this.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = this.getUniformOffset('cc_lightSizeRangeAngle', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(0.0, light.range, 0.0, 0.0); + this.offsetVec4(_uboVec, uniformOffset); + } + const isHDR = sceneData.isHDR; + const lightMeterScale = 10000.0; + uniformOffset = this.getUniformOffset('cc_lightColor', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.color.x, light.color.y, light.color.z, 0); + if (light.useColorTemperature) { + const finalColor = light.finalColor; + _uboVec.x = finalColor.x; + _uboVec.y = finalColor.y; + _uboVec.z = finalColor.z; + } + if (isHDR) { + _uboVec.w = (light).luminance * camera.exposure * lightMeterScale; + } else { + _uboVec.w = (light).luminance; + } + this.offsetVec4(_uboVec, uniformOffset); + } } public setBuiltinRangedDirectionalLightConstants (light: RangedDirectionalLight, camera: Camera): void { - // TODO + const director = cclegacy.director; + const pipeline = (director.root as Root).pipeline; + const sceneData = pipeline.pipelineSceneData; + if (!this.addConstant('CCForwardLight', this.getParentLayout(), UpdateFrequency.PER_BATCH)) return; + uniformOffset = this.getUniformOffset('cc_lightPos', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.position.x, light.position.y, light.position.z, LightType.RANGED_DIRECTIONAL); + this.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = this.getUniformOffset('cc_lightSizeRangeAngle', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.right.x, light.right.y, light.right.z, 0.0); + this.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = this.getUniformOffset('cc_lightDir', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.direction.x, light.direction.y, light.direction.z, 0); + this.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = this.getUniformOffset('cc_lightBoundingSizeVS', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + const scale = light.scale; + _uboVec.set(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5, 0); + this.offsetVec4(_uboVec, uniformOffset); + } + const isHDR = sceneData.isHDR; + uniformOffset = this.getUniformOffset('cc_lightColor', Type.FLOAT4); + if (this.hasUniform(uniformOffset)) { + _uboVec.set(light.color.x, light.color.y, light.color.z, 0); + if (light.useColorTemperature) { + const finalColor = light.finalColor; + _uboVec.x = finalColor.x; + _uboVec.y = finalColor.y; + _uboVec.z = finalColor.z; + } + if (isHDR) { + _uboVec.w = light.illuminance * camera.exposure; + } else { + _uboVec.w = light.illuminance; + } + this.offsetVec4(_uboVec, uniformOffset); + } } public hasSampler (name: string): boolean { const id = this._lg.attributeIndex.get(name); @@ -310,19 +563,21 @@ export class WebSetter { } // protected - protected _data: RenderData; + protected _data: RenderData; protected _lg: LayoutGraphData; + protected _vertID: number = -1; protected _currBlock; protected _currStage: string = ''; + protected _currFrequency: UpdateFrequency = UpdateFrequency.PER_PASS; protected _currCount; protected _currConstant: number[] = []; } function setShadowUBOLightView ( setter: WebSetter, - camera: Camera, + camera: Camera | null, light: Light, - level: number, + csmLevel: number, layout = 'default', ): void { const director = cclegacy.director; @@ -343,8 +598,8 @@ function setShadowUBOLightView ( if (shadowInfo.enabled) { if (shadowInfo.type === ShadowType.ShadowMap) { // update CSM layers - if (light && light.node) { - csmLayers.update(sceneData, camera); + if (light && light.node && light.type === LightType.DIRECTIONAL) { + csmLayers.update(sceneData, camera!); } } } @@ -378,7 +633,7 @@ function setShadowUBOLightView ( setter.offsetVec4(_uboVec, uniformOffset); } } else { - const layer = csmLayers.layers[level]; + const layer = csmLayers.layers[csmLevel]; matShadowView = layer.matShadowView; matShadowProj = layer.matShadowProj; matShadowViewProj = layer.matShadowViewProj; @@ -433,6 +688,8 @@ function setShadowUBOLightView ( Mat4.invert(_matView, spotLight.node!.getWorldMatrix()); } if (setter.hasUniform(matViewOffset)) setter.offsetMat4(_matView, matViewOffset); + let matShadowInvProj!: Mat4; + let matShadowProj!: Mat4; if (setter.hasUniform(matViewProOffset)) { Mat4.perspective( _mulMatView, @@ -445,6 +702,8 @@ function setShadowUBOLightView ( cap.clipSpaceSignY, 0, ); + matShadowProj = _mulMatView.clone(); + matShadowInvProj = _mulMatView.clone().invert(); Mat4.multiply(_matView, _mulMatView, _matView); setter.offsetMat4(_matView, matViewProOffset); } @@ -463,6 +722,47 @@ function setShadowUBOLightView ( _uboVec.set(LightType.SPOT, packing, spotLight.shadowNormalBias, 0.0); setter.offsetVec4(_uboVec, uniformOffset); } + uniformOffset = setter.getUniformOffset('cc_shadowProjDepthInfo', Type.FLOAT4); + if (setter.hasUniform(uniformOffset)) { + _uboVec.set(matShadowProj.m10, matShadowProj.m14, matShadowProj.m11, matShadowProj.m15); + setter.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = setter.getUniformOffset('cc_shadowInvProjDepthInfo', Type.FLOAT4); + if (setter.hasUniform(uniformOffset)) { + _uboVec.set(matShadowInvProj.m10, matShadowInvProj.m14, matShadowInvProj.m11, matShadowInvProj.m15); + setter.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = setter.getUniformOffset('cc_shadowProjInfo', Type.FLOAT4); + if (setter.hasUniform(uniformOffset)) { + _uboVec.set(matShadowProj.m00, matShadowProj.m05, 1.0 / matShadowProj.m00, 1.0 / matShadowProj.m05); + setter.offsetVec4(_uboVec, uniformOffset); + } + } + break; + } + case LightType.SPHERE: { + uniformOffset = setter.getUniformOffset('cc_shadowWHPBInfo', Type.FLOAT4); + if (setter.hasUniform(uniformOffset)) { + _uboVec.set(shadowInfo.size.x, shadowInfo.size.y, 1.0, 0.0); + setter.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = setter.getUniformOffset('cc_shadowLPNNInfo', Type.FLOAT4); + if (setter.hasUniform(uniformOffset)) { + _uboVec.set(LightType.SPHERE, packing, 0.0, 0.0); + setter.offsetVec4(_uboVec, uniformOffset); + } + break; + } + case LightType.POINT: { + uniformOffset = setter.getUniformOffset('cc_shadowWHPBInfo', Type.FLOAT4); + if (setter.hasUniform(uniformOffset)) { + _uboVec.set(shadowInfo.size.x, shadowInfo.size.y, 1.0, 0.0); + setter.offsetVec4(_uboVec, uniformOffset); + } + uniformOffset = setter.getUniformOffset('cc_shadowLPNNInfo', Type.FLOAT4); + if (setter.hasUniform(uniformOffset)) { + _uboVec.set(LightType.POINT, packing, 0.0, 0.0); + setter.offsetVec4(_uboVec, uniformOffset); } break; } @@ -831,7 +1131,7 @@ function setTextureUBOView (setter: WebSetter, camera: Camera | null, cfg: Reado setter.setSampler('cc_spotShadowMap', pipeline.defaultSampler); } if (!setter.hasTexture('cc_spotShadowMap')) { - setter.setTexture('cc_spotShadowMap', pipeline.defaultTexture); + setter.setTexture('cc_spotShadowMap', pipeline.defaultTexture); } } @@ -879,7 +1179,53 @@ function getResourceDimension (type: TextureType): ResourceDimension { return ResourceDimension.TEXTURE2D; } -export class WebRenderQueueBuilder extends WebSetter implements RenderQueueBuilder { +export class WebSceneBuilder extends WebSetter implements SceneBuilder { + constructor ( + data: RenderData, + layoutGraph: LayoutGraphData, + rg: RenderGraph, + sceneId: number, + scene: SceneData, + ) { + super(data, layoutGraph); + this._renderGraph = rg; + this._scene = scene; + this._vertID = sceneId; + } + update ( + data: RenderData, + layoutGraph: LayoutGraphData, + rg: RenderGraph, + sceneId: number, + scene: SceneData, + ): void { + this._data = data; + this._lg = layoutGraph; + this._renderGraph = rg; + this._scene = scene; + this._vertID = sceneId; + } + + useLightFrustum (light: Light, csmLevel = 0, optCamera: Camera | undefined = undefined): void { + this._scene.light.light = light; + this._scene.light.level = csmLevel; + this._scene.light.culledByLight = true; + if (optCamera) { + this._scene.camera = optCamera; + } + if (this._scene.flags & SceneFlags.NON_BUILTIN) { + return; + } + const queueId = this._renderGraph.getParent(this._vertID); + const passId = this._renderGraph.getParent(queueId); + const layoutName = this._renderGraph.getLayout(passId); + setShadowUBOLightView(this, this._scene.camera, light, csmLevel, layoutName); + } + private _renderGraph: RenderGraph; + private _scene: SceneData; +} + +export class WebRenderQueueBuilder extends WebSetter implements RenderQueueBuilder { constructor (data: RenderData, renderGraph: RenderGraph, layoutGraph: LayoutGraphData, vertID: number, queue: RenderQueue, pipeline: PipelineSceneData) { super(data, layoutGraph); this._renderGraph = renderGraph; @@ -887,6 +1233,15 @@ export class WebRenderQueueBuilder extends WebSetter implements RenderQueueBuild this._queue = queue; this._pipeline = pipeline; } + update (data: RenderData, renderGraph: RenderGraph, layoutGraph: LayoutGraphData, vertID: number, queue: RenderQueue, pipeline: PipelineSceneData): void { + this._data = data; + this._lg = layoutGraph; + this._renderGraph = renderGraph; + this._vertID = vertID; + this._queue = queue; + this._pipeline = pipeline; + } + setArrayBuffer (name: string, arrayBuffer: ArrayBuffer): void { throw new Error('Method not implemented.'); } @@ -897,16 +1252,17 @@ export class WebRenderQueueBuilder extends WebSetter implements RenderQueueBuild this._renderGraph.setName(this._vertID, name); } - getLayoutName (): string { - const parId = this._renderGraph.getParent(this._vertID); - const layoutName = isEnableEffect() ? this._renderGraph.getLayout(parId) : 'default'; - return layoutName; - } - addSceneOfCamera (camera: Camera, light: LightInfo, sceneFlags = SceneFlags.NONE, name = 'Camera'): void { - const sceneData = new SceneData(camera.scene, camera, sceneFlags, light); - this._renderGraph.addVertex(RenderGraphValue.Scene, sceneData, name, '', new RenderData(), false, this._vertID); - const layoutName = this.getLayoutName(); + const lightTarget = light.light; + const sceneData = renderGraphPool.createSceneData( + camera.scene, + camera, + sceneFlags, + lightTarget ? CullingFlags.CAMERA_FRUSTUM | CullingFlags.LIGHT_BOUNDS : CullingFlags.CAMERA_FRUSTUM, + lightTarget, + ); + this._renderGraph.addVertex(RenderGraphValue.Scene, sceneData, name, '', renderGraphPool.createRenderData(), false, this._vertID); + const layoutName = this.getParentLayout(); const scene: Scene = cclegacy.director.getScene(); setCameraUBOValues( this, @@ -915,37 +1271,53 @@ export class WebRenderQueueBuilder extends WebSetter implements RenderQueueBuild camera.scene || (scene ? scene.renderScene : null), layoutName, ); - if (sceneFlags & SceneFlags.SHADOW_CASTER) { - setShadowUBOLightView(this, camera, light.light!, light.level, layoutName); + if (sceneFlags & SceneFlags.SHADOW_CASTER || (lightTarget && lightTarget.type !== LightType.DIRECTIONAL)) { + setShadowUBOLightView(this, camera, lightTarget!, light.level, layoutName); } else { setShadowUBOView(this, camera, layoutName); } setTextureUBOView(this, camera, this._pipeline); initGlobalDescBinding(this._data, layoutName); } - addScene (camera: Camera, sceneFlags = SceneFlags.NONE): void { - const sceneData = new SceneData(camera.scene, camera, sceneFlags); - this._renderGraph.addVertex(RenderGraphValue.Scene, sceneData, 'Scene', '', new RenderData(), false, this._vertID); - } - addSceneCulledByDirectionalLight (camera: Camera, sceneFlags: SceneFlags, light: DirectionalLight, level: number): void { - const sceneData = new SceneData(camera.scene, camera, sceneFlags, new LightInfo(light, level)); - this._renderGraph.addVertex(RenderGraphValue.Scene, sceneData, 'Scene', '', new RenderData(), false, this._vertID); - } - addSceneCulledBySpotLight (camera: Camera, sceneFlags: SceneFlags, light: SpotLight): void { - const sceneData = new SceneData(camera.scene, camera, sceneFlags, new LightInfo(light, 0)); - this._renderGraph.addVertex(RenderGraphValue.Scene, sceneData, 'Scene', '', new RenderData(), false, this._vertID); + addScene (camera: Camera, sceneFlags = SceneFlags.NONE, light: Light | undefined = undefined): SceneBuilder { + const sceneData = renderGraphPool.createSceneData( + camera.scene, + camera, + sceneFlags, + light ? CullingFlags.CAMERA_FRUSTUM | CullingFlags.LIGHT_BOUNDS : CullingFlags.CAMERA_FRUSTUM, + light, + ); + const renderData = renderGraphPool.createRenderData(); + const sceneId = this._renderGraph.addVertex(RenderGraphValue.Scene, sceneData, 'Scene', '', renderData, false, this._vertID); + if (!(sceneFlags & SceneFlags.NON_BUILTIN)) { + const layoutName = this.getParentLayout(); + setCameraUBOValues( + this, + camera, + this._pipeline, + camera.scene, + layoutName, + ); + if (light && light.type !== LightType.DIRECTIONAL) setShadowUBOLightView(this, camera, light, 0, layoutName); + else if (!(sceneFlags & SceneFlags.SHADOW_CASTER)) setShadowUBOView(this, camera, layoutName); + setTextureUBOView(this, camera, this._pipeline); + initGlobalDescBinding(this._data, layoutName); + } + const sceneBuilder = pipelinePool.sceneBuilder.add(); + sceneBuilder.update(renderData, this._lg, this._renderGraph, sceneId, sceneData); + return sceneBuilder; } addFullscreenQuad (material: Material, passID: number, sceneFlags = SceneFlags.NONE, name = 'Quad'): void { this._renderGraph.addVertex( RenderGraphValue.Blit, - new Blit(material, passID, sceneFlags, null), + renderGraphPool.createBlit(material, passID, sceneFlags, null), name, '', - new RenderData(), + renderGraphPool.createRenderData(), false, this._vertID, ); - const layoutName = this.getLayoutName(); + const layoutName = this.getParentLayout(); const scene: Scene | null = cclegacy.director.getScene(); setCameraUBOValues( this, @@ -965,14 +1337,14 @@ export class WebRenderQueueBuilder extends WebSetter implements RenderQueueBuild addCameraQuad (camera: Camera, material: Material, passID: number, sceneFlags = SceneFlags.NONE): void { this._renderGraph.addVertex( RenderGraphValue.Blit, - new Blit(material, passID, sceneFlags, camera), + renderGraphPool.createBlit(material, passID, sceneFlags, camera), 'CameraQuad', '', - new RenderData(), + renderGraphPool.createRenderData(), false, this._vertID, ); - const layoutName = this.getLayoutName(); + const layoutName = this.getParentLayout(); const scene: Scene = cclegacy.director.getScene(); setCameraUBOValues( this, @@ -990,24 +1362,26 @@ export class WebRenderQueueBuilder extends WebSetter implements RenderQueueBuild initGlobalDescBinding(this._data, layoutName); } clearRenderTarget (name: string, color: Color = new Color()): void { + const clearView = renderGraphPool.createClearView(name, ClearFlagBit.COLOR); + clearView.clearColor.copy(color); this._renderGraph.addVertex( RenderGraphValue.Clear, - [new ClearView(name, ClearFlagBit.COLOR, color)], + [clearView], 'ClearRenderTarget', '', - new RenderData(), + renderGraphPool.createRenderData(), false, this._vertID, ); } setViewport (viewport: Viewport): void { - this._queue.viewport = new Viewport().copy(viewport); + const currViewport = pipelinePool.viewport.add(); + this._queue.viewport = currViewport.copy(viewport); } addCustomCommand (customBehavior: string): void { throw new Error('Method not implemented.'); } private _renderGraph: RenderGraph; - private _vertID: number; private _queue: RenderQueue; private _pipeline: PipelineSceneData; } @@ -1023,7 +1397,24 @@ export class WebRenderSubpassBuilder extends WebSetter implements RenderSubpassB ) { super(data, layoutGraph); this._renderGraph = renderGraph; - this._layoutGraph = layoutGraph; + this._vertID = vertID; + this._subpass = subpass; + this._pipeline = pipeline; + + const layoutName = this._renderGraph.component(RenderGraphComponent.Layout, this._vertID); + this._layoutID = layoutGraph.locateChild(layoutGraph.nullVertex(), layoutName); + } + update ( + data: RenderData, + renderGraph: RenderGraph, + layoutGraph: LayoutGraphData, + vertID: number, + subpass: RasterSubpass, + pipeline: PipelineSceneData, + ): void { + this._data = data; + this._lg = layoutGraph; + this._renderGraph = renderGraph; this._vertID = vertID; this._subpass = subpass; this._pipeline = pipeline; @@ -1063,15 +1454,16 @@ export class WebRenderSubpassBuilder extends WebSetter implements RenderSubpassB throw new Error('Method not implemented.'); } addQueue (hint: QueueHint = QueueHint.RENDER_OPAQUE, layoutName = 'default'): RenderQueueBuilder { - const layoutId = this._layoutGraph.locateChild(this._layoutID, layoutName); + const layoutId = this._lg.locateChild(this._layoutID, layoutName); if (DEBUG) { assert(layoutId !== 0xFFFFFFFF); } - const queue = new RenderQueue(hint); - queue.phaseID = layoutId; - const data = new RenderData(); + const queue = renderGraphPool.createRenderQueue(hint, layoutId); + const data = renderGraphPool.createRenderData(); const queueID = this._renderGraph.addVertex(RenderGraphValue.Queue, queue, '', layoutName, data, false, this._vertID); - return new WebRenderQueueBuilder(data, this._renderGraph, this._layoutGraph, queueID, queue, this._pipeline); + const queueBuilder = pipelinePool.renderQueueBuilder.add(); + queueBuilder.update(data, this._renderGraph, this._lg, queueID, queue, this._pipeline); + return queueBuilder; } get showStatistics (): boolean { return this._subpass.showStatistics; @@ -1079,28 +1471,35 @@ export class WebRenderSubpassBuilder extends WebSetter implements RenderSubpassB set showStatistics (enable: boolean) { this._subpass.showStatistics = enable; } - - private readonly _renderGraph: RenderGraph; - private readonly _vertID: number; - private readonly _layoutID: number; - private readonly _subpass: RasterSubpass; - private readonly _pipeline: PipelineSceneData; - private readonly _layoutGraph: LayoutGraphData; + private _renderGraph: RenderGraph; + private _layoutID: number; + private _subpass: RasterSubpass; + private _pipeline: PipelineSceneData; } export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleRenderPassBuilder { constructor (data: RenderData, renderGraph: RenderGraph, layoutGraph: LayoutGraphData, resourceGraph: ResourceGraph, vertID: number, pass: RasterPass, pipeline: PipelineSceneData) { super(data, layoutGraph); this._renderGraph = renderGraph; - this._layoutGraph = layoutGraph; this._resourceGraph = resourceGraph; this._vertID = vertID; this._pass = pass; this._pipeline = pipeline; - const layoutName = this._renderGraph.component(RenderGraphComponent.Layout, this._vertID); this._layoutID = layoutGraph.locateChild(layoutGraph.nullVertex(), layoutName); } + update (data: RenderData, renderGraph: RenderGraph, layoutGraph: LayoutGraphData, resourceGraph: ResourceGraph, vertID: number, pass: RasterPass, pipeline: PipelineSceneData): void { + this._renderGraph = renderGraph; + this._lg = layoutGraph; + this._resourceGraph = resourceGraph; + this._vertID = vertID; + this._pass = pass; + this._pipeline = pipeline; + this._data = data; + const layoutName = this._renderGraph.component(RenderGraphComponent.Layout, this._vertID); + this._layoutID = layoutGraph.locateChild(layoutGraph.nullVertex(), layoutName); + } + setCustomShaderStages (name: string, stageFlags: ShaderStageFlagBit): void { throw new Error('Method not implemented.'); } @@ -1125,7 +1524,7 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR if (loadOp === LoadOp.LOAD) { clearFlag = ClearFlagBit.NONE; } - const view = new RasterView( + const view = renderGraphPool.createRasterView( '', AccessType.WRITE, @@ -1133,15 +1532,15 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR loadOp, storeOp, clearFlag, - clearColor, ); + view.clearColor.copy(clearColor); 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 = new RasterView( + const view = renderGraphPool.createRasterView( '', AccessType.WRITE, @@ -1149,8 +1548,8 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR loadOp, storeOp, clearFlag, - new Color(depth, stencil, 0, 0), ); + view.clearColor.set(depth, stencil, 0, 0); this._pass.rasterViews.set(name, view); } resolveRenderTarget (source: string, target: string): void { @@ -1165,13 +1564,13 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR // TODO } private _addComputeResource (name: string, accessType: AccessType, slotName: string): void { - const view = new ComputeView(slotName); + 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._layoutGraph.attributeIndex.get(descriptorName); + const descriptorID = this._lg.attributeIndex.get(descriptorName); assert(descriptorID !== undefined); } if (this._pass.computeViews.has(name)) { @@ -1183,7 +1582,7 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR addTexture (name: string, slotName: string, sampler: Sampler | null = null): void { this._addComputeResource(name, AccessType.READ, slotName); if (sampler) { - const descriptorID = this._layoutGraph.attributeIndex.get(slotName)!; + const descriptorID = this._lg.attributeIndex.get(slotName)!; this._data.samplers.set(descriptorID, sampler); } } @@ -1196,64 +1595,66 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR addRenderSubpass (layoutName = ''): RenderSubpassBuilder { const name = 'Raster'; const subpassID = this._pass.subpassGraph.numVertices(); - this._pass.subpassGraph.addVertex(name, new Subpass()); - const subpass = new RasterSubpass(subpassID, 1, 0); - const data = new RenderData(); + this._pass.subpassGraph.addVertex(name, renderGraphPool.createSubpass()); + const subpass = renderGraphPool.createRasterSubpass(subpassID, 1, 0); + const data = renderGraphPool.createRenderData(); const vertID = this._renderGraph.addVertex(RenderGraphValue.RasterSubpass, subpass, name, layoutName, data, false); - const result = new WebRenderSubpassBuilder(data, this._renderGraph, this._layoutGraph, vertID, subpass, this._pipeline); + const result = pipelinePool.renderSubpassBuilder.add(); + result.update(data, this._renderGraph, this._lg, vertID, subpass, this._pipeline); return result; } addQueue (hint: QueueHint = QueueHint.RENDER_OPAQUE, layoutName = 'default'): WebRenderQueueBuilder { - const layoutId = this._layoutGraph.locateChild(this._layoutID, layoutName); + const layoutId = this._lg.locateChild(this._layoutID, layoutName); if (DEBUG) { assert(layoutId !== 0xFFFFFFFF); } - const queue = new RenderQueue(hint); - queue.phaseID = layoutId; - const data = new RenderData(); + const queue = renderGraphPool.createRenderQueue(hint, layoutId); + const data = renderGraphPool.createRenderData(); const queueID = this._renderGraph.addVertex(RenderGraphValue.Queue, queue, '', layoutName, data, false, this._vertID); - return new WebRenderQueueBuilder(data, this._renderGraph, this._layoutGraph, queueID, queue, this._pipeline); + const result = pipelinePool.renderQueueBuilder.add(); + result.update(data, this._renderGraph, this._lg, queueID, queue, this._pipeline); + return result; } addFullscreenQuad (material: Material, passID: number, sceneFlags = SceneFlags.NONE, name = 'FullscreenQuad'): void { - const queue = new RenderQueue(QueueHint.RENDER_TRANSPARENT); + const queue = renderGraphPool.createRenderQueue(QueueHint.RENDER_TRANSPARENT); const queueId = this._renderGraph.addVertex( RenderGraphValue.Queue, queue, 'Queue', '', - new RenderData(), + renderGraphPool.createRenderData(), false, this._vertID, ); this._renderGraph.addVertex( RenderGraphValue.Blit, - new Blit(material, passID, sceneFlags, null), + renderGraphPool.createBlit(material, passID, sceneFlags, null), name, '', - new RenderData(), + renderGraphPool.createRenderData(), false, queueId, ); } addCameraQuad (camera: Camera, material: Material, passID: number, sceneFlags: SceneFlags, name = 'CameraQuad'): void { - const queue = new RenderQueue(QueueHint.RENDER_TRANSPARENT); + const queue = renderGraphPool.createRenderQueue(QueueHint.RENDER_TRANSPARENT); const queueId = this._renderGraph.addVertex( RenderGraphValue.Queue, queue, 'Queue', '', - new RenderData(), + renderGraphPool.createRenderData(), false, this._vertID, ); this._renderGraph.addVertex( RenderGraphValue.Blit, - new Blit(material, passID, sceneFlags, camera), + renderGraphPool.createBlit(material, passID, sceneFlags, camera), name, '', - new RenderData(), + renderGraphPool.createRenderData(), false, queueId, ); @@ -1267,13 +1668,11 @@ export class WebRenderPassBuilder extends WebSetter implements BasicMultisampleR set showStatistics (enable: boolean) { this._pass.showStatistics = enable; } - private readonly _renderGraph: RenderGraph; - private readonly _vertID: number; - private readonly _layoutID: number; - private readonly _pass: RasterPass; - private readonly _pipeline: PipelineSceneData; - private readonly _layoutGraph: LayoutGraphData; - private readonly _resourceGraph: ResourceGraph; + private _renderGraph: RenderGraph; + private _layoutID: number; + private _pass: RasterPass; + private _pipeline: PipelineSceneData; + private _resourceGraph: ResourceGraph; } export class WebComputeQueueBuilder extends WebSetter implements ComputeQueueBuilder { @@ -1284,6 +1683,14 @@ export class WebComputeQueueBuilder extends WebSetter implements ComputeQueueBui this._queue = queue; this._pipeline = pipeline; } + update (data: RenderData, renderGraph: RenderGraph, layoutGraph: LayoutGraphData, vertID: number, queue: RenderQueue, pipeline: PipelineSceneData): void { + this._data = data; + this._lg = layoutGraph; + this._renderGraph = renderGraph; + this._vertID = vertID; + this._queue = queue; + this._pipeline = pipeline; + } setArrayBuffer (name: string, arrayBuffer: ArrayBuffer): void { throw new Error('Method not implemented.'); } @@ -1303,29 +1710,39 @@ export class WebComputeQueueBuilder extends WebSetter implements ComputeQueueBui ): void { this._renderGraph.addVertex( RenderGraphValue.Dispatch, - new Dispatch(material, passID, threadGroupCountX, threadGroupCountY, threadGroupCountZ), + renderGraphPool.createDispatch(material, passID, threadGroupCountX, threadGroupCountY, threadGroupCountZ), name, '', - new RenderData(), + renderGraphPool.createRenderData(), false, this._vertID, ); } - private readonly _renderGraph: RenderGraph; - private readonly _vertID: number; - private readonly _queue: RenderQueue; - private readonly _pipeline: PipelineSceneData; + private _renderGraph: RenderGraph; + private _queue: RenderQueue; + private _pipeline: PipelineSceneData; } export class WebComputePassBuilder extends WebSetter implements ComputePassBuilder { constructor (data: RenderData, renderGraph: RenderGraph, layoutGraph: LayoutGraphData, resourceGraph: ResourceGraph, vertID: number, pass: ComputePass, pipeline: PipelineSceneData) { super(data, layoutGraph); this._renderGraph = renderGraph; - this._layoutGraph = layoutGraph; + this._resourceGraph = resourceGraph; + this._vertID = vertID; + this._pass = pass; + this._pipeline = pipeline; + + const layoutName = this._renderGraph.component(RenderGraphComponent.Layout, this._vertID); + this._layoutID = layoutGraph.locateChild(layoutGraph.nullVertex(), layoutName); + } + update (data: RenderData, renderGraph: RenderGraph, layoutGraph: LayoutGraphData, resourceGraph: ResourceGraph, vertID: number, pass: ComputePass, pipeline: PipelineSceneData): void { + this._data = data; + this._renderGraph = renderGraph; + this._lg = layoutGraph; this._resourceGraph = resourceGraph; this._vertID = vertID; this._pass = pass; @@ -1359,25 +1776,26 @@ export class WebComputePassBuilder extends WebSetter implements ComputePassBuild throw new Error('Method not implemented.'); } addQueue (layoutName = 'default'): WebComputeQueueBuilder { - const layoutId = this._layoutGraph.locateChild(this._layoutID, layoutName); + const layoutId = this._lg.locateChild(this._layoutID, layoutName); if (DEBUG) { assert(layoutId !== 0xFFFFFFFF); } - const queue = new RenderQueue(); - queue.phaseID = layoutId; - const data = new RenderData(); + 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); - return new WebComputeQueueBuilder(data, this._renderGraph, this._layoutGraph, queueID, queue, this._pipeline); + const computeQueueBuilder = pipelinePool.computeQueueBuilder.add(); + computeQueueBuilder.update(data, this._renderGraph, this._lg, queueID, queue, this._pipeline); + return computeQueueBuilder; } private _addComputeResource (name: string, accessType: AccessType, slotName: string): void { - const view = new ComputeView(slotName); + 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._layoutGraph.attributeIndex.get(descriptorName); + const descriptorID = this._lg.attributeIndex.get(descriptorName); assert(descriptorID !== undefined); } if (this._pass.computeViews.has(name)) { @@ -1387,13 +1805,11 @@ export class WebComputePassBuilder extends WebSetter implements ComputePassBuild } } - private readonly _renderGraph: RenderGraph; - private readonly _layoutGraph: LayoutGraphData; - private readonly _resourceGraph: ResourceGraph; - private readonly _vertID: number; - private readonly _layoutID: number; - private readonly _pass: ComputePass; - private readonly _pipeline: PipelineSceneData; + private _renderGraph: RenderGraph; + private _resourceGraph: ResourceGraph; + private _layoutID: number; + private _pass: ComputePass; + private _pipeline: PipelineSceneData; } export class WebMovePassBuilder { @@ -1444,7 +1860,7 @@ export class WebCopyPassBuilder { function isManaged (residency: ResourceResidency): boolean { return residency === ResourceResidency.MANAGED - || residency === ResourceResidency.MEMORYLESS; + || residency === ResourceResidency.MEMORYLESS; } export class WebPipeline implements BasicPipeline { @@ -1464,6 +1880,7 @@ export class WebPipeline implements BasicPipeline { throw new Error('Method not implemented.'); } addRenderWindow (name: string, format: Format, width: number, height: number, renderWindow: RenderWindow): number { + // Objects need to be held for a long time, so there is no need to use pool management const desc = new ResourceDesc(); desc.dimension = ResourceDimension.TEXTURE2D; desc.width = width; @@ -1633,11 +2050,12 @@ export class WebPipeline implements BasicPipeline { public addComputePass (passName: string): ComputePassBuilder { const name = 'Compute'; - const pass = new ComputePass(); + const pass = renderGraphPool.createComputePass(); - const data = new RenderData(); + const data = renderGraphPool.createRenderData(); const vertID = this._renderGraph!.addVertex(RenderGraphValue.Compute, pass, name, passName, data, false); - const result = new WebComputePassBuilder(data, this._renderGraph!, this._layoutGraph, this._resourceGraph, vertID, pass, this._pipelineSceneData); + const result = pipelinePool.computePassBuilder.add(); + result.update(data, this._renderGraph!, this._layoutGraph, this._resourceGraph, vertID, pass, this._pipelineSceneData); setComputeConstants(result, passName); initGlobalDescBinding(data, passName); return result; @@ -1645,12 +2063,12 @@ export class WebPipeline implements BasicPipeline { public addUploadPass (uploadPairs: UploadPair[]): void { const name = 'UploadPass'; - const pass = new CopyPass(); + const pass = renderGraphPool.createCopyPass(); for (const up of uploadPairs) { pass.uploadPairs.push(up); } - const vertID = this._renderGraph!.addVertex(RenderGraphValue.Copy, pass, name, '', new RenderData(), false); + const vertID = this._renderGraph!.addVertex(RenderGraphValue.Copy, pass, name, '', renderGraphPool.createRenderData(), false); // const result = new WebCopyPassBuilder(this._renderGraph!, vertID, pass); } @@ -1673,7 +2091,7 @@ export class WebPipeline implements BasicPipeline { } const resDesc = this.resourceGraph.getDesc(tarVerId); const currRaster = this.addRenderPass(resDesc.width, resDesc.height, 'copy-pass'); - currRaster.addRenderTarget(targetName, LoadOp.CLEAR, StoreOp.STORE, new Color(0, 0, 0, 0)); + currRaster.addRenderTarget(targetName, LoadOp.CLEAR, StoreOp.STORE, pipelinePool.createColor()); currRaster.addTexture(pair.source, 'outputResultMap'); currRaster.addQueue(QueueHint.NONE).addFullscreenQuad(this._copyPassMat, 0, SceneFlags.NONE); } @@ -1740,13 +2158,14 @@ export class WebPipeline implements BasicPipeline { public activate (swapchain: Swapchain): boolean { this._device = deviceManager.gfxDevice; + pipelinePool = new PipelinePool(); + renderGraphPool = pipelinePool.renderGraphPool; createGfxDescriptorSetsAndPipelines(this._device, this._layoutGraph); this._globalDSManager = new GlobalDSManager(this._device); this._globalDescSetData = this.getGlobalDescriptorSetData()!; this._globalDescriptorSetLayout = this._globalDescSetData.descriptorSetLayout; this._globalDescriptorSetInfo = new DescriptorSetInfo(this._globalDescriptorSetLayout!); - this._globalDescriptorSet = isEnableEffect() ? this._device.createDescriptorSet(this._globalDescriptorSetInfo) - : this._globalDescSetData.descriptorSet; + this._globalDescriptorSet = this._device.createDescriptorSet(this._globalDescriptorSetInfo); this._globalDSManager.globalDescriptorSet = this.globalDescriptorSet; this._compileMaterial(); this.setMacroBool('CC_USE_HDR', this._pipelineSceneData.isHDR); @@ -1778,13 +2197,12 @@ export class WebPipeline implements BasicPipeline { this.setMacroInt('CC_DIR_LIGHT_SHADOW_TYPE', 0); // 0: CC_CASCADED_LAYERS_TRANSITION_OFF, 1: CC_CASCADED_LAYERS_TRANSITION_ON - this.setMacroBool('CC_CASCADED_LAYERS_TRANSITION', false); + this.setMacroBool('CC_CASCADED_LAYERS_TRANSITION', false); // enable the deferred pipeline if (this.usesDeferredPipeline) { this.setMacroInt('CC_PIPELINE_TYPE', 1); } - this._forward = new ForwardPipelineBuilder(); this._deferred = new DeferredPipelineBuilder(); this.builder = new CustomPipelineBuilder(); @@ -1898,6 +2316,7 @@ export class WebPipeline implements BasicPipeline { } beginSetup (): void { if (!this._renderGraph) this._renderGraph = new RenderGraph(); + pipelinePool.reset(); } endSetup (): void { this.compile(); @@ -2025,7 +2444,6 @@ export class WebPipeline implements BasicPipeline { // noop } endFrame (): void { - // this._renderGraph = null; this.renderGraph?.clear(); } @@ -2089,6 +2507,27 @@ export class WebPipeline implements BasicPipeline { this.execute(); this.endFrame(); } + addBuiltinReflectionProbePass (camera: Camera): void { + const reflectionProbeManager = cclegacy.internal.reflectionProbeManager as ReflectionProbeManager; + if (!reflectionProbeManager) return; + const probes = reflectionProbeManager.getProbes(); + if (probes.length === 0) return; + for (let i = 0; i < probes.length; i++) { + const probe = probes[i]; + if (probe.needRender) { + if (probes[i].probeType === ProbeType.PLANAR) { + buildReflectionProbePass(camera, this, probe, probe.realtimePlanarTexture!.window!, 0); + } else if (EDITOR) { + for (let faceIdx = 0; faceIdx < probe.bakedCubeTextures.length; faceIdx++) { + probe.updateCameraDir(faceIdx); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + buildReflectionProbePass(camera, this, probe, probe.bakedCubeTextures[faceIdx].window!, faceIdx); + } + probe.needRender = false; + } + } + } + } addRenderPassImpl (width: number, height: number, layoutName: string, count = 1, quality = 0): BasicMultisampleRenderPassBuilder { if (DEBUG) { const stageId = this.layoutGraph.locateChild(this.layoutGraph.nullVertex(), layoutName); @@ -2098,16 +2537,17 @@ export class WebPipeline implements BasicPipeline { assert(Boolean(layout.descriptorSets.get(UpdateFrequency.PER_PASS))); } const name = 'Raster'; - const pass = new RasterPass(); + const pass = renderGraphPool.createRasterPass(); pass.viewport.width = width; pass.viewport.height = height; pass.count = count; pass.quality = quality; - const data = new RenderData(); + const data = renderGraphPool.createRenderData(); const vertID = this._renderGraph!.addVertex(RenderGraphValue.RasterPass, pass, name, layoutName, data, false); - const result = new WebRenderPassBuilder(data, this._renderGraph!, this._layoutGraph, this._resourceGraph, vertID, pass, this._pipelineSceneData); - this._updateRasterPassConstants(result, width, height, isEnableEffect() ? layoutName : 'default'); + const result = pipelinePool.renderPassBuilder.add(); + result.update(data, this._renderGraph!, this._layoutGraph, this._resourceGraph, vertID, pass, this._pipelineSceneData); + this._updateRasterPassConstants(result, width, height, layoutName); initGlobalDescBinding(data, layoutName); return result; } diff --git a/cocos/rendering/custom/web-program-library.ts b/cocos/rendering/custom/web-program-library.ts index 0e8164f6b38..ba3416f56ef 100644 --- a/cocos/rendering/custom/web-program-library.ts +++ b/cocos/rendering/custom/web-program-library.ts @@ -25,7 +25,7 @@ /* eslint-disable max-len */ import { EffectAsset } from '../../asset/assets'; -import { Attribute, DescriptorSetLayout, DescriptorType, DESCRIPTOR_BUFFER_TYPE, DESCRIPTOR_SAMPLER_TYPE, Device, MemoryAccessBit, PipelineLayout, PipelineLayoutInfo, Shader, ShaderInfo, ShaderStage, ShaderStageFlagBit, Type, Uniform, UniformBlock, UniformInputAttachment, UniformSampler, UniformSamplerTexture, UniformStorageBuffer, UniformStorageImage, UniformTexture, deviceManager } from '../../gfx'; +import { Attribute, DescriptorSetLayout, DescriptorType, DESCRIPTOR_BUFFER_TYPE, DESCRIPTOR_SAMPLER_TYPE, Device, MemoryAccessBit, PipelineLayout, PipelineLayoutInfo, Shader, ShaderInfo, ShaderStage, ShaderStageFlagBit, Type, Uniform, UniformBlock, UniformInputAttachment, UniformSampler, UniformSamplerTexture, UniformStorageBuffer, UniformStorageImage, UniformTexture, deviceManager, PipelineState, DescriptorSetLayoutInfo, DescriptorSetInfo } from '../../gfx'; import { genHandles, getActiveAttributes, getCombinationDefines, getShaderInstanceName, getSize, getVariantKey, populateMacros, prepareDefines } from '../../render-scene/core/program-utils'; import { getDeviceShaderVersion, MacroRecord } from '../../render-scene'; import { IProgramInfo } from '../../render-scene/core/program-lib'; @@ -33,15 +33,15 @@ import { DescriptorBlockData, DescriptorData, DescriptorSetData, DescriptorSetLa import { ProgramLibrary, ProgramProxy } from './private'; import { DescriptorTypeOrder, UpdateFrequency } from './types'; import { ProgramGroup, ProgramInfo } from './web-types'; -import { getCustomPassID, getCustomPhaseID, getOrCreateDescriptorSetLayout, getEmptyDescriptorSetLayout, getEmptyPipelineLayout, initializeDescriptorSetLayoutInfo, makeDescriptorSetLayoutData, getDescriptorSetLayout, getOrCreateDescriptorID, getDescriptorTypeOrder, getProgramID, getDescriptorNameID, getDescriptorName, INVALID_ID, ENABLE_SUBPASS, getCustomSubpassID } from './layout-graph-utils'; -import { assert } from '../../core/platform/debug'; -import { IDescriptorSetLayoutInfo, localDescriptorSetLayout } from '../define'; +import { getCustomPassID, getCustomPhaseID, getOrCreateDescriptorSetLayout, getEmptyDescriptorSetLayout, getEmptyPipelineLayout, initializeDescriptorSetLayoutInfo, makeDescriptorSetLayoutData, getDescriptorSetLayout, getOrCreateDescriptorID, getDescriptorTypeOrder, getProgramID, getDescriptorNameID, getDescriptorName, INVALID_ID, ENABLE_SUBPASS, getCustomSubpassID, generateConstantMacros, populatePipelineLayoutInfo } from './layout-graph-utils'; +import { assert, error } from '../../core/platform/debug'; +import { IDescriptorSetLayoutInfo, UBOSkinning, localDescriptorSetLayout } from '../define'; import { PipelineRuntime } from './pipeline'; const _setIndex = [2, 1, 3, 0]; // make IProgramInfo from IShaderInfo -function makeProgramInfo (effectName: string, shader: EffectAsset.IShaderInfo): IProgramInfo { +export function makeProgramInfo (effectName: string, shader: EffectAsset.IShaderInfo): IProgramInfo { const programInfo = { ...shader } as IProgramInfo; programInfo.effectName = effectName; @@ -87,7 +87,7 @@ function findBinding (shaderInfo: ShaderInfo, name: string): { set: number, bind } } // eslint-disable-next-line no-console - throw console.error('binding not found in shaderInfo!'); + throw error('binding not found in shaderInfo!'); } function overwriteShaderSourceBinding (shaderInfo: ShaderInfo, source: string): string { @@ -138,7 +138,7 @@ function overwriteShaderProgramBinding (shaderInfo: ShaderInfo, programInfo: IPr } // overwrite IProgramInfo using gfx.ShaderInfo -function overwriteProgramBlockInfo (shaderInfo: ShaderInfo, programInfo: IProgramInfo): void { +export function overwriteProgramBlockInfo (shaderInfo: ShaderInfo, programInfo: IProgramInfo): void { overwriteShaderProgramBinding(shaderInfo, programInfo); const set = _setIndex[UpdateFrequency.PER_BATCH]; for (const block of programInfo.blocks) { @@ -154,7 +154,7 @@ function overwriteProgramBlockInfo (shaderInfo: ShaderInfo, programInfo: IProgra } } if (!found) { - console.error(`Block ${block.name} not found in shader ${shaderInfo.name}`); + error(`Block ${block.name} not found in shader ${shaderInfo.name}`); } } } @@ -270,7 +270,7 @@ function populateMergedShaderInfo ( for (const block of descriptorBlock.descriptors) { const uniformBlock = layout.uniformBlocks.get(block.descriptorID); if (uniformBlock === undefined) { - console.error(`Failed to find uniform block ${block.descriptorID} in layout`); + error(`Failed to find uniform block ${block.descriptorID} in layout`); continue; } blockSizes.push(getSize(uniformBlock.members)); @@ -286,7 +286,7 @@ function populateMergedShaderInfo ( ++binding; } if (binding !== descriptorBlock.offset + descriptorBlock.capacity) { - console.error(`Uniform buffer binding mismatch for set ${set}`); + error(`Uniform buffer binding mismatch for set ${set}`); } break; case DescriptorTypeOrder.DYNAMIC_UNIFORM_BUFFER: @@ -541,7 +541,7 @@ function calculateFlattenedBinding ( } // make gfx.ShaderInfo -function makeShaderInfo ( +export function makeShaderInfo ( lg: LayoutGraphData, passLayouts: PipelineLayoutData, phaseLayouts: PipelineLayoutData, @@ -650,14 +650,23 @@ function makeShaderInfo ( return [shaderInfo, blockSizes]; } -class WebProgramProxy implements ProgramProxy { - constructor (shader: Shader) { +export function getGLSLSource (info: IProgramInfo, version: string): { vert: string, frag: string, compute?: string } { + if (version === 'glsl1') return info.glsl1; + if (version === 'glsl3') return info.glsl3; + if (version === 'glsl4') return info.glsl4; + return { vert: '', frag: '' }; +} + +export class WebProgramProxy implements ProgramProxy { + constructor (shader: Shader, pipelineStateIn: PipelineState | null = null) { this.shader = shader; + this.pipelineState = pipelineStateIn; } get name (): string { return this.shader.name; } readonly shader: Shader; + pipelineState: PipelineState | null = null; } // find name and type from local descriptor set info @@ -675,12 +684,12 @@ function getDescriptorNameAndType (source: IDescriptorSetLayoutInfo, binding: nu return [v.name, type]; } } - console.error('descriptor not found'); + error('descriptor not found'); return ['', Type.UNKNOWN]; } // make DescriptorSetLayoutData from local descriptor set info -function makeLocalDescriptorSetLayoutData ( +export function makeLocalDescriptorSetLayoutData ( lg: LayoutGraphData, source: IDescriptorSetLayoutInfo, ): DescriptorSetLayoutData { @@ -695,7 +704,7 @@ function makeLocalDescriptorSetLayoutData ( data.descriptorBlocks.push(block); const binding = data.bindingMap.get(nameID); if (binding !== undefined) { - console.error(`duplicate descriptor name '${name}'`); + error(`duplicate descriptor name '${name}'`); } data.bindingMap.set(nameID, b.binding); const v = source.layouts[name]; @@ -707,7 +716,7 @@ function makeLocalDescriptorSetLayoutData ( } // make descriptor sets for ShaderProgramData (PerBatch, PerInstance) -function buildProgramData ( +export function buildProgramData ( programName: string, srcShaderInfo: EffectAsset.IShaderInfo, lg: LayoutGraphData, @@ -737,7 +746,7 @@ function buildProgramData ( setData.descriptorSetLayoutInfo, ); if (localDescriptorSetLayout.bindings.length !== setData.descriptorSetLayoutInfo.bindings.length) { - console.error('local descriptor set layout inconsistent'); + error('local descriptor set layout inconsistent'); } else { for (let k = 0; k !== localDescriptorSetLayout.bindings.length; ++k) { const b = localDescriptorSetLayout.bindings[k]; @@ -746,7 +755,7 @@ function buildProgramData ( || b.descriptorType !== b2.descriptorType || b.count !== b2.count || b.stageFlags !== b2.stageFlags) { - console.error('local descriptor set layout inconsistent'); + error('local descriptor set layout inconsistent'); } } } @@ -771,7 +780,7 @@ function buildProgramData ( } // get or create PerProgram gfx.DescriptorSetLayout -function getOrCreateProgramDescriptorSetLayout ( +export function getOrCreateProgramDescriptorSetLayout ( device: Device, lg: LayoutGraphData, phaseID: number, @@ -798,7 +807,7 @@ function getOrCreateProgramDescriptorSetLayout ( } // get PerProgram gfx.DescriptorSetLayout -function getProgramDescriptorSetLayout ( +export function getProgramDescriptorSetLayout ( device: Device, lg: LayoutGraphData, phaseID: number, @@ -825,7 +834,7 @@ function getProgramDescriptorSetLayout ( } // find shader program in LayoutGraphData -function getEffectShader ( +export function getEffectShader ( lg: LayoutGraphData, effect: EffectAsset, pass: EffectAsset.IPassInfo, @@ -833,20 +842,20 @@ function getEffectShader ( const programName = pass.program; const passID = getCustomPassID(lg, pass.pass); if (passID === INVALID_ID) { - console.error(`Invalid render pass, program: ${programName}`); + error(`Invalid render pass, program: ${programName}`); return [INVALID_ID, INVALID_ID, INVALID_ID, null, INVALID_ID]; } const enableSubpass = pass.subpass && pass.subpass !== '' && ENABLE_SUBPASS; const subpassID = enableSubpass ? getCustomSubpassID(lg, passID, pass.subpass!) : INVALID_ID; if (enableSubpass && subpassID === INVALID_ID) { - console.error(`Invalid render subpass, program: ${programName}`); + error(`Invalid render subpass, program: ${programName}`); return [INVALID_ID, INVALID_ID, INVALID_ID, null, INVALID_ID]; } const phaseID = getCustomPhaseID(lg, subpassID === INVALID_ID ? passID : subpassID, pass.phase); if (phaseID === INVALID_ID) { - console.error(`Invalid render phase, program: ${programName}`); + error(`Invalid render phase, program: ${programName}`); return [INVALID_ID, INVALID_ID, INVALID_ID, null, INVALID_ID]; } let srcShaderInfo: EffectAsset.IShaderInfo | null = null; @@ -863,10 +872,10 @@ function getEffectShader ( } // valid IShaderInfo is compatible -function validateShaderInfo (srcShaderInfo: EffectAsset.IShaderInfo): number { +export function validateShaderInfo (srcShaderInfo: EffectAsset.IShaderInfo): number { // source shader info if (srcShaderInfo.descriptors === undefined) { - console.error(`No descriptors in shader: ${srcShaderInfo.name}, please reimport ALL effects`); + error(`No descriptors in shader: ${srcShaderInfo.name}, please reimport ALL effects`); return 1; } return 0; @@ -881,6 +890,75 @@ export class WebProgramLibrary implements ProgramLibrary { } } } + init (deviceIn: Device): void { + if (this.device === deviceIn) { + return; + } + this.device = deviceIn; + this.emptyDescriptorSetLayout = this.device.createDescriptorSetLayout(new DescriptorSetLayoutInfo()); + this.emptyPipelineLayout = this.device.createPipelineLayout(new PipelineLayoutInfo()); + + // update ubo + // tips: for compatibility with old version, when maxVertexUniformVectors is 128, maxJoints = 30 + let maxJoints: number = (this.device.capabilities.maxVertexUniformVectors - 38) / 3; + maxJoints = maxJoints < 256 ? maxJoints : 256; + UBOSkinning.initLayout(maxJoints); + + // init layout graph + const lg = this.layoutGraph; + for (const v of lg.vertices()) { + const layout: PipelineLayoutData = lg.get('Layout').get(v) as PipelineLayoutData; + for (const [update, set] of layout.descriptorSets) { + initializeDescriptorSetLayoutInfo(set.descriptorSetLayoutData, set.descriptorSetLayoutInfo); + set.descriptorSetLayout = this.device.createDescriptorSetLayout(set.descriptorSetLayoutInfo); + assert(!!set.descriptorSetLayout); + set.descriptorSet = this.device.createDescriptorSet(new DescriptorSetInfo(set.descriptorSetLayout)); + assert(!!set.descriptorSet); + } + } + + for (const v of lg.vertices()) { + if (!lg.holds(LayoutGraphDataValue.RenderPhase, v)) { + continue; + } + const phaseID = v; + const subpassOrPassID = lg.getParent(phaseID); + const passLayout = lg.getLayout(subpassOrPassID); + const phaseLayout = lg.getLayout(phaseID); + const info = new PipelineLayoutInfo(); + populatePipelineLayoutInfo(passLayout, UpdateFrequency.PER_PASS, info); + populatePipelineLayoutInfo(phaseLayout, UpdateFrequency.PER_PHASE, info); + populatePipelineLayoutInfo(phaseLayout, UpdateFrequency.PER_BATCH, info); + populatePipelineLayoutInfo(phaseLayout, UpdateFrequency.PER_INSTANCE, info); + const phase = lg.getRenderPhase(phaseID); + phase.pipelineLayout = this.device.createPipelineLayout(info); + } + + // init local descriptor set + { + const localSetLayout = localDescriptorSetLayout; + this.localLayoutData = makeLocalDescriptorSetLayoutData(lg, localSetLayout); + const info = new DescriptorSetLayoutInfo(); + initializeDescriptorSetLayoutInfo(this.localLayoutData, info); + this.localDescriptorSetLayout = this.device.createDescriptorSetLayout(info); + assert(!!this.localDescriptorSetLayout); + + let numUniformBuffers = 0; + for (const block of this.localLayoutData.descriptorBlocks) { + if (block.type !== DescriptorTypeOrder.UNIFORM_BUFFER + && block.type !== DescriptorTypeOrder.DYNAMIC_UNIFORM_BUFFER) { + continue; + } + for (const d of block.descriptors) { + numUniformBuffers += d.count; + } + } + assert(numUniformBuffers === 7); // 7 is currently max uniform binding + } + + // generate constant macros string + generateConstantMacros(this.device, this.layoutGraph.constantMacros); + } // add effect to database addEffect (effect: EffectAsset): void { const lg = this.layoutGraph; @@ -889,7 +967,7 @@ export class WebProgramLibrary implements ProgramLibrary { const programName = pass.program; const [passID, subpassID, phaseID, srcShaderInfo] = getEffectShader(lg, effect, pass); if (srcShaderInfo === null || validateShaderInfo(srcShaderInfo)) { - console.error(`program: ${programName} not found`); + error(`program: ${programName} not found`); continue; } assert(passID !== INVALID_ID && phaseID !== INVALID_ID); @@ -950,7 +1028,7 @@ export class WebProgramLibrary implements ProgramLibrary { const programName = pass.program; const [passID, subpassID, phaseID, srcShaderInfo, shaderID] = getEffectShader(lg, effect, pass); if (srcShaderInfo === null || validateShaderInfo(srcShaderInfo)) { - console.error(`program: ${programName} not valid`); + error(`program: ${programName} not valid`); continue; } assert(passID !== INVALID_ID && phaseID !== INVALID_ID && shaderID !== INVALID_ID); @@ -986,13 +1064,13 @@ export class WebProgramLibrary implements ProgramLibrary { // get phase const group = this.phases.get(phaseID); if (group === undefined) { - console.error(`Invalid render phase, program: ${programName}`); + error(`Invalid render phase, program: ${programName}`); return ''; } // get info const info = group.programInfos.get(programName); if (info === undefined) { - console.error(`Invalid program, program: ${programName}`); + error(`Invalid program, program: ${programName}`); return ''; } return getVariantKey(info.programInfo, defines); @@ -1004,13 +1082,13 @@ export class WebProgramLibrary implements ProgramLibrary { // get phase const group = this.phases.get(phaseID); if (group === undefined) { - console.error(`Invalid render phase, program: ${name}`); + error(`Invalid render phase, program: ${name}`); return null; } // get info const info = group.programInfos.get(name); if (info === undefined) { - console.error(`Invalid program, program: ${name}`); + error(`Invalid program, program: ${name}`); return null; } const programInfo = info.programInfo; @@ -1035,7 +1113,7 @@ export class WebProgramLibrary implements ProgramLibrary { if (deviceShaderVersion) { src = programInfo[deviceShaderVersion]; } else { - console.error('Invalid GFX API!'); + error('Invalid GFX API!'); } // prepare shader info @@ -1096,12 +1174,12 @@ export class WebProgramLibrary implements ProgramLibrary { assert(phaseID !== INVALID_ID); const group = this.phases.get(phaseID); if (!group) { - console.error(`Invalid render phase, program: ${programName}`); + error(`Invalid render phase, program: ${programName}`); return []; } const info = group.programInfos.get(programName); if (!info) { - console.error(`Invalid program, program: ${programName}`); + error(`Invalid program, program: ${programName}`); return []; } return info.blockSizes; @@ -1111,12 +1189,12 @@ export class WebProgramLibrary implements ProgramLibrary { assert(phaseID !== INVALID_ID); const group = this.phases.get(phaseID); if (!group) { - console.error(`Invalid render phase, program: ${programName}`); + error(`Invalid render phase, program: ${programName}`); return {}; } const info = group.programInfos.get(programName); if (!info) { - console.error(`Invalid program, program: ${programName}`); + error(`Invalid program, program: ${programName}`); return {}; } return info.handleMap; @@ -1177,7 +1255,12 @@ export class WebProgramLibrary implements ProgramLibrary { } readonly layoutGraph: LayoutGraphData; readonly phases: Map = new Map(); - mergeHighFrequency = false; - fixedLocal = true; - pipeline: PipelineRuntime | null = null; + public mergeHighFrequency: boolean = false; + public fixedLocal: boolean = true; + public localLayoutData: DescriptorSetLayoutData = new DescriptorSetLayoutData(); + public localDescriptorSetLayout: DescriptorSetLayout | null = null; + public emptyDescriptorSetLayout: DescriptorSetLayout | null = null; + public emptyPipelineLayout: PipelineLayout | null = null; + public pipeline: PipelineRuntime | null = null; + public device: Device | null = null; } diff --git a/cocos/rendering/custom/web-scene.ts b/cocos/rendering/custom/web-scene.ts index ea0272151da..168d3c0d556 100644 --- a/cocos/rendering/custom/web-scene.ts +++ b/cocos/rendering/custom/web-scene.ts @@ -136,13 +136,13 @@ export class WebSceneTask implements SceneTask { } public start (): void { - this._sceneCulling(); + // do nothing } public join (): void { // for web-pipeline, do nothing } public submit (): void { - + // do nothing } get camera (): scene.Camera | null { return this._camera; } get renderScene (): RenderScene | null { diff --git a/cocos/rendering/forward/forward-stage.ts b/cocos/rendering/forward/forward-stage.ts index 3a486735198..86a6f2dde06 100644 --- a/cocos/rendering/forward/forward-stage.ts +++ b/cocos/rendering/forward/forward-stage.ts @@ -122,6 +122,7 @@ export class ForwardStage extends RenderStage { } public destroy (): void { + // do nothing } public render (camera: Camera): void { @@ -179,8 +180,14 @@ export class ForwardStage extends RenderStage { const framebuffer = camera.window.framebuffer; const renderPass = pipeline.getRenderPass(camera.clearFlag & this._clearFlag, framebuffer); - cmdBuff.beginRenderPass(renderPass, framebuffer, this._renderArea, - colors, camera.clearDepth, camera.clearStencil); + cmdBuff.beginRenderPass( + renderPass, + framebuffer, + this._renderArea, + colors, + camera.clearDepth, + camera.clearStencil, + ); cmdBuff.bindDescriptorSet(SetIndex.GLOBAL, pipeline.descriptorSet); this._renderQueues[0].recordCommandBuffer(device, renderPass, cmdBuff); diff --git a/cocos/rendering/post-process/passes/base-pass.ts b/cocos/rendering/post-process/passes/base-pass.ts index 54fecf11729..8260e67f517 100644 --- a/cocos/rendering/post-process/passes/base-pass.ts +++ b/cocos/rendering/post-process/passes/base-pass.ts @@ -41,14 +41,14 @@ export function disablePostProcessForDebugView (): boolean { return debugView.singleMode as number > 0; } -export function getShadowMapSampler (): Sampler | null { +export function getShadowMapSampler (): Sampler | undefined { if (!_pointSampler) { const director = cclegacy.director; const pipeline = director.root.pipeline; const device = pipeline.device; _pointSampler = device.getSampler(_samplerPointInfo); } - return _pointSampler; + return _pointSampler || undefined; } export abstract class BasePass { diff --git a/cocos/rendering/post-process/passes/forward-final-pass.ts b/cocos/rendering/post-process/passes/forward-final-pass.ts index 1ae8bdead67..8452c212963 100644 --- a/cocos/rendering/post-process/passes/forward-final-pass.ts +++ b/cocos/rendering/post-process/passes/forward-final-pass.ts @@ -1,6 +1,6 @@ import { Vec4 } from '../../../core'; import { ClearFlagBit, Format } from '../../../gfx'; -import { Camera } from '../../../render-scene/scene'; +import { Camera, SKYBOX_FLAG } from '../../../render-scene/scene'; import { getCameraUniqueID } from '../../custom/define'; import { Pipeline } from '../../custom/pipeline'; import { passContext } from '../utils/pass-context'; @@ -17,7 +17,7 @@ export class ForwardFinalPass extends BasePass { return; } - passContext.clearFlag = camera.clearFlag & ClearFlagBit.COLOR; + passContext.clearFlag = camera.clearFlag & ClearFlagBit.COLOR | (camera.clearFlag & SKYBOX_FLAG); Vec4.set(passContext.clearColor, camera.clearColor.x, camera.clearColor.y, camera.clearColor.z, camera.clearColor.w); passContext.material = this.material; diff --git a/cocos/rendering/post-process/passes/forward-pass.ts b/cocos/rendering/post-process/passes/forward-pass.ts index 4fa9f366640..c16d0b493c7 100644 --- a/cocos/rendering/post-process/passes/forward-pass.ts +++ b/cocos/rendering/post-process/passes/forward-pass.ts @@ -1,7 +1,7 @@ import { Vec4 } from '../../../core'; import { ClearFlagBit, Format } from '../../../gfx'; -import { Camera, ShadowType } from '../../../render-scene/scene'; +import { Camera, ShadowType, SKYBOX_FLAG } from '../../../render-scene/scene'; import { LightInfo, QueueHint, SceneFlags } from '../../custom/types'; import { getCameraUniqueID } from '../../custom/define'; import { Pipeline } from '../../custom/pipeline'; @@ -38,7 +38,7 @@ export class ForwardPass extends BasePass { } public render (camera: Camera, ppl: Pipeline): void { - passContext.clearFlag = ClearFlagBit.COLOR | (camera.clearFlag & ClearFlagBit.DEPTH_STENCIL); + passContext.clearFlag = ClearFlagBit.COLOR | (camera.clearFlag & ClearFlagBit.DEPTH_STENCIL) | (camera.clearFlag & SKYBOX_FLAG); Vec4.set(passContext.clearColor, 0, 0, 0, 0); Vec4.set(passContext.clearDepthColor, camera.clearDepth, camera.clearStencil, 0, 0); @@ -70,13 +70,15 @@ export class ForwardPass extends BasePass { } } } - pass.addQueue(QueueHint.RENDER_OPAQUE) - .addSceneOfCamera( - camera, - new LightInfo(), - SceneFlags.OPAQUE_OBJECT | SceneFlags.CUTOUT_OBJECT - | SceneFlags.DEFAULT_LIGHTING | SceneFlags.GEOMETRY, - ); + const forwardQueue = pass.addQueue(QueueHint.RENDER_OPAQUE); + forwardQueue.addSceneOfCamera( + camera, + new LightInfo(), + SceneFlags.OPAQUE_OBJECT | SceneFlags.CUTOUT_OBJECT + | SceneFlags.GEOMETRY, + ); + const forwardAddQueue = pass.addQueue(QueueHint.RENDER_TRANSPARENT, 'forward-add'); + passContext.addSceneLights(forwardAddQueue, camera); const shadowInfo = ppl.pipelineSceneData.shadows; if (camera.scene?.mainLight && shadowInfo.enabled && shadowInfo.type === ShadowType.Planar) { pass.addQueue(QueueHint.RENDER_TRANSPARENT, 'planar-shadow') @@ -84,7 +86,7 @@ export class ForwardPass extends BasePass { camera, new LightInfo(camera.scene?.mainLight), SceneFlags.TRANSPARENT_OBJECT | SceneFlags.SHADOW_CASTER - | SceneFlags.DEFAULT_LIGHTING | SceneFlags.GEOMETRY, + | SceneFlags.GEOMETRY, ); } passContext.forwardPass = this; diff --git a/cocos/rendering/post-process/post-process-builder.ts b/cocos/rendering/post-process/post-process-builder.ts index 1087b1f82d2..61823854cb0 100644 --- a/cocos/rendering/post-process/post-process-builder.ts +++ b/cocos/rendering/post-process/post-process-builder.ts @@ -186,7 +186,7 @@ export class PostProcessBuilder implements PipelineBuilder { this.applyPreviewCamera(camera); } - buildReflectionProbePasss(camera, ppl); + ppl.addBuiltinReflectionProbePass(camera); passContext.postProcess = camera.postProcess || globalPP; diff --git a/cocos/rendering/post-process/utils/pass-context.ts b/cocos/rendering/post-process/utils/pass-context.ts index a8d783e92b2..e6c2a755e1b 100644 --- a/cocos/rendering/post-process/utils/pass-context.ts +++ b/cocos/rendering/post-process/utils/pass-context.ts @@ -1,14 +1,17 @@ import { EDITOR } from 'internal:constants'; -import { QueueHint, ResourceResidency, SceneFlags } from '../../custom/types'; +import { LightInfo, QueueHint, ResourceResidency, SceneFlags } from '../../custom/types'; import { ClearFlagBit, Color, Format, LoadOp, Rect, StoreOp, Viewport } from '../../../gfx'; -import { Pipeline, RenderPassBuilder } from '../../custom/pipeline'; -import { Camera } from '../../../render-scene/scene'; +import { Pipeline, RenderPassBuilder, RenderQueueBuilder } from '../../custom/pipeline'; +import { Camera, SKYBOX_FLAG } from '../../../render-scene/scene'; import { Material } from '../../../asset/assets'; import { PostProcess } from '../components'; import { getRenderArea } from '../../custom/define'; -import { Vec4 } from '../../../core'; +import { Vec4, geometry } from '../../../core'; +const sphere = geometry.Sphere.create(0, 0, 0, 1); +const boundingBox = new geometry.AABB(); +const rangedDirLightBoundingBox = new geometry.AABB(0.0, 0.0, 0.0, 0.5, 0.5, 0.5); export class PassContext { clearFlag: ClearFlagBit = ClearFlagBit.COLOR; clearColor = new Color(); @@ -81,6 +84,50 @@ export class PassContext { return this; } + addSceneLights (queue: RenderQueueBuilder, camera: Camera, flags: SceneFlags = SceneFlags.BLEND): void { + const scene = camera.scene!; + for (let i = 0; i < scene.spotLights.length; i++) { + const light = scene.spotLights[i]; + if (light.baked) { + continue; + } + geometry.Sphere.set(sphere, light.position.x, light.position.y, light.position.z, light.range); + if (geometry.intersect.sphereFrustum(sphere, camera.frustum)) { + queue.addSceneOfCamera(camera, new LightInfo(light), flags); + } + } + // sphere lights + for (let i = 0; i < scene.sphereLights.length; i++) { + const light = scene.sphereLights[i]; + if (light.baked) { + continue; + } + geometry.Sphere.set(sphere, light.position.x, light.position.y, light.position.z, light.range); + if (geometry.intersect.sphereFrustum(sphere, camera.frustum)) { + queue.addSceneOfCamera(camera, new LightInfo(light), flags); + } + } + // point lights + for (let i = 0; i < scene.pointLights.length; i++) { + const light = scene.pointLights[i]; + if (light.baked) { + continue; + } + geometry.Sphere.set(sphere, light.position.x, light.position.y, light.position.z, light.range); + if (geometry.intersect.sphereFrustum(sphere, camera.frustum)) { + queue.addSceneOfCamera(camera, new LightInfo(light), flags); + } + } + // ranged dir lights + for (let i = 0; i < scene.rangedDirLights.length; i++) { + const light = scene.rangedDirLights[i]; + geometry.AABB.transform(boundingBox, rangedDirLightBoundingBox, light.node!.getWorldMatrix()); + if (geometry.intersect.aabbFrustum(boundingBox, camera.frustum)) { + queue.addSceneOfCamera(camera, new LightInfo(light), flags); + } + } + } + updateViewPort (): void { const camera = this.camera; if (!camera) { @@ -111,7 +158,7 @@ export class PassContext { // return this; // } - addRasterView (name: string, format: Format, offscreen = true, residency?: ResourceResidency): PassContext { + addRasterView (name: string, format: Format, offscreen = true, residency: ResourceResidency = ResourceResidency.MANAGED): PassContext { const ppl = this.ppl; const camera = this.camera; const pass = this.pass; @@ -154,10 +201,11 @@ export class PassContext { const clearFlag = this.clearFlag & ClearFlagBit.COLOR; let loadOp = LoadOp.CLEAR; - if (clearFlag === ClearFlagBit.NONE) { + if (clearFlag === ClearFlagBit.NONE && !(this.clearFlag & SKYBOX_FLAG)) { loadOp = LoadOp.LOAD; + } else if (this.clearFlag & SKYBOX_FLAG) { + clearColor.set(0, 0, 0, 1); } - pass.addRenderTarget(name, loadOp, StoreOp.STORE, clearColor); } return this; diff --git a/cocos/rendering/render-additive-light-queue.ts b/cocos/rendering/render-additive-light-queue.ts index 0b6534f7ccc..f24a66eb08a 100644 --- a/cocos/rendering/render-additive-light-queue.ts +++ b/cocos/rendering/render-additive-light-queue.ts @@ -183,7 +183,7 @@ export class RenderAdditiveLightQueue { } } - private _bindForwardAddLight(validPunctualLights: Light[], passLayout = 'default'): void { + private _bindForwardAddLight (validPunctualLights: Light[], passLayout = 'default'): void { const renderObjects = this._pipeline.pipelineSceneData.renderObjects; for (let i = 0; i < renderObjects.length; i++) { const ro = renderObjects[i]; diff --git a/cocos/rendering/scene-culling.ts b/cocos/rendering/scene-culling.ts index ca507ed247e..64173902a93 100644 --- a/cocos/rendering/scene-culling.ts +++ b/cocos/rendering/scene-culling.ts @@ -41,7 +41,7 @@ const roPool = new Pool((): IRenderObject => ({ model: null!, dep function getRenderObject (model: Model, camera: Camera): IRenderObject { let depth = 0; if (model.node) { - Vec3.subtract(_tempVec3, model.node.worldPosition, camera.position); + Vec3.subtract(_tempVec3, model.worldBounds ? model.worldBounds.center : model.node.worldPosition, camera.position); depth = Vec3.dot(_tempVec3, camera.forward); } const ro = roPool.alloc(); diff --git a/native/cocos/renderer/pipeline/SceneCulling.cpp b/native/cocos/renderer/pipeline/SceneCulling.cpp index 46babac15dc..d94ad2cb40e 100644 --- a/native/cocos/renderer/pipeline/SceneCulling.cpp +++ b/native/cocos/renderer/pipeline/SceneCulling.cpp @@ -57,7 +57,8 @@ RenderObject genRenderObject(const scene::Model *model, const scene::Camera *cam if (model->getNode()) { const auto *node = model->getTransform(); cc::Vec3 position; - cc::Vec3::subtract(node->getWorldPosition(), camera->getPosition(), &position); + const auto *bounds = model->getWorldBounds(); + Vec3::subtract(bounds ? bounds->center : node->getWorldPosition(), camera->getPosition(), &position); depth = position.dot(camera->getForward()); } diff --git a/native/cocos/renderer/pipeline/custom/FrameGraphDispatcher.cpp b/native/cocos/renderer/pipeline/custom/FrameGraphDispatcher.cpp index 040539105b7..4969e137e2e 100644 --- a/native/cocos/renderer/pipeline/custom/FrameGraphDispatcher.cpp +++ b/native/cocos/renderer/pipeline/custom/FrameGraphDispatcher.cpp @@ -312,12 +312,12 @@ RenderingInfo FrameGraphDispatcher::getRenderPassAndFrameBuffer(RenderGraph::ver CC_EXPECTS(tex.texture); fbInfo.colorTextures.emplace_back(tex.texture); }, - [&](const IntrusivePtr &res) { + [&](const PersistentBuffer &res) { std::ignore = res; CC_EXPECTS(false); }, - [&](const IntrusivePtr &tex) { - fbInfo.colorTextures.emplace_back(tex); + [&](const PersistentTexture &tex) { + fbInfo.colorTextures.emplace_back(tex.texture); }, [&](const IntrusivePtr &fb) { CC_EXPECTS(fb->getColorTextures().size() == 1); @@ -1566,7 +1566,7 @@ gfx::SamplerInfo makePointSamplerInfo() { return gfx::SamplerInfo{gfx::Filter::POINT, gfx::Filter::POINT, gfx::Filter::POINT}; } -SubresourceView makeSubresourceView(const ResourceDesc &srcDesc, const ResourceDesc &targetDesc, const gfx::ResourceRange &range) { +SubresourceView makeSubresourceView(const ResourceDesc &targetDesc, const gfx::ResourceRange& range) { SubresourceView view{}; view.firstArraySlice = range.firstSlice; view.numArraySlices = range.numSlices; @@ -1575,7 +1575,6 @@ SubresourceView makeSubresourceView(const ResourceDesc &srcDesc, const ResourceD view.firstPlane = range.basePlane; view.numPlanes = range.planeCount; view.format = targetDesc.format; - view.viewType = srcDesc.viewType; view.textureView = nullptr; return view; } @@ -1692,7 +1691,6 @@ void subresourceAnalysis(ResourceAccessGraph &rag, ResourceGraph &resg) { auto descResViewID = findVertex(subres, resg); auto targetResID = rag.resourceIndex.at(resName); - const auto &srcDesc = get(ResourceGraph::DescTag{}, resg, descResViewID); const auto &targetName = get(ResourceGraph::NameTag{}, resg, targetResID); const auto &targetDesc = get(ResourceGraph::DescTag{}, resg, targetResID); const auto &srcResourceRange = rag.movedSourceStatus.at(subres).range; @@ -1700,7 +1698,7 @@ void subresourceAnalysis(ResourceAccessGraph &rag, ResourceGraph &resg) { const auto &indexName = concatResName(targetName, subres, rag.resource()); auto subresID = findVertex(indexName, resg); if (subresID == ResourceGraph::null_vertex()) { - const auto &subView = makeSubresourceView(srcDesc, targetDesc, srcResourceRange); + const auto &subView = makeSubresourceView(targetDesc, srcResourceRange); // register to resourcegraph subresID = addVertex( SubresourceViewTag{}, diff --git a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp index ad997864dcd..b880d430cd8 100644 --- a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp @@ -26,14 +26,18 @@ #include "cocos/application/ApplicationManager.h" #include "cocos/renderer/gfx-base/GFXDef-common.h" #include "cocos/renderer/gfx-base/GFXDevice.h" +#include "cocos/renderer/pipeline/Define.h" #include "cocos/renderer/pipeline/PipelineSceneData.h" #include "cocos/renderer/pipeline/custom/LayoutGraphTypes.h" +#include "cocos/renderer/pipeline/custom/NativePipelineTypes.h" #include "cocos/renderer/pipeline/custom/NativeTypes.h" #include "cocos/renderer/pipeline/custom/NativeUtils.h" #include "cocos/renderer/pipeline/custom/RenderGraphTypes.h" #include "cocos/renderer/pipeline/custom/details/GslUtils.h" #include "cocos/scene/Camera.h" +#include "cocos/scene/DirectionalLight.h" #include "cocos/scene/Fog.h" +#include "cocos/scene/Shadow.h" #include "cocos/scene/Skybox.h" #include "cocos/scene/SpotLight.h" @@ -254,12 +258,12 @@ float getPCFRadius( void setShadowUBOView( gfx::Device &device, const LayoutGraphData &layoutGraph, - const pipeline::PipelineSceneData &sceneData, + const pipeline::PipelineSceneData &pplSceneData, const scene::DirectionalLight &mainLight, RenderData &data) { - const auto &shadowInfo = *sceneData.getShadows(); - const auto &csmLayers = *sceneData.getCSMLayers(); - const auto &csmSupported = sceneData.getCSMSupported(); + const auto &shadowInfo = *pplSceneData.getShadows(); + const auto &csmLayers = *pplSceneData.getCSMLayers(); + const auto &csmSupported = pplSceneData.getCSMSupported(); const auto &packing = pipeline::supportsR32FloatTexture(&device) ? 0.0F : 1.0F; Vec4 vec4ShadowInfo{}; if (shadowInfo.isEnabled()) { @@ -373,12 +377,12 @@ void setShadowUBOView( void setShadowUBOLightView( gfx::Device *device, const LayoutGraphData &layoutGraph, - const pipeline::PipelineSceneData &sceneData, + const pipeline::PipelineSceneData &pplSceneData, + const BuiltinCascadedShadowMap *csm, const scene::Light &light, uint32_t level, RenderData &data) { - const auto &shadowInfo = *sceneData.getShadows(); - const auto &csmLayers = *sceneData.getCSMLayers(); + const auto &shadowInfo = *pplSceneData.getShadows(); const auto &packing = pipeline::supportsR32FloatTexture(device) ? 0.0F : 1.0F; const auto &cap = device->getCapabilities(); Vec4 vec4ShadowInfo{}; @@ -395,29 +399,31 @@ void setShadowUBOLightView( Mat4 matShadowProj; Mat4 matShadowViewProj; scene::CSMLevel levelCount{}; + CC_EXPECTS(csm); if (mainLight.isShadowFixedArea() || mainLight.getCSMLevel() == scene::CSMLevel::LEVEL_1) { - matShadowView = csmLayers.getSpecialLayer()->getMatShadowView(); - matShadowProj = csmLayers.getSpecialLayer()->getMatShadowProj(); - matShadowViewProj = csmLayers.getSpecialLayer()->getMatShadowViewProj(); + matShadowView = csm->specialLayer.shadowView; + matShadowProj = csm->specialLayer.shadowProj; + matShadowViewProj = csm->specialLayer.shadowViewProj; if (mainLight.isShadowFixedArea()) { near = mainLight.getShadowNear(); far = mainLight.getShadowFar(); levelCount = static_cast(0); } else { near = 0.1F; - far = csmLayers.getSpecialLayer()->getShadowCameraFar(); + far = csm->specialLayer.shadowCameraFar; levelCount = scene::CSMLevel::LEVEL_1; } vec4ShadowInfo.set(static_cast(scene::LightType::DIRECTIONAL), packing, mainLight.getShadowNormalBias(), 0); setVec4Impl(data, layoutGraph, "cc_shadowLPNNInfo", vec4ShadowInfo); } else { - const auto &layer = *csmLayers.getLayers()[level]; - matShadowView = layer.getMatShadowView(); - matShadowProj = layer.getMatShadowProj(); - matShadowViewProj = layer.getMatShadowViewProj(); - - near = layer.getSplitCameraNear(); - far = layer.getSplitCameraFar(); + CC_EXPECTS(level < csm->layers.size()); + const auto &layer = csm->layers[level]; + matShadowView = layer.shadowView; + matShadowProj = layer.shadowProj; + matShadowViewProj = layer.shadowViewProj; + + near = layer.splitCameraNear; + far = layer.splitCameraFar; levelCount = mainLight.getCSMLevel(); } setMat4Impl(data, layoutGraph, "cc_matLightView", matShadowView); @@ -489,12 +495,120 @@ void setShadowUBOLightView( setColorImpl(data, layoutGraph, "cc_shadowColor", gfx::Color{color[0], color[1], color[2], color[3]}); } +namespace { + +void updatePlanarNormalAndDistance( + const LayoutGraphData &layoutGraph, + const scene::Shadows &shadowInfo, + RenderData &data) { + const Vec3 normal = shadowInfo.getNormal().getNormalized(); + const Vec4 planarNDInfo{normal.x, normal.y, normal.z, -shadowInfo.getDistance()}; + setVec4Impl(data, layoutGraph, "cc_planarNDInfo", planarNDInfo); +} + +std::tuple +computeShadowMatrices( + const gfx::DeviceCaps &cap, + const Mat4 &matShadowCamera, + float fov, float farPlane) { + auto matShadowView = matShadowCamera.getInversed(); + + Mat4 matShadowProj; + Mat4::createPerspective( + fov, 1.0F, 0.001F, farPlane, + true, cap.clipSpaceMinZ, cap.clipSpaceSignY, + 0, &matShadowProj); + + Mat4 matShadowViewProj = matShadowProj; + Mat4 matShadowInvProj = matShadowProj; + + matShadowInvProj.inverse(); + matShadowViewProj.multiply(matShadowView); + + return {matShadowView, matShadowViewProj, matShadowProj, matShadowInvProj}; +} + +} // namespace + +void setPunctualLightShadowUBO( + gfx::Device *device, + const LayoutGraphData &layoutGraph, + const pipeline::PipelineSceneData &pplSceneData, + const scene::DirectionalLight *mainLight, + const scene::Light &light, + RenderData &data) { + const auto &shadowInfo = *pplSceneData.getShadows(); + const auto &packing = pipeline::supportsR32FloatTexture(device) ? 0.0F : 1.0F; + const auto &cap = device->getCapabilities(); + Vec4 vec4ShadowInfo{}; + + if (mainLight) { + // update planar PROJ + updatePlanarNormalAndDistance(layoutGraph, shadowInfo, data); + } + + // ShadowMap + switch (light.getType()) { + case scene::LightType::DIRECTIONAL: + // noop + break; + case scene::LightType::SPHERE: { + const auto &shadowSize = shadowInfo.getSize(); + setVec4Impl( + data, layoutGraph, "cc_shadowWHPBInfo", + Vec4{shadowSize.x, shadowSize.y, 1.0F, 0.0F}); + setVec4Impl( + data, layoutGraph, "cc_shadowLPNNInfo", + Vec4{static_cast(scene::LightType::SPHERE), packing, 0.0F, 0.0F}); + } break; + case scene::LightType::SPOT: { + const auto &shadowSize = shadowInfo.getSize(); + const auto &spotLight = dynamic_cast(light); + const auto &matShadowCamera = spotLight.getNode()->getWorldMatrix(); + const auto [matShadowView, matShadowViewProj, matShadowProj, matShadowInvProj] = + computeShadowMatrices(cap, matShadowCamera, spotLight.getAngle(), spotLight.getRange()); + + setMat4Impl(data, layoutGraph, "cc_matLightView", matShadowView); + setMat4Impl(data, layoutGraph, "cc_matLightViewProj", matShadowViewProj); + setVec4Impl( + data, layoutGraph, "cc_shadowNFLSInfo", + Vec4{0.1F, spotLight.getRange(), 0.0F, 0.0F}); + setVec4Impl( + data, layoutGraph, "cc_shadowWHPBInfo", + Vec4{shadowSize.x, shadowSize.y, spotLight.getShadowPcf(), spotLight.getShadowBias()}); + setVec4Impl( + data, layoutGraph, "cc_shadowLPNNInfo", + Vec4{static_cast(scene::LightType::SPOT), packing, spotLight.getShadowNormalBias(), 0.0F}); + setVec4Impl( + data, layoutGraph, "cc_shadowInvProjDepthInfo", + Vec4{matShadowInvProj.m[10], matShadowInvProj.m[14], matShadowInvProj.m[11], matShadowInvProj.m[15]}); + setVec4Impl( + data, layoutGraph, "cc_shadowProjDepthInfo", + Vec4{matShadowProj.m[10], matShadowProj.m[14], matShadowProj.m[11], matShadowProj.m[15]}); + setVec4Impl( + data, layoutGraph, "cc_shadowProjInfo", + Vec4{matShadowProj.m[00], matShadowProj.m[05], 1.0F / matShadowProj.m[00], 1.0F / matShadowProj.m[05]}); + } break; + case scene::LightType::POINT: { + const auto &shadowSize = shadowInfo.getSize(); + setVec4Impl( + data, layoutGraph, "cc_shadowWHPBInfo", + Vec4{shadowSize.x, shadowSize.y, 1.0F, 0.0F}); + setVec4Impl( + data, layoutGraph, "cc_shadowLPNNInfo", + Vec4{static_cast(scene::LightType::POINT), packing, 0.0F, 0.0F}); + } break; + default: + break; + } +} + void setLegacyTextureUBOView( gfx::Device &device, const LayoutGraphData &layoutGraph, - const pipeline::PipelineSceneData &sceneData, + const pipeline::PipelineSceneData &pplSceneData, RenderData &data) { - const auto &skybox = *sceneData.getSkybox(); + const auto &skybox = *pplSceneData.getSkybox(); if (skybox.getReflectionMap()) { auto &texture = *skybox.getReflectionMap()->getGFXTexture(); auto *sampler = device.getSampler(skybox.getReflectionMap()->getSamplerInfo()); @@ -536,6 +650,251 @@ void setLegacyTextureUBOView( // setTextureImpl(data, layoutGraph, "cc_spotShadowMap", BuiltinResMgr::getInstance()->get("default-texture")->getGFXTexture()); } +namespace { + +float kLightMeterScale{10000.0F}; + +} // namespace + +void setLightUBO( + const scene::Light *light, bool bHDR, float exposure, + const scene::Shadows *shadowInfo, + char *buffer, size_t bufferSize) { + CC_EXPECTS(bufferSize % sizeof(float) == 0); + const auto maxSize = bufferSize / sizeof(float); + auto *lightBufferData = reinterpret_cast(buffer); + + size_t offset = 0; + + Vec3 position = Vec3(0.0F, 0.0F, 0.0F); + float size = 0.F; + float range = 0.F; + float luminanceHDR = 0.F; + float luminanceLDR = 0.F; + if (light->getType() == scene::LightType::SPHERE) { + const auto *sphereLight = static_cast(light); + position = sphereLight->getPosition(); + size = sphereLight->getSize(); + range = sphereLight->getRange(); + luminanceHDR = sphereLight->getLuminanceHDR(); + luminanceLDR = sphereLight->getLuminanceLDR(); + } else if (light->getType() == scene::LightType::SPOT) { + const auto *spotLight = static_cast(light); + position = spotLight->getPosition(); + size = spotLight->getSize(); + range = spotLight->getRange(); + luminanceHDR = spotLight->getLuminanceHDR(); + luminanceLDR = spotLight->getLuminanceLDR(); + } else if (light->getType() == scene::LightType::POINT) { + const auto *pointLight = static_cast(light); + position = pointLight->getPosition(); + size = 0.0F; + range = pointLight->getRange(); + luminanceHDR = pointLight->getLuminanceHDR(); + luminanceLDR = pointLight->getLuminanceLDR(); + } else if (light->getType() == scene::LightType::RANGED_DIRECTIONAL) { + const auto *rangedDirLight = static_cast(light); + position = rangedDirLight->getPosition(); + size = 0.0F; + range = 0.0F; + luminanceHDR = rangedDirLight->getIlluminanceHDR(); + luminanceLDR = rangedDirLight->getIlluminanceLDR(); + } + auto index = offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET; + CC_EXPECTS(index + 4 < maxSize); + lightBufferData[index++] = position.x; + lightBufferData[index++] = position.y; + lightBufferData[index] = position.z; + + index = offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET; + CC_EXPECTS(index + 4 < maxSize); + lightBufferData[index++] = size; + lightBufferData[index] = range; + + index = offset + pipeline::UBOForwardLight::LIGHT_COLOR_OFFSET; + CC_EXPECTS(index + 4 < maxSize); + const auto &color = light->getColor(); + if (light->isUseColorTemperature()) { + const auto &tempRGB = light->getColorTemperatureRGB(); + lightBufferData[index++] = color.x * tempRGB.x; + lightBufferData[index++] = color.y * tempRGB.y; + lightBufferData[index++] = color.z * tempRGB.z; + } else { + lightBufferData[index++] = color.x; + lightBufferData[index++] = color.y; + lightBufferData[index++] = color.z; + } + + if (bHDR) { + lightBufferData[index] = luminanceHDR * exposure * kLightMeterScale; + } else { + lightBufferData[index] = luminanceLDR; + } + + switch (light->getType()) { + case scene::LightType::SPHERE: + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET + 3] = + static_cast(scene::LightType::SPHERE); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = 0; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = 0; + break; + case scene::LightType::SPOT: { + const auto *spotLight = static_cast(light); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET + 3] = static_cast(scene::LightType::SPOT); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = spotLight->getSpotAngle(); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = + (shadowInfo->isEnabled() && + spotLight->isShadowEnabled() && + shadowInfo->getType() == scene::ShadowType::SHADOW_MAP) + ? 1.0F + : 0.0F; + + index = offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET; + const auto &direction = spotLight->getDirection(); + lightBufferData[index++] = direction.x; + lightBufferData[index++] = direction.y; + lightBufferData[index] = direction.z; + } break; + case scene::LightType::POINT: + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET + 3] = static_cast(scene::LightType::POINT); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = 0; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = 0; + break; + case scene::LightType::RANGED_DIRECTIONAL: { + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET + 3] = static_cast(scene::LightType::RANGED_DIRECTIONAL); + + const auto *rangedDirLight = static_cast(light); + const Vec3 &right = rangedDirLight->getRight(); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 0] = right.x; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 1] = right.y; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = right.z; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = 0; + + const auto &direction = rangedDirLight->getDirection(); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET + 0] = direction.x; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET + 1] = direction.y; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET + 2] = direction.z; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET + 3] = 0; + + const auto &scale = rangedDirLight->getScale(); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_BOUNDING_SIZE_VS_OFFSET + 0] = scale.x * 0.5F; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_BOUNDING_SIZE_VS_OFFSET + 1] = scale.y * 0.5F; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_BOUNDING_SIZE_VS_OFFSET + 2] = scale.z * 0.5F; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_BOUNDING_SIZE_VS_OFFSET + 3] = 0; + } break; + default: + break; + } +} + +const BuiltinCascadedShadowMap *getBuiltinShadowCSM( + const PipelineRuntime &pplRuntime, + const scene::Camera &camera, + const scene::DirectionalLight *mainLight) { + const auto &ppl = dynamic_cast(pplRuntime); + // no main light + if (!mainLight) { + return nullptr; + } + // not attached to a node + if (!mainLight->getNode()) { + return nullptr; + } + const pipeline::PipelineSceneData &pplSceneData = *pplRuntime.getPipelineSceneData(); + auto &csmLayers = *pplSceneData.getCSMLayers(); + const auto &shadows = *pplSceneData.getShadows(); + // shadow not enabled + if (!shadows.isEnabled()) { + return nullptr; + } + // shadow type is planar + if (shadows.getType() == scene::ShadowType::PLANAR) { + return nullptr; + } + + // find csm + const BuiltinCascadedShadowMapKey key{&camera, mainLight}; + auto iter = ppl.builtinCSMs.find(key); + if (iter != ppl.builtinCSMs.end()) { + return &iter->second; + } + + // add new csm info + bool added = false; + std::tie(iter, added) = ppl.builtinCSMs.emplace( + std::piecewise_construct, + std::forward_as_tuple(key), + std::forward_as_tuple()); + CC_ENSURES(added); + + auto &csm = iter->second; + + // update csm layers + csmLayers.update(&pplSceneData, &camera); + + // copy csm data + CC_EXPECTS(csm.layers.size() == csmLayers.getLayers().size()); + for (uint32_t i = 0; i != csm.layers.size(); ++i) { + const auto &src = *csmLayers.getLayers()[i]; + auto &dst = csm.layers[i]; + dst.shadowView = src.getMatShadowView(); + dst.shadowProj = src.getMatShadowProj(); + dst.shadowViewProj = src.getMatShadowViewProj(); + dst.validFrustum = src.getValidFrustum(); + dst.splitFrustum = src.getSplitFrustum(); + dst.lightViewFrustum = src.getLightViewFrustum(); + dst.castLightViewBoundingBox = src.getCastLightViewBoundingBox(); + dst.shadowCameraFar = src.getShadowCameraFar(); + dst.splitCameraNear = src.getSplitCameraNear(); + dst.splitCameraFar = src.getSplitCameraFar(); + dst.csmAtlas = src.getCSMAtlas(); + } + + { + const auto &src = *csmLayers.getSpecialLayer(); + auto &dst = csm.specialLayer; + dst.shadowView = src.getMatShadowView(); + dst.shadowProj = src.getMatShadowProj(); + dst.shadowViewProj = src.getMatShadowViewProj(); + dst.validFrustum = src.getValidFrustum(); + dst.splitFrustum = src.getSplitFrustum(); + dst.lightViewFrustum = src.getLightViewFrustum(); + dst.castLightViewBoundingBox = src.getCastLightViewBoundingBox(); + dst.shadowCameraFar = src.getShadowCameraFar(); + } + + csm.shadowDistance = mainLight->getShadowDistance(); + + return &csm; +} + +const geometry::Frustum &getBuiltinShadowFrustum( + const PipelineRuntime &pplRuntime, + const scene::Camera &camera, + const scene::DirectionalLight *mainLight, + uint32_t level) { + const auto &ppl = dynamic_cast(pplRuntime); + + const auto &shadows = *ppl.pipelineSceneData->getShadows(); + if (shadows.getType() == scene::ShadowType::PLANAR) { + return camera.getFrustum(); + } + + BuiltinCascadedShadowMapKey key{&camera, mainLight}; + auto iter = ppl.builtinCSMs.find(key); + if (iter == ppl.builtinCSMs.end()) { + throw std::runtime_error("Builtin shadow CSM not found"); + } + + const auto &csmLevel = mainLight->getCSMLevel(); + const auto &csm = iter->second; + + if (mainLight->isShadowFixedArea() || csmLevel == scene::CSMLevel::LEVEL_1) { + return csm.specialLayer.validFrustum; + } + return csm.layers[level].validFrustum; +} + } // namespace render } // namespace cc diff --git a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.h b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.h index b123a5332ac..9c74502dfaa 100644 --- a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.h +++ b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.h @@ -24,17 +24,26 @@ #pragma once #include +#include "cocos/math/Vec4.h" +#include "cocos/renderer/gfx-base/GFXDevice.h" #include "cocos/renderer/pipeline/custom/LayoutGraphFwd.h" #include "cocos/renderer/pipeline/custom/NativeFwd.h" +#include "cocos/renderer/pipeline/custom/NativePipelineFwd.h" #include "cocos/renderer/pipeline/custom/RenderGraphFwd.h" namespace cc { namespace scene { class Camera; +class Light; class DirectionalLight; +class Shadows; } // namespace scene +namespace geometry { +class Frustum; +} // namespace geometry + namespace gfx { class Device; } // namespace gfx @@ -71,16 +80,43 @@ void setShadowUBOLightView( gfx::Device *device, const LayoutGraphData &layoutGraph, const pipeline::PipelineSceneData &sceneData, + const BuiltinCascadedShadowMap *csm, const scene::Light &light, uint32_t level, RenderData &data); +// Additive light +void setLightUBO( + const scene::Light *light, bool bHDR, float exposure, + const scene::Shadows *shadowInfo, + char *buffer, size_t bufferSize); + +void setPunctualLightShadowUBO( + gfx::Device *device, + const LayoutGraphData &layoutGraph, + const pipeline::PipelineSceneData &pplSceneData, + const scene::DirectionalLight *mainLight, + const scene::Light &light, + RenderData &data); + // Render graph void updateRasterPassConstants(uint32_t width, uint32_t height, Setter &setter); // Geometry void setupQuadVertexBuffer(gfx::Device &device, const Vec4 &viewport, float vbData[16]); +// Shadow +const BuiltinCascadedShadowMap *getBuiltinShadowCSM( + const PipelineRuntime &pplRuntime, + const scene::Camera &camera, + const scene::DirectionalLight *mainLight); + +const geometry::Frustum &getBuiltinShadowFrustum( + const PipelineRuntime &pplRuntime, + const scene::Camera &camera, + const scene::DirectionalLight *mainLight, + uint32_t level); + } // namespace render } // namespace cc diff --git a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp index 3e72aefa9b6..7e196feb953 100644 --- a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp @@ -28,14 +28,15 @@ #include "LayoutGraphGraphs.h" #include "LayoutGraphTypes.h" #include "LayoutGraphUtils.h" +#include "NativeBuiltinUtils.h" #include "NativePipelineFwd.h" #include "NativePipelineTypes.h" +#include "NativeRenderGraphUtils.h" #include "NativeUtils.h" #include "PrivateTypes.h" #include "RenderGraphGraphs.h" #include "RenderGraphTypes.h" #include "RenderingModule.h" -#include "NativeRenderGraphUtils.h" #include "cocos/renderer/gfx-base/GFXBarrier.h" #include "cocos/renderer/gfx-base/GFXDef-common.h" #include "cocos/renderer/gfx-base/GFXDescriptorSetLayout.h" @@ -60,10 +61,11 @@ namespace { constexpr uint32_t INVALID_ID = 0xFFFFFFFF; constexpr gfx::Color RASTER_COLOR{0.0, 1.0, 0.0, 1.0}; +constexpr gfx::Color RASTER_UPLOAD_COLOR{1.0, 1.0, 0.0, 1.0}; constexpr gfx::Color RENDER_QUEUE_COLOR{0.0, 0.5, 0.5, 1.0}; constexpr gfx::Color COMPUTE_COLOR{0.0, 0.0, 1.0, 1.0}; -gfx::MarkerInfo makeMarkerInfo(const char *str, const gfx::Color &color) { +gfx::MarkerInfo makeMarkerInfo(const char* str, const gfx::Color& color) { return gfx::MarkerInfo{str, color}; } @@ -1135,9 +1137,6 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { } } void begin(const RasterPass& pass, RenderGraph::vertex_descriptor vertID) const { -#if CC_DEBUG - ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RASTER_COLOR)); -#endif const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); if (!renderData.custom.empty()) { const auto& passes = ctx.ppl->custom.renderPasses; @@ -1193,7 +1192,7 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { if (subpass.subpassID) { ctx.cmdBuff->nextSubpass(); } - //ctx.cmdBuff->setViewport(subpass); + // ctx.cmdBuff->setViewport(subpass); tryBindPerPassDescriptorSet(vertID); ctx.subpassIndex = subpass.subpassID; // noop @@ -1393,16 +1392,14 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { tryBindPerPassDescriptorSet(sceneID); } const auto* scene = camera->getScene(); - const auto& queueDesc = ctx.context.sceneCulling.sceneQueryIndex.at(sceneID); - const auto& queue = ctx.context.sceneCulling.renderQueues[queueDesc.renderQueueTarget]; - queue.opaqueQueue.recordCommandBuffer( - ctx.device, camera, ctx.currentPass, ctx.cmdBuff, 0); - queue.opaqueInstancingQueue.recordCommandBuffer( - ctx.currentPass, ctx.cmdBuff); - queue.transparentQueue.recordCommandBuffer( - ctx.device, camera, ctx.currentPass, ctx.cmdBuff, 0); - queue.transparentInstancingQueue.recordCommandBuffer( - ctx.currentPass, ctx.cmdBuff); + const auto& queueDesc = ctx.context.sceneCulling.renderQueueIndex.at(sceneID); + const auto& queue = ctx.context.sceneCulling.renderQueues[queueDesc.renderQueueTarget.value]; + + queue.recordCommands(ctx.cmdBuff, ctx.currentPass, 0); + + if (any(sceneData.flags & SceneFlags::REFLECTION_PROBE)) { + queue.probeQueue.removeMacro(); + } if (any(sceneData.flags & SceneFlags::UI)) { submitUICommands(ctx.currentPass, ctx.currentPassLayoutID, camera, ctx.cmdBuff); @@ -1492,10 +1489,6 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { ctx.cmdBuff->endRenderPass(); ctx.currentPass = nullptr; ctx.currentPassLayoutID = LayoutGraphData::null_vertex(); - -#if CC_DEBUG - ctx.cmdBuff->endMarker(); -#endif } void end(const RasterSubpass& subpass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static) const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID); @@ -1687,6 +1680,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { visitObject( vertID, ctx.g, [&](const RasterPass& pass) { +#if CC_DEBUG + ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RASTER_COLOR)); +#endif mountResources(pass); { const auto& layoutName = get(RenderGraph::LayoutTag{}, ctx.g, vertID); @@ -1695,9 +1691,15 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { } // update UniformBuffers and DescriptorSets in all children { +#if CC_DEBUG + ctx.cmdBuff->beginMarker(makeMarkerInfo("Upload", RASTER_UPLOAD_COLOR)); +#endif auto colors = ctx.g.colors(ctx.scratch); RenderGraphUploadVisitor visitor{{}, ctx}; boost::depth_first_visit(gv, vertID, visitor, get(colors, ctx.g)); +#if CC_DEBUG + ctx.cmdBuff->endMarker(); +#endif } if (pass.showStatistics) { const auto* profiler = ctx.ppl->getProfiler(); @@ -1803,6 +1805,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { [&](const RasterPass& pass) { end(pass, vertID); rearBarriers(vertID); +#if CC_DEBUG + ctx.cmdBuff->endMarker(); +#endif }, [&](const RasterSubpass& subpass) { end(subpass, vertID); @@ -1987,7 +1992,7 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { { auto& context = ppl.nativeContext; auto& sceneCulling = context.sceneCulling; - sceneCulling.buildRenderQueues(rg, lg, *ppl.pipelineSceneData); + sceneCulling.buildRenderQueues(rg, lg, ppl); auto& group = ppl.nativeContext.resourceGroups[context.nextFenceValue]; // notice: we cannot use ranged-for of sceneCulling.renderQueues CC_EXPECTS(sceneCulling.numRenderQueues <= sceneCulling.renderQueues.size()); @@ -1997,6 +2002,16 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { } } + // light manangement + { + auto& ctx = ppl.nativeContext; + ctx.lightResources.clear(); + ctx.lightResources.buildLights( + ctx.sceneCulling, + ppl.pipelineSceneData->isHDR(), + ppl.pipelineSceneData->getShadows()); + } + // gpu driven if constexpr (ENABLE_GPU_DRIVEN) { // TODO(jilin): consider populating renderSceneResources here @@ -2018,6 +2033,11 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { // upload buffers { + auto& ctx = ppl.nativeContext; +#if CC_DEBUG + submit.primaryCommandBuffer->beginMarker(makeMarkerInfo("Internal Upload", RASTER_UPLOAD_COLOR)); +#endif + // scene const auto& sceneCulling = ppl.nativeContext.sceneCulling; for (uint32_t queueID = 0; queueID != sceneCulling.numRenderQueues; ++queueID) { // notice: we cannot use ranged-for of sceneCulling.renderQueues @@ -2026,6 +2046,13 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { queue.opaqueInstancingQueue.uploadBuffers(submit.primaryCommandBuffer); queue.transparentInstancingQueue.uploadBuffers(submit.primaryCommandBuffer); } + + // lights + ctx.lightResources.buildLightBuffer(submit.primaryCommandBuffer); + ctx.lightResources.tryUpdateRenderSceneLocalDescriptorSet(sceneCulling); +#if CC_DEBUG + submit.primaryCommandBuffer->endMarker(); +#endif } ccstd::pmr::unordered_map< diff --git a/native/cocos/renderer/pipeline/custom/NativePipeline.cpp b/native/cocos/renderer/pipeline/custom/NativePipeline.cpp index 004765b67e4..85cec43b9cf 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipeline.cpp +++ b/native/cocos/renderer/pipeline/custom/NativePipeline.cpp @@ -32,8 +32,12 @@ #include "cocos/renderer/pipeline/custom/RenderGraphGraphs.h" #include "cocos/renderer/pipeline/custom/RenderingModule.h" #include "cocos/renderer/pipeline/custom/details/GslUtils.h" +#include "cocos/scene/ReflectionProbe.h" +#include "cocos/scene/ReflectionProbeManager.h" #include "cocos/scene/RenderScene.h" #include "cocos/scene/RenderWindow.h" +#include "pipeline/custom/RenderInterfaceTypes.h" + #if CC_USE_DEBUG_RENDERER #include "profiler/DebugRenderer.h" #endif @@ -42,14 +46,14 @@ namespace cc { namespace render { -template +template void addSubresourceNode(ResourceGraph::vertex_descriptor v, const ccstd::string &name, ResourceGraph &resg); template <> void addSubresourceNode(ResourceGraph::vertex_descriptor v, const ccstd::string &name, ResourceGraph &resg) { - const auto& desc = get(ResourceGraph::DescTag{}, resg, v); - const auto& traits = get(ResourceGraph::TraitsTag{}, resg, v); - const auto& samplerInfo = get(ResourceGraph::SamplerTag{}, resg, v); + const auto &desc = get(ResourceGraph::DescTag{}, resg, v); + const auto &traits = get(ResourceGraph::TraitsTag{}, resg, v); + const auto &samplerInfo = get(ResourceGraph::SamplerTag{}, resg, v); SubresourceView view{ nullptr, @@ -60,7 +64,6 @@ void addSubresourceNode(ResourceGraph::vertex_descri 1, // numArraySlices 0, // firstPlane 1, // numPlanes - desc.viewType, }; ccstd::string depthName{name}; @@ -120,6 +123,7 @@ PipelineCapabilities NativePipeline::getCapabilities() const { void NativePipeline::beginSetup() { renderGraph = RenderGraph(get_allocator()); + builtinCSMs.clear(); } void NativePipeline::endSetup() { @@ -314,7 +318,7 @@ uint32_t NativePipeline::addTexture(const ccstd::string &name, gfx::TextureType sampleCount, residency == ResourceResidency::MEMORYLESS ? gfx::TextureFlagBit::LAZILY_ALLOCATED : gfx::TextureFlagBit::NONE, flags, - type + type, }; return addVertex( ManagedTextureTag{}, @@ -351,15 +355,16 @@ void NativePipeline::updateBuffer(const ccstd::string &name, uint32_t size) { updateResource(name, gfx::Format::UNKNOWN, size, 0, 0, 0, 0, gfx::SampleCount::X1); } -uint32_t NativePipeline::addResource(const ccstd::string& name, ResourceDimension dimension, +uint32_t NativePipeline::addResource( + const ccstd::string &name, ResourceDimension dimension, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount, ResourceFlags flags, ResourceResidency residency) { - return dimension == ResourceDimension::BUFFER ? addBuffer(name, width, flags, residency) : - addTexture(name, getTextureType(dimension, arraySize), format, width, height, depth, arraySize, mipLevels, sampleCount, flags, residency); + return dimension == ResourceDimension::BUFFER ? addBuffer(name, width, flags, residency) : addTexture(name, getTextureType(dimension, arraySize), format, width, height, depth, arraySize, mipLevels, sampleCount, flags, residency); } -void NativePipeline::updateResource(const ccstd::string& name, gfx::Format format, +void NativePipeline::updateResource( + const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, // NOLINT(bugprone-easily-swappable-parameters) gfx::SampleCount sampleCount) { auto resID = findVertex(ccstd::pmr::string(name, get_allocator()), resourceGraph); @@ -390,7 +395,7 @@ void NativePipeline::updateResource(const ccstd::string& name, gfx::Format forma resourceGraph.invalidatePersistentRenderPassAndFramebuffer(tex.texture.get()); } }, - [&](ManagedBuffer &/*buffer*/) { + [&](ManagedBuffer & /*buffer*/) { desc.width = width; }, [](const auto & /*res*/) {}); @@ -407,7 +412,7 @@ uint32_t NativePipeline::addStorageTexture(const ccstd::string &name, gfx::Forma desc.format = format; desc.sampleCount = gfx::SampleCount::X1; desc.textureFlags = gfx::TextureFlagBit::NONE; - desc.flags = ResourceFlags::STORAGE | ResourceFlags::SAMPLED | ResourceFlags::TRANSFER_SRC | ResourceFlags::TRANSFER_DST;; + desc.flags = ResourceFlags::STORAGE | ResourceFlags::SAMPLED | ResourceFlags::TRANSFER_SRC | ResourceFlags::TRANSFER_DST; CC_EXPECTS(residency == ResourceResidency::MANAGED || residency == ResourceResidency::MEMORYLESS); @@ -684,6 +689,99 @@ void NativePipeline::endFrame() { // noop } +namespace { + +gfx::LoadOp getLoadOpOfClearFlag(gfx::ClearFlagBit clearFlag, AttachmentType attachment) { + gfx::LoadOp loadOp = gfx::LoadOp::CLEAR; + if (!(clearFlag & gfx::ClearFlagBit::COLOR) && attachment == AttachmentType::RENDER_TARGET) { + if (static_cast(clearFlag) & cc::scene::Camera::SKYBOX_FLAG) { + loadOp = gfx::LoadOp::CLEAR; + } else { + loadOp = gfx::LoadOp::LOAD; + } + } + if ((clearFlag & gfx::ClearFlagBit::DEPTH_STENCIL) != gfx::ClearFlagBit::DEPTH_STENCIL && attachment == AttachmentType::DEPTH_STENCIL) { + if (!(clearFlag & gfx::ClearFlagBit::DEPTH)) { + loadOp = gfx::LoadOp::LOAD; + } + if (!(clearFlag & gfx::ClearFlagBit::STENCIL)) { + loadOp = gfx::LoadOp::LOAD; + } + } + return loadOp; +} + +void updateCameraUBO(Setter &setter, const scene::Camera *camera, NativePipeline &ppl) { + auto sceneData = ppl.pipelineSceneData; + auto *skybox = sceneData->getSkybox(); + setter.setBuiltinCameraConstants(camera); +} + +void buildReflectionProbePass( + const scene::Camera *camera, + render::NativePipeline *pipeline, + const scene::ReflectionProbe *probe, + scene::RenderWindow *renderWindow, + int faceIdx) { + const std::string cameraName = "Camera" + std::to_string(faceIdx); + const auto &area = probe->renderArea(); + const auto width = static_cast(area.x); + const auto height = static_cast(area.y); + const auto *probeCamera = probe->getCamera(); + const std::string probePassRTName = "reflectionProbePassColor" + cameraName; + const std::string probePassDSName = "reflectionProbePassDS" + cameraName; + if (!pipeline->containsResource(probePassRTName)) { + pipeline->addRenderWindow(probePassRTName, gfx::Format::RGBA8, width, height, renderWindow); + pipeline->addDepthStencil(probePassDSName, gfx::Format::DEPTH_STENCIL, width, height, ResourceResidency::EXTERNAL); + } + pipeline->updateRenderWindow(probePassRTName, renderWindow); + pipeline->updateDepthStencil(probePassDSName, width, height, gfx::Format::DEPTH_STENCIL); + std::unique_ptr passBuilder(pipeline->addRenderPass(width, height, "default")); + passBuilder->setName("ReflectionProbePass" + std::to_string(faceIdx)); + gfx::Viewport currViewport{}; + currViewport.width = width; + currViewport.height = height; + passBuilder->setViewport(currViewport); + gfx::Color clearColor{}; + clearColor.x = probeCamera->getClearColor().x; + clearColor.y = probeCamera->getClearColor().y; + clearColor.z = probeCamera->getClearColor().z; + clearColor.w = probeCamera->getClearColor().w; + passBuilder->addRenderTarget( + probePassRTName, + getLoadOpOfClearFlag(probeCamera->getClearFlag(), AttachmentType::RENDER_TARGET), + gfx::StoreOp::STORE, + clearColor); + passBuilder->addDepthStencil( + probePassDSName, + getLoadOpOfClearFlag(probeCamera->getClearFlag(), AttachmentType::DEPTH_STENCIL), + gfx::StoreOp::STORE, + probeCamera->getClearDepth(), + probeCamera->getClearStencil(), + probeCamera->getClearFlag()); + std::unique_ptr queueBuilder( + passBuilder->addQueue(QueueHint::RENDER_OPAQUE, "reflect-map")); + LightInfo lightInfo{}; + lightInfo.probe = const_cast(probe); + queueBuilder->addSceneOfCamera(const_cast(camera), lightInfo, SceneFlags::REFLECTION_PROBE | SceneFlags::OPAQUE_OBJECT); + updateCameraUBO(*queueBuilder, probeCamera, *pipeline); +} + +} // namespace + +void NativePipeline::addBuiltinReflectionProbePass(const scene::Camera *camera) { + const auto *reflectProbeManager = scene::ReflectionProbeManager::getInstance(); + if (!reflectProbeManager) return; + const auto &probes = reflectProbeManager->getAllProbes(); + for (auto *probe : probes) { + if (probe->needRender()) { + if (probe->getProbeType() == scene::ReflectionProbe::ProbeType::PLANAR) { + buildReflectionProbePass(camera, this, probe, probe->getRealtimePlanarTexture()->getWindow(), 0); + } + } + } +} + RenderPassBuilder *NativePipeline::addRenderPass( uint32_t width, uint32_t height, const ccstd::string &passName) { @@ -782,7 +880,7 @@ void NativePipeline::addMovePass(const ccstd::vector &movePairs) { namespace { void setupGpuDrivenResources( - NativePipeline& ppl, uint32_t cullingID, ResourceGraph& resg, const std::string &hzbName) { + NativePipeline &ppl, uint32_t cullingID, ResourceGraph &resg, const std::string &hzbName) { ccstd::pmr::string name(resg.get_allocator()); { // init resource name = "_GpuInit"; @@ -859,7 +957,6 @@ void setupGpuDrivenResources( } } if (!hzbName.empty()) { - } } @@ -894,7 +991,8 @@ void NativePipeline::addBuiltinGpuCullingPass( copyPass.copyPairs.emplace_back(std::move(copyPair)); } - auto copyID = addVertex2(CopyTag{}, + auto copyID = addVertex2( + CopyTag{}, std::forward_as_tuple("CopyInitialIndirectBuffer"), std::forward_as_tuple(), std::forward_as_tuple(), @@ -911,7 +1009,7 @@ void NativePipeline::addBuiltinGpuCullingPass( std::piecewise_construct, std::forward_as_tuple(drawIndirectBuffer), std::forward_as_tuple()); - auto& view = res.first->second.emplace_back(); + auto &view = res.first->second.emplace_back(); view.name = "CCDrawIndirectBuffer"; view.accessType = AccessType::WRITE; view.shaderStageFlags = gfx::ShaderStageFlagBit::COMPUTE; @@ -921,7 +1019,7 @@ void NativePipeline::addBuiltinGpuCullingPass( std::piecewise_construct, std::forward_as_tuple(drawInstanceBuffer), std::forward_as_tuple()); - auto& view = res.first->second.emplace_back(); + auto &view = res.first->second.emplace_back(); view.name = "CCDrawInstanceBuffer"; view.accessType = AccessType::WRITE; view.shaderStageFlags = gfx::ShaderStageFlagBit::COMPUTE; @@ -931,13 +1029,14 @@ void NativePipeline::addBuiltinGpuCullingPass( std::piecewise_construct, std::forward_as_tuple(visibilityBuffer), std::forward_as_tuple()); - auto& view = res.first->second.emplace_back(); + auto &view = res.first->second.emplace_back(); view.name = "CCVisibilityBuffer"; view.accessType = AccessType::WRITE; view.shaderStageFlags = gfx::ShaderStageFlagBit::COMPUTE; } - auto computePassID = addVertex2(ComputeTag{}, + auto computePassID = addVertex2( + ComputeTag{}, std::forward_as_tuple("Scene"), std::forward_as_tuple(), std::forward_as_tuple(), @@ -1096,6 +1195,7 @@ bool NativePipeline::activate(gfx::Swapchain *swapchainIn) { const auto &lg = programLibrary->layoutGraph; const auto numNodes = num_vertices(lg); nativeContext.layoutGraphResources.reserve(numNodes); + nativeContext.lightResources.init(*programLibrary, device, 16); for (uint32_t i = 0; i != numNodes; ++i) { auto &node = nativeContext.layoutGraphResources.emplace_back(); diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h b/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h index 846d97ac32d..f83927c1b64 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h +++ b/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h @@ -29,17 +29,35 @@ */ // clang-format off #pragma once -#include "cocos/base/std/hash/hash.h" #include "cocos/base/std/variant.h" -#include "cocos/renderer/pipeline/InstancedBuffer.h" #include "cocos/renderer/pipeline/custom/NativeFwd.h" namespace cc { +namespace scene { + +class ReflectionProbe; + +} // namespace scene + +namespace render { + +template +using Array4 = std::array; + +} // namespace render + +} // namespace cc + +#include "cocos/base/std/hash/hash.h" + +namespace cc { + namespace render { class NativeRenderNode; class NativeSetter; +class NativeSceneBuilder; class NativeRenderSubpassBuilderImpl; class NativeRenderQueueBuilder; class NativeRenderSubpassBuilder; @@ -51,6 +69,7 @@ class NativeComputeQueueBuilder; class NativeComputePassBuilder; struct RenderInstancingQueue; struct DrawInstance; +struct ProbeHelperQueue; struct RenderDrawQueue; struct NativeRenderQueue; struct ResourceGroup; @@ -64,13 +83,23 @@ struct QuadResource; enum class ResourceType; struct SceneResource; -struct CullingKey; -struct CullingQueries; +struct FrustumCullingKey; +struct FrustumCullingID; +struct FrustumCulling; +struct LightBoundsCullingID; +struct LightBoundsCullingKey; +struct LightBoundsCulling; +struct NativeRenderQueueID; struct NativeRenderQueueDesc; +struct LightBoundsCullingResult; struct SceneCulling; +struct LightResource; struct NativeRenderContext; class NativeProgramLibrary; struct PipelineCustomization; +struct BuiltinShadowTransform; +struct BuiltinCascadedShadowMapKey; +struct BuiltinCascadedShadowMap; class NativePipeline; class NativeProgramProxy; class NativeRenderingModule; @@ -82,8 +111,18 @@ class NativeRenderingModule; namespace ccstd { template <> -struct hash { - hash_t operator()(const cc::render::CullingKey& val) const noexcept; +struct hash { + hash_t operator()(const cc::render::FrustumCullingKey& val) const noexcept; +}; + +template <> +struct hash { + hash_t operator()(const cc::render::FrustumCullingID& val) const noexcept; +}; + +template <> +struct hash { + hash_t operator()(const cc::render::LightBoundsCullingKey& val) const noexcept; }; } // namespace ccstd diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp index d8de417ef46..4046c22bc68 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp +++ b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp @@ -49,6 +49,15 @@ RenderInstancingQueue::RenderInstancingQueue(RenderInstancingQueue const& rhs, c passInstances(rhs.passInstances, alloc), instanceBuffers(rhs.instanceBuffers, alloc) {} +ProbeHelperQueue::ProbeHelperQueue(const allocator_type& alloc) noexcept +: probeMap(alloc) {} + +ProbeHelperQueue::ProbeHelperQueue(ProbeHelperQueue&& rhs, const allocator_type& alloc) +: probeMap(std::move(rhs.probeMap), alloc) {} + +ProbeHelperQueue::ProbeHelperQueue(ProbeHelperQueue const& rhs, const allocator_type& alloc) +: probeMap(rhs.probeMap, alloc) {} + RenderDrawQueue::RenderDrawQueue(const allocator_type& alloc) noexcept : instances(alloc) {} @@ -61,12 +70,14 @@ RenderDrawQueue::RenderDrawQueue(RenderDrawQueue const& rhs, const allocator_typ NativeRenderQueue::NativeRenderQueue(const allocator_type& alloc) noexcept : opaqueQueue(alloc), transparentQueue(alloc), + probeQueue(alloc), 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), @@ -75,10 +86,12 @@ NativeRenderQueue::NativeRenderQueue(SceneFlags sceneFlagsIn, uint32_t subpassOr 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), sceneFlags(rhs.sceneFlags), - subpassOrPassLayoutID(rhs.subpassOrPassLayoutID) {} + subpassOrPassLayoutID(rhs.subpassOrPassLayoutID), + lightByteOffset(rhs.lightByteOffset) {} ResourceGroup::ResourceGroup(const allocator_type& alloc) noexcept : instancingBuffers(alloc) {} @@ -159,36 +172,56 @@ SceneResource::SceneResource(SceneResource&& rhs, const allocator_type& alloc) storageBuffers(std::move(rhs.storageBuffers), alloc), storageImages(std::move(rhs.storageImages), alloc) {} -CullingQueries::CullingQueries(const allocator_type& alloc) noexcept -: culledResultIndex(alloc) {} +FrustumCulling::FrustumCulling(const allocator_type& alloc) noexcept +: resultIndex(alloc) {} + +FrustumCulling::FrustumCulling(FrustumCulling&& rhs, const allocator_type& alloc) +: resultIndex(std::move(rhs.resultIndex), alloc) {} -CullingQueries::CullingQueries(CullingQueries&& rhs, const allocator_type& alloc) -: culledResultIndex(std::move(rhs.culledResultIndex), alloc) {} +FrustumCulling::FrustumCulling(FrustumCulling const& rhs, const allocator_type& alloc) +: resultIndex(rhs.resultIndex, alloc) {} -CullingQueries::CullingQueries(CullingQueries const& rhs, const allocator_type& alloc) -: culledResultIndex(rhs.culledResultIndex, alloc) {} +LightBoundsCulling::LightBoundsCulling(const allocator_type& alloc) noexcept +: resultIndex(alloc) {} + +LightBoundsCulling::LightBoundsCulling(LightBoundsCulling&& rhs, const allocator_type& alloc) +: resultIndex(std::move(rhs.resultIndex), alloc) {} + +LightBoundsCulling::LightBoundsCulling(LightBoundsCulling const& rhs, const allocator_type& alloc) +: resultIndex(rhs.resultIndex, alloc) {} SceneCulling::SceneCulling(const allocator_type& alloc) noexcept -: sceneQueries(alloc), - culledResults(alloc), +: frustumCullings(alloc), + frustumCullingResults(alloc), + lightBoundsCullings(alloc), + lightBoundsCullingResults(alloc), renderQueues(alloc), - sceneQueryIndex(alloc) {} + renderQueueIndex(alloc) {} SceneCulling::SceneCulling(SceneCulling&& rhs, const allocator_type& alloc) -: sceneQueries(std::move(rhs.sceneQueries), alloc), - culledResults(std::move(rhs.culledResults), 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), - sceneQueryIndex(std::move(rhs.sceneQueryIndex), alloc), - numCullingQueries(rhs.numCullingQueries), + renderQueueIndex(std::move(rhs.renderQueueIndex), alloc), + numFrustumCulling(rhs.numFrustumCulling), + numLightBoundsCulling(rhs.numLightBoundsCulling), numRenderQueues(rhs.numRenderQueues), gpuCullingPassID(rhs.gpuCullingPassID) {} +LightResource::LightResource(const allocator_type& alloc) noexcept +: cpuBuffer(alloc), + lights(alloc), + lightIndex(alloc) {} + NativeRenderContext::NativeRenderContext(std::unique_ptr defaultResourceIn, const allocator_type& alloc) noexcept : defaultResource(std::move(defaultResourceIn)), resourceGroups(alloc), layoutGraphResources(alloc), renderSceneResources(alloc), - sceneCulling(alloc) {} + sceneCulling(alloc), + lightResources(alloc) {} NativeProgramLibrary::NativeProgramLibrary(const allocator_type& alloc) noexcept : layoutGraph(alloc), diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h index 2f5704b28f8..c6c1cbb9c1f 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h +++ b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h @@ -33,6 +33,8 @@ #include "cocos/base/Ptr.h" #include "cocos/base/std/container/string.h" #include "cocos/base/std/hash/hash.h" +#include "cocos/core/geometry/AABB.h" +#include "cocos/core/geometry/Frustum.h" #include "cocos/renderer/gfx-base/GFXFramebuffer.h" #include "cocos/renderer/gfx-base/GFXRenderPass.h" #include "cocos/renderer/pipeline/GlobalDescriptorSetManager.h" @@ -41,6 +43,7 @@ #include "cocos/renderer/pipeline/custom/NativeTypes.h" #include "cocos/renderer/pipeline/custom/details/Map.h" #include "cocos/renderer/pipeline/custom/details/Set.h" +#include "cocos/scene/ReflectionProbe.h" #ifdef _MSC_VER #pragma warning(push) @@ -93,8 +96,8 @@ class NativeSetter : public NativeRenderNode { void setBuiltinSpotLightConstants(const scene::SpotLight *light, const scene::Camera *camera) /*implements*/; void setBuiltinPointLightConstants(const scene::PointLight *light, const scene::Camera *camera) /*implements*/; void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) /*implements*/; - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) /*implements*/; - void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) /*implements*/; + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) /*implements*/; + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) /*implements*/; void setVec4ArraySize(const ccstd::string& name, uint32_t sz); void setVec4ArrayElem(const ccstd::string& name, const cc::Vec4& vec, uint32_t id); @@ -106,6 +109,88 @@ class NativeSetter : public NativeRenderNode { uint32_t layoutID{LayoutGraphData::null_vertex()}; }; +class NativeSceneBuilder final : public SceneBuilder, public NativeSetter { +public: + NativeSceneBuilder(const PipelineRuntime* pipelineRuntimeIn, RenderGraph* renderGraphIn, uint32_t nodeIDIn, const LayoutGraphData* layoutGraphIn, uint32_t layoutIDIn) noexcept + : NativeSetter(pipelineRuntimeIn, renderGraphIn, nodeIDIn, layoutGraphIn, layoutIDIn) {} + + ccstd::string getName() const override { + return NativeRenderNode::getName(); + } + void setName(const ccstd::string &name) override { + NativeRenderNode::setName(name); + } + void setCustomBehavior(const ccstd::string &name) override { + NativeRenderNode::setCustomBehavior(name); + } + + void setMat4(const ccstd::string &name, const Mat4 &mat) override { + NativeSetter::setMat4(name, mat); + } + void setQuaternion(const ccstd::string &name, const Quaternion &quat) override { + NativeSetter::setQuaternion(name, quat); + } + void setColor(const ccstd::string &name, const gfx::Color &color) override { + NativeSetter::setColor(name, color); + } + void setVec4(const ccstd::string &name, const Vec4 &vec) override { + NativeSetter::setVec4(name, vec); + } + void setVec2(const ccstd::string &name, const Vec2 &vec) override { + NativeSetter::setVec2(name, vec); + } + void setFloat(const ccstd::string &name, float v) override { + NativeSetter::setFloat(name, v); + } + void setArrayBuffer(const ccstd::string &name, const ArrayBuffer *arrayBuffer) override { + NativeSetter::setArrayBuffer(name, arrayBuffer); + } + void setBuffer(const ccstd::string &name, gfx::Buffer *buffer) override { + NativeSetter::setBuffer(name, buffer); + } + void setTexture(const ccstd::string &name, gfx::Texture *texture) override { + NativeSetter::setTexture(name, texture); + } + void setReadWriteBuffer(const ccstd::string &name, gfx::Buffer *buffer) override { + NativeSetter::setReadWriteBuffer(name, buffer); + } + void setReadWriteTexture(const ccstd::string &name, gfx::Texture *texture) override { + NativeSetter::setReadWriteTexture(name, texture); + } + void setSampler(const ccstd::string &name, gfx::Sampler *sampler) override { + NativeSetter::setSampler(name, sampler); + } + void setBuiltinCameraConstants(const scene::Camera *camera) override { + NativeSetter::setBuiltinCameraConstants(camera); + } + void setBuiltinShadowMapConstants(const scene::DirectionalLight *light) override { + NativeSetter::setBuiltinShadowMapConstants(light); + } + void setBuiltinDirectionalLightConstants(const scene::DirectionalLight *light, const scene::Camera *camera) override { + NativeSetter::setBuiltinDirectionalLightConstants(light, camera); + } + void setBuiltinSphereLightConstants(const scene::SphereLight *light, const scene::Camera *camera) override { + NativeSetter::setBuiltinSphereLightConstants(light, camera); + } + void setBuiltinSpotLightConstants(const scene::SpotLight *light, const scene::Camera *camera) override { + NativeSetter::setBuiltinSpotLightConstants(light, camera); + } + void setBuiltinPointLightConstants(const scene::PointLight *light, const scene::Camera *camera) override { + NativeSetter::setBuiltinPointLightConstants(light, camera); + } + void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) override { + NativeSetter::setBuiltinRangedDirectionalLightConstants(light, camera); + } + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) override { + NativeSetter::setBuiltinDirectionalLightFrustumConstants(camera, light, csmLevel); + } + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) override { + NativeSetter::setBuiltinSpotLightFrustumConstants(light); + } + + void useLightFrustum(IntrusivePtr light, uint32_t csmLevel, const scene::Camera *optCamera) override; +}; + class NativeRenderSubpassBuilderImpl : public NativeSetter { public: NativeRenderSubpassBuilderImpl(const PipelineRuntime* pipelineRuntimeIn, RenderGraph* renderGraphIn, uint32_t nodeIDIn, const LayoutGraphData* layoutGraphIn, uint32_t layoutIDIn) @@ -195,17 +280,15 @@ class NativeRenderQueueBuilder final : public RenderQueueBuilder, public NativeS void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) override { NativeSetter::setBuiltinRangedDirectionalLightConstants(light, camera); } - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) override { - NativeSetter::setBuiltinDirectionalLightViewConstants(light, level); + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) override { + NativeSetter::setBuiltinDirectionalLightFrustumConstants(camera, light, csmLevel); } - void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) override { - NativeSetter::setBuiltinSpotLightViewConstants(light); + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) override { + NativeSetter::setBuiltinSpotLightFrustumConstants(light); } void addSceneOfCamera(scene::Camera *camera, LightInfo light, SceneFlags sceneFlags) override; - void addScene(const scene::Camera *camera, SceneFlags sceneFlags, const scene::Light *light) override; - void addSceneCulledByDirectionalLight(const scene::Camera *camera, SceneFlags sceneFlags, scene::DirectionalLight *light, uint32_t level) override; - void addSceneCulledBySpotLight(const scene::Camera *camera, SceneFlags sceneFlags, scene::SpotLight *light) override; + SceneBuilder *addScene(const scene::Camera *camera, SceneFlags sceneFlags, scene::Light *light) override; void addFullscreenQuad(Material *material, uint32_t passID, SceneFlags sceneFlags) override; void addCameraQuad(scene::Camera *camera, Material *material, uint32_t passID, SceneFlags sceneFlags) override; void clearRenderTarget(const ccstd::string &name, const gfx::Color &color) override; @@ -285,11 +368,11 @@ class NativeRenderSubpassBuilder final : public RenderSubpassBuilder, public Nat void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) override { NativeSetter::setBuiltinRangedDirectionalLightConstants(light, camera); } - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) override { - NativeSetter::setBuiltinDirectionalLightViewConstants(light, level); + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) override { + NativeSetter::setBuiltinDirectionalLightFrustumConstants(camera, light, csmLevel); } - void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) override { - NativeSetter::setBuiltinSpotLightViewConstants(light); + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) override { + NativeSetter::setBuiltinSpotLightFrustumConstants(light); } void addRenderTarget(const ccstd::string &name, AccessType accessType, const ccstd::string &slotName, gfx::LoadOp loadOp, gfx::StoreOp storeOp, const gfx::Color &color) override { @@ -396,11 +479,11 @@ class NativeMultisampleRenderSubpassBuilder final : public MultisampleRenderSubp void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) override { NativeSetter::setBuiltinRangedDirectionalLightConstants(light, camera); } - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) override { - NativeSetter::setBuiltinDirectionalLightViewConstants(light, level); + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) override { + NativeSetter::setBuiltinDirectionalLightFrustumConstants(camera, light, csmLevel); } - void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) override { - NativeSetter::setBuiltinSpotLightViewConstants(light); + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) override { + NativeSetter::setBuiltinSpotLightFrustumConstants(light); } void addRenderTarget(const ccstd::string &name, AccessType accessType, const ccstd::string &slotName, gfx::LoadOp loadOp, gfx::StoreOp storeOp, const gfx::Color &color) override { @@ -510,11 +593,11 @@ class NativeComputeSubpassBuilder final : public ComputeSubpassBuilder, public N void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) override { NativeSetter::setBuiltinRangedDirectionalLightConstants(light, camera); } - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) override { - NativeSetter::setBuiltinDirectionalLightViewConstants(light, level); + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) override { + NativeSetter::setBuiltinDirectionalLightFrustumConstants(camera, light, csmLevel); } - void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) override { - NativeSetter::setBuiltinSpotLightViewConstants(light); + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) override { + NativeSetter::setBuiltinSpotLightFrustumConstants(light); } void addRenderTarget(const ccstd::string &name, const ccstd::string &slotName) override; @@ -597,11 +680,11 @@ class NativeRenderPassBuilder final : public RenderPassBuilder, public NativeSet void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) override { NativeSetter::setBuiltinRangedDirectionalLightConstants(light, camera); } - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) override { - NativeSetter::setBuiltinDirectionalLightViewConstants(light, level); + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) override { + NativeSetter::setBuiltinDirectionalLightFrustumConstants(camera, light, csmLevel); } - void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) override { - NativeSetter::setBuiltinSpotLightViewConstants(light); + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) override { + NativeSetter::setBuiltinSpotLightFrustumConstants(light); } void addRenderTarget(const ccstd::string &name, gfx::LoadOp loadOp, gfx::StoreOp storeOp, const gfx::Color &color) override; @@ -696,11 +779,11 @@ class NativeMultisampleRenderPassBuilder final : public MultisampleRenderPassBui void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) override { NativeSetter::setBuiltinRangedDirectionalLightConstants(light, camera); } - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) override { - NativeSetter::setBuiltinDirectionalLightViewConstants(light, level); + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) override { + NativeSetter::setBuiltinDirectionalLightFrustumConstants(camera, light, csmLevel); } - void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) override { - NativeSetter::setBuiltinSpotLightViewConstants(light); + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) override { + NativeSetter::setBuiltinSpotLightFrustumConstants(light); } void addRenderTarget(const ccstd::string &name, gfx::LoadOp loadOp, gfx::StoreOp storeOp, const gfx::Color &color) override; @@ -794,11 +877,11 @@ class NativeComputeQueueBuilder final : public ComputeQueueBuilder, public Nativ void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) override { NativeSetter::setBuiltinRangedDirectionalLightConstants(light, camera); } - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) override { - NativeSetter::setBuiltinDirectionalLightViewConstants(light, level); + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) override { + NativeSetter::setBuiltinDirectionalLightFrustumConstants(camera, light, csmLevel); } - void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) override { - NativeSetter::setBuiltinSpotLightViewConstants(light); + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) override { + NativeSetter::setBuiltinSpotLightFrustumConstants(light); } void addDispatch(uint32_t threadGroupCountX, uint32_t threadGroupCountY, uint32_t threadGroupCountZ, Material *material, uint32_t passID) override; @@ -876,11 +959,11 @@ class NativeComputePassBuilder final : public ComputePassBuilder, public NativeS void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) override { NativeSetter::setBuiltinRangedDirectionalLightConstants(light, camera); } - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) override { - NativeSetter::setBuiltinDirectionalLightViewConstants(light, level); + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) override { + NativeSetter::setBuiltinDirectionalLightFrustumConstants(camera, light, csmLevel); } - void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) override { - NativeSetter::setBuiltinSpotLightViewConstants(light); + void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) override { + NativeSetter::setBuiltinSpotLightFrustumConstants(light); } void addTexture(const ccstd::string &name, const ccstd::string &slotName, gfx::Sampler *sampler, uint32_t plane) override; @@ -912,9 +995,9 @@ struct RenderInstancingQueue { void sort(); void uploadBuffers(gfx::CommandBuffer *cmdBuffer) const; void recordCommandBuffer( - gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuffer, - gfx::DescriptorSet *ds = nullptr, uint32_t offset = 0, - const ccstd::vector *dynamicOffsets = nullptr) const; + gfx::RenderPass *renderPass, uint32_t subpassIndex, + gfx::CommandBuffer *cmdBuffer, + uint32_t lightByteOffset = 0xFFFFFFFF) const; ccstd::pmr::vector sortedBatches; PmrUnorderedMap passInstances; @@ -930,6 +1013,36 @@ struct DrawInstance { uint32_t passIndex{0}; }; +struct ProbeHelperQueue { + using allocator_type = boost::container::pmr::polymorphic_allocator; + allocator_type get_allocator() const noexcept { // NOLINT + return {probeMap.get_allocator().resource()}; + } + + ProbeHelperQueue(const allocator_type& alloc) noexcept; // NOLINT + ProbeHelperQueue(ProbeHelperQueue&& rhs, const allocator_type& alloc); + ProbeHelperQueue(ProbeHelperQueue const& rhs, const allocator_type& alloc); + + ProbeHelperQueue(ProbeHelperQueue&& rhs) noexcept = default; + ProbeHelperQueue(ProbeHelperQueue const& rhs) = delete; + ProbeHelperQueue& operator=(ProbeHelperQueue&& rhs) = default; + ProbeHelperQueue& operator=(ProbeHelperQueue const& rhs) = default; + + static LayoutGraphData::vertex_descriptor getDefaultId(const LayoutGraphData &lg); + + inline void clear() noexcept { + probeMap.clear(); + } + + void removeMacro() const; + + static uint32_t getPassIndexFromLayout(const IntrusivePtr& subModel, LayoutGraphData::vertex_descriptor phaseLayoutId); + + void applyMacro(const LayoutGraphData &lg, const scene::Model& model, LayoutGraphData::vertex_descriptor probeLayoutId); + + ccstd::pmr::vector probeMap; +}; + struct RenderDrawQueue { using allocator_type = boost::container::pmr::polymorphic_allocator; allocator_type get_allocator() const noexcept { // NOLINT @@ -948,9 +1061,10 @@ struct RenderDrawQueue { void add(const scene::Model& model, float depth, uint32_t subModelIdx, uint32_t passIdx); void sortOpaqueOrCutout(); void sortTransparent(); - void recordCommandBuffer(gfx::Device *device, const scene::Camera *camera, - gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuffer, - uint32_t subpassIndex) const; + void recordCommandBuffer( + gfx::RenderPass *renderPass, uint32_t subpassIndex, + gfx::CommandBuffer *cmdBuffer, + uint32_t lightByteOffset = 0xFFFFFFFF) const; ccstd::pmr::vector instances; }; @@ -973,13 +1087,17 @@ struct NativeRenderQueue { void sort(); void clear() noexcept; bool empty() const noexcept; + void recordCommands( + gfx::CommandBuffer *cmdBuffer, gfx::RenderPass *renderPass, uint32_t subpassIndex) const; RenderDrawQueue opaqueQueue; RenderDrawQueue transparentQueue; + ProbeHelperQueue probeQueue; RenderInstancingQueue opaqueInstancingQueue; RenderInstancingQueue transparentInstancingQueue; SceneFlags sceneFlags{SceneFlags::NONE}; uint32_t subpassOrPassLayoutID{0xFFFFFFFF}; + uint32_t lightByteOffset{0xFFFFFFFF}; }; struct ResourceGroup { @@ -1105,7 +1223,7 @@ struct LayoutGraphNodeResource { LayoutGraphNodeResource& operator=(LayoutGraphNodeResource const& rhs) = delete; void syncResources() noexcept; - PmrFlatMap uniformBuffers; + ccstd::pmr::unordered_map uniformBuffers; DescriptorSetPool descriptorSetPool; PmrTransparentMap programResources; }; @@ -1146,56 +1264,124 @@ struct SceneResource { ccstd::pmr::unordered_map> storageImages; }; -struct CullingKey { +struct FrustumCullingKey { const scene::Camera* camera{nullptr}; + const scene::ReflectionProbe* probe{nullptr}; const scene::Light* light{nullptr}; - bool castShadow{false}; uint32_t lightLevel{0xFFFFFFFF}; + bool castShadow{false}; +}; + +inline bool operator==(const FrustumCullingKey& lhs, const FrustumCullingKey& rhs) noexcept { + return std::forward_as_tuple(lhs.camera, lhs.probe, lhs.light, lhs.lightLevel, lhs.castShadow) == + std::forward_as_tuple(rhs.camera, rhs.probe, rhs.light, rhs.lightLevel, rhs.castShadow); +} + +inline bool operator!=(const FrustumCullingKey& lhs, const FrustumCullingKey& rhs) noexcept { + return !(lhs == rhs); +} + +struct FrustumCullingID { + explicit operator uint32_t() const { + return value; + } + + uint32_t value{0xFFFFFFFF}; +}; + +inline bool operator==(const FrustumCullingID& lhs, const FrustumCullingID& rhs) noexcept { + return std::forward_as_tuple(lhs.value) == + std::forward_as_tuple(rhs.value); +} + +inline bool operator!=(const FrustumCullingID& lhs, const FrustumCullingID& rhs) noexcept { + return !(lhs == rhs); +} + +struct FrustumCulling { + using allocator_type = boost::container::pmr::polymorphic_allocator; + allocator_type get_allocator() const noexcept { // NOLINT + return {resultIndex.get_allocator().resource()}; + } + + FrustumCulling(const allocator_type& alloc) noexcept; // NOLINT + FrustumCulling(FrustumCulling&& rhs, const allocator_type& alloc); + FrustumCulling(FrustumCulling const& rhs, const allocator_type& alloc); + + FrustumCulling(FrustumCulling&& rhs) noexcept = default; + FrustumCulling(FrustumCulling const& rhs) = delete; + FrustumCulling& operator=(FrustumCulling&& rhs) = default; + FrustumCulling& operator=(FrustumCulling const& rhs) = default; + + ccstd::pmr::unordered_map resultIndex; }; -inline bool operator==(const CullingKey& lhs, const CullingKey& rhs) noexcept { - return std::forward_as_tuple(lhs.camera, lhs.light, lhs.castShadow, lhs.lightLevel) == - std::forward_as_tuple(rhs.camera, rhs.light, rhs.castShadow, rhs.lightLevel); +struct LightBoundsCullingID { + explicit operator uint32_t() const { + return value; + } + + uint32_t value{0xFFFFFFFF}; +}; + +struct LightBoundsCullingKey { + FrustumCullingID frustumCullingID; + const scene::Camera* camera{nullptr}; + const scene::ReflectionProbe* probe{nullptr}; + const scene::Light* cullingLight{nullptr}; +}; + +inline bool operator==(const LightBoundsCullingKey& lhs, const LightBoundsCullingKey& rhs) noexcept { + return std::forward_as_tuple(lhs.frustumCullingID, lhs.camera, lhs.probe, lhs.cullingLight) == + std::forward_as_tuple(rhs.frustumCullingID, rhs.camera, rhs.probe, rhs.cullingLight); } -inline bool operator!=(const CullingKey& lhs, const CullingKey& rhs) noexcept { +inline bool operator!=(const LightBoundsCullingKey& lhs, const LightBoundsCullingKey& rhs) noexcept { return !(lhs == rhs); } -struct CullingQueries { +struct LightBoundsCulling { using allocator_type = boost::container::pmr::polymorphic_allocator; allocator_type get_allocator() const noexcept { // NOLINT - return {culledResultIndex.get_allocator().resource()}; + return {resultIndex.get_allocator().resource()}; } - CullingQueries(const allocator_type& alloc) noexcept; // NOLINT - CullingQueries(CullingQueries&& rhs, const allocator_type& alloc); - CullingQueries(CullingQueries const& rhs, const allocator_type& alloc); + LightBoundsCulling(const allocator_type& alloc) noexcept; // NOLINT + LightBoundsCulling(LightBoundsCulling&& rhs, const allocator_type& alloc); + LightBoundsCulling(LightBoundsCulling const& rhs, const allocator_type& alloc); - CullingQueries(CullingQueries&& rhs) noexcept = default; - CullingQueries(CullingQueries const& rhs) = delete; - CullingQueries& operator=(CullingQueries&& rhs) = default; - CullingQueries& operator=(CullingQueries const& rhs) = default; + LightBoundsCulling(LightBoundsCulling&& rhs) noexcept = default; + LightBoundsCulling(LightBoundsCulling const& rhs) = delete; + LightBoundsCulling& operator=(LightBoundsCulling&& rhs) = default; + LightBoundsCulling& operator=(LightBoundsCulling const& rhs) = default; - ccstd::pmr::unordered_map culledResultIndex; + ccstd::pmr::unordered_map resultIndex; +}; + +struct NativeRenderQueueID { + explicit operator uint32_t() const { + return value; + } + + uint32_t value{0xFFFFFFFF}; }; struct NativeRenderQueueDesc { - NativeRenderQueueDesc() = default; - NativeRenderQueueDesc(uint32_t culledSourceIn, uint32_t renderQueueTargetIn, scene::LightType lightTypeIn) noexcept // NOLINT - : culledSource(culledSourceIn), - renderQueueTarget(renderQueueTargetIn), - lightType(lightTypeIn) {} - - uint32_t culledSource{0xFFFFFFFF}; - uint32_t renderQueueTarget{0xFFFFFFFF}; + FrustumCullingID frustumCulledResultID; + LightBoundsCullingID lightBoundsCulledResultID; + NativeRenderQueueID renderQueueTarget; scene::LightType lightType{scene::LightType::UNKNOWN}; }; +struct LightBoundsCullingResult { + ccstd::vector instances; + uint32_t lightByteOffset{0xFFFFFFFF}; +}; + struct SceneCulling { using allocator_type = boost::container::pmr::polymorphic_allocator; allocator_type get_allocator() const noexcept { // NOLINT - return {sceneQueries.get_allocator().resource()}; + return {frustumCullings.get_allocator().resource()}; } SceneCulling(const allocator_type& alloc) noexcept; // NOLINT @@ -1207,23 +1393,64 @@ struct SceneCulling { SceneCulling& operator=(SceneCulling const& rhs) = delete; void clear() noexcept; - void buildRenderQueues(const RenderGraph& rg, const LayoutGraphData& lg, const pipeline::PipelineSceneData& pplSceneData); + void buildRenderQueues(const RenderGraph& rg, const LayoutGraphData& lg, const NativePipeline& ppl); private: - uint32_t getOrCreateSceneCullingQuery(const SceneData& sceneData); - uint32_t createRenderQueue(SceneFlags sceneFlags, LayoutGraphData::vertex_descriptor subpassOrPassLayoutID); + 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); - void batchCulling(const pipeline::PipelineSceneData& pplSceneData); + void batchFrustumCulling(const NativePipeline& ppl); + void batchLightBoundsCulling(); void fillRenderQueues(const RenderGraph& rg, const pipeline::PipelineSceneData& pplSceneData); public: - ccstd::pmr::unordered_map sceneQueries; - ccstd::pmr::vector> culledResults; + ccstd::pmr::unordered_map frustumCullings; + ccstd::pmr::vector> frustumCullingResults; + ccstd::pmr::unordered_map lightBoundsCullings; + ccstd::pmr::vector lightBoundsCullingResults; ccstd::pmr::vector renderQueues; - PmrFlatMap sceneQueryIndex; - uint32_t numCullingQueries{0}; + PmrFlatMap renderQueueIndex; + uint32_t numFrustumCulling{0}; + uint32_t numLightBoundsCulling{0}; uint32_t numRenderQueues{0}; uint32_t gpuCullingPassID{0xFFFFFFFF}; }; +struct LightResource { + using allocator_type = boost::container::pmr::polymorphic_allocator; + allocator_type get_allocator() const noexcept { // NOLINT + return {cpuBuffer.get_allocator().resource()}; + } + + LightResource(const allocator_type& alloc) noexcept; // NOLINT + LightResource(LightResource&& rhs) = delete; + LightResource(LightResource const& rhs) = delete; + LightResource& operator=(LightResource&& rhs) = delete; + LightResource& operator=(LightResource const& rhs) = delete; + + void init(const NativeProgramLibrary& programLib, gfx::Device* deviceIn, uint32_t maxNumLights); + void buildLights( + SceneCulling& sceneCulling, + bool bHDR, + const scene::Shadows* shadowInfo); + void tryUpdateRenderSceneLocalDescriptorSet(const SceneCulling& sceneCulling); + void clear(); + + uint32_t addLight(const scene::Light* light, bool bHDR, float exposure, const scene::Shadows *shadowInfo); + void buildLightBuffer(gfx::CommandBuffer* cmdBuffer) const; + + ccstd::pmr::vector cpuBuffer; + const NativeProgramLibrary* programLibrary{nullptr}; + gfx::Device* device{nullptr}; + uint32_t elementSize{0}; + uint32_t maxNumLights{16}; + uint32_t binding{0xFFFFFFFF}; + bool resized{false}; + IntrusivePtr lightBuffer; + IntrusivePtr firstLightBufferView; + ccstd::pmr::vector lights; + PmrFlatMap lightIndex; +}; + struct NativeRenderContext { using allocator_type = boost::container::pmr::polymorphic_allocator; allocator_type get_allocator() const noexcept { // NOLINT @@ -1245,6 +1472,7 @@ struct NativeRenderContext { ccstd::pmr::unordered_map renderSceneResources; QuadResource fullscreenQuad; SceneCulling sceneCulling; + LightResource lightResources; }; class NativeProgramLibrary final : public ProgramLibrary { @@ -1314,6 +1542,36 @@ struct PipelineCustomization { PmrTransparentMap> renderCommands; }; +struct BuiltinShadowTransform { + Mat4 shadowView; + Mat4 shadowProj; + Mat4 shadowViewProj; + geometry::Frustum validFrustum; + geometry::Frustum splitFrustum; + geometry::Frustum lightViewFrustum; + geometry::AABB castLightViewBoundingBox; + float shadowCameraFar{0}; + float splitCameraNear{0}; + float splitCameraFar{0}; + Vec4 csmAtlas; +}; + +struct BuiltinCascadedShadowMapKey { + const scene::Camera* camera{nullptr}; + const scene::DirectionalLight* light{nullptr}; +}; + +inline bool operator<(const BuiltinCascadedShadowMapKey& lhs, const BuiltinCascadedShadowMapKey& rhs) noexcept { + return std::forward_as_tuple(lhs.camera, lhs.light) < + std::forward_as_tuple(rhs.camera, rhs.light); +} + +struct BuiltinCascadedShadowMap { + Array4 layers; + BuiltinShadowTransform specialLayer; + float shadowDistance{0}; +}; + class NativePipeline final : public Pipeline { public: using allocator_type = boost::container::pmr::polymorphic_allocator; @@ -1357,18 +1615,18 @@ class NativePipeline final : public Pipeline { void beginSetup() override; void endSetup() override; bool containsResource(const ccstd::string &name) const override; - uint32_t addExternalTexture(const ccstd::string &name, gfx::Texture *texture, ResourceFlags flags) override; - void updateExternalTexture(const ccstd::string &name, gfx::Texture *texture) override; uint32_t addRenderWindow(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, scene::RenderWindow *renderWindow) override; void updateRenderWindow(const ccstd::string &name, scene::RenderWindow *renderWindow) override; uint32_t addRenderTarget(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, ResourceResidency residency) override; uint32_t addDepthStencil(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, ResourceResidency residency) override; void updateRenderTarget(const ccstd::string &name, uint32_t width, uint32_t height, gfx::Format format) override; void updateDepthStencil(const ccstd::string &name, uint32_t width, uint32_t height, gfx::Format format) override; - uint32_t addTexture(const ccstd::string &name, gfx::TextureType type, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount, ResourceFlags flags, ResourceResidency residency) override; - void updateTexture(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount) override; uint32_t addBuffer(const ccstd::string &name, uint32_t size, ResourceFlags flags, ResourceResidency residency) override; void updateBuffer(const ccstd::string &name, uint32_t size) override; + uint32_t addExternalTexture(const ccstd::string &name, gfx::Texture *texture, ResourceFlags flags) override; + void updateExternalTexture(const ccstd::string &name, gfx::Texture *texture) override; + uint32_t addTexture(const ccstd::string &name, gfx::TextureType type, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount, ResourceFlags flags, ResourceResidency residency) override; + void updateTexture(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount) override; uint32_t addResource(const ccstd::string &name, ResourceDimension dimension, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount, ResourceFlags flags, ResourceResidency residency) override; void updateResource(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount) override; void beginFrame() override; @@ -1376,6 +1634,7 @@ class NativePipeline final : public Pipeline { void endFrame() override; void addResolvePass(const ccstd::vector &resolvePairs) override; void addCopyPass(const ccstd::vector ©Pairs) override; + void addBuiltinReflectionProbePass(const scene::Camera *camera) override; gfx::DescriptorSetLayout *getDescriptorSetLayout(const ccstd::string &shaderName, UpdateFrequency freq) override; uint32_t addStorageBuffer(const ccstd::string &name, gfx::Format format, uint32_t size, ResourceResidency residency) override; @@ -1424,6 +1683,7 @@ class NativePipeline final : public Pipeline { NativeRenderContext nativeContext; ResourceGraph resourceGraph; RenderGraph renderGraph; + mutable PmrFlatMap builtinCSMs; PipelineStatistics statistics; PipelineCustomization custom; }; @@ -1463,12 +1723,28 @@ class NativeRenderingModule final : public RenderingModule { namespace ccstd { -inline hash_t hash::operator()(const cc::render::CullingKey& val) const noexcept { +inline hash_t hash::operator()(const cc::render::FrustumCullingKey& val) const noexcept { hash_t seed = 0; hash_combine(seed, val.camera); + hash_combine(seed, val.probe); hash_combine(seed, val.light); - hash_combine(seed, val.castShadow); hash_combine(seed, val.lightLevel); + hash_combine(seed, val.castShadow); + return seed; +} + +inline hash_t hash::operator()(const cc::render::FrustumCullingID& 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); + hash_combine(seed, val.camera); + hash_combine(seed, val.probe); + hash_combine(seed, val.cullingLight); return seed; } diff --git a/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp b/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp index 44054563cd6..273413f8cac 100644 --- a/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp @@ -50,8 +50,8 @@ void NativeRenderNode::setCustomBehavior(const ccstd::string &name) { // NOLINT( RenderGraph::vertex_descriptor RenderGraph::getPassID(vertex_descriptor nodeID) const { CC_EXPECTS(nodeID != null_vertex()); for (auto parentID = nodeID; - parentID != RenderGraph::null_vertex(); - parentID = parent(nodeID, *this)) { + parentID != RenderGraph::null_vertex(); + parentID = parent(nodeID, *this)) { nodeID = parentID; } CC_ENSURES(nodeID != null_vertex()); @@ -690,10 +690,27 @@ ComputeQueueBuilder *NativeComputeSubpassBuilder::addQueue(const ccstd::string & return new NativeComputeQueueBuilder(pipelineRuntime, renderGraph, queueID, layoutGraph, phaseLayoutID); } +namespace { + +CullingFlags makeCullingFlags(const LightInfo &light) { + auto cullingFlags = CullingFlags::NONE; + if (light.culledByLight) { + cullingFlags |= CullingFlags::LIGHT_FRUSTUM; + } else { + cullingFlags |= CullingFlags::CAMERA_FRUSTUM; + if (light.light) { + cullingFlags |= CullingFlags::LIGHT_BOUNDS; + } + } + return cullingFlags; +} + +} // namespace + void NativeRenderQueueBuilder::addSceneOfCamera( scene::Camera *camera, LightInfo light, SceneFlags sceneFlags) { const auto *pLight = light.light.get(); - SceneData scene(camera->getScene(), camera, sceneFlags, light); + SceneData scene(camera->getScene(), camera, sceneFlags, light, makeCullingFlags(light), light.light); auto sceneID = addVertex2( SceneTag{}, std::forward_as_tuple("Camera"), @@ -712,12 +729,18 @@ void NativeRenderQueueBuilder::addSceneOfCamera( *pipelineRuntime->getPipelineSceneData(), camera->getScene()->getMainLight(), data); + // notice: if light is not directional light, csm will be nullptr + const auto *csm = getBuiltinShadowCSM( + *pipelineRuntime, *camera, + dynamic_cast(pLight)); + if (any(sceneFlags & SceneFlags::SHADOW_CASTER)) { if (pLight) { setShadowUBOLightView( pipelineRuntime->getDevice(), *layoutGraph, *pipelineRuntime->getPipelineSceneData(), + csm, // csm might be nullptr *pLight, light.level, data); } } else { @@ -736,23 +759,108 @@ void NativeRenderQueueBuilder::addSceneOfCamera( data); } -void NativeRenderQueueBuilder::addScene(const scene::Camera *camera, SceneFlags sceneFlags, const scene::Light *light) { - std::ignore = light; - SceneData data(camera->getScene(), camera, sceneFlags, LightInfo{}); +void NativeSceneBuilder::useLightFrustum( + IntrusivePtr light, uint32_t csmLevel, const scene::Camera *optCamera) { + auto &sceneData = get(SceneTag{}, nodeID, *renderGraph); + sceneData.light.light = light; + sceneData.light.level = csmLevel; + sceneData.light.culledByLight = true; + if (optCamera) { + sceneData.camera = optCamera; + } - auto sceneID = addVertex2( + // Disable camera frustum projection + sceneData.cullingFlags &= ~CullingFlags::CAMERA_FRUSTUM; + + // Enable light frustum projection + sceneData.cullingFlags |= CullingFlags::LIGHT_FRUSTUM; + + if (any(sceneData.flags & SceneFlags::NON_BUILTIN)) { + return; + } + + switch (light->getType()) { + case scene::LightType::DIRECTIONAL: { + setBuiltinDirectionalLightFrustumConstants( + sceneData.camera, dynamic_cast(light.get()), csmLevel); + } break; + case scene::LightType::SPOT: { + setBuiltinSpotLightFrustumConstants( + dynamic_cast(light.get())); + } break; + default: + // noop + break; + } +} + +SceneBuilder *NativeRenderQueueBuilder::addScene( + const scene::Camera *camera, SceneFlags sceneFlags, scene::Light *light) { + const auto sceneID = addVertex2( SceneTag{}, std::forward_as_tuple("Scene"), std::forward_as_tuple(), std::forward_as_tuple(), std::forward_as_tuple(), - std::forward_as_tuple(std::move(data)), + std::forward_as_tuple( + camera->getScene(), // Scene and camera should be decoupled. + camera, // They are coupled for now. + sceneFlags, + LightInfo{nullptr, 0}, + // Objects are projected to camera by default and are culled further if light is available. + light ? CullingFlags::CAMERA_FRUSTUM | CullingFlags::LIGHT_BOUNDS + : CullingFlags::CAMERA_FRUSTUM, + light), *renderGraph, nodeID); CC_ENSURES(sceneID != RenderGraph::null_vertex()); + auto builder = std::make_unique( + pipelineRuntime, + renderGraph, + sceneID, + layoutGraph, + layoutID); + + if (!any(sceneFlags & SceneFlags::NON_BUILTIN)) { + // objects are projected to camera, set camera ubo + builder->setBuiltinCameraConstants(camera); + + if (light) { + switch (light->getType()) { + case scene::LightType::DIRECTIONAL: { + const auto *pDirLight = dynamic_cast(light); + builder->setBuiltinDirectionalLightConstants(pDirLight, camera); + } break; + case scene::LightType::SPHERE: { + const auto *pSphereLight = dynamic_cast(light); + builder->setBuiltinSphereLightConstants(pSphereLight, camera); + } break; + case scene::LightType::SPOT: { + const auto *pSpotLight = dynamic_cast(light); + builder->setBuiltinSpotLightConstants(pSpotLight, camera); + } break; + case scene::LightType::POINT: { + const auto *pPointLight = dynamic_cast(light); + builder->setBuiltinPointLightConstants(pPointLight, camera); + } break; + default: + // noop + break; + } + } + + // set builtin legacy ubo + auto &data = get(RenderGraph::DataTag{}, *renderGraph, sceneID); + setLegacyTextureUBOView( + *pipelineRuntime->getDevice(), + *layoutGraph, + *pipelineRuntime->getPipelineSceneData(), + data); + } + if (any(sceneFlags & SceneFlags::GPU_DRIVEN)) { const auto passID = renderGraph->getPassID(nodeID); - const auto cullingID = dynamic_cast(pipelineRuntime)->nativeContext.sceneCulling.gpuCullingPassID; + const auto cullingID = dynamic_cast(pipelineRuntime)->nativeContext.sceneCulling.gpuCullingPassID; CC_EXPECTS(cullingID != 0xFFFFFFFF); if (holds(passID, *renderGraph)) { ccstd::pmr::string drawIndirectBuffer("CCDrawIndirectBuffer"); @@ -760,14 +868,14 @@ void NativeRenderQueueBuilder::addScene(const scene::Camera *camera, SceneFlags ccstd::pmr::string drawInstanceBuffer("CCDrawInstanceBuffer"); drawInstanceBuffer.append(std::to_string(cullingID)); - auto& rasterPass = get(RasterPassTag{}, passID, *renderGraph); + auto &rasterPass = get(RasterPassTag{}, passID, *renderGraph); if (rasterPass.computeViews.find(drawIndirectBuffer) != rasterPass.computeViews.end()) { auto res = rasterPass.computeViews.emplace( std::piecewise_construct, std::forward_as_tuple(drawIndirectBuffer), std::forward_as_tuple()); CC_ENSURES(res.second); - auto& view = res.first->second.emplace_back(); + auto &view = res.first->second.emplace_back(); view.name = "CCDrawIndirectBuffer"; view.accessType = AccessType::READ; view.shaderStageFlags = gfx::ShaderStageFlagBit::VERTEX | gfx::ShaderStageFlagBit::FRAGMENT; @@ -778,49 +886,15 @@ void NativeRenderQueueBuilder::addScene(const scene::Camera *camera, SceneFlags std::forward_as_tuple(drawInstanceBuffer), std::forward_as_tuple()); CC_ENSURES(res.second); - auto& view = res.first->second.emplace_back(); + auto &view = res.first->second.emplace_back(); view.name = "CCDrawInstanceBuffer"; view.accessType = AccessType::READ; view.shaderStageFlags = gfx::ShaderStageFlagBit::VERTEX | gfx::ShaderStageFlagBit::FRAGMENT; } } } -} -void NativeRenderQueueBuilder::addSceneCulledByDirectionalLight( - const scene::Camera *camera, SceneFlags sceneFlags, - scene::DirectionalLight *light, uint32_t level) { - CC_EXPECTS(light); - CC_EXPECTS(light->getType() != scene::LightType::UNKNOWN); - SceneData data(camera->getScene(), camera, sceneFlags, LightInfo{light, level}); - - auto sceneID = addVertex2( - SceneTag{}, - std::forward_as_tuple("Scene"), - std::forward_as_tuple(), - std::forward_as_tuple(), - std::forward_as_tuple(), - std::forward_as_tuple(std::move(data)), - *renderGraph, nodeID); - CC_ENSURES(sceneID != RenderGraph::null_vertex()); -} - -void NativeRenderQueueBuilder::addSceneCulledBySpotLight( - const scene::Camera *camera, SceneFlags sceneFlags, - scene::SpotLight *light) { - CC_EXPECTS(light); - CC_EXPECTS(light->getType() != scene::LightType::UNKNOWN); - SceneData data(camera->getScene(), camera, sceneFlags, LightInfo{light, 0}); - - auto sceneID = addVertex2( - SceneTag{}, - std::forward_as_tuple("Scene"), - std::forward_as_tuple(), - std::forward_as_tuple(), - std::forward_as_tuple(), - std::forward_as_tuple(std::move(data)), - *renderGraph, nodeID); - CC_ENSURES(sceneID != RenderGraph::null_vertex()); + return builder.release(); } void NativeRenderQueueBuilder::addFullscreenQuad( diff --git a/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp b/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp index 3f95918ac34..d4b4f4c9d69 100644 --- a/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp @@ -26,13 +26,83 @@ #include #include "NativePipelineTypes.h" #include "cocos/renderer/pipeline/Define.h" +#include "cocos/renderer/pipeline/InstancedBuffer.h" #include "cocos/renderer/pipeline/PipelineStateManager.h" +#include "cocos/renderer/pipeline/custom/LayoutGraphGraphs.h" #include "cocos/renderer/pipeline/custom/details/GslUtils.h" namespace cc { namespace render { +LayoutGraphData::vertex_descriptor ProbeHelperQueue::getDefaultId(const LayoutGraphData &lg) { + const auto passID = locate(LayoutGraphData::null_vertex(), "default", lg); + CC_ENSURES(passID != LayoutGraphData::null_vertex()); + const auto phaseID = locate(passID, "default", lg); + CC_ENSURES(phaseID != LayoutGraphData::null_vertex()); + return phaseID; +} + +void ProbeHelperQueue::removeMacro() const { + for (auto *subModel : probeMap) { + std::vector patches; + patches.insert(patches.end(), subModel->getPatches().begin(), subModel->getPatches().end()); + + for (int j = 0; j != patches.size(); ++j) { + const cc::scene::IMacroPatch &patch = patches[j]; + if (patch.name == "CC_USE_RGBE_OUTPUT") { + patches.erase(patches.begin() + j); + break; + } + } + + subModel->onMacroPatchesStateChanged(patches); + } +} + +uint32_t ProbeHelperQueue::getPassIndexFromLayout( + const cc::IntrusivePtr &subModel, + LayoutGraphData::vertex_descriptor phaseLayoutId) { + const auto &passes = subModel->getPasses(); + for (uint32_t k = 0; k != passes->size(); ++k) { + if (passes->at(k)->getPhaseID() == phaseLayoutId) { + return static_cast(k); + } + } + return 0xFFFFFFFF; +} + +void ProbeHelperQueue::applyMacro( + const LayoutGraphData &lg, const cc::scene::Model &model, + LayoutGraphData::vertex_descriptor probeLayoutId) { + const std::vector> &subModels = model.getSubModels(); + for (const auto &subModel : subModels) { + const bool isTransparent = subModel->getPasses()->at(0)->getBlendState()->targets[0].blend; + if (isTransparent) { + continue; + } + + auto passIdx = getPassIndexFromLayout(subModel, probeLayoutId); + bool bUseReflectPass = true; + if (passIdx < 0) { + probeLayoutId = getDefaultId(lg); + passIdx = getPassIndexFromLayout(subModel, probeLayoutId); + bUseReflectPass = false; + } + if (passIdx < 0) { + continue; + } + if (!bUseReflectPass) { + std::vector patches; + patches.insert(patches.end(), subModel->getPatches().begin(), subModel->getPatches().end()); + const cc::scene::IMacroPatch useRGBEPatch = {"CC_USE_RGBE_OUTPUT", true}; + patches.emplace_back(useRGBEPatch); + subModel->onMacroPatchesStateChanged(patches); + probeMap.emplace_back(subModel); + } + } +} + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) void RenderDrawQueue::add(const scene::Model &model, float depth, uint32_t subModelIdx, uint32_t passIdx) { const auto *subModel = model.getSubModels()[subModelIdx].get(); @@ -62,9 +132,9 @@ void RenderDrawQueue::sortTransparent() { } void RenderDrawQueue::recordCommandBuffer( - gfx::Device * /*device*/, const scene::Camera * /*camera*/, - gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, - uint32_t subpassIndex) const { + gfx::RenderPass *renderPass, uint32_t subpassIndex, + gfx::CommandBuffer *cmdBuff, + uint32_t lightByteOffset) const { for (const auto &instance : instances) { const auto *subModel = instance.subModel; @@ -76,7 +146,11 @@ void RenderDrawQueue::recordCommandBuffer( cmdBuff->bindPipelineState(pso); cmdBuff->bindDescriptorSet(pipeline::materialSet, pass->getDescriptorSet()); - cmdBuff->bindDescriptorSet(pipeline::localSet, subModel->getDescriptorSet()); + if (lightByteOffset != 0xFFFFFFFF) { + cmdBuff->bindDescriptorSet(pipeline::localSet, subModel->getDescriptorSet(), 1, &lightByteOffset); + } else { + cmdBuff->bindDescriptorSet(pipeline::localSet, subModel->getDescriptorSet()); + } cmdBuff->bindInputAssembler(inputAssembler); cmdBuff->draw(inputAssembler); } @@ -138,8 +212,9 @@ void RenderInstancingQueue::uploadBuffers(gfx::CommandBuffer *cmdBuffer) const { } void RenderInstancingQueue::recordCommandBuffer( - gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuffer, - gfx::DescriptorSet *ds, uint32_t offset, const ccstd::vector *dynamicOffsets) const { + gfx::RenderPass *renderPass, uint32_t subpassIndex, + gfx::CommandBuffer *cmdBuffer, + uint32_t lightByteOffset) const { // const auto &renderQueue = sortedBatches; for (const auto *instanceBuffer : renderQueue) { if (!instanceBuffer->hasPendingModels()) { @@ -154,16 +229,13 @@ void RenderInstancingQueue::recordCommandBuffer( continue; } auto *pso = pipeline::PipelineStateManager::getOrCreatePipelineState( - drawPass, instance.shader, instance.ia, renderPass); + drawPass, instance.shader, instance.ia, renderPass, subpassIndex); if (lastPSO != pso) { cmdBuffer->bindPipelineState(pso); lastPSO = pso; } - if (ds) { - cmdBuffer->bindDescriptorSet(pipeline::globalSet, ds, 1, &offset); - } - if (dynamicOffsets) { - cmdBuffer->bindDescriptorSet(pipeline::localSet, instance.descriptorSet, *dynamicOffsets); + if (lightByteOffset != 0xFFFFFFFF) { + cmdBuffer->bindDescriptorSet(pipeline::localSet, instance.descriptorSet, 1, &lightByteOffset); } else { cmdBuffer->bindDescriptorSet(pipeline::localSet, instance.descriptorSet, instanceBuffer->dynamicOffsets()); } @@ -180,6 +252,20 @@ void NativeRenderQueue::sort() { transparentInstancingQueue.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); +} + } // namespace render } // namespace cc diff --git a/native/cocos/renderer/pipeline/custom/NativeResourceGraph.cpp b/native/cocos/renderer/pipeline/custom/NativeResourceGraph.cpp index 750616d549a..736283688ab 100644 --- a/native/cocos/renderer/pipeline/custom/NativeResourceGraph.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeResourceGraph.cpp @@ -143,12 +143,12 @@ gfx::TextureInfo getTextureInfo(const ResourceDesc& desc) { }; } -gfx::TextureViewInfo getTextureViewInfo(const SubresourceView& subresView) { +gfx::TextureViewInfo getTextureViewInfo(const SubresourceView& subresView, gfx::TextureType viewType) { using namespace gfx; // NOLINT(google-build-using-namespace) return { nullptr, - subresView.viewType, + viewType, subresView.format, subresView.indexOrFirstMipLevel, subresView.numMipLevels, @@ -207,12 +207,12 @@ void ResourceGraph::mount(gfx::Device* device, vertex_descriptor vertID) { CC_ENSURES(texture.texture); texture.fenceValue = nextFenceValue; }, - [&](const IntrusivePtr& buffer) { - CC_EXPECTS(buffer); + [&](const PersistentBuffer& buffer) { + CC_EXPECTS(buffer.buffer); std::ignore = buffer; }, - [&](const IntrusivePtr& texture) { - CC_EXPECTS(texture); + [&](const PersistentTexture& texture) { + CC_EXPECTS(texture.texture); std::ignore = texture; }, [&](const IntrusivePtr& fb) { @@ -252,7 +252,8 @@ void ResourceGraph::mount(gfx::Device* device, vertex_descriptor vertID) { mount(device, parentID); // NOLINT(misc-no-recursion) auto* parentTexture = resg.getTexture(parentID); if (!view.textureView) { - auto textureViewInfo = getTextureViewInfo(originView); + const auto& desc = get(ResourceGraph::DescTag{}, resg, vertID); + auto textureViewInfo = getTextureViewInfo(originView, desc.viewType); textureViewInfo.texture = parentTexture; view.textureView = device->createTexture(textureViewInfo); } diff --git a/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp b/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp index 0b9a2865259..2b6cf72cf8c 100644 --- a/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp @@ -1,17 +1,26 @@ +#include "cocos/renderer/pipeline/Define.h" +#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/details/GslUtils.h" #include "cocos/renderer/pipeline/custom/details/Range.h" #include "cocos/scene/Octree.h" +#include "cocos/scene/ReflectionProbe.h" #include "cocos/scene/RenderScene.h" #include "cocos/scene/Skybox.h" #include "cocos/scene/SpotLight.h" +#include + namespace cc { namespace render { +const static uint32_t REFLECTION_PROBE_DEFAULT_MASK = ~static_cast(pipeline::LayerList::UI_2D) & ~static_cast(pipeline::LayerList::PROFILER) & ~static_cast(pipeline::LayerList::UI_3D) & ~static_cast(pipeline::LayerList::GIZMOS) & ~static_cast(pipeline::LayerList::SCENE_GIZMO) & ~static_cast(pipeline::LayerList::EDITOR); + void NativeRenderQueue::clear() noexcept { + probeQueue.clear(); opaqueQueue.instances.clear(); transparentQueue.instances.clear(); opaqueInstancingQueue.clear(); @@ -27,43 +36,84 @@ bool NativeRenderQueue::empty() const noexcept { transparentInstancingQueue.empty(); } -uint32_t SceneCulling::getOrCreateSceneCullingQuery(const SceneData& sceneData) { +FrustumCullingID SceneCulling::getOrCreateFrustumCulling(const SceneData& sceneData) { const auto* const scene = sceneData.scene; // get or add scene to queries - auto& queries = sceneQueries[scene]; + auto& queries = frustumCullings[scene]; // check cast shadow const bool bCastShadow = any(sceneData.flags & SceneFlags::SHADOW_CASTER); // get or create query source // make query key - const auto key = CullingKey{ + const auto key = FrustumCullingKey{ sceneData.camera, + sceneData.light.probe, sceneData.light.light, - bCastShadow, sceneData.light.level, + bCastShadow, }; // find query source - auto iter = queries.culledResultIndex.find(key); - if (iter == queries.culledResultIndex.end()) { + auto iter = queries.resultIndex.find(key); + if (iter == queries.resultIndex.end()) { // create query source // make query source id - const auto sourceID = numCullingQueries++; - if (numCullingQueries > culledResults.size()) { + const FrustumCullingID frustomCulledResultID{numFrustumCulling++}; + if (numFrustumCulling > frustumCullingResults.size()) { // space is not enough, create query source - CC_EXPECTS(numCullingQueries == culledResults.size() + 1); - culledResults.emplace_back(); + CC_EXPECTS(numFrustumCulling == frustumCullingResults.size() + 1); + frustumCullingResults.emplace_back(); } // add query source to query index bool added = false; - std::tie(iter, added) = queries.culledResultIndex.emplace(key, sourceID); + std::tie(iter, added) = queries.resultIndex.emplace(key, frustomCulledResultID); CC_ENSURES(added); } return iter->second; } -uint32_t SceneCulling::createRenderQueue( +LightBoundsCullingID SceneCulling::getOrCreateLightBoundsCulling( + const SceneData& sceneData, FrustumCullingID frustumCullingID) { + if (!any(sceneData.cullingFlags & CullingFlags::LIGHT_BOUNDS)) { + return {}; + } + + CC_EXPECTS(sceneData.shadingLight); + const auto* const scene = sceneData.scene; + CC_EXPECTS(scene); + + auto& queries = lightBoundsCullings[scene]; + + // get or create query source + // make query key + const auto key = LightBoundsCullingKey{ + frustumCullingID, + sceneData.camera, + sceneData.light.probe, + sceneData.shadingLight, + }; + + // find query source + auto iter = queries.resultIndex.find(key); + if (iter == queries.resultIndex.end()) { + // create query source + // make query source id + const LightBoundsCullingID lightBoundsCullingID{numLightBoundsCulling++}; + if (numLightBoundsCulling > lightBoundsCullingResults.size()) { + // space is not enough, create query source + CC_EXPECTS(numLightBoundsCulling == lightBoundsCullingResults.size() + 1); + lightBoundsCullingResults.emplace_back(); + } + // add query source to query index + bool added = false; + std::tie(iter, added) = queries.resultIndex.emplace(key, lightBoundsCullingID); + CC_ENSURES(added); + } + return iter->second; +} + +NativeRenderQueueID SceneCulling::createRenderQueue( SceneFlags sceneFlags, LayoutGraphData::vertex_descriptor subpassOrPassLayoutID) { const auto targetID = numRenderQueues++; if (numRenderQueues > renderQueues.size()) { @@ -77,7 +127,7 @@ uint32_t SceneCulling::createRenderQueue( CC_EXPECTS(rq.subpassOrPassLayoutID == 0xFFFFFFFF); rq.sceneFlags = sceneFlags; rq.subpassOrPassLayoutID = subpassOrPassLayoutID; - return targetID; + return NativeRenderQueueID{targetID}; } void SceneCulling::collectCullingQueries( @@ -91,7 +141,8 @@ void SceneCulling::collectCullingQueries( CC_EXPECTS(false); continue; } - const auto sourceID = getOrCreateSceneCullingQuery(sceneData); + 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 @@ -99,12 +150,23 @@ void SceneCulling::collectCullingQueries( : scene::LightType::UNKNOWN; // add render queue to query source - sceneQueryIndex.emplace(vertID, NativeRenderQueueDesc(sourceID, targetID, lightType)); + renderQueueIndex.emplace( + vertID, + NativeRenderQueueDesc{ + frustomCulledResultID, + lightBoundsCullingID, + targetID, + lightType, + }); } } namespace { -const pipeline::PipelineSceneData* pSceneData = nullptr; + +const pipeline::PipelineSceneData* kPipelineSceneData = nullptr; + +const LayoutGraphData* kLayoutGraph = nullptr; + bool isNodeVisible(const Node* node, uint32_t visibility) { return node && ((visibility & node->getLayer()) == node->getLayer()); } @@ -113,46 +175,46 @@ uint32_t isModelVisible(const scene::Model& model, uint32_t visibility) { return visibility & static_cast(model.getVisFlags()); } +bool isReflectProbeMask(const scene::Model& model) { + return ((model.getNode()->getLayer() & REFLECTION_PROBE_DEFAULT_MASK) == model.getNode()->getLayer()) || (REFLECTION_PROBE_DEFAULT_MASK & static_cast(model.getVisFlags())); +} + +bool isIntersectAABB(const geometry::AABB& lAABB, const geometry::AABB& rAABB) { + return !lAABB.aabbAabb(rAABB); +} + bool isFrustumVisible(const scene::Model& model, const geometry::Frustum& frustum, bool castShadow) { const auto* const modelWorldBounds = model.getWorldBounds(); if (!modelWorldBounds) { - return true; + return false; } geometry::AABB transWorldBounds{}; transWorldBounds.set(modelWorldBounds->getCenter(), modelWorldBounds->getHalfExtents()); - const scene::Shadows& shadows = *pSceneData->getShadows(); + const scene::Shadows& shadows = *kPipelineSceneData->getShadows(); if (shadows.getType() == scene::ShadowType::PLANAR && castShadow) { modelWorldBounds->transform(shadows.getMatLight(), &transWorldBounds); } - return transWorldBounds.aabbFrustum(frustum); + return !transWorldBounds.aabbFrustum(frustum); } void octreeCulling( const scene::Octree& octree, - const scene::Model* skyboxModelToSkip, + const scene::Model* skyboxModel, const scene::RenderScene& scene, const scene::Camera& camera, const geometry::Frustum& cameraOrLightFrustum, bool bCastShadow, ccstd::vector& models) { const auto visibility = camera.getVisibility(); + const auto camSkyboxFlag = (static_cast(camera.getClearFlag()) & scene::Camera::SKYBOX_FLAG); + if (!bCastShadow && skyboxModel && camSkyboxFlag) { + models.emplace_back(skyboxModel); + } // add instances without world bounds for (const auto& pModel : scene.getModels()) { CC_EXPECTS(pModel); const auto& model = *pModel; - if (!model.isEnabled()) { - continue; - } - // has world bounds, should be in octree - if (model.getWorldBounds()) { - continue; - } - // is skybox, skip - if (&model == skyboxModelToSkip) { - continue; - } - // check cast shadow - if (bCastShadow && !model.isCastShadow()) { + if (!model.isEnabled() || !model.getNode() || model.getWorldBounds() || (bCastShadow && !model.isCastShadow())) { continue; } // filter model by view visibility @@ -173,113 +235,117 @@ void octreeCulling( } void bruteForceCulling( - const scene::Model* skyboxModelToSkip, + const scene::Model* skyboxModel, const scene::RenderScene& scene, const scene::Camera& camera, const geometry::Frustum& cameraOrLightFrustum, bool bCastShadow, + const scene::ReflectionProbe* probe, ccstd::vector& models) { const auto visibility = camera.getVisibility(); + const auto camSkyboxFlag = (static_cast(camera.getClearFlag()) & scene::Camera::SKYBOX_FLAG); + if (!bCastShadow && skyboxModel && camSkyboxFlag) { + models.emplace_back(skyboxModel); + } for (const auto& pModel : scene.getModels()) { CC_EXPECTS(pModel); const auto& model = *pModel; - if (!model.isEnabled()) { + if (!model.isEnabled() || !model.getNode() || (bCastShadow && !model.isCastShadow())) { continue; } - if (bCastShadow && !model.isCastShadow()) { + // lod culling + if (scene.isCulledByLod(&camera, &model)) { continue; } - // filter model by view visibility - if (isNodeVisible(model.getNode(), visibility) || isModelVisible(model, visibility)) { - // frustum culling - if (!isFrustumVisible(model, cameraOrLightFrustum, bCastShadow)) { - continue; - } - // is skybox, skip - if (&model == skyboxModelToSkip) { - continue; - } - // lod culling - if (scene.isCulledByLod(&camera, &model)) { - continue; + if (!probe || (probe && probe->getProbeType() == cc::scene::ReflectionProbe::ProbeType::CUBE)) { + // filter model by view visibility + if (isNodeVisible(model.getNode(), visibility) || isModelVisible(model, visibility)) { + const auto* const wBounds = model.getWorldBounds(); + // frustum culling + if (wBounds && ((!probe && isFrustumVisible(model, cameraOrLightFrustum, bCastShadow)) || + (probe && isIntersectAABB(*wBounds, *probe->getBoundingBox())))) { + continue; + } + + models.emplace_back(&model); } + } else if (isReflectProbeMask(model)) { models.emplace_back(&model); } } } void sceneCulling( - const scene::Model* skyboxModelToSkip, + const scene::Model* skyboxModel, const scene::RenderScene& scene, const scene::Camera& camera, const geometry::Frustum& cameraOrLightFrustum, bool bCastShadow, + const scene::ReflectionProbe* probe, ccstd::vector& models) { const auto* const octree = scene.getOctree(); - if (octree && octree->isEnabled()) { + if (octree && octree->isEnabled() && !probe) { octreeCulling( - *octree, skyboxModelToSkip, + *octree, skyboxModel, scene, camera, cameraOrLightFrustum, bCastShadow, models); } else { bruteForceCulling( - skyboxModelToSkip, - scene, camera, cameraOrLightFrustum, bCastShadow, models); + skyboxModel, + scene, camera, cameraOrLightFrustum, bCastShadow, probe, models); } } } // namespace -void SceneCulling::batchCulling(const pipeline::PipelineSceneData& pplSceneData) { +void SceneCulling::batchFrustumCulling(const NativePipeline& ppl) { + const auto& pplSceneData = *ppl.getPipelineSceneData(); const auto* const skybox = pplSceneData.getSkybox(); - const auto* const skyboxModelToSkip = skybox ? skybox->getModel() : nullptr; + const auto* const skyboxModel = skybox && skybox->isEnabled() ? skybox->getModel() : nullptr; - for (const auto& [scene, queries] : sceneQueries) { + for (const auto& [scene, queries] : frustumCullings) { CC_ENSURES(scene); - for (const auto& [key, sourceID] : queries.culledResultIndex) { + for (const auto& [key, frustomCulledResultID] : queries.resultIndex) { CC_EXPECTS(key.camera); CC_EXPECTS(key.camera->getScene() == scene); - const auto& camera = *key.camera; const auto* light = key.light; const auto level = key.lightLevel; const auto bCastShadow = key.castShadow; + const auto* probe = key.probe; + const auto& camera = probe ? *probe->getCamera() : *key.camera; + CC_EXPECTS(frustomCulledResultID.value < frustumCullingResults.size()); + auto& models = frustumCullingResults[frustomCulledResultID.value]; - CC_EXPECTS(sourceID < culledResults.size()); - auto& models = culledResults[sourceID]; + if (probe) { + sceneCulling( + skyboxModel, + *scene, camera, + camera.getFrustum(), + bCastShadow, + probe, + models); + continue; + } if (light) { switch (light->getType()) { case scene::LightType::SPOT: sceneCulling( - skyboxModelToSkip, + skyboxModel, *scene, camera, dynamic_cast(light)->getFrustum(), bCastShadow, + nullptr, models); break; case scene::LightType::DIRECTIONAL: { - auto& csmLayers = *pplSceneData.getCSMLayers(); const auto* mainLight = dynamic_cast(light); - const auto& csmLevel = mainLight->getCSMLevel(); - const geometry::Frustum* frustum = nullptr; - const auto& shadows = *pplSceneData.getShadows(); - if (shadows.getType() == scene::ShadowType::PLANAR) { - frustum = &camera.getFrustum(); - } else { - if (shadows.isEnabled() && shadows.getType() == scene::ShadowType::SHADOW_MAP && mainLight && mainLight->getNode()) { - csmLayers.update(&pplSceneData, &camera); - } - // const - if (mainLight->isShadowFixedArea() || csmLevel == scene::CSMLevel::LEVEL_1) { - frustum = &csmLayers.getSpecialLayer()->getValidFrustum(); - } else { - frustum = &csmLayers.getLayers()[level]->getValidFrustum(); - } - } + const auto& frustum = getBuiltinShadowFrustum(ppl, camera, mainLight, level); sceneCulling( - skyboxModelToSkip, + skyboxModel, *scene, camera, - *frustum, + frustum, bCastShadow, + nullptr, models); } break; default: @@ -288,10 +354,11 @@ void SceneCulling::batchCulling(const pipeline::PipelineSceneData& pplSceneData) } } else { sceneCulling( - skyboxModelToSkip, + skyboxModel, *scene, camera, camera.getFrustum(), bCastShadow, + nullptr, models); } } @@ -299,6 +366,112 @@ void SceneCulling::batchCulling(const pipeline::PipelineSceneData& pplSceneData) } namespace { + +void executeSphereLightCulling( + const scene::SphereLight& light, + const ccstd::vector& frustumCullingResult, + ccstd::vector& lightBoundsCullingResult) { + const auto& lightAABB = light.getAABB(); + for (const auto* const model : frustumCullingResult) { + CC_EXPECTS(model); + const auto* const modelBounds = model->getWorldBounds(); + if (!modelBounds || modelBounds->aabbAabb(lightAABB)) { + lightBoundsCullingResult.emplace_back(model); + } + } +} + +void executeSpotLightCulling( + const scene::SpotLight& light, + const ccstd::vector& frustumCullingResult, + ccstd::vector& lightBoundsCullingResult) { + const auto& lightAABB = light.getAABB(); + const auto& lightFrustum = light.getFrustum(); + for (const auto* const model : frustumCullingResult) { + CC_EXPECTS(model); + const auto* const modelBounds = model->getWorldBounds(); + if (!modelBounds || (modelBounds->aabbAabb(lightAABB) && modelBounds->aabbFrustum(lightFrustum))) { + lightBoundsCullingResult.emplace_back(model); + } + } +} + +void executePointLightCulling( + const scene::PointLight& light, + const ccstd::vector& frustumCullingResult, + ccstd::vector& lightBoundsCullingResult) { + const auto& lightAABB = light.getAABB(); + for (const auto* const model : frustumCullingResult) { + CC_EXPECTS(model); + const auto* const modelBounds = model->getWorldBounds(); + if (!modelBounds || modelBounds->aabbAabb(lightAABB)) { + lightBoundsCullingResult.emplace_back(model); + } + } +} + +void executeRangedDirectionalLightCulling( + const scene::RangedDirectionalLight& light, + const ccstd::vector& frustumCullingResult, + ccstd::vector& lightBoundsCullingResult) { + const geometry::AABB rangedDirLightBoundingBox(0.0F, 0.0F, 0.0F, 0.5F, 0.5F, 0.5F); + // when execute render graph, we should never update world matrix + // light->getNode()->updateWorldTransform(); + geometry::AABB lightAABB{}; + rangedDirLightBoundingBox.transform(light.getNode()->getWorldMatrix(), &lightAABB); + for (const auto* const model : frustumCullingResult) { + CC_EXPECTS(model); + const auto* const modelBounds = model->getWorldBounds(); + if (!modelBounds || modelBounds->aabbAabb(lightAABB)) { + lightBoundsCullingResult.emplace_back(model); + } + } +} + +} // namespace + +void SceneCulling::batchLightBoundsCulling() { + for (const auto& [scene, queries] : lightBoundsCullings) { + CC_ENSURES(scene); + for (const auto& [key, cullingID] : queries.resultIndex) { + CC_EXPECTS(key.camera); + CC_EXPECTS(key.camera->getScene() == scene); + const auto& frustumCullingResult = frustumCullingResults.at(key.frustumCullingID.value); + auto& lightBoundsCullingResult = lightBoundsCullingResults.at(cullingID.value); + CC_EXPECTS(lightBoundsCullingResult.instances.empty()); + switch (key.cullingLight->getType()) { + case scene::LightType::SPHERE: { + const auto* light = dynamic_cast(key.cullingLight); + CC_ENSURES(light); + executeSphereLightCulling(*light, frustumCullingResult, lightBoundsCullingResult.instances); + } break; + case scene::LightType::SPOT: { + const auto* light = dynamic_cast(key.cullingLight); + CC_ENSURES(light); + executeSpotLightCulling(*light, frustumCullingResult, lightBoundsCullingResult.instances); + } break; + case scene::LightType::POINT: { + const auto* light = dynamic_cast(key.cullingLight); + CC_ENSURES(light); + executePointLightCulling(*light, frustumCullingResult, lightBoundsCullingResult.instances); + } break; + case scene::LightType::RANGED_DIRECTIONAL: { + const auto* light = dynamic_cast(key.cullingLight); + CC_ENSURES(light); + executeRangedDirectionalLightCulling(*light, frustumCullingResult, lightBoundsCullingResult.instances); + } break; + case scene::LightType::DIRECTIONAL: + case scene::LightType::UNKNOWN: + default: + // noop + break; + } + } + } +} + +namespace { + bool isBlend(const scene::Pass& pass) { bool bBlend = false; for (const auto& target : pass.getBlendState()->targets) { @@ -314,7 +487,8 @@ float computeSortingDepth(const scene::Camera& camera, const scene::Model& model if (model.getNode()) { const auto* node = model.getTransform(); Vec3 position; - Vec3::subtract(node->getWorldPosition(), camera.getPosition(), &position); + const auto* bounds = model.getWorldBounds(); + Vec3::subtract(bounds ? bounds->center : node->getWorldPosition(), camera.getPosition(), &position); depth = position.dot(camera.getForward()); } return depth; @@ -324,15 +498,23 @@ void addRenderObject( LayoutGraphData::vertex_descriptor phaseLayoutID, const bool bDrawOpaqueOrMask, const bool bDrawBlend, + const bool bDrawProbe, const scene::Camera& camera, const scene::Model& model, NativeRenderQueue& queue) { + if (bDrawProbe) { + queue.probeQueue.applyMacro(*kLayoutGraph, model, phaseLayoutID); + } const auto& subModels = model.getSubModels(); const auto subModelCount = subModels.size(); for (uint32_t subModelIdx = 0; subModelIdx < subModelCount; ++subModelIdx) { const auto& subModel = subModels[subModelIdx]; const auto& passes = *(subModel->getPasses()); const auto passCount = passes.size(); + auto probeIt = std::find(queue.probeQueue.probeMap.begin(), queue.probeQueue.probeMap.end(), subModel.get()); + if (probeIt != queue.probeQueue.probeMap.end()) { + phaseLayoutID = ProbeHelperQueue::getDefaultId(*kLayoutGraph); + } for (uint32_t passIdx = 0; passIdx < passCount; ++passIdx) { auto& pass = *passes[passIdx]; // check phase @@ -378,9 +560,10 @@ void addRenderObject( void SceneCulling::fillRenderQueues( const RenderGraph& rg, const pipeline::PipelineSceneData& pplSceneData) { const auto* const skybox = pplSceneData.getSkybox(); - for (auto&& [sceneID, desc] : sceneQueryIndex) { + for (auto&& [sceneID, desc] : renderQueueIndex) { CC_EXPECTS(holds(sceneID, rg)); - const auto sourceID = desc.culledSource; + const auto frustomCulledResultID = desc.frustumCulledResultID; + const auto lightBoundsCullingID = desc.lightBoundsCulledResultID; const auto targetID = desc.renderQueueTarget; const auto& sceneData = get(SceneTag{}, sceneID, rg); @@ -388,8 +571,8 @@ void SceneCulling::fillRenderQueues( 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); - - if (!bDrawShadowCaster && !bDrawBlend && !bDrawOpaqueOrMask) { + const bool bDrawProbe = any(sceneData.flags & SceneFlags::REFLECTION_PROBE); + if (!bDrawShadowCaster && !bDrawBlend && !bDrawOpaqueOrMask && !bDrawProbe) { // nothing to draw continue; } @@ -402,37 +585,32 @@ void SceneCulling::fillRenderQueues( CC_EXPECTS(phaseLayoutID != LayoutGraphData::null_vertex()); // culling source - CC_EXPECTS(sourceID < culledResults.size()); - const auto& sourceModels = culledResults[sourceID]; + CC_EXPECTS(frustomCulledResultID.value < frustumCullingResults.size()); + const auto& sourceModels = [&]() -> const auto& { + // is culled by light bounds + if (lightBoundsCullingID.value != 0xFFFFFFFF) { + CC_EXPECTS(lightBoundsCullingID.value < lightBoundsCullingResults.size()); + return lightBoundsCullingResults.at(lightBoundsCullingID.value).instances; + } + // not culled by light bounds + return frustumCullingResults.at(frustomCulledResultID.value); + } + (); // native queue target - CC_EXPECTS(targetID < renderQueues.size()); - auto& nativeQueue = renderQueues[targetID]; + CC_EXPECTS(targetID.value < renderQueues.size()); + auto& nativeQueue = renderQueues[targetID.value]; CC_EXPECTS(nativeQueue.empty()); // skybox const auto* camera = sceneData.camera; CC_EXPECTS(camera); - if (!any(sceneData.flags & SceneFlags::SHADOW_CASTER) && - skybox && skybox->isEnabled() && - (static_cast(camera->getClearFlag()) & scene::Camera::SKYBOX_FLAG)) { - CC_EXPECTS(skybox->getModel()); - const auto& model = *skybox->getModel(); - const auto* node = model.getNode(); - float depth = 0; - if (node) { - Vec3 tempVec3{}; - tempVec3 = node->getWorldPosition() - camera->getPosition(); - depth = tempVec3.dot(camera->getForward()); - } - nativeQueue.opaqueQueue.add(model, depth, 0, 0); - } // fill native queue for (const auto* const model : sourceModels) { addRenderObject( phaseLayoutID, bDrawOpaqueOrMask, bDrawBlend, - *sceneData.camera, *model, nativeQueue); + bDrawProbe, *sceneData.camera, *model, nativeQueue); } // post-processing @@ -442,26 +620,188 @@ void SceneCulling::fillRenderQueues( void SceneCulling::buildRenderQueues( const RenderGraph& rg, const LayoutGraphData& lg, - const pipeline::PipelineSceneData& pplSceneData) { - pSceneData = &pplSceneData; + const NativePipeline& ppl) { + kPipelineSceneData = ppl.pipelineSceneData; + kLayoutGraph = ≶ collectCullingQueries(rg, lg); - batchCulling(pplSceneData); - fillRenderQueues(rg, pplSceneData); + batchFrustumCulling(ppl); + batchLightBoundsCulling(); // cull frustum-culling's results by light bounds + fillRenderQueues(rg, *ppl.pipelineSceneData); } void SceneCulling::clear() noexcept { - sceneQueries.clear(); - for (auto& c : culledResults) { + // frustum culling + frustumCullings.clear(); + for (auto& c : frustumCullingResults) { c.clear(); } + // light bounds culling + lightBoundsCullings.clear(); + for (auto& c : lightBoundsCullingResults) { + c.instances.clear(); + c.lightByteOffset = 0xFFFFFFFF; + } + // native render queues for (auto& q : renderQueues) { q.clear(); } - sceneQueryIndex.clear(); - numCullingQueries = 0; + + // clear render graph scene vertex query index + renderQueueIndex.clear(); + // do not clear this->renderQueues, it is reused to avoid memory allocation + + // reset all counters + numFrustumCulling = 0; + numLightBoundsCulling = 0; numRenderQueues = 0; } +void LightResource::init(const NativeProgramLibrary& programLib, gfx::Device* deviceIn, uint32_t maxNumLightsIn) { + CC_EXPECTS(!device); + device = deviceIn; + programLibrary = &programLib; + + const auto& instanceLayout = programLibrary->localLayoutData; + const auto attrID = at(programLib.layoutGraph.attributeIndex, std::string_view{"CCForwardLight"}); + const auto& uniformBlock = instanceLayout.uniformBlocks.at(attrID); + + elementSize = boost::alignment::align_up( + getUniformBlockSize(uniformBlock.members), + device->getCapabilities().uboOffsetAlignment); + maxNumLights = maxNumLightsIn; + binding = programLib.localLayoutData.bindingMap.at(attrID); + + const auto bufferSize = elementSize * maxNumLights; + + lightBuffer = device->createBuffer(gfx::BufferInfo{ + gfx::BufferUsageBit::UNIFORM | gfx::BufferUsageBit::TRANSFER_DST, + gfx::MemoryUsageBit::HOST | gfx::MemoryUsageBit::DEVICE, + bufferSize, + elementSize, + }); + firstLightBufferView = device->createBuffer({lightBuffer, 0, elementSize}); + + cpuBuffer.resize(bufferSize); + lights.reserve(maxNumLights); + lightIndex.reserve(maxNumLights); + + CC_ENSURES(elementSize); + CC_ENSURES(maxNumLights); + + resized = true; +} + +uint32_t LightResource::addLight( + const scene::Light* light, + bool bHDR, + float exposure, + const scene::Shadows* shadowInfo) { + // already added + auto iter = lightIndex.find(light); + if (iter != lightIndex.end()) { + return iter->second; + } + + // resize buffer + if (lights.size() == maxNumLights) { + resized = true; + maxNumLights *= 2; + const auto bufferSize = elementSize * maxNumLights; + lightBuffer->resize(bufferSize); + firstLightBufferView = device->createBuffer({lightBuffer, 0, elementSize}); + cpuBuffer.resize(bufferSize); + lights.reserve(maxNumLights); + lightIndex.reserve(maxNumLights); + } + CC_ENSURES(lights.size() < maxNumLights); + + // add light + const auto lightID = static_cast(lights.size()); + lights.emplace_back(light); + auto res = lightIndex.emplace(light, lightID); + CC_ENSURES(res.second); + + // update buffer + const auto offset = elementSize * lightID; + setLightUBO(light, bHDR, exposure, shadowInfo, cpuBuffer.data() + offset, elementSize); + + return lightID * elementSize; +} + +void LightResource::buildLights( + SceneCulling& sceneCulling, + bool bHDR, + const scene::Shadows* shadowInfo) { + // build light buffer + for (const auto& [scene, lightBoundsCullings] : sceneCulling.lightBoundsCullings) { + for (const auto& [key, lightBoundsCullingID] : lightBoundsCullings.resultIndex) { + float exposure = 1.0F; + if (key.camera) { + exposure = key.camera->getExposure(); + } else if (key.probe && key.probe->getCamera()) { + exposure = key.probe->getCamera()->getExposure(); + } else { + CC_EXPECTS(false); + } + const auto lightByteOffset = addLight( + key.cullingLight, + bHDR, + exposure, + shadowInfo); + + // save light byte offset for each light bounds culling + auto& result = sceneCulling.lightBoundsCullingResults.at(lightBoundsCullingID.value); + result.lightByteOffset = lightByteOffset; + } + } + + // assign light byte offset to each queue + for (const auto& [sceneID, desc] : sceneCulling.renderQueueIndex) { + if (desc.lightBoundsCulledResultID.value == 0xFFFFFFFF) { + continue; + } + const auto lightByteOffset = sceneCulling.lightBoundsCullingResults.at( + desc.lightBoundsCulledResultID.value) + .lightByteOffset; + + sceneCulling.renderQueues.at(desc.renderQueueTarget.value).lightByteOffset = lightByteOffset; + } +} + +void LightResource::clear() { + std::fill(cpuBuffer.begin(), cpuBuffer.end(), 0); + lights.clear(); + lightIndex.clear(); +} + +void LightResource::buildLightBuffer(gfx::CommandBuffer* cmdBuffer) const { + if (lights.empty()) { + return; + } + cmdBuffer->updateBuffer( + lightBuffer, + cpuBuffer.data(), + static_cast(lights.size()) * elementSize); +} + +void LightResource::tryUpdateRenderSceneLocalDescriptorSet(const SceneCulling& sceneCulling) { + if (!resized) { + return; + } + + for (const auto& [scene, culling] : sceneCulling.frustumCullings) { + for (const auto& model : scene->getModels()) { + CC_EXPECTS(model); + for (const auto& submodel : model->getSubModels()) { + auto* set = submodel->getDescriptorSet(); + set->bindBuffer(binding, firstLightBufferView); + set->update(); + } + } + } + resized = false; +} + } // namespace render } // namespace cc diff --git a/native/cocos/renderer/pipeline/custom/NativeSetter.cpp b/native/cocos/renderer/pipeline/custom/NativeSetter.cpp index bb313d6cfca..16141922bd0 100644 --- a/native/cocos/renderer/pipeline/custom/NativeSetter.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeSetter.cpp @@ -26,6 +26,7 @@ #include "cocos/renderer/pipeline/custom/NativePipelineTypes.h" #include "cocos/renderer/pipeline/custom/NativeUtils.h" #include "cocos/renderer/pipeline/custom/RenderGraphGraphs.h" +#include "cocos/scene/Light.h" #include "cocos/scene/RenderScene.h" #include "cocos/scene/SpotLight.h" @@ -122,21 +123,27 @@ void NativeSetter::setBuiltinCameraConstants(const scene::Camera *camera) { camera->getScene()->getMainLight(), data); } -void NativeSetter::setBuiltinDirectionalLightViewConstants( +void NativeSetter::setBuiltinDirectionalLightFrustumConstants( + const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t level) { CC_EXPECTS(light); + // if csm is actually activated, csm is not nullptr + // update and get csm + const auto *csm = getBuiltinShadowCSM(*pipelineRuntime, *camera, light); + + // set data auto *device = pipelineRuntime->getDevice(); const auto &sceneData = *pipelineRuntime->getPipelineSceneData(); auto &data = get(RenderGraph::DataTag{}, *renderGraph, nodeID); - setShadowUBOLightView(device, *layoutGraph, sceneData, *light, level, data); + setShadowUBOLightView(device, *layoutGraph, sceneData, csm, *light, level, data); } -void NativeSetter::setBuiltinSpotLightViewConstants(const scene::SpotLight *light) { +void NativeSetter::setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) { CC_EXPECTS(light); auto *device = pipelineRuntime->getDevice(); const auto &sceneData = *pipelineRuntime->getPipelineSceneData(); auto &data = get(RenderGraph::DataTag{}, *renderGraph, nodeID); - setShadowUBOLightView(device, *layoutGraph, sceneData, *light, 0, data); + setShadowUBOLightView(device, *layoutGraph, sceneData, nullptr, *light, 0, data); } void NativeSetter::setBuiltinShadowMapConstants( @@ -148,19 +155,177 @@ void NativeSetter::setBuiltinShadowMapConstants( setShadowUBOView(*device, *layoutGraph, sceneData, *light, data); } +namespace { + +constexpr float LIGHT_METER_SCALE = 10000.0F; +constexpr bool ENABLE_NEW_MULTI_LIGHT = false; + +} // namespace + void NativeSetter::setBuiltinDirectionalLightConstants(const scene::DirectionalLight *light, const scene::Camera *camera) { } -void NativeSetter::setBuiltinSphereLightConstants(const scene::SphereLight *light, const scene::Camera *camera) { +void NativeSetter::setBuiltinSphereLightConstants( + const scene::SphereLight *light, const scene::Camera *camera) { + CC_EXPECTS(light); + const auto &sceneData = *pipelineRuntime->getPipelineSceneData(); + const auto &shadowInfo = *sceneData.getShadows(); + + auto &data = get(RenderGraph::DataTag{}, *renderGraph, nodeID); + + if constexpr (ENABLE_NEW_MULTI_LIGHT) { + setVec4Impl( + data, *layoutGraph, "cc_lightPos", + toVec4(light->getPosition(), static_cast(scene::LightType::SPHERE))); + auto color = toVec4(light->getColor()); + if (light->isUseColorTemperature()) { + const auto &rgb = light->getColorTemperatureRGB(); + color.x *= rgb.x; + color.y *= rgb.y; + color.z *= rgb.z; + } + if (sceneData.isHDR()) { + color.w = light->getLuminance() * camera->getExposure() * LIGHT_METER_SCALE; + } else { + color.w = light->getLuminance(); + } + + setVec4Impl( + data, *layoutGraph, "cc_lightColor", color); + setVec4Impl( + data, *layoutGraph, "cc_lightSizeRangeAngle", + Vec4( + light->getSize(), + light->getRange(), + 0.F, + 0.F)); + } + setPunctualLightShadowUBO( + pipelineRuntime->getDevice(), *layoutGraph, sceneData, + camera->getScene()->getMainLight(), *light, data); } void NativeSetter::setBuiltinSpotLightConstants(const scene::SpotLight *light, const scene::Camera *camera) { + CC_EXPECTS(light); + const auto &sceneData = *this->pipelineRuntime->getPipelineSceneData(); + const auto &shadowInfo = *sceneData.getShadows(); + + auto &data = get(RenderGraph::DataTag{}, *renderGraph, nodeID); + + if constexpr (ENABLE_NEW_MULTI_LIGHT) { + setVec4Impl( + data, *layoutGraph, "cc_lightPos", + toVec4(light->getPosition(), static_cast(scene::LightType::SPOT))); + + auto color = toVec4(light->getColor()); + if (light->isUseColorTemperature()) { + const auto &rgb = light->getColorTemperatureRGB(); + color.x *= rgb.x; + color.y *= rgb.y; + color.z *= rgb.z; + } + if (sceneData.isHDR()) { + color.w = light->getLuminance() * camera->getExposure() * LIGHT_METER_SCALE; + } else { + color.w = light->getLuminance(); + } + + setVec4Impl( + data, *layoutGraph, "cc_lightColor", color); + + setVec4Impl( + data, *layoutGraph, "cc_lightSizeRangeAngle", + Vec4( + light->getSize(), + light->getRange(), + light->getSpotAngle(), + shadowInfo.isEnabled() && + light->isShadowEnabled() && + shadowInfo.getType() == scene::ShadowType::SHADOW_MAP + ? 1.0F + : 0.0F)); + + setVec4Impl( + data, *layoutGraph, "cc_lightDir", + toVec4(light->getDirection())); + } + setPunctualLightShadowUBO( + pipelineRuntime->getDevice(), *layoutGraph, sceneData, + camera->getScene()->getMainLight(), *light, data); } void NativeSetter::setBuiltinPointLightConstants(const scene::PointLight *light, const scene::Camera *camera) { + CC_EXPECTS(light); + const auto &sceneData = *this->pipelineRuntime->getPipelineSceneData(); + const auto &shadowInfo = *sceneData.getShadows(); + + auto &data = get(RenderGraph::DataTag{}, *renderGraph, nodeID); + + if constexpr (ENABLE_NEW_MULTI_LIGHT) { + setVec4Impl( + data, *layoutGraph, "cc_lightPos", + toVec4(light->getPosition(), static_cast(scene::LightType::POINT))); + auto color = toVec4(light->getColor()); + if (light->isUseColorTemperature()) { + const auto &rgb = light->getColorTemperatureRGB(); + color.x *= rgb.x; + color.y *= rgb.y; + color.z *= rgb.z; + } + if (sceneData.isHDR()) { + color.w = light->getLuminance() * camera->getExposure() * LIGHT_METER_SCALE; + } else { + color.w = light->getLuminance(); + } + + setVec4Impl( + data, *layoutGraph, "cc_lightColor", color); + setVec4Impl( + data, *layoutGraph, "cc_lightSizeRangeAngle", + Vec4( + 0.F, + light->getRange(), + 0.F, + 0.F)); + } + setPunctualLightShadowUBO( + pipelineRuntime->getDevice(), *layoutGraph, sceneData, + camera->getScene()->getMainLight(), *light, data); } void NativeSetter::setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) { + const auto &sceneData = *this->pipelineRuntime->getPipelineSceneData(); + const auto &shadowInfo = *sceneData.getShadows(); + + auto &data = get(RenderGraph::DataTag{}, *renderGraph, nodeID); + + if constexpr (ENABLE_NEW_MULTI_LIGHT) { + setVec4Impl( + data, *layoutGraph, "cc_lightPos", + toVec4(light->getPosition(), static_cast(scene::LightType::RANGED_DIRECTIONAL))); + auto color = toVec4(light->getColor()); + if (light->isUseColorTemperature()) { + const auto &rgb = light->getColorTemperatureRGB(); + color.x *= rgb.x; + color.y *= rgb.y; + color.z *= rgb.z; + } + if (sceneData.isHDR()) { + color.w = light->getIlluminance() * camera->getExposure(); + } else { + color.w = light->getIlluminance(); + } + + setVec4Impl( + data, *layoutGraph, "cc_lightColor", color); + setVec4Impl( + data, *layoutGraph, "cc_lightSizeRangeAngle", + Vec4( + light->getRight().x, + light->getRight().y, + light->getRight().z, + 0.F)); + } } } // namespace render diff --git a/native/cocos/renderer/pipeline/custom/NativeUtils.h b/native/cocos/renderer/pipeline/custom/NativeUtils.h index 82595dc3c92..83a11227a36 100644 --- a/native/cocos/renderer/pipeline/custom/NativeUtils.h +++ b/native/cocos/renderer/pipeline/custom/NativeUtils.h @@ -24,7 +24,10 @@ #pragma once #include "cocos/core/ArrayBuffer.h" +#include "cocos/math/Vec2.h" +#include "cocos/math/Vec3.h" #include "cocos/math/Vec4.h" +#include "cocos/renderer/gfx-base/GFXDef-common.h" #include "cocos/renderer/pipeline/custom/LayoutGraphFwd.h" #include "cocos/renderer/pipeline/custom/RenderGraphFwd.h" #include "cocos/renderer/pipeline/custom/RenderInterfaceFwd.h" @@ -75,6 +78,10 @@ void setReadWriteTextureImpl(RenderData &data, const LayoutGraphData &lg, const void setSamplerImpl(RenderData &data, const LayoutGraphData &lg, const ccstd::string &name, gfx::Sampler *sampler); +inline Vec4 toVec4(const Vec3 &vec, float w = 0.0F) noexcept { + return {vec.x, vec.y, vec.z, w}; +} + } // namespace render } // namespace cc diff --git a/native/cocos/renderer/pipeline/custom/PrivateFwd.h b/native/cocos/renderer/pipeline/custom/PrivateFwd.h index 9bdfb3b4e7d..5c6f8fc2802 100644 --- a/native/cocos/renderer/pipeline/custom/PrivateFwd.h +++ b/native/cocos/renderer/pipeline/custom/PrivateFwd.h @@ -30,7 +30,6 @@ // clang-format off #pragma once #include "cocos/base/std/variant.h" -#include "cocos/renderer/core/ProgramLib.h" #include "cocos/renderer/pipeline/custom/RenderInterfaceFwd.h" namespace cc { diff --git a/native/cocos/renderer/pipeline/custom/RenderCommonFwd.h b/native/cocos/renderer/pipeline/custom/RenderCommonFwd.h index 1628d725ef7..929719b046a 100644 --- a/native/cocos/renderer/pipeline/custom/RenderCommonFwd.h +++ b/native/cocos/renderer/pipeline/custom/RenderCommonFwd.h @@ -31,7 +31,6 @@ #pragma once #include "cocos/base/std/hash/hash.h" #include "cocos/base/std/variant.h" -#include "cocos/renderer/gfx-base/GFXDef-common.h" namespace cc { diff --git a/native/cocos/renderer/pipeline/custom/RenderCommonJsb.cpp b/native/cocos/renderer/pipeline/custom/RenderCommonJsb.cpp index c7fed6ceedb..249ddbbd78c 100644 --- a/native/cocos/renderer/pipeline/custom/RenderCommonJsb.cpp +++ b/native/cocos/renderer/pipeline/custom/RenderCommonJsb.cpp @@ -41,6 +41,9 @@ bool nativevalue_to_se(const cc::render::LightInfo &from, se::Value &to, se::Obj nativevalue_to_se(from.light, tmp, ctx); obj->setProperty("light", tmp); + nativevalue_to_se(from.probe, tmp, ctx); + obj->setProperty("probe", tmp); + nativevalue_to_se(from.level, tmp, ctx); obj->setProperty("level", tmp); @@ -241,6 +244,10 @@ bool sevalue_to_native(const se::Value &from, cc::render: if(!field.isNullOrUndefined()) { ok &= sevalue_to_native(field, &(to->light), ctx); } + obj->getProperty("probe", &field, true); + if(!field.isNullOrUndefined()) { + ok &= sevalue_to_native(field, &(to->probe), ctx); + } obj->getProperty("level", &field, true); if(!field.isNullOrUndefined()) { ok &= sevalue_to_native(field, &(to->level), ctx); diff --git a/native/cocos/renderer/pipeline/custom/RenderCommonSerialization.h b/native/cocos/renderer/pipeline/custom/RenderCommonSerialization.h index 4c9467badf2..913dbab6af1 100644 --- a/native/cocos/renderer/pipeline/custom/RenderCommonSerialization.h +++ b/native/cocos/renderer/pipeline/custom/RenderCommonSerialization.h @@ -39,12 +39,14 @@ namespace render { inline void save(OutputArchive& ar, const LightInfo& v) { // skip, light: IntrusivePtr + // skip, probe: scene::ReflectionProbe save(ar, v.level); save(ar, v.culledByLight); } inline void load(InputArchive& ar, LightInfo& v) { // skip, light: IntrusivePtr + // skip, probe: scene::ReflectionProbe load(ar, v.level); load(ar, v.culledByLight); } diff --git a/native/cocos/renderer/pipeline/custom/RenderCommonTypes.h b/native/cocos/renderer/pipeline/custom/RenderCommonTypes.h index cad5d71c02c..873286c62f6 100644 --- a/native/cocos/renderer/pipeline/custom/RenderCommonTypes.h +++ b/native/cocos/renderer/pipeline/custom/RenderCommonTypes.h @@ -39,6 +39,16 @@ namespace cc { +namespace scene { + +class ReflectionProbe; + +} // namespace scene + +} // namespace cc + +namespace cc { + namespace render { enum class UpdateFrequency { @@ -126,6 +136,10 @@ constexpr bool operator!(ResourceFlags e) noexcept { return e == static_cast(0); } +constexpr ResourceFlags operator~(ResourceFlags e) noexcept { + return static_cast(~static_cast>(e)); +} + constexpr bool any(ResourceFlags e) noexcept { return !!e; } @@ -158,6 +172,7 @@ enum class SceneFlags : uint32_t { DRAW_NON_INSTANCING = 0x1000, REFLECTION_PROBE = 0x2000, GPU_DRIVEN = 0x4000, + NON_BUILTIN = 0x8000, ALL = 0xFFFFFFFF, }; @@ -181,6 +196,10 @@ constexpr bool operator!(SceneFlags e) noexcept { return e == static_cast(0); } +constexpr SceneFlags operator~(SceneFlags e) noexcept { + return static_cast(~static_cast>(e)); +} + constexpr bool any(SceneFlags e) noexcept { return !!e; } @@ -211,8 +230,9 @@ enum class ClearValueType { struct LightInfo { LightInfo() = default; - LightInfo(IntrusivePtr lightIn, uint32_t levelIn, bool culledByLightIn) noexcept + LightInfo(IntrusivePtr lightIn, uint32_t levelIn, bool culledByLightIn, scene::ReflectionProbe* probeIn) noexcept : light(std::move(lightIn)), + probe(probeIn), level(levelIn), culledByLight(culledByLightIn) {} LightInfo(IntrusivePtr lightIn, uint32_t levelIn) noexcept @@ -220,6 +240,7 @@ struct LightInfo { level(levelIn) {} IntrusivePtr light; + scene::ReflectionProbe* probe{nullptr}; uint32_t level{0}; bool culledByLight{false}; }; @@ -307,6 +328,10 @@ constexpr bool operator!(ResolveFlags e) noexcept { return e == static_cast(0); } +constexpr ResolveFlags operator~(ResolveFlags e) noexcept { + return static_cast(~static_cast>(e)); +} + constexpr bool any(ResolveFlags e) noexcept { return !!e; } diff --git a/native/cocos/renderer/pipeline/custom/RenderGraphFwd.h b/native/cocos/renderer/pipeline/custom/RenderGraphFwd.h index 0ec6698a935..9714e8db649 100644 --- a/native/cocos/renderer/pipeline/custom/RenderGraphFwd.h +++ b/native/cocos/renderer/pipeline/custom/RenderGraphFwd.h @@ -32,7 +32,6 @@ #include "cocos/base/std/hash/hash.h" #include "cocos/base/std/variant.h" #include "cocos/renderer/pipeline/custom/RenderCommonFwd.h" -#include "cocos/scene/Camera.h" namespace cc { @@ -46,7 +45,9 @@ struct ResourceTraits; struct RenderSwapchain; struct ResourceStates; struct ManagedBuffer; +struct PersistentBuffer; struct ManagedTexture; +struct PersistentTexture; struct ManagedResource; struct Subpass; struct SubpassGraph; @@ -80,6 +81,9 @@ struct ClearTag; struct ViewportTag; struct ClearView; struct RenderQueue; + +enum class CullingFlags : uint32_t; + struct SceneData; struct Dispatch; struct Blit; diff --git a/native/cocos/renderer/pipeline/custom/RenderGraphGraphs.h b/native/cocos/renderer/pipeline/custom/RenderGraphGraphs.h index e14bfd7c411..9f129ec00ea 100644 --- a/native/cocos/renderer/pipeline/custom/RenderGraphGraphs.h +++ b/native/cocos/renderer/pipeline/custom/RenderGraphGraphs.h @@ -1523,7 +1523,7 @@ holds_alternative(ResourceGraph::vertex_descriptor v, const Reso template <> inline bool -holds_alternative>(ResourceGraph::vertex_descriptor v, const ResourceGraph& g) noexcept { // NOLINT +holds_alternative(ResourceGraph::vertex_descriptor v, const ResourceGraph& g) noexcept { // NOLINT return ccstd::holds_alternative< impl::ValueHandle>( g._vertices[v].handle); @@ -1531,7 +1531,7 @@ holds_alternative>(ResourceGraph::vertex_descriptor v, template <> inline bool -holds_alternative>(ResourceGraph::vertex_descriptor v, const ResourceGraph& g) noexcept { // NOLINT +holds_alternative(ResourceGraph::vertex_descriptor v, const ResourceGraph& g) noexcept { // NOLINT return ccstd::holds_alternative< impl::ValueHandle>( g._vertices[v].handle); @@ -1601,8 +1601,8 @@ get(ResourceGraph::vertex_descriptor v, ResourceGraph& g) { } template <> -inline IntrusivePtr& -get>(ResourceGraph::vertex_descriptor v, ResourceGraph& g) { +inline PersistentBuffer& +get(ResourceGraph::vertex_descriptor v, ResourceGraph& g) { auto& handle = ccstd::get< impl::ValueHandle>( g._vertices[v].handle); @@ -1610,8 +1610,8 @@ get>(ResourceGraph::vertex_descriptor v, ResourceGraph } template <> -inline IntrusivePtr& -get>(ResourceGraph::vertex_descriptor v, ResourceGraph& g) { +inline PersistentTexture& +get(ResourceGraph::vertex_descriptor v, ResourceGraph& g) { auto& handle = ccstd::get< impl::ValueHandle>( g._vertices[v].handle); @@ -1686,8 +1686,8 @@ get(ResourceGraph::vertex_descriptor v, const ResourceGraph& g) } template <> -inline const IntrusivePtr& -get>(ResourceGraph::vertex_descriptor v, const ResourceGraph& g) { +inline const PersistentBuffer& +get(ResourceGraph::vertex_descriptor v, const ResourceGraph& g) { const auto& handle = ccstd::get< impl::ValueHandle>( g._vertices[v].handle); @@ -1695,8 +1695,8 @@ get>(ResourceGraph::vertex_descriptor v, const Resourc } template <> -inline const IntrusivePtr& -get>(ResourceGraph::vertex_descriptor v, const ResourceGraph& g) { +inline const PersistentTexture& +get(ResourceGraph::vertex_descriptor v, const ResourceGraph& g) { const auto& handle = ccstd::get< impl::ValueHandle>( g._vertices[v].handle); @@ -1763,7 +1763,7 @@ get(ManagedTextureTag /*tag*/, ResourceGraph::vertex_descriptor v, ResourceGraph return g.managedTextures[handle.value]; } -inline IntrusivePtr& +inline PersistentBuffer& get(PersistentBufferTag /*tag*/, ResourceGraph::vertex_descriptor v, ResourceGraph& g) { auto& handle = ccstd::get< impl::ValueHandle>( @@ -1771,7 +1771,7 @@ get(PersistentBufferTag /*tag*/, ResourceGraph::vertex_descriptor v, ResourceGra return g.buffers[handle.value]; } -inline IntrusivePtr& +inline PersistentTexture& get(PersistentTextureTag /*tag*/, ResourceGraph::vertex_descriptor v, ResourceGraph& g) { auto& handle = ccstd::get< impl::ValueHandle>( @@ -1835,7 +1835,7 @@ get(ManagedTextureTag /*tag*/, ResourceGraph::vertex_descriptor v, const Resourc return g.managedTextures[handle.value]; } -inline const IntrusivePtr& +inline const PersistentBuffer& get(PersistentBufferTag /*tag*/, ResourceGraph::vertex_descriptor v, const ResourceGraph& g) { const auto& handle = ccstd::get< impl::ValueHandle>( @@ -1843,7 +1843,7 @@ get(PersistentBufferTag /*tag*/, ResourceGraph::vertex_descriptor v, const Resou return g.buffers[handle.value]; } -inline const IntrusivePtr& +inline const PersistentTexture& get(PersistentTextureTag /*tag*/, ResourceGraph::vertex_descriptor v, const ResourceGraph& g) { const auto& handle = ccstd::get< impl::ValueHandle>( @@ -1939,9 +1939,9 @@ get_if(ResourceGraph::vertex_descriptor v, ResourceGraph* pGraph } template <> -inline IntrusivePtr* -get_if>(ResourceGraph::vertex_descriptor v, ResourceGraph* pGraph) noexcept { // NOLINT - IntrusivePtr* ptr = nullptr; +inline PersistentBuffer* +get_if(ResourceGraph::vertex_descriptor v, ResourceGraph* pGraph) noexcept { // NOLINT + PersistentBuffer* ptr = nullptr; if (!pGraph) { return ptr; } @@ -1956,9 +1956,9 @@ get_if>(ResourceGraph::vertex_descriptor v, ResourceGr } template <> -inline IntrusivePtr* -get_if>(ResourceGraph::vertex_descriptor v, ResourceGraph* pGraph) noexcept { // NOLINT - IntrusivePtr* ptr = nullptr; +inline PersistentTexture* +get_if(ResourceGraph::vertex_descriptor v, ResourceGraph* pGraph) noexcept { // NOLINT + PersistentTexture* ptr = nullptr; if (!pGraph) { return ptr; } @@ -2096,9 +2096,9 @@ get_if(ResourceGraph::vertex_descriptor v, const ResourceGraph* } template <> -inline const IntrusivePtr* -get_if>(ResourceGraph::vertex_descriptor v, const ResourceGraph* pGraph) noexcept { // NOLINT - const IntrusivePtr* ptr = nullptr; +inline const PersistentBuffer* +get_if(ResourceGraph::vertex_descriptor v, const ResourceGraph* pGraph) noexcept { // NOLINT + const PersistentBuffer* ptr = nullptr; if (!pGraph) { return ptr; } @@ -2113,9 +2113,9 @@ get_if>(ResourceGraph::vertex_descriptor v, const Reso } template <> -inline const IntrusivePtr* -get_if>(ResourceGraph::vertex_descriptor v, const ResourceGraph* pGraph) noexcept { // NOLINT - const IntrusivePtr* ptr = nullptr; +inline const PersistentTexture* +get_if(ResourceGraph::vertex_descriptor v, const ResourceGraph* pGraph) noexcept { // NOLINT + const PersistentTexture* ptr = nullptr; if (!pGraph) { return ptr; } @@ -2432,7 +2432,7 @@ void addVertexImpl( // NOLINT template void addVertexImpl( // NOLINT ValueT &&val, ResourceGraph &g, ResourceGraph::Vertex &vert, // NOLINT - std::enable_if_t, IntrusivePtr>::value>* dummy = nullptr) { // NOLINT + std::enable_if_t, PersistentBuffer>::value>* dummy = nullptr) { // NOLINT vert.handle = impl::ValueHandle{ gsl::narrow_cast(g.buffers.size())}; g.buffers.emplace_back(std::forward(val)); @@ -2441,7 +2441,7 @@ void addVertexImpl( // NOLINT template void addVertexImpl( // NOLINT ValueT &&val, ResourceGraph &g, ResourceGraph::Vertex &vert, // NOLINT - std::enable_if_t, IntrusivePtr>::value>* dummy = nullptr) { // NOLINT + std::enable_if_t, PersistentTexture>::value>* dummy = nullptr) { // NOLINT vert.handle = impl::ValueHandle{ gsl::narrow_cast(g.textures.size())}; g.textures.emplace_back(std::forward(val)); diff --git a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.cpp b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.cpp index bcb977b9e35..46c87e4227e 100644 --- a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.cpp +++ b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.cpp @@ -171,6 +171,11 @@ SubpassGraph::Vertex::Vertex(Vertex const& rhs, const allocator_type& alloc) : outEdges(rhs.outEdges, alloc), inEdges(rhs.inEdges, alloc) {} +RasterSubpass::RasterSubpass(const allocator_type& alloc) noexcept +: rasterViews(alloc), + computeViews(alloc), + resolvePairs(alloc) {} + RasterSubpass::RasterSubpass(uint32_t subpassIDIn, uint32_t countIn, uint32_t qualityIn, const allocator_type& alloc) noexcept : rasterViews(alloc), computeViews(alloc), @@ -199,6 +204,10 @@ RasterSubpass::RasterSubpass(RasterSubpass const& rhs, const allocator_type& all quality(rhs.quality), showStatistics(rhs.showStatistics) {} +ComputeSubpass::ComputeSubpass(const allocator_type& alloc) noexcept +: rasterViews(alloc), + computeViews(alloc) {} + ComputeSubpass::ComputeSubpass(uint32_t subpassIDIn, const allocator_type& alloc) noexcept : rasterViews(alloc), computeViews(alloc), diff --git a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h index 75abe2d1bf1..300a53b8c6b 100644 --- a/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h +++ b/native/cocos/renderer/pipeline/custom/RenderGraphTypes.h @@ -211,6 +211,15 @@ struct ManagedBuffer { uint64_t fenceValue{0}; }; +struct PersistentBuffer { + PersistentBuffer() = default; + PersistentBuffer(IntrusivePtr bufferIn) noexcept // NOLINT + : buffer(std::move(bufferIn)) {} + + IntrusivePtr buffer; + uint64_t fenceValue{0}; +}; + struct ManagedTexture { ManagedTexture() = default; ManagedTexture(IntrusivePtr textureIn) noexcept // NOLINT @@ -222,6 +231,17 @@ struct ManagedTexture { uint64_t fenceValue{0}; }; +struct PersistentTexture { + PersistentTexture() = default; + PersistentTexture(IntrusivePtr textureIn) noexcept // NOLINT + : texture(std::move(textureIn)) {} + + bool checkResource(const ResourceDesc &desc) const; + + IntrusivePtr texture; + uint64_t fenceValue{0}; +}; + struct ManagedResource { uint32_t unused{0}; }; @@ -390,6 +410,7 @@ struct RasterSubpass { return {rasterViews.get_allocator().resource()}; } + RasterSubpass(const allocator_type& alloc) noexcept; // NOLINT RasterSubpass(uint32_t subpassIDIn, uint32_t countIn, uint32_t qualityIn, const allocator_type& alloc) noexcept; RasterSubpass(RasterSubpass&& rhs, const allocator_type& alloc); RasterSubpass(RasterSubpass const& rhs, const allocator_type& alloc); @@ -415,6 +436,7 @@ struct ComputeSubpass { return {rasterViews.get_allocator().resource()}; } + ComputeSubpass(const allocator_type& alloc) noexcept; // NOLINT ComputeSubpass(uint32_t subpassIDIn, const allocator_type& alloc) noexcept; ComputeSubpass(ComputeSubpass&& rhs, const allocator_type& alloc); ComputeSubpass(ComputeSubpass const& rhs, const allocator_type& alloc); @@ -516,7 +538,6 @@ struct SubresourceView { uint16_t numArraySlices{0}; uint16_t firstPlane{0}; uint16_t numPlanes{0}; - gfx::TextureType viewType; }; struct ResourceGraph { @@ -637,8 +658,8 @@ struct ResourceGraph { // PolymorphicGraph using VertexTag = ccstd::variant; - using VertexValue = ccstd::variant*, IntrusivePtr*, IntrusivePtr*, RenderSwapchain*, FormatView*, SubresourceView*>; - using VertexConstValue = ccstd::variant*, const IntrusivePtr*, const IntrusivePtr*, const RenderSwapchain*, const FormatView*, const SubresourceView*>; + using VertexValue = ccstd::variant*, RenderSwapchain*, FormatView*, SubresourceView*>; + using VertexConstValue = ccstd::variant*, const RenderSwapchain*, const FormatView*, const SubresourceView*>; using VertexHandle = ccstd::variant< impl::ValueHandle, impl::ValueHandle, @@ -701,8 +722,8 @@ struct ResourceGraph { ccstd::pmr::vector resources; ccstd::pmr::vector managedBuffers; ccstd::pmr::vector managedTextures; - ccstd::pmr::vector> buffers; - ccstd::pmr::vector> textures; + ccstd::pmr::vector buffers; + ccstd::pmr::vector textures; ccstd::pmr::vector> framebuffers; ccstd::pmr::vector swapchains; ccstd::pmr::vector formatViews; @@ -847,18 +868,57 @@ struct RenderQueue { gfx::Viewport viewport; }; +enum class CullingFlags : uint32_t { + NONE = 0, + CAMERA_FRUSTUM = 0x1, + LIGHT_FRUSTUM = 0x2, + LIGHT_BOUNDS = 0x4, +}; + +constexpr CullingFlags operator|(const CullingFlags lhs, const CullingFlags rhs) noexcept { + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +constexpr CullingFlags operator&(const CullingFlags lhs, const CullingFlags rhs) noexcept { + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + +constexpr CullingFlags& operator|=(CullingFlags& lhs, const CullingFlags rhs) noexcept { + return lhs = lhs | rhs; +} + +constexpr CullingFlags& operator&=(CullingFlags& lhs, const CullingFlags rhs) noexcept { + return lhs = lhs & rhs; +} + +constexpr bool operator!(CullingFlags e) noexcept { + return e == static_cast(0); +} + +constexpr CullingFlags operator~(CullingFlags e) noexcept { + return static_cast(~static_cast>(e)); +} + +constexpr bool any(CullingFlags e) noexcept { + return !!e; +} + struct SceneData { SceneData() = default; - SceneData(const scene::RenderScene* sceneIn, const scene::Camera* cameraIn, SceneFlags flagsIn, LightInfo lightIn) noexcept + SceneData(const scene::RenderScene* sceneIn, const scene::Camera* cameraIn, SceneFlags flagsIn, LightInfo lightIn, CullingFlags cullingFlagsIn, IntrusivePtr shadingLightIn) noexcept : scene(sceneIn), camera(cameraIn), light(std::move(lightIn)), - flags(flagsIn) {} + flags(flagsIn), + cullingFlags(cullingFlagsIn), + shadingLight(std::move(shadingLightIn)) {} const scene::RenderScene* scene{nullptr}; const scene::Camera* camera{nullptr}; LightInfo light; SceneFlags flags{SceneFlags::NONE}; + CullingFlags cullingFlags{CullingFlags::CAMERA_FRUSTUM}; + IntrusivePtr shadingLight; }; struct Dispatch { diff --git a/native/cocos/renderer/pipeline/custom/RenderInterfaceFwd.h b/native/cocos/renderer/pipeline/custom/RenderInterfaceFwd.h index 7a72a5f60e8..8a6545cc0e1 100644 --- a/native/cocos/renderer/pipeline/custom/RenderInterfaceFwd.h +++ b/native/cocos/renderer/pipeline/custom/RenderInterfaceFwd.h @@ -30,9 +30,6 @@ // clang-format off #pragma once #include "cocos/base/std/variant.h" -#include "cocos/core/assets/EffectAsset.h" -#include "cocos/renderer/core/PassUtils.h" -#include "cocos/renderer/pipeline/PipelineSceneData.h" #include "cocos/renderer/pipeline/custom/CustomFwd.h" namespace cc { @@ -47,6 +44,7 @@ enum class SubpassCapabilities : uint32_t; struct PipelineCapabilities; class RenderNode; class Setter; +class SceneBuilder; class RenderQueueBuilder; class BasicRenderPassBuilder; class BasicMultisampleRenderPassBuilder; diff --git a/native/cocos/renderer/pipeline/custom/RenderInterfaceTypes.h b/native/cocos/renderer/pipeline/custom/RenderInterfaceTypes.h index 29e13290601..dfa2210c52b 100644 --- a/native/cocos/renderer/pipeline/custom/RenderInterfaceTypes.h +++ b/native/cocos/renderer/pipeline/custom/RenderInterfaceTypes.h @@ -320,6 +320,10 @@ constexpr bool operator!(SubpassCapabilities e) noexcept { return e == static_cast(0); } +constexpr SubpassCapabilities operator~(SubpassCapabilities e) noexcept { + return static_cast(~static_cast>(e)); +} + constexpr bool any(SubpassCapabilities e) noexcept { return !!e; } @@ -460,10 +464,23 @@ class Setter : public RenderNode { virtual void setBuiltinSpotLightConstants(const scene::SpotLight *light, const scene::Camera *camera) = 0; virtual void setBuiltinPointLightConstants(const scene::PointLight *light, const scene::Camera *camera) = 0; virtual void setBuiltinRangedDirectionalLightConstants(const scene::RangedDirectionalLight *light, const scene::Camera *camera) = 0; - virtual void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light, uint32_t level) = 0; - virtual void setBuiltinSpotLightViewConstants(const scene::SpotLight *light) = 0; - void setBuiltinDirectionalLightViewConstants(const scene::DirectionalLight *light) { - setBuiltinDirectionalLightViewConstants(light, 0); + virtual void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light, uint32_t csmLevel) = 0; + virtual void setBuiltinSpotLightFrustumConstants(const scene::SpotLight *light) = 0; + void setBuiltinDirectionalLightFrustumConstants(const scene::Camera *camera, const scene::DirectionalLight *light) { + setBuiltinDirectionalLightFrustumConstants(camera, light, 0); + } +}; + +class SceneBuilder : public Setter { +public: + SceneBuilder() noexcept = default; + + virtual void useLightFrustum(IntrusivePtr light, uint32_t csmLevel, const scene::Camera *optCamera) = 0; + void useLightFrustum(IntrusivePtr light) { + useLightFrustum(std::move(light), 0, nullptr); + } + void useLightFrustum(IntrusivePtr light, uint32_t csmLevel) { + useLightFrustum(std::move(light), csmLevel, nullptr); } }; @@ -488,9 +505,7 @@ class RenderQueueBuilder : public Setter { * @param sceneFlags @en Rendering flags of the scene @zh 场景渲染标志位 */ virtual void addSceneOfCamera(scene::Camera *camera, LightInfo light, SceneFlags sceneFlags) = 0; - virtual void addScene(const scene::Camera *camera, SceneFlags sceneFlags, const scene::Light *light) = 0; - virtual void addSceneCulledByDirectionalLight(const scene::Camera *camera, SceneFlags sceneFlags, scene::DirectionalLight *light, uint32_t level) = 0; - virtual void addSceneCulledBySpotLight(const scene::Camera *camera, SceneFlags sceneFlags, scene::SpotLight *light) = 0; + virtual SceneBuilder *addScene(const scene::Camera *camera, SceneFlags sceneFlags, scene::Light *light) = 0; /** * @en Render a full-screen quad. * @zh 渲染全屏四边形 @@ -528,8 +543,8 @@ class RenderQueueBuilder : public Setter { void addSceneOfCamera(scene::Camera *camera, LightInfo light) { addSceneOfCamera(camera, std::move(light), SceneFlags::NONE); } - void addScene(const scene::Camera *camera, SceneFlags sceneFlags) { - addScene(camera, sceneFlags, nullptr); + SceneBuilder *addScene(const scene::Camera *camera, SceneFlags sceneFlags) { + return addScene(camera, sceneFlags, nullptr); } void addFullscreenQuad(Material *material, uint32_t passID) { addFullscreenQuad(material, passID, SceneFlags::NONE); @@ -771,14 +786,14 @@ class BasicPipeline : public PipelineRuntime { * @param format @en Format of the resource @zh 资源的格式 */ virtual void updateDepthStencil(const ccstd::string &name, uint32_t width, uint32_t height, gfx::Format format) = 0; - virtual uint32_t addResource(const ccstd::string &name, ResourceDimension dimension, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount, ResourceFlags flags, ResourceResidency residency) = 0; - virtual void updateResource(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount) = 0; - virtual uint32_t addTexture(const ccstd::string &name, gfx::TextureType type, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount, ResourceFlags flags, ResourceResidency residency) = 0; - virtual void updateTexture(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount) = 0; virtual uint32_t addBuffer(const ccstd::string &name, uint32_t size, ResourceFlags flags, ResourceResidency residency) = 0; virtual void updateBuffer(const ccstd::string &name, uint32_t size) = 0; virtual uint32_t addExternalTexture(const ccstd::string &name, gfx::Texture *texture, ResourceFlags flags) = 0; virtual void updateExternalTexture(const ccstd::string &name, gfx::Texture *texture) = 0; + virtual uint32_t addTexture(const ccstd::string &name, gfx::TextureType type, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount, ResourceFlags flags, ResourceResidency residency) = 0; + virtual void updateTexture(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount) = 0; + virtual uint32_t addResource(const ccstd::string &name, ResourceDimension dimension, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount, ResourceFlags flags, ResourceResidency residency) = 0; + virtual void updateResource(const ccstd::string &name, gfx::Format format, uint32_t width, uint32_t height, uint32_t depth, uint32_t arraySize, uint32_t mipLevels, gfx::SampleCount sampleCount) = 0; /** * @engineInternal * @en Begin rendering one frame @@ -844,6 +859,7 @@ class BasicPipeline : public PipelineRuntime { * @param copyPairs @en Array of copy source and target @zh 拷贝来源与目标的数组 */ virtual void addCopyPass(const ccstd::vector ©Pairs) = 0; + virtual void addBuiltinReflectionProbePass(const scene::Camera *camera) = 0; /** * @engineInternal */ @@ -1497,6 +1513,7 @@ class PipelineBuilder { * @param pipeline @en Current render pipeline @zh 当前管线 */ virtual void setup(const ccstd::vector &cameras, BasicPipeline *pipeline) = 0; + virtual void onGlobalPipelineStateChanged() = 0; }; /** diff --git a/native/cocos/renderer/pipeline/custom/details/Map.h b/native/cocos/renderer/pipeline/custom/details/Map.h index a82d85a0472..ae6be1cc54d 100644 --- a/native/cocos/renderer/pipeline/custom/details/Map.h +++ b/native/cocos/renderer/pipeline/custom/details/Map.h @@ -106,6 +106,46 @@ using PmrUnorderedStringMultiMap = std::unordered_multimap< TransparentStringHash, std::equal_to<>, boost::container::pmr::polymorphic_allocator>>; +template +inline typename std::map, Allocator>::mapped_type& +at(std::map, Allocator>& m, const KeyLike& key) { + auto iter = m.find(key); + if (iter == m.end()) { + throw std::out_of_range("at(std::map) out of range"); + } + return iter->second; +} + +template +inline typename std::map, Allocator>::mapped_type const& +at(const std::map, Allocator>& m, const KeyLike& key) { + auto iter = m.find(key); + if (iter == m.end()) { + throw std::out_of_range("at(std::map) out of range"); + } + return iter->second; +} + +template +inline typename boost::container::flat_map, Allocator>::mapped_type& +at(boost::container::flat_map, Allocator>& m, const KeyLike& key) { + auto iter = m.find(key); + if (iter == m.end()) { + throw std::out_of_range("at(boost::container::flat_map) out of range"); + } + return iter->second; +} + +template +inline typename boost::container::flat_map, Allocator>::mapped_type const& +at(const boost::container::flat_map, Allocator>& m, const KeyLike& key) { + auto iter = m.find(key); + if (iter == m.end()) { + throw std::out_of_range("at(boost::container::flat_map) out of range"); + } + return iter->second; +} + } // namespace cc namespace ccstd { diff --git a/native/tools/swig-config/render.i b/native/tools/swig-config/render.i index dbaf304bec1..877b83049fe 100644 --- a/native/tools/swig-config/render.i +++ b/native/tools/swig-config/render.i @@ -81,6 +81,7 @@ using namespace cc::render; %release_returned_cpp_object_in_gc(cc::render::RenderPassBuilder::addMultisampleRenderSubpass); %release_returned_cpp_object_in_gc(cc::render::RenderPassBuilder::addComputeSubpass); %release_returned_cpp_object_in_gc(cc::render::ComputePassBuilder::addQueue); +%release_returned_cpp_object_in_gc(cc::render::RenderQueueBuilder::addScene); %release_returned_cpp_object_in_gc(cc::render::Pipeline::addRenderPass); %release_returned_cpp_object_in_gc(cc::render::Pipeline::addComputePass);