mirror of
https://github.com/cocos/cocos-engine.git
synced 2026-09-06 15:48:37 +08:00
2233 lines
91 KiB
TypeScript
2233 lines
91 KiB
TypeScript
/****************************************************************************
|
|
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
|
|
|
|
http://www.cocos.com
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* ========================= !DO NOT CHANGE THE FOLLOWING SECTION MANUALLY! =========================
|
|
* The following section is auto-generated.
|
|
* ========================= !DO NOT CHANGE THE FOLLOWING SECTION MANUALLY! =========================
|
|
*/
|
|
/* eslint-disable max-len */
|
|
import { assert } from '@base/debug';
|
|
import { cclegacy } from '@base/global';
|
|
import { memop } from '@base/utils';
|
|
import { Vec3, Vec4 } from '@base/math';
|
|
import { getPhaseID, InstancedBuffer, PipelineStateManager } from '..';
|
|
import intersect from '../../core/geometry/intersect';
|
|
import { Sphere } from '../../core/geometry/sphere';
|
|
import { AccessFlagBit, Attribute, Buffer, BufferFlagBit, BufferInfo, BufferUsageBit, BufferViewInfo, Color, ColorAttachment, CommandBuffer, DepthStencilAttachment, DescriptorSet, DescriptorSetInfo, Device, deviceManager, DispatchInfo, Format, Framebuffer, FramebufferInfo, GeneralBarrierInfo, InputAssembler, InputAssemblerInfo, LoadOp, MemoryUsageBit, PipelineBindPoint, PipelineState, PipelineStateInfo, Rect, RenderPass, RenderPassInfo, Shader, StoreOp, SurfaceTransform, Swapchain, Texture, TextureInfo, TextureType, TextureUsageBit, Viewport } from '../../gfx';
|
|
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 { PipelineSceneData } from '../pipeline-scene-data';
|
|
import { PipelineInputAssemblerData } from '../render-pipeline';
|
|
import { DescriptorSetData, LayoutGraphData, PipelineLayoutData, RenderPhaseData, RenderStageData } from './layout-graph';
|
|
import { BasicPipeline } from './pipeline';
|
|
import { SceneVisitor } from './scene';
|
|
import { Blit, ClearView, ComputePass, ComputeSubpass, ComputeView, CopyPass, Dispatch, FormatView, ManagedBuffer, ManagedResource, ManagedTexture, MovePass, PersistentBuffer, PersistentTexture, RasterPass, RasterSubpass, RasterView, RaytracePass, RenderData, RenderGraph, RenderGraphVisitor, RenderQueue, RenderSwapchain, ResolvePass, ResourceDesc, ResourceGraph, ResourceGraphVisitor, ResourceTraits, SceneData, SubresourceView } from './render-graph';
|
|
import { AccessType, AttachmentType, QueueHint, ResourceDimension, ResourceFlags, ResourceResidency, SceneFlags, UpdateFrequency } from './types';
|
|
import { PipelineUBO } from '../pipeline-ubo';
|
|
import { WebSceneTask, WebSceneTransversal } from './web-scene';
|
|
import { WebSceneVisitor } from './web-scene-visitor';
|
|
import { RenderAdditiveLightQueue } from '../render-additive-light-queue';
|
|
import { DefaultVisitor, depthFirstSearch, ReferenceGraphView } from './graph';
|
|
import { VectorGraphColorMap } from './effect';
|
|
import { getDescriptorSetDataFromLayout, getDescriptorSetDataFromLayoutId, getRenderArea, mergeSrcToTargetDesc, updateGlobalDescBinding, validPunctualLightsCulling } from './define';
|
|
import { RenderReflectionProbeQueue } from '../render-reflection-probe-queue';
|
|
import { SceneCulling } from './scene-culling';
|
|
import { WebPipeline } from './web-pipeline';
|
|
|
|
class ResourceVisitor implements ResourceGraphVisitor {
|
|
name: string;
|
|
constructor (resName = '') {
|
|
this.name = resName;
|
|
if (context) {
|
|
const ppl = context.pipeline as any;
|
|
ppl.resourceUses.push(resName);
|
|
}
|
|
}
|
|
set resName (value: string) {
|
|
this.name = value;
|
|
}
|
|
checkTexture (name: string): boolean {
|
|
const dTex = context.deviceTextures.get(name)!;
|
|
const resID = context.resourceGraph.vertex(this.name);
|
|
const desc = context.resourceGraph.getDesc(resID);
|
|
let res = false;
|
|
if (dTex.texture) {
|
|
res = dTex.texture.width === desc.width && dTex.texture.height === desc.height;
|
|
} else if (dTex.swapchain) {
|
|
res = dTex.swapchain.width === desc.width && dTex.swapchain.height === desc.height;
|
|
}
|
|
return res;
|
|
}
|
|
createDeviceTex (value: PersistentTexture | Framebuffer | ManagedResource | RenderSwapchain): void {
|
|
if (!context.deviceTextures.get(this.name)) {
|
|
const deviceTex = new DeviceTexture(this.name, value);
|
|
context.deviceTextures.set(this.name, deviceTex);
|
|
} else if (!this.checkTexture(this.name)) {
|
|
const dTex = context.deviceTextures.get(this.name)!;
|
|
dTex.texture?.destroy();
|
|
const deviceTex = new DeviceTexture(this.name, value);
|
|
context.deviceTextures.set(this.name, deviceTex);
|
|
}
|
|
}
|
|
checkBuffer (name: string): boolean {
|
|
const dBuf = context.deviceBuffers.get(name)!;
|
|
const resID = context.resourceGraph.vertex(this.name);
|
|
const desc = context.resourceGraph.getDesc(resID);
|
|
return dBuf.buffer!.size >= desc.width;
|
|
}
|
|
createDeviceBuf (value: ManagedBuffer | PersistentBuffer): void {
|
|
const mount: boolean = !!context.deviceBuffers.get(this.name);
|
|
if (!mount) {
|
|
const deviceBuf = new DeviceBuffer(this.name, value);
|
|
context.deviceBuffers.set(this.name, deviceBuf);
|
|
} else if (!this.checkBuffer(this.name)) {
|
|
const dBuf = context.deviceBuffers.get(this.name)!;
|
|
dBuf.buffer?.destroy();
|
|
const deviceBuf = new DeviceBuffer(this.name, value);
|
|
context.deviceBuffers.set(this.name, deviceBuf);
|
|
}
|
|
}
|
|
managed (value: ManagedResource): void {
|
|
this.createDeviceTex(value);
|
|
}
|
|
managedBuffer (value: ManagedBuffer): void {
|
|
this.createDeviceBuf(value);
|
|
}
|
|
managedTexture (value: ManagedTexture): void {
|
|
// noop
|
|
}
|
|
persistentBuffer (value: PersistentBuffer): void {
|
|
this.createDeviceBuf(value);
|
|
}
|
|
persistentTexture (value: PersistentTexture): void {
|
|
this.createDeviceTex(value);
|
|
}
|
|
framebuffer (value: Framebuffer): void {
|
|
this.createDeviceTex(value);
|
|
}
|
|
swapchain (value: RenderSwapchain): void {
|
|
this.createDeviceTex(value);
|
|
}
|
|
formatView (value: FormatView): void {
|
|
// noop
|
|
}
|
|
subresourceView (value: SubresourceView): void {
|
|
// noop
|
|
}
|
|
}
|
|
|
|
let context: ExecutorContext;
|
|
class DeviceResource {
|
|
protected _name: string;
|
|
constructor (name: string) {
|
|
this._name = name;
|
|
}
|
|
get name (): string { return this._name; }
|
|
}
|
|
class DeviceTexture extends DeviceResource {
|
|
protected _texture: Texture | null = null;
|
|
protected _swapchain: Swapchain | null = null;
|
|
protected _framebuffer: Framebuffer | null = null;
|
|
protected _desc: ResourceDesc | null = null;
|
|
protected _trait: ResourceTraits | null = null;
|
|
get texture (): Texture | null { return this._texture; }
|
|
set framebuffer (val: Framebuffer | null) { this._framebuffer = val; }
|
|
get framebuffer (): Framebuffer | null { return this._framebuffer; }
|
|
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._trait = resGraph.getTraits(verID);
|
|
if (tex instanceof Texture) {
|
|
this._texture = tex;
|
|
return;
|
|
}
|
|
if (tex instanceof Framebuffer) {
|
|
this._framebuffer = tex;
|
|
return;
|
|
}
|
|
if (tex instanceof RenderSwapchain) {
|
|
this._swapchain = tex.swapchain;
|
|
return;
|
|
}
|
|
|
|
const info = this._desc!;
|
|
let type = TextureType.TEX2D;
|
|
|
|
switch (info.dimension) {
|
|
case ResourceDimension.TEXTURE1D:
|
|
type = TextureType.TEX1D;
|
|
break;
|
|
case ResourceDimension.TEXTURE2D:
|
|
type = TextureType.TEX2D;
|
|
break;
|
|
case ResourceDimension.TEXTURE3D:
|
|
type = TextureType.TEX3D;
|
|
break;
|
|
default:
|
|
}
|
|
let usageFlags = TextureUsageBit.NONE;
|
|
if (info.flags & ResourceFlags.COLOR_ATTACHMENT) usageFlags |= TextureUsageBit.COLOR_ATTACHMENT;
|
|
if (info.flags & ResourceFlags.DEPTH_STENCIL_ATTACHMENT) usageFlags |= TextureUsageBit.DEPTH_STENCIL_ATTACHMENT;
|
|
if (info.flags & ResourceFlags.INPUT_ATTACHMENT) usageFlags |= TextureUsageBit.INPUT_ATTACHMENT;
|
|
if (info.flags & ResourceFlags.SAMPLED) usageFlags |= TextureUsageBit.SAMPLED;
|
|
if (info.flags & ResourceFlags.STORAGE) usageFlags |= TextureUsageBit.STORAGE;
|
|
if (info.flags & ResourceFlags.TRANSFER_SRC) usageFlags |= TextureUsageBit.TRANSFER_SRC;
|
|
if (info.flags & ResourceFlags.TRANSFER_DST) usageFlags |= TextureUsageBit.TRANSFER_DST;
|
|
|
|
this._texture = context.device.createTexture(new TextureInfo(
|
|
type,
|
|
usageFlags,
|
|
info.format,
|
|
info.width,
|
|
info.height,
|
|
));
|
|
}
|
|
|
|
release (): void {
|
|
if (this.framebuffer) {
|
|
this.framebuffer.destroy();
|
|
this._framebuffer = null;
|
|
}
|
|
if (this.texture) {
|
|
this.texture.destroy();
|
|
this._texture = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
function isShadowMap (graphScene: GraphScene): boolean | null {
|
|
const pSceneData: PipelineSceneData = cclegacy.director.root.pipeline.pipelineSceneData;
|
|
return pSceneData.shadows.enabled
|
|
&& pSceneData.shadows.type === ShadowType.ShadowMap
|
|
&& graphScene.scene
|
|
&& (graphScene.scene.flags & SceneFlags.SHADOW_CASTER) !== 0;
|
|
}
|
|
|
|
class DeviceBuffer extends DeviceResource {
|
|
private _buffer: Buffer | null;
|
|
|
|
get buffer (): Buffer | null {
|
|
return this._buffer;
|
|
}
|
|
|
|
constructor (name: string, buffer: ManagedBuffer | PersistentBuffer) {
|
|
super(name);
|
|
const resGraph = context.resourceGraph;
|
|
const verID = resGraph.vertex(name);
|
|
const desc = resGraph.getDesc(verID);
|
|
const bufferInfo = new BufferInfo();
|
|
bufferInfo.size = desc.width;
|
|
bufferInfo.memUsage = MemoryUsageBit.DEVICE;
|
|
|
|
if (desc.flags & ResourceFlags.INDIRECT) bufferInfo.usage |= BufferUsageBit.INDIRECT;
|
|
if (desc.flags & ResourceFlags.UNIFORM) bufferInfo.usage |= BufferUsageBit.UNIFORM;
|
|
if (desc.flags & ResourceFlags.STORAGE) bufferInfo.usage |= BufferUsageBit.STORAGE;
|
|
if (desc.flags & ResourceFlags.TRANSFER_SRC) bufferInfo.usage |= BufferUsageBit.TRANSFER_SRC;
|
|
if (desc.flags & ResourceFlags.TRANSFER_DST) bufferInfo.usage |= BufferUsageBit.TRANSFER_DST;
|
|
|
|
this._buffer = context.device.createBuffer(bufferInfo);
|
|
}
|
|
|
|
release (): void {
|
|
if (this._buffer) {
|
|
this._buffer.destroy();
|
|
this._buffer = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
const _vec4Array = new Float32Array(4);
|
|
class BlitDesc {
|
|
private _isUpdate = false;
|
|
private _isGatherLight = false;
|
|
private _blit: Blit;
|
|
private _screenQuad: PipelineInputAssemblerData | null = null;
|
|
private _queue: DeviceRenderQueue | null = null;
|
|
private _stageDesc: DescriptorSet | undefined;
|
|
// If VOLUMETRIC_LIGHTING is turned on, it needs to be assigned
|
|
private _lightVolumeBuffer: Buffer | null = null;
|
|
private _lightMeterScale = 10000.0;
|
|
private _lightBufferData!: Float32Array;
|
|
get screenQuad (): PipelineInputAssemblerData | null { return this._screenQuad; }
|
|
get blit (): Blit { return this._blit; }
|
|
set blit (blit: Blit) { this._blit = blit; }
|
|
get stageDesc (): DescriptorSet | undefined { return this._stageDesc; }
|
|
constructor (blit: Blit, queue: DeviceRenderQueue) {
|
|
this._blit = blit;
|
|
this._queue = queue;
|
|
}
|
|
/**
|
|
* @zh
|
|
* 创建四边形输入汇集器。
|
|
*/
|
|
protected _createQuadInputAssembler (): PipelineInputAssemblerData {
|
|
return context.blit.pipelineIAData;
|
|
}
|
|
createScreenQuad (): void {
|
|
if (!this._screenQuad) {
|
|
this._screenQuad = this._createQuadInputAssembler();
|
|
}
|
|
}
|
|
private _gatherVolumeLights (camera: Camera): void {
|
|
if (!camera.scene) { return; }
|
|
const pipeline = context.pipeline;
|
|
const cmdBuff = context.commandBuffer;
|
|
|
|
const sphereLights = camera.scene.sphereLights;
|
|
const spotLights = camera.scene.spotLights;
|
|
const _sphere = Sphere.create(0, 0, 0, 1);
|
|
const exposure = camera.exposure;
|
|
|
|
let idx = 0;
|
|
const maxLights = UBODeferredLight.LIGHTS_PER_PASS;
|
|
const elementLen = Vec4.length; // sizeof(vec4) / sizeof(float32)
|
|
const fieldLen = elementLen * maxLights;
|
|
|
|
for (let i = 0; i < sphereLights.length && idx < maxLights; i++, ++idx) {
|
|
const light = sphereLights[i];
|
|
Sphere.set(_sphere, light.position.x, light.position.y, light.position.z, light.range);
|
|
if (intersect.sphereFrustum(_sphere, camera.frustum)) {
|
|
// cc_lightPos
|
|
Vec3.toArray(_vec4Array, light.position);
|
|
_vec4Array[3] = 0;
|
|
this._lightBufferData.set(_vec4Array, idx * elementLen);
|
|
|
|
// cc_lightColor
|
|
Vec3.toArray(_vec4Array, light.color);
|
|
if (light.useColorTemperature) {
|
|
const tempRGB = light.colorTemperatureRGB;
|
|
_vec4Array[0] *= tempRGB.x;
|
|
_vec4Array[1] *= tempRGB.y;
|
|
_vec4Array[2] *= tempRGB.z;
|
|
}
|
|
|
|
if (pipeline.pipelineSceneData.isHDR) {
|
|
_vec4Array[3] = light.luminance * exposure * this._lightMeterScale;
|
|
} else {
|
|
_vec4Array[3] = light.luminance;
|
|
}
|
|
|
|
this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 1);
|
|
|
|
// cc_lightSizeRangeAngle
|
|
_vec4Array[0] = light.size;
|
|
_vec4Array[1] = light.range;
|
|
_vec4Array[2] = 0.0;
|
|
this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 2);
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < spotLights.length && idx < maxLights; i++, ++idx) {
|
|
const light = spotLights[i];
|
|
Sphere.set(_sphere, light.position.x, light.position.y, light.position.z, light.range);
|
|
if (intersect.sphereFrustum(_sphere, camera.frustum)) {
|
|
// cc_lightPos
|
|
Vec3.toArray(_vec4Array, light.position);
|
|
_vec4Array[3] = 1;
|
|
this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 0);
|
|
|
|
// cc_lightColor
|
|
Vec3.toArray(_vec4Array, light.color);
|
|
if (light.useColorTemperature) {
|
|
const tempRGB = light.colorTemperatureRGB;
|
|
_vec4Array[0] *= tempRGB.x;
|
|
_vec4Array[1] *= tempRGB.y;
|
|
_vec4Array[2] *= tempRGB.z;
|
|
}
|
|
if (pipeline.pipelineSceneData.isHDR) {
|
|
_vec4Array[3] = light.luminance * exposure * this._lightMeterScale;
|
|
} else {
|
|
_vec4Array[3] = light.luminance;
|
|
}
|
|
this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 1);
|
|
|
|
// cc_lightSizeRangeAngle
|
|
_vec4Array[0] = light.size;
|
|
_vec4Array[1] = light.range;
|
|
_vec4Array[2] = light.spotAngle;
|
|
this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 2);
|
|
|
|
// cc_lightDir
|
|
Vec3.toArray(_vec4Array, light.direction);
|
|
this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 3);
|
|
}
|
|
}
|
|
|
|
// the count of lights is set to cc_lightDir[0].w
|
|
const offset = fieldLen * 3 + 3;
|
|
this._lightBufferData.set([idx], offset);
|
|
|
|
cmdBuff.updateBuffer(this._lightVolumeBuffer!, this._lightBufferData);
|
|
}
|
|
update (): void {
|
|
if (this.blit.sceneFlags & SceneFlags.VOLUMETRIC_LIGHTING
|
|
&& this.blit.camera && !this._isGatherLight) {
|
|
this._gatherVolumeLights(this.blit.camera);
|
|
this._isGatherLight = true;
|
|
this._isUpdate = false;
|
|
}
|
|
if (!this._isUpdate) {
|
|
this._stageDesc!.update();
|
|
this._isUpdate = true;
|
|
}
|
|
}
|
|
|
|
reset (): void {
|
|
this._isUpdate = false;
|
|
this._isGatherLight = false;
|
|
}
|
|
|
|
createStageDescriptor (): void {
|
|
const blit = this.blit;
|
|
const pass = blit.material!.passes[blit.passID];
|
|
const device = context.device;
|
|
this._stageDesc = context.blit.stageDescs.get(pass);
|
|
if (!this._stageDesc) {
|
|
this._stageDesc = device.createDescriptorSet(new DescriptorSetInfo(pass.localSetLayout));
|
|
context.blit.stageDescs.set(pass, this._stageDesc);
|
|
}
|
|
if (this.blit.sceneFlags & SceneFlags.VOLUMETRIC_LIGHTING) {
|
|
this._lightVolumeBuffer = context.blit.lightVolumeBuffer;
|
|
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);
|
|
}
|
|
}
|
|
|
|
class DeviceComputeQueue {
|
|
private _devicePass: DeviceComputePass | undefined;
|
|
private _hint: QueueHint = QueueHint.NONE;
|
|
private _phaseID: number = getPhaseID('default');
|
|
private _renderPhase: RenderPhaseData | null = null;
|
|
private _descSetData: DescriptorSetData | null = null;
|
|
private _layoutID = -1;
|
|
private _isUpdateUBO = false;
|
|
private _isUploadInstance = false;
|
|
private _isUploadBatched = false;
|
|
private _queueId = -1;
|
|
init (devicePass: DeviceComputePass, renderQueue: RenderQueue, id: number): void {
|
|
this.reset();
|
|
this.queueHint = renderQueue.hint;
|
|
this.queueId = id;
|
|
this._devicePass = devicePass;
|
|
this._phaseID = cclegacy.rendering.getPhaseID(devicePass.passID, context.renderGraph.getLayout(id));
|
|
}
|
|
get phaseID (): number { return this._phaseID; }
|
|
set layoutID (value: number) {
|
|
this._layoutID = value;
|
|
const layoutGraph = context.layoutGraph;
|
|
this._renderPhase = layoutGraph.tryGetRenderPhase(value);
|
|
const layout = layoutGraph.getLayout(value);
|
|
this._descSetData = layout.descriptorSets.get(UpdateFrequency.PER_PHASE)!;
|
|
}
|
|
get layoutID (): number { return this._layoutID; }
|
|
get descSetData (): DescriptorSetData | null { return this._descSetData; }
|
|
get renderPhase (): RenderPhaseData | null { return this._renderPhase; }
|
|
set queueId (val) { this._queueId = val; }
|
|
get queueId (): number { return this._queueId; }
|
|
set isUpdateUBO (update: boolean) { this._isUpdateUBO = update; }
|
|
get isUpdateUBO (): boolean { return this._isUpdateUBO; }
|
|
set isUploadInstance (value: boolean) { this._isUploadInstance = value; }
|
|
get isUploadInstance (): boolean { return this._isUploadInstance; }
|
|
set isUploadBatched (value: boolean) { this._isUploadBatched = value; }
|
|
get isUploadBatched (): boolean { return this._isUploadBatched; }
|
|
|
|
reset (): void {
|
|
this._isUpdateUBO = false;
|
|
this._isUploadInstance = false;
|
|
this._isUploadBatched = false;
|
|
}
|
|
set queueHint (value: QueueHint) { this._hint = value; }
|
|
get queueHint (): QueueHint { return this._hint; }
|
|
get devicePass (): DeviceComputePass { return this._devicePass!; }
|
|
|
|
record (): void {
|
|
if (this._descSetData && this._descSetData.descriptorSet) {
|
|
context.commandBuffer
|
|
.bindDescriptorSet(SetIndex.COUNT, this._descSetData.descriptorSet);
|
|
}
|
|
}
|
|
}
|
|
|
|
class DeviceRenderQueue {
|
|
private _preSceneTasks: DevicePreSceneTask[] = [];
|
|
private _sceneTasks: DeviceSceneTask[] = [];
|
|
private _postSceneTasks: DevicePostSceneTask[] = [];
|
|
private _devicePass: DeviceRenderPass | undefined;
|
|
private _hint: QueueHint = QueueHint.NONE;
|
|
private _graphQueue!: RenderQueue;
|
|
private _phaseID: number = getPhaseID('default');
|
|
private _renderPhase: RenderPhaseData | null = null;
|
|
private _descSetData: DescriptorSetData | null = null;
|
|
private _viewport: Viewport | null = null;
|
|
private _scissor: Rect | null = null;
|
|
private _layoutID = -1;
|
|
private _isUpdateUBO = false;
|
|
private _isUploadInstance = false;
|
|
private _isUploadBatched = false;
|
|
protected _transversal: DeviceSceneTransversal | null = null;
|
|
get phaseID (): number { return this._phaseID; }
|
|
set layoutID (value: number) {
|
|
this._layoutID = value;
|
|
const layoutGraph = context.layoutGraph;
|
|
this._renderPhase = layoutGraph.tryGetRenderPhase(value);
|
|
const layout = layoutGraph.getLayout(value);
|
|
this._descSetData = layout.descriptorSets.get(UpdateFrequency.PER_PHASE)!;
|
|
}
|
|
get layoutID (): number { return this._layoutID; }
|
|
get descSetData (): DescriptorSetData | null { return this._descSetData; }
|
|
get renderPhase (): RenderPhaseData | null { return this._renderPhase; }
|
|
get viewport (): Viewport | null { return this._viewport; }
|
|
get scissor (): Rect | null { return this._scissor; }
|
|
private _sceneVisitor!: WebSceneVisitor;
|
|
private _blitDesc: BlitDesc | null = null;
|
|
private _queueId = -1;
|
|
set queueId (val) { this._queueId = val; }
|
|
get queueId (): number { return this._queueId; }
|
|
set isUpdateUBO (update: boolean) { this._isUpdateUBO = update; }
|
|
get isUpdateUBO (): boolean { return this._isUpdateUBO; }
|
|
set isUploadInstance (value: boolean) { this._isUploadInstance = value; }
|
|
get isUploadInstance (): boolean { return this._isUploadInstance; }
|
|
set isUploadBatched (value: boolean) { this._isUploadBatched = value; }
|
|
get isUploadBatched (): boolean { return this._isUploadBatched; }
|
|
init (devicePass: DeviceRenderPass, renderQueue: RenderQueue, id: number): void {
|
|
this.reset();
|
|
this._graphQueue = renderQueue;
|
|
this.queueHint = renderQueue.hint;
|
|
const viewport = this._viewport = renderQueue.viewport;
|
|
if (viewport) {
|
|
this._scissor = new Rect(viewport.left, viewport.top, viewport.width, viewport.height);
|
|
}
|
|
this.queueId = id;
|
|
this._devicePass = devicePass;
|
|
this._phaseID = cclegacy.rendering.getPhaseID(devicePass.passID, context.renderGraph.getLayout(id));
|
|
if (!this._sceneVisitor) {
|
|
this._sceneVisitor = new WebSceneVisitor(
|
|
context.commandBuffer,
|
|
context.pipeline.pipelineSceneData,
|
|
);
|
|
}
|
|
}
|
|
createBlitDesc (blit: Blit): void {
|
|
if (!this._blitDesc) {
|
|
this._blitDesc = new BlitDesc(blit, this);
|
|
}
|
|
this._blitDesc.createScreenQuad();
|
|
this._blitDesc.createStageDescriptor();
|
|
}
|
|
addSceneTask (scene: GraphScene): void {
|
|
if (!this._transversal) {
|
|
this._transversal = new DeviceSceneTransversal(this, context.pipelineSceneData, scene);
|
|
}
|
|
this._transversal.graphScene = scene;
|
|
this._preSceneTasks.push(this._transversal.preRenderPass(this._sceneVisitor));
|
|
this._sceneTasks.push(this._transversal.transverse(this._sceneVisitor));
|
|
}
|
|
reset (): void {
|
|
this._postSceneTasks.length = this._preSceneTasks.length = this._sceneTasks.length = 0;
|
|
this._isUpdateUBO = false;
|
|
this._isUploadInstance = false;
|
|
this._isUploadBatched = false;
|
|
this._blitDesc?.reset();
|
|
}
|
|
get graphQueue (): RenderQueue { return this._graphQueue; }
|
|
get blitDesc (): BlitDesc | null { return this._blitDesc; }
|
|
get sceneTasks (): DeviceSceneTask[] { return this._sceneTasks; }
|
|
set queueHint (value: QueueHint) { this._hint = value; }
|
|
get queueHint (): QueueHint { return this._hint; }
|
|
get devicePass (): DeviceRenderPass { return this._devicePass!; }
|
|
get preSceneTasks (): DevicePreSceneTask[] { return this._preSceneTasks; }
|
|
preRecord (): void {
|
|
for (const task of this._preSceneTasks) {
|
|
task.start();
|
|
task.join();
|
|
task.submit();
|
|
}
|
|
}
|
|
|
|
record (): void {
|
|
if (this._descSetData && this._descSetData.descriptorSet) {
|
|
context.commandBuffer
|
|
.bindDescriptorSet(SetIndex.COUNT, this._descSetData.descriptorSet);
|
|
}
|
|
for (const task of this._sceneTasks) {
|
|
task.start();
|
|
task.join();
|
|
task.submit();
|
|
}
|
|
}
|
|
|
|
postRecord (): void {
|
|
for (const task of this._postSceneTasks) {
|
|
task.start();
|
|
task.join();
|
|
task.submit();
|
|
}
|
|
}
|
|
}
|
|
|
|
class SubmitInfo {
|
|
// <scene id, shadow queue>
|
|
public additiveLight: RenderAdditiveLightQueue | null = null;
|
|
public reflectionProbe: RenderReflectionProbeQueue | null = null;
|
|
|
|
reset (): void {
|
|
this.additiveLight = null;
|
|
this.reflectionProbe = null;
|
|
}
|
|
}
|
|
|
|
class RenderPassLayoutInfo {
|
|
protected _layoutID = 0;
|
|
protected _stage: RenderStageData | null = null;
|
|
protected _layout: PipelineLayoutData;
|
|
protected _inputName: string;
|
|
protected _descriptorSet: DescriptorSet | null = null;
|
|
constructor (layoutId: number, input: [string, ComputeView[]]) {
|
|
this._inputName = input[0];
|
|
this._layoutID = layoutId;
|
|
const lg = context.layoutGraph;
|
|
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));
|
|
} 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!);
|
|
}
|
|
}
|
|
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));
|
|
// }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
get descriptorSet (): DescriptorSet | null { return this._descriptorSet; }
|
|
get layoutID (): number { return this._layoutID; }
|
|
get stage (): RenderStageData | null { return this._stage; }
|
|
get layout (): PipelineLayoutData { return this._layout; }
|
|
}
|
|
|
|
class RasterPassInfo {
|
|
protected _id!: number;
|
|
protected _pass!: RasterPass;
|
|
get id (): number { return this._id; }
|
|
get pass (): RasterPass { return this._pass; }
|
|
private _copyPass (pass: RasterPass): void {
|
|
const rasterPass = this._pass || new RasterPass();
|
|
rasterPass.width = pass.width;
|
|
rasterPass.height = pass.height;
|
|
rasterPass.versionName = pass.versionName;
|
|
rasterPass.version = pass.version;
|
|
rasterPass.showStatistics = pass.showStatistics;
|
|
rasterPass.viewport.copy(pass.viewport);
|
|
for (const val of pass.rasterViews) {
|
|
const currRasterKey = val[0];
|
|
const currRasterView = val[1];
|
|
const rasterView = rasterPass.rasterViews.get(currRasterKey) || new RasterView();
|
|
rasterView.slotName = currRasterView.slotName;
|
|
rasterView.accessType = currRasterView.accessType;
|
|
rasterView.attachmentType = currRasterView.attachmentType;
|
|
rasterView.loadOp = currRasterView.loadOp ? LoadOp.CLEAR : LoadOp.LOAD;
|
|
rasterView.storeOp = currRasterView.storeOp;
|
|
rasterView.clearFlags = currRasterView.clearFlags;
|
|
rasterView.slotID = currRasterView.slotID;
|
|
rasterView.clearColor.copy(currRasterView.clearColor);
|
|
rasterPass.rasterViews.set(currRasterKey, rasterView);
|
|
}
|
|
for (const val of pass.computeViews) {
|
|
const currComputeViews = val[1];
|
|
const currComputeKey = val[0];
|
|
const computeViews: ComputeView[] = rasterPass.computeViews.get(currComputeKey) || [];
|
|
if (computeViews.length) computeViews.length = currComputeViews.length;
|
|
let idx = 0;
|
|
for (const currComputeView of currComputeViews) {
|
|
const computeView = computeViews[idx] || new ComputeView();
|
|
computeView.name = currComputeView.name;
|
|
computeView.accessType = currComputeView.accessType;
|
|
computeView.clearFlags = currComputeView.clearFlags;
|
|
computeView.clearValue.x = currComputeView.clearValue.x;
|
|
computeView.clearValue.y = currComputeView.clearValue.y;
|
|
computeView.clearValue.z = currComputeView.clearValue.z;
|
|
computeView.clearValue.w = currComputeView.clearValue.w;
|
|
computeView.clearValueType = currComputeView.clearValueType;
|
|
computeViews[idx] = computeView;
|
|
idx++;
|
|
}
|
|
rasterPass.computeViews.set(currComputeKey, computeViews);
|
|
}
|
|
this._pass = rasterPass;
|
|
}
|
|
applyInfo (id: number, pass: RasterPass): void {
|
|
this._id = id;
|
|
this._copyPass(pass);
|
|
}
|
|
}
|
|
|
|
const profilerViewport = new Viewport();
|
|
const renderPassArea = new Rect();
|
|
const resourceVisitor = new ResourceVisitor();
|
|
class DeviceRenderPass {
|
|
protected _renderPass: RenderPass;
|
|
protected _framebuffer: Framebuffer;
|
|
protected _clearColor: Color[] = [];
|
|
protected _deviceQueues: DeviceRenderQueue[] = [];
|
|
protected _clearDepth = 1;
|
|
protected _clearStencil = 0;
|
|
protected _passID: number;
|
|
protected _layoutName: string;
|
|
protected _viewport: Viewport | null = null;
|
|
private _rasterInfo: RasterPassInfo;
|
|
private _layout: RenderPassLayoutInfo | null = null;
|
|
constructor (passInfo: RasterPassInfo) {
|
|
this._rasterInfo = passInfo;
|
|
const device = context.device;
|
|
this._layoutName = context.renderGraph.getLayout(passInfo.id);
|
|
this._passID = cclegacy.rendering.getPassID(this._layoutName);
|
|
const depthStencilAttachment = new DepthStencilAttachment();
|
|
depthStencilAttachment.format = Format.DEPTH_STENCIL;
|
|
depthStencilAttachment.depthLoadOp = LoadOp.DISCARD;
|
|
depthStencilAttachment.stencilLoadOp = LoadOp.DISCARD;
|
|
depthStencilAttachment.stencilStoreOp = StoreOp.DISCARD;
|
|
depthStencilAttachment.depthStoreOp = StoreOp.DISCARD;
|
|
|
|
const colors: ColorAttachment[] = [];
|
|
const colorTexs: Texture[] = [];
|
|
let depthTex: Texture | null = null;
|
|
let swapchain: Swapchain | null = null;
|
|
let framebuffer: Framebuffer | null = null;
|
|
for (const cv of passInfo.pass.computeViews) {
|
|
this._applyRenderLayout(cv);
|
|
}
|
|
// update the layout descriptorSet
|
|
if (this.renderLayout && this.renderLayout.descriptorSet) {
|
|
this.renderLayout.descriptorSet.update();
|
|
}
|
|
for (const [resName, rasterV] of passInfo.pass.rasterViews) {
|
|
let resTex = context.deviceTextures.get(resName);
|
|
if (!resTex) {
|
|
this.visitResource(resName);
|
|
resTex = context.deviceTextures.get(resName)!;
|
|
} else {
|
|
const resGraph = context.resourceGraph;
|
|
const resId = resGraph.vertex(resName);
|
|
const resFbo = resGraph._vertices[resId]._object;
|
|
if (resTex.framebuffer && resFbo instanceof Framebuffer && resTex.framebuffer !== resFbo) {
|
|
resTex.framebuffer = resFbo;
|
|
} else if (resTex.texture) {
|
|
const desc = resGraph.getDesc(resId);
|
|
if (resTex.texture.width !== desc.width || resTex.texture.height !== desc.height) {
|
|
resTex.texture.resize(desc.width, desc.height);
|
|
}
|
|
}
|
|
}
|
|
if (!swapchain) swapchain = resTex.swapchain;
|
|
if (!framebuffer) framebuffer = resTex.framebuffer;
|
|
const clearFlag = rasterV.clearFlags & 0xffffffff;
|
|
switch (rasterV.attachmentType) {
|
|
case AttachmentType.RENDER_TARGET:
|
|
{
|
|
if (!resTex.swapchain && !resTex.framebuffer) colorTexs.push(resTex.texture!);
|
|
const colorAttachment = new ColorAttachment();
|
|
colorAttachment.format = resTex.description!.format;
|
|
colorAttachment.sampleCount = resTex.description!.sampleCount;
|
|
colorAttachment.loadOp = rasterV.loadOp;
|
|
colorAttachment.storeOp = rasterV.storeOp;
|
|
colorAttachment.barrier = device.getGeneralBarrier(new GeneralBarrierInfo(
|
|
rasterV.loadOp === LoadOp.LOAD ? AccessFlagBit.COLOR_ATTACHMENT_WRITE : AccessFlagBit.NONE,
|
|
rasterV.storeOp === StoreOp.STORE ? AccessFlagBit.COLOR_ATTACHMENT_WRITE : AccessFlagBit.NONE,
|
|
));
|
|
this._clearColor.push(rasterV.clearColor);
|
|
colors.push(colorAttachment);
|
|
}
|
|
break;
|
|
case AttachmentType.DEPTH_STENCIL:
|
|
depthStencilAttachment.depthStoreOp = rasterV.storeOp;
|
|
depthStencilAttachment.stencilStoreOp = rasterV.storeOp;
|
|
depthStencilAttachment.depthLoadOp = rasterV.loadOp;
|
|
depthStencilAttachment.stencilLoadOp = rasterV.loadOp;
|
|
depthStencilAttachment.barrier = device.getGeneralBarrier(new GeneralBarrierInfo(
|
|
rasterV.loadOp === LoadOp.LOAD ? AccessFlagBit.DEPTH_STENCIL_ATTACHMENT_WRITE : AccessFlagBit.NONE,
|
|
rasterV.storeOp === StoreOp.STORE ? AccessFlagBit.DEPTH_STENCIL_ATTACHMENT_WRITE : AccessFlagBit.NONE,
|
|
));
|
|
if (!resTex.swapchain && !resTex.framebuffer) depthTex = resTex.texture!;
|
|
this._clearDepth = rasterV.clearColor.x;
|
|
this._clearStencil = rasterV.clearColor.y;
|
|
break;
|
|
case AttachmentType.SHADING_RATE:
|
|
// noop
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
if (colors.length === 0) {
|
|
const colorAttachment = new ColorAttachment();
|
|
colors.push(colorAttachment);
|
|
}
|
|
if (colorTexs.length === 0 && !swapchain && !framebuffer) {
|
|
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;
|
|
}
|
|
this._renderPass = device.createRenderPass(new RenderPassInfo(colors, depthStencilAttachment));
|
|
this._framebuffer = framebuffer || device.createFramebuffer(new FramebufferInfo(
|
|
this._renderPass,
|
|
swapchain ? [swapchain.colorTexture] : colorTexs,
|
|
swapchain ? swapchain.depthStencilTexture : depthTex,
|
|
));
|
|
}
|
|
get layoutName (): string { return this._layoutName; }
|
|
get passID (): number { return this._passID; }
|
|
get renderLayout (): RenderPassLayoutInfo | null { return this._layout; }
|
|
get renderPass (): RenderPass { return this._renderPass; }
|
|
get framebuffer (): Framebuffer { return this._framebuffer; }
|
|
get clearColor (): Color[] { return this._clearColor; }
|
|
get clearDepth (): number { return this._clearDepth; }
|
|
get clearStencil (): number { return this._clearStencil; }
|
|
get deviceQueues (): DeviceRenderQueue[] { return this._deviceQueues; }
|
|
get rasterPassInfo (): RasterPassInfo { return this._rasterInfo; }
|
|
get viewport (): Viewport | null { return this._viewport; }
|
|
visitResource (resName: string): void {
|
|
const resourceGraph = context.resourceGraph;
|
|
const vertId = resourceGraph.vertex(resName);
|
|
resourceVisitor.resName = resName;
|
|
resourceGraph.visitVertex(resourceVisitor, vertId);
|
|
}
|
|
addQueue (queue: DeviceRenderQueue): void { this._deviceQueues.push(queue); }
|
|
prePass (): void {
|
|
context.descriptorSet = getDescriptorSetDataFromLayout(this.layoutName)!.descriptorSet;
|
|
for (const queue of this._deviceQueues) {
|
|
queue.preRecord();
|
|
}
|
|
}
|
|
protected _applyRenderLayout (input: [string, ComputeView[]]): void {
|
|
const stageName = context.renderGraph.getLayout(this.rasterPassInfo.id);
|
|
if (stageName) {
|
|
const layoutGraph = context.layoutGraph;
|
|
const stageId = layoutGraph.locateChild(layoutGraph.nullVertex(), stageName);
|
|
if (stageId !== 0xFFFFFFFF) {
|
|
this._layout = new RenderPassLayoutInfo(stageId, input);
|
|
}
|
|
}
|
|
}
|
|
getGlobalDescData (): DescriptorSetData {
|
|
const stageId = context.layoutGraph.locateChild(context.layoutGraph.nullVertex(), 'default');
|
|
assert(stageId !== 0xFFFFFFFF);
|
|
const layout = context.layoutGraph.getLayout(stageId);
|
|
const layoutData = layout.descriptorSets.get(UpdateFrequency.PER_PASS)!;
|
|
return layoutData;
|
|
}
|
|
|
|
protected _applyViewport (frameTex: Texture): void {
|
|
this._viewport = null;
|
|
const viewport = this._rasterInfo.pass.viewport;
|
|
if (viewport.left !== 0
|
|
|| viewport.top !== 0
|
|
|| viewport.width !== 0
|
|
|| viewport.height !== 0) {
|
|
this._viewport = viewport;
|
|
}
|
|
}
|
|
|
|
protected _showProfiler (rect: Rect): void {
|
|
const profiler = context.pipeline.profiler!;
|
|
if (!profiler || !profiler.enabled) {
|
|
return;
|
|
}
|
|
const renderPass = this._renderPass;
|
|
const cmdBuff = context.commandBuffer;
|
|
const submodel = profiler.subModels[0];
|
|
const pass = submodel.passes[0];
|
|
const ia = submodel.inputAssembler;
|
|
const device = context.device;
|
|
const pso = PipelineStateManager.getOrCreatePipelineState(
|
|
device,
|
|
pass,
|
|
submodel.shaders[0],
|
|
renderPass,
|
|
ia,
|
|
);
|
|
const descData = getDescriptorSetDataFromLayoutId(pass.passID)!;
|
|
mergeSrcToTargetDesc(descData.descriptorSet, context.descriptorSet, true);
|
|
profilerViewport.width = rect.width;
|
|
profilerViewport.height = rect.height;
|
|
cmdBuff.setViewport(profilerViewport);
|
|
cmdBuff.setScissor(rect);
|
|
cmdBuff.bindPipelineState(pso);
|
|
cmdBuff.bindDescriptorSet(SetIndex.MATERIAL, pass.descriptorSet);
|
|
cmdBuff.bindDescriptorSet(SetIndex.LOCAL, submodel.descriptorSet);
|
|
cmdBuff.bindInputAssembler(ia);
|
|
cmdBuff.draw(ia);
|
|
}
|
|
|
|
// record common buffer
|
|
record (): void {
|
|
const tex = this.framebuffer.colorTextures[0]!;
|
|
this._applyViewport(tex);
|
|
const cmdBuff = context.commandBuffer;
|
|
if (this._viewport) {
|
|
renderPassArea.x = this._viewport.left;
|
|
renderPassArea.y = this._viewport.top;
|
|
renderPassArea.width = this._viewport.width;
|
|
renderPassArea.height = this._viewport.height;
|
|
} else {
|
|
renderPassArea.y = renderPassArea.x = 0;
|
|
renderPassArea.width = tex.width;
|
|
renderPassArea.height = tex.height;
|
|
}
|
|
cmdBuff.beginRenderPass(
|
|
this.renderPass,
|
|
this.framebuffer,
|
|
renderPassArea,
|
|
this.clearColor,
|
|
this.clearDepth,
|
|
this.clearStencil,
|
|
);
|
|
cmdBuff.bindDescriptorSet(
|
|
SetIndex.GLOBAL,
|
|
context.descriptorSet!,
|
|
);
|
|
for (const queue of this._deviceQueues) {
|
|
queue.record();
|
|
}
|
|
if (this._rasterInfo.pass.showStatistics) {
|
|
this._showProfiler(renderPassArea);
|
|
}
|
|
cmdBuff.endRenderPass();
|
|
}
|
|
|
|
postPass (): void {
|
|
// this.submitMap.clear();
|
|
for (const queue of this._deviceQueues) {
|
|
queue.postRecord();
|
|
}
|
|
}
|
|
resetResource (id: number, pass: RasterPass): void {
|
|
this._rasterInfo.applyInfo(id, pass);
|
|
this._layoutName = context.renderGraph.getLayout(id);
|
|
this._passID = cclegacy.rendering.getPassID(this._layoutName);
|
|
this._deviceQueues.length = 0;
|
|
let framebuffer: Framebuffer | null = null;
|
|
const colTextures: Texture[] = [];
|
|
let depTexture = this._framebuffer ? this._framebuffer.depthStencilTexture : null;
|
|
for (const cv of this._rasterInfo.pass.computeViews) {
|
|
this._applyRenderLayout(cv);
|
|
}
|
|
// update the layout descriptorSet
|
|
if (this.renderLayout && this.renderLayout.descriptorSet) {
|
|
this.renderLayout.descriptorSet.update();
|
|
}
|
|
|
|
const resGraph = context.resourceGraph;
|
|
const currentWidth = this._framebuffer ? this._framebuffer.width : 0;
|
|
const currentHeight = this._framebuffer ? this._framebuffer.height : 0;
|
|
|
|
let width = 0;
|
|
let height = 0;
|
|
for (const [resName, rasterV] of this._rasterInfo.pass.rasterViews) {
|
|
if (rasterV.attachmentType === AttachmentType.SHADING_RATE) {
|
|
continue;
|
|
}
|
|
const resId = resGraph.vertex(resName);
|
|
const resDesc = resGraph.getDesc(resId);
|
|
width = resDesc.width;
|
|
height = resDesc.height;
|
|
break;
|
|
}
|
|
const needRebuild = (width !== currentWidth) || (height !== currentHeight);
|
|
|
|
for (const [resName, rasterV] of this._rasterInfo.pass.rasterViews) {
|
|
let deviceTex = context.deviceTextures.get(resName)!;
|
|
const currTex = deviceTex;
|
|
if (!deviceTex) {
|
|
this.visitResource(resName);
|
|
deviceTex = context.deviceTextures.get(resName)!;
|
|
}
|
|
const resGraph = context.resourceGraph;
|
|
const resId = resGraph.vertex(resName);
|
|
const resFbo = resGraph._vertices[resId]._object;
|
|
const resDesc = resGraph.getDesc(resId);
|
|
if (deviceTex.framebuffer && resFbo instanceof Framebuffer && deviceTex.framebuffer !== resFbo) {
|
|
framebuffer = this._framebuffer = deviceTex.framebuffer = resFbo;
|
|
} else if (!currTex || (deviceTex.texture && needRebuild)) {
|
|
const gfxTex = deviceTex.texture!;
|
|
if (currTex) gfxTex.resize(resDesc.width, resDesc.height);
|
|
switch (rasterV.attachmentType) {
|
|
case AttachmentType.RENDER_TARGET:
|
|
colTextures.push(gfxTex);
|
|
break;
|
|
case AttachmentType.DEPTH_STENCIL:
|
|
depTexture = gfxTex;
|
|
break;
|
|
case AttachmentType.SHADING_RATE:
|
|
// noop
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
if (!framebuffer && colTextures.length) {
|
|
this._framebuffer.destroy();
|
|
this._framebuffer = context.device.createFramebuffer(new FramebufferInfo(
|
|
this._renderPass,
|
|
colTextures,
|
|
depTexture,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
class ComputePassInfo {
|
|
protected _id!: number;
|
|
protected _pass!: ComputePass;
|
|
get id (): number { return this._id; }
|
|
get pass (): ComputePass { return this._pass; }
|
|
private _copyPass (pass: ComputePass): void {
|
|
const computePass = this._pass || new ComputePass();
|
|
for (const val of pass.computeViews) {
|
|
const currComputeViews = val[1];
|
|
const currComputeKey = val[0];
|
|
const computeViews: ComputeView[] = computePass.computeViews.get(currComputeKey) || [];
|
|
if (computeViews.length) computeViews.length = currComputeViews.length;
|
|
let idx = 0;
|
|
for (const currComputeView of currComputeViews) {
|
|
const computeView = computeViews[idx] || new ComputeView();
|
|
computeView.name = currComputeView.name;
|
|
computeView.accessType = currComputeView.accessType;
|
|
computeView.clearFlags = currComputeView.clearFlags;
|
|
computeView.clearValue.x = currComputeView.clearValue.x;
|
|
computeView.clearValue.y = currComputeView.clearValue.y;
|
|
computeView.clearValue.z = currComputeView.clearValue.z;
|
|
computeView.clearValue.w = currComputeView.clearValue.w;
|
|
computeView.clearValueType = currComputeView.clearValueType;
|
|
computeViews[idx] = computeView;
|
|
idx++;
|
|
}
|
|
computePass.computeViews.set(currComputeKey, computeViews);
|
|
}
|
|
this._pass = computePass;
|
|
}
|
|
applyInfo (id: number, pass: ComputePass): void {
|
|
this._id = id;
|
|
this._copyPass(pass);
|
|
}
|
|
}
|
|
|
|
class DeviceComputePass {
|
|
protected _deviceQueues: DeviceComputeQueue[] = [];
|
|
protected _passID: number;
|
|
protected _layoutName: string;
|
|
protected _viewport: Viewport | null = null;
|
|
private _computeInfo: ComputePassInfo;
|
|
private _layout: RenderPassLayoutInfo | null = null;
|
|
constructor (passInfo: ComputePassInfo) {
|
|
this._computeInfo = passInfo;
|
|
this._layoutName = context.renderGraph.getLayout(passInfo.id);
|
|
this._passID = cclegacy.rendering.getPassID(this._layoutName);
|
|
|
|
for (const cv of passInfo.pass.computeViews) {
|
|
let resTex = context.deviceTextures.get(cv[0]);
|
|
if (!resTex) {
|
|
this.visitResource(cv[0]);
|
|
resTex = context.deviceTextures.get(cv[0])!;
|
|
}
|
|
this._applyRenderLayout(cv);
|
|
}
|
|
// update the layout descriptorSet
|
|
if (this.renderLayout && this.renderLayout.descriptorSet) {
|
|
this.renderLayout.descriptorSet.update();
|
|
}
|
|
}
|
|
get layoutName (): string { return this._layoutName; }
|
|
get passID (): number { return this._passID; }
|
|
get renderLayout (): RenderPassLayoutInfo | null { return this._layout; }
|
|
|
|
get deviceQueues (): DeviceComputeQueue[] { return this._deviceQueues; }
|
|
get computePassInfo (): ComputePassInfo { return this._computeInfo; }
|
|
visitResource (resName: string): void {
|
|
const resourceGraph = context.resourceGraph;
|
|
const vertId = resourceGraph.vertex(resName);
|
|
resourceVisitor.resName = resName;
|
|
resourceGraph.visitVertex(resourceVisitor, vertId);
|
|
}
|
|
addQueue (queue: DeviceComputeQueue): void { this._deviceQueues.push(queue); }
|
|
prePass (): void {
|
|
// noop
|
|
}
|
|
protected _applyRenderLayout (input: [string, ComputeView[]]): void {
|
|
const stageName = context.renderGraph.getLayout(this._computeInfo.id);
|
|
if (stageName) {
|
|
const layoutGraph = context.layoutGraph;
|
|
const stageId = layoutGraph.locateChild(layoutGraph.nullVertex(), stageName);
|
|
if (stageId !== 0xFFFFFFFF) {
|
|
this._layout = new RenderPassLayoutInfo(stageId, input);
|
|
}
|
|
}
|
|
}
|
|
getGlobalDescData (): DescriptorSetData {
|
|
const stageId = context.layoutGraph.locateChild(context.layoutGraph.nullVertex(), 'default');
|
|
assert(stageId !== 0xFFFFFFFF);
|
|
const layout = context.layoutGraph.getLayout(stageId);
|
|
const layoutData = layout.descriptorSets.get(UpdateFrequency.PER_PASS)!;
|
|
return layoutData;
|
|
}
|
|
|
|
// record common buffer
|
|
record (): void {
|
|
const cmdBuff = context.commandBuffer;
|
|
|
|
cmdBuff.bindDescriptorSet(
|
|
SetIndex.GLOBAL,
|
|
context.descriptorSet!,
|
|
);
|
|
for (const queue of this._deviceQueues) {
|
|
queue.record();
|
|
}
|
|
const renderData = context.renderGraph.getData(this._computeInfo.id);
|
|
updateGlobalDescBinding(renderData, context.renderGraph.getLayout(this._computeInfo.id));
|
|
}
|
|
|
|
postPass (): void {
|
|
// noop
|
|
}
|
|
resetResource (id: number, pass: ComputePass): void {
|
|
this._computeInfo.applyInfo(id, pass);
|
|
this._layoutName = context.renderGraph.getLayout(id);
|
|
this._passID = cclegacy.rendering.getPassID(this._layoutName);
|
|
this._deviceQueues.length = 0;
|
|
const colTextures: Texture[] = [];
|
|
for (const cv of this._computeInfo.pass.computeViews) {
|
|
this._applyRenderLayout(cv);
|
|
}
|
|
// update the layout descriptorSet
|
|
if (this.renderLayout && this.renderLayout.descriptorSet) {
|
|
this.renderLayout.descriptorSet.update();
|
|
}
|
|
}
|
|
}
|
|
|
|
class DeviceSceneTransversal extends WebSceneTransversal {
|
|
protected _currentQueue: DeviceRenderQueue;
|
|
protected _graphScene: GraphScene;
|
|
protected _preSceneTask: DevicePreSceneTask | undefined;
|
|
protected _sceneTask: DeviceSceneTask | undefined;
|
|
protected _postSceneTask: DevicePostSceneTask | undefined;
|
|
constructor (quque: DeviceRenderQueue, sceneData: PipelineSceneData, graphSceneData: GraphScene) {
|
|
const camera = graphSceneData.scene ? graphSceneData.scene.camera : null;
|
|
super(camera, sceneData, context.ubo);
|
|
this._currentQueue = quque;
|
|
this._graphScene = graphSceneData;
|
|
}
|
|
set graphScene (graphScene: GraphScene) {
|
|
this._graphScene = graphScene;
|
|
this._camera = graphScene.scene ? graphScene.scene.camera : null;
|
|
if (this._camera) this._scene = this._camera.scene!;
|
|
}
|
|
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);
|
|
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);
|
|
return this._sceneTask;
|
|
}
|
|
public postRenderPass (visitor: WebSceneVisitor): DevicePostSceneTask {
|
|
if (!this._postSceneTask) {
|
|
this._postSceneTask = new DevicePostSceneTask(this._sceneData, context.ubo, this._camera, visitor);
|
|
}
|
|
return this._postSceneTask;
|
|
}
|
|
}
|
|
class GraphScene {
|
|
scene: SceneData | null = null;
|
|
blit: Blit | null = null;
|
|
dispatch: Dispatch | null = null;
|
|
sceneID = -1;
|
|
private _copyScene (scene: SceneData | null): void {
|
|
if (scene) {
|
|
if (!this.scene) {
|
|
this.scene = new SceneData();
|
|
}
|
|
this.scene.scene = scene.scene;
|
|
this.scene.light.level = scene.light.level;
|
|
this.scene.light.light = scene.light.light;
|
|
this.scene.flags = scene.flags;
|
|
this.scene.camera = scene.camera;
|
|
return;
|
|
}
|
|
this.scene = null;
|
|
}
|
|
private _copyBlit (blit: Blit | null): void {
|
|
if (blit) {
|
|
if (!this.blit) {
|
|
this.blit = new Blit(blit.material, blit.passID, blit.sceneFlags, blit.camera);
|
|
}
|
|
this.blit.material = blit.material;
|
|
this.blit.passID = blit.passID;
|
|
this.blit.sceneFlags = blit.sceneFlags;
|
|
this.blit.camera = blit.camera;
|
|
return;
|
|
}
|
|
this.blit = null;
|
|
}
|
|
init (scene: SceneData | null, blit: Blit | null, sceneID): void {
|
|
this._copyScene(scene);
|
|
this._copyBlit(blit);
|
|
this.sceneID = sceneID;
|
|
}
|
|
}
|
|
class DevicePreSceneTask extends WebSceneTask {
|
|
protected _currentQueue: DeviceRenderQueue;
|
|
protected _renderPass: RenderPass;
|
|
protected _submitInfo: SubmitInfo | null = null;
|
|
protected _graphScene: GraphScene;
|
|
private _cmdBuff: CommandBuffer;
|
|
constructor (queue: DeviceRenderQueue, graphScene: GraphScene, visitor: SceneVisitor) {
|
|
super(
|
|
context.pipelineSceneData,
|
|
context.ubo,
|
|
graphScene.scene && graphScene.scene.camera ? graphScene.scene.camera : null,
|
|
visitor,
|
|
);
|
|
this._currentQueue = queue;
|
|
this._graphScene = graphScene;
|
|
this._renderPass = queue.devicePass.renderPass;
|
|
this._cmdBuff = context.commandBuffer;
|
|
}
|
|
apply (queue: DeviceRenderQueue, graphScene: GraphScene): void {
|
|
this._currentQueue = queue;
|
|
this._graphScene = graphScene;
|
|
this._renderPass = queue.devicePass.renderPass;
|
|
this._cmdBuff = context.commandBuffer;
|
|
const camera = graphScene.scene && graphScene.scene.camera ? graphScene.scene.camera : null;
|
|
if (camera) {
|
|
this._scene = camera.scene!;
|
|
this._camera = camera;
|
|
}
|
|
}
|
|
get graphScene (): GraphScene { return this._graphScene; }
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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;
|
|
protected _renderPass: RenderPass;
|
|
protected _graphScene: GraphScene;
|
|
constructor (queue: DeviceRenderQueue, graphScene: GraphScene, visitor: SceneVisitor) {
|
|
super(
|
|
context.pipelineSceneData,
|
|
context.ubo,
|
|
graphScene.scene && graphScene.scene.camera ? graphScene.scene.camera : null,
|
|
visitor,
|
|
);
|
|
this._currentQueue = queue;
|
|
this._renderPass = this._currentQueue.devicePass.renderPass;
|
|
this._graphScene = graphScene;
|
|
}
|
|
apply (queue: DeviceRenderQueue, graphScene: GraphScene): void {
|
|
this._currentQueue = queue;
|
|
this._graphScene = graphScene;
|
|
this._renderPass = queue.devicePass.renderPass;
|
|
const camera = graphScene.scene && graphScene.scene.camera ? graphScene.scene.camera : null;
|
|
if (camera) {
|
|
this._scene = camera.scene!;
|
|
this._camera = camera;
|
|
}
|
|
}
|
|
get graphScene (): GraphScene { return this._graphScene; }
|
|
public start (): void {
|
|
// noop
|
|
}
|
|
|
|
protected _recordUI (): void {
|
|
const devicePass = this._currentQueue.devicePass;
|
|
const rasterId = devicePass.rasterPassInfo.id;
|
|
const passRenderData = context.renderGraph.getData(rasterId);
|
|
// CCGlobal
|
|
this._updateGlobal(passRenderData);
|
|
// CCCamera, CCShadow, CCCSM
|
|
const queueId = this._currentQueue.queueId;
|
|
const queueRenderData = context.renderGraph.getData(queueId)!;
|
|
this._updateGlobal(queueRenderData);
|
|
|
|
const layoutName = context.renderGraph.getLayout(rasterId);
|
|
const descSetData = getDescriptorSetDataFromLayout(layoutName);
|
|
if (context.descriptorSet) {
|
|
mergeSrcToTargetDesc(descSetData!.descriptorSet, context.descriptorSet, true);
|
|
}
|
|
this._currentQueue.isUpdateUBO = true;
|
|
|
|
const batches = this.camera!.scene!.batches;
|
|
for (let i = 0; i < batches.length; i++) {
|
|
const batch = batches[i];
|
|
let visible = false;
|
|
if (this.camera!.visibility & batch.visFlags) {
|
|
visible = true;
|
|
}
|
|
|
|
if (!visible) continue;
|
|
// shaders.length always equals actual used passes.length
|
|
const count = batch.shaders.length;
|
|
for (let j = 0; j < count; j++) {
|
|
const pass = batch.passes[j];
|
|
if (pass.phaseID !== this._currentQueue.phaseID) continue;
|
|
const shader = batch.shaders[j];
|
|
const inputAssembler: InputAssembler = batch.inputAssembler!;
|
|
const pso = PipelineStateManager.getOrCreatePipelineState(deviceManager.gfxDevice, pass, shader, this._renderPass, inputAssembler);
|
|
this.visitor.bindPipelineState(pso);
|
|
this.visitor.bindDescriptorSet(SetIndex.MATERIAL, pass.descriptorSet);
|
|
const ds = batch.descriptorSet!;
|
|
this.visitor.bindDescriptorSet(SetIndex.LOCAL, ds);
|
|
this.visitor.bindInputAssembler(inputAssembler);
|
|
this.visitor.draw(inputAssembler);
|
|
}
|
|
}
|
|
}
|
|
|
|
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++) {
|
|
const currDesc = toGpuDesc.gpuDescriptors[extResId[i]];
|
|
if (currDesc.gpuBuffer) currDesc.gpuBuffer = null;
|
|
else if (currDesc.gpuTextureView) {
|
|
currDesc.gpuTextureView = null;
|
|
currDesc.gpuSampler = null;
|
|
} else if (currDesc.gpuTexture) {
|
|
currDesc.gpuTexture = null;
|
|
currDesc.gpuSampler = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private _recordBlit (): void {
|
|
if (!this.graphScene.blit) { return; }
|
|
|
|
const blit = this.graphScene.blit;
|
|
const currMat = blit.material;
|
|
const pass = currMat!.passes[blit.passID];
|
|
pass.update();
|
|
const shader = pass.getShaderVariant();
|
|
const devicePass = this._currentQueue.devicePass;
|
|
const screenIa: InputAssembler = this._currentQueue.blitDesc!.screenQuad!.quadIA!;
|
|
const globalDesc = context.descriptorSet;
|
|
let pso: PipelineState | null = null;
|
|
if (pass !== null && shader !== null && screenIa !== null) {
|
|
pso = PipelineStateManager.getOrCreatePipelineState(
|
|
context.device,
|
|
pass,
|
|
shader,
|
|
devicePass.renderPass,
|
|
screenIa,
|
|
);
|
|
}
|
|
if (pso) {
|
|
this.visitor.bindPipelineState(pso);
|
|
const layoutStage = devicePass.renderLayout;
|
|
const layoutDesc = layoutStage!.descriptorSet!;
|
|
const extResId: number[] = [];
|
|
// if (isEnableEffect()) this.visitor.bindDescriptorSet(SetIndex.GLOBAL, layoutDesc);
|
|
this.visitor.bindDescriptorSet(SetIndex.MATERIAL, pass.descriptorSet);
|
|
this.visitor.bindDescriptorSet(SetIndex.LOCAL, this._currentQueue.blitDesc!.stageDesc!);
|
|
this.visitor.bindInputAssembler(screenIa);
|
|
this.visitor.draw(screenIa);
|
|
// The desc data obtained from the outside should be cleaned up so that the data can be modified
|
|
this._clearExtBlitDesc(layoutDesc, extResId);
|
|
}
|
|
}
|
|
|
|
protected _updateGlobal (data: RenderData): void {
|
|
const devicePass = this._currentQueue.devicePass;
|
|
updateGlobalDescBinding(data, context.renderGraph.getLayout(devicePass.rasterPassInfo.id));
|
|
}
|
|
|
|
protected _updateRenderData (): void {
|
|
if (this._currentQueue.isUpdateUBO) return;
|
|
const devicePass = this._currentQueue.devicePass;
|
|
const rasterId = devicePass.rasterPassInfo.id;
|
|
const passRenderData = context.renderGraph.getData(rasterId);
|
|
// CCGlobal
|
|
this._updateGlobal(passRenderData);
|
|
// CCCamera, CCShadow, CCCSM
|
|
const queueId = this._currentQueue.queueId;
|
|
const queueRenderData = context.renderGraph.getData(queueId)!;
|
|
this._updateGlobal(queueRenderData);
|
|
|
|
const layoutName = context.renderGraph.getLayout(rasterId);
|
|
const descSetData = getDescriptorSetDataFromLayout(layoutName);
|
|
mergeSrcToTargetDesc(descSetData!.descriptorSet, context.descriptorSet, true);
|
|
this._currentQueue.isUpdateUBO = true;
|
|
}
|
|
|
|
private _applyViewport (): void {
|
|
const queueViewport = this._currentQueue.viewport;
|
|
if (queueViewport) {
|
|
this.visitor.setViewport(queueViewport);
|
|
this.visitor.setScissor(this._currentQueue.scissor!);
|
|
} else if (!this._currentQueue.devicePass.viewport) {
|
|
const texture = this._currentQueue.devicePass.framebuffer.colorTextures[0]!;
|
|
const graphScene = this.graphScene;
|
|
const lightInfo = graphScene.scene ? graphScene.scene.light : null;
|
|
const area = isShadowMap(this.graphScene) && graphScene.scene && lightInfo!.light
|
|
? getRenderArea(this.camera!, texture.width, texture.height, lightInfo!.light, lightInfo!.level)
|
|
: getRenderArea(this.camera!, texture.width, texture.height);
|
|
sceneViewport.left = area.x;
|
|
sceneViewport.top = area.y;
|
|
sceneViewport.width = area.width;
|
|
sceneViewport.height = area.height;
|
|
this.visitor.setViewport(sceneViewport);
|
|
this.visitor.setScissor(area);
|
|
}
|
|
}
|
|
|
|
private _recordAdditiveLights (): void {
|
|
context.additiveLight?.recordCommandBuffer(
|
|
context.device,
|
|
this._renderPass,
|
|
context.commandBuffer,
|
|
);
|
|
}
|
|
|
|
public submit (): void {
|
|
const devicePass = this._currentQueue.devicePass;
|
|
const sceneCulling = context.culling;
|
|
this._updateRenderData();
|
|
this._applyViewport();
|
|
// Currently processing blit and camera first
|
|
if (this.graphScene.blit) {
|
|
this._recordBlit();
|
|
return;
|
|
}
|
|
const renderQueueDesc = sceneCulling.sceneQueryIndex.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);
|
|
if (graphSceneData.flags & SceneFlags.GEOMETRY) {
|
|
this.camera!.geometryRenderer?.render(
|
|
devicePass.renderPass,
|
|
context.commandBuffer,
|
|
context.pipeline.pipelineSceneData,
|
|
);
|
|
}
|
|
if (graphSceneData.flags & SceneFlags.UI) {
|
|
this._recordUI();
|
|
}
|
|
if (graphSceneData.flags & SceneFlags.REFLECTION_PROBE) {
|
|
this._recordReflectionProbe();
|
|
}
|
|
}
|
|
}
|
|
|
|
class DevicePostSceneTask extends WebSceneTask { }
|
|
|
|
class ExecutorPools {
|
|
constructor (context: ExecutorContext) {
|
|
this.deviceQueuePool = new memop.RecyclePool<DeviceRenderQueue>((): DeviceRenderQueue => new DeviceRenderQueue(), 16);
|
|
this.computeQueuePool = new memop.RecyclePool<DeviceComputeQueue>((): DeviceComputeQueue => new DeviceComputeQueue(), 16);
|
|
this.graphScenePool = new memop.RecyclePool<GraphScene>((): GraphScene => new GraphScene(), 16);
|
|
this.rasterPassInfoPool = new memop.RecyclePool<RasterPassInfo>((): RasterPassInfo => new RasterPassInfo(), 16);
|
|
this.computePassInfoPool = new memop.RecyclePool<ComputePassInfo>((): ComputePassInfo => new ComputePassInfo(), 16);
|
|
this.reflectionProbe = new memop.RecyclePool<RenderReflectionProbeQueue>((): RenderReflectionProbeQueue => new RenderReflectionProbeQueue(context.pipeline), 8);
|
|
this.passPool = new memop.RecyclePool<IRenderPass>((): { priority: number; hash: number; depth: number; shaderId: number; subModel: any; passIdx: number; } => ({
|
|
priority: 0,
|
|
hash: 0,
|
|
depth: 0,
|
|
shaderId: 0,
|
|
subModel: null!,
|
|
passIdx: 0,
|
|
}), 64);
|
|
}
|
|
addPassInfo (): IRenderPass {
|
|
return this.passPool.add();
|
|
}
|
|
resetPassInfo (): void {
|
|
this.passPool.reset();
|
|
}
|
|
addDeviceQueue (): DeviceRenderQueue {
|
|
return this.deviceQueuePool.add();
|
|
}
|
|
addComputeQueue (): DeviceComputeQueue {
|
|
return this.computeQueuePool.add();
|
|
}
|
|
addGraphScene (): GraphScene {
|
|
return this.graphScenePool.add();
|
|
}
|
|
addReflectionProbe (): RenderReflectionProbeQueue {
|
|
return this.reflectionProbe.add();
|
|
}
|
|
addRasterPassInfo (): RasterPassInfo {
|
|
return this.rasterPassInfoPool.add();
|
|
}
|
|
addComputePassInfo (): ComputePassInfo {
|
|
return this.computePassInfoPool.add();
|
|
}
|
|
reset (): void {
|
|
this.deviceQueuePool.reset();
|
|
this.computeQueuePool.reset();
|
|
this.graphScenePool.reset();
|
|
this.reflectionProbe.reset();
|
|
this.resetPassInfo();
|
|
this.computePassInfoPool.reset();
|
|
}
|
|
readonly deviceQueuePool: memop.RecyclePool<DeviceRenderQueue>;
|
|
readonly computeQueuePool: memop.RecyclePool<DeviceComputeQueue>;
|
|
readonly graphScenePool: memop.RecyclePool<GraphScene>;
|
|
readonly reflectionProbe: memop.RecyclePool<RenderReflectionProbeQueue>;
|
|
readonly passPool: memop.RecyclePool<IRenderPass>;
|
|
readonly rasterPassInfoPool: memop.RecyclePool<RasterPassInfo>;
|
|
readonly computePassInfoPool: memop.RecyclePool<ComputePassInfo>;
|
|
}
|
|
|
|
const vbData = new Float32Array(4 * 4);
|
|
const quadRect = new Rect();
|
|
// The attribute length of the volume light
|
|
const volLightAttrCount = 5;
|
|
|
|
class BlitInfo {
|
|
private _pipelineIAData: PipelineInputAssemblerData;
|
|
private _context: ExecutorContext;
|
|
private _width: number;
|
|
private _height: number;
|
|
private _lightVolumeBuffer!: Buffer;
|
|
private _lightBufferData!: Float32Array;
|
|
private _deferredLitsBufView!: Buffer;
|
|
private _localUBO!: Buffer;
|
|
private _stageDescs: Map<Pass, DescriptorSet> = new Map();
|
|
get pipelineIAData (): PipelineInputAssemblerData {
|
|
return this._pipelineIAData;
|
|
}
|
|
get deferredLitsBufView (): Buffer {
|
|
return this._deferredLitsBufView;
|
|
}
|
|
get lightVolumeBuffer (): Buffer {
|
|
return this._lightVolumeBuffer;
|
|
}
|
|
get lightBufferData (): Float32Array {
|
|
return this._lightBufferData;
|
|
}
|
|
get stageDescs (): Map<Pass, DescriptorSet> {
|
|
return this._stageDescs;
|
|
}
|
|
get emptyLocalUBO (): Buffer {
|
|
return this._localUBO;
|
|
}
|
|
constructor (context: ExecutorContext) {
|
|
this._context = context;
|
|
this._width = context.width;
|
|
this._height = context.height;
|
|
this._pipelineIAData = this._createQuadInputAssembler();
|
|
const vb = this._genQuadVertexData(SurfaceTransform.IDENTITY, new Rect(0, 0, context.width, context.height));
|
|
this._pipelineIAData.quadVB!.update(vb);
|
|
this._createLightVolumes();
|
|
this._localUBO = context.device.createBuffer(new BufferInfo(
|
|
BufferUsageBit.UNIFORM | BufferUsageBit.TRANSFER_DST,
|
|
MemoryUsageBit.DEVICE,
|
|
UBOLocal.SIZE,
|
|
UBOLocal.SIZE,
|
|
));
|
|
}
|
|
|
|
resize (width, height): void {
|
|
if (width !== this._width || height !== this._height) {
|
|
quadRect.y = quadRect.x = 0;
|
|
quadRect.width = width;
|
|
quadRect.height = height;
|
|
const vb = this._genQuadVertexData(SurfaceTransform.IDENTITY, quadRect);
|
|
this._pipelineIAData.quadVB!.update(vb);
|
|
}
|
|
}
|
|
|
|
private _createLightVolumes (): void {
|
|
const device = this._context.root.device;
|
|
let totalSize = Float32Array.BYTES_PER_ELEMENT * volLightAttrCount * 4 * UBODeferredLight.LIGHTS_PER_PASS;
|
|
totalSize = Math.ceil(totalSize / device.capabilities.uboOffsetAlignment) * device.capabilities.uboOffsetAlignment;
|
|
|
|
this._lightVolumeBuffer = device.createBuffer(new BufferInfo(
|
|
BufferUsageBit.UNIFORM | BufferUsageBit.TRANSFER_DST,
|
|
MemoryUsageBit.HOST | MemoryUsageBit.DEVICE,
|
|
totalSize,
|
|
device.capabilities.uboOffsetAlignment,
|
|
));
|
|
|
|
this._deferredLitsBufView = device.createBuffer(new BufferViewInfo(this._lightVolumeBuffer, 0, totalSize));
|
|
this._lightBufferData = new Float32Array(totalSize / Float32Array.BYTES_PER_ELEMENT);
|
|
}
|
|
|
|
private _genQuadVertexData (surfaceTransform: SurfaceTransform, renderArea: Rect): Float32Array {
|
|
const minX = renderArea.x / this._context.width;
|
|
const maxX = (renderArea.x + renderArea.width) / this._context.width;
|
|
let minY = renderArea.y / this._context.height;
|
|
let maxY = (renderArea.y + renderArea.height) / this._context.height;
|
|
if (this._context.root.device.capabilities.screenSpaceSignY > 0) {
|
|
const temp = maxY;
|
|
maxY = minY;
|
|
minY = temp;
|
|
}
|
|
let n = 0;
|
|
switch (surfaceTransform) {
|
|
case (SurfaceTransform.IDENTITY):
|
|
n = 0;
|
|
vbData[n++] = -1.0; vbData[n++] = -1.0; vbData[n++] = minX; vbData[n++] = maxY;
|
|
vbData[n++] = 1.0; vbData[n++] = -1.0; vbData[n++] = maxX; vbData[n++] = maxY;
|
|
vbData[n++] = -1.0; vbData[n++] = 1.0; vbData[n++] = minX; vbData[n++] = minY;
|
|
vbData[n++] = 1.0; vbData[n++] = 1.0; vbData[n++] = maxX; vbData[n++] = minY;
|
|
break;
|
|
case (SurfaceTransform.ROTATE_90):
|
|
n = 0;
|
|
vbData[n++] = -1.0; vbData[n++] = -1.0; vbData[n++] = maxX; vbData[n++] = maxY;
|
|
vbData[n++] = 1.0; vbData[n++] = -1.0; vbData[n++] = maxX; vbData[n++] = minY;
|
|
vbData[n++] = -1.0; vbData[n++] = 1.0; vbData[n++] = minX; vbData[n++] = maxY;
|
|
vbData[n++] = 1.0; vbData[n++] = 1.0; vbData[n++] = minX; vbData[n++] = minY;
|
|
break;
|
|
case (SurfaceTransform.ROTATE_180):
|
|
n = 0;
|
|
vbData[n++] = -1.0; vbData[n++] = -1.0; vbData[n++] = minX; vbData[n++] = minY;
|
|
vbData[n++] = 1.0; vbData[n++] = -1.0; vbData[n++] = maxX; vbData[n++] = minY;
|
|
vbData[n++] = -1.0; vbData[n++] = 1.0; vbData[n++] = minX; vbData[n++] = maxY;
|
|
vbData[n++] = 1.0; vbData[n++] = 1.0; vbData[n++] = maxX; vbData[n++] = maxY;
|
|
break;
|
|
case (SurfaceTransform.ROTATE_270):
|
|
n = 0;
|
|
vbData[n++] = -1.0; vbData[n++] = -1.0; vbData[n++] = minX; vbData[n++] = minY;
|
|
vbData[n++] = 1.0; vbData[n++] = -1.0; vbData[n++] = minX; vbData[n++] = maxY;
|
|
vbData[n++] = -1.0; vbData[n++] = 1.0; vbData[n++] = maxX; vbData[n++] = minY;
|
|
vbData[n++] = 1.0; vbData[n++] = 1.0; vbData[n++] = maxX; vbData[n++] = maxY;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return vbData;
|
|
}
|
|
private _createQuadInputAssembler (): PipelineInputAssemblerData {
|
|
// create vertex buffer
|
|
const inputAssemblerData = new PipelineInputAssemblerData();
|
|
|
|
const vbStride = Float32Array.BYTES_PER_ELEMENT * 4;
|
|
const vbSize = vbStride * 4;
|
|
const device = cclegacy.director.root.device;
|
|
const quadVB: Buffer = device.createBuffer(new BufferInfo(
|
|
BufferUsageBit.VERTEX | BufferUsageBit.TRANSFER_DST,
|
|
MemoryUsageBit.DEVICE | MemoryUsageBit.HOST,
|
|
vbSize,
|
|
vbStride,
|
|
));
|
|
|
|
if (!quadVB) {
|
|
return inputAssemblerData;
|
|
}
|
|
|
|
// create index buffer
|
|
const ibStride = Uint16Array.BYTES_PER_ELEMENT;
|
|
const ibSize = ibStride * 6;
|
|
|
|
const quadIB: Buffer = device.createBuffer(new BufferInfo(
|
|
BufferUsageBit.INDEX | BufferUsageBit.TRANSFER_DST,
|
|
MemoryUsageBit.DEVICE,
|
|
ibSize,
|
|
ibStride,
|
|
));
|
|
|
|
if (!quadIB) {
|
|
return inputAssemblerData;
|
|
}
|
|
|
|
const indices = new Uint16Array(6);
|
|
indices[0] = 0; indices[1] = 1; indices[2] = 2;
|
|
indices[3] = 1; indices[4] = 3; indices[5] = 2;
|
|
|
|
quadIB.update(indices.buffer);
|
|
|
|
// create input assembler
|
|
|
|
const attributes = new Array<Attribute>(2);
|
|
attributes[0] = new Attribute('a_position', Format.RG32F);
|
|
attributes[1] = new Attribute('a_texCoord', Format.RG32F);
|
|
|
|
const quadIA = device.createInputAssembler(new InputAssemblerInfo(
|
|
attributes,
|
|
[quadVB],
|
|
quadIB,
|
|
));
|
|
|
|
inputAssemblerData.quadIB = quadIB;
|
|
inputAssemblerData.quadVB = quadVB;
|
|
inputAssemblerData.quadIA = quadIA;
|
|
return inputAssemblerData;
|
|
}
|
|
}
|
|
|
|
class ExecutorContext {
|
|
constructor (
|
|
pipeline: BasicPipeline,
|
|
ubo: PipelineUBO,
|
|
device: Device,
|
|
resourceGraph: ResourceGraph,
|
|
renderGraph: RenderGraph,
|
|
layoutGraph: LayoutGraphData,
|
|
width: number,
|
|
height: number,
|
|
descriptorSet = null,
|
|
) {
|
|
this.pipeline = pipeline;
|
|
this.device = device;
|
|
this.commandBuffer = device.commandBuffer;
|
|
this.pipelineSceneData = pipeline.pipelineSceneData;
|
|
this.resourceGraph = resourceGraph;
|
|
this.renderGraph = renderGraph;
|
|
this.root = cclegacy.director.root;
|
|
this.ubo = ubo;
|
|
this.layoutGraph = layoutGraph;
|
|
this.width = width;
|
|
this.height = height;
|
|
this.additiveLight = new RenderAdditiveLightQueue(pipeline);
|
|
this.pools = new ExecutorPools(this);
|
|
this.blit = new BlitInfo(this);
|
|
this.culling = new SceneCulling();
|
|
this.descriptorSet = descriptorSet;
|
|
}
|
|
reset (): void {
|
|
this.culling.clear();
|
|
this.pools.reset();
|
|
this.cullCamera = null;
|
|
for (const infoMap of this.submitMap) {
|
|
for (const info of infoMap[1]) info[1].reset();
|
|
}
|
|
}
|
|
resize (width: number, height: number): void {
|
|
this.width = width;
|
|
this.height = height;
|
|
this.blit.resize(width, height);
|
|
}
|
|
readonly device: Device;
|
|
readonly pipeline: BasicPipeline;
|
|
readonly commandBuffer: CommandBuffer;
|
|
readonly pipelineSceneData: PipelineSceneData;
|
|
readonly resourceGraph: ResourceGraph;
|
|
readonly devicePasses: Map<number, DeviceRenderPass> = new Map<number, DeviceRenderPass>();
|
|
readonly deviceTextures: Map<string, DeviceTexture> = new Map<string, DeviceTexture>();
|
|
readonly deviceBuffers: Map<string, DeviceBuffer> = new Map<string, DeviceBuffer>();
|
|
readonly layoutGraph: LayoutGraphData;
|
|
readonly root: Root;
|
|
readonly ubo: PipelineUBO;
|
|
readonly additiveLight: RenderAdditiveLightQueue;
|
|
readonly submitMap: Map<Camera, Map<number, SubmitInfo>> = new Map<Camera, Map<number, SubmitInfo>>();
|
|
readonly pools: ExecutorPools;
|
|
readonly blit: BlitInfo;
|
|
readonly culling: SceneCulling;
|
|
renderGraph: RenderGraph;
|
|
width: number;
|
|
height: number;
|
|
cullCamera;
|
|
descriptorSet: DescriptorSet | null;
|
|
}
|
|
|
|
export class Executor {
|
|
constructor (
|
|
pipeline: BasicPipeline,
|
|
ubo: PipelineUBO,
|
|
device: Device,
|
|
resourceGraph: ResourceGraph,
|
|
layoutGraph: LayoutGraphData,
|
|
width: number,
|
|
height: number,
|
|
) {
|
|
context = this._context = new ExecutorContext(
|
|
pipeline,
|
|
ubo,
|
|
device,
|
|
resourceGraph,
|
|
new RenderGraph(),
|
|
layoutGraph,
|
|
width,
|
|
height,
|
|
);
|
|
}
|
|
|
|
resize (width: number, height: number): void {
|
|
context.resize(width, height);
|
|
}
|
|
|
|
private _removeDeviceResource (): void {
|
|
const pipeline: any = context.pipeline;
|
|
const resourceUses = pipeline.resourceUses;
|
|
const deletes: string[] = [];
|
|
const deviceTexs = context.deviceTextures;
|
|
for (const [name, dTex] of deviceTexs) {
|
|
const resId = context.resourceGraph.vertex(name);
|
|
const trait = context.resourceGraph.getTraits(resId);
|
|
if (!resourceUses.includes(name)) {
|
|
switch (trait.residency) {
|
|
case ResourceResidency.MANAGED:
|
|
deletes.push(name);
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
for (const name of deletes) {
|
|
deviceTexs.get(name)!.release();
|
|
deviceTexs.delete(name);
|
|
}
|
|
|
|
const deletesBuff: string[] = [];
|
|
const deviceBuffs = context.deviceBuffers;
|
|
for (const [name, dBuff] of deviceBuffs) {
|
|
const resId = context.resourceGraph.vertex(name);
|
|
const trait = context.resourceGraph.getTraits(resId);
|
|
if (!resourceUses.includes(name)) {
|
|
switch (trait.residency) {
|
|
case ResourceResidency.MANAGED:
|
|
deletesBuff.push(name);
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
for (const name of deletesBuff) {
|
|
deviceBuffs.get(name)!.release();
|
|
deviceBuffs.delete(name);
|
|
}
|
|
resourceUses.length = 0;
|
|
}
|
|
|
|
execute (rg: RenderGraph): void {
|
|
context.renderGraph = rg;
|
|
context.reset();
|
|
const cmdBuff = context.commandBuffer;
|
|
context.culling.buildRenderQueues(rg, context.layoutGraph, context.pipelineSceneData);
|
|
context.culling.uploadInstancing(cmdBuff);
|
|
this._removeDeviceResource();
|
|
cmdBuff.begin();
|
|
if (!this._visitor) this._visitor = new RenderVisitor();
|
|
depthFirstSearch(this._visitor.graphView, this._visitor, this._visitor.colorMap);
|
|
cmdBuff.end();
|
|
context.device.queue.submit([cmdBuff]);
|
|
}
|
|
|
|
release (): void {
|
|
context.devicePasses.clear();
|
|
for (const [k, v] of context.deviceTextures) {
|
|
v.release();
|
|
}
|
|
context.deviceTextures.clear();
|
|
|
|
for (const [k, v] of context.deviceBuffers) {
|
|
v.release();
|
|
}
|
|
context.deviceBuffers.clear();
|
|
}
|
|
readonly _context: ExecutorContext;
|
|
private _visitor: RenderVisitor | undefined;
|
|
}
|
|
|
|
class BaseRenderVisitor {
|
|
public queueID = 0xFFFFFFFF;
|
|
public sceneID = 0xFFFFFFFF;
|
|
public passID = 0xFFFFFFFF;
|
|
public dispatchID = 0xFFFFFFFF;
|
|
public currPass: DeviceRenderPass | DeviceComputePass | undefined;
|
|
public currQueue: DeviceRenderQueue |DeviceComputeQueue | undefined;
|
|
public rg: RenderGraph;
|
|
constructor () {
|
|
this.rg = context.renderGraph;
|
|
}
|
|
protected _isRasterPass (u: number): boolean {
|
|
return !!context.renderGraph.tryGetRasterPass(u);
|
|
}
|
|
protected isComputePass (u: number): boolean {
|
|
return !!context.renderGraph.tryGetCompute(u);
|
|
}
|
|
protected isDispatch (u: number): boolean {
|
|
return !!context.renderGraph.tryGetDispatch(u);
|
|
}
|
|
protected _isQueue (u: number): boolean {
|
|
return !!context.renderGraph.tryGetQueue(u);
|
|
}
|
|
protected _isScene (u: number): boolean {
|
|
return !!context.renderGraph.tryGetScene(u);
|
|
}
|
|
protected _isBlit (u: number): boolean {
|
|
return !!context.renderGraph.tryGetBlit(u);
|
|
}
|
|
applyID (id: number): void {
|
|
if (this._isRasterPass(id)) {
|
|
this.passID = id;
|
|
} else if (this._isQueue(id)) {
|
|
this.queueID = id;
|
|
} else if (this._isScene(id) || this._isBlit(id)) {
|
|
this.sceneID = id;
|
|
} else if (this.isComputePass(id)) {
|
|
this.passID = id;
|
|
} else if (this.isDispatch(id)) {
|
|
this.dispatchID = id;
|
|
}
|
|
}
|
|
}
|
|
|
|
class PreRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor {
|
|
constructor () {
|
|
super();
|
|
}
|
|
clear (value: ClearView[]): void {
|
|
// do nothing
|
|
}
|
|
viewport (value: Viewport): void {
|
|
// do nothing
|
|
}
|
|
rasterPass (pass: RasterPass): void {
|
|
if (!this.rg.getValid(this.passID)) return;
|
|
const devicePasses = context.devicePasses;
|
|
const passHash = pass.hashValue;
|
|
this.currPass = devicePasses.get(passHash);
|
|
if (!this.currPass) {
|
|
const rasterInfo = context.pools.addRasterPassInfo();
|
|
rasterInfo.applyInfo(this.passID, pass);
|
|
this.currPass = new DeviceRenderPass(rasterInfo);
|
|
devicePasses.set(passHash, this.currPass);
|
|
} else {
|
|
this.currPass.resetResource(this.passID, pass);
|
|
}
|
|
}
|
|
rasterSubpass (value: RasterSubpass): void {
|
|
// noop
|
|
}
|
|
computeSubpass (value: ComputeSubpass): void {
|
|
// noop
|
|
}
|
|
resolve (value: ResolvePass): void {
|
|
// noop
|
|
}
|
|
move (value: MovePass): void {
|
|
// noop
|
|
}
|
|
raytrace (value: RaytracePass): void {
|
|
// noop
|
|
}
|
|
compute (pass: ComputePass): void {
|
|
if (!this.rg.getValid(this.passID)) return;
|
|
const devicePasses = context.devicePasses;
|
|
const computeInfo = new ComputePassInfo();
|
|
computeInfo.applyInfo(this.passID, pass);
|
|
this.currPass = new DeviceComputePass(computeInfo);
|
|
|
|
this.currPass.prePass();
|
|
this.currPass.record();
|
|
this.currPass.postPass();
|
|
}
|
|
copy (value: CopyPass): void {
|
|
if (value.uploadPairs.length) {
|
|
for (const upload of value.uploadPairs) {
|
|
const resBuffers = context.deviceBuffers;
|
|
const resourceGraph = context.resourceGraph;
|
|
const vertId = resourceGraph.vertex(upload.target);
|
|
resourceVisitor.resName = upload.target;
|
|
resourceGraph.visitVertex(resourceVisitor, vertId);
|
|
|
|
const gfxBuffer = resBuffers.get(upload.target);
|
|
context.device.commandBuffer.updateBuffer(gfxBuffer!.buffer!, upload.source, upload.source.byteLength);
|
|
}
|
|
}
|
|
}
|
|
queue (value: RenderQueue): void {
|
|
if (!this.rg.getValid(this.queueID)) return;
|
|
let deviceQueue: DeviceComputeQueue | DeviceRenderQueue;
|
|
if ('rasterPassInfo' in this.currPass!) {
|
|
deviceQueue = context.pools.addDeviceQueue();
|
|
deviceQueue.init(this.currPass, value, this.queueID);
|
|
this.currQueue = deviceQueue;
|
|
this.currPass.addQueue(deviceQueue);
|
|
} else {
|
|
deviceQueue = context.pools.addComputeQueue();
|
|
deviceQueue.init(this.currPass!, value, this.queueID);
|
|
this.currQueue = deviceQueue;
|
|
this.currPass!.addQueue(deviceQueue);
|
|
}
|
|
const layoutName = this.rg.getLayout(this.queueID);
|
|
if (layoutName) {
|
|
const layoutGraph = context.layoutGraph;
|
|
if (this.currPass!.renderLayout) {
|
|
const layoutId = layoutGraph.locateChild(this.currPass!.renderLayout.layoutID, layoutName);
|
|
this.currQueue.layoutID = layoutId;
|
|
}
|
|
}
|
|
}
|
|
scene (value: SceneData): void {
|
|
if (!this.rg.getValid(this.sceneID)) return;
|
|
const renderQueue = this.currQueue as DeviceRenderQueue;
|
|
const graphScene = context.pools.addGraphScene();
|
|
graphScene.init(value, null, this.sceneID);
|
|
renderQueue.addSceneTask(graphScene);
|
|
}
|
|
blit (value: Blit): void {
|
|
if (!this.rg.getValid(this.sceneID)) return;
|
|
const renderQueue = this.currQueue as DeviceRenderQueue;
|
|
const graphScene = context.pools.addGraphScene();
|
|
graphScene.init(null, value, -1);
|
|
renderQueue.addSceneTask(graphScene);
|
|
}
|
|
dispatch (value: Dispatch): void {
|
|
let pso: PipelineState | null = null;
|
|
const devicePass = this.currPass as DeviceComputePass;
|
|
const pass = value.material?.passes[value.passID];
|
|
pass?.update();
|
|
const shader = pass?.getShaderVariant();
|
|
|
|
if (pass !== null && shader !== null) {
|
|
const psoInfo = new PipelineStateInfo(
|
|
shader,
|
|
pass?.pipelineLayout,
|
|
);
|
|
psoInfo.bindPoint = PipelineBindPoint.COMPUTE;
|
|
pso = deviceManager.gfxDevice.createPipelineState(psoInfo);
|
|
}
|
|
const cmdBuff = context.commandBuffer;
|
|
if (pso) {
|
|
cmdBuff.bindPipelineState(pso);
|
|
const layoutStage = devicePass.renderLayout;
|
|
const layoutDesc = layoutStage!.descriptorSet!;
|
|
const extResId: number[] = [];
|
|
cmdBuff.bindDescriptorSet(SetIndex.GLOBAL, layoutDesc);
|
|
}
|
|
|
|
const gx = value.threadGroupCountX;
|
|
const gy = value.threadGroupCountY;
|
|
const gz = value.threadGroupCountZ;
|
|
(cmdBuff as any).dispatch(new DispatchInfo(gx, gy, gz));
|
|
}
|
|
}
|
|
|
|
class PostRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor {
|
|
constructor () {
|
|
super();
|
|
}
|
|
clear (value: ClearView[]): void {
|
|
// do nothing
|
|
}
|
|
viewport (value: Viewport): void {
|
|
// do nothing
|
|
}
|
|
rasterPass (pass: RasterPass): void {
|
|
const devicePasses = context.devicePasses;
|
|
const passHash = pass.hashValue;
|
|
const currPass = devicePasses.get(passHash);
|
|
if (!currPass) return;
|
|
this.currPass = currPass;
|
|
this.currPass.prePass();
|
|
this.currPass.record();
|
|
this.currPass.postPass();
|
|
}
|
|
rasterSubpass (value: RasterSubpass): void {
|
|
// noop
|
|
}
|
|
computeSubpass (value: ComputeSubpass): void {
|
|
// noop
|
|
}
|
|
resolve (value: ResolvePass): void {
|
|
// noop
|
|
}
|
|
compute (value: ComputePass): void {
|
|
// noop
|
|
}
|
|
copy (value: CopyPass): void {
|
|
// noop
|
|
}
|
|
move (value: MovePass): void {
|
|
// noop
|
|
}
|
|
raytrace (value: RaytracePass): void {
|
|
// noop
|
|
}
|
|
queue (value: RenderQueue): void {
|
|
// collect scene results
|
|
}
|
|
scene (value: SceneData): void {
|
|
// scene command list finished
|
|
}
|
|
blit (value: Blit): void {
|
|
// noop
|
|
}
|
|
dispatch (value: Dispatch): void {
|
|
// noop
|
|
}
|
|
}
|
|
|
|
export class RenderVisitor extends DefaultVisitor {
|
|
private _preVisitor: PreRenderVisitor;
|
|
private _postVisitor: PostRenderVisitor;
|
|
private _graphView: ReferenceGraphView<RenderGraph>;
|
|
private _colorMap: VectorGraphColorMap;
|
|
constructor () {
|
|
super();
|
|
this._preVisitor = new PreRenderVisitor();
|
|
this._postVisitor = new PostRenderVisitor();
|
|
this._graphView = new ReferenceGraphView<RenderGraph>(context.renderGraph);
|
|
this._colorMap = new VectorGraphColorMap(context.renderGraph.numVertices());
|
|
}
|
|
|
|
get graphView (): ReferenceGraphView<RenderGraph> { return this._graphView; }
|
|
get colorMap (): VectorGraphColorMap { return this._colorMap; }
|
|
discoverVertex (u: number, gv: ReferenceGraphView<RenderGraph>): void {
|
|
const g = gv.g;
|
|
this._preVisitor.applyID(u);
|
|
g.visitVertex(this._preVisitor, u);
|
|
}
|
|
finishVertex (v: number, gv: ReferenceGraphView<RenderGraph>): void {
|
|
const g = gv.g;
|
|
g.visitVertex(this._postVisitor, v);
|
|
}
|
|
}
|