diff --git a/.github/workflows/generate-android-ndk-cache.yml b/.github/workflows/generate-android-ndk-cache.yml new file mode 100644 index 00000000000..b83a53e92a8 --- /dev/null +++ b/.github/workflows/generate-android-ndk-cache.yml @@ -0,0 +1,31 @@ +name: Generate Android NDK Cache + +on: + workflow_dispatch: + inputs: + android_ndk_sdk_version: + description: 'Android NDK Version' + type: string + default: 'r21e' + required: true + +jobs: + generate_android_ndk_cache: + name: "Generate Android NDK Cache" + runs-on: ubuntu-latest + steps: + - name: Setup Android NDK + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: ${{ github.event.inputs.android_ndk_sdk_version }} + local-cache: true + + - name: Verify + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + run: | + cd ${ANDROID_NDK_HOME} + cat source.properties + platform=$(uname -s | tr '[:upper:]' '[:lower:]') + ./toolchains/llvm/prebuilt/${platform}-x86_64/bin/clang -v diff --git a/.github/workflows/generate-emsdk-cache.yml b/.github/workflows/generate-emsdk-cache.yml new file mode 100644 index 00000000000..09b5221a0e7 --- /dev/null +++ b/.github/workflows/generate-emsdk-cache.yml @@ -0,0 +1,26 @@ +name: Generate Emsdk Cache + +on: + workflow_dispatch: + inputs: + emsdk_version: + description: 'Emsdk version' + type: string + default: '3.1.45' + required: true + +jobs: + generate_emsdk_cache: + name: "Generate Emsdk cache" + runs-on: ubuntu-latest + steps: + - name: Setup emsdk + uses: dumganhar/setup-emsdk@997d2cde2deabda085a11f98e86e842915b0e846 + with: + version: ${{ github.event.inputs.emsdk_version }} + actions-cache-folder: 'emsdk-cache' + + - name: Verify + run: | + which emcc + emcc -v \ No newline at end of file diff --git a/.github/workflows/generate-oh-sdk-cache.yml b/.github/workflows/generate-oh-sdk-cache.yml new file mode 100644 index 00000000000..4e4c609de1c --- /dev/null +++ b/.github/workflows/generate-oh-sdk-cache.yml @@ -0,0 +1,80 @@ +name: Generate OH SDK Cache + +on: + workflow_dispatch: + inputs: + oh_sdk_version: + description: 'Openharmony SDK version' + type: string + default: '9' + required: true + +jobs: + generate_oh_sdk_cache: + name: "Generate OH SDK cache" + runs-on: ubuntu-latest + steps: + - uses: actions/setup-java@v3 + id: setup-jdk + with: + distribution: 'zulu' + java-version: '17' + + - name: Get oh sdk cache directory path + id: oh-sdk-cache-dir-path + run: | + echo "cache dir: " + echo "dir=$HOME/openharmony" >> $GITHUB_OUTPUT + + - name: Output cache dir + run: | + echo "Output cache dir: ${{ steps.oh-sdk-cache-dir-path.outputs.dir }}" + + - name: Cache OH SDK + id: cache-oh-sdk + uses: actions/cache@v3 + env: + cache-name: cache-oh-sdk-${{ github.event.inputs.oh_sdk_version }} + with: + path: ${{ steps.oh-sdk-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-build-${{ env.cache-name }} + + - name: Add package.json + run: | + echo "{}" > package.json + echo "{\"name\": \"tests\",\"lockfileVersion\": 3,\"requires\": true,\"packages\": {}}" > package-lock.json + - uses: actions/setup-node@v3 + with: + node-version: 14 + cache: 'npm' + + - if: ${{ steps.cache-oh-sdk.outputs.cache-hit != 'true' }} + name: No Cache found, install oh sdk + continue-on-error: false + run: | + if [ ! -d "$HOME/openharmony" ]; then + mkdir -p $HOME/openharmony + echo "Download commandline-tools-linux.zip ..." + curl -o commandline-tools-linux.zip "https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_package_901_9/b1/v3/E6zhv5UFQ2-inIwNJhTN6Q/commandline-tools-linux-2.0.0.2.zip?HW-CC-KV=V1&HW-CC-Date=20230621T074401Z&HW-CC-Expire=315360000&HW-CC-Sign=621224257B02079B1E76C0A56FDF21483400B1E3556213F88DC79BC9BE7D595D" + echo "Unzip commandline-tools-linux.zip ..." + unzip commandline-tools-linux.zip -d $HOME/openharmony > /dev/null + cd $HOME/openharmony + ls -l + cd command-line-tools + echo "=============== PATCHING sdkmanager/bin/sdkmgr file ===============" + sed -i "s@-Dfile.encoding=UTF-8@-Dfile.encoding=UTF-8 -Duser.country=CN@g" ./sdkmanager/bin/sdkmgr + cd bin + ./sdkmgr list + echo "=============== INSTALL HOS toolchains:${{ github.event.inputs.oh_sdk_version }} ===============" + ./sdkmgr install toolchains:${{ github.event.inputs.oh_sdk_version }} --accept-license > /dev/null + echo "=============== INSTALL OH SDK ets:${{ github.event.inputs.oh_sdk_version }} ===============" + ./sdkmgr install OpenHarmony/ets:${{ github.event.inputs.oh_sdk_version }} --accept-license > /dev/null + echo "=============== INSTALL OH SDK js:${{ github.event.inputs.oh_sdk_version }} ===============" + ./sdkmgr install OpenHarmony/js:${{ github.event.inputs.oh_sdk_version }} --accept-license > /dev/null + echo "=============== INSTALL OH SDK native:${{ github.event.inputs.oh_sdk_version }} ===============" + ./sdkmgr install OpenHarmony/native:${{ github.event.inputs.oh_sdk_version }} --accept-license > /dev/null + echo "=============== INSTALL OH SDK toolchains:${{ github.event.inputs.oh_sdk_version }} ===============" + ./sdkmgr install OpenHarmony/toolchains:${{ github.event.inputs.oh_sdk_version }} --accept-license > /dev/null + echo "=============== INSTALL OH SDK DONE ===============" + ./sdkmgr list + fi \ No newline at end of file diff --git a/.github/workflows/generate-vulkan-sdk-cache.yml b/.github/workflows/generate-vulkan-sdk-cache.yml new file mode 100644 index 00000000000..2904d1a9493 --- /dev/null +++ b/.github/workflows/generate-vulkan-sdk-cache.yml @@ -0,0 +1,22 @@ +name: Generate Vulkan SDK Cache + +on: + workflow_dispatch: + inputs: + vulkan_sdk_version: + description: 'Vulkan SDK version' + type: string + default: '1.2.189.0' + required: true + +jobs: + generate_vulkan_sdk_cache: + name: "Generate Vulkan SDK cache" + runs-on: windows-2019 + steps: + - name: Setup Vulkan SDK + uses: humbletim/setup-vulkan-sdk@v1.2.0 + with: + vulkan-query-version: ${{ github.event.inputs.vulkan_sdk_version }} + vulkan-components: Vulkan-Headers, Vulkan-Loader + vulkan-use-cache: true \ No newline at end of file diff --git a/.github/workflows/native-bindings.yml b/.github/workflows/native-bindings.yml index 188cefc723e..52d014120a1 100644 --- a/.github/workflows/native-bindings.yml +++ b/.github/workflows/native-bindings.yml @@ -26,6 +26,7 @@ jobs: with: ndk-version: r21e add-to-path: false + local-cache: true - name: Generate decorators run: | cd native diff --git a/.github/workflows/native-compile-platforms.yml b/.github/workflows/native-compile-platforms.yml index 272a98f70bb..e17a552204b 100644 --- a/.github/workflows/native-compile-platforms.yml +++ b/.github/workflows/native-compile-platforms.yml @@ -74,6 +74,7 @@ jobs: with: ndk-version: r21e add-to-path: false + local-cache: true - uses: actions/setup-java@v3 id: setup-jdk with: @@ -144,6 +145,7 @@ jobs: with: ndk-version: r21e add-to-path: false + local-cache: true - uses: actions/setup-java@v3 id: setup-jdk with: @@ -237,7 +239,7 @@ jobs: id: cache-oh-sdk uses: actions/cache@v3 env: - cache-name: cache-oh-sdk-2 + cache-name: cache-oh-sdk-9 with: path: ${{ steps.oh-sdk-cache-dir-path.outputs.dir }} key: ${{ runner.os }}-build-${{ env.cache-name }} @@ -510,33 +512,3 @@ jobs: cmake --build . --config Debug -- -jobs $NUM_OF_CORES CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO fi echo "Compile iOS Done!" - - compile_wgpu_mac: - name: "Emscripten" - runs-on: macos-latest - if: contains( github.event.pull_request.title, 'WGPU_CI_ON' ) - steps: - - uses: actions/checkout@v2 - - name: Download external libraries - shell: bash - run: | - EXT_VERSION=`node ./.github/workflows/get-native-external-version.js` - git clone --branch $EXT_VERSION --depth 1 https://github.com/cocos/cocos-engine-external native/external - - name: Setup Emscripten - run: | - NATIVE_ROOT=$GITHUB_WORKSPACE/native - git clone https://github.com/emscripten-core/emsdk.git NATIVE_ROOT/../../emsdk - cd NATIVE_ROOT/../../emsdk - ./emsdk install 3.1.17 - ./emsdk activate 3.1.17 - source ./emsdk_env.sh - emcc -v - - name: Compile - env: - COCOS_ENGINE_DEV: 1 - run: | - NATIVE_ROOT=$GITHUB_WORKSPACE/native - cd $NATIVE_ROOT/cocos/renderer/gfx-wgpu - $NATIVE_ROOT/../../emsdk/upstream/emscripten/emcmake cmake . - $NATIVE_ROOT/../../emsdk/upstream/emscripten/emmake make - echo "Compile WGPU by ems on MacOS Done!" diff --git a/.github/workflows/native-compile-webgpu.yml b/.github/workflows/native-compile-webgpu.yml new file mode 100644 index 00000000000..91b16e6359f --- /dev/null +++ b/.github/workflows/native-compile-webgpu.yml @@ -0,0 +1,66 @@ +name: Compile WebGPU + +on: + pull_request: + paths: + - 'native/external-config.json' + - 'native/cocos/base/**' + - 'native/cocos/renderer/gfx-base/**' + - 'native/cocos/renderer/gfx-wgpu/**' + - 'native/cocos/renderer/gfx-validator/**' + - 'native/cocos/renderer/gfx-empty/**' + - '.github/workflows/native-compile-webgpu.yml' + +# github.head_ref is only defined on pull_request events +concurrency: + group: ${{ github.workflow }}-${{ github.actor }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + compile_wgpu: + if: + (! contains(github.event.pull_request.body, '[X] does not change any runtime related code or build configuration')) + name: "Emscripten" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Download external libraries + shell: bash + run: | + EXT_VERSION=`node ./.github/workflows/get-native-external-version.js` + git clone --branch $EXT_VERSION --depth 1 https://github.com/cocos/cocos-engine-external native/external + + - name: Setup emsdk + uses: dumganhar/setup-emsdk@997d2cde2deabda085a11f98e86e842915b0e846 + with: + version: 3.1.45 + actions-cache-folder: 'emsdk-cache' + + - name: Verify + run: | + which emcc + emcc -v + + - name: Install ninja + run: | + if ! command -v ninja &> /dev/null; then + echo "Ninja not found, installing..." + # sudo apt update + sudo apt install ninja-build + else + echo "Ninja is already installed." + fi + which ninja + + - name: Compile + # env: + # COCOS_ENGINE_DEV: 1 + run: | + NATIVE_ROOT=$GITHUB_WORKSPACE/native + cd $NATIVE_ROOT/cocos/renderer/gfx-wgpu + mkdir build + cd build + cmake .. -GNinja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Debug + ninja + echo "============== Compile WGPU by ems on Ubuntu Done! ==============" diff --git a/.github/workflows/native-linter-android.yml b/.github/workflows/native-linter-android.yml index da9c6cea328..edc7d90ae92 100644 --- a/.github/workflows/native-linter-android.yml +++ b/.github/workflows/native-linter-android.yml @@ -39,6 +39,7 @@ jobs: with: ndk-version: r21e add-to-path: false + local-cache: true - name: Get changed files uses: PatriceJiang/paths-filter@master id: listchanged diff --git a/.gitignore b/.gitignore index 229c3ea39a6..7ba394514c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -/lib +lib/ +.turbo/ /bin /web.config .idea diff --git a/cc.config.json b/cc.config.json index 7b51e5dd86e..cb74327075f 100644 --- a/cc.config.json +++ b/cc.config.json @@ -678,6 +678,12 @@ "type": "boolean", "value": false, "internal": true + }, + "CULL_MESHOPT": { + "comment": "An internal constant to indicate whether we cull the meshopt wasm module and asm.js module.", + "type": "boolean", + "value": true, + "internal": true } }, diff --git a/cocos/2d/assembler/label/letter-font.ts b/cocos/2d/assembler/label/letter-font.ts index 3acef2a0bcf..c189b39fb2a 100644 --- a/cocos/2d/assembler/label/letter-font.ts +++ b/cocos/2d/assembler/label/letter-font.ts @@ -25,7 +25,7 @@ import { js } from '../../../core'; import { Label, LabelOutline } from '../../components'; import { bmfontUtils } from './bmfontUtils'; -import { shareLabelInfo, LetterAtlas, computeHash } from './font-utils'; +import { shareLabelInfo, LetterAtlas, computeHash, LetterRenderTexture } from './font-utils'; const _atlasWidth = 1024; const _atlasHeight = 1024; @@ -39,7 +39,7 @@ export const letterFont = js.mixin(bmfontUtils, { _shareAtlas = new LetterAtlas(_atlasWidth, _atlasHeight); } - return _shareAtlas.getTexture(); + return _shareAtlas.getTexture() as LetterRenderTexture | null; }, _updateFontFamily (comp) { @@ -47,12 +47,12 @@ export const letterFont = js.mixin(bmfontUtils, { shareLabelInfo.fontFamily = this._getFontFamily(comp); // outline - const outline = comp.getComponent(LabelOutline); - if (outline && outline.enabled) { + const isOutlined = comp.enableOutline && comp.outlineWidth > 0; + if (isOutlined) { shareLabelInfo.isOutlined = true; - shareLabelInfo.margin = outline.width; - shareLabelInfo.out = outline.color.clone(); - shareLabelInfo.out.a = outline.color.a * comp.color.a / 255.0; + shareLabelInfo.margin = comp.outlineWidth; + shareLabelInfo.out = comp.outlineColor.clone(); + shareLabelInfo.out.a = comp.outlineColor.color.a * comp.color.a / 255.0; } else { shareLabelInfo.isOutlined = false; shareLabelInfo.margin = 0; diff --git a/cocos/2d/assembler/label/ttfUtils.ts b/cocos/2d/assembler/label/ttfUtils.ts index 941cad48eac..c4ff09c4aa0 100644 --- a/cocos/2d/assembler/label/ttfUtils.ts +++ b/cocos/2d/assembler/label/ttfUtils.ts @@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { Label, LabelOutline, LabelShadow } from '../../components'; +import { Label } from '../../components'; import { ISharedLabelData } from './font-utils'; import { UITransform } from '../../framework/ui-transform'; import { dynamicAtlasManager } from '../../utils/dynamic-atlas/atlas-manager'; @@ -30,13 +30,20 @@ import { TextOutputLayoutData, TextOutputRenderData } from './text-output-data'; import { TextStyle } from './text-style'; import { TextLayout } from './text-layout'; import { view } from '../../../ui/view'; +import { approx } from '../../../core'; const Overflow = Label.Overflow; export const ttfUtils = { - updateProcessingData (style: TextStyle, layout: TextLayout, - outputLayoutData: TextOutputLayoutData, outputRenderData: TextOutputRenderData, comp: Label, trans: UITransform): void { + updateProcessingData ( + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + outputRenderData: TextOutputRenderData, + comp: Label, + trans: UITransform, + ): void { // font info // both style.isSystemFontUsed = comp.useSystemFont; style.fontSize = comp.fontSize; @@ -62,25 +69,23 @@ export const ttfUtils = { style.underlineHeight = comp.underlineHeight; // outline// both - let outlineComp = LabelOutline && comp.getComponent(LabelOutline); - outlineComp = (outlineComp && outlineComp.enabled && outlineComp.width > 0) ? outlineComp : null; - if (outlineComp) { + const isOutlined = comp.enableOutline && comp.outlineWidth > 0; + if (isOutlined) { style.isOutlined = true; - style.outlineColor.set(outlineComp.color); - style.outlineWidth = outlineComp.width; + style.outlineColor.set(comp.outlineColor); + style.outlineWidth = comp.outlineWidth; } else { style.isOutlined = false; } // shadow// both - let shadowComp = LabelShadow && comp.getComponent(LabelShadow); - shadowComp = (shadowComp && shadowComp.enabled) ? shadowComp : null; - if (shadowComp) { + const isShadow = comp.enableShadow && (comp.shadowBlur > 0 || !approx(comp.shadowOffset.x, 0) || !approx(comp.shadowOffset.y, 0)); + if (isShadow) { style.hasShadow = true; - style.shadowColor.set(shadowComp.color); - style.shadowBlur = shadowComp.blur; - style.shadowOffsetX = shadowComp.offset.x; - style.shadowOffsetY = shadowComp.offset.y; + style.shadowColor.set(comp.shadowColor); + style.shadowBlur = comp.shadowBlur; + style.shadowOffsetX = comp.shadowOffset.x; + style.shadowOffsetY = comp.shadowOffset.y; } else { style.hasShadow = false; } @@ -126,8 +131,15 @@ export const ttfUtils = { // TextProcessing processing.processingString(false, style, layout, outputLayoutData, comp.string); - processing.generateRenderInfo(false, style, layout, outputLayoutData, outputRenderData, - comp.string, this.generateVertexData); + processing.generateRenderInfo( + false, + style, + layout, + outputLayoutData, + outputRenderData, + comp.string, + this.generateVertexData, + ); const renderData = comp.renderData; renderData.textureDirty = true; @@ -173,9 +185,11 @@ export const ttfUtils = { }, updateVertexData (comp: Label): void { + // no needs to update vertex data }, updateUVs (comp: Label): void { + // no needs to update uv data }, _updateFontFamily (comp: Label): string { diff --git a/cocos/2d/components/deprecated.ts b/cocos/2d/components/deprecated.ts index 0e8eb00bceb..cd2ed98e9d1 100644 --- a/cocos/2d/components/deprecated.ts +++ b/cocos/2d/components/deprecated.ts @@ -31,7 +31,8 @@ import { UIMeshRenderer } from './ui-mesh-renderer'; import { Graphics } from './graphics'; import { UIStaticBatch } from './ui-static-batch'; import { UIOpacity } from './ui-opacity'; -import { js, cclegacy, replaceProperty } from '../../core'; +import { js, cclegacy, replaceProperty, markAsWarning } from '../../core'; +import { LabelShadow } from './label-shadow'; /** * Alias of [[Mask]] @@ -125,3 +126,29 @@ replaceProperty(MaskType, 'MaskType', [ targetName: 'MaskType', }, ]); + +markAsWarning(LabelOutline.prototype, 'LabelOutline.prototype', [ + { + name: 'width', + suggest: 'Please use Label.prototype.outlineWidth instead.', + }, + { + name: 'color', + suggest: 'Please use Label.prototype.outlineColor instead.', + }, +]); + +markAsWarning(LabelShadow.prototype, 'LabelShadow.prototype', [ + { + name: 'color', + suggest: 'Please use Label.prototype.shadowColor instead.', + }, + { + name: 'offset', + suggest: 'Please use Label.prototype.shadowOffset instead.', + }, + { + name: 'blur', + suggest: 'Please use Label.prototype.shadowBlur instead.', + }, +]); diff --git a/cocos/2d/components/label-outline.ts b/cocos/2d/components/label-outline.ts index ba50ae80fa8..5f765d19fcf 100644 --- a/cocos/2d/components/label-outline.ts +++ b/cocos/2d/components/label-outline.ts @@ -25,7 +25,7 @@ import { ccclass, help, executionOrder, menu, tooltip, requireComponent, executeInEditMode, serializable } from 'cc.decorator'; import { Component } from '../../scene-graph/component'; -import { Color, cclegacy } from '../../core'; +import { Color, assertIsTrue, cclegacy } from '../../core'; import { Label } from './label'; /** @@ -35,15 +35,7 @@ import { Label } from './label'; * @zh * 描边效果组件,用于字体描边,只能用于系统字体。 * - * @example - * ```ts - * import { Node, Label, LabelOutline } from 'cc'; - * // Create a new node and add label components. - * const node = new Node("New Label"); - * const label = node.addComponent(Label); - * const outline = node.addComponent(LabelOutline); - * node.parent = this.node; - * ``` + * @deprecated since v3.8.2, please use [[Label.enableOutline]] instead. */ @ccclass('cc.LabelOutline') @help('i18n:cc.LabelOutline') @@ -52,11 +44,6 @@ import { Label } from './label'; @requireComponent(Label) @executeInEditMode export class LabelOutline extends Component { - @serializable - protected _color = new Color(0, 0, 0, 255); - @serializable - protected _width = 2; - /** * @en * Outline color. @@ -64,25 +51,19 @@ export class LabelOutline extends Component { * @zh * 改变描边的颜色。 * - * @example - * ```ts - * import { Color } from 'cc'; - * outline.color = new Color(0.5, 0.3, 0.7, 1.0); - * ``` + * @deprecated since v3.8.2, please use [[Label.outlineColor]] instead. */ @tooltip('i18n:labelOutline.color') - // @constget get color (): Readonly { - return this._color; + const label = this.node.getComponent(Label); + assertIsTrue(label); + return label.outlineColor; } set color (value) { - if (this._color === value) { - return; - } - - this._color.set(value); - this._updateRenderData(); + const label = this.node.getComponent(Label); + assertIsTrue(label); + label.outlineColor = value; } /** @@ -92,38 +73,37 @@ export class LabelOutline extends Component { * @zh * 改变描边的宽度。 * - * @example - * ```ts - * outline.width = 3; - * ``` + * @deprecated since v3.8.2, please use [[Label.outlineWidth]] instead. */ @tooltip('i18n:labelOutline.width') get width (): number { - return this._width; + const label = this.node.getComponent(Label); + assertIsTrue(label); + return label.outlineWidth; } set width (value) { - if (this._width === value) { - return; - } - - this._width = value; - this._updateRenderData(); + const label = this.node.getComponent(Label); + assertIsTrue(label); + label.outlineWidth = value; } + /** + * @deprecated since v3.8.2, please use [[Label.enableOutline]] instead. + */ public onEnable (): void { - this._updateRenderData(); + const label = this.node.getComponent(Label); + assertIsTrue(label); + label.enableOutline = true; } + /** + * @deprecated since v3.8.2, please use [[Label.enableOutline]] instead. + */ public onDisable (): void { - this._updateRenderData(); - } - - protected _updateRenderData (): void { const label = this.node.getComponent(Label); - if (label) { - label.updateRenderData(true); - } + assertIsTrue(label); + label.enableOutline = false; } } diff --git a/cocos/2d/components/label-shadow.ts b/cocos/2d/components/label-shadow.ts index bab05719cbd..36e7685d0ff 100644 --- a/cocos/2d/components/label-shadow.ts +++ b/cocos/2d/components/label-shadow.ts @@ -25,19 +25,14 @@ import { ccclass, help, executionOrder, menu, tooltip, requireComponent, executeInEditMode, serializable } from 'cc.decorator'; import { Component } from '../../scene-graph/component'; -import { Color, Vec2 } from '../../core'; +import { Color, Vec2, assertIsTrue } from '../../core'; import { Label } from './label'; /** * @en Shadow effect for Label component, only for system fonts or TTF fonts. * @zh 用于给 Label 组件添加阴影效果,只能用于系统字体或 ttf 字体。 - * @example - * import { Node, Label, LabelShadow } from 'cc'; - * // Create a new node and add label components. - * const node = new Node("New Label"); - * const label = node.addComponent(Label); - * const shadow = node.addComponent(LabelShadow); - * node.parent = this.node; + * + * @deprecated since v3.8.2, please use [[Label.enableShadow]] instead. */ @ccclass('cc.LabelShadow') @help('i18n:cc.LabelShadow') @@ -46,13 +41,6 @@ import { Label } from './label'; @requireComponent(Label) @executeInEditMode export class LabelShadow extends Component { - @serializable - protected _color = new Color(0, 0, 0, 255); - @serializable - protected _offset = new Vec2(2, 2); - @serializable - protected _blur = 2; - /** * @en * Shadow color. @@ -60,24 +48,19 @@ export class LabelShadow extends Component { * @zh * 阴影的颜色。 * - * @example - * ```ts - * import { Color } from 'cc'; - * labelShadow.color = new Color(0.5, 0.3, 0.7, 1.0); - * ``` + * @deprecated since v3.8.2, please use [[Label.shadowColor]] instead. */ @tooltip('i18n:labelShadow.color') get color (): Readonly { - return this._color; + const label = this.node.getComponent(Label); + assertIsTrue(label); + return label.shadowColor; } set color (value) { - if (this._color === value) { - return; - } - - this._color.set(value); - this._updateRenderData(); + const label = this.node.getComponent(Label); + assertIsTrue(label); + label.shadowColor = value; } /** @@ -87,20 +70,19 @@ export class LabelShadow extends Component { * @zh * 字体与阴影的偏移。 * - * @example - * ```ts - * import { Vec2 } from 'cc'; - * labelShadow.offset = new Vec2(2, 2); - * ``` + * @deprecated since v3.8.2, please use [[Label.shadowOffset]] instead. */ @tooltip('i18n:labelShadow.offset') get offset (): Vec2 { - return this._offset; + const label = this.node.getComponent(Label); + assertIsTrue(label); + return label.shadowOffset; } set offset (value) { - this._offset = value; - this._updateRenderData(); + const label = this.node.getComponent(Label); + assertIsTrue(label); + label.shadowOffset = value; } /** @@ -110,33 +92,36 @@ export class LabelShadow extends Component { * @zh * 阴影的模糊程度。 * - * @example - * ```ts - * labelShadow.blur = 2; - * ``` + * @deprecated since v3.8.2, please use [[Label.shadowBlur]] instead. */ @tooltip('i18n:labelShadow.blur') get blur (): number { - return this._blur; + const label = this.node.getComponent(Label); + assertIsTrue(label); + return label.shadowBlur; } set blur (value) { - this._blur = value; - this._updateRenderData(); + const label = this.node.getComponent(Label); + assertIsTrue(label); + label.shadowBlur = value; } + /** + * @deprecated since v3.8.2, please use [[Label.enableShadow]] instead. + */ public onEnable (): void { - this._updateRenderData(); + const label = this.node.getComponent(Label); + assertIsTrue(label); + label.enableShadow = true; } + /** + * @deprecated since v3.8.2, please use [[Label.enableShadow]] instead. + */ public onDisable (): void { - this._updateRenderData(); - } - - protected _updateRenderData (): void { const label = this.node.getComponent(Label); - if (label) { - label.updateRenderData(true); - } + assertIsTrue(label); + label.enableShadow = false; } } diff --git a/cocos/2d/components/label.ts b/cocos/2d/components/label.ts index b22846c7875..37003194c46 100644 --- a/cocos/2d/components/label.ts +++ b/cocos/2d/components/label.ts @@ -28,7 +28,7 @@ import { BYTEDANCE, EDITOR, JSB } from 'internal:constants'; import { minigame } from 'pal/minigame'; import { BitmapFont, Font, SpriteFrame } from '../assets'; import { ImageAsset, Texture2D } from '../../asset/assets'; -import { ccenum, cclegacy, Color } from '../../core'; +import { ccenum, cclegacy, Color, Vec2 } from '../../core'; import { IBatcher } from '../renderer/i-batcher'; import { FontAtlas } from '../assets/bitmap-font'; import { CanvasPool, ISharedLabelData, LetterRenderTexture } from '../assembler/label/font-utils'; @@ -594,15 +594,152 @@ export class Label extends UIRenderer { @editable @displayOrder(18) @tooltip('i18n:label.underline_height') - public get underlineHeight (): number { + get underlineHeight (): number { return this._underlineHeight; } - public set underlineHeight (value) { + set underlineHeight (value) { if (this._underlineHeight === value) return; this._underlineHeight = value; this.markForUpdateRenderData(); } + /** + ** @en + ** Outline effect used to change the display, only for system fonts or TTF fonts. + ** + ** @zh + ** 描边效果组件,用于字体描边,只能用于系统字体或 ttf 字体。 + **/ + @editable + @visible(function (this: Label) { return !(this._font instanceof BitmapFont); }) + @displayOrder(19) + @tooltip('i18n:label.outline_enable') + get enableOutline (): boolean { + return this._enableOutline; + } + set enableOutline (value) { + if (this._enableOutline === value) return; + this._enableOutline = value; + this.markForUpdateRenderData(); + } + + /** + * @en + * Outline color. + * + * @zh + * 改变描边的颜色。 + */ + @editable + @visible(function (this: Label) { return this._enableOutline && !(this._font instanceof BitmapFont); }) + @displayOrder(20) + @tooltip('i18n:label.outline_color') + get outlineColor (): Color { + return this._outlineColor; + } + set outlineColor (value) { + if (this._outlineColor === value) return; + this._outlineColor.set(value); + this.markForUpdateRenderData(); + } + + /** + * @en + * Change the outline width. + * + * @zh + * 改变描边的宽度。 + */ + @editable + @visible(function (this: Label) { return this._enableOutline && !(this._font instanceof BitmapFont); }) + @displayOrder(21) + @tooltip('i18n:label.outline_width') + get outlineWidth (): number { + return this._outlineWidth; + } + set outlineWidth (value) { + if (this._outlineWidth === value) return; + this._outlineWidth = value; + this.markForUpdateRenderData(); + } + + /** + * @en Shadow effect for Label component, only for system fonts or TTF fonts. Disabled when cache mode is char. + * @zh 用于给 Label 组件添加阴影效果,只能用于系统字体或 ttf 字体。在缓存模式为 char 时不可用。 + */ + @editable + @visible(function (this: Label) { return !(this._font instanceof BitmapFont) && (this.cacheMode !== CacheMode.CHAR); }) + @displayOrder(22) + @tooltip('i18n:label.shadow_enable') + get enableShadow (): boolean { + return this._enableShadow; + } + set enableShadow (value) { + if (this._enableShadow === value) return; + this._enableShadow = value; + this.markForUpdateRenderData(); + } + + /** + * @en + * Shadow color. + * + * @zh + * 阴影的颜色。 + */ + @editable + @visible(function (this: Label) { return this._enableShadow && !(this._font instanceof BitmapFont) && (this.cacheMode !== CacheMode.CHAR); }) + @displayOrder(23) + @tooltip('i18n:label.shadow_color') + get shadowColor (): Color { + return this._shadowColor; + } + set shadowColor (value) { + if (this._shadowColor === value) return; + this._shadowColor.set(value); + this.markForUpdateRenderData(); + } + + /** + * @en + * Offset between font and shadow. + * + * @zh + * 字体与阴影的偏移。 + */ + @editable + @visible(function (this: Label) { return this._enableShadow && !(this._font instanceof BitmapFont) && (this.cacheMode !== CacheMode.CHAR); }) + @displayOrder(24) + @tooltip('i18n:label.shadow_offset') + get shadowOffset (): Vec2 { + return this._shadowOffset; + } + set shadowOffset (value) { + if (this._shadowOffset === value) return; + this._shadowOffset.set(value); + this.markForUpdateRenderData(); + } + + /** + * @en + * A non-negative float specifying the level of shadow blur. + * + * @zh + * 阴影的模糊程度。 + */ + @editable + @visible(function (this: Label) { return this._enableShadow && !(this._font instanceof BitmapFont) && (this.cacheMode !== CacheMode.CHAR); }) + @displayOrder(25) + @tooltip('i18n:label.shadow_blur') + get shadowBlur (): number { + return this._shadowBlur; + } + set shadowBlur (value) { + if (this._shadowBlur === value) return; + this._shadowBlur = value; + this.markForUpdateRenderData(); + } + /** * @deprecated since v3.7.0, this is an engine private interface that will be removed in the future. */ @@ -705,6 +842,20 @@ export class Label extends UIRenderer { protected _underlineHeight = 2; @serializable protected _cacheMode = CacheMode.NONE; + @serializable + protected _enableOutline = false; + @serializable + protected _outlineColor = new Color(0, 0, 0, 255); + @serializable + protected _outlineWidth = 2; + @serializable + protected _enableShadow = false; + @serializable + protected _shadowColor = new Color(0, 0, 0, 255); + @serializable + protected _shadowOffset = new Vec2(2, 2); + @serializable + protected _shadowBlur = 2; // don't need serialize // 这个保存了旧项目的 file 数据 diff --git a/cocos/2d/components/mask.ts b/cocos/2d/components/mask.ts index a01247055d4..4cb13d13d42 100644 --- a/cocos/2d/components/mask.ts +++ b/cocos/2d/components/mask.ts @@ -456,11 +456,9 @@ export class Mask extends Component { protected _removeMaskNode (): void { if (this._sprite) { - this._sprite.destroy(); this._sprite = null; } if (this._graphics) { - this._graphics.destroy(); this._graphics = null; } } diff --git a/cocos/2d/renderer/batcher-2d.ts b/cocos/2d/renderer/batcher-2d.ts index 13e7126ec76..253b90ef0ef 100644 --- a/cocos/2d/renderer/batcher-2d.ts +++ b/cocos/2d/renderer/batcher-2d.ts @@ -490,8 +490,15 @@ export class Batcher2D implements IBatcher { * @param mat - The material used * @param enableBatch - component support multi draw batch or not */ - public commitMiddleware (comp: UIRenderer, meshBuffer: MeshBuffer, indexOffset: number, - indexCount: number, tex: TextureBase, mat: Material, enableBatch: boolean): void { + public commitMiddleware ( + comp: UIRenderer, + meshBuffer: MeshBuffer, + indexOffset: number, + indexCount: number, + tex: TextureBase, + mat: Material, + enableBatch: boolean, + ): void { // check if need merge draw batch const texture = tex.getGFXTexture(); if (enableBatch && this._middlewareEnableBatch && this._middlewareBuffer === meshBuffer @@ -654,7 +661,7 @@ export class Batcher2D implements IBatcher { this._currBID = -1; // Request ia failed - if (!ia) { + if (!ia || !this._currTexture) { return; } @@ -671,7 +678,7 @@ export class Batcher2D implements IBatcher { const curDrawBatch = this._currStaticRoot ? this._currStaticRoot._requireDrawBatch() : this._drawBatchPool.alloc(); curDrawBatch.visFlags = this._currLayer; - curDrawBatch.texture = this._currTexture!; + curDrawBatch.texture = this._currTexture; curDrawBatch.sampler = this._currSampler; curDrawBatch.inputAssembler = ia; curDrawBatch.useLocalData = this._currTransform; diff --git a/cocos/3d/assets/mesh.ts b/cocos/3d/assets/mesh.ts index d064d93ddb0..5011a6e09d8 100644 --- a/cocos/3d/assets/mesh.ts +++ b/cocos/3d/assets/mesh.ts @@ -28,11 +28,11 @@ import { Asset } from '../../asset/assets/asset'; import { IDynamicGeometry } from '../../primitive/define'; import { BufferBlob } from '../misc/buffer-blob'; import { Skeleton } from './skeleton'; -import { geometry, cclegacy, sys, warnID, Mat4, Quat, Vec3, assertIsTrue, murmurhash2_32_gc, errorID } from '../../core'; +import { geometry, cclegacy, sys, warnID, Mat4, Quat, Vec3, assertIsTrue, murmurhash2_32_gc, errorID, halfToFloat } from '../../core'; import { RenderingSubMesh } from '../../asset/assets'; import { Attribute, Device, Buffer, BufferInfo, AttributeName, BufferUsageBit, Feature, Format, - FormatInfos, FormatType, MemoryUsageBit, PrimitiveMode, getTypedArrayConstructor, DrawInfo, FormatInfo, deviceManager, + FormatInfos, FormatType, MemoryUsageBit, PrimitiveMode, getTypedArrayConstructor, DrawInfo, FormatInfo, deviceManager, FormatFeatureBit, } from '../../gfx'; import { Morph } from './morph'; import { MorphRendering, createMorphRendering } from './morph-rendering'; @@ -409,6 +409,11 @@ export class Mesh extends Asset { if (this.struct.encoded) { // decode mesh data info = decodeMesh(info); } + if (this.struct.quantized + && !(deviceManager.gfxDevice.getFormatFeatures(Format.RGB16F) & FormatFeatureBit.VERTEX_ATTRIBUTE)) { + // dequantize mesh data + info = dequantizeMesh(info); + } this._struct = info.struct; this._data = info.data; @@ -1507,11 +1512,6 @@ export function decodeMesh (mesh: Mesh.ICreateInfo): Mesh.ICreateInfo { return mesh; } - // decode the mesh - if (!MeshoptDecoder.supported) { - return mesh; - } - const res_checker = (res: number): void => { if (res < 0) { errorID(14204, res); @@ -1581,4 +1581,150 @@ export function inflateMesh (mesh: Mesh.ICreateInfo): Mesh.ICreateInfo { return mesh; } +export function dequantizeMesh (mesh: Mesh.ICreateInfo): Mesh.ICreateInfo { + const struct = JSON.parse(JSON.stringify(mesh.struct)) as Mesh.IStruct; + + const bufferBlob = new BufferBlob(); + bufferBlob.setNextAlignment(0); + + function transformVertex ( + reader: ((offset: number) => number), + writer: ((offset: number, value: number) => void), + count: number, + components: number, + componentSize: number, + readerStride: number, + writerStride: number, + ): void { + for (let i = 0; i < count; i++) { + for (let j = 0; j < components; j++) { + const inputOffset = readerStride * i + componentSize * j; + const outputOffset = writerStride * i + componentSize * j; + writer(outputOffset, reader(inputOffset)); + } + } + } + + function dequantizeHalf ( + reader: ((offset: number) => number), + writer: ((offset: number, value: number) => void), + count: number, + components: number, + readerStride: number, + writerStride: number, + ): void { + for (let i = 0; i < count; i++) { + for (let j = 0; j < components; j++) { + const inputOffset = readerStride * i + 2 * j; + const outputOffset = writerStride * i + 4 * j; + const value = halfToFloat(reader(inputOffset)); + writer(outputOffset, value); + } + } + } + + for (let i = 0; i < struct.vertexBundles.length; ++i) { + const bundle = struct.vertexBundles[i]; + const view = bundle.view; + const attributes = bundle.attributes; + const oldAttributes = mesh.struct.vertexBundles[i].attributes; + const strides: number[] = []; + const dequantizes: boolean[] = []; + const readers: ((offset: number) => number)[] = []; + for (let j = 0; j < attributes.length; ++j) { + const attr = attributes[j]; + const inputView = new DataView(mesh.data.buffer, view.offset + getOffset(oldAttributes, j)); + const reader = getReader(inputView, attr.format); + let dequantize = true; + switch (attr.format) { + case Format.R16F: + attr.format = Format.R32F; + break; + case Format.RG16F: + attr.format = Format.RG32F; + break; + case Format.RGB16F: + attr.format = Format.RGB32F; + break; + case Format.RGBA16F: + attr.format = Format.RGBA32F; + break; + default: + dequantize = false; + break; + } + strides.push(FormatInfos[attr.format].size); + dequantizes.push(dequantize); + readers.push(reader!); + } + const netStride = strides.reduce((acc, cur) => acc + cur, 0); + const newBuffer = new Uint8Array(netStride * view.count); + for (let j = 0; j < attributes.length; ++j) { + const attribute = attributes[j]; + const reader = readers[j]; + const outputView = new DataView(newBuffer.buffer, getOffset(attributes, j)); + const writer = getWriter(outputView, attribute.format)!; + const dequantize = dequantizes[j]; + const formatInfo = FormatInfos[attribute.format]; + if (dequantize) { + dequantizeHalf( + reader, + writer, + view.count, + formatInfo.count, + view.stride, + netStride, + ); + } else { + transformVertex( + reader, + writer, + view.count, + formatInfo.count, + formatInfo.size / formatInfo.count, + view.stride, + netStride, + ); + } + } + + bufferBlob.setNextAlignment(netStride); + const newView: Mesh.IBufferView = { + offset: bufferBlob.getLength(), + length: newBuffer.byteLength, + count: view.count, + stride: netStride, + }; + bundle.view = newView; + bufferBlob.addBuffer(newBuffer); + } + + // dump index buffer + for (const primitive of struct.primitives) { + if (primitive.indexView === undefined) { + continue; + } + const view = primitive.indexView; + const buffer = new Uint8Array(mesh.data.buffer, view.offset, view.length); + bufferBlob.setNextAlignment(view.stride); + const newView: Mesh.IBufferView = { + offset: bufferBlob.getLength(), + length: buffer.byteLength, + count: view.count, + stride: view.stride, + }; + primitive.indexView = newView; + bufferBlob.addBuffer(buffer); + } + + const data = new Uint8Array(bufferBlob.getCombined()); + + struct.quantized = false; + + return { + struct, + data, + }; +} + // function get diff --git a/cocos/3d/framework/mesh-renderer.ts b/cocos/3d/framework/mesh-renderer.ts index 9869186e7d1..8c4543f3182 100644 --- a/cocos/3d/framework/mesh-renderer.ts +++ b/cocos/3d/framework/mesh-renderer.ts @@ -899,6 +899,7 @@ export class MeshRenderer extends ModelRenderer { if (this._mesh) { const meshStruct = this._mesh.struct; this._model.createBoundingShape(meshStruct.minPosition, meshStruct.maxPosition); + this._model.updateWorldBound(); } // Initialize lighting map before model initializing // because the lighting map will influence the model's shader diff --git a/cocos/3d/misc/mesh-codec.ts b/cocos/3d/misc/mesh-codec.ts index ba9f5088c9d..f875a7a0571 100644 --- a/cocos/3d/misc/mesh-codec.ts +++ b/cocos/3d/misc/mesh-codec.ts @@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { WASM_SUPPORT_MODE } from 'internal:constants'; +import { CULL_MESHOPT, WASM_SUPPORT_MODE } from 'internal:constants'; import { ensureWasmModuleReady, instantiateWasm } from 'pal/wasm'; import { sys, logID } from '../../core'; @@ -90,4 +90,6 @@ export function InitDecoder (): Promise { })); } -game.onPostInfrastructureInitDelegate.add(InitDecoder); +if (!CULL_MESHOPT) { + game.onPostInfrastructureInitDelegate.add(InitDecoder); +} diff --git a/cocos/gfx/webgpu/webgpu-define.ts b/cocos/gfx/webgpu/webgpu-define.ts index 73283fd0868..155206bef17 100644 --- a/cocos/gfx/webgpu/webgpu-define.ts +++ b/cocos/gfx/webgpu/webgpu-define.ts @@ -27,7 +27,7 @@ */ import { WEBGPU } from 'internal:constants'; -import { gfx, webgpuAdapter, glslalgWasmModule, promiseForWebGPUInstantiation } from '../../webgpu/instantiated'; +import { gfx, webgpuAdapter, glslangWasmModule, promiseForWebGPUInstantiation, spirvOptModule, twgslModule } from '../../webgpu/instantiated'; import { Texture, CommandBuffer, DescriptorSet, Device, InputAssembler, Buffer, Shader } from './override'; @@ -197,339 +197,157 @@ WEBGPU && promiseForWebGPUInstantiation.then(() => { oldDeviceCopyBuffersToTexture.call(this, buffers, texture, regions); }; - function seperateCombinedSamplerTexture (shaderSource: string) { - // sampler and texture - const samplerTexturArr = shaderSource.match(/(.*?)\(set = \d+, binding = \d+\) uniform(.*?)sampler\w* \w+;/g); - const count = samplerTexturArr?.length ? samplerTexturArr?.length : 0; - let code = shaderSource; + const SEPARATE_SAMPLER_BINDING_OFFSET = 16; + function seperateCombinedSamplerTexture(shaderSource: string) { + // gather + let samplerReg = /.*?(\(set = \d+, binding = )(\d+)\) uniform[^;]+sampler(\w*) (\w+);/g; + let iter = samplerReg.exec(shaderSource); + // samplerName, samplerType + const referredMap = new Map(); + while (iter) { + const samplerName = iter[4]; + const samplerType = iter[3]; + referredMap.set(samplerName, samplerType); + iter = samplerReg.exec(shaderSource); + } - const referredFuncMap = new Map(); - const samplerSet = new Set(); - samplerTexturArr?.every((str) => { - // `(?<=)` not compatible with str.match on safari. - // let textureName = str.match(/(?<=uniform(.*?)sampler\w* )(\w+)(?=;)/g)!.toString(); - const textureNameRegExpStr = '(?<=uniform(.*?)sampler\\w* )(\\w+)(?=;)'; - let textureName = (new RegExp(textureNameRegExpStr, 'g')).exec(str)![0]; - - let samplerStr = str.replace(textureName, `${textureName}Sampler`); - - // let samplerFunc = samplerStr.match(/(?<=uniform(.*?))sampler(\w*)/g)!.toString(); - const samplerRegExpStr = '(?<=uniform(.*?))sampler(\\w*)'; - let samplerFunc = (new RegExp(samplerRegExpStr, 'g')).exec(str)![0]; - samplerFunc = samplerFunc.replace('sampler', ''); - if (samplerFunc === '') { - textureName = textureName.replace('Sampler', ''); - } else { - const samplerReplaceReg = new RegExp('(?<=uniform(.*?))(sampler\\w*)', 'g'); - samplerStr = samplerStr.replace(samplerReplaceReg, 'sampler'); - - // layout (set = a, binding = b) uniform sampler2D cctex; - // to: - // layout (set = a, binding = b) uniform sampler cctexSampler; - // layout (set = a, binding = b + maxTextureNum) uniform texture2D cctex; - const samplerReg = new RegExp('(?<=binding = )(\\d+)(?=\\))', 'g'); - const samplerBindingStr = samplerReg.exec(str)![0]; - const samplerBinding = Number(samplerBindingStr) + 16; - samplerStr = samplerStr.replace(samplerReg, samplerBinding.toString()); - - const textureReg = new RegExp('(?<=uniform(.*?))(sampler)(?=\\w*)', 'g'); - const textureStr = str.replace(textureReg, 'texture'); - code = code.replace(str, `${textureStr}\n${samplerStr}`); - } + // replaceAll --> es 2021 required + let code = shaderSource; + // referredMap.forEach((value, key)=> { + // const samplerName = key; + // const samplerType = value; + // const exp = new RegExp(`\\b${samplerName}\\b([^;])`); + // let it = exp.exec(code); + // while (it) { + // code = code.replace(exp, `sampler${samplerType}(_${samplerName}, _${samplerName}_sampler)${it[1]}`); + // it = exp.exec(code); + // } + // }); + let sampReg = /.*?(\(set = \d+, binding = )(\d+)\) uniform[^;]+sampler(\w*) (\w+);/g; + let it = sampReg.exec(code); + while (it) { + code = code.replace(sampReg, `layout$1 $2) uniform texture$3 $4;\nlayout$1 $2 + ${SEPARATE_SAMPLER_BINDING_OFFSET}) uniform sampler $4_sampler;\n`); + it = sampReg.exec(code); + } - if (!samplerSet.has(`${textureName}Sampler`)) { - samplerSet.add(`${textureName}Sampler`); - // gathering referred func - let referredFuncStr = `([\\w]+)[\\s]*\\([0-9a-zA-Z_\\s,]*?sampler${samplerFunc}[^\\)]+\\)[\\s]*{`; - if (samplerFunc === '') { - referredFuncStr = `([\\w]+)[\\s]*\\([0-9a-zA-Z_\\s,]*?sampler([\\S]+)[^\\)]+\\)[\\s]*{`; + const builtinSample = ['texture', 'textureSize', 'texelFetch', 'textureLod']; + const replaceBultin = function (samplerName: string, samplerType: string, target: string) { + builtinSample.forEach((sampleFunc) => { + const builtinSampleReg = new RegExp(`${sampleFunc}\\s*\\(\\s*${samplerName}\\s*,`); + let builtinFuncIter = builtinSampleReg.exec(target); + while (builtinFuncIter) { + target = target.replace(builtinFuncIter[0], `${sampleFunc}(sampler${samplerType}(${samplerName}, ${samplerName}_sampler),`); + builtinFuncIter = builtinSampleReg.exec(target); } - const referredFuncRe = new RegExp(referredFuncStr, 'g'); - let reArr = referredFuncRe.exec(code); - while (reArr) { - // first to see if it's wrapped by #if 0 \n ... \n #ndif - let smpFunc = samplerFunc; - if (smpFunc === '') { - smpFunc = reArr[2]; - } - const searchTarget = code.slice(0, reArr.index); - const defValQueue: { str: string, b: boolean, condition: RegExpExecArray }[] = []; - let searchIndex = 1; - - while (searchIndex > 0) { - let ifIndex = searchTarget.indexOf('#if', searchIndex); - let elseIndex = searchTarget.indexOf('#else', searchIndex); - let endIndex = searchTarget.indexOf('#endif', searchIndex); - - if (ifIndex === -1 && elseIndex === -1 && endIndex === -1) { - break; - } - - if (ifIndex === -1) ifIndex = Number.MAX_SAFE_INTEGER; - if (elseIndex === -1) elseIndex = Number.MAX_SAFE_INTEGER; - if (endIndex === -1) endIndex = Number.MAX_SAFE_INTEGER; - - // next start with '#' is #if(def) - if (ifIndex < elseIndex && ifIndex < endIndex) { - const ifdef = (new RegExp(`#if(def)?[\\s]+(!)?([\\S]+)([\\s+]?(==)?(!=)[\\s+]?([\\S]+))?`, 'gm')).exec(searchTarget.slice(ifIndex))!; - defValQueue[defValQueue.length] = { str: ifdef[3], b: true, condition: ifdef }; - searchIndex = ifIndex + 1; - continue; - } - - if (elseIndex < endIndex && elseIndex < ifIndex) { - defValQueue[defValQueue.length - 1].b = false; - searchIndex = elseIndex + 1; - continue; - } + }); + return target; + } - if (endIndex < elseIndex && endIndex < ifIndex) { - defValQueue.pop(); - searchIndex = endIndex + 1; - continue; - } + let funcReg = /\s([\S]+)\s*\(([\w\s,]+)\)[\s|\\|n]*{/g; + let funcIter = funcReg.exec(code); + const funcSet = new Set(); + const paramTypeMap = new Map(); + while (funcIter) { + paramTypeMap.clear(); + + const params = funcIter[2]; + let paramsRes = params.slice(); + if (params.includes('sampler')) { + const paramIndexSet = new Set(); + const paramArr = params.split(','); + + for (let i = 0; i < paramArr.length; ++i) { + const paramDecl = paramArr[i].split(' '); + // assert(paramDecl.length >= 2) + const typeDecl = paramDecl[paramDecl.length - 2]; + if (typeDecl.includes('sampler') && typeDecl !== 'sampler') { + const samplerType = typeDecl.replace('sampler', ''); + const paramName = paramDecl[paramDecl.length - 1]; + paramsRes = paramsRes.replace(paramArr[i], ` texture${samplerType} ${paramName}, sampler ${paramName}_sampler`); + paramIndexSet.add(i); + paramTypeMap.set(paramName, samplerType); } - - let defCheck = true; - for (let i = 0; i < defValQueue.length; i++) { - const ifdef = defValQueue[i].condition; - let evalRes = false; - if (ifdef[1]) { - evalRes = !!(new RegExp(`#define[\\s]+${ifdef[3]}`, 'gm')).exec(searchTarget); - } else { - const defVal = (new RegExp(`#define[\\s]+${ifdef[3]}[\\s]+([\\S]+).*`, 'gm')).exec(searchTarget)![1]; - if (ifdef[4]) { - const conditionVal = ifdef[7]; - evalRes = ifdef[5] ? defVal === conditionVal : defVal !== conditionVal; - } else if (ifdef[3]) { - evalRes = (defVal !== '0' && !ifdef[2]); + } + // let singleParamReg = new RegExp(`(\\W?)(\\w+)\\s+\\b([^,)]+)\\b`); + + code = code.replace(params, paramsRes); + + const funcName = funcIter[1]; + // function may overload + if (!funcSet.has(funcName)) { + // const samplerTypePrefix = '1D|2D|3D|Cube|Buffer'; + const funcSamplerReg = new RegExp(`${funcName}\\s*?\\((\\s*[^;\\{]+)`, 'g'); + const matches = code.matchAll(funcSamplerReg); + for (let matched of matches) { + if (!matched[1].match(/\b\w+\b\s*\b\w+\b/g)) { + const stripStr = matched[1][matched[1].length - 1] === ')' ? matched[1].slice(0, -1) : matched[1]; + let params = stripStr.split(','); + let queued = 0; // '(' + let paramIndex = 0; + for (let i = 0; i < params.length; ++i) { + if (params[i].includes('(')) { + ++queued; + } + if (params[i].includes(')')) { + --queued; + } + + if (!queued || i === params.length - 1) { + if (paramIndexSet.has(paramIndex)) { + params[i] += `, ${params[i]}_sampler`; + } + ++paramIndex; + } } + const newParams = params.join(','); + const newInvokeStr = matched[0].replace(stripStr, newParams); + code = code.replace(matched[0], newInvokeStr); } - - if (defValQueue[i].b !== evalRes) { - defCheck = false; - break; - } + // else: function declare } - - const key = `${reArr[1]}_${smpFunc}_${textureName}Sampler`; - if (!referredFuncMap.has(key) && defCheck) { - referredFuncMap.set(key, [reArr[1], smpFunc, `${textureName}Sampler`]); - } - reArr = referredFuncRe.exec(code); } - } - // cctex in main() called directly - // .*?texture\( - const regStr = `texture\\(\\b(${textureName})\\b`; - const re = new RegExp(regStr); - let reArr = re.exec(code); - while (reArr) { - code = code.replace(re, `texture(sampler${samplerFunc}(${textureName},${textureName}Sampler)`); - reArr = re.exec(code); - } - return true; - }); - - const functionTemplates = new Map(); - const functionDeps = new Map(); - let forwardDecls = ''; - // function - referredFuncMap.forEach((pair) => { - //pre: if existed, replace - //getVec3DisplacementFromTexture\(cc_TangentDisplacements[^\\n]+ - const textureStr = pair[2].slice(0, -7); - const codePieceStr = `${pair[0]}\\((.*?)${textureStr}[^\\n]+`; - const codePieceRe = new RegExp(codePieceStr); - let res = codePieceRe.exec(code); - while (res) { - let replaceStr = res[0].replace(`${pair[0]}(`, `${pair[0]}_${pair[1]}_${pair[2]}_specialized(`); - replaceStr = replaceStr.replace(`${textureStr},`, ''); - code = code.replace(codePieceRe, replaceStr); - res = codePieceRe.exec(code); - } - - // 1. fn definition - const fnDeclReStr = `[\\n|\\W][\\w]+[\\W]+${pair[0]}[\\s]*\\((.*?)sampler${pair[1]}[^{]+`; - const fnDeclRe = new RegExp(fnDeclReStr); - const fnDecl = fnDeclRe.exec(code); - - let redefFunc = ''; - if (!functionTemplates.has(`${pair[0]}_${pair[1]}`)) { - const funcBodyStart = code.slice(fnDecl!.index); - - const funcRedefine = (funcStr: string) => { - const samplerType = `sampler${pair[1]}`; - const textureRe = (new RegExp(`.*?${samplerType}[\\s]+([\\S]+)[,\\)]`)).exec(funcStr)!; - const textureName = textureRe[1]; - const paramReStr = `${samplerType}[\\s]+${textureName}`; - let funcDef = funcStr.replace(new RegExp(paramReStr), `texture${pair[1]} ${textureName}`); - funcDef = funcDef.replace(pair[0], `${pair[0]}SAMPLER_SPEC`); - // 2. texture(...) inside, builtin funcs - const textureOpArr = ['texture', 'textureSize', 'texelFetch', 'textureLod']; - for (let i = 0; i < textureOpArr.length; i++) { - const texFuncReStr = `(${textureOpArr[i]})\\(${textureName},`; - const texFuncRe = new RegExp(texFuncReStr, 'g'); - funcDef = funcDef.replace(texFuncRe, `$1(${samplerType}(${textureName}TEXTURE_HOLDER, ${textureName}SAMPLER_HOLDER),`); - } - return funcDef; - }; - - const firstIfStatement = funcBodyStart.indexOf('#if'); - const firstElseStatement = funcBodyStart.indexOf('#e'); //#endif, #else, #elif maybe? - if ((firstElseStatement !== -1 && firstIfStatement > firstElseStatement) || (firstElseStatement === -1 && firstElseStatement !== -1)) { // ooops, now func body starts in a #if statement. - let startIndex = 0; - let count = 1; // already in #if - while (count > 0 && startIndex < funcBodyStart.length) { - const nextSymbolIdx = funcBodyStart.indexOf('#', startIndex); - const startSliceIdx = startIndex === 0 ? startIndex : startIndex - 1; - if (funcBodyStart[nextSymbolIdx + 1] === 'i') { // #if - count++; - redefFunc += funcBodyStart.slice(startSliceIdx, nextSymbolIdx); - } else if (funcBodyStart[nextSymbolIdx + 1] === 'e' && funcBodyStart[nextSymbolIdx + 2] === 'l') { //#elif, #else - if (count === 1) { - const tempFuncStr = funcBodyStart.slice(startSliceIdx, nextSymbolIdx - 1); - const funcDefStr = funcRedefine(tempFuncStr); - redefFunc += `\n${funcDefStr}`; - } else { - redefFunc += `\n${funcBodyStart.slice(startSliceIdx, nextSymbolIdx)}`; - } - } else if (funcBodyStart[nextSymbolIdx + 1] === 'e' && funcBodyStart[nextSymbolIdx + 2] === 'n') { //#endif - count--; - if (count === 0) { - const tempFuncStr = funcBodyStart.slice(startSliceIdx, nextSymbolIdx - 1); - const funcDefStr = funcRedefine(tempFuncStr); - redefFunc += `\n${funcDefStr}`; - } else { - redefFunc += `\n${funcBodyStart.slice(startSliceIdx, nextSymbolIdx)}`; - } - } else { // #define, dont care - redefFunc += funcBodyStart.slice(startSliceIdx, nextSymbolIdx); - } - startIndex = nextSymbolIdx + 1; + let count = 1; + let startIndex = code.indexOf(funcIter[1], funcIter.index); + startIndex = code.indexOf('{', startIndex) + 1; + let endIndex = 0; + while (count) { + if (code.at(startIndex) === '{') { + ++count; + } else if (code.at(startIndex) === '}') { + --count; } - //`(?:.(?!layout))+${pair[2]};` - const searchTarget = code.slice(0, fnDecl!.index); - const res = (new RegExp(`#if.+[\\s]*$`)).exec(searchTarget); - redefFunc = `${res![0]}${redefFunc}\n#endif`; - } else { - let count = 0; - let matchBegin = false; - let startIndex = 0; - let endIndex = 0; - for (let i = 0; i < funcBodyStart.length; ++i) { - if (funcBodyStart[i] === '{') { - ++count; - if (!matchBegin) { - matchBegin = true; - startIndex = i; - } - } else if (funcBodyStart[i] === '}') { - --count; - } - - if (matchBegin && count === 0) { - endIndex = i; - break; - } + if (count === 0) { + endIndex = startIndex; + break; } - const rawFunc = `${fnDecl![0]}${funcBodyStart.slice(startIndex, endIndex + 1)}`; - redefFunc = funcRedefine(rawFunc); - } - - functionTemplates.set(`${pair[0]}_${pair[1]}`, redefFunc); - } else { - redefFunc = functionTemplates.get(`${pair[0]}_${pair[1]}`)!; - } - - const depsFuncs: string[] = []; - const iterator = referredFuncMap.values(); - let val = iterator.next().value; - while (val) { - const funcDepReStr = `\\b(${val[0] as string})\\b`; - if (redefFunc.search(funcDepReStr) !== -1) { - depsFuncs[depsFuncs.length] = val[0]; + const nextLeft = code.indexOf('{', startIndex + 1); + const nextRight = code.indexOf('}', startIndex + 1); + startIndex = nextLeft === -1 ? nextRight : Math.min(nextLeft, nextRight); } - val = iterator.next().value; - } - // for (let i = 0; i < referredFuncMap.values.length; ++i) { - // const funcDepReStr = `\\b(${referredFuncMap.values[i].fnName as string})\\b`; - // if (redefFunc.search(funcDepReStr) !== -1) { - // depsFuncs[depsFuncs.length] = referredFuncMap.values[i].fnName; - // } - // } - functionDeps.set(`${pair[0]}_${pair[1]}`, depsFuncs); - - const specializedFuncs = new Map(); - const specialize = (funcs: string[]) => { - funcs.every((str) => { - if (!specializedFuncs.has(`${pair[0]}_${pair[2]}_specialized`)) { - const samplerReStr = `(\\w+)SAMPLER_HOLDER`; - const textureName = pair[2].slice(0, pair[2].length - 7); // xxxxSampler - const textureStr = `(\\w+)TEXTURE_HOLDER`; - let funcTemplate = functionTemplates.get(str); - funcTemplate = funcTemplate!.replace(new RegExp(samplerReStr, 'g'), pair[2]); - funcTemplate = funcTemplate.replace(new RegExp(textureStr, 'g'), textureName); - funcTemplate = funcTemplate.replace(new RegExp('SAMPLER_SPEC', 'g'), `_${pair[1]}_${pair[2]}_specialized`); - funcTemplate = funcTemplate.replace(new RegExp(`texture${pair[1]}\\s+\\w+,`, 'g'), ''); - // funcTemplate = funcTemplate.replace('SAMPLER_SPEC', `_${pair[2]}_specialized`); - - for (let i = 0; i < depsFuncs.length; ++i) { - const depFuncStr = `${depsFuncs[i]}([\\W]?)*\\([^,)]+(,)?`; - funcTemplate = funcTemplate.replace(new RegExp(depFuncStr, 'g'), `${depsFuncs[i]}_${pair[1]}_${pair[2]}_specialized(`); - } - - let declStr = fnDecl![0].replace(pair[0], `${str}_${pair[2]}_specialized`); - declStr = declStr.replace(new RegExp(`sampler${pair[1]}[^,)]+(,)?`, 'g'), ``); - declStr += `;`; - specializedFuncs.set(declStr, funcTemplate); - } else { - return specialize(functionDeps.get(str)!); - } - return true; + const funcBody = code.slice(funcIter.index, endIndex); + let newFunc = funcBody; + paramTypeMap.forEach((type, name) => { + newFunc = replaceBultin(name, type, newFunc); }); - return true; - }; - specialize([`${pair[0]}_${pair[1]}`]); - //(?:.(?!layout))+cc_PositionDisplacementsSampler; - const samplerDefReStr = `(?:.(?!layout))+${pair[2]};`; - const samplerDef = (new RegExp(samplerDefReStr)).exec(code); - - let funcImpls = ''; - for (const [key, value] of specializedFuncs) { - forwardDecls += `\n${key}\n`; - funcImpls += `\n${value}\n`; + + code = code.replace(funcBody, newFunc); + funcSet.add(funcIter[1]); } + funcIter = funcReg.exec(code); + } - code = code.replace(samplerDef![0], `${samplerDef![0]}\n${funcImpls}`); + referredMap.forEach((type, name) => { + code = replaceBultin(name, type, code); }); - // some function appears before it's defined so forward declaration is needed - forwardDecls += '\nvec3 SRGBToLinear (vec3 gamma);\nfloat getDisplacementWeight(int index);\n'; - - const highpIdx = code.indexOf('precision highp'); - const mediumpIdx = code.indexOf('precision mediump'); - const lowpIdx = code.indexOf('precision lowp'); - /////////////////////////////////////////////////////////// // isNan, isInf has been removed in dawn:tint let functionDefs = ''; const precisionKeyWord = 'highp'; - - // const getPrecision = (idx: number) => { - // if (highpIdx !== -1 && highpIdx < idx) { - // precisionKeyWord = 'highp'; - // } else if (mediumpIdx !== -1 && mediumpIdx < idx && highpIdx < mediumpIdx) { - // precisionKeyWord = 'mediump'; - // } else if (lowpIdx !== -1 && lowpIdx < idx && mediumpIdx < lowpIdx && highpIdx < lowpIdx) { - // precisionKeyWord = 'lowp'; - // } - // }; - const isNanIndex = code.indexOf('isnan'); if (isNanIndex !== -1) { // getPrecision(isNanIndex); @@ -557,31 +375,84 @@ WEBGPU && promiseForWebGPUInstantiation.then(() => { let firstPrecisionIdx = code.indexOf('precision'); firstPrecisionIdx = code.indexOf(';', firstPrecisionIdx); firstPrecisionIdx += 1; - code = `${code.slice(0, firstPrecisionIdx)}\n${forwardDecls}\n${functionDefs}\n${code.slice(firstPrecisionIdx)}`; + code = `${code.slice(0, firstPrecisionIdx)}\n${functionDefs}\n${code.slice(firstPrecisionIdx)}`; return code; } + function reflect(wgsl: string[]) { + const bindingList: number[][] = []; + for (let wgslStr of wgsl) { + // @group(1) @binding(0) var x_78 : Constants; + // @group(1) @binding(1) var albedoMap : texture_2d; + const reg = new RegExp(/@group\((\d)\)\s+@binding\((\d+)\)/g); + let iter = reg.exec(wgslStr); + while (iter) { + const set = +iter[1]; + const binding = +iter[2]; + while (bindingList.length <= set) { + bindingList.push([]); + } + bindingList[set][bindingList[set].length] = binding; + iter = reg.exec(wgslStr); + } + } + return bindingList; + } + + function overwriteBlock(info: ShaderInfo, code: string): string { + const regexp = new RegExp(/layout\(([^\)]+)\)\s+uniform\s+\b(\w+)\b/g); + let src = code; + let iter = regexp.exec(src); + if (iter) { + const blockName = iter[2]; + const block = info.blocks.find((ele) => { return ele.name === blockName; }); + const binding = block?.binding; + const overwriteStr = iter[0].replace(iter[1], `${iter[1]}, set = 0, binding = ${binding}`); + src = src.replace(iter[0], overwriteStr); + iter = regexp.exec(src); + } + return src; + } + const createShader = Device.prototype.createShader; Device.prototype.createShader = function (shaderInfo: ShaderInfo) { - const spvDatas: any = []; + const wgslStages: string[] = []; for (let i = 0; i < shaderInfo.stages.length; ++i) { - shaderInfo.stages[i].source = seperateCombinedSamplerTexture(shaderInfo.stages[i].source); + let glslSource = seperateCombinedSamplerTexture(shaderInfo.stages[i].source); const stageStr = shaderInfo.stages[i].stage === ShaderStageFlagBit.VERTEX ? 'vertex' : shaderInfo.stages[i].stage === ShaderStageFlagBit.FRAGMENT ? 'fragment' : 'compute'; - const sourceCode = `#version 450\n${shaderInfo.stages[i].source}`; - const spv = glslalgWasmModule.glslang.compileGLSL(sourceCode, stageStr, true, '1.3'); - spvDatas.push(spv); + // if (stageStr === 'compute') { + // glslSource = overwriteBlock(shaderInfo, glslSource); + // } + const sourceCode = `#version 450\n#define CC_USE_WGPU 1\n${glslSource}`; + const spv = glslangWasmModule.glslang.compileGLSL(sourceCode, stageStr, false, '1.3'); + + const twgsl = twgslModule.twgsl; + const wgsl = twgsl.convertSpirV2WGSL(spv); + if (wgsl === '') { + console.error("empty wgsl"); + } + shaderInfo.stages[i].source = wgsl; + wgslStages.push(wgsl); } - const shader = this.createShaderNative(shaderInfo, spvDatas); + const shader = this.createShaderNative(shaderInfo); + // optioanl : reflect bindings in shader + { + const bindingList = reflect(wgslStages); + for (let bindings of bindingList) { + const u8Array = new Uint8Array(bindings); + shader.reflectBinding(u8Array); + } + } return shader; }; // if property being frequently get in TS, try cache it // attention: invalid if got this object from a native object, // eg. inputAssembler.indexBuffer.objectID - function cacheReadOnlyWGPUProperties (type: T, props: string[]) { + function cacheReadOnlyWGPUProperties(type: T, props: string[]) { const descriptor = { writable: true }; props.map((prop) => { return Object.defineProperty(type['prototype'], `_${prop}`, descriptor); @@ -595,7 +466,7 @@ WEBGPU && promiseForWebGPUInstantiation.then(() => { for (let prop of props) { res[`_${prop}`] = res[`${prop}`]; Object.defineProperty(res, `${prop}`, { - get () { + get() { return this[`_${prop}`]; } }); diff --git a/cocos/input/input.ts b/cocos/input/input.ts index 4cf549caa09..cbbe0d923e8 100644 --- a/cocos/input/input.ts +++ b/cocos/input/input.ts @@ -25,9 +25,9 @@ */ import { EDITOR_NOT_IN_PREVIEW, NATIVE } from 'internal:constants'; -import { TouchInputSource, MouseInputSource, KeyboardInputSource, AccelerometerInputSource, GamepadInputDevice, HandleInputDevice, HMDInputDevice, HandheldInputDevice } from 'pal/input'; +import { AccelerometerInputSource, GamepadInputDevice, HMDInputDevice, HandheldInputDevice, HandleInputDevice, KeyboardInputSource, MouseInputSource, TouchInputSource } from 'pal/input'; import { touchManager } from '../../pal/input/touch-manager'; -import { sys, EventTarget } from '../core'; +import { EventTarget, error, sys } from '../core'; import { Event, EventAcceleration, EventGamepad, EventHandle, EventHandheld, EventHMD, EventKeyboard, EventMouse, EventTouch, Touch } from './types'; import { InputEventType } from './types/event-enum'; @@ -62,7 +62,7 @@ class InputEventDispatcher implements IEventDispatcher { } } -const pointerEventTypeMap: Record = { +const pointerEventTypeMap: Record = { [InputEventType.MOUSE_DOWN]: InputEventType.TOUCH_START, [InputEventType.MOUSE_MOVE]: InputEventType.TOUCH_MOVE, [InputEventType.MOUSE_UP]: InputEventType.TOUCH_END, @@ -160,28 +160,40 @@ export class Input { /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchMouseDownEvent (nativeMouseEvent: any): void { this._mouseInput.dispatchMouseDownEvent?.(nativeMouseEvent); } + private _dispatchMouseDownEvent (nativeMouseEvent: any): void { + this._mouseInput.dispatchMouseDownEvent?.(nativeMouseEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchMouseMoveEvent (nativeMouseEvent: any): void { this._mouseInput.dispatchMouseMoveEvent?.(nativeMouseEvent); } + private _dispatchMouseMoveEvent (nativeMouseEvent: any): void { + this._mouseInput.dispatchMouseMoveEvent?.(nativeMouseEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchMouseUpEvent (nativeMouseEvent: any): void { this._mouseInput.dispatchMouseUpEvent?.(nativeMouseEvent); } + private _dispatchMouseUpEvent (nativeMouseEvent: any): void { + this._mouseInput.dispatchMouseUpEvent?.(nativeMouseEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchMouseScrollEvent (nativeMouseEvent: any): void { this._mouseInput.dispatchScrollEvent?.(nativeMouseEvent); } + private _dispatchMouseScrollEvent (nativeMouseEvent: any): void { + this._mouseInput.dispatchScrollEvent?.(nativeMouseEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchKeyboardDownEvent (nativeKeyboardEvent: any): void { this._keyboardInput.dispatchKeyboardDownEvent?.(nativeKeyboardEvent); } + private _dispatchKeyboardDownEvent (nativeKeyboardEvent: any): void { + this._keyboardInput.dispatchKeyboardDownEvent?.(nativeKeyboardEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchKeyboardUpEvent (nativeKeyboardEvent: any): void { this._keyboardInput.dispatchKeyboardUpEvent?.(nativeKeyboardEvent); } + private _dispatchKeyboardUpEvent (nativeKeyboardEvent: any): void { + this._keyboardInput.dispatchKeyboardUpEvent?.(nativeKeyboardEvent); + } /** * @en @@ -229,6 +241,37 @@ export class Input { } this._eventTarget.off(eventType, callback, target); } + + /** + * @en + * Get touch object by touch ID. + * @zh + * 通过 touch ID 获取 touch对象。 + */ + public getTouch (touchID: number): Readonly | undefined { + return touchManager.getTouch(touchID); + } + + /** + * @en + * Get all the current touches objects as array. + * @zh + * 获取当前 所有touch对象 的数组。 + */ + public getAllTouches (): Touch[] { + return touchManager.getAllTouches(); + } + + /** + * @en + * Get the number of touches. + * @zh + * 获取当前 touch 对象的数量。 + */ + public getTouchCount (): number { + return touchManager.getTouchCount(); + } + /** * @en * Sets whether to enable the accelerometer event listener or not. @@ -264,7 +307,7 @@ export class Input { private _simulateEventTouch (eventMouse: EventMouse): void { const eventType = pointerEventTypeMap[eventMouse.type]; const touchID = 0; - const touch = touchManager.getTouch(touchID, eventMouse.getLocationX(), eventMouse.getLocationY()); + const touch = touchManager.getOrCreateTouch(touchID, eventMouse.getLocationX(), eventMouse.getLocationY()); if (!touch) { return; } @@ -295,8 +338,8 @@ export class Input { break; } } catch (e) { - console.error(`Error occurs in an event listener: ${event.type}`); - console.error(e); + error(`Error occurs in an event listener: ${event.type}`); + error(e); } } } @@ -304,10 +347,18 @@ export class Input { private _registerEvent (): void { if (sys.hasFeature(sys.Feature.INPUT_TOUCH)) { const eventTouchList = this._eventTouchList; - this._touchInput.on(InputEventType.TOUCH_START, (event): void => { this._dispatchOrPushEventTouch(event, eventTouchList); }); - this._touchInput.on(InputEventType.TOUCH_MOVE, (event): void => { this._dispatchOrPushEventTouch(event, eventTouchList); }); - this._touchInput.on(InputEventType.TOUCH_END, (event): void => { this._dispatchOrPushEventTouch(event, eventTouchList); }); - this._touchInput.on(InputEventType.TOUCH_CANCEL, (event): void => { this._dispatchOrPushEventTouch(event, eventTouchList); }); + this._touchInput.on(InputEventType.TOUCH_START, (event): void => { + this._dispatchOrPushEventTouch(event, eventTouchList); + }); + this._touchInput.on(InputEventType.TOUCH_MOVE, (event): void => { + this._dispatchOrPushEventTouch(event, eventTouchList); + }); + this._touchInput.on(InputEventType.TOUCH_END, (event): void => { + this._dispatchOrPushEventTouch(event, eventTouchList); + }); + this._touchInput.on(InputEventType.TOUCH_CANCEL, (event): void => { + this._dispatchOrPushEventTouch(event, eventTouchList); + }); } if (sys.hasFeature(sys.Feature.EVENT_MOUSE)) { @@ -328,42 +379,66 @@ export class Input { this._simulateEventTouch(event); this._dispatchOrPushEvent(event, eventMouseList); }); - this._mouseInput.on(InputEventType.MOUSE_WHEEL, (event): void => { this._dispatchOrPushEvent(event, eventMouseList); }); + this._mouseInput.on(InputEventType.MOUSE_WHEEL, (event): void => { + this._dispatchOrPushEvent(event, eventMouseList); + }); } if (sys.hasFeature(sys.Feature.EVENT_KEYBOARD)) { const eventKeyboardList = this._eventKeyboardList; - this._keyboardInput.on(InputEventType.KEY_DOWN, (event): void => { this._dispatchOrPushEvent(event, eventKeyboardList); }); - this._keyboardInput.on(InputEventType.KEY_PRESSING, (event): void => { this._dispatchOrPushEvent(event, eventKeyboardList); }); - this._keyboardInput.on(InputEventType.KEY_UP, (event): void => { this._dispatchOrPushEvent(event, eventKeyboardList); }); + this._keyboardInput.on(InputEventType.KEY_DOWN, (event): void => { + this._dispatchOrPushEvent(event, eventKeyboardList); + }); + this._keyboardInput.on(InputEventType.KEY_PRESSING, (event): void => { + this._dispatchOrPushEvent(event, eventKeyboardList); + }); + this._keyboardInput.on(InputEventType.KEY_UP, (event): void => { + this._dispatchOrPushEvent(event, eventKeyboardList); + }); } if (sys.hasFeature(sys.Feature.EVENT_ACCELEROMETER)) { const eventAccelerationList = this._eventAccelerationList; - this._accelerometerInput.on(InputEventType.DEVICEMOTION, (event): void => { this._dispatchOrPushEvent(event, eventAccelerationList); }); + this._accelerometerInput.on(InputEventType.DEVICEMOTION, (event): void => { + this._dispatchOrPushEvent(event, eventAccelerationList); + }); } if (sys.hasFeature(sys.Feature.EVENT_GAMEPAD)) { const eventGamepadList = this._eventGamepadList; - GamepadInputDevice._on(InputEventType.GAMEPAD_CHANGE, (event): void => { this._dispatchOrPushEvent(event, eventGamepadList); }); - GamepadInputDevice._on(InputEventType.GAMEPAD_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventGamepadList); }); - GamepadInputDevice._on(InputEventType.HANDLE_POSE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventGamepadList); }); + GamepadInputDevice._on(InputEventType.GAMEPAD_CHANGE, (event): void => { + this._dispatchOrPushEvent(event, eventGamepadList); + }); + GamepadInputDevice._on(InputEventType.GAMEPAD_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventGamepadList); + }); + GamepadInputDevice._on(InputEventType.HANDLE_POSE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventGamepadList); + }); } if (sys.hasFeature(sys.Feature.EVENT_HANDLE)) { const eventHandleList = this._eventHandleList; - this._handleInput._on(InputEventType.HANDLE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventHandleList); }); - this._handleInput._on(InputEventType.HANDLE_POSE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventHandleList); }); + this._handleInput._on(InputEventType.HANDLE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventHandleList); + }); + this._handleInput._on(InputEventType.HANDLE_POSE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventHandleList); + }); } if (sys.hasFeature(sys.Feature.EVENT_HMD)) { const eventHMDList = this._eventHMDList; - this._hmdInput._on(InputEventType.HMD_POSE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventHMDList); }); + this._hmdInput._on(InputEventType.HMD_POSE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventHMDList); + }); } if (sys.hasFeature(sys.Feature.EVENT_HANDHELD)) { const eventHandheldList = this._eventHandheldList; - this._handheldInput._on(InputEventType.HANDHELD_POSE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventHandheldList); }); + this._handheldInput._on(InputEventType.HANDHELD_POSE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventHandheldList); + }); } } diff --git a/cocos/input/types/event-enum.ts b/cocos/input/types/event-enum.ts index c8c2a8d4e82..96d3844f8d4 100644 --- a/cocos/input/types/event-enum.ts +++ b/cocos/input/types/event-enum.ts @@ -265,7 +265,7 @@ export enum SystemEventType { * @en The event type for node's sibling order changed. * @zh 当节点在兄弟节点中的顺序发生变化时触发的事件。 * - * @deprecated since v3.3, please use Node.EventType.SIBLING_ORDER_CHANGED instead + * @deprecated since v3.3, please use Node.EventType.CHILDREN_ORDER_CHANGED instead */ SIBLING_ORDER_CHANGED = 'sibling-order-changed', } diff --git a/cocos/input/types/touch.ts b/cocos/input/types/touch.ts index 1be58d480e2..2e0cc238702 100644 --- a/cocos/input/types/touch.ts +++ b/cocos/input/types/touch.ts @@ -199,7 +199,7 @@ export class Touch { _vec2.set(this._point); _vec2.subtract(this._prevPoint); - out.set(cclegacy.view.getScaleX(), cclegacy.view.getScaleY()); + out.set(cclegacy.view.getScaleX() as number, cclegacy.view.getScaleY() as number); Vec2.divide(out, _vec2, out); return out; } @@ -261,7 +261,7 @@ export class Touch { * @param x - x position of the touch point * @param y - y position of the touch point */ - public setTouchInfo (id = 0, x?: number, y?: number): void { + public setTouchInfo (id: number = 0, x: number = 0, y: number = 0): void { this._prevPoint = this._point; this._point = new Vec2(x || 0, y || 0); this._id = id; @@ -321,6 +321,21 @@ export class Touch { } this._lastModified = cclegacy.game.frameStartTime; } + + /** + * @zh Touch 对象的原始数据不应该被修改。如果你需要这么做,最好克隆一个新的对象。 + * @en The original Touch object shouldn't be modified. If you need to, it's better to clone a new one. + */ + public clone (): Touch { + const touchID = this.getID(); + this.getStartLocation(_vec2); + const clonedTouch = new Touch(_vec2.x, _vec2.y, touchID); + this.getLocation(_vec2); + clonedTouch.setPoint(_vec2.x, _vec2.y); + this.getPreviousLocation(_vec2); + clonedTouch.setPrevPoint(_vec2); + return clonedTouch; + } } cclegacy.Touch = Touch; diff --git a/cocos/misc/camera-component.ts b/cocos/misc/camera-component.ts index e4201ecfcbd..2c2067e5d5e 100644 --- a/cocos/misc/camera-component.ts +++ b/cocos/misc/camera-component.ts @@ -313,7 +313,7 @@ export class Camera extends Component { */ @type(FOVAxis) @displayOrder(7) - @visible(function (this: Camera): boolean { + @visible(function visible (this: Camera): boolean { return this._projection === ProjectionType.PERSPECTIVE; }) @tooltip('i18n:camera.fov_axis') @@ -354,10 +354,10 @@ export class Camera extends Component { * @zh 正交模式下的相机视角高度。 */ @displayOrder(9) - @visible(function (this: Camera): boolean { + @visible(function visible (this: Camera): boolean { return this._projection === ProjectionType.ORTHO; }) - @rangeMin(1) + @rangeMin(1e-6) @tooltip('i18n:camera.ortho_height') get orthoHeight (): number { return this._orthoHeight; diff --git a/cocos/physics-2d/box2d-wasm/instantiated.ts b/cocos/physics-2d/box2d-wasm/instantiated.ts index 5156374fd49..023cb80afe1 100644 --- a/cocos/physics-2d/box2d-wasm/instantiated.ts +++ b/cocos/physics-2d/box2d-wasm/instantiated.ts @@ -35,7 +35,8 @@ export const B2 = {} as any; export function getImplPtr (wasmObject: any): number { // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return (wasmObject).$$.ptr; + if (!wasmObject) return 0; + return (wasmObject).$$.ptr as number; } /** diff --git a/cocos/physics-2d/box2d-wasm/joints/joint-2d.ts b/cocos/physics-2d/box2d-wasm/joints/joint-2d.ts index 2f0990e0d6b..9d28ebc03e3 100644 --- a/cocos/physics-2d/box2d-wasm/joints/joint-2d.ts +++ b/cocos/physics-2d/box2d-wasm/joints/joint-2d.ts @@ -22,7 +22,7 @@ THE SOFTWARE. */ -import { B2, addImplPtrReference, addImplPtrReferenceWASM, getImplPtr } from '../instantiated'; +import { B2, addImplPtrReference, addImplPtrReferenceWASM, getImplPtr, removeImplPtrReference, removeImplPtrReferenceWASM } from '../instantiated'; import { IJoint2D } from '../../spec/i-physics-joint'; import { Joint2D, PhysicsSystem2D, RigidBody2D } from '../../framework'; import { B2PhysicsWorld } from '../physics-world'; @@ -54,7 +54,7 @@ export class B2Joint implements IJoint2D { } onDisable (): void { - PhysicsSystem2D.instance._callAfterStep(this, this._destroy); + PhysicsSystem2D.instance._callAfterStep(this, this.destroy); } // need init after body and connected body init @@ -62,6 +62,13 @@ export class B2Joint implements IJoint2D { PhysicsSystem2D.instance._callAfterStep(this, this._init); } + apply (): void { + PhysicsSystem2D.instance._callAfterStep(this, this.destroy); + if (this.comp!.enabledInHierarchy) { + PhysicsSystem2D.instance._callAfterStep(this, this._init); + } + } + _init (): void { if (this._inited) return; @@ -106,9 +113,11 @@ export class B2Joint implements IJoint2D { this._inited = true; } - _destroy (): void { + destroy (): void { if (!this._inited) return; + removeImplPtrReference(getImplPtr(this._b2joint)); + removeImplPtrReferenceWASM(getImplPtr(this._b2joint)); (PhysicsSystem2D.instance.physicsWorld as B2PhysicsWorld).impl.DestroyJoint(this._b2joint!); this._b2joint = null; diff --git a/cocos/physics-2d/box2d-wasm/joints/mouse-joint.ts b/cocos/physics-2d/box2d-wasm/joints/mouse-joint.ts index f5d359056da..51f73a9cfd2 100644 --- a/cocos/physics-2d/box2d-wasm/joints/mouse-joint.ts +++ b/cocos/physics-2d/box2d-wasm/joints/mouse-joint.ts @@ -118,7 +118,7 @@ export class B2MouseJoint extends B2Joint implements IMouseJoint { } onTouchEnd (event: Touch): void { - this._destroy(); + this.destroy(); this._isTouched = false; } diff --git a/cocos/physics-2d/box2d-wasm/rigid-body.ts b/cocos/physics-2d/box2d-wasm/rigid-body.ts index feb13037e4c..a92e0533863 100644 --- a/cocos/physics-2d/box2d-wasm/rigid-body.ts +++ b/cocos/physics-2d/box2d-wasm/rigid-body.ts @@ -22,7 +22,7 @@ THE SOFTWARE. */ -import { B2 } from './instantiated'; +import { B2, getTSObjectFromWASMObject, getTSObjectFromWASMObjectPtr } from './instantiated'; import { IRigidBody2D } from '../spec/i-rigid-body'; import { RigidBody2D } from '../framework/components/rigid-body-2d'; import { PhysicsSystem2D } from '../framework/physics-system'; @@ -31,8 +31,10 @@ import { Vec2, toRadian, Vec3, Quat, IVec2Like, toDegree, TWO_PI, HALF_PI } from import { PHYSICS_2D_PTM_RATIO, ERigidBody2DType } from '../framework/physics-types'; import { Node } from '../../scene-graph/node'; -import { Collider2D } from '../framework'; +import { Collider2D, Joint2D } from '../framework'; import { NodeEventType } from '../../scene-graph/node-event'; +import { B2Shape2D } from './shapes/shape-2d'; +import { B2Joint } from './joints/joint-2d'; const tempVec3 = new Vec3(); const tempVec2_1 = { x: 0, y: 0 };//new B2.Vec2(0, 0); @@ -114,6 +116,29 @@ export class B2RigidBody2D implements IRigidBody2D { _destroy (): void { if (!this._inited) return; + //collect all fixtures attached to this rigid body and process them + const fixtureList = this.impl?.GetFixtureList(); + if (fixtureList) { + let shapeTSObj = getTSObjectFromWASMObject(fixtureList); + while (shapeTSObj && shapeTSObj.impl) { + shapeTSObj.destroy(); + const nextFixture = fixtureList.GetNext(); + shapeTSObj = getTSObjectFromWASMObject(nextFixture); + } + } + + //collect all joints attached to this rigid body and process them + const jointListPtr = this.impl?.GetJointList(); + if (jointListPtr) { + let jointWASMPtr = B2.JointEdgeGetJoint(jointListPtr) as number; + let jointTSObj = getTSObjectFromWASMObjectPtr(jointWASMPtr); + while (jointTSObj) { + jointTSObj.destroy(); + jointWASMPtr = B2.JointEdgeGetNext(jointListPtr) as number; + jointTSObj = getTSObjectFromWASMObjectPtr(jointWASMPtr); + } + } + (PhysicsSystem2D.instance.physicsWorld as B2PhysicsWorld).removeBody(this); this._inited = false; diff --git a/cocos/physics-2d/box2d-wasm/shapes/shape-2d.ts b/cocos/physics-2d/box2d-wasm/shapes/shape-2d.ts index 5f102e03580..84ab4a5881b 100644 --- a/cocos/physics-2d/box2d-wasm/shapes/shape-2d.ts +++ b/cocos/physics-2d/box2d-wasm/shapes/shape-2d.ts @@ -77,7 +77,7 @@ export class B2Shape2D implements IBaseShape { } onDisable (): void { - PhysicsSystem2D.instance._callAfterStep(this, this._destroy); + PhysicsSystem2D.instance._callAfterStep(this, this.destroy); } start (): void { @@ -92,7 +92,7 @@ export class B2Shape2D implements IBaseShape { } apply (): void { - this._destroy(); + this.destroy(); if (this.collider.enabledInHierarchy) { this._init(); } @@ -200,7 +200,7 @@ export class B2Shape2D implements IBaseShape { this._inited = true; } - _destroy (): void { + destroy (): void { if (!this._inited) return; const fixtures = this._fixtures; diff --git a/cocos/physics-2d/box2d/joints/joint-2d.ts b/cocos/physics-2d/box2d/joints/joint-2d.ts index 8aa60b35467..9cf1d9b7087 100644 --- a/cocos/physics-2d/box2d/joints/joint-2d.ts +++ b/cocos/physics-2d/box2d/joints/joint-2d.ts @@ -62,6 +62,13 @@ export class b2Joint implements IJoint2D { PhysicsSystem2D.instance._callAfterStep(this, this._init); } + apply (): void { + PhysicsSystem2D.instance._callAfterStep(this, this._destroy); + if (this.comp!.enabledInHierarchy) { + PhysicsSystem2D.instance._callAfterStep(this, this._init); + } + } + _init (): void { if (this._inited) return; diff --git a/cocos/physics-2d/framework/components/joints/joint-2d.ts b/cocos/physics-2d/framework/components/joints/joint-2d.ts index 57e2d0285a0..dd4653afc0e 100644 --- a/cocos/physics-2d/framework/components/joints/joint-2d.ts +++ b/cocos/physics-2d/framework/components/joints/joint-2d.ts @@ -81,7 +81,7 @@ export class Joint2D extends Component { * @zh * 关节所绑定的刚体组件。 */ - _body: RigidBody2D | null = null + _body: RigidBody2D | null = null; get body (): RigidBody2D | null { return this._body; } @@ -132,4 +132,16 @@ export class Joint2D extends Component { this._joint.onDestroy(); } } + + /** + * @en + * If the physics engine is box2d, need to call this function to apply current changes to joint, this will regenerate inner box2d joint. + * @zh + * 如果物理引擎是 box2d, 需要调用此函数来应用当前 joint 中的修改。 + */ + apply (): void { + if (this._joint && this._joint.apply) { + this._joint.apply(); + } + } } diff --git a/cocos/physics-2d/framework/physics-selector.ts b/cocos/physics-2d/framework/physics-selector.ts index 958ae1da47b..443f778831d 100644 --- a/cocos/physics-2d/framework/physics-selector.ts +++ b/cocos/physics-2d/framework/physics-selector.ts @@ -297,6 +297,7 @@ const ENTIRE_JOINT: IEntireJoint = { impl: null, initialize: FUNC, + apply: FUNC, setDampingRatio: FUNC, setFrequency: FUNC, diff --git a/cocos/physics-2d/spec/i-physics-joint.ts b/cocos/physics-2d/spec/i-physics-joint.ts index 67b5d7cd999..ae23b558420 100644 --- a/cocos/physics-2d/spec/i-physics-joint.ts +++ b/cocos/physics-2d/spec/i-physics-joint.ts @@ -22,15 +22,13 @@ THE SOFTWARE. */ - - import { IVec2Like } from '../../core'; import { ILifecycle } from '../../physics/spec/i-lifecycle'; import { Joint2D, RigidBody2D } from '../framework'; export interface IJoint2D extends ILifecycle { readonly impl: any; - + apply (): void; initialize (v: Joint2D): void; } diff --git a/cocos/physics/utils/util.ts b/cocos/physics/utils/util.ts index ead93adcf0e..cd2c5c6c661 100644 --- a/cocos/physics/utils/util.ts +++ b/cocos/physics/utils/util.ts @@ -22,7 +22,7 @@ THE SOFTWARE. */ -import { equals, Vec3, IVec3Like } from '../../core'; +import { equals, Vec3, IVec3Like, murmurhash2_32_gc } from '../../core'; import { CharacterController, CharacterTriggerEventType, Collider, CollisionEventType, IContactEquation, TriggerEventType } from '../framework'; export { cylinder } from '../../primitive'; @@ -69,23 +69,21 @@ export const CollisionEventObject = { export function shrinkPositions (buffer: Float32Array | number[]): number[] { const pos: number[] = []; + const posHashMap = {}; if (buffer.length >= 3) { - // eslint-disable-next-line no-unused-expressions - pos[0] = buffer[0], pos[1] = buffer[1], pos[2] = buffer[2]; + pos[0] = buffer[0]; + pos[1] = buffer[1]; + pos[2] = buffer[2]; const len = buffer.length; for (let i = 3; i < len; i += 3) { const p0 = buffer[i]; const p1 = buffer[i + 1]; const p2 = buffer[i + 2]; - const len2 = pos.length; - let isNew = true; - for (let j = 0; j < len2; j += 3) { - if (equals(p0, pos[j]) && equals(p1, pos[j + 1]) && equals(p2, pos[j + 2])) { - isNew = false; - break; - } - } - if (isNew) { + const str = String(p0) + String(p1) + String(p2); + //todo: directly use buffer as input + const hash = murmurhash2_32_gc(str, 666); + if (posHashMap[hash] !== str) { + posHashMap[hash] = str; pos.push(p0); pos.push(p1); pos.push(p2); } } diff --git a/cocos/render-scene/core/pass.ts b/cocos/render-scene/core/pass.ts index 408d5938c5f..c09b83e9396 100644 --- a/cocos/render-scene/core/pass.ts +++ b/cocos/render-scene/core/pass.ts @@ -39,7 +39,7 @@ import { MacroRecord, MaterialProperty, customizeType, getBindingFromHandle, getDefaultFromType, getStringFromType, getOffsetFromHandle, getTypeFromHandle, type2reader, type2writer, getCountFromHandle, type2validator, } from './pass-utils'; -import { RenderPassStage, RenderPriority } from '../../rendering/define'; +import { RenderPassStage, RenderPriority, SetIndex } from '../../rendering/define'; import { InstancedBuffer } from '../../rendering/instanced-buffer'; import { ProgramLibrary } from '../../rendering/custom/private'; @@ -469,10 +469,19 @@ export class Pass { * @zh 重置所有 texture 和 sampler 为初始默认值。 */ public resetTextures (): void { - for (let i = 0; i < this._shaderInfo.samplerTextures.length; i++) { - const u = this._shaderInfo.samplerTextures[i]; - for (let j = 0; j < u.count; j++) { - this.resetTexture(u.name, j); + if (cclegacy.rendering) { + const set = this._shaderInfo.descriptors[SetIndex.MATERIAL]; + for (const combined of set.samplerTextures) { + for (let j = 0; j < combined.count; ++j) { + this.resetTexture(combined.name, j); + } + } + } else { + for (let i = 0; i < this._shaderInfo.samplerTextures.length; i++) { + const u = this._shaderInfo.samplerTextures[i]; + for (let j = 0; j < u.count; j++) { + this.resetTexture(u.name, j); + } } } } diff --git a/cocos/render-scene/scene/submodel.ts b/cocos/render-scene/scene/submodel.ts index a1e51f42617..041c1b7060a 100644 --- a/cocos/render-scene/scene/submodel.ts +++ b/cocos/render-scene/scene/submodel.ts @@ -32,7 +32,6 @@ import { DescriptorSet, DescriptorSetInfo, Device, InputAssembler, Texture, Text import { errorID, Mat4, cclegacy } from '../../core'; import { getPhaseID } from '../../rendering/pass-phase'; import { Root } from '../../root'; -import { MacroRecord } from '../core/pass-utils'; const _dsInfo = new DescriptorSetInfo(null!); const MAX_PASS_COUNT = 8; @@ -55,7 +54,6 @@ export class SubModel { protected _shaders: Shader[] | null = null; protected _subMesh: RenderingSubMesh | null = null; protected _patches: IMacroPatch[] | null = null; - protected _globalPatches: MacroRecord | null = null; protected _priority: RenderPriority = RenderPriority.DEFAULT; protected _inputAssembler: InputAssembler | null = null; protected _descriptorSet: DescriptorSet | null = null; @@ -296,7 +294,6 @@ export class SubModel { this.priority = RenderPriority.DEFAULT; this._patches = null; - this._globalPatches = null; this._subMesh = null; this._passes = null; @@ -327,19 +324,6 @@ export class SubModel { * @zh 管线更新回调 */ public onPipelineStateChanged (): void { - const root = cclegacy.director.root as Root; - const pipeline = root.pipeline; - const pipelinePatches = Object.entries(pipeline.macros); - if (!this._globalPatches && pipelinePatches.length === 0) { - return; - } else if (pipelinePatches.length) { - if (this._globalPatches && pipelinePatches.length === this._globalPatches.length) { - const globalPatches = Object.entries(this._globalPatches); - const patchesStateUnchanged = JSON.stringify(pipelinePatches.sort()) === JSON.stringify(globalPatches.sort()); - if (patchesStateUnchanged) return; - } - } - this._globalPatches = pipeline.macros; const passes = this._passes; if (!passes) { return; } @@ -363,6 +347,7 @@ export class SubModel { return; } else if (patches) { patches = patches.sort(); + // Sorting on shorter patches outperforms hashing, with negative optimization on longer global patches. if (this._patches && patches.length === this._patches.length) { const patchesStateUnchanged = JSON.stringify(patches) === JSON.stringify(this._patches); if (patchesStateUnchanged) return; diff --git a/cocos/rendering/custom/builtin-pipelines.ts b/cocos/rendering/custom/builtin-pipelines.ts index 1403ee8d602..b1e31a85d11 100644 --- a/cocos/rendering/custom/builtin-pipelines.ts +++ b/cocos/rendering/custom/builtin-pipelines.ts @@ -106,7 +106,7 @@ export class DeferredPipelineBuilder implements PipelineBuilder { // Lighting Pass const lightInfo = setupLightingPass(ppl, info, useCluster); // Deferred ForwardPass, for non-surface-shader material and transparent material - setupDeferredForward(ppl, info, lightInfo.rtName); + setupDeferredForward(ppl, info, lightInfo.rtName, useCluster); // Postprocess setupPostprocessPass(ppl, info, lightInfo.rtName); diff --git a/cocos/rendering/custom/compiler.ts b/cocos/rendering/custom/compiler.ts index da170462e25..7ed6779bba5 100644 --- a/cocos/rendering/custom/compiler.ts +++ b/cocos/rendering/custom/compiler.ts @@ -28,9 +28,11 @@ import { VectorGraphColorMap } from './effect'; import { DefaultVisitor, depthFirstSearch, ReferenceGraphView } from './graph'; import { LayoutGraphData } from './layout-graph'; import { BasicPipeline } from './pipeline'; -import { Blit, ClearView, ComputePass, ComputeSubpass, CopyPass, Dispatch, FormatView, ManagedBuffer, ManagedResource, ManagedTexture, MovePass, +import { + Blit, ClearView, ComputePass, ComputeSubpass, CopyPass, Dispatch, FormatView, ManagedBuffer, ManagedResource, ManagedTexture, MovePass, RasterPass, RasterSubpass, RaytracePass, RenderGraph, RenderGraphVisitor, RasterView, ComputeView, - RenderQueue, RenderSwapchain, ResolvePass, ResourceGraph, ResourceGraphVisitor, SceneData, SubresourceView } from './render-graph'; + RenderQueue, RenderSwapchain, ResolvePass, ResourceGraph, ResourceGraphVisitor, SceneData, SubresourceView, PersistentBuffer, PersistentTexture, +} from './render-graph'; import { AccessType, ResourceResidency, SceneFlags } from './types'; import { hashCombineNum, hashCombineStr } from './define'; @@ -83,10 +85,11 @@ class PassVisitor implements RenderGraphVisitor { public queueID = 0xFFFFFFFF; public sceneID = 0xFFFFFFFF; public passID = 0xFFFFFFFF; + public dispatchID = 0xFFFFFFFF; // output resourcetexture id public resID = 0xFFFFFFFF; public context: CompilerContext; - private _currPass: RasterPass | CopyPass | null = null; + private _currPass: RasterPass | CopyPass | ComputePass | null = null; private _resVisitor: ResourceVisitor; constructor (context: CompilerContext) { this.context = context; @@ -98,6 +101,12 @@ class PassVisitor implements RenderGraphVisitor { protected _isCopyPass (u: number): boolean { return !!this.context.renderGraph.tryGetCopy(u); } + protected _isCompute (u: number): boolean { + return !!this.context.renderGraph.tryGetCompute(u); + } + protected _isDispatch (u: number): boolean { + return !!this.context.renderGraph.tryGetDispatch(u); + } protected _isQueue (u: number): boolean { return !!this.context.renderGraph.tryGetQueue(u); } @@ -242,12 +251,14 @@ class PassVisitor implements RenderGraphVisitor { } applyID (id: number, resId: number): void { this.resID = resId; - if (this._isRasterPass(id) || this._isCopyPass(id)) { + if (this._isRasterPass(id) || this._isCopyPass(id) || this._isCompute(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._isDispatch(id)) { + this.dispatchID = id; } } rasterPass (pass: RasterPass): void { @@ -258,10 +269,20 @@ class PassVisitor implements RenderGraphVisitor { // } this._currPass = pass; } - rasterSubpass (value: RasterSubpass): void {} - computeSubpass (value: ComputeSubpass): void {} - compute (value: ComputePass): void {} - resolve (value: ResolvePass): void {} + rasterSubpass (value: RasterSubpass): void { + // noop + } + computeSubpass (value: ComputeSubpass): void { + // noop + } + compute (value: ComputePass): void { + this._currPass = value; + const rg = context.renderGraph; + rg.setValid(this.passID, true); + } + resolve (value: ResolvePass): void { + // noop + } copy (value: CopyPass): void { const rg = context.renderGraph; if (rg.getValid(this.passID)) { @@ -283,18 +304,32 @@ class PassVisitor implements RenderGraphVisitor { } } } - move (value: MovePass): void {} - raytrace (value: RaytracePass): void {} - queue (value: RenderQueue): void {} + move (value: MovePass): void { + // noop + } + raytrace (value: RaytracePass): void { + // noop + } + queue (value: RenderQueue): void { + // noop + } scene (value: SceneData): void { this._fetchValidPass(); } blit (value: Blit): void { this._fetchValidPass(); } - dispatch (value: Dispatch): void {} - clear (value: ClearView[]): void {} - viewport (value: Viewport): void {} + dispatch (value: Dispatch): void { + const rg = this.context.renderGraph; + rg.setValid(this.queueID, true); + rg.setValid(this.dispatchID, true); + } + clear (value: ClearView[]): void { + // noop + } + viewport (value: Viewport): void { + // noop + } } class PassManagerVisitor extends DefaultVisitor { @@ -342,19 +377,20 @@ class ResourceVisitor implements ResourceGraphVisitor { managed (value: ManagedResource): void { this.dependency(); } - persistentBuffer (value: Buffer): void { + persistentBuffer (value: Buffer | PersistentBuffer): void { + // noop } dependency (): void { if (!this._passManagerVis) { - this._passManagerVis = new PassManagerVisitor(this._context, this.resID); + this._passManagerVis = new PassManagerVisitor(this._context, this.resID); } else { this._passManagerVis.resId = this.resID; } depthFirstSearch(this._passManagerVis.graphView, this._passManagerVis, this._passManagerVis.colorMap); } - persistentTexture (value: Texture): void { + persistentTexture (value: Texture | PersistentTexture): void { this.dependency(); } framebuffer (value: Framebuffer): void { @@ -364,8 +400,10 @@ class ResourceVisitor implements ResourceGraphVisitor { this.dependency(); } formatView (value: FormatView): void { + // noop } subresourceView (value: SubresourceView): void { + // noop } } diff --git a/cocos/rendering/custom/define.ts b/cocos/rendering/custom/define.ts index b131e820357..3704f56ac7d 100644 --- a/cocos/rendering/custom/define.ts +++ b/cocos/rendering/custom/define.ts @@ -45,7 +45,7 @@ import { ImageAsset, Material, Texture2D } from '../../asset/assets'; import { getProfilerCamera, SRGBToLinear } from '../pipeline-funcs'; import { RenderWindow } from '../../render-scene/core/render-window'; import { RenderData, RenderGraph } from './render-graph'; -import { WebPipeline } from './web-pipeline'; +import { WebComputePassBuilder, WebPipeline } from './web-pipeline'; import { DescriptorSetData, LayoutGraph, LayoutGraphData } from './layout-graph'; import { AABB } from '../../core/geometry'; import { DebugViewCompositeType, DebugViewSingleType } from '../debug-view'; @@ -2178,7 +2178,7 @@ export function buildHBAOPasses ( return { rtName: haboCombined.rtName, dsName: inputDS }; } -export const MAX_LIGHTS_PER_CLUSTER = 100; +export const MAX_LIGHTS_PER_CLUSTER = 200; export const CLUSTERS_X = 16; export const CLUSTERS_Y = 8; export const CLUSTERS_Z = 24; @@ -2240,6 +2240,9 @@ export function buildLightClusterBuildPass ( const width = camera.width * ppl.pipelineSceneData.shadingScale; const height = camera.height * ppl.pipelineSceneData.shadingScale; + if ('setCurrConstant' in clusterPass) { // web-pipeline + (clusterPass as WebComputePassBuilder).addConstant('CCConst', 'cluster-build-cs'); + } clusterPass.setVec4('cc_nearFar', new Vec4(camera.nearClip, camera.farClip, camera.getClipSpaceMinz(), 0)); clusterPass.setVec4('cc_viewPort', new Vec4(0, 0, width, height)); clusterPass.setVec4('cc_workGroup', new Vec4(CLUSTERS_X, CLUSTERS_Y, CLUSTERS_Z, 0)); @@ -2280,6 +2283,9 @@ export function buildLightClusterCullingPass ( const width = camera.width * ppl.pipelineSceneData.shadingScale; const height = camera.height * ppl.pipelineSceneData.shadingScale; + if ('setCurrConstant' in clusterPass) { // web-pipeline + (clusterPass as WebComputePassBuilder).addConstant('CCConst', 'cluster-build-cs'); + } clusterPass.setVec4('cc_nearFar', new Vec4(camera.nearClip, camera.farClip, camera.getClipSpaceMinz(), 0)); clusterPass.setVec4('cc_viewPort', new Vec4(width, height, width, height)); clusterPass.setVec4('cc_workGroup', new Vec4(CLUSTERS_X, CLUSTERS_Y, CLUSTERS_Z, 0)); diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index 5c1191dc03d..95f9cbae566 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -36,6 +36,7 @@ import { AccessFlagBit, Attribute, Buffer, + BufferFlagBit, BufferInfo, BufferUsageBit, BufferViewInfo, @@ -47,6 +48,7 @@ import { DescriptorSetInfo, Device, deviceManager, + DispatchInfo, Format, Framebuffer, FramebufferInfo, @@ -55,7 +57,9 @@ import { InputAssemblerInfo, LoadOp, MemoryUsageBit, + PipelineBindPoint, PipelineState, + PipelineStateInfo, Rect, RenderPass, RenderPassInfo, @@ -95,6 +99,8 @@ import { ManagedResource, ManagedTexture, MovePass, + PersistentBuffer, + PersistentTexture, RasterPass, RasterSubpass, RasterView, @@ -113,6 +119,7 @@ import { SubresourceView, } from './render-graph'; import { + AccessType, AttachmentType, QueueHint, ResourceDimension, @@ -137,32 +144,74 @@ import { } 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; } - createDeviceTex (value: Texture | Framebuffer | ManagedResource | RenderSwapchain): void { - const deviceTex = new DeviceTexture(this.name, value); - context.deviceTextures.set(this.name, deviceTex); + 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 { - // noop + this.createDeviceBuf(value); } managedTexture (value: ManagedTexture): void { // noop } - persistentBuffer (value: Buffer): void { - // noop + persistentBuffer (value: PersistentBuffer): void { + this.createDeviceBuf(value); } - persistentTexture (value: Texture): void { + persistentTexture (value: PersistentTexture): void { this.createDeviceTex(value); } framebuffer (value: Framebuffer): void { @@ -214,7 +263,7 @@ class DeviceTexture extends DeviceResource { this._desc.textureFlags = desc.textureFlags; this._desc.width = desc.width; } - constructor (name: string, tex: Texture | Framebuffer | RenderSwapchain | ManagedResource) { + constructor (name: string, tex: PersistentTexture | Framebuffer | RenderSwapchain | ManagedResource) { super(name); const resGraph = context.resourceGraph; const verID = resGraph.vertex(name); @@ -287,8 +336,35 @@ function isShadowMap (graphScene: GraphScene): boolean | null { } class DeviceBuffer extends DeviceResource { - constructor (name: string) { + 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; + } } } @@ -454,12 +530,67 @@ class BlitDesc { } } +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 _hint: QueueHint = QueueHint.NONE; private _graphQueue!: RenderQueue; private _phaseID: number = getPhaseID('default'); private _renderPhase: RenderPhaseData | null = null; @@ -522,7 +653,7 @@ class DeviceRenderQueue { } addSceneTask (scene: GraphScene): void { if (!this._transversal) { - this._transversal = new DeviceSceneTransversal(this, context.pipelineSceneData, scene); + this._transversal = new DeviceSceneTransversal(this, context.pipelineSceneData, scene); } this._transversal.graphScene = scene; this._preSceneTasks.push(this._transversal.preRenderPass(this._sceneVisitor)); @@ -594,14 +725,17 @@ class RenderPassLayoutInfo { 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.pipeline.descriptorSet; + 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 layoutDesc = layoutData.descriptorSet!; - if (!gfxTex) { + + 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); @@ -616,8 +750,19 @@ class RenderPassLayoutInfo { // const buffer = layoutDesc.getBuffer(block.offset + i); // const texture = layoutDesc.getTexture(block.offset + i); if (descriptorID === block.descriptors[i].descriptorID) { - layoutDesc.bindTexture(block.offset + i, gfxTex); - layoutDesc.bindSampler(block.offset + i, context.device.getSampler(samplerInfo)); + 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; } @@ -806,6 +951,10 @@ class DeviceRenderPass { // 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, @@ -832,6 +981,7 @@ class DeviceRenderPass { } addQueue (queue: DeviceRenderQueue): void { this._deviceQueues.push(queue); } prePass (): void { + context.descriptorSet = getDescriptorSetDataFromLayout(this.layoutName)!.descriptorSet; for (const queue of this._deviceQueues) { queue.preRecord(); } @@ -884,7 +1034,7 @@ class DeviceRenderPass { ia, ); const descData = getDescriptorSetDataFromLayoutId(pass.passID)!; - mergeSrcToTargetDesc(descData.descriptorSet, context.pipeline.descriptorSet, true); + mergeSrcToTargetDesc(descData.descriptorSet, context.descriptorSet, true); profilerViewport.width = rect.width; profilerViewport.height = rect.height; cmdBuff.setViewport(profilerViewport); @@ -921,7 +1071,7 @@ class DeviceRenderPass { ); cmdBuff.bindDescriptorSet( SetIndex.GLOBAL, - context.pipeline.descriptorSet, + context.descriptorSet!, ); for (const queue of this._deviceQueues) { queue.record(); @@ -1013,6 +1163,135 @@ class DeviceRenderPass { } } +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; @@ -1180,6 +1459,23 @@ class DeviceSceneTask extends WebSceneTask { } 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]; @@ -1243,7 +1539,7 @@ class DeviceSceneTask extends WebSceneTask { const shader = pass.getShaderVariant(); const devicePass = this._currentQueue.devicePass; const screenIa: InputAssembler = this._currentQueue.blitDesc!.screenQuad!.quadIA!; - const globalDesc = context.pipeline.descriptorSet; + const globalDesc = context.descriptorSet; let pso: PipelineState | null = null; if (pass !== null && shader !== null && screenIa !== null) { pso = PipelineStateManager.getOrCreatePipelineState( @@ -1288,7 +1584,7 @@ class DeviceSceneTask extends WebSceneTask { const layoutName = context.renderGraph.getLayout(rasterId); const descSetData = getDescriptorSetDataFromLayout(layoutName); - mergeSrcToTargetDesc(descSetData!.descriptorSet, context.pipeline.descriptorSet, true); + mergeSrcToTargetDesc(descSetData!.descriptorSet, context.descriptorSet, true); this._currentQueue.isUpdateUBO = true; } @@ -1332,7 +1628,7 @@ class DeviceSceneTask extends WebSceneTask { return; } const renderQueueDesc = sceneCulling.sceneQueryIndex.get(this.graphScene.sceneID)!; - const renderQueue = sceneCulling.renderQueues[renderQueueDesc.renderQueueTarget]; + 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); @@ -1340,7 +1636,7 @@ class DeviceSceneTask extends WebSceneTask { this._recordAdditiveLights(); this.visitor.bindDescriptorSet( SetIndex.GLOBAL, - context.pipeline.descriptorSet, + context.descriptorSet!, ); } @@ -1362,13 +1658,15 @@ class DeviceSceneTask extends WebSceneTask { } } -class DevicePostSceneTask extends WebSceneTask {} +class DevicePostSceneTask extends WebSceneTask { } class ExecutorPools { constructor (context: ExecutorContext) { this.deviceQueuePool = new RecyclePool((): DeviceRenderQueue => new DeviceRenderQueue(), 16); + this.computeQueuePool = new RecyclePool((): DeviceComputeQueue => new DeviceComputeQueue(), 16); this.graphScenePool = new RecyclePool((): GraphScene => new GraphScene(), 16); this.rasterPassInfoPool = new RecyclePool((): RasterPassInfo => new RasterPassInfo(), 16); + this.computePassInfoPool = new RecyclePool((): ComputePassInfo => new ComputePassInfo(), 16); this.reflectionProbe = new RecyclePool((): RenderReflectionProbeQueue => new RenderReflectionProbeQueue(context.pipeline), 8); this.passPool = new RecyclePool((): { priority: number; hash: number; depth: number; shaderId: number; subModel: any; passIdx: number; } => ({ priority: 0, @@ -1388,6 +1686,9 @@ class ExecutorPools { addDeviceQueue (): DeviceRenderQueue { return this.deviceQueuePool.add(); } + addComputeQueue (): DeviceComputeQueue { + return this.computeQueuePool.add(); + } addGraphScene (): GraphScene { return this.graphScenePool.add(); } @@ -1397,17 +1698,24 @@ class ExecutorPools { 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: RecyclePool; + readonly computeQueuePool: RecyclePool; readonly graphScenePool: RecyclePool; readonly reflectionProbe: RecyclePool; readonly passPool: RecyclePool; readonly rasterPassInfoPool: RecyclePool; + readonly computePassInfoPool: RecyclePool; } const vbData = new Float32Array(4 * 4); @@ -1492,8 +1800,8 @@ class BlitInfo { let maxY = (renderArea.y + renderArea.height) / this._context.height; if (this._context.root.device.capabilities.screenSpaceSignY > 0) { const temp = maxY; - maxY = minY; - minY = temp; + maxY = minY; + minY = temp; } let n = 0; switch (surfaceTransform) { @@ -1550,7 +1858,7 @@ class BlitInfo { } // create index buffer - const ibStride = Uint8Array.BYTES_PER_ELEMENT; + const ibStride = Uint16Array.BYTES_PER_ELEMENT; const ibSize = ibStride * 6; const quadIB: Buffer = device.createBuffer(new BufferInfo( @@ -1564,11 +1872,11 @@ class BlitInfo { return inputAssemblerData; } - const indices = new Uint8Array(6); + 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); + quadIB.update(indices.buffer); // create input assembler @@ -1599,6 +1907,7 @@ class ExecutorContext { layoutGraph: LayoutGraphData, width: number, height: number, + descriptorSet = null, ) { this.pipeline = pipeline; this.device = device; @@ -1615,6 +1924,7 @@ class ExecutorContext { this.pools = new ExecutorPools(this); this.blit = new BlitInfo(this); this.culling = new SceneCulling(); + this.descriptorSet = descriptorSet; } reset (): void { this.culling.clear(); @@ -1636,6 +1946,7 @@ class ExecutorContext { readonly resourceGraph: ResourceGraph; readonly devicePasses: Map = new Map(); readonly deviceTextures: Map = new Map(); + readonly deviceBuffers: Map = new Map(); readonly layoutGraph: LayoutGraphData; readonly root: Root; readonly ubo: PipelineUBO; @@ -1648,6 +1959,7 @@ class ExecutorContext { width: number; height: number; cullCamera; + descriptorSet: DescriptorSet | null; } export class Executor { @@ -1697,6 +2009,26 @@ export class Executor { 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 { @@ -1719,6 +2051,11 @@ export class Executor { v.release(); } context.deviceTextures.clear(); + + for (const [k, v] of context.deviceBuffers) { + v.release(); + } + context.deviceBuffers.clear(); } readonly _context: ExecutorContext; private _visitor: RenderVisitor | undefined; @@ -1728,8 +2065,9 @@ class BaseRenderVisitor { public queueID = 0xFFFFFFFF; public sceneID = 0xFFFFFFFF; public passID = 0xFFFFFFFF; - public currPass: DeviceRenderPass | undefined; - public currQueue: DeviceRenderQueue | undefined; + public dispatchID = 0xFFFFFFFF; + public currPass: DeviceRenderPass | DeviceComputePass | undefined; + public currQueue: DeviceRenderQueue |DeviceComputeQueue | undefined; public rg: RenderGraph; constructor () { this.rg = context.renderGraph; @@ -1737,6 +2075,12 @@ class BaseRenderVisitor { 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); } @@ -1747,7 +2091,17 @@ class BaseRenderVisitor { 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; } + 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; + } } } @@ -1781,27 +2135,54 @@ class PreRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor { computeSubpass (value: ComputeSubpass): void { // noop } - compute (value: ComputePass): void { - // noop - } resolve (value: ResolvePass): void { // noop } - copy (value: CopyPass): 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; - const deviceQueue = context.pools.addDeviceQueue(); - deviceQueue.init(this.currPass!, value, this.queueID); - this.currQueue = deviceQueue; - this.currPass!.addQueue(deviceQueue); + 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; @@ -1813,18 +2194,46 @@ class PreRenderVisitor extends BaseRenderVisitor implements RenderGraphVisitor { } scene (value: SceneData): void { if (!this.rg.getValid(this.sceneID)) return; + const renderQueue = this.currQueue as DeviceRenderQueue; const graphScene = context.pools.addGraphScene(); graphScene.init(value, null, this.sceneID); - this.currQueue!.addSceneTask(graphScene); + 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); - this.currQueue!.addSceneTask(graphScene); + renderQueue.addSceneTask(graphScene); } dispatch (value: Dispatch): void { - // noop + 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)); } } diff --git a/cocos/rendering/custom/pipeline-define.ts b/cocos/rendering/custom/pipeline-define.ts index 87d6a02dd34..1c10c17436c 100644 --- a/cocos/rendering/custom/pipeline-define.ts +++ b/cocos/rendering/custom/pipeline-define.ts @@ -35,7 +35,7 @@ export class CameraInfo { this.width = width; this.height = height; } - public camera; + public camera: Camera; public id = 0xFFFFFFFF; public windowID = 0xFFFFFFFF; public width = 0; @@ -53,9 +53,12 @@ function prepareRenderWindow (camera: Camera): number { return windowID; } -export function prepareResource (ppl: BasicPipeline, camera: Camera, +export function prepareResource ( + ppl: BasicPipeline, + camera: Camera, initResourceFunc: (ppl: BasicPipeline, info: CameraInfo) => void, - updateResourceFunc: (ppl: BasicPipeline, info: CameraInfo) => void): CameraInfo { + updateResourceFunc: (ppl: BasicPipeline, info: CameraInfo) => void, +): CameraInfo { let info = cameraInfos.get(camera); if (info !== undefined) { let width = camera.window.width; @@ -67,6 +70,9 @@ export function prepareResource (ppl: BasicPipeline, camera: Camera, height = 1; } const windowID = prepareRenderWindow(camera); + info.width = width; + info.height = height; + info.windowID = windowID; updateResourceFunc(ppl, info); return info; } @@ -84,7 +90,7 @@ export function prepareResource (ppl: BasicPipeline, camera: Camera, return info; } -function buildShadowRes (ppl: BasicPipeline, name: string, width, height): void { +function buildShadowRes (ppl: BasicPipeline, name: string, width: number, height: number): void { const fboW = width; const fboH = height; const shadowMapName = name; @@ -111,7 +117,7 @@ export function setupShadowRes (ppl: BasicPipeline, cameraInfo: CameraInfo): Sha const _validLights: Light[] = shadowInfo.validLights; let n = 0; let m = 0; - for (;n < shadow.maxReceived && m < validPunctualLights.length;) { + for (; n < shadow.maxReceived && m < validPunctualLights.length;) { const light = validPunctualLights[m]; if (light.type === LightType.SPOT) { const spotLight = light as any; @@ -149,12 +155,17 @@ export function setupShadowRes (ppl: BasicPipeline, cameraInfo: CameraInfo): Sha return shadowInfo; } -export const updateShadowRes = setupShadowRes; +export const updateShadowRes = setupShadowRes; let shadowPass; -function buildShadowPass (passName: Readonly, +function buildShadowPass ( + passName: Readonly, ppl: BasicPipeline, - camera: Camera, light: Light, level: number, - width: Readonly, height: Readonly): void { + camera: Camera, + light: Light, + level: number, + width: Readonly, + height: Readonly, +): void { const fboW = width; const fboH = height; const area = getRenderArea(camera, width, height, light, level); @@ -166,12 +177,21 @@ function buildShadowPass (passName: Readonly, shadowPass.name = passName; shadowPass.setViewport(new Viewport(0, 0, fboW, fboH)); shadowPass.addRenderTarget(shadowMapName, LoadOp.CLEAR, StoreOp.STORE, new Color(1, 1, 1, camera.clearColor.w)); - shadowPass.addDepthStencil(`${shadowMapName}Depth`, LoadOp.CLEAR, StoreOp.DISCARD, - camera.clearDepth, camera.clearStencil, ClearFlagBit.DEPTH_STENCIL); + shadowPass.addDepthStencil( + `${shadowMapName}Depth`, + LoadOp.CLEAR, + StoreOp.DISCARD, + camera.clearDepth, + camera.clearStencil, + ClearFlagBit.DEPTH_STENCIL, + ); } const queue = shadowPass.addQueue(QueueHint.RENDER_OPAQUE, 'shadow-caster'); - queue.addSceneOfCamera(camera, new LightInfo(light, level), - SceneFlags.SHADOW_CASTER); + queue.addSceneOfCamera( + camera, + new LightInfo(light, level), + SceneFlags.SHADOW_CASTER, + ); queue.setViewport(new Viewport(area.x, area.y, area.width, area.height)); } export function setupShadowPass (ppl: BasicPipeline, cameraInfo: CameraInfo): void { @@ -185,14 +205,28 @@ export function setupShadowPass (ppl: BasicPipeline, cameraInfo: CameraInfo): vo if (mainLight && mainLight.shadowEnabled) { shadowInfo.mainLightShadowNames[0] = `MainLightShadow${cameraInfo.id}`; if (mainLight.shadowFixedArea) { - buildShadowPass(shadowInfo.mainLightShadowNames[0], ppl, - camera, mainLight, 0, mapWidth, mapHeight); + buildShadowPass( + shadowInfo.mainLightShadowNames[0], + ppl, + camera, + mainLight, + 0, + mapWidth, + mapHeight, + ); } else { const csmLevel = ppl.pipelineSceneData.csmSupported ? mainLight.csmLevel : 1; shadowInfo.mainLightShadowNames[0] = `MainLightShadow${cameraInfo.id}`; for (let i = 0; i < csmLevel; i++) { - buildShadowPass(shadowInfo.mainLightShadowNames[0], ppl, - camera, mainLight, i, mapWidth, mapHeight); + buildShadowPass( + shadowInfo.mainLightShadowNames[0], + ppl, + camera, + mainLight, + i, + mapWidth, + mapHeight, + ); } } } @@ -201,8 +235,15 @@ export function setupShadowPass (ppl: BasicPipeline, cameraInfo: CameraInfo): vo const light = shadowInfo.validLights[l]; const passName = `SpotLightShadow${l.toString()}${cameraInfo.id}`; shadowInfo.spotLightShadowNames[l] = passName; - buildShadowPass(passName, ppl, - camera, light, 0, mapWidth, mapHeight); + buildShadowPass( + passName, + ppl, + camera, + light, + 0, + mapWidth, + mapHeight, + ); } } @@ -215,8 +256,13 @@ export function setupForwardRes (ppl: BasicPipeline, cameraInfo: CameraInfo, isO if (!isOffScreen) { ppl.addRenderWindow(`ForwardColor${cameraInfo.id}`, Format.BGRA8, width, height, cameraInfo.camera.window); } else { - ppl.addRenderTarget(`ForwardColor${cameraInfo.id}`, getRTFormatBeforeToneMapping(ppl), - width, height, ResourceResidency.PERSISTENT); + ppl.addRenderTarget( + `ForwardColor${cameraInfo.id}`, + getRTFormatBeforeToneMapping(ppl), + width, + height, + ResourceResidency.PERSISTENT, + ); } ppl.addDepthStencil(`ForwardDepthStencil${cameraInfo.id}`, Format.DEPTH_STENCIL, width, height); } @@ -235,7 +281,7 @@ export function updateForwardRes (ppl: BasicPipeline, cameraInfo: CameraInfo, is ppl.updateDepthStencil(`ForwardDepthStencil${cameraInfo.id}`, width, height); } -export function setupDeferredForward (ppl: BasicPipeline, cameraInfo: CameraInfo, inputColor: string): void { +export function setupDeferredForward (ppl: BasicPipeline, cameraInfo: CameraInfo, inputColor: string, clusterLighting?: boolean): void { const area = getRenderArea(cameraInfo.camera, cameraInfo.camera.window.width, cameraInfo.camera.window.height); const width = area.width; const height = area.height; @@ -255,10 +301,12 @@ export function setupDeferredForward (ppl: BasicPipeline, cameraInfo: CameraInfo } } + let sceneFlags = SceneFlags.OPAQUE_OBJECT | SceneFlags.PLANAR_SHADOW | SceneFlags.CUTOUT_OBJECT + | SceneFlags.DRAW_INSTANCING; + sceneFlags |= clusterLighting ? SceneFlags.CLUSTERED_LIGHTING : SceneFlags.DEFAULT_LIGHTING; + forwardPass.addQueue(QueueHint.RENDER_OPAQUE, 'deferred-forward') - .addSceneOfCamera(camera, new LightInfo(), - SceneFlags.OPAQUE_OBJECT | SceneFlags.PLANAR_SHADOW | SceneFlags.CUTOUT_OBJECT - | SceneFlags.DEFAULT_LIGHTING | SceneFlags.DRAW_INSTANCING); + .addSceneOfCamera(camera, new LightInfo(), sceneFlags); forwardPass.addQueue(QueueHint.RENDER_TRANSPARENT, 'deferred-forward') .addSceneOfCamera(camera, new LightInfo(), SceneFlags.TRANSPARENT_OBJECT | SceneFlags.GEOMETRY); } @@ -286,22 +334,29 @@ export function setupForwardPass (ppl: BasicPipeline, cameraInfo: CameraInfo, is } } const camera = cameraInfo.camera; - forwardPass.addRenderTarget(`ForwardColor${cameraInfo.id}`, + forwardPass.addRenderTarget( + `ForwardColor${cameraInfo.id}`, isOffScreen ? LoadOp.CLEAR : getLoadOpOfClearFlag(camera.clearFlag, AttachmentType.RENDER_TARGET), StoreOp.STORE, - new Color(camera.clearColor.x, camera.clearColor.y, camera.clearColor.z, camera.clearColor.w)); - forwardPass.addDepthStencil(`ForwardDepthStencil${cameraInfo.id}`, + new Color(camera.clearColor.x, camera.clearColor.y, camera.clearColor.z, camera.clearColor.w), + ); + forwardPass.addDepthStencil( + `ForwardDepthStencil${cameraInfo.id}`, isOffScreen ? LoadOp.CLEAR : getLoadOpOfClearFlag(camera.clearFlag, AttachmentType.DEPTH_STENCIL), // If the depth texture is used by subsequent passes, it must be set to store. isOffScreen ? StoreOp.DISCARD : StoreOp.STORE, camera.clearDepth, camera.clearStencil, - camera.clearFlag); + camera.clearFlag, + ); forwardPass .addQueue(QueueHint.RENDER_OPAQUE) - .addSceneOfCamera(camera, new LightInfo(), + .addSceneOfCamera( + camera, + new LightInfo(), SceneFlags.OPAQUE_OBJECT | SceneFlags.PLANAR_SHADOW | SceneFlags.CUTOUT_OBJECT - | SceneFlags.DEFAULT_LIGHTING | SceneFlags.DRAW_INSTANCING); + | SceneFlags.DEFAULT_LIGHTING | SceneFlags.DRAW_INSTANCING, + ); let sceneFlags = SceneFlags.TRANSPARENT_OBJECT | SceneFlags.GEOMETRY; if (!isOffScreen) { @@ -337,10 +392,11 @@ export function setupReflectionProbeRes (ppl: BasicPipeline, info: CameraInfo): const probes = cclegacy.internal.reflectionProbeManager.getProbes(); if (probes.length === 0) return; for (let i = 0; i < probes.length; i++) { - const probe = probes[i]; + const probe = probes[i] as ReflectionProbe; if (probe.needRender) { if (probes[i].probeType === ProbeType.PLANAR) { - buildReflectionProbeRes(ppl, probe, probe.realtimePlanarTexture.window!, 0); + const window: RenderWindow = probe.realtimePlanarTexture!.window!; + buildReflectionProbeRes(ppl, probe, window, 0); } else if (EDITOR) { for (let faceIdx = 0; faceIdx < probe.bakedCubeTextures.length; faceIdx++) { probe.updateCameraDir(faceIdx); @@ -366,10 +422,20 @@ function buildReflectProbePass (ppl: BasicPipeline, info: CameraInfo, probe: Ref const probePass = ppl.addRenderPass(width, height, 'default'); probePass.name = `ReflectionProbePass${faceIdx}`; probePass.setViewport(new Viewport(0, 0, width, height)); - probePass.addRenderTarget(probePassRTName, getLoadOpOfClearFlag(probeCamera.clearFlag, AttachmentType.RENDER_TARGET), - StoreOp.STORE, new Color(probeCamera.clearColor.x, probeCamera.clearColor.y, probeCamera.clearColor.z, probeCamera.clearColor.w)); - probePass.addDepthStencil(probePassDSName, getLoadOpOfClearFlag(probeCamera.clearFlag, AttachmentType.DEPTH_STENCIL), - StoreOp.STORE, probeCamera.clearDepth, probeCamera.clearStencil, probeCamera.clearFlag); + probePass.addRenderTarget( + probePassRTName, + getLoadOpOfClearFlag(probeCamera.clearFlag, AttachmentType.RENDER_TARGET), + StoreOp.STORE, + new Color(probeCamera.clearColor.x, probeCamera.clearColor.y, probeCamera.clearColor.z, probeCamera.clearColor.w), + ); + probePass.addDepthStencil( + probePassDSName, + getLoadOpOfClearFlag(probeCamera.clearFlag, AttachmentType.DEPTH_STENCIL), + StoreOp.STORE, + probeCamera.clearDepth, + probeCamera.clearStencil, + probeCamera.clearFlag, + ); const passBuilder = probePass.addQueue(QueueHint.RENDER_OPAQUE); passBuilder.addSceneOfCamera(info.camera, new LightInfo(), SceneFlags.REFLECTION_PROBE); updateCameraUBO(passBuilder as unknown as any, probeCamera, ppl); @@ -380,10 +446,11 @@ export function setupReflectionProbePass (ppl: BasicPipeline, info: CameraInfo): const probes = cclegacy.internal.reflectionProbeManager.getProbes(); if (probes.length === 0) return; for (let i = 0; i < probes.length; i++) { - const probe = probes[i]; + const probe = probes[i] as ReflectionProbe; if (probe.needRender) { if (probes[i].probeType === ProbeType.PLANAR) { - buildReflectProbePass(ppl, info, probe, probe.realtimePlanarTexture.window!, 0); + const window = probe.realtimePlanarTexture!.window!; + buildReflectProbePass(ppl, info, probe, window, 0); } else if (EDITOR) { for (let faceIdx = 0; faceIdx < probe.bakedCubeTextures.length; faceIdx++) { probe.updateCameraDir(faceIdx); @@ -465,8 +532,17 @@ export function setupScenePassTiled (pipeline: BasicPipeline, info: CameraInfo, gBufferPass.addRenderTarget(gBufferPassRTName, AccessType.WRITE, '_', LoadOp.CLEAR, StoreOp.DISCARD, emptyColor); gBufferPass.addRenderTarget(gBufferPassNormal, AccessType.WRITE, '_', LoadOp.CLEAR, StoreOp.DISCARD, emptyColor); gBufferPass.addRenderTarget(gBufferPassEmissive, AccessType.WRITE, '_', LoadOp.CLEAR, StoreOp.DISCARD, emptyColor); - gBufferPass.addDepthStencil(gBufferPassDSName, AccessType.WRITE, '_', '_', - LoadOp.CLEAR, StoreOp.DISCARD, camera.clearDepth, camera.clearStencil, camera.clearFlag); + gBufferPass.addDepthStencil( + gBufferPassDSName, + AccessType.WRITE, + '_', + '_', + LoadOp.CLEAR, + StoreOp.DISCARD, + camera.clearDepth, + camera.clearStencil, + camera.clearFlag, + ); gBufferPass .addQueue(QueueHint.RENDER_OPAQUE, 'gbuffer-tiled') .addSceneOfCamera(camera, new LightInfo(), SceneFlags.OPAQUE_OBJECT | SceneFlags.CUTOUT_OBJECT); @@ -494,7 +570,9 @@ export function setupScenePassTiled (pipeline: BasicPipeline, info: CameraInfo, const deferredLightingPassRTName = `deferredLightingPassRTName${info.id}`; lightingPass.addRenderTarget(deferredLightingPassRTName, AccessType.WRITE, '_', LoadOp.CLEAR, StoreOp.STORE, rtColor); lightingPass.addQueue(QueueHint.RENDER_TRANSPARENT, 'deferred-lighting-tiled').addCameraQuad( - camera, lightingInfo.deferredLightingMaterial, 1, + camera, + lightingInfo.deferredLightingMaterial, + 1, SceneFlags.VOLUMETRIC_LIGHTING, ); return { rtName: deferredLightingPassRTName }; @@ -608,7 +686,9 @@ export function setupLightingPass (pipeline: BasicPipeline, info: CameraInfo, us lightingClearColor.w = 0; lightingPass.addRenderTarget(deferredLightingPassRTName, LoadOp.CLEAR, StoreOp.STORE, lightingClearColor); lightingPass.addQueue(QueueHint.RENDER_TRANSPARENT).addCameraQuad( - camera, lightingInfo.deferredLightingMaterial, 0, + camera, + lightingInfo.deferredLightingMaterial, + 0, SceneFlags.VOLUMETRIC_LIGHTING, ); // lightingPass.addQueue(QueueHint.RENDER_TRANSPARENT).addSceneOfCamera(camera, new LightInfo(), @@ -640,9 +720,11 @@ export function updatePostprocessRes (ppl: BasicPipeline, info: CameraInfo): voi ppl.updateDepthStencil(postprocessPassDS, width, height); } let postInfo: PostInfo; -export function setupPostprocessPass (ppl: BasicPipeline, +export function setupPostprocessPass ( + ppl: BasicPipeline, info: CameraInfo, - inputTex: string): { rtName: string; dsName: string; } { + inputTex: string, +): { rtName: string; dsName: string; } { if (!postInfo) { postInfo = new PostInfo(); } @@ -665,13 +747,29 @@ export function setupPostprocessPass (ppl: BasicPipeline, postClearColor.y = camera.clearColor.y; postClearColor.z = camera.clearColor.z; } - postprocessPass.addRenderTarget(postprocessPassRTName, - getLoadOpOfClearFlag(camera.clearFlag, AttachmentType.RENDER_TARGET), StoreOp.STORE, postClearColor); - postprocessPass.addDepthStencil(postprocessPassDS, + postprocessPass.addRenderTarget( + postprocessPassRTName, + getLoadOpOfClearFlag(camera.clearFlag, AttachmentType.RENDER_TARGET), + + StoreOp.STORE, + + postClearColor, + ); + postprocessPass.addDepthStencil( + postprocessPassDS, getLoadOpOfClearFlag(camera.clearFlag, AttachmentType.DEPTH_STENCIL), - StoreOp.STORE, camera.clearDepth, camera.clearStencil, camera.clearFlag); + StoreOp.STORE, + + camera.clearDepth, + + camera.clearStencil, + + camera.clearFlag, + ); postprocessPass.addQueue(QueueHint.NONE).addCameraQuad( - camera, postInfo.postMaterial, 0, + camera, + postInfo.postMaterial, + 0, SceneFlags.NONE, ); if (getProfilerCamera() === camera) { @@ -680,8 +778,10 @@ export function setupPostprocessPass (ppl: BasicPipeline, return { rtName: postprocessPassRTName, dsName: postprocessPassDS }; } -export function setupUIRes (ppl: BasicPipeline, - info: CameraInfo): void { +export function setupUIRes ( + ppl: BasicPipeline, + info: CameraInfo, +): void { const camera = info.camera; const area = getRenderArea(camera, camera.window.width, camera.window.height); const width = area.width; @@ -693,8 +793,10 @@ export function setupUIRes (ppl: BasicPipeline, ppl.addDepthStencil(dsUIAndProfilerPassDSName, Format.DEPTH_STENCIL, width, height, ResourceResidency.MANAGED); } -export function updateUIRes (ppl: BasicPipeline, - info: CameraInfo): void { +export function updateUIRes ( + ppl: BasicPipeline, + info: CameraInfo, +): void { const camera = info.camera; const area = getRenderArea(camera, camera.window.width, camera.window.height); const width = area.width; @@ -706,8 +808,10 @@ export function updateUIRes (ppl: BasicPipeline, ppl.updateDepthStencil(dsUIAndProfilerPassDSName, width, height); } -export function setupUIPass (ppl: BasicPipeline, - info: CameraInfo): void { +export function setupUIPass ( + ppl: BasicPipeline, + info: CameraInfo, +): void { const camera = info.camera; const area = getRenderArea(camera, camera.window.width, camera.window.height); const width = area.width; @@ -717,14 +821,22 @@ export function setupUIPass (ppl: BasicPipeline, const uiAndProfilerPass = ppl.addRenderPass(width, height, 'default'); uiAndProfilerPass.name = `CameraUIAndProfilerPass${info.id}`; uiAndProfilerPass.setViewport(new Viewport(area.x, area.y, width, height)); - uiAndProfilerPass.addRenderTarget(dsUIAndProfilerPassRTName, + uiAndProfilerPass.addRenderTarget( + dsUIAndProfilerPassRTName, getLoadOpOfClearFlag(camera.clearFlag, AttachmentType.RENDER_TARGET), StoreOp.STORE, - new Color(camera.clearColor.x, camera.clearColor.y, camera.clearColor.z, camera.clearColor.w)); - uiAndProfilerPass.addDepthStencil(dsUIAndProfilerPassDSName, + new Color(camera.clearColor.x, camera.clearColor.y, camera.clearColor.z, camera.clearColor.w), + ); + uiAndProfilerPass.addDepthStencil( + dsUIAndProfilerPassDSName, getLoadOpOfClearFlag(camera.clearFlag, AttachmentType.DEPTH_STENCIL), StoreOp.STORE, - camera.clearDepth, camera.clearStencil, camera.clearFlag); + camera.clearDepth, + + camera.clearStencil, + + camera.clearFlag, + ); const sceneFlags = SceneFlags.UI; uiAndProfilerPass .addQueue(QueueHint.RENDER_TRANSPARENT) diff --git a/cocos/rendering/custom/render-graph.ts b/cocos/rendering/custom/render-graph.ts index fa3f241f839..e2b609289da 100644 --- a/cocos/rendering/custom/render-graph.ts +++ b/cocos/rendering/custom/render-graph.ts @@ -150,6 +150,14 @@ export class ManagedBuffer { fenceValue = 0; } +export class PersistentBuffer { + constructor (buffer: Buffer | null = null) { + this.buffer = buffer; + } + /*refcount*/ buffer: Buffer | null; + fenceValue = 0; +} + export class ManagedTexture { constructor (texture: Texture | null = null) { this.texture = texture; @@ -158,6 +166,14 @@ export class ManagedTexture { fenceValue = 0; } +export class PersistentTexture { + constructor (texture: Texture | null = null) { + this.texture = texture; + } + /*refcount*/ texture: Texture | null; + fenceValue = 0; +} + export class ManagedResource { unused = 0; } @@ -573,8 +589,8 @@ export interface ResourceGraphValueType { [ResourceGraphValue.Managed]: ManagedResource [ResourceGraphValue.ManagedBuffer]: ManagedBuffer [ResourceGraphValue.ManagedTexture]: ManagedTexture - [ResourceGraphValue.PersistentBuffer]: Buffer - [ResourceGraphValue.PersistentTexture]: Texture + [ResourceGraphValue.PersistentBuffer]: PersistentBuffer + [ResourceGraphValue.PersistentTexture]: PersistentTexture [ResourceGraphValue.Framebuffer]: Framebuffer [ResourceGraphValue.Swapchain]: RenderSwapchain [ResourceGraphValue.FormatView]: FormatView @@ -585,8 +601,8 @@ export interface ResourceGraphVisitor { managed(value: ManagedResource): unknown; managedBuffer(value: ManagedBuffer): unknown; managedTexture(value: ManagedTexture): unknown; - persistentBuffer(value: Buffer): unknown; - persistentTexture(value: Texture): unknown; + persistentBuffer(value: PersistentBuffer): unknown; + persistentTexture(value: PersistentTexture): unknown; framebuffer(value: Framebuffer): unknown; swapchain(value: RenderSwapchain): unknown; formatView(value: FormatView): unknown; @@ -601,7 +617,9 @@ export type ResourceGraphObject = ManagedResource | Framebuffer | RenderSwapchain | FormatView -| SubresourceView; +| SubresourceView +| PersistentBuffer +| PersistentTexture; //----------------------------------------------------------------- // Graph Concept @@ -1046,9 +1064,9 @@ export class ResourceGraph implements BidirectionalGraph case ResourceGraphValue.ManagedTexture: return visitor.managedTexture(vert._object as ManagedTexture); case ResourceGraphValue.PersistentBuffer: - return visitor.persistentBuffer(vert._object as Buffer); + return visitor.persistentBuffer(vert._object as PersistentBuffer); case ResourceGraphValue.PersistentTexture: - return visitor.persistentTexture(vert._object as Texture); + return visitor.persistentTexture(vert._object as PersistentTexture); case ResourceGraphValue.Framebuffer: return visitor.framebuffer(vert._object as Framebuffer); case ResourceGraphValue.Swapchain: @@ -1082,16 +1100,16 @@ export class ResourceGraph implements BidirectionalGraph throw Error('value id not match'); } } - getPersistentBuffer (v: number): Buffer { + getPersistentBuffer (v: number): PersistentBuffer { if (this._vertices[v]._id === ResourceGraphValue.PersistentBuffer) { - return this._vertices[v]._object as Buffer; + return this._vertices[v]._object as PersistentBuffer; } else { throw Error('value id not match'); } } - getPersistentTexture (v: number): Texture { + getPersistentTexture (v: number): PersistentTexture { if (this._vertices[v]._id === ResourceGraphValue.PersistentTexture) { - return this._vertices[v]._object as Texture; + return this._vertices[v]._object as PersistentTexture; } else { throw Error('value id not match'); } @@ -1145,16 +1163,16 @@ export class ResourceGraph implements BidirectionalGraph return null; } } - tryGetPersistentBuffer (v: number): Buffer | null { + tryGetPersistentBuffer (v: number): PersistentBuffer | null { if (this._vertices[v]._id === ResourceGraphValue.PersistentBuffer) { - return this._vertices[v]._object as Buffer; + return this._vertices[v]._object as PersistentBuffer; } else { return null; } } - tryGetPersistentTexture (v: number): Texture | null { + tryGetPersistentTexture (v: number): PersistentTexture | null { if (this._vertices[v]._id === ResourceGraphValue.PersistentTexture) { - return this._vertices[v]._object as Texture; + return this._vertices[v]._object as PersistentTexture; } else { return null; } diff --git a/cocos/rendering/custom/web-pipeline.ts b/cocos/rendering/custom/web-pipeline.ts index 5385969849f..9bf641544d9 100644 --- a/cocos/rendering/custom/web-pipeline.ts +++ b/cocos/rendering/custom/web-pipeline.ts @@ -27,8 +27,8 @@ import { systemInfo } from 'pal/system-info'; import { DEBUG } from 'internal:constants'; import { Buffer, DescriptorSetLayout, Device, Feature, Format, FormatFeatureBit, Sampler, Swapchain, Texture, ClearFlagBit, DescriptorSet, deviceManager, Viewport, API, CommandBuffer, Type, SamplerInfo, Filter, Address, DescriptorSetInfo, LoadOp, StoreOp, ShaderStageFlagBit, BufferInfo, TextureInfo, TextureType, UniformBlock, ResolveMode, SampleCount, Color } from '../../gfx'; import { Mat4, Quat, toRadian, Vec2, Vec3, Vec4, assert, macro, cclegacy, IVec4Like, IMat4Like, IVec2Like, Color as CoreColor } from '../../core'; -import { AccessType, AttachmentType, CopyPair, LightInfo, LightingMode, MovePair, QueueHint, ResolvePair, ResourceDimension, ResourceFlags, ResourceResidency, SceneFlags, UpdateFrequency } from './types'; -import { ComputeView, RasterView, Blit, ClearView, ComputePass, CopyPass, Dispatch, ManagedBuffer, ManagedResource, MovePass, RasterPass, RasterSubpass, RenderData, RenderGraph, RenderGraphComponent, RenderGraphValue, RenderQueue, RenderSwapchain, ResourceDesc, ResourceGraph, ResourceGraphValue, ResourceStates, ResourceTraits, SceneData, Subpass } from './render-graph'; +import { AccessType, AttachmentType, CopyPair, LightInfo, LightingMode, MovePair, QueueHint, ResolvePair, ResourceDimension, ResourceFlags, ResourceResidency, SceneFlags, UpdateFrequency, UploadPair } from './types'; +import { ComputeView, RasterView, Blit, ClearView, ComputePass, CopyPass, Dispatch, ManagedBuffer, ManagedResource, MovePass, RasterPass, RasterSubpass, RenderData, RenderGraph, RenderGraphComponent, RenderGraphValue, RenderQueue, RenderSwapchain, ResourceDesc, ResourceGraph, ResourceGraphValue, ResourceStates, ResourceTraits, SceneData, Subpass, PersistentBuffer } from './render-graph'; import { ComputePassBuilder, ComputeQueueBuilder, ComputeSubpassBuilder, BasicPipeline, PipelineBuilder, RenderPassBuilder, RenderQueueBuilder, RenderSubpassBuilder, PipelineType, BasicRenderPassBuilder, PipelineCapabilities, BasicMultisampleRenderPassBuilder } from './pipeline'; import { PipelineSceneData } from '../pipeline-scene-data'; import { Model, Camera, ShadowType, CSMLevel, DirectionalLight, SpotLight, PCFType, Shadows, SphereLight, PointLight, RangedDirectionalLight } from '../../render-scene/scene'; @@ -198,7 +198,7 @@ export class WebSetter { value.fill(0); this._data.constants.set(num, value); } - this.setCurrConstant(block); + this.setCurrConstant(block, stage); return true; } public setMat4 (name: string, mat: Mat4, idx = 0): void { @@ -637,6 +637,13 @@ function setShadowUBOView (setter: WebSetter, camera: Camera | null, layout = 'd } } +function setComputeConstants (setter: WebSetter, layoutName: string): void { + const director = cclegacy.director; + const root = director.root; + const pipeline = root.pipeline as WebPipeline; + setter.addConstant('CCConst', layoutName); +} + function setCameraUBOValues ( setter: WebSetter, camera: Readonly | null, @@ -1339,7 +1346,7 @@ export class WebComputePassBuilder extends WebSetter implements ComputePassBuild throw new Error('Method not implemented.'); } addStorageBuffer (name: string, accessType: AccessType, slotName: string): void { - throw new Error('Method not implemented.'); + this._addComputeResource(name, accessType, slotName); } addStorageImage (name: string, accessType: AccessType, slotName: string): void { throw new Error('Method not implemented.'); @@ -1358,6 +1365,24 @@ export class WebComputePassBuilder extends WebSetter implements ComputePassBuild const queueID = this._renderGraph.addVertex(RenderGraphValue.Queue, queue, '', layoutName, data, false, this._vertID); return new WebComputeQueueBuilder(data, this._renderGraph, this._layoutGraph, queueID, queue, this._pipeline); } + + private _addComputeResource (name: string, accessType: AccessType, slotName: string): void { + const view = new ComputeView(slotName); + view.accessType = accessType; + if (DEBUG) { + assert(Boolean(view.name)); + assert(Boolean(name && this._resourceGraph.contains(name))); + const descriptorName = view.name; + const descriptorID = this._layoutGraph.attributeIndex.get(descriptorName); + assert(descriptorID !== undefined); + } + if (this._pass.computeViews.has(name)) { + this._pass.computeViews.get(name)?.push(view); + } else { + this._pass.computeViews.set(name, [view]); + } + } + private readonly _renderGraph: RenderGraph; private readonly _layoutGraph: LayoutGraphData; private readonly _resourceGraph: ResourceGraph; @@ -1598,6 +1623,30 @@ export class WebPipeline implements BasicPipeline { // TODO: implement resolve pass throw new Error('Method not implemented.'); } + + public addComputePass (passName: string): ComputePassBuilder { + const name = 'Compute'; + const pass = new ComputePass(); + + const data = new RenderData(); + const vertID = this._renderGraph!.addVertex(RenderGraphValue.Compute, pass, name, passName, data, false); + const result = new WebComputePassBuilder(data, this._renderGraph!, this._layoutGraph, this._resourceGraph, vertID, pass, this._pipelineSceneData); + setComputeConstants(result, passName); + initGlobalDescBinding(data, passName); + return result; + } + + public addUploadPass (uploadPairs: UploadPair[]): void { + const name = 'UploadPass'; + const pass = new CopyPass(); + for (const up of uploadPairs) { + pass.uploadPairs.push(up); + } + + const vertID = this._renderGraph!.addVertex(RenderGraphValue.Copy, pass, name, '', new RenderData(), false); + // const result = new WebCopyPassBuilder(this._renderGraph!, vertID, pass); + } + public addCopyPass (copyPairs: CopyPair[]): void { // const renderData = new RenderData(); // const vertID = this._renderGraph!.addVertex( @@ -1626,7 +1675,7 @@ export class WebPipeline implements BasicPipeline { let str = ''; str += `#define CC_DEVICE_SUPPORT_FLOAT_TEXTURE ${this._device.getFormatFeatures(Format.RGBA32F) & (FormatFeatureBit.RENDER_TARGET | FormatFeatureBit.SAMPLED_TEXTURE) ? 1 : 0}\n`; - str += `#define CC_ENABLE_CLUSTERED_LIGHT_CULLING ${clusterEnabled ? 1 : 0}\n`; + // str += `#define CC_ENABLE_CLUSTERED_LIGHT_CULLING ${clusterEnabled ? 1 : 0}\n`; // defined in material str += `#define CC_DEVICE_MAX_VERTEX_UNIFORM_VECTORS ${this._device.capabilities.maxVertexUniformVectors}\n`; str += `#define CC_DEVICE_MAX_FRAGMENT_UNIFORM_VECTORS ${this._device.capabilities.maxFragmentUniformVectors}\n`; str += `#define CC_DEVICE_CAN_BENEFIT_FROM_INPUT_ATTACHMENT ${this._device.hasFeature(Feature.INPUT_ATTACHMENT_BENEFIT) ? 1 : 0}\n`; @@ -1856,11 +1905,22 @@ export class WebPipeline implements BasicPipeline { desc.format = format; desc.flags = ResourceFlags.STORAGE; + if (residency === ResourceResidency.PERSISTENT) { + return this._resourceGraph.addVertex( + ResourceGraphValue.PersistentBuffer, + new PersistentBuffer(), + name, + desc, + new ResourceTraits(ResourceResidency.PERSISTENT), + new ResourceStates(), + new SamplerInfo(), + ); + } + return this._resourceGraph.addVertex( ResourceGraphValue.ManagedBuffer, new ManagedBuffer(), name, - desc, new ResourceTraits(residency), new ResourceStates(), diff --git a/cocos/rendering/custom/web-program-library.ts b/cocos/rendering/custom/web-program-library.ts index 35b9f691675..ba0e5e7ee51 100644 --- a/cocos/rendering/custom/web-program-library.ts +++ b/cocos/rendering/custom/web-program-library.ts @@ -24,7 +24,7 @@ /* eslint-disable max-len */ import { EffectAsset } from '../../asset/assets'; -import { Attribute, DescriptorSetLayout, DescriptorType, DESCRIPTOR_BUFFER_TYPE, DESCRIPTOR_SAMPLER_TYPE, Device, MemoryAccessBit, PipelineLayout, PipelineLayoutInfo, Shader, ShaderInfo, ShaderStage, ShaderStageFlagBit, Type, Uniform, UniformBlock, UniformInputAttachment, UniformSampler, UniformSamplerTexture, UniformStorageBuffer, UniformStorageImage, UniformTexture } from '../../gfx'; +import { Attribute, DescriptorSetLayout, DescriptorType, DESCRIPTOR_BUFFER_TYPE, DESCRIPTOR_SAMPLER_TYPE, Device, MemoryAccessBit, PipelineLayout, PipelineLayoutInfo, Shader, ShaderInfo, ShaderStage, ShaderStageFlagBit, Type, Uniform, UniformBlock, UniformInputAttachment, UniformSampler, UniformSamplerTexture, UniformStorageBuffer, UniformStorageImage, UniformTexture, deviceManager } from '../../gfx'; import { genHandles, getActiveAttributes, getCombinationDefines, getShaderInstanceName, getSize, getVariantKey, populateMacros, prepareDefines } from '../../render-scene/core/program-utils'; import { getDeviceShaderVersion, MacroRecord } from '../../render-scene'; import { IProgramInfo } from '../../render-scene/core/program-lib'; @@ -49,8 +49,89 @@ function makeProgramInfo (effectName: string, shader: EffectAsset.IShaderInfo): return programInfo; } +function findBinding (shaderInfo: ShaderInfo, name: string): { set: number, binding: number } { + for (const v of shaderInfo.blocks) { + if (v.name === name) { + return { set: v.set, binding: v.binding }; + } + } + for (const v of shaderInfo.buffers) { + if (v.name === name) { + return { set: v.set, binding: v.binding }; + } + } + for (const v of shaderInfo.samplerTextures) { + if (v.name === name) { + return { set: v.set, binding: v.binding }; + } + } + for (const v of shaderInfo.samplers) { + if (v.name === name) { + return { set: v.set, binding: v.binding }; + } + } + for (const v of shaderInfo.textures) { + if (v.name === name) { + return { set: v.set, binding: v.binding }; + } + } + for (const v of shaderInfo.images) { + if (v.name === name) { + return { set: v.set, binding: v.binding }; + } + } + for (const v of shaderInfo.subpassInputs) { + if (v.name === name) { + return { set: v.set, binding: v.binding }; + } + } + // eslint-disable-next-line no-console + throw console.error('binding not found in shaderInfo!'); +} + +function overwriteShaderSourceBinding (shaderInfo: ShaderInfo, source: string): string { + let code = source; + const samplerExp = /layout\s*\(([^\)])+\)\s+uniform\s+(\b\w+\b\s+)?sampler(\w+)\s+(\b\w+\b)/g; + let samplerIter = samplerExp.exec(code); + while (samplerIter) { + const name = samplerIter[4]; + const { set, binding } = findBinding(shaderInfo, name); + const precStr = samplerIter[2] ? samplerIter[2] : ''; + const replaceStr = `layout(set = ${set}, binding = ${binding}) uniform ${precStr} sampler${samplerIter[3]} ${samplerIter[4]}`; + code = code.replace(samplerIter[0], replaceStr); + samplerIter = samplerExp.exec(code); + } + const blockExp = /layout\s*\(([^\)])+\)\s*(readonly)?\s*\b(uniform|buffer)\b\s+(\b\w+\b)\s*[{;]/g; + let blockIter = blockExp.exec(code); + while (blockIter) { + const name = blockIter[4]; + const { set, binding } = findBinding(shaderInfo, name); + const accessStr = blockIter[2] ? blockIter[2] : ''; + const replaceStr = `layout(set = ${set}, binding = ${binding}) ${accessStr} ${blockIter[3]} ${blockIter[4]} {`; + code = code.replace(blockIter[0], replaceStr); + blockIter = blockExp.exec(code); + } + return code; +} + +function overwriteShaderProgramBinding (shaderInfo: ShaderInfo, programInfo: IProgramInfo): void { + const version = getDeviceShaderVersion(deviceManager.gfxDevice); + if (version !== 'glsl4') { + return; + } + if (programInfo.glsl4.vert) { + programInfo.glsl4.vert = overwriteShaderSourceBinding(shaderInfo, programInfo.glsl4.vert); + } + if (programInfo.glsl4.frag) { + programInfo.glsl4.frag = overwriteShaderSourceBinding(shaderInfo, programInfo.glsl4.frag); + } if (programInfo.glsl4.compute) { + programInfo.glsl4.compute = overwriteShaderSourceBinding(shaderInfo, programInfo.glsl4.compute); + } +} + // overwrite IProgramInfo using gfx.ShaderInfo function overwriteProgramBlockInfo (shaderInfo: ShaderInfo, programInfo: IProgramInfo): void { + overwriteShaderProgramBinding(shaderInfo, programInfo); const set = _setIndex[UpdateFrequency.PER_BATCH]; for (const block of programInfo.blocks) { let found = false; @@ -74,7 +155,9 @@ function overwriteProgramBlockInfo (shaderInfo: ShaderInfo, programInfo: IProgra function populateGroupedShaderInfo ( layout: DescriptorSetLayoutData, descriptorInfo: EffectAsset.IDescriptorInfo, - set: number, shaderInfo: ShaderInfo, blockSizes: number[], + set: number, + shaderInfo: ShaderInfo, + blockSizes: number[], ): void { for (const descriptorBlock of layout.descriptorBlocks) { const visibility = descriptorBlock.visibility; @@ -88,9 +171,13 @@ function populateGroupedShaderInfo ( } blockSizes.push(getSize(block.members)); shaderInfo.blocks.push( - new UniformBlock(set, binding, block.name, + new UniformBlock( + set, + binding, + block.name, block.members.map((m): Uniform => new Uniform(m.name, m.type, m.count)), - 1), // count is always 1 for UniformBlock + 1, + ), // count is always 1 for UniformBlock ); ++binding; } @@ -103,9 +190,7 @@ function populateGroupedShaderInfo ( if (tex.stageFlags !== visibility) { continue; } - shaderInfo.samplerTextures.push(new UniformSamplerTexture( - set, binding, tex.name, tex.type, tex.count, - )); + shaderInfo.samplerTextures.push(new UniformSamplerTexture(set, binding, tex.name, tex.type, tex.count)); ++binding; } break; @@ -114,9 +199,7 @@ function populateGroupedShaderInfo ( if (sampler.stageFlags !== visibility) { continue; } - shaderInfo.samplers.push(new UniformSampler( - set, binding, sampler.name, sampler.count, - )); + shaderInfo.samplers.push(new UniformSampler(set, binding, sampler.name, sampler.count)); ++binding; } break; @@ -125,9 +208,7 @@ function populateGroupedShaderInfo ( if (texture.stageFlags !== visibility) { continue; } - shaderInfo.textures.push(new UniformTexture( - set, binding, texture.name, texture.type, texture.count, - )); + shaderInfo.textures.push(new UniformTexture(set, binding, texture.name, texture.type, texture.count)); ++binding; } break; @@ -136,9 +217,7 @@ function populateGroupedShaderInfo ( if (buffer.stageFlags !== visibility) { continue; } - shaderInfo.buffers.push(new UniformStorageBuffer( - set, binding, buffer.name, 1, buffer.memoryAccess, - )); // effect compiler guarantees buffer count = 1 + shaderInfo.buffers.push(new UniformStorageBuffer(set, binding, buffer.name, 1, buffer.memoryAccess)); // effect compiler guarantees buffer count = 1 ++binding; } break; @@ -150,9 +229,7 @@ function populateGroupedShaderInfo ( if (image.stageFlags !== visibility) { continue; } - shaderInfo.images.push(new UniformStorageImage( - set, binding, image.name, image.type, image.count, image.memoryAccess, - )); + shaderInfo.images.push(new UniformStorageImage(set, binding, image.name, image.type, image.count, image.memoryAccess)); ++binding; } break; @@ -161,9 +238,7 @@ function populateGroupedShaderInfo ( if (subpassInput.stageFlags !== visibility) { continue; } - shaderInfo.subpassInputs.push(new UniformInputAttachment( - set, subpassInput.binding, subpassInput.name, subpassInput.count, - )); + shaderInfo.subpassInputs.push(new UniformInputAttachment(set, subpassInput.binding, subpassInput.name, subpassInput.count)); ++binding; } break; @@ -173,9 +248,13 @@ function populateGroupedShaderInfo ( } // add merged descriptor to gfx.ShaderInfo -function populateMergedShaderInfo (valueNames: string[], +function populateMergedShaderInfo ( + valueNames: string[], layout: DescriptorSetLayoutData, - set: number, shaderInfo: ShaderInfo, blockSizes: number[]): void { + set: number, + shaderInfo: ShaderInfo, + blockSizes: number[], +): void { for (const descriptorBlock of layout.descriptorBlocks) { let binding = descriptorBlock.offset; switch (descriptorBlock.type) { @@ -188,9 +267,13 @@ function populateMergedShaderInfo (valueNames: string[], } blockSizes.push(getSize(uniformBlock.members)); shaderInfo.blocks.push( - new UniformBlock(set, binding, valueNames[block.descriptorID], + new UniformBlock( + set, + binding, + valueNames[block.descriptorID], uniformBlock.members.map((m): Uniform => new Uniform(m.name, m.type, m.count)), - 1), // count is always 1 for UniformBlock + 1, + ), // count is always 1 for UniformBlock ); ++binding; } @@ -203,32 +286,29 @@ function populateMergedShaderInfo (valueNames: string[], break; case DescriptorTypeOrder.SAMPLER_TEXTURE: for (const tex of descriptorBlock.descriptors) { - shaderInfo.samplerTextures.push(new UniformSamplerTexture( - set, binding, valueNames[tex.descriptorID], tex.type, tex.count, - )); + shaderInfo.samplerTextures.push(new UniformSamplerTexture(set, binding, valueNames[tex.descriptorID], tex.type, tex.count)); ++binding; } break; case DescriptorTypeOrder.SAMPLER: for (const sampler of descriptorBlock.descriptors) { - shaderInfo.samplers.push(new UniformSampler( - set, binding, valueNames[sampler.descriptorID], sampler.count, - )); + shaderInfo.samplers.push(new UniformSampler(set, binding, valueNames[sampler.descriptorID], sampler.count)); ++binding; } break; case DescriptorTypeOrder.TEXTURE: for (const texture of descriptorBlock.descriptors) { - shaderInfo.textures.push(new UniformTexture( - set, binding, valueNames[texture.descriptorID], texture.type, texture.count, - )); + shaderInfo.textures.push(new UniformTexture(set, binding, valueNames[texture.descriptorID], texture.type, texture.count)); ++binding; } break; case DescriptorTypeOrder.STORAGE_BUFFER: for (const buffer of descriptorBlock.descriptors) { shaderInfo.buffers.push(new UniformStorageBuffer( - set, binding, valueNames[buffer.descriptorID], 1, + set, + binding, + valueNames[buffer.descriptorID], + 1, MemoryAccessBit.READ_WRITE/*buffer.memoryAccess*/, )); // effect compiler guarantees buffer count = 1 ++binding; @@ -240,7 +320,11 @@ function populateMergedShaderInfo (valueNames: string[], case DescriptorTypeOrder.STORAGE_IMAGE: for (const image of descriptorBlock.descriptors) { shaderInfo.images.push(new UniformStorageImage( - set, binding, valueNames[image.descriptorID], image.type, image.count, + set, + binding, + valueNames[image.descriptorID], + image.type, + image.count, MemoryAccessBit.READ_WRITE/*image.memoryAccess*/, )); ++binding; @@ -248,9 +332,7 @@ function populateMergedShaderInfo (valueNames: string[], break; case DescriptorTypeOrder.INPUT_ATTACHMENT: for (const subpassInput of descriptorBlock.descriptors) { - shaderInfo.subpassInputs.push(new UniformInputAttachment( - set, binding, valueNames[subpassInput.descriptorID], subpassInput.count, - )); + shaderInfo.subpassInputs.push(new UniformInputAttachment(set, binding, valueNames[subpassInput.descriptorID], subpassInput.count)); ++binding; } break; @@ -262,56 +344,53 @@ function populateMergedShaderInfo (valueNames: string[], // add descriptor from effect to gfx.ShaderInfo function populateShaderInfo ( descriptorInfo: EffectAsset.IDescriptorInfo, - set: number, shaderInfo: ShaderInfo, blockSizes: number[], + set: number, + shaderInfo: ShaderInfo, + blockSizes: number[], ): void { for (let i = 0; i < descriptorInfo.blocks.length; i++) { const block = descriptorInfo.blocks[i]; blockSizes.push(getSize(block.members)); - shaderInfo.blocks.push(new UniformBlock(set, block.binding, block.name, - block.members.map((m): Uniform => new Uniform(m.name, m.type, m.count)), 1)); // effect compiler guarantees block count = 1 + shaderInfo.blocks.push(new UniformBlock( + set, + block.binding, + block.name, + block.members.map((m): Uniform => new Uniform(m.name, m.type, m.count)), + 1, + )); // effect compiler guarantees block count = 1 } for (let i = 0; i < descriptorInfo.samplerTextures.length; i++) { const samplerTexture = descriptorInfo.samplerTextures[i]; - shaderInfo.samplerTextures.push(new UniformSamplerTexture( - set, samplerTexture.binding, samplerTexture.name, samplerTexture.type, samplerTexture.count, - )); + shaderInfo.samplerTextures.push(new UniformSamplerTexture(set, samplerTexture.binding, samplerTexture.name, samplerTexture.type, samplerTexture.count)); } for (let i = 0; i < descriptorInfo.samplers.length; i++) { const sampler = descriptorInfo.samplers[i]; - shaderInfo.samplers.push(new UniformSampler( - set, sampler.binding, sampler.name, sampler.count, - )); + shaderInfo.samplers.push(new UniformSampler(set, sampler.binding, sampler.name, sampler.count)); } for (let i = 0; i < descriptorInfo.textures.length; i++) { const texture = descriptorInfo.textures[i]; - shaderInfo.textures.push(new UniformTexture( - set, texture.binding, texture.name, texture.type, texture.count, - )); + shaderInfo.textures.push(new UniformTexture(set, texture.binding, texture.name, texture.type, texture.count)); } for (let i = 0; i < descriptorInfo.buffers.length; i++) { const buffer = descriptorInfo.buffers[i]; - shaderInfo.buffers.push(new UniformStorageBuffer( - set, buffer.binding, buffer.name, 1, buffer.memoryAccess, - )); // effect compiler guarantees buffer count = 1 + shaderInfo.buffers.push(new UniformStorageBuffer(set, buffer.binding, buffer.name, 1, buffer.memoryAccess)); // effect compiler guarantees buffer count = 1 } for (let i = 0; i < descriptorInfo.images.length; i++) { const image = descriptorInfo.images[i]; - shaderInfo.images.push(new UniformStorageImage( - set, image.binding, image.name, image.type, image.count, image.memoryAccess, - )); + shaderInfo.images.push(new UniformStorageImage(set, image.binding, image.name, image.type, image.count, image.memoryAccess)); } for (let i = 0; i < descriptorInfo.subpassInputs.length; i++) { const subpassInput = descriptorInfo.subpassInputs[i]; - shaderInfo.subpassInputs.push(new UniformInputAttachment( - set, subpassInput.binding, subpassInput.name, subpassInput.count, - )); + shaderInfo.subpassInputs.push(new UniformInputAttachment(set, subpassInput.binding, subpassInput.name, subpassInput.count)); } } // add fixed local descriptors to gfx.ShaderInfo function populateLocalShaderInfo ( target: EffectAsset.IDescriptorInfo, - source: IDescriptorSetLayoutInfo, shaderInfo: ShaderInfo, blockSizes: number[], + source: IDescriptorSetLayoutInfo, + shaderInfo: ShaderInfo, + blockSizes: number[], ): void { const set = _setIndex[UpdateFrequency.PER_INSTANCE]; for (let i = 0; i < target.blocks.length; i++) { @@ -323,8 +402,13 @@ function populateLocalShaderInfo ( continue; } blockSizes.push(getSize(block.members)); - shaderInfo.blocks.push(new UniformBlock(set, binding.binding, block.name, - block.members.map((m): Uniform => new Uniform(m.name, m.type, m.count)), 1)); // effect compiler guarantees block count = 1 + shaderInfo.blocks.push(new UniformBlock( + set, + binding.binding, + block.name, + block.members.map((m): Uniform => new Uniform(m.name, m.type, m.count)), + 1, + )); // effect compiler guarantees block count = 1 } for (let i = 0; i < target.samplerTextures.length; i++) { const samplerTexture = target.samplerTextures[i]; @@ -334,9 +418,7 @@ function populateLocalShaderInfo ( console.warn(`builtin samplerTexture '${samplerTexture.name}' not available!`); continue; } - shaderInfo.samplerTextures.push(new UniformSamplerTexture( - set, binding.binding, samplerTexture.name, samplerTexture.type, samplerTexture.count, - )); + shaderInfo.samplerTextures.push(new UniformSamplerTexture(set, binding.binding, samplerTexture.name, samplerTexture.type, samplerTexture.count)); } } @@ -362,21 +444,25 @@ function getIDescriptorSetLayoutInfoSamplerTextureCapacity (info: IDescriptorSet return capacity; } -function setFlattenedUniformBlockBinding (setOffsets: number[], - descriptors: UniformBlock[]): void { +function setFlattenedUniformBlockBinding ( + setOffsets: number[], + descriptors: UniformBlock[], +): void { for (const d of descriptors) { d.flattened = setOffsets[d.set] + d.binding; } } -function setFlattenedSamplerTextureBinding (setOffsets: number[], +function setFlattenedSamplerTextureBinding ( + setOffsets: number[], uniformBlockCapacities: number[], descriptors: UniformSamplerTexture[] | UniformSampler[] | UniformTexture[] | UniformStorageBuffer[] | UniformStorageImage[] - | UniformInputAttachment[]): void { + | UniformInputAttachment[], +): void { for (const d of descriptors) { d.flattened = setOffsets[d.set] + d.binding - uniformBlockCapacities[d.set]; } @@ -463,16 +549,26 @@ function makeShaderInfo ( const passLayout = passLayouts.descriptorSets.get(UpdateFrequency.PER_PASS); if (passLayout) { descriptorSets[UpdateFrequency.PER_PASS] = passLayout.descriptorSetLayoutData; - populateMergedShaderInfo(lg.valueNames, passLayout.descriptorSetLayoutData, - _setIndex[UpdateFrequency.PER_PASS], shaderInfo, blockSizes); + populateMergedShaderInfo( + lg.valueNames, + passLayout.descriptorSetLayoutData, + _setIndex[UpdateFrequency.PER_PASS], + shaderInfo, + blockSizes, + ); } } { // phase const phaseLayout = phaseLayouts.descriptorSets.get(UpdateFrequency.PER_PHASE); if (phaseLayout) { descriptorSets[UpdateFrequency.PER_PHASE] = phaseLayout.descriptorSetLayoutData; - populateMergedShaderInfo(lg.valueNames, phaseLayout.descriptorSetLayoutData, - _setIndex[UpdateFrequency.PER_PHASE], shaderInfo, blockSizes); + populateMergedShaderInfo( + lg.valueNames, + phaseLayout.descriptorSetLayoutData, + _setIndex[UpdateFrequency.PER_PHASE], + shaderInfo, + blockSizes, + ); } } { // batch @@ -481,16 +577,27 @@ function makeShaderInfo ( const perBatch = programData.layout.descriptorSets.get(UpdateFrequency.PER_BATCH); if (perBatch) { descriptorSets[UpdateFrequency.PER_BATCH] = perBatch.descriptorSetLayoutData; - populateMergedShaderInfo(lg.valueNames, perBatch.descriptorSetLayoutData, - _setIndex[UpdateFrequency.PER_BATCH], shaderInfo, blockSizes); + populateMergedShaderInfo( + lg.valueNames, + perBatch.descriptorSetLayoutData, + _setIndex[UpdateFrequency.PER_BATCH], + shaderInfo, + blockSizes, + ); } } else { const batchLayout = phaseLayouts.descriptorSets.get(UpdateFrequency.PER_BATCH); if (batchLayout) { descriptorSets[UpdateFrequency.PER_BATCH] = batchLayout.descriptorSetLayoutData; - populateGroupedShaderInfo(batchLayout.descriptorSetLayoutData, - batchInfo, _setIndex[UpdateFrequency.PER_BATCH], - shaderInfo, blockSizes); + populateGroupedShaderInfo( + batchLayout.descriptorSetLayoutData, + batchInfo, + + _setIndex[UpdateFrequency.PER_BATCH], + shaderInfo, + + blockSizes, + ); } } } @@ -504,17 +611,28 @@ function makeShaderInfo ( const perInstance = programData.layout.descriptorSets.get(UpdateFrequency.PER_INSTANCE); if (perInstance) { descriptorSets[UpdateFrequency.PER_INSTANCE] = perInstance.descriptorSetLayoutData; - populateMergedShaderInfo(lg.valueNames, perInstance.descriptorSetLayoutData, - _setIndex[UpdateFrequency.PER_INSTANCE], shaderInfo, blockSizes); + populateMergedShaderInfo( + lg.valueNames, + perInstance.descriptorSetLayoutData, + _setIndex[UpdateFrequency.PER_INSTANCE], + shaderInfo, + blockSizes, + ); } } } else { const instanceLayout = phaseLayouts.descriptorSets.get(UpdateFrequency.PER_INSTANCE); if (instanceLayout) { descriptorSets[UpdateFrequency.PER_INSTANCE] = instanceLayout.descriptorSetLayoutData; - populateGroupedShaderInfo(instanceLayout.descriptorSetLayoutData, - instanceInfo, _setIndex[UpdateFrequency.PER_INSTANCE], - shaderInfo, blockSizes); + populateGroupedShaderInfo( + instanceLayout.descriptorSetLayoutData, + instanceInfo, + + _setIndex[UpdateFrequency.PER_INSTANCE], + shaderInfo, + + blockSizes, + ); } } } @@ -554,8 +672,10 @@ function getDescriptorNameAndType (source: IDescriptorSetLayoutInfo, binding: nu } // make DescriptorSetLayoutData from local descriptor set info -function makeLocalDescriptorSetLayoutData (lg: LayoutGraphData, - source: IDescriptorSetLayoutInfo): DescriptorSetLayoutData { +function makeLocalDescriptorSetLayoutData ( + lg: LayoutGraphData, + source: IDescriptorSetLayoutInfo, +): DescriptorSetLayoutData { const data = new DescriptorSetLayoutData(); for (const b of source.bindings) { const [name, type] = getDescriptorNameAndType(source, b.binding); @@ -582,24 +702,32 @@ function makeLocalDescriptorSetLayoutData (lg: LayoutGraphData, function buildProgramData ( programName: string, srcShaderInfo: EffectAsset.IShaderInfo, - lg: LayoutGraphData, phase: RenderPhaseData, programData: ShaderProgramData, + lg: LayoutGraphData, + phase: RenderPhaseData, + programData: ShaderProgramData, fixedLocal: boolean, ): void { { - const perBatch = makeDescriptorSetLayoutData(lg, + const perBatch = makeDescriptorSetLayoutData( + lg, UpdateFrequency.PER_BATCH, _setIndex[UpdateFrequency.PER_BATCH], - srcShaderInfo.descriptors[UpdateFrequency.PER_BATCH]); + srcShaderInfo.descriptors[UpdateFrequency.PER_BATCH], + ); const setData = new DescriptorSetData(perBatch); - initializeDescriptorSetLayoutInfo(setData.descriptorSetLayoutData, - setData.descriptorSetLayoutInfo); + initializeDescriptorSetLayoutInfo( + setData.descriptorSetLayoutData, + setData.descriptorSetLayoutInfo, + ); programData.layout.descriptorSets.set(UpdateFrequency.PER_BATCH, setData); } if (fixedLocal) { const perInstance = makeLocalDescriptorSetLayoutData(lg, localDescriptorSetLayout); const setData = new DescriptorSetData(perInstance); - initializeDescriptorSetLayoutInfo(setData.descriptorSetLayoutData, - setData.descriptorSetLayoutInfo); + initializeDescriptorSetLayoutInfo( + setData.descriptorSetLayoutData, + setData.descriptorSetLayoutInfo, + ); if (localDescriptorSetLayout.bindings.length !== setData.descriptorSetLayoutInfo.bindings.length) { console.error('local descriptor set layout inconsistent'); } else { @@ -616,13 +744,17 @@ function buildProgramData ( } programData.layout.descriptorSets.set(UpdateFrequency.PER_INSTANCE, setData); } else { - const perInstance = makeDescriptorSetLayoutData(lg, + const perInstance = makeDescriptorSetLayoutData( + lg, UpdateFrequency.PER_INSTANCE, _setIndex[UpdateFrequency.PER_INSTANCE], - srcShaderInfo.descriptors[UpdateFrequency.PER_INSTANCE]); + srcShaderInfo.descriptors[UpdateFrequency.PER_INSTANCE], + ); const setData = new DescriptorSetData(perInstance); - initializeDescriptorSetLayoutInfo(setData.descriptorSetLayoutData, - setData.descriptorSetLayoutInfo); + initializeDescriptorSetLayoutInfo( + setData.descriptorSetLayoutData, + setData.descriptorSetLayoutInfo, + ); programData.layout.descriptorSets.set(UpdateFrequency.PER_INSTANCE, setData); } const shaderID = phase.shaderPrograms.length; @@ -631,9 +763,13 @@ function buildProgramData ( } // get or create PerProgram gfx.DescriptorSetLayout -function getOrCreateProgramDescriptorSetLayout (device: Device, - lg: LayoutGraphData, phaseID: number, - programName: string, rate: UpdateFrequency): DescriptorSetLayout { +function getOrCreateProgramDescriptorSetLayout ( + device: Device, + lg: LayoutGraphData, + phaseID: number, + programName: string, + rate: UpdateFrequency, +): DescriptorSetLayout { assert(rate < UpdateFrequency.PER_PHASE); const phase = lg.getRenderPhase(phaseID); const programID = phase.shaderIndex.get(programName); @@ -654,9 +790,13 @@ function getOrCreateProgramDescriptorSetLayout (device: Device, } // get PerProgram gfx.DescriptorSetLayout -function getProgramDescriptorSetLayout (device: Device, - lg: LayoutGraphData, phaseID: number, - programName: string, rate: UpdateFrequency): DescriptorSetLayout | null { +function getProgramDescriptorSetLayout ( + device: Device, + lg: LayoutGraphData, + phaseID: number, + programName: string, + rate: UpdateFrequency, +): DescriptorSetLayout | null { assert(rate < UpdateFrequency.PER_PHASE); const phase = lg.getRenderPhase(phaseID); const programID = phase.shaderIndex.get(programName); @@ -677,8 +817,11 @@ function getProgramDescriptorSetLayout (device: Device, } // find shader program in LayoutGraphData -function getEffectShader (lg: LayoutGraphData, effect: EffectAsset, - pass: EffectAsset.IPassInfo): [number, number, number, EffectAsset.IShaderInfo | null, number] { +function getEffectShader ( + lg: LayoutGraphData, + effect: EffectAsset, + pass: EffectAsset.IPassInfo, +): [number, number, number, EffectAsset.IShaderInfo | null, number] { const programName = pass.program; const passID = getCustomPassID(lg, pass.pass); if (passID === INVALID_ID) { @@ -766,8 +909,14 @@ export class WebProgramLibrary implements ProgramLibrary { } // shaderInfo and blockSizes - const [shaderInfo, blockSizes] = makeShaderInfo(lg, passLayout, phaseLayout, - srcShaderInfo, programData, this.fixedLocal); + const [shaderInfo, blockSizes] = makeShaderInfo( + lg, + passLayout, + phaseLayout, + srcShaderInfo, + programData, + this.fixedLocal, + ); // overwrite programInfo overwriteProgramBlockInfo(shaderInfo, programInfo); @@ -777,9 +926,7 @@ export class WebProgramLibrary implements ProgramLibrary { // attributes const attributes = new Array(); for (const attr of programInfo.attributes) { - attributes.push(new Attribute( - attr.name, attr.format, attr.isNormalized, 0, attr.isInstanced, attr.location, - )); + attributes.push(new Attribute(attr.name, attr.format, attr.isNormalized, 0, attr.isInstanced, attr.location)); } // create programInfo const info = new ProgramInfo(programInfo, shaderInfo, attributes, blockSizes, handleMap); @@ -805,9 +952,7 @@ export class WebProgramLibrary implements ProgramLibrary { } const defines = getCombinationDefines(combination); defines.forEach( - (defines) => this.getProgramVariant( - device, phaseID, programName, defines, - ), + (defines) => this.getProgramVariant(device, phaseID, programName, defines), ); } } @@ -887,8 +1032,14 @@ export class WebProgramLibrary implements ProgramLibrary { // prepare shader info const shaderInfo = info.shaderInfo; - shaderInfo.stages[0].source = prefix + src.vert; - shaderInfo.stages[1].source = prefix + src.frag; + if (src.compute) { + shaderInfo.stages[0].source = prefix + src.compute; + shaderInfo.stages[0].stage = ShaderStageFlagBit.COMPUTE; + shaderInfo.stages.length = 1; + } else { + shaderInfo.stages[0].source = prefix + src.vert; + shaderInfo.stages[1].source = prefix + src.frag; + } shaderInfo.attributes = getActiveAttributes(programInfo, info.attributes, defines); shaderInfo.name = getShaderInstanceName(name, macroArray); @@ -909,8 +1060,13 @@ export class WebProgramLibrary implements ProgramLibrary { const subpassOrPassID = this.layoutGraph.getParent(phaseID); return getOrCreateDescriptorSetLayout(this.layoutGraph, subpassOrPassID, phaseID, UpdateFrequency.PER_BATCH); } - return getOrCreateProgramDescriptorSetLayout(device, this.layoutGraph, - phaseID, programName, UpdateFrequency.PER_BATCH); + return getOrCreateProgramDescriptorSetLayout( + device, + this.layoutGraph, + phaseID, + programName, + UpdateFrequency.PER_BATCH, + ); } // get local descriptor set layout getLocalDescriptorSetLayout (device: Device, phaseID: number, programName: string): DescriptorSetLayout { @@ -919,8 +1075,13 @@ export class WebProgramLibrary implements ProgramLibrary { const subpassOrPassID = this.layoutGraph.getParent(phaseID); return getOrCreateDescriptorSetLayout(this.layoutGraph, subpassOrPassID, phaseID, UpdateFrequency.PER_INSTANCE); } - return getOrCreateProgramDescriptorSetLayout(device, this.layoutGraph, - phaseID, programName, UpdateFrequency.PER_INSTANCE); + return getOrCreateProgramDescriptorSetLayout( + device, + this.layoutGraph, + phaseID, + programName, + UpdateFrequency.PER_INSTANCE, + ); } // get related uniform block sizes getBlockSizes (phaseID: number, programName: string): number[] { diff --git a/cocos/rendering/pipeline-ubo.ts b/cocos/rendering/pipeline-ubo.ts index ae7c9486490..e1888383a30 100644 --- a/cocos/rendering/pipeline-ubo.ts +++ b/cocos/rendering/pipeline-ubo.ts @@ -76,19 +76,21 @@ export class PipelineUBO { } const debugView = root.debugView; - fv[UBOGlobal.DEBUG_VIEW_MODE_OFFSET] = debugView.singleMode as number; - - for (let i = 1; i <= 3; i++) { + for (let i = 0; i <= 3; i++) { fv[UBOGlobal.DEBUG_VIEW_MODE_OFFSET + i] = 0.0; } - for (let i = DebugViewCompositeType.DIRECT_DIFFUSE as number; i < (DebugViewCompositeType.MAX_BIT_COUNT as unknown as number); i++) { - const offset = i >> 3; - const bit = i % 8; - fv[UBOGlobal.DEBUG_VIEW_MODE_OFFSET + 1 + offset] += (debugView.isCompositeModeEnabled(i) ? 1.0 : 0.0) * (10.0 ** bit); - } + if (debugView.isEnabled()) { + fv[UBOGlobal.DEBUG_VIEW_MODE_OFFSET] = debugView.singleMode as number; + + for (let i = DebugViewCompositeType.DIRECT_DIFFUSE as number; i < (DebugViewCompositeType.MAX_BIT_COUNT as unknown as number); i++) { + const offset = i >> 3; + const bit = i % 8; + fv[UBOGlobal.DEBUG_VIEW_MODE_OFFSET + 1 + offset] += (debugView.isCompositeModeEnabled(i) ? 1.0 : 0.0) * (10.0 ** bit); + } - fv[UBOGlobal.DEBUG_VIEW_MODE_OFFSET + 3] += (debugView.lightingWithAlbedo ? 1.0 : 0.0) * (10.0 ** 6.0); - fv[UBOGlobal.DEBUG_VIEW_MODE_OFFSET + 3] += (debugView.csmLayerColoration ? 1.0 : 0.0) * (10.0 ** 7.0); + fv[UBOGlobal.DEBUG_VIEW_MODE_OFFSET + 3] += (debugView.lightingWithAlbedo ? 1.0 : 0.0) * (10.0 ** 6.0); + fv[UBOGlobal.DEBUG_VIEW_MODE_OFFSET + 3] += (debugView.csmLayerColoration ? 1.0 : 0.0) * (10.0 ** 7.0); + } } public static updateCameraUBOView ( @@ -567,6 +569,6 @@ export class PipelineUBO { } } - public destroy (): void { - } + // eslint-disable-next-line @typescript-eslint/no-empty-function + public destroy (): void {} } diff --git a/cocos/rendering/post-process/passes/forward-pass.ts b/cocos/rendering/post-process/passes/forward-pass.ts index fb73550de00..4fa9f366640 100644 --- a/cocos/rendering/post-process/passes/forward-pass.ts +++ b/cocos/rendering/post-process/passes/forward-pass.ts @@ -17,10 +17,11 @@ export class ForwardPass extends BasePass { depthBufferShadingScale = 1; calcDepthSlot (camera: Camera): void { - let canUsePrevDepth = !!passContext.depthSlotName; - canUsePrevDepth = !(camera.clearFlag & ClearFlagBit.DEPTH_STENCIL); + const depthSlotName = !!passContext.depthSlotName; + let canUsePrevDepth = !(camera.clearFlag & ClearFlagBit.DEPTH_STENCIL); canUsePrevDepth = canUsePrevDepth && passContext.shadingScale === this.depthBufferShadingScale; if (canUsePrevDepth) { + if (!depthSlotName) passContext.depthSlotName = super.slotName(camera, 1); return; } this.depthBufferShadingScale = passContext.shadingScale; diff --git a/cocos/rendering/post-process/post-process-builder.ts b/cocos/rendering/post-process/post-process-builder.ts index fa656a459b1..eb56eb6baa1 100644 --- a/cocos/rendering/post-process/post-process-builder.ts +++ b/cocos/rendering/post-process/post-process-builder.ts @@ -237,7 +237,9 @@ export class PostProcessBuilder implements PipelineBuilder { } if (pass.name === 'BloomPass') { - (pass as BloomPass).hdrInputName = floatOutputPass.getHDRInputName(); + // for override post-process builder + (pass as BloomPass).hdrInputName = (floatOutputPass === undefined || floatOutputPass === null) + ? '' : floatOutputPass.getHDRInputName(); } pass.lastPass = lastPass; diff --git a/cocos/rendering/render-additive-light-queue.ts b/cocos/rendering/render-additive-light-queue.ts index ce569f7260e..0b6534f7ccc 100644 --- a/cocos/rendering/render-additive-light-queue.ts +++ b/cocos/rendering/render-additive-light-queue.ts @@ -27,7 +27,7 @@ import { Model } from '../render-scene/scene/model'; import { PipelineStateManager } from './pipeline-state-manager'; import { Vec3, nextPow2, Mat4, Color, Pool, geometry, cclegacy } from '../core'; import { Device, RenderPass, Buffer, BufferUsageBit, MemoryUsageBit, - BufferInfo, BufferViewInfo, CommandBuffer } from '../gfx'; + BufferInfo, BufferViewInfo, CommandBuffer, deviceManager } from '../gfx'; import { RenderInstancedQueue } from './render-instanced-queue'; import { SphereLight } from '../render-scene/scene/sphere-light'; import { SpotLight } from '../render-scene/scene/spot-light'; @@ -170,7 +170,7 @@ export class RenderAdditiveLightQueue { const keys = descriptorSetMap.keys; for (let i = 0; i < keys.length; i++) { - const key = keys[i]; + const key = keys[i] as Light; const descriptorSet = descriptorSetMap.get(key)!; if (descriptorSet) { const binding = isEnableEffect() ? getDescBindingFromName('CCShadow') : UBOShadow.BINDING; @@ -183,7 +183,7 @@ export class RenderAdditiveLightQueue { } } - private _bindForwardAddLight (validPunctualLights, passLayout = 'default'): void { + private _bindForwardAddLight(validPunctualLights: Light[], passLayout = 'default'): void { const renderObjects = this._pipeline.pipelineSceneData.renderObjects; for (let i = 0; i < renderObjects.length; i++) { const ro = renderObjects[i]; @@ -490,7 +490,7 @@ export class RenderAdditiveLightQueue { this._lightBuffer.resize(this._lightBufferStride * this._lightBufferCount); this._lightBufferData = new Float32Array(this._lightBufferElementCount * this._lightBufferCount); - this._firstLightBufferView.initialize(new BufferViewInfo(this._lightBuffer, 0, UBOForwardLight.SIZE)); + this._firstLightBufferView = deviceManager.gfxDevice.createBuffer(new BufferViewInfo(this._lightBuffer, 0, UBOForwardLight.SIZE)); } for (let l = 0, offset = 0; l < validPunctualLights.length; l++, offset += this._lightBufferElementCount) { diff --git a/cocos/scene-graph/node-event.ts b/cocos/scene-graph/node-event.ts index 9d18185c689..901862c301c 100644 --- a/cocos/scene-graph/node-event.ts +++ b/cocos/scene-graph/node-event.ts @@ -245,11 +245,19 @@ export enum NodeEventType { LAYER_CHANGED = 'layer-changed', /** - * @en The event type for node's sibling order changed. - * @zh 当节点在兄弟节点中的顺序发生变化时触发的事件。 + * @en This event indicates that the order of child nodes has been changed. + * @zh 该事件表示子节点的排序发生了改变。 + * @deprecated since v3.8.2 @en Please use `CHILDREN_ORDER_CHANGED`. @zh 请使用 `CHILDREN_ORDER_CHANGED`。 */ SIBLING_ORDER_CHANGED = 'sibling-order-changed', + /** + * @en This event indicates that the order of child nodes has been changed. + * @zh 该事件表示子节点的排序发生了改变。 + */ + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values + CHILDREN_ORDER_CHANGED = 'sibling-order-changed', + /** * @en * Note: This event is only emitted from the top most node whose active value did changed, diff --git a/cocos/scene-graph/node.jsb.ts b/cocos/scene-graph/node.jsb.ts index ed2e4274521..a9e05315986 100644 --- a/cocos/scene-graph/node.jsb.ts +++ b/cocos/scene-graph/node.jsb.ts @@ -280,7 +280,7 @@ nodeProto.on = function (type, callback, target, useCapture: any = false) { this._registeredNodeEventTypeMask |= REGISTERED_EVENT_MASK_LAYER_CHANGED; } break; - case NodeEventType.SIBLING_ORDER_CHANGED: + case NodeEventType.CHILDREN_ORDER_CHANGED: if (!(this._registeredNodeEventTypeMask & REGISTERED_EVENT_MASK_SIBLING_ORDER_CHANGED)) { this._registerOnSiblingOrderChanged(); this._registeredNodeEventTypeMask |= REGISTERED_EVENT_MASK_SIBLING_ORDER_CHANGED; @@ -492,7 +492,7 @@ nodeProto.destroyAllChildren = function destroyAllChildren() { }; nodeProto._onSiblingOrderChanged = function () { - this.emit(NodeEventType.SIBLING_ORDER_CHANGED); + this.emit(NodeEventType.CHILDREN_ORDER_CHANGED); }; nodeProto._onActivateNode = function (shouldActiveNow) { diff --git a/cocos/scene-graph/node.ts b/cocos/scene-graph/node.ts index c1724a27da3..294c8d242fa 100644 --- a/cocos/scene-graph/node.ts +++ b/cocos/scene-graph/node.ts @@ -1309,7 +1309,7 @@ export class Node extends CCObject implements ISchedulable, CustomSerializable { this._children[i]._siblingIndex = i; } - this.emit(NodeEventType.SIBLING_ORDER_CHANGED); + this.emit(NodeEventType.CHILDREN_ORDER_CHANGED); } protected _instantiate (cloned, isSyncedNode): any { diff --git a/cocos/spine/lib/spine-define.ts b/cocos/spine/lib/spine-define.ts index 28d53f939f2..f267bf1c1fe 100644 --- a/cocos/spine/lib/spine-define.ts +++ b/cocos/spine/lib/spine-define.ts @@ -524,7 +524,6 @@ function overrideProperty_VertexAttachment (): void { proto: prototype, property: 'id', getter: prototype.getId, - setter: prototype.setId, }, { proto: prototype, @@ -727,15 +726,12 @@ function overrideProperty_RegionAttachment (): void { property: 'rendererObject', getter: prototype.getRendererObject, }, - { - proto: prototype, - property: 'offset', - getter: prototype.getOffset, - }, ]; propertyPolyfills.forEach((prop): void => { js.getset(prop.proto, prop.property, prop.getter, prop.setter); }); + + overrideDefineArrayProp(prototype, prototype.getOffset, 'offset'); overrideDefineArrayPropGetSet(prototype, prototype.getUVs, prototype.setUVs, spine.wasmUtil.wasm.VectorFloat, 'uvs'); } @@ -766,6 +762,11 @@ function overrideProperty_SlotData (): void { property: 'index', getter: prototype.getIndex, }, + { + proto: prototype, + property: 'boneData', + getter: prototype.getBoneData, + }, { proto: prototype, property: 'name', @@ -791,8 +792,6 @@ function overrideProperty_SlotData (): void { propertyPolyfills.forEach((prop): void => { js.getset(prop.proto, prop.property, prop.getter, prop.setter); }); - overrideDefineArrayProp(prototype, prototype.getBoneData, 'boneData'); - overrideDefineArrayProp(prototype, prototype.getDeform, 'deform'); } function overrideProperty_IkConstraint (): void { @@ -1226,13 +1225,14 @@ function overrideProperty_Slot (): void { }, { proto: prototype, - property: 'deform', - getter: prototype.getDeform, + property: 'skeleton', + getter: prototype.getSkeleton, }, ]; propertyPolyfills.forEach((prop): void => { js.getset(prop.proto, prop.property, prop.getter); }); + overrideDefineArrayProp(prototype, prototype.getDeform, 'deform'); } function overrideProperty_Skin (): void { diff --git a/cocos/ui/editbox/edit-box-impl-base.ts b/cocos/ui/editbox/edit-box-impl-base.ts index 7b742034370..9ce78026dc9 100644 --- a/cocos/ui/editbox/edit-box-impl-base.ts +++ b/cocos/ui/editbox/edit-box-impl-base.ts @@ -37,11 +37,22 @@ export class EditBoxImplBase { */ public _delegate: EditBox | null = null; - public init (delegate: EditBox): void {} + /** + * @engineInternal dirty flag to update the matrix + */ + public _dirtyFlag: boolean | null = false; - public onEnable (): void {} + public init (delegate: EditBox): void { + // To be overrode + } + + public onEnable (): void { + // To be overrode + } - public update (): void { } + public update (): void { + // To be overrode + } public onDisable (): void { if (this._editing) { @@ -53,9 +64,13 @@ export class EditBoxImplBase { this._delegate = null; } - public setTabIndex (index: number): void {} + public setTabIndex (index: number): void { + // To be overrode + } - public setSize (width: number, height: number): void {} + public setSize (width: number, height: number): void { + // To be overrode + } public setFocus (value): void { if (value) { @@ -69,7 +84,11 @@ export class EditBoxImplBase { return this._editing; } - public beginEditing (): void {} + public beginEditing (): void { + // To be overrode + } - public endEditing (): void {} + public endEditing (): void { + // To be overrode + } } diff --git a/cocos/ui/editbox/edit-box-impl.ts b/cocos/ui/editbox/edit-box-impl.ts index 14e1bb11270..f6a36d25700 100644 --- a/cocos/ui/editbox/edit-box-impl.ts +++ b/cocos/ui/editbox/edit-box-impl.ts @@ -137,6 +137,7 @@ export class EditBoxImpl extends EditBoxImplBase { } public update (): void { + if (!this._dirtyFlag) return; this._updateMatrix(); } @@ -625,12 +626,12 @@ export class EditBoxImpl extends EditBoxImplBase { this._delegate!._editBoxEditingDidEnded(); }; - elem.addEventListener('compositionstart', cbs.compositionStart); - elem.addEventListener('compositionend', cbs.compositionEnd); - elem.addEventListener('input', cbs.onInput); - elem.addEventListener('keydown', cbs.onKeydown); - elem.addEventListener('blur', cbs.onBlur); - elem.addEventListener('touchstart', cbs.onClick); + elem.addEventListener('compositionstart', cbs.compositionStart as EventListenerOrEventListenerObject); + elem.addEventListener('compositionend', cbs.compositionEnd as EventListenerOrEventListenerObject); + elem.addEventListener('input', cbs.onInput as EventListenerOrEventListenerObject); + elem.addEventListener('keydown', cbs.onKeydown as EventListenerOrEventListenerObject); + elem.addEventListener('blur', cbs.onBlur as EventListenerOrEventListenerObject); + elem.addEventListener('touchstart', cbs.onClick as EventListenerOrEventListenerObject); } private _removeEventListeners (): void { if (!this._edTxt) { @@ -640,12 +641,12 @@ export class EditBoxImpl extends EditBoxImplBase { const elem = this._edTxt; const cbs = this.__eventListeners; - elem.removeEventListener('compositionstart', cbs.compositionStart); - elem.removeEventListener('compositionend', cbs.compositionEnd); - elem.removeEventListener('input', cbs.onInput); - elem.removeEventListener('keydown', cbs.onKeydown); - elem.removeEventListener('blur', cbs.onBlur); - elem.removeEventListener('touchstart', cbs.onClick); + elem.removeEventListener('compositionstart', cbs.compositionStart as EventListenerOrEventListenerObject); + elem.removeEventListener('compositionend', cbs.compositionEnd as EventListenerOrEventListenerObject); + elem.removeEventListener('input', cbs.onInput as EventListenerOrEventListenerObject); + elem.removeEventListener('keydown', cbs.onKeydown as EventListenerOrEventListenerObject); + elem.removeEventListener('blur', cbs.onBlur as EventListenerOrEventListenerObject); + elem.removeEventListener('touchstart', cbs.onClick as EventListenerOrEventListenerObject); cbs.compositionStart = null; cbs.compositionEnd = null; diff --git a/cocos/ui/editbox/edit-box.ts b/cocos/ui/editbox/edit-box.ts index 4bca693cdc7..63846eeb9cb 100644 --- a/cocos/ui/editbox/edit-box.ts +++ b/cocos/ui/editbox/edit-box.ts @@ -492,6 +492,9 @@ export class EditBox extends Component { public _editBoxEditingDidBegan (): void { ComponentEventHandler.emitEvents(this.editingDidBegan, this); this.node.emit(EventType.EDITING_DID_BEGAN, this); + if (this._impl) { + this._impl._dirtyFlag = true; + } } /** @@ -503,6 +506,9 @@ export class EditBox extends Component { public _editBoxEditingDidEnded (text?: string): void { ComponentEventHandler.emitEvents(this.editingDidEnded, this); this.node.emit(EventType.EDITING_DID_ENDED, this, text); + if (this._impl) { + this._impl._dirtyFlag = false; + } } /** diff --git a/cocos/ui/layout.ts b/cocos/ui/layout.ts index 88c64aec0c1..11bc4cf7b8a 100644 --- a/cocos/ui/layout.ts +++ b/cocos/ui/layout.ts @@ -736,7 +736,7 @@ export class Layout extends Component { this.node.on(NodeEventType.ANCHOR_CHANGED, this._doLayoutDirty, this); this.node.on(NodeEventType.CHILD_ADDED, this._childAdded, this); this.node.on(NodeEventType.CHILD_REMOVED, this._childRemoved, this); - this.node.on(NodeEventType.SIBLING_ORDER_CHANGED, this._childrenChanged, this); + this.node.on(NodeEventType.CHILDREN_ORDER_CHANGED, this._childrenChanged, this); this.node.on('childrenSiblingOrderChanged', this.updateLayout, this); this._addChildrenEventListeners(); } @@ -747,7 +747,7 @@ export class Layout extends Component { this.node.off(NodeEventType.ANCHOR_CHANGED, this._doLayoutDirty, this); this.node.off(NodeEventType.CHILD_ADDED, this._childAdded, this); this.node.off(NodeEventType.CHILD_REMOVED, this._childRemoved, this); - this.node.off(NodeEventType.SIBLING_ORDER_CHANGED, this._childrenChanged, this); + this.node.off(NodeEventType.CHILDREN_ORDER_CHANGED, this._childrenChanged, this); this.node.off('childrenSiblingOrderChanged', this.updateLayout, this); this._removeChildrenEventListeners(); } diff --git a/cocos/webgpu/instantiated.ts b/cocos/webgpu/instantiated.ts index baef8832445..92d548d1fae 100644 --- a/cocos/webgpu/instantiated.ts +++ b/cocos/webgpu/instantiated.ts @@ -30,15 +30,22 @@ import { WASM_SUPPORT_MODE, WEBGPU } from 'internal:constants'; import webgpuUrl from 'external:emscripten/webgpu/webgpu_wasm.wasm'; import glslangUrl from 'external:emscripten/webgpu/glslang.wasm'; +import twgslUrl from 'external:emscripten/webgpu/twgsl.wasm' + import wasmDevice from 'external:emscripten/webgpu/webgpu_wasm.js'; import glslangLoader from 'external:emscripten/webgpu/glslang.js'; +import twgslLoader from 'external:emscripten/webgpu/twgsl.js' import { legacyCC } from '../core/global-exports'; import { WebAssemblySupportMode } from '../misc/webassembly-support'; -export const glslalgWasmModule: any = { +export const glslangWasmModule: any = { glslang: null, }; +export const twgslModule: any = { + twgsl: null, +}; + export const gfx: any = legacyCC.gfx = { wasmBinary: null, nativeDevice: null, @@ -54,7 +61,10 @@ export const promiseForWebGPUInstantiation = (() => { // TODO: we need to support AsmJS fallback option return Promise.all([ glslangLoader(new URL(glslangUrl, import.meta.url).href).then((res) => { - glslalgWasmModule.glslang = res; + glslangWasmModule.glslang = res; + }), + twgslLoader(new URL(twgslUrl, import.meta.url).href).then((data) => { + twgslModule.twgsl = data; }), new Promise((resolve) => { fetch(new URL(webgpuUrl, import.meta.url).href).then((response) => { diff --git a/editor/assets/chunks/common/color/tone-mapping.chunk b/editor/assets/chunks/common/color/tone-mapping.chunk index 5f35af77b09..6808b806772 100644 --- a/editor/assets/chunks/common/color/tone-mapping.chunk +++ b/editor/assets/chunks/common/color/tone-mapping.chunk @@ -8,10 +8,12 @@ vec3 HDRToLDR(vec3 color) #if CC_USE_DEBUG_VIEW == CC_SURFACES_DEBUG_VIEW_COMPOSITE_AND_MISC && CC_SURFACES_ENABLE_DEBUG_VIEW if (IS_DEBUG_VIEW_COMPOSITE_ENABLE_TONE_MAPPING) #endif + { // linear exposure has already applied to light intensity #if CC_TONE_MAPPING_TYPE == HDR_TONE_MAPPING_ACES color.rgb = ACESToneMap(color.rgb); #endif + } #endif return color; diff --git a/editor/assets/chunks/common/math/coordinates.chunk b/editor/assets/chunks/common/math/coordinates.chunk index c994cfc699d..1628ab0bb9e 100644 --- a/editor/assets/chunks/common/math/coordinates.chunk +++ b/editor/assets/chunks/common/math/coordinates.chunk @@ -2,7 +2,7 @@ // cc_cameraPos.w is flipNDCSign #pragma define CC_HANDLE_NDC_SAMPLE_FLIP(uv, flipNDCSign) uv = flipNDCSign == 1.0 ? vec2(uv.x, 1.0 - uv.y) : uv -#ifdef CC_USE_METAL +#if defined(CC_USE_METAL) || defined(CC_USE_WGPU) #define CC_HANDLE_SAMPLE_NDC_FLIP_STATIC(y) y = -y #else #define CC_HANDLE_SAMPLE_NDC_FLIP_STATIC(y) diff --git a/editor/assets/chunks/legacy/shading-cluster-additive.chunk b/editor/assets/chunks/legacy/shading-cluster-additive.chunk index 0bf35248e78..ba4c16be3a7 100644 --- a/editor/assets/chunks/legacy/shading-cluster-additive.chunk +++ b/editor/assets/chunks/legacy/shading-cluster-additive.chunk @@ -2,7 +2,7 @@ #pragma define CLUSTERS_X 16u #pragma define CLUSTERS_Y 8u #pragma define CLUSTERS_Z 24u -#pragma define MAX_LIGHTS_PER_CLUSTER 100u +#pragma define MAX_LIGHTS_PER_CLUSTER 200u #pragma rate b_ccLightsBuffer pass #pragma glBinding(0) diff --git a/editor/assets/chunks/shading-entries/data-structures/fs-input.chunk b/editor/assets/chunks/shading-entries/data-structures/fs-input.chunk index bf3553519f8..c038209b091 100644 --- a/editor/assets/chunks/shading-entries/data-structures/fs-input.chunk +++ b/editor/assets/chunks/shading-entries/data-structures/fs-input.chunk @@ -31,7 +31,7 @@ void CCSurfacesGetFragmentInput(out SurfacesStandardFragmentInput fsInput) #define FSInput_worldTangent v_tangent.xyz // unnormalized #define FSInput_mirrorNormal v_tangent.w #else - #define FSInput_worldTangent vec3(0.0, 0.0, 0.0) + #define FSInput_worldTangent vec3(1.0, 1.0, 1.0) // normalize zero vector may crash on webgpu, use 1 instead #define FSInput_mirrorNormal 1.0 #endif diff --git a/editor/assets/chunks/shading-entries/main-functions/misc/sky-fs.chunk b/editor/assets/chunks/shading-entries/main-functions/misc/sky-fs.chunk index cd1e3391a55..4b19e669c80 100644 --- a/editor/assets/chunks/shading-entries/main-functions/misc/sky-fs.chunk +++ b/editor/assets/chunks/shading-entries/main-functions/misc/sky-fs.chunk @@ -15,6 +15,14 @@ void main() { vec4 color = SurfacesFragmentModifyBaseColorAndTransparency(); color.a = 1.0; + // HDR Fog + // todo: apply fogColorBrightness to linear fogColor for supporting scatter lighting with HDR + #if CC_USE_FOG != CC_FOG_NONE + float fogFactor = 1.0; + CC_TRANSFER_FOG_BASE(vec4(FSInput_worldPos, 1.0), fogFactor); + CC_APPLY_FOG_BASE(color, fogFactor); + #endif + #if CC_USE_RGBE_OUTPUT color = packRGBE(color.rgb); #else//todo: change to #elif !CC_USE_FLOAT_OUTPUT when sky render queue has been fixed with custom pipeline @@ -22,12 +30,5 @@ void main() { color.rgb = LinearToSRGB(color.rgb); #endif - //todo: LDR fogging in gamma space, HDR fogging should move before tone mapping - #if CC_USE_FOG != CC_FOG_NONE - float fogFactor = 1.0; - CC_TRANSFER_FOG_BASE(vec4(FSInput_worldPos, 1.0), fogFactor); - CC_APPLY_FOG_BASE(color, fogFactor); - #endif - fragColorX = color; } diff --git a/editor/assets/chunks/shading-entries/main-functions/render-to-scene/pipeline/forward-fs.chunk b/editor/assets/chunks/shading-entries/main-functions/render-to-scene/pipeline/forward-fs.chunk index 17565ac6f50..386daaf0908 100644 --- a/editor/assets/chunks/shading-entries/main-functions/render-to-scene/pipeline/forward-fs.chunk +++ b/editor/assets/chunks/shading-entries/main-functions/render-to-scene/pipeline/forward-fs.chunk @@ -128,6 +128,18 @@ void main() { #endif #endif + // Fog, rgbe and gamma output can't apply fog with forward-add pass + // todo: apply fogColorBrightness to linear fogColor for supporting scatter lighting with HDR + #if CC_USE_FOG != CC_FOG_NONE && (!CC_USE_FLOAT_OUTPUT || CC_IS_TRANSPARENCY_PASS) + #if !CC_FORWARD_ADD + #ifdef CC_SURFACES_LIGHTING_MODIFY_FOG + color.rgb = CCSurfacesLightingModifyFog(fogFactor, color.rgb, surfaceData, lightingResult); + #else + CC_APPLY_FOG_BASE(color, fogFactor); + #endif + #endif + #endif + // Color output #if CC_USE_RGBE_OUTPUT color = packRGBE(color.rgb); // for reflection-map @@ -139,16 +151,5 @@ void main() { #endif #endif - // Fog, rgbe and gamma output can't apply fog with forward-add pass - // todo: apply fogColorBrightness to fogColor for supporting scatter lighting with HDR - #if CC_USE_FOG != CC_FOG_NONE && (!CC_USE_FLOAT_OUTPUT || CC_IS_TRANSPARENCY_PASS) - #if !CC_FORWARD_ADD - #ifdef CC_SURFACES_LIGHTING_MODIFY_FOG - color.rgb = CCSurfacesLightingModifyFog(fogFactor, color.rgb, surfaceData, lightingResult); - #else - CC_APPLY_FOG_BASE(color, fogFactor); - #endif - #endif - #endif fragColorX = color; } diff --git a/editor/assets/default_prefab/2d/Camera.prefab.meta b/editor/assets/default_prefab/2d/Camera.prefab.meta index 2a9efe9d3ad..ad7cdf57623 100644 --- a/editor/assets/default_prefab/2d/Camera.prefab.meta +++ b/editor/assets/default_prefab/2d/Camera.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "3487d118-0158-4983-93fe-c3822790e7c5", diff --git a/editor/assets/default_prefab/2d/ui/Canvas.prefab.meta b/editor/assets/default_prefab/2d/ui/Canvas.prefab.meta index 7a02f8602a3..f33a54cb75e 100644 --- a/editor/assets/default_prefab/2d/ui/Canvas.prefab.meta +++ b/editor/assets/default_prefab/2d/ui/Canvas.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "4c33600e-9ca9-483b-b734-946008261697", diff --git a/editor/assets/default_prefab/3d/Capsule.prefab.meta b/editor/assets/default_prefab/3d/Capsule.prefab.meta index 2cdc8360ae1..76db4af8e75 100644 --- a/editor/assets/default_prefab/3d/Capsule.prefab.meta +++ b/editor/assets/default_prefab/3d/Capsule.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "73ce1f7f-d1f4-4942-ad93-66ca3b3041ab", diff --git a/editor/assets/default_prefab/3d/Cone.prefab.meta b/editor/assets/default_prefab/3d/Cone.prefab.meta index caef62b443a..b34605a1e5a 100644 --- a/editor/assets/default_prefab/3d/Cone.prefab.meta +++ b/editor/assets/default_prefab/3d/Cone.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "6350d660-e888-4acf-a552-f3b719ae9110", diff --git a/editor/assets/default_prefab/3d/Cube.prefab.meta b/editor/assets/default_prefab/3d/Cube.prefab.meta index 7a3b4277d85..0155ae18b60 100644 --- a/editor/assets/default_prefab/3d/Cube.prefab.meta +++ b/editor/assets/default_prefab/3d/Cube.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "30da77a1-f02d-4ede-aa56-403452ee7fde", diff --git a/editor/assets/default_prefab/3d/Cylinder.prefab.meta b/editor/assets/default_prefab/3d/Cylinder.prefab.meta index de6fd2091f3..23303d97fc7 100644 --- a/editor/assets/default_prefab/3d/Cylinder.prefab.meta +++ b/editor/assets/default_prefab/3d/Cylinder.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "ab3e16f9-671e-48a7-90b7-d0884d9cbb85", diff --git a/editor/assets/default_prefab/3d/Plane.prefab.meta b/editor/assets/default_prefab/3d/Plane.prefab.meta index 762d5e1c3cb..28f5dda5f49 100644 --- a/editor/assets/default_prefab/3d/Plane.prefab.meta +++ b/editor/assets/default_prefab/3d/Plane.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "40563723-f8fc-4216-99ea-a81636435c10", diff --git a/editor/assets/default_prefab/3d/Quad.prefab.meta b/editor/assets/default_prefab/3d/Quad.prefab.meta index 05f6a30a78f..8e16ec5e9e8 100644 --- a/editor/assets/default_prefab/3d/Quad.prefab.meta +++ b/editor/assets/default_prefab/3d/Quad.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "34a07346-9f62-4a84-90ae-cb83f7a426c1", diff --git a/editor/assets/default_prefab/3d/Sphere.prefab.meta b/editor/assets/default_prefab/3d/Sphere.prefab.meta index 379f0318d21..2226e2e1585 100644 --- a/editor/assets/default_prefab/3d/Sphere.prefab.meta +++ b/editor/assets/default_prefab/3d/Sphere.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "655c9519-1a37-472b-bae6-29fefac0b550", diff --git a/editor/assets/default_prefab/3d/Torus.prefab.meta b/editor/assets/default_prefab/3d/Torus.prefab.meta index 1c916b0f66e..03be60e79be 100644 --- a/editor/assets/default_prefab/3d/Torus.prefab.meta +++ b/editor/assets/default_prefab/3d/Torus.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "d47f5d5e-c931-4ff4-987b-cc818a728b82", diff --git a/editor/assets/default_prefab/Camera.prefab.meta b/editor/assets/default_prefab/Camera.prefab.meta index 647ff9aef3a..92ea57a3a5a 100644 --- a/editor/assets/default_prefab/Camera.prefab.meta +++ b/editor/assets/default_prefab/Camera.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "bb0a6472-cd67-4afb-a031-94fca8f4cc92", diff --git a/editor/assets/default_prefab/Terrain.prefab.meta b/editor/assets/default_prefab/Terrain.prefab.meta index 66b2240a22e..7b34955b2f4 100644 --- a/editor/assets/default_prefab/Terrain.prefab.meta +++ b/editor/assets/default_prefab/Terrain.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "90e8b0d4-12dc-412d-9156-ea1fdb18c15b", diff --git a/editor/assets/default_prefab/effects/Particle System.prefab.meta b/editor/assets/default_prefab/effects/Particle System.prefab.meta index fc311329e1c..ac7961c5284 100644 --- a/editor/assets/default_prefab/effects/Particle System.prefab.meta +++ b/editor/assets/default_prefab/effects/Particle System.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "f09a0597-10e6-49e5-8759-a148b5e85395", diff --git a/editor/assets/default_prefab/light/Directional Light.prefab.meta b/editor/assets/default_prefab/light/Directional Light.prefab.meta index 89d28c73bdc..b92f75d0535 100644 --- a/editor/assets/default_prefab/light/Directional Light.prefab.meta +++ b/editor/assets/default_prefab/light/Directional Light.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "a0e9756d-9128-4f49-8097-e041c8b733b8", diff --git a/editor/assets/default_prefab/light/Light Probe Group.prefab.meta b/editor/assets/default_prefab/light/Light Probe Group.prefab.meta index 197281caacb..359cec2c3c4 100644 --- a/editor/assets/default_prefab/light/Light Probe Group.prefab.meta +++ b/editor/assets/default_prefab/light/Light Probe Group.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "50dfda40-7c45-4868-a876-2fe2a4c782f4", diff --git a/editor/assets/default_prefab/light/Point Light.prefab.meta b/editor/assets/default_prefab/light/Point Light.prefab.meta index 598d473c0e3..7d1909c9a08 100644 --- a/editor/assets/default_prefab/light/Point Light.prefab.meta +++ b/editor/assets/default_prefab/light/Point Light.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "03029371-ee64-4f14-820a-d495ad7cdc29", diff --git a/editor/assets/default_prefab/light/Ranged Directional Light.prefab.meta b/editor/assets/default_prefab/light/Ranged Directional Light.prefab.meta index 69df4adf90d..925508bc6e4 100644 --- a/editor/assets/default_prefab/light/Ranged Directional Light.prefab.meta +++ b/editor/assets/default_prefab/light/Ranged Directional Light.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "df72d0f6-49d3-452a-b082-8b23d38b33af", diff --git a/editor/assets/default_prefab/light/Reflection Probe.prefab.meta b/editor/assets/default_prefab/light/Reflection Probe.prefab.meta index 0e0ab920114..c38fbdde580 100644 --- a/editor/assets/default_prefab/light/Reflection Probe.prefab.meta +++ b/editor/assets/default_prefab/light/Reflection Probe.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "d8b49b64-cfba-4cfa-be53-1e469547b28b", diff --git a/editor/assets/default_prefab/light/Sphere Light.prefab.meta b/editor/assets/default_prefab/light/Sphere Light.prefab.meta index 1bebad0a135..77bee59cc15 100644 --- a/editor/assets/default_prefab/light/Sphere Light.prefab.meta +++ b/editor/assets/default_prefab/light/Sphere Light.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "4182ee46-ffa0-4de2-b66b-c93cc6c7e9b8", diff --git a/editor/assets/default_prefab/light/Spot Light.prefab.meta b/editor/assets/default_prefab/light/Spot Light.prefab.meta index d60bc004f17..5a000536c5a 100644 --- a/editor/assets/default_prefab/light/Spot Light.prefab.meta +++ b/editor/assets/default_prefab/light/Spot Light.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "7a49aa24-bd7a-40a8-b31a-b2a9da85abcd", diff --git a/editor/assets/default_prefab/ui/Button.prefab.meta b/editor/assets/default_prefab/ui/Button.prefab.meta index 22327fc1710..ad185655c69 100644 --- a/editor/assets/default_prefab/ui/Button.prefab.meta +++ b/editor/assets/default_prefab/ui/Button.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "90bdd2a9-2838-4888-b66c-e94c8b7a5169", diff --git a/editor/assets/default_prefab/ui/Canvas.prefab.meta b/editor/assets/default_prefab/ui/Canvas.prefab.meta index a398ec48963..7b7c6384ab0 100644 --- a/editor/assets/default_prefab/ui/Canvas.prefab.meta +++ b/editor/assets/default_prefab/ui/Canvas.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "f773db21-62b8-4540-956a-29bacf5ddbf5", diff --git a/editor/assets/default_prefab/ui/EditBox.prefab.meta b/editor/assets/default_prefab/ui/EditBox.prefab.meta index d72c1f6fb58..7661b7f8c90 100644 --- a/editor/assets/default_prefab/ui/EditBox.prefab.meta +++ b/editor/assets/default_prefab/ui/EditBox.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "05e79121-8675-4551-9ad7-1b901a4025db", diff --git a/editor/assets/default_prefab/ui/Graphics.prefab.meta b/editor/assets/default_prefab/ui/Graphics.prefab.meta index 7c692252572..7b4463acc28 100644 --- a/editor/assets/default_prefab/ui/Graphics.prefab.meta +++ b/editor/assets/default_prefab/ui/Graphics.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "c96e159e-43ea-4a16-8279-05bc39119d1a", diff --git a/editor/assets/default_prefab/ui/Label.prefab.meta b/editor/assets/default_prefab/ui/Label.prefab.meta index 6a5eacbd453..881efa9de41 100644 --- a/editor/assets/default_prefab/ui/Label.prefab.meta +++ b/editor/assets/default_prefab/ui/Label.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "36008810-7ad3-47c0-8112-e30aee089e45", diff --git a/editor/assets/default_prefab/ui/Layout.prefab.meta b/editor/assets/default_prefab/ui/Layout.prefab.meta index 01428016c51..5d691e90730 100644 --- a/editor/assets/default_prefab/ui/Layout.prefab.meta +++ b/editor/assets/default_prefab/ui/Layout.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "a9ef7dfc-ea8b-4cf8-918e-36da948c4de0", diff --git a/editor/assets/default_prefab/ui/Mask.prefab.meta b/editor/assets/default_prefab/ui/Mask.prefab.meta index 377ec5e6f5a..3792c4b84bc 100644 --- a/editor/assets/default_prefab/ui/Mask.prefab.meta +++ b/editor/assets/default_prefab/ui/Mask.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "7fa63aed-f3e2-46a5-8a7c-c1a1adf6cea6", diff --git a/editor/assets/default_prefab/ui/ParticleSystem2D.prefab.meta b/editor/assets/default_prefab/ui/ParticleSystem2D.prefab.meta index 7072bfdef2b..7fc1b6ea004 100644 --- a/editor/assets/default_prefab/ui/ParticleSystem2D.prefab.meta +++ b/editor/assets/default_prefab/ui/ParticleSystem2D.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "f396261e-3e06-41ec-bdd6-9a8b6d99026f", diff --git a/editor/assets/default_prefab/ui/ProgressBar.prefab.meta b/editor/assets/default_prefab/ui/ProgressBar.prefab.meta index e73a3746162..8f13645d809 100644 --- a/editor/assets/default_prefab/ui/ProgressBar.prefab.meta +++ b/editor/assets/default_prefab/ui/ProgressBar.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "0d9353c4-6fb9-49bb-bc62-77f1750078c2", diff --git a/editor/assets/default_prefab/ui/RichText.prefab.meta b/editor/assets/default_prefab/ui/RichText.prefab.meta index 262b90f4b93..73797af04e4 100644 --- a/editor/assets/default_prefab/ui/RichText.prefab.meta +++ b/editor/assets/default_prefab/ui/RichText.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "fc6bfcfa-8086-4326-809b-0ba1226bac7d", diff --git a/editor/assets/default_prefab/ui/ScrollView.prefab.meta b/editor/assets/default_prefab/ui/ScrollView.prefab.meta index 98626d08d6f..92ae53d13fe 100644 --- a/editor/assets/default_prefab/ui/ScrollView.prefab.meta +++ b/editor/assets/default_prefab/ui/ScrollView.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "c1baa707-78d6-4b89-8d5d-0b7fdf0c39bc", diff --git a/editor/assets/default_prefab/ui/Slider.prefab.meta b/editor/assets/default_prefab/ui/Slider.prefab.meta index 70669c12858..14586825920 100644 --- a/editor/assets/default_prefab/ui/Slider.prefab.meta +++ b/editor/assets/default_prefab/ui/Slider.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "2bd7e5b6-cd8c-41a1-8136-ddb8efbf6326", diff --git a/editor/assets/default_prefab/ui/Sprite.prefab.meta b/editor/assets/default_prefab/ui/Sprite.prefab.meta index 26cd9988001..c0f94735dbb 100644 --- a/editor/assets/default_prefab/ui/Sprite.prefab.meta +++ b/editor/assets/default_prefab/ui/Sprite.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "9db8cd0b-cbe4-42e7-96a9-a239620c0a9d", diff --git a/editor/assets/default_prefab/ui/SpriteRenderer.prefab.meta b/editor/assets/default_prefab/ui/SpriteRenderer.prefab.meta index a257d54f0a8..77bbcb0ab98 100644 --- a/editor/assets/default_prefab/ui/SpriteRenderer.prefab.meta +++ b/editor/assets/default_prefab/ui/SpriteRenderer.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "279ed042-5a65-4efe-9afb-2fc23c61e15a", diff --git a/editor/assets/default_prefab/ui/SpriteSplash.prefab.meta b/editor/assets/default_prefab/ui/SpriteSplash.prefab.meta index cb3d5a47078..2727ee85ff7 100644 --- a/editor/assets/default_prefab/ui/SpriteSplash.prefab.meta +++ b/editor/assets/default_prefab/ui/SpriteSplash.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "e5f21aad-3a69-4011-ac62-b74352ac025e", diff --git a/editor/assets/default_prefab/ui/TiledMap.prefab.meta b/editor/assets/default_prefab/ui/TiledMap.prefab.meta index 53301ade82b..3072ca81b17 100644 --- a/editor/assets/default_prefab/ui/TiledMap.prefab.meta +++ b/editor/assets/default_prefab/ui/TiledMap.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "3139fa4f-8c42-4ce6-98be-15e848d9734c", diff --git a/editor/assets/default_prefab/ui/Toggle.prefab.meta b/editor/assets/default_prefab/ui/Toggle.prefab.meta index c05f65aa9f0..4aece91b233 100644 --- a/editor/assets/default_prefab/ui/Toggle.prefab.meta +++ b/editor/assets/default_prefab/ui/Toggle.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "0e89afe7-56de-4f99-96a1-cba8a75bedd2", diff --git a/editor/assets/default_prefab/ui/ToggleContainer.prefab.meta b/editor/assets/default_prefab/ui/ToggleContainer.prefab.meta index e3affc6f159..9c6c100db85 100644 --- a/editor/assets/default_prefab/ui/ToggleContainer.prefab.meta +++ b/editor/assets/default_prefab/ui/ToggleContainer.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "2af73429-41d1-4346-9062-7798e42945dd", diff --git a/editor/assets/default_prefab/ui/VideoPlayer.prefab.meta b/editor/assets/default_prefab/ui/VideoPlayer.prefab.meta index 24a28385ecc..fa11ee31a45 100644 --- a/editor/assets/default_prefab/ui/VideoPlayer.prefab.meta +++ b/editor/assets/default_prefab/ui/VideoPlayer.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "7e089eaf-fa97-40d7-8a20-741a152585df", diff --git a/editor/assets/default_prefab/ui/WebView.prefab.meta b/editor/assets/default_prefab/ui/WebView.prefab.meta index 7ad242dfe02..2085c013dbb 100644 --- a/editor/assets/default_prefab/ui/WebView.prefab.meta +++ b/editor/assets/default_prefab/ui/WebView.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "9c541fa2-1dc8-4d8b-813a-aec89133f5b1", diff --git a/editor/assets/default_prefab/ui/Widget.prefab.meta b/editor/assets/default_prefab/ui/Widget.prefab.meta index e1ed816df8a..68560828750 100644 --- a/editor/assets/default_prefab/ui/Widget.prefab.meta +++ b/editor/assets/default_prefab/ui/Widget.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "36ed4422-3542-4cc4-bf02-dc4bfc590836", diff --git a/editor/assets/default_prefab/ui/pageView.prefab.meta b/editor/assets/default_prefab/ui/pageView.prefab.meta index 3c4e7e8d451..000fc2d496c 100644 --- a/editor/assets/default_prefab/ui/pageView.prefab.meta +++ b/editor/assets/default_prefab/ui/pageView.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "20a5d8cb-ccad-4543-a937-fccd98c9f3de", diff --git a/editor/assets/effects/advanced/fabric.effect.meta b/editor/assets/effects/advanced/fabric.effect.meta index 8a62097c4df..8940a2567de 100644 --- a/editor/assets/effects/advanced/fabric.effect.meta +++ b/editor/assets/effects/advanced/fabric.effect.meta @@ -1,5 +1,5 @@ { - "ver": "1.7.0", + "ver": "1.7.1", "importer": "effect", "imported": true, "uuid": "b25c7601-1d07-4a56-86c5-62e83ea7c61e", diff --git a/editor/assets/effects/advanced/leaf.effect b/editor/assets/effects/advanced/leaf.effect index aa690844804..f43c3248f56 100644 --- a/editor/assets/effects/advanced/leaf.effect +++ b/editor/assets/effects/advanced/leaf.effect @@ -113,7 +113,7 @@ CCProgram macro-remapping %{ // ui displayed macros #pragma define-meta HAS_SECOND_UV #pragma define-meta USE_TWOSIDE - //use FSInput_faceSideSign for materials #pragma define-meta USE_TWOSIDE_MATERIAL + //use FSInput_faceSideSign for different textures #pragma define-meta USE_TWOSIDE_MATERIAL #pragma define-meta USE_VERTEX_COLOR #pragma define-meta USE_DITHERED_ALPHA_TEST diff --git a/editor/assets/effects/pipeline/cluster-culling.effect b/editor/assets/effects/pipeline/cluster-culling.effect index 9c418b01383..510d4ad9bfe 100644 --- a/editor/assets/effects/pipeline/cluster-culling.effect +++ b/editor/assets/effects/pipeline/cluster-culling.effect @@ -88,10 +88,18 @@ CCProgram cluster-main %{ vec3 center = (cluster.minBounds + cluster.maxBounds) * 0.5; float sphereRadius = sqrt(dot(halfExtents, halfExtents)); light.cc_lightDir = ((cc_matView) * (vec4(light.cc_lightDir.xyz, 1.0))); - light.cc_lightDir.xyz = normalize((light.cc_lightDir - ((cc_matView) * (vec4(0,0,0, 1.0)))).xyz).xyz; + light.cc_lightDir.xyz = (light.cc_lightDir - ((cc_matView) * (vec4(0,0,0, 1.0)))).xyz; + if (length(light.cc_lightDir.xyz) > 0.1) { + light.cc_lightDir.xyz = normalize(light.cc_lightDir.xyz); + } vec3 v = center - light.cc_lightPos.xyz; float lenSq = dot(v, v); float v1Len = dot(v, light.cc_lightDir.xyz); + if(light.cc_lightDir.w == 1.0) { + v1Len = sqrt(lenSq); + return (v1Len <= sphereRadius + light.cc_lightSizeRangeAngle.y); + } + float cosAngle = light.cc_lightSizeRangeAngle.z; float sinAngle = sqrt(1.0 - cosAngle * cosAngle); float distanceClosestPoint = cosAngle * sqrt(lenSq - v1Len * v1Len) - v1Len * sinAngle; @@ -108,7 +116,7 @@ CCProgram cluster-main %{ layout(local_size_x = LOCAL_SIZE_X, local_size_y = LOCAL_SIZE_Y, local_size_z = LOCAL_SIZE_Z) in; void main() { - uint visibleLights[100]; + uint visibleLights[200]; uint visibleCount = 0u; uint clusterIndex = gl_GlobalInvocationID.z * gl_WorkGroupSize.x * gl_WorkGroupSize.y + gl_GlobalInvocationID.y * gl_WorkGroupSize.x + @@ -127,7 +135,7 @@ CCProgram cluster-main %{ } barrier(); for (uint i = 0u; i < batchSize; i++) { - if (visibleCount < 100u && ccLightIntersectsCluster(lights[i], cluster)) { + if (visibleCount < 200u && ccLightIntersectsCluster(lights[i], cluster)) { visibleLights[visibleCount] = lightOffset + i; visibleCount++; } diff --git a/editor/assets/effects/pipeline/float-output-process.effect b/editor/assets/effects/pipeline/float-output-process.effect index de8f2d61d44..153ce534f7d 100644 --- a/editor/assets/effects/pipeline/float-output-process.effect +++ b/editor/assets/effects/pipeline/float-output-process.effect @@ -42,14 +42,15 @@ CCProgram copy-fs %{ CCProgram tonemap-fs %{ precision highp float; + #define CC_SURFACES_ENABLE_DEBUG_VIEW 1 #include #include - #include - #include + #include #include + #include + #include #include #include - #include in vec2 v_uv; @@ -60,16 +61,7 @@ CCProgram tonemap-fs %{ layout(location = 0) out vec4 fragColor; - vec3 HDR2LDR_PostProcess(vec3 color) { - #if CC_USE_HDR && CC_TONE_MAPPING_TYPE == TONE_MAPPING_ACES - return ACESToneMap(color.rgb); - #else - return color.rgb; - #endif - } - vec4 CCFragOutput_PostProcess(vec4 color) { - // fog related vec4 worldPos = vec4(0.0); #if CC_USE_FOG != CC_FOG_NONE @@ -79,22 +71,23 @@ CCProgram tonemap-fs %{ worldPos = GetWorldPosFromNDCPosRH(posHS, cc_matProj, cc_matViewProjInv); #endif - // todo: apply fogColorBrightness to fogColor for supporting scatter lighting with HDR + // HDR fog + // todo: apply fogColorBrightness to linear fogColor for supporting scatter lighting with HDR + #if CC_USE_FOG != CC_FOG_NONE + float fogFactor = 1.0; + CC_TRANSFER_FOG_BASE(worldPos, fogFactor); + CC_APPLY_FOG_BASE(color, fogFactor); + #endif // tone mapping if (!DebugViewNeedDisplayOriginalData()) { #if CC_USE_FLOAT_OUTPUT - color.rgb = HDR2LDR_PostProcess(color.rgb); + color.rgb = HDRToLDR(color.rgb); color.rgb = LinearToSRGB(color.rgb); #endif } // LDR fog - #if CC_USE_FOG != CC_FOG_NONE - float fogFactor = 1.0; - CC_TRANSFER_FOG_BASE(worldPos, fogFactor); - CC_APPLY_FOG_BASE(color, fogFactor); - #endif return color; } diff --git a/editor/assets/effects/pipeline/post-process/post-final.effect b/editor/assets/effects/pipeline/post-process/post-final.effect index a1c6e08c4d2..3d8aa313f37 100644 --- a/editor/assets/effects/pipeline/post-process/post-final.effect +++ b/editor/assets/effects/pipeline/post-process/post-final.effect @@ -33,7 +33,7 @@ CCProgram fs %{ layout(location = 0) out vec4 fragColor; void main () { - fragColor = vec4(texture(inputTexture, v_uv).rgb, 1.0); + fragColor = texture(inputTexture, v_uv); } }% diff --git a/editor/assets/effects/util/dcc/imported-metallic-roughness.effect b/editor/assets/effects/util/dcc/imported-metallic-roughness.effect index a385d707dde..2ab10f99fb8 100644 --- a/editor/assets/effects/util/dcc/imported-metallic-roughness.effect +++ b/editor/assets/effects/util/dcc/imported-metallic-roughness.effect @@ -25,7 +25,7 @@ CCEffect %{ emissiveMap: { value: grey, editor: { displayName: EmissiveColorMap } } alphaSource: { value: 1.0, editor: { slide: true, range: [0, 1.0], step: 0.001 } } alphaSourceMap: { value: grey, editor: { parent: USE_OPACITY_MAP } } - alphaThreshold: { value: 0.5, target: albedoScaleAndCutoff.w, editor: { parent: USE_ALPHA_TEST, slide: true, range: [0, 1.0], step: 0.001 } } + alphaThreshold: { value: 0.5, editor: { parent: USE_ALPHA_TEST, slide: true, range: [0, 1.0], step: 0.001 } } normalStrength: { value: 1.0, editor: { parent: USE_NORMAL_MAP, slide: true, range: [0, 1.0], step: 0.001 } } normalMap: { value: normal } - &forward-add @@ -55,8 +55,8 @@ CCEffect %{ properties: tilingOffset: { value: [1.0, 1.0, 0.0, 0.0] } mainColor: { value: [1.0, 1.0, 1.0, 1.0], target: albedo, editor: { displayName: Albedo, type: color } } - albedoScale: { value: [1.0, 1.0, 1.0], target: albedoScaleAndCutoff.xyz } - alphaThreshold: { value: 0.5, target: albedoScaleAndCutoff.w, editor: { parent: USE_ALPHA_TEST } } + albedoScale: { value: 1.0, editor: { displayName: BaseWeight } } + alphaThreshold: { value: 0.5, editor: { parent: USE_ALPHA_TEST } } mainTexture: { value: grey, target: albedoMap, editor: { displayName: AlbedoMap } } alphaSource: { value: 1.0, editor: { slide: true, range: [0, 1.0], step: 0.001 } } alphaSourceMap: { value: grey, editor: { parent: USE_OPACITY_MAP } } @@ -96,7 +96,6 @@ CCProgram shared-ubos %{ uniform Constants { vec4 tilingOffset; vec4 albedo; - vec4 albedoScaleAndCutoff; vec4 emissive; float emissiveScale; float occlusion; @@ -105,6 +104,7 @@ CCProgram shared-ubos %{ float normalStrength; float alphaSource; float albedoScale; + float alphaThreshold; float specularIntensity; }; }% @@ -237,7 +237,7 @@ CCProgram surface-fragment %{ #endif #endif #if USE_ALPHA_TEST - if (baseColor.a < albedoScaleAndCutoff.w) discard; + if (baseColor.a < alphaThreshold) discard; #endif return baseColor; } @@ -265,7 +265,7 @@ CCProgram surface-fragment %{ #endif #endif - if (alpha < albedoScaleAndCutoff.w) discard; + if (alpha < alphaThreshold) discard; #endif } diff --git a/editor/assets/primitives.fbx b/editor/assets/primitives.fbx index f93a097616a..1ddad73bd08 100755 Binary files a/editor/assets/primitives.fbx and b/editor/assets/primitives.fbx differ diff --git a/editor/assets/tools/debug-view-runtime-control.prefab.meta b/editor/assets/tools/debug-view-runtime-control.prefab.meta index 34527f2712b..f9cb4c0b18f 100644 --- a/editor/assets/tools/debug-view-runtime-control.prefab.meta +++ b/editor/assets/tools/debug-view-runtime-control.prefab.meta @@ -1,5 +1,5 @@ { - "ver": "1.1.48", + "ver": "1.1.49", "importer": "prefab", "imported": true, "uuid": "7f4ddeab-efa9-4b76-bf6a-029520f68461", diff --git a/editor/i18n/en/assets.js b/editor/i18n/en/assets.js index d2393bfd1bc..84943ce0549 100644 --- a/editor/i18n/en/assets.js +++ b/editor/i18n/en/assets.js @@ -93,7 +93,7 @@ module.exports = { propertyTips: { // macros USE_DITHERED_ALPHA_TEST: 'Make transparency using opaque dithered alpha clip with TAA.', - USE_TWOSIDE: 'Two sided material for single-face objects, normal get inverse on back-face. Cull mode should set to None.', + USE_TWOSIDE: 'Two sided lighting for single-face objects, normal get inverse on back-face automatically. Cull mode should set to None.', IS_ANISOTROPY: 'Anisotropic materials, such as hair, disc, metal with micro-wires.', USE_VERTEX_COLOR: 'Use vertex color, will become darker if mesh does not contain vertex color data.', FIX_ANISOTROPIC_ROTATION_MAP: 'Fix the anomalous seam at the black-white joint of the anisotropic rotation map, turn it on if you encounter this problem.', diff --git a/editor/i18n/en/localization.js b/editor/i18n/en/localization.js index b90cc521b55..1e1703a6c74 100755 --- a/editor/i18n/en/localization.js +++ b/editor/i18n/en/localization.js @@ -409,6 +409,13 @@ module.exports = link(mixin({ font_underline: 'Font underlined', spacing_x: 'The spacing between text characters, only available in BMFont', underline_height: 'The height of underline', + outline_enable: 'Whether outline is enabled', + outline_width: 'The width of outline', + outline_color: 'The color of outline', + shadow_enable: 'Whether shadow is enabled', + shadow_color: 'The color of shadow', + shadow_offset: 'Offset between font and shadow', + shadow_blur: 'A non-negative float specifying the level of shadow blur', }, labelOutline: { color: 'Outline color', @@ -1035,6 +1042,10 @@ module.exports = link(mixin({ label: "Box2D Based 2D Physics System", description: "2D Physics system that based on Box2D.", }, + physics_2d_box2d_wasm: { + label: "Box2D-wasm Based 2D Physics System", + description: "2D Physics system that based on Box2D-wasm.", + }, intersection_2d: { label: "2D Intersection Algorithms", description: "Include 2D intersection algorithms.", diff --git a/editor/i18n/zh/assets.js b/editor/i18n/zh/assets.js index edf9f8acf94..e02c185354e 100644 --- a/editor/i18n/zh/assets.js +++ b/editor/i18n/zh/assets.js @@ -93,7 +93,7 @@ module.exports = { propertyTips: { // macros USE_DITHERED_ALPHA_TEST: '使用抖动透贴的方式来实现半透明效果,最好同时开启 TAA', - USE_TWOSIDE: '双面材质,通常用于单面物体,正面和背面的法线相反。还需将 Cull Mode 设为 None', + USE_TWOSIDE: '双面材质,仅影响光照,通常用于单面物体,正面和背面的法线会自动取反。还需将 Cull Mode 设为 None', IS_ANISOTROPY: '各向异性材质,通常用于头发、光碟、拉丝金属等', USE_VERTEX_COLOR: '使用顶点色,如果模型本身没有顶点色可能会发黑', FIX_ANISOTROPIC_ROTATION_MAP: '修复各向异性旋转图黑白相接处的异常接缝,遇到此问题再开启', @@ -325,84 +325,65 @@ module.exports = { name: '填充顶点色', title: '如果模型没有顶点颜色属性,添加颜色属性,填充为白色。', }, + meshOptimize: { + name: '网格优化', + title: '是否优化网格数据。', + vertexCache: { + name: '顶点缓存', + title: '优化顶点缓冲区以提高顶点缓存命中率。
建议对顶点数较高的模型启用此选项。', + }, + vertexFetch: { + name: '顶点提取', + title: '优化顶点缓冲区以提高顶点提取效率。
建议对顶点数较高的模型启用此选项。', + }, + overdraw: { + name: '过度绘制', + title: '优化顶点缓冲区以减少过度绘制。
建议对顶点数较高的模型启用此选项。', + }, + }, meshSimplify: { - name: 'Mesh 简化', - title: 'Mesh 简化可以被用来简化导入的模型,可以在需要模型减面时使用。
在一些少数情况下减面后的模型可能会出现显示异常,如发生这种情况请尝试调整参数并重试。', - simplification: { - name: 'Simplification', - title: 'Simplification', - si: { - name: 'Achieve The Ratio R', - title: 'Achieve The Ratio R', - }, - sa: { - name: 'Aggressively Simplify', - title: 'Aggressively Simplify', - }, + name: '网格简化', + title: '是否简化网格数据。', + targetRatio: { + name: '目标比率', + title: '简化网格数据的目标顶点数的比例。
建议将此值设置为 0.5。', }, - scene: { - name: 'Scene', - title: 'Scene', - kn: { - name: 'Keep Nodes Transform', - title: 'Keep Nodes Transform', - }, - ke: { - name: 'Keep Extras Data', - title: 'Keep Extras Data', - }, + autoErrorRate: { + name: '自动误差率', + title: '是否自动计算简化网格数据的误差率。', }, - miscellaneous: { - name: 'Miscellaneous', - title: 'Miscellaneous', - noq: { - name: 'Disable Quantization', - title: 'Disable Quantization', - }, - v: { - name: 'Verbose Output', - title: 'Verbose Output', - }, + errorRate: { + name: '误差率', + title: '简化网格数据的最大误差率。
此值还会影响结果大小。
建议调整直到获得良好的结果。', }, - algorithm: { - name: '减面算法', - simplify: 'simplify', - gltfpack: 'gltfpack (已废弃)', + lockBoundary: { + name: '锁定边界', + title: '是否锁定简化网格数据的边界。', }, - simplify: { - targetRatio: { - name: 'LOD 压缩比例', - title: '减面之后的目标面数比例,0 代表减面至最少,1 代表没有减面的原模型。', - }, - preserveSurfaceCurvature: { - name: '保留表面曲率', - title: 'Preserve Surface Curvature', - }, - preserveBorderEdges: { - name: '保留边界边', - title: 'Preserve Border Edges', - }, - preserveUVSeamEdges: { - name: '保留 UV 缝合边', - title: 'Preserve UV Seam Edges', - }, - preserveUVFoldoverEdges: { - name: '保留 UV 折叠边', - title: 'Preserve UV Foldover Edges', - }, - agressiveness: { - name: '误差距离', - title: '模型减面算法的激进程度。
当设置数值越高时,算法的减面策略会越激进,但是过于激进的策略更有可能导致结果错误。', - }, - maxIterationCount: { - name: '计算迭代次数', - title: '最大重复计数代表减面算法运行的重复次数。
高数值可以使算法运行结果更接近目标,但也会增加运行时间和结果错误的概率。', - }, + }, + meshCluster: { + name: '网格切块', + title: '是否分割网格数据。', + generateBounding: { + name: '生成包围体', + title: '是否为聚类的网格数据生成包围球和法线锥。', + }, + }, + meshCompress:{ + name: '网格压缩', + title: '是否压缩网格数据。', + encode: { + name: '编码', + title: '对网格数据进行编码以减少数据大小。', + }, + compress: { + name: '压缩', + title: '对网格数据进行压缩以减少数据大小。', }, - gltfpack: { - warn: '当前资源使用的减面算法 gltfpack 已被废弃,请选用新的 simplify 减面算法。', + quantize: { + name: '量化', + title: '对网格数据进行量化以减少数据大小。', }, - warn: '警告:优化后,网格资源的数量和名称会发生改变,这将会造成组件引用的资源丢失,请及时手动更新;(另外,对于模型资源中预生成的预制体,资源同步机制会自动更新)', }, animationBakeRate: { name: '动画烘焙速率', diff --git a/editor/i18n/zh/localization.js b/editor/i18n/zh/localization.js index f71b9c0888c..0a0ea4306d6 100755 --- a/editor/i18n/zh/localization.js +++ b/editor/i18n/zh/localization.js @@ -398,6 +398,13 @@ module.exports = link(mixin({ font_underline: '字体加下划线', spacing_x: '文本字符之间的间距。仅在使用 BMFont 位图字体时生效', underline_height: '下划线高度', + outline_enable: '是否启用描边', + outline_width: '描边宽度', + outline_color: '描边颜色', + shadow_enable: '是否启用阴影', + shadow_color: '阴影颜色', + shadow_offset: '阴影偏移量', + shadow_blur: '阴影模糊程度', }, labelOutline: { color: '描边的颜色', @@ -1009,6 +1016,10 @@ module.exports = link(mixin({ label: "基于 Box2D 的 2D 物理系统", description: "基于 Box2D 的 2D 物理系统支持。", }, + physics_2d_box2d_wasm: { + label: "基于 Box2D-wasm 的 2D 物理系统", + description: "基于 Box2D-wasm 的 2D 物理系统支持。", + }, intersection_2d: { label: "2D 相交检测算法", description: "包含用于二维相交检测的算法。", diff --git a/editor/inspector/components.js b/editor/inspector/components.js index 37fd3598924..1dd378709ac 100644 --- a/editor/inspector/components.js +++ b/editor/inspector/components.js @@ -15,8 +15,6 @@ module.exports = { 'cc.SkeletalAnimation': join(__dirname, './components/skeletal-animation.js'), 'cc.SphereLight': join(__dirname, './components/sphere-light.js'), 'cc.SpotLight': join(__dirname, './components/spot-light.js'), - 'cc.PointLight': join(__dirname, './components/point-light.js'), - 'cc.RangedDirectionalLight': join(__dirname, './components/ranged-directional-light.js'), 'cc.Sprite': join(__dirname, './components/sprite.js'), 'cc.Terrain': join(__dirname, './components/terrain.js'), 'cc.VideoPlayer': join(__dirname, './components/video-player.js'), diff --git a/editor/inspector/components/widget.js b/editor/inspector/components/widget.js index a4b52964acd..85e97d536a5 100644 --- a/editor/inspector/components/widget.js +++ b/editor/inspector/components/widget.js @@ -8,6 +8,7 @@ const Vue = require('vue/dist/vue.min.js'); const propUtils = require('../utils/prop'); const cssMediaWidth = 340; +let layout = 'vertical'; exports.template = `