diff --git a/cocos/rendering/custom/define.ts b/cocos/rendering/custom/define.ts index 02a96b69176..3d8d81a43b7 100644 --- a/cocos/rendering/custom/define.ts +++ b/cocos/rendering/custom/define.ts @@ -27,6 +27,7 @@ import { BufferInfo, Buffer, BufferUsageBit, ClearFlagBit, Color, DescriptorSet, Format, Rect, Sampler, StoreOp, Texture, Viewport, MemoryUsageBit, UniformBlock, Device, + API, } from '../../gfx'; import { ReflectionProbe } from '../../render-scene/scene/reflection-probe'; import { Camera, SkyBoxFlagValue } from '../../render-scene/scene/camera'; @@ -422,13 +423,15 @@ export function getDescBinding (descId, descData: DescriptorSetData): number { export function getDescBindingFromName (bindingName: string): number { const pipeline = cclegacy.director.root.pipeline as WebPipeline; + const isWebGPU = pipeline.device.gfxAPI === API.WEBGPU; const layoutGraph = pipeline.layoutGraph; const vertIds = layoutGraph.v(); const descId = layoutGraph.attributeIndex.get(bindingName); let currDesData: DescriptorSetData; for (const i of vertIds) { const layout = layoutGraph.getLayout(i); - for (const [k, descData] of layout.descriptorSets) { + const sets = layout.getSets(isWebGPU); + for (const [k, descData] of sets) { const layoutData = descData.descriptorSetLayoutData; const blocks = layoutData.descriptorBlocks; for (const b of blocks) { @@ -491,17 +494,19 @@ export function getDescriptorSetDataFromLayout (layoutName: string): DescriptorS return descLayout; } const webPip = cclegacy.director.root.pipeline as WebPipeline; + const isWebGPU = webPip.device.gfxAPI === API.WEBGPU; const stageId = webPip.layoutGraph.locateChild(webPip.layoutGraph.N, layoutName); const layout = webPip.layoutGraph.getLayout(stageId); - const layoutData = layout.descriptorSets.get(UpdateFrequency.PER_PASS); + const layoutData = layout.getSet(UpdateFrequency.PER_PASS, isWebGPU); layouts.set(layoutName, layoutData!); return layoutData; } export function getDescriptorSetDataFromLayoutId (id: number): DescriptorSetData | undefined { const webPip = cclegacy.director.root.pipeline as WebPipeline; + const isWebGPU = webPip.device.gfxAPI === API.WEBGPU; const layout = webPip.layoutGraph.getLayout(id); - const layoutData = layout.descriptorSets.get(UpdateFrequency.PER_PASS); + const layoutData = layout.getSet(UpdateFrequency.PER_PASS, isWebGPU); return layoutData; } @@ -514,7 +519,8 @@ function getUniformBlock (block: string, layoutName: string): UniformBlock | und const lg = webPip.layoutGraph; const nodeId = lg.locateChild(0xFFFFFFFF, layoutName); const ppl = lg.getLayout(nodeId); - const layout = ppl.descriptorSets.get(UpdateFrequency.PER_PASS)!.descriptorSetLayoutData; + const isWebGPU = webPip.device.gfxAPI === API.WEBGPU; + const layout = ppl.getSet(UpdateFrequency.PER_PASS, isWebGPU)!.descriptorSetLayoutData; const nameID: number = lg.attributeIndex.get(block)!; return layout.uniformBlocks.get(nameID); } diff --git a/cocos/rendering/custom/index.ts b/cocos/rendering/custom/index.ts index 8fdecb0abaf..988ebf29f0d 100644 --- a/cocos/rendering/custom/index.ts +++ b/cocos/rendering/custom/index.ts @@ -29,7 +29,7 @@ import { macro } from '../../core/platform/macro'; import { LayoutGraphData, loadLayoutGraphData } from './layout-graph'; import { BinaryInputArchive } from './binary-archive'; import { WebProgramLibrary } from './web-program-library'; -import { Device } from '../../gfx'; +import { API, Device } from '../../gfx'; import { initializeLayoutGraphData, terminateLayoutGraphData, getCustomPassID, getCustomPhaseID, getCustomSubpassID } from './layout-graph-utils'; import { ProgramLibrary } from './private'; import { forceResizeAllWindows } from './framework'; @@ -97,7 +97,11 @@ export function init (device: Device, arrayBuffer: ArrayBuffer | null): void { } export function destroy (): void { - terminateLayoutGraphData(defaultLayoutGraph); + if (_pipeline) { + terminateLayoutGraphData(defaultLayoutGraph, _pipeline.device.gfxAPI === API.WEBGPU); + } else { + terminateLayoutGraphData(defaultLayoutGraph, false); + } } export function getPassID (name: string | undefined): number { diff --git a/cocos/rendering/custom/layout-graph-utils.ts b/cocos/rendering/custom/layout-graph-utils.ts index 26ecb3411dd..35fc0bc4715 100644 --- a/cocos/rendering/custom/layout-graph-utils.ts +++ b/cocos/rendering/custom/layout-graph-utils.ts @@ -25,7 +25,7 @@ /* eslint-disable max-len */ import { EffectAsset } from '../../asset/assets'; import { assert, error, warn } from '../../core'; -import { DescriptorSetInfo, DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutInfo, DescriptorType, Device, Feature, Format, FormatFeatureBit, GetTypeSize, PipelineLayout, PipelineLayoutInfo, ShaderStageFlagBit, Type, Uniform, UniformBlock } from '../../gfx'; +import { API, DescriptorSetInfo, DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutInfo, DescriptorType, Device, Feature, Format, FormatFeatureBit, GetTypeSize, MemoryAccessBit, PipelineLayout, PipelineLayoutInfo, SampleType, ShaderStageFlagBit, Type, Uniform, UniformBlock, ViewDimension } from '../../gfx'; import { UBOForwardLightEnum, UBOSkinning } from '../define'; import type { DescriptorGroupBlockIndex, @@ -247,9 +247,11 @@ function createDescriptorSetLayout (device: Device | null, layoutData: Descripto } export function createGfxDescriptorSetsAndPipelines (device: Device | null, g: LayoutGraphData): void { + const isWebGPU = device ? device.gfxAPI === API.WEBGPU : false; for (let i = 0; i < g._layouts.length; ++i) { const ppl: PipelineLayoutData = g.getLayout(i); - ppl.descriptorSets.forEach((value, key): void => { + const sets = ppl.getSets(isWebGPU); + sets.forEach((value, key): void => { const level = value; const layoutData = level.descriptorSetLayoutData; if (device) { @@ -277,23 +279,82 @@ function getDescriptorBlockData (map: Map, index: D return newBlock; } +function getDescriptorGroupBlockData (map: Map, index: DescriptorGroupBlockIndex): DescriptorBlockData { + const key = JSON.stringify(index); + const block = map.get(key); + if (block) { + return block; + } + const newBlock = new DescriptorBlockData( + index.descriptorType, + index.visibility, + 0, + index.accessType, + index.viewDimension, + index.sampleType, + index.format, + ); + map.set(key, newBlock); + return newBlock; +} + +function getViewDimension (type: Type): ViewDimension { + switch (type) { + case Type.SAMPLER1D: + case Type.TEXTURE1D: + case Type.IMAGE1D: + return ViewDimension.TEX1D; + case Type.SAMPLER2D: + case Type.TEXTURE2D: + case Type.IMAGE2D: + return ViewDimension.TEX2D; + case Type.SAMPLER2D_ARRAY: + case Type.TEXTURE2D_ARRAY: + case Type.IMAGE2D_ARRAY: + return ViewDimension.TEX2D_ARRAY; + case Type.SAMPLER_CUBE: + case Type.TEXTURE_CUBE: + case Type.IMAGE_CUBE: + return ViewDimension.TEXCUBE; + case Type.SAMPLER3D: + case Type.TEXTURE3D: + case Type.IMAGE3D: + return ViewDimension.TEX3D; + default: + return ViewDimension.UNKNOWN; + } +} + // make DescriptorSetLayoutData from effect directly export function makeDescriptorSetLayoutData ( lg: LayoutGraphData, rate: UpdateFrequency, set: number, descriptors: EffectAsset.IDescriptorInfo, + isWebGPU: boolean, ): DescriptorSetLayoutData { const map = new Map(); const uniformBlocks: Map = new Map(); + for (let i = 0; i < descriptors.blocks.length; i++) { const cb = descriptors.blocks[i]; - const block = getDescriptorBlockData(map, { - updateFrequency: rate, - parameterType: ParameterType.TABLE, - descriptorType: DescriptorTypeOrder.UNIFORM_BUFFER, - visibility: cb.stageFlags, - }); + const block = isWebGPU + ? getDescriptorGroupBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.UNIFORM_BUFFER, + visibility: cb.stageFlags, + accessType: MemoryAccessBit.READ_ONLY, + viewDimension: ViewDimension.BUFFER, + sampleType: SampleType.FLOAT, + format: Format.UNKNOWN, + }) + : getDescriptorBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.UNIFORM_BUFFER, + visibility: cb.stageFlags, + }); const nameID = getOrCreateDescriptorID(lg, cb.name); block.descriptors.push(new DescriptorData(nameID, Type.UNKNOWN, 1)); // add uniform buffer @@ -301,78 +362,147 @@ export function makeDescriptorSetLayoutData ( } for (let i = 0; i < descriptors.samplerTextures.length; i++) { const samplerTexture = descriptors.samplerTextures[i]; - const block = getDescriptorBlockData(map, { - updateFrequency: rate, - parameterType: ParameterType.TABLE, - descriptorType: DescriptorTypeOrder.SAMPLER_TEXTURE, - visibility: samplerTexture.stageFlags, - }); + const block = isWebGPU + ? getDescriptorGroupBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.SAMPLER_TEXTURE, + visibility: samplerTexture.stageFlags, + accessType: MemoryAccessBit.READ_ONLY, + viewDimension: getViewDimension(samplerTexture.type), + sampleType: samplerTexture.sampleType, + format: Format.UNKNOWN, + }) + : getDescriptorBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.SAMPLER_TEXTURE, + visibility: samplerTexture.stageFlags, + }); const nameID = getOrCreateDescriptorID(lg, samplerTexture.name); block.descriptors.push(new DescriptorData(nameID, samplerTexture.type, samplerTexture.count)); } for (let i = 0; i < descriptors.samplers.length; i++) { const sampler = descriptors.samplers[i]; - const block = getDescriptorBlockData(map, { - updateFrequency: rate, - parameterType: ParameterType.TABLE, - descriptorType: DescriptorTypeOrder.SAMPLER, - visibility: sampler.stageFlags, - }); + const block = isWebGPU + ? getDescriptorGroupBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.SAMPLER, + visibility: sampler.stageFlags, + accessType: MemoryAccessBit.READ_ONLY, + viewDimension: ViewDimension.UNKNOWN, + sampleType: SampleType.FLOAT, + format: Format.UNKNOWN, + }) + : getDescriptorBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.SAMPLER, + visibility: sampler.stageFlags, + }); const nameID = getOrCreateDescriptorID(lg, sampler.name); block.descriptors.push(new DescriptorData(nameID, Type.SAMPLER, sampler.count)); } for (let i = 0; i < descriptors.textures.length; i++) { const texture = descriptors.textures[i]; - const block = getDescriptorBlockData(map, { - updateFrequency: rate, - parameterType: ParameterType.TABLE, - descriptorType: DescriptorTypeOrder.TEXTURE, - visibility: texture.stageFlags, - }); + const block = isWebGPU + ? getDescriptorGroupBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.TEXTURE, + visibility: texture.stageFlags, + accessType: MemoryAccessBit.READ_ONLY, + viewDimension: getViewDimension(texture.type), + sampleType: texture.sampleType, + format: Format.UNKNOWN, + }) + : getDescriptorBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.TEXTURE, + visibility: texture.stageFlags, + }); const nameID = getOrCreateDescriptorID(lg, texture.name); block.descriptors.push(new DescriptorData(nameID, texture.type, texture.count)); } for (let i = 0; i < descriptors.buffers.length; i++) { const buffer = descriptors.buffers[i]; - const block = getDescriptorBlockData(map, { - updateFrequency: rate, - parameterType: ParameterType.TABLE, - descriptorType: DescriptorTypeOrder.STORAGE_BUFFER, - visibility: buffer.stageFlags, - }); + const block = isWebGPU + ? getDescriptorGroupBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.STORAGE_BUFFER, + visibility: buffer.stageFlags, + accessType: MemoryAccessBit.READ_ONLY, + viewDimension: ViewDimension.BUFFER, + sampleType: SampleType.FLOAT, + format: Format.UNKNOWN, + }) + : getDescriptorBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.STORAGE_BUFFER, + visibility: buffer.stageFlags, + }); const nameID = getOrCreateDescriptorID(lg, buffer.name); block.descriptors.push(new DescriptorData(nameID, Type.UNKNOWN, 1)); } for (let i = 0; i < descriptors.images.length; i++) { const image = descriptors.images[i]; - const block = getDescriptorBlockData(map, { - updateFrequency: rate, - parameterType: ParameterType.TABLE, - descriptorType: DescriptorTypeOrder.STORAGE_IMAGE, - visibility: image.stageFlags, - }); + const block = isWebGPU + ? getDescriptorGroupBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.STORAGE_IMAGE, + visibility: image.stageFlags, + accessType: MemoryAccessBit.READ_ONLY, + viewDimension: getViewDimension(image.type), + sampleType: SampleType.FLOAT, + format: Format.UNKNOWN, // TODO(zhouzhenglong): Add storage image format + }) + : getDescriptorBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.STORAGE_IMAGE, + visibility: image.stageFlags, + }); const nameID = getOrCreateDescriptorID(lg, image.name); block.descriptors.push(new DescriptorData(nameID, image.type, image.count)); } for (let i = 0; i < descriptors.subpassInputs.length; i++) { const subpassInput = descriptors.subpassInputs[i]; - const block = getDescriptorBlockData(map, { - updateFrequency: rate, - parameterType: ParameterType.TABLE, - descriptorType: DescriptorTypeOrder.INPUT_ATTACHMENT, - visibility: subpassInput.stageFlags, - }); + const block = isWebGPU + ? getDescriptorGroupBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.INPUT_ATTACHMENT, + visibility: subpassInput.stageFlags, + accessType: MemoryAccessBit.READ_ONLY, + viewDimension: ViewDimension.TEX2D, + sampleType: SampleType.FLOAT, + format: Format.UNKNOWN, + }) + : getDescriptorBlockData(map, { + updateFrequency: rate, + parameterType: ParameterType.TABLE, + descriptorType: DescriptorTypeOrder.INPUT_ATTACHMENT, + visibility: subpassInput.stageFlags, + }); const nameID = getOrCreateDescriptorID(lg, subpassInput.name); block.descriptors.push(new DescriptorData(nameID, Type.UNKNOWN, subpassInput.count)); } // sort blocks - const flattenedBlocks = Array.from(map).sort(sortDescriptorBlocks); + const flattenedBlocks = isWebGPU + ? Array.from(map).sort(sortDescriptorGroupBlocks) + : Array.from(map).sort(sortDescriptorBlocks); + const data = new DescriptorSetLayoutData(set, 0); // calculate bindings let capacity = 0; for (const [key, block] of flattenedBlocks) { - const index = JSON.parse(key) as DescriptorBlockIndex; + const index = JSON.parse(key) as DescriptorBlockIndex | DescriptorGroupBlockIndex; block.offset = capacity; for (const d of block.descriptors) { if (index.descriptorType === DescriptorTypeOrder.UNIFORM_BUFFER) { @@ -438,8 +568,9 @@ export function populatePipelineLayoutInfo ( layout: PipelineLayoutData, rate: UpdateFrequency, info: PipelineLayoutInfo, + isWebGPU: boolean, ): void { - const set = layout.descriptorSets.get(rate); + const set = layout.getSet(rate, isWebGPU); if (set && set.descriptorSetLayout) { info.setLayouts.push(set.descriptorSetLayout); } else { @@ -462,12 +593,14 @@ export function generateConstantMacros (device: Device, constantMacros: string): // initialize layout graph module export function initializeLayoutGraphData (device: Device, lg: LayoutGraphData): void { + const isWebGPU = device.gfxAPI === API.WEBGPU; // create descriptor sets _emptyDescriptorSetLayout = device.createDescriptorSetLayout(new DescriptorSetLayoutInfo()); _emptyPipelineLayout = device.createPipelineLayout(new PipelineLayoutInfo()); for (const v of lg.v()) { const layoutData = lg.getLayout(v); - for (const [_, set] of layoutData.descriptorSets) { + const sets = layoutData.getSets(isWebGPU); + for (const [_, set] of sets) { if (set.descriptorSetLayout !== null) { warn('descriptor set layout already initialized. It will be overwritten'); } @@ -488,20 +621,21 @@ export function initializeLayoutGraphData (device: Device, lg: LayoutGraphData): 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); + populatePipelineLayoutInfo(passLayout, UpdateFrequency.PER_PASS, info, isWebGPU); + populatePipelineLayoutInfo(phaseLayout, UpdateFrequency.PER_PHASE, info, isWebGPU); + populatePipelineLayoutInfo(phaseLayout, UpdateFrequency.PER_BATCH, info, isWebGPU); + populatePipelineLayoutInfo(phaseLayout, UpdateFrequency.PER_INSTANCE, info, isWebGPU); const phase = lg.j(phaseID); phase.pipelineLayout = device.createPipelineLayout(info); } } // terminate layout graph module -export function terminateLayoutGraphData (lg: LayoutGraphData): void { +export function terminateLayoutGraphData (lg: LayoutGraphData, isWebGPU: boolean): void { for (const v of lg.v()) { const layoutData = lg.getLayout(v); - for (const [_, set] of layoutData.descriptorSets) { + const sets = layoutData.getSets(isWebGPU); + for (const [_, set] of sets) { if (set.descriptorSetLayout !== null) { set.descriptorSetLayout.destroy(); } @@ -527,10 +661,11 @@ export function getOrCreateDescriptorSetLayout ( subpassOrPassID: number, phaseID: number, rate: UpdateFrequency, + isWebGPU: boolean, ): DescriptorSetLayout { if (rate < UpdateFrequency.PER_PASS) { const phaseData = lg.getLayout(phaseID); - const data = phaseData.descriptorSets.get(rate); + const data = phaseData.getSet(rate, isWebGPU); if (data) { if (!data.descriptorSetLayout) { error('descriptor set layout not initialized'); @@ -545,7 +680,7 @@ export function getOrCreateDescriptorSetLayout ( assert(subpassOrPassID === lg.getParent(phaseID)); const passData = lg.getLayout(subpassOrPassID); - const data = passData.descriptorSets.get(rate); + const data = passData.getSet(rate, isWebGPU); if (data) { if (!data.descriptorSetLayout) { error('descriptor set layout not initialized'); @@ -562,10 +697,11 @@ export function getDescriptorSetLayout ( subpassOrPassID: number, phaseID: number, rate: UpdateFrequency, + isWebGPU: boolean, ): DescriptorSetLayout | null { if (rate < UpdateFrequency.PER_PASS) { const phaseData = lg.getLayout(phaseID); - const data = phaseData.descriptorSets.get(rate); + const data = phaseData.getSet(rate, isWebGPU); if (data) { if (!data.descriptorSetLayout) { error('descriptor set layout not initialized'); @@ -580,7 +716,7 @@ export function getDescriptorSetLayout ( assert(subpassOrPassID === lg.getParent(phaseID)); const passData = lg.getLayout(subpassOrPassID); - const data = passData.descriptorSets.get(rate); + const data = passData.getSet(rate, isWebGPU); if (data) { if (!data.descriptorSetLayout) { error('descriptor set layout not initialized'); @@ -636,10 +772,11 @@ export function getDescriptorName (lg: LayoutGraphData, nameID: number): string export function getPerPassDescriptorSetLayoutData ( lg: LayoutGraphData, subpassOrPassID: number, + isWebGPU: boolean, ): DescriptorSetLayoutData | null { assert(subpassOrPassID !== lg.N); const node = lg.getLayout(subpassOrPassID); - const set = node.descriptorSets.get(UpdateFrequency.PER_PASS); + const set = node.getSet(UpdateFrequency.PER_PASS, isWebGPU); if (set === undefined) { return null; } @@ -649,10 +786,11 @@ export function getPerPassDescriptorSetLayoutData ( export function getPerPhaseDescriptorSetLayoutData ( lg: LayoutGraphData, phaseID: number, + isWebGPU: boolean, ): DescriptorSetLayoutData | null { assert(phaseID !== lg.N); const node = lg.getLayout(phaseID); - const set = node.descriptorSets.get(UpdateFrequency.PER_PHASE); + const set = node.getSet(UpdateFrequency.PER_PHASE, isWebGPU); if (set === undefined) { return null; } @@ -662,13 +800,14 @@ export function getPerPhaseDescriptorSetLayoutData ( export function getPerBatchDescriptorSetLayoutData ( lg: LayoutGraphData, phaseID: number, - programID, + programID: number, + isWebGPU: boolean, ): DescriptorSetLayoutData | null { assert(phaseID !== lg.N); const phase = lg.j(phaseID); assert(programID < phase.shaderPrograms.length); const program = phase.shaderPrograms[programID]; - const set = program.layout.descriptorSets.get(UpdateFrequency.PER_BATCH); + const set = program.layout.getSet(UpdateFrequency.PER_BATCH, isWebGPU); if (set === undefined) { return null; } @@ -678,13 +817,14 @@ export function getPerBatchDescriptorSetLayoutData ( export function getPerInstanceDescriptorSetLayoutData ( lg: LayoutGraphData, phaseID: number, - programID, + programID: number, + isWebGPU: boolean, ): DescriptorSetLayoutData | null { assert(phaseID !== lg.N); const phase = lg.j(phaseID); assert(programID < phase.shaderPrograms.length); const program = phase.shaderPrograms[programID]; - const set = program.layout.descriptorSets.get(UpdateFrequency.PER_INSTANCE); + const set = program.layout.getSet(UpdateFrequency.PER_INSTANCE, isWebGPU); if (set === undefined) { return null; } diff --git a/cocos/rendering/custom/layout-graph.ts b/cocos/rendering/custom/layout-graph.ts index cb68776f143..729f66af7d8 100644 --- a/cocos/rendering/custom/layout-graph.ts +++ b/cocos/rendering/custom/layout-graph.ts @@ -603,6 +603,12 @@ export class PipelineLayoutData { this.descriptorSets.clear(); this.descriptorGroups.clear(); } + getSets (isWebGPU: boolean): Map { + return isWebGPU ? this.descriptorGroups : this.descriptorSets; + } + getSet (frequency: UpdateFrequency, isWebGPU: boolean): DescriptorSetData | undefined { + return isWebGPU ? this.descriptorGroups.get(frequency) : this.descriptorSets.get(frequency); + } readonly descriptorSets: Map = new Map(); readonly descriptorGroups: Map = new Map(); } diff --git a/cocos/rendering/custom/web-program-library.ts b/cocos/rendering/custom/web-program-library.ts index 15dfd29be08..b4f650760b0 100644 --- a/cocos/rendering/custom/web-program-library.ts +++ b/cocos/rendering/custom/web-program-library.ts @@ -26,7 +26,7 @@ /* eslint-disable max-len */ import { EffectAsset } from '../../asset/assets'; import { assert, error, errorID } from '../../core/platform/debug'; -import { Attribute, DESCRIPTOR_BUFFER_TYPE, DESCRIPTOR_SAMPLER_TYPE, DescriptorSetInfo, DescriptorSetLayout, DescriptorSetLayoutInfo, DescriptorType, Device, deviceManager, MemoryAccessBit, PipelineLayout, PipelineLayoutInfo, PipelineState, Shader, ShaderInfo, ShaderStage, ShaderStageFlagBit, Type, Uniform, UniformBlock, UniformInputAttachment, UniformSampler, UniformSamplerTexture, UniformStorageBuffer, UniformStorageImage, UniformTexture } from '../../gfx'; +import { API, Attribute, DESCRIPTOR_BUFFER_TYPE, DESCRIPTOR_SAMPLER_TYPE, DescriptorSetInfo, DescriptorSetLayout, DescriptorSetLayoutInfo, DescriptorType, Device, deviceManager, MemoryAccessBit, PipelineLayout, PipelineLayoutInfo, PipelineState, Shader, ShaderInfo, ShaderStage, ShaderStageFlagBit, Type, Uniform, UniformBlock, UniformInputAttachment, UniformSampler, UniformSamplerTexture, UniformStorageBuffer, UniformStorageImage, UniformTexture } from '../../gfx'; import { getDeviceShaderVersion, MacroRecord } from '../../render-scene'; import { IProgramInfo } from '../../render-scene/core/program-lib'; import { genHandles, getActiveAttributes, getCombinationDefines, getShaderInstanceName, getSize, getVariantKey, populateMacros, prepareDefines } from '../../render-scene/core/program-utils'; @@ -553,13 +553,14 @@ export function makeShaderInfo ( srcShaderInfo: EffectAsset.IShaderInfo, programData: ShaderProgramData | null, fixedLocal: boolean, + isWebGPU: boolean, ): [ShaderInfo, Array] { const descriptorSets: Array = [null, null, null, null]; let fixedInstanceDescriptorSetLayout: IDescriptorSetLayoutInfo | null = null; const shaderInfo = new ShaderInfo(); const blockSizes: number[] = []; { // pass - const passLayout = passLayouts.descriptorSets.get(UpdateFrequency.PER_PASS); + const passLayout = passLayouts.getSet(UpdateFrequency.PER_PASS, isWebGPU); if (passLayout) { descriptorSets[UpdateFrequency.PER_PASS] = passLayout.descriptorSetLayoutData; populateMergedShaderInfo( @@ -572,7 +573,7 @@ export function makeShaderInfo ( } } { // phase - const phaseLayout = phaseLayouts.descriptorSets.get(UpdateFrequency.PER_PHASE); + const phaseLayout = phaseLayouts.getSet(UpdateFrequency.PER_PHASE, isWebGPU); if (phaseLayout) { descriptorSets[UpdateFrequency.PER_PHASE] = phaseLayout.descriptorSetLayoutData; populateMergedShaderInfo( @@ -587,7 +588,7 @@ export function makeShaderInfo ( { // batch const batchInfo = srcShaderInfo.descriptors[UpdateFrequency.PER_BATCH]; if (programData) { - const perBatch = programData.layout.descriptorSets.get(UpdateFrequency.PER_BATCH); + const perBatch = programData.layout.getSet(UpdateFrequency.PER_BATCH, isWebGPU); if (perBatch) { descriptorSets[UpdateFrequency.PER_BATCH] = perBatch.descriptorSetLayoutData; populateMergedShaderInfo( @@ -599,7 +600,7 @@ export function makeShaderInfo ( ); } } else { - const batchLayout = phaseLayouts.descriptorSets.get(UpdateFrequency.PER_BATCH); + const batchLayout = phaseLayouts.getSet(UpdateFrequency.PER_BATCH, isWebGPU); if (batchLayout) { descriptorSets[UpdateFrequency.PER_BATCH] = batchLayout.descriptorSetLayoutData; populateGroupedShaderInfo( @@ -621,7 +622,7 @@ export function makeShaderInfo ( fixedInstanceDescriptorSetLayout = localDescriptorSetLayout; populateLocalShaderInfo(instanceInfo, localDescriptorSetLayout, shaderInfo, blockSizes); } else { - const perInstance = programData.layout.descriptorSets.get(UpdateFrequency.PER_INSTANCE); + const perInstance = programData.layout.getSet(UpdateFrequency.PER_INSTANCE, isWebGPU); if (perInstance) { descriptorSets[UpdateFrequency.PER_INSTANCE] = perInstance.descriptorSetLayoutData; populateMergedShaderInfo( @@ -634,7 +635,7 @@ export function makeShaderInfo ( } } } else { - const instanceLayout = phaseLayouts.descriptorSets.get(UpdateFrequency.PER_INSTANCE); + const instanceLayout = phaseLayouts.getSet(UpdateFrequency.PER_INSTANCE, isWebGPU); if (instanceLayout) { descriptorSets[UpdateFrequency.PER_INSTANCE] = instanceLayout.descriptorSetLayoutData; populateGroupedShaderInfo( @@ -728,20 +729,23 @@ export function buildProgramData ( phase: RenderPhaseData, programData: ShaderProgramData, fixedLocal: boolean, + isWebGPU: boolean, ): void { + const programSets = programData.layout.getSets(isWebGPU); { const perBatch = makeDescriptorSetLayoutData( lg, UpdateFrequency.PER_BATCH, _setIndex[UpdateFrequency.PER_BATCH], srcShaderInfo.descriptors[UpdateFrequency.PER_BATCH], + isWebGPU, ); const setData = new DescriptorSetData(perBatch); initializeDescriptorSetLayoutInfo( setData.descriptorSetLayoutData, setData.descriptorSetLayoutInfo, ); - programData.layout.descriptorSets.set(UpdateFrequency.PER_BATCH, setData); + programSets.set(UpdateFrequency.PER_BATCH, setData); } if (fixedLocal) { const perInstance = makeLocalDescriptorSetLayoutData(lg, localDescriptorSetLayout); @@ -764,20 +768,21 @@ export function buildProgramData ( } } } - programData.layout.descriptorSets.set(UpdateFrequency.PER_INSTANCE, setData); + programSets.set(UpdateFrequency.PER_INSTANCE, setData); } else { const perInstance = makeDescriptorSetLayoutData( lg, UpdateFrequency.PER_INSTANCE, _setIndex[UpdateFrequency.PER_INSTANCE], srcShaderInfo.descriptors[UpdateFrequency.PER_INSTANCE], + isWebGPU, ); const setData = new DescriptorSetData(perInstance); initializeDescriptorSetLayoutInfo( setData.descriptorSetLayoutData, setData.descriptorSetLayoutInfo, ); - programData.layout.descriptorSets.set(UpdateFrequency.PER_INSTANCE, setData); + programSets.set(UpdateFrequency.PER_INSTANCE, setData); } const shaderID = phase.shaderPrograms.length; phase.shaderIndex.set(programName, shaderID); @@ -792,6 +797,7 @@ export function getOrCreateProgramDescriptorSetLayout ( programName: string, rate: UpdateFrequency, ): DescriptorSetLayout { + const isWebGPU = device.gfxAPI === API.WEBGPU; assert(rate < UpdateFrequency.PER_PHASE); const phase = lg.j(phaseID); const programID = phase.shaderIndex.get(programName); @@ -799,7 +805,7 @@ export function getOrCreateProgramDescriptorSetLayout ( return getEmptyDescriptorSetLayout(); } const programData = phase.shaderPrograms[programID]; - const layout = programData.layout.descriptorSets.get(rate); + const layout = programData.layout.getSet(rate, isWebGPU); if (layout === undefined) { return getEmptyDescriptorSetLayout(); } @@ -819,6 +825,7 @@ export function getProgramDescriptorSetLayout ( programName: string, rate: UpdateFrequency, ): DescriptorSetLayout | null { + const isWebGPU = device.gfxAPI === API.WEBGPU; assert(rate < UpdateFrequency.PER_PHASE); const phase = lg.j(phaseID); const programID = phase.shaderIndex.get(programName); @@ -826,7 +833,7 @@ export function getProgramDescriptorSetLayout ( return null; } const programData = phase.shaderPrograms[programID]; - const layout = programData.layout.descriptorSets.get(rate); + const layout = programData.layout.getSet(rate, isWebGPU); if (layout === undefined) { return null; } @@ -900,6 +907,7 @@ export class WebProgramLibrary implements ProgramLibrary { return; } this.device = deviceIn; + this.isWebGPU = deviceIn.gfxAPI === API.WEBGPU; this.emptyDescriptorSetLayout = this.device.createDescriptorSetLayout(new DescriptorSetLayoutInfo()); this.emptyPipelineLayout = this.device.createPipelineLayout(new PipelineLayoutInfo()); @@ -910,10 +918,12 @@ export class WebProgramLibrary implements ProgramLibrary { UBOSkinning.initLayout(maxJoints); // init layout graph + const isWebGPU = this.isWebGPU; const lg = this.layoutGraph; for (const v of lg.v()) { const layout: PipelineLayoutData = lg.getLayout(v); - for (const [update, set] of layout.descriptorSets) { + const sets = layout.getSets(isWebGPU); + for (const [update, set] of sets) { initializeDescriptorSetLayoutInfo(set.descriptorSetLayoutData, set.descriptorSetLayoutInfo); set.descriptorSetLayout = this.device.createDescriptorSetLayout(set.descriptorSetLayoutInfo); assert(!!set.descriptorSetLayout); @@ -931,10 +941,10 @@ export class WebProgramLibrary implements ProgramLibrary { 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); + populatePipelineLayoutInfo(passLayout, UpdateFrequency.PER_PASS, info, isWebGPU); + populatePipelineLayoutInfo(phaseLayout, UpdateFrequency.PER_PHASE, info, isWebGPU); + populatePipelineLayoutInfo(phaseLayout, UpdateFrequency.PER_BATCH, info, isWebGPU); + populatePipelineLayoutInfo(phaseLayout, UpdateFrequency.PER_INSTANCE, info, isWebGPU); const phase = lg.j(phaseID); phase.pipelineLayout = this.device.createPipelineLayout(info); } @@ -996,7 +1006,15 @@ export class WebProgramLibrary implements ProgramLibrary { if (!this.mergeHighFrequency) { const phase = lg.j(phaseID); programData = new ShaderProgramData(); - buildProgramData(programName, srcShaderInfo, lg, phase, programData, this.fixedLocal); + buildProgramData( + programName, + srcShaderInfo, + lg, + phase, + programData, + this.fixedLocal, + this.isWebGPU, + ); } // shaderInfo and blockSizes @@ -1007,6 +1025,7 @@ export class WebProgramLibrary implements ProgramLibrary { srcShaderInfo, programData, this.fixedLocal, + this.isWebGPU, ); // overwrite programInfo @@ -1149,7 +1168,13 @@ export class WebProgramLibrary implements ProgramLibrary { if (this.mergeHighFrequency) { assert(phaseID !== INVALID_ID); const subpassOrPassID = this.layoutGraph.getParent(phaseID); - return getOrCreateDescriptorSetLayout(this.layoutGraph, subpassOrPassID, phaseID, UpdateFrequency.PER_BATCH); + return getOrCreateDescriptorSetLayout( + this.layoutGraph, + subpassOrPassID, + phaseID, + UpdateFrequency.PER_BATCH, + this.isWebGPU, + ); } return getOrCreateProgramDescriptorSetLayout( device, @@ -1164,7 +1189,13 @@ export class WebProgramLibrary implements ProgramLibrary { if (this.mergeHighFrequency) { assert(phaseID !== INVALID_ID); const subpassOrPassID = this.layoutGraph.getParent(phaseID); - return getOrCreateDescriptorSetLayout(this.layoutGraph, subpassOrPassID, phaseID, UpdateFrequency.PER_INSTANCE); + return getOrCreateDescriptorSetLayout( + this.layoutGraph, + subpassOrPassID, + phaseID, + UpdateFrequency.PER_INSTANCE, + this.isWebGPU, + ); } return getOrCreateProgramDescriptorSetLayout( device, @@ -1230,19 +1261,43 @@ export class WebProgramLibrary implements ProgramLibrary { // craete pipeline layout const info = new PipelineLayoutInfo(); - const passSet = getDescriptorSetLayout(this.layoutGraph, subpassOrPassID, phaseID, UpdateFrequency.PER_PASS); + const passSet = getDescriptorSetLayout( + this.layoutGraph, + subpassOrPassID, + phaseID, + UpdateFrequency.PER_PASS, + this.isWebGPU, + ); if (passSet) { info.setLayouts.push(passSet); } - const phaseSet = getDescriptorSetLayout(this.layoutGraph, subpassOrPassID, phaseID, UpdateFrequency.PER_PHASE); + const phaseSet = getDescriptorSetLayout( + this.layoutGraph, + subpassOrPassID, + phaseID, + UpdateFrequency.PER_PHASE, + this.isWebGPU, + ); if (phaseSet) { info.setLayouts.push(phaseSet); } - const batchSet = getProgramDescriptorSetLayout(device, lg, phaseID, programName, UpdateFrequency.PER_BATCH); + const batchSet = getProgramDescriptorSetLayout( + device, + lg, + phaseID, + programName, + UpdateFrequency.PER_BATCH, + ); if (batchSet) { info.setLayouts.push(batchSet); } - const instanceSet = getProgramDescriptorSetLayout(device, lg, phaseID, programName, UpdateFrequency.PER_INSTANCE); + const instanceSet = getProgramDescriptorSetLayout( + device, + lg, + phaseID, + programName, + UpdateFrequency.PER_INSTANCE, + ); if (instanceSet) { info.setLayouts.push(instanceSet); } @@ -1268,4 +1323,5 @@ export class WebProgramLibrary implements ProgramLibrary { public emptyPipelineLayout: PipelineLayout | null = null; public pipeline: PipelineRuntime | null = null; public device: Device | null = null; + public isWebGPU: boolean = false; }