diff --git a/examples/jsm/inspector/ui/Profiler.js b/examples/jsm/inspector/ui/Profiler.js index 4e5c7f70295466..4d6a4cd8fea92e 100644 --- a/examples/jsm/inspector/ui/Profiler.js +++ b/examples/jsm/inspector/ui/Profiler.js @@ -21,7 +21,7 @@ export class Profiler extends EventDispatcher { this.nextTabOriginalIndex = 0; // Track the original order of tabs as they are added this.horizontalAlign = 'right'; // 'left' or 'right' - this.verticalAlign = 'top'; // 'top' or 'bottom' + this.verticalAlign = 'top'; // 'top' or 'bottom' this.setupShell(); this.setupResizing(); @@ -2152,7 +2152,7 @@ export class Profiler extends EventDispatcher { const isRight = this.position === 'right'; let horizontal = this.horizontalAlign; // 'left' or 'right' - let vertical = this.verticalAlign; // 'top' or 'bottom' + let vertical = this.verticalAlign; // 'top' or 'bottom' if ( isVisible ) { diff --git a/examples/jsm/misc/RollerCoaster.js b/examples/jsm/misc/RollerCoaster.js index 228f8a1e58685a..399c9ac2ffbb9e 100644 --- a/examples/jsm/misc/RollerCoaster.js +++ b/examples/jsm/misc/RollerCoaster.js @@ -223,7 +223,7 @@ class RollerCoasterGeometry extends BufferGeometry { let headingChange = Math.atan2( sample2.x, sample2.z ) - Math.atan2( sample1.x, sample1.z ); if ( headingChange > Math.PI ) headingChange -= Math.PI * 2; - if ( headingChange < -Math.PI ) headingChange += Math.PI * 2; + if ( headingChange < - Math.PI ) headingChange += Math.PI * 2; quaternion.premultiply( rollQuaternion.setFromAxisAngle( forward, - Math.atan( headingChange * 8 ) * 0.5 ) ); @@ -398,7 +398,7 @@ class RollerCoasterLiftersGeometry extends BufferGeometry { let headingChange = Math.atan2( sample2.x, sample2.z ) - Math.atan2( sample1.x, sample1.z ); if ( headingChange > Math.PI ) headingChange -= Math.PI * 2; - if ( headingChange < -Math.PI ) headingChange += Math.PI * 2; + if ( headingChange < - Math.PI ) headingChange += Math.PI * 2; bankedQuaternion.copy( quaternion ); rollQuaternion.setFromAxisAngle( tangent, - Math.atan( headingChange * 8 ) * 0.5 ); diff --git a/examples/jsm/physics/JoltPhysics.js b/examples/jsm/physics/JoltPhysics.js index 27af0174154323..811785bf4fe2fa 100644 --- a/examples/jsm/physics/JoltPhysics.js +++ b/examples/jsm/physics/JoltPhysics.js @@ -204,7 +204,7 @@ async function JoltPhysics() { } - function setMeshVelocity( mesh, velocity, index = 0 ) { + function setMeshVelocity( /*mesh, velocity, index = 0*/ ) { /* let body = meshMap.get( mesh ); diff --git a/examples/jsm/renderers/CSS2DRenderer.js b/examples/jsm/renderers/CSS2DRenderer.js index 292923cc81c04e..a675a809c1d97e 100644 --- a/examples/jsm/renderers/CSS2DRenderer.js +++ b/examples/jsm/renderers/CSS2DRenderer.js @@ -256,10 +256,10 @@ class CSS2DRenderer { // coordinates const tx = _vector.x * _widthHalf + _widthHalf; - const ty = -_vector.y * _heightHalf + _heightHalf; + const ty = - _vector.y * _heightHalf + _heightHalf; // transform - element.style.transform = `translate(${-cx}%, ${-cy}%) translate(${tx}px, ${ty}px) rotate(${angle}rad)`; + element.style.transform = `translate(${- cx}%, ${- cy}%) translate(${tx}px, ${ty}px) rotate(${angle}rad)`; if ( element.parentNode !== domElement ) { diff --git a/examples/jsm/tsl/display/RetroPassNode.js b/examples/jsm/tsl/display/RetroPassNode.js index bcf42e8b9de77d..073e32beb4b762 100644 --- a/examples/jsm/tsl/display/RetroPassNode.js +++ b/examples/jsm/tsl/display/RetroPassNode.js @@ -158,7 +158,7 @@ class RetroPassNode extends PassNode { let finalUV; - if ( texture.isCubeTextureNode ) { + if ( texture.isCubeTextureNode ) { finalUV = reflectVector; diff --git a/package-lock.json b/package-lock.json index 2e03c341c1786d..d931c1163d8282 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@eslint/js": "^9.0.0", "@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-terser": "^1.0.0", - "eslint": "^9.0.0", + "eslint": "^10.8.0", "eslint-config-mdcs": "^5.0.0", "eslint-plugin-compat": "^7.0.0", "eslint-plugin-html": "^8.1.3", @@ -147,81 +147,44 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", + "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", - "minimatch": "^3.1.5" + "minimatch": "^10.2.4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@eslint/core": "^1.2.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", - "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.3.0", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/js": { @@ -238,27 +201,27 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0", + "@eslint/core": "^1.2.1", "levn": "^0.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@humanfs/core": { @@ -582,9 +545,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -599,9 +559,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -616,9 +573,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -633,9 +587,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -650,9 +601,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -667,9 +615,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -684,9 +629,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -701,9 +643,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -718,9 +657,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -735,9 +671,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -752,9 +685,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -769,9 +699,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -786,9 +713,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -892,6 +816,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -976,9 +907,9 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1005,22 +936,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/are-docs-informative": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", @@ -1056,11 +971,14 @@ "license": "CC0-1.0" }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/baseline-browser-mapping": { "version": "2.10.19", @@ -1083,14 +1001,16 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/browserslist": { @@ -1134,16 +1054,6 @@ "dev": true, "license": "MIT" }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001788", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", @@ -1178,23 +1088,6 @@ "node": ">= 10" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/chromium-bidi": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-16.0.1.tgz", @@ -1227,26 +1120,6 @@ "node": ">=20" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/comment-parser": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.7.tgz", @@ -1257,13 +1130,6 @@ "node": ">= 12.0.0" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1454,33 +1320,33 @@ } }, "node_modules/eslint": { - "version": "9.39.5", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", - "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", + "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", "dev": true, "license": "MIT", + "workspaces": [ + "packages/*" + ], "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.6", - "@eslint/js": "9.39.5", - "@eslint/plugin-kit": "^0.4.1", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", - "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -1490,8 +1356,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", + "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -1499,7 +1364,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" @@ -1597,80 +1462,51 @@ "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, - "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-jsdoc/node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.16.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1882,16 +1718,6 @@ "dev": true, "license": "ISC" }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1952,23 +1778,6 @@ "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2039,29 +1848,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/js2xmlparser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", @@ -2240,13 +2026,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/magic-string": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.1.0.tgz", @@ -2330,16 +2109,19 @@ "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.8" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mitt": { @@ -2450,19 +2232,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parse-imports-exports": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", @@ -2652,16 +2421,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/rollup": { "version": "4.62.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.3.tgz", @@ -2856,19 +2615,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", diff --git a/package.json b/package.json index 35092bafefdaa8..ec5fd58bb4c3cf 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "@eslint/js": "^9.0.0", "@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-terser": "^1.0.0", - "eslint": "^9.0.0", + "eslint": "^10.8.0", "eslint-config-mdcs": "^5.0.0", "eslint-plugin-compat": "^7.0.0", "eslint-plugin-html": "^8.1.3", diff --git a/src/loaders/ImageBitmapLoader.js b/src/loaders/ImageBitmapLoader.js index fc51a88bb3721c..f66097df9281b2 100644 --- a/src/loaders/ImageBitmapLoader.js +++ b/src/loaders/ImageBitmapLoader.js @@ -186,6 +186,8 @@ class ImageBitmapLoader extends Loader { scope.manager.itemEnd( url ); + return imageBitmap; // see #34150 + } ).catch( function ( e ) { if ( onError ) onError( e ); diff --git a/src/renderers/common/XRManager.js b/src/renderers/common/XRManager.js index 234d13aca96138..b320a717f40359 100644 --- a/src/renderers/common/XRManager.js +++ b/src/renderers/common/XRManager.js @@ -229,15 +229,6 @@ class XRManager extends EventDispatcher { */ this._currentPixelRatio = null; - /** - * The renderer's sample count before XR temporarily overrides it. - * - * @private - * @type {?number} - * @default null - */ - this._currentSamples = null; - /** * The current size of the renderer's canvas * in logical pixel unit. @@ -737,9 +728,7 @@ class XRManager extends EventDispatcher { */ _validateWebGPUSession() { - const renderer = this._renderer; - - if ( renderer.backend.isWebGPUBackend !== true ) return; + if ( this._renderer.backend.isWebGPUBackend !== true ) return; if ( this._session.enabledFeatures.includes( 'webgpu' ) === false ) { @@ -747,15 +736,6 @@ class XRManager extends EventDispatcher { } - if ( renderer.samples > 0 ) { - - warnOnce( 'THREE.XRManager: WebGPU XR does not support MSAA yet. Disabling MSAA for this XR session.' ); - - if ( this._currentSamples === null ) this._currentSamples = renderer.samples; - renderer._samples = 0; - - } - } /** @@ -770,8 +750,7 @@ class XRManager extends EventDispatcher { const webgpuBinding = this.getWebGPUBinding(); const glProjLayer = webgpuBinding.createProjectionLayer( { - colorFormat: webgpuBinding.getPreferredColorFormat(), - depthStencilFormat: 'depth24plus' + colorFormat: webgpuBinding.getPreferredColorFormat() } ); this._glProjLayer = glProjLayer; @@ -787,7 +766,10 @@ class XRManager extends EventDispatcher { depthBuffer: true, multiview: false, useArrayDepthTexture: true, - samples: 0 + storeMultisampledColorBuffer: false, + storeMultisampledDepthBuffer: false, + storeMultisampledStencilBuffer: false, + samples: this._renderer.samples } ); this._xrRenderTarget.texture.isArrayTexture = true; @@ -810,46 +792,27 @@ class XRManager extends EventDispatcher { _disposeWebGPUSession() { const renderer = this._renderer; - const xrRenderTarget = this._xrRenderTarget; - - if ( xrRenderTarget === null || renderer.backend.isWebGPUBackend !== true ) return; - - // XR textures are external (from XRGPUBinding), so clear cached state before disposal. const backend = renderer.backend; - const texturesModule = renderer._textures; - - const renderTargetData = backend.get ? backend.get( xrRenderTarget ) : null; - if ( renderTargetData ) { - - renderTargetData.descriptors = undefined; - - } - - const deleteResource = ( resource ) => { - - if ( resource === null || resource === undefined ) return; - - if ( backend.delete ) backend.delete( resource ); - if ( texturesModule.delete ) texturesModule.delete( resource ); + const xrRenderTarget = this._xrRenderTarget; - }; + if ( xrRenderTarget === null || backend.isWebGPUBackend !== true ) return; - for ( let i = 0; i < xrRenderTarget.textures.length; i ++ ) { + if ( renderer._renderContexts && renderer._renderContexts.dispose ) { - deleteResource( xrRenderTarget.textures[ i ] ); + renderer._renderContexts.dispose(); } - deleteResource( xrRenderTarget.depthTexture ); - deleteResource( xrRenderTarget ); + xrRenderTarget.dispose(); - if ( renderer._renderContexts && renderer._renderContexts.dispose ) { + // The external texture can be registered before the render target is initialized. + for ( const texture of xrRenderTarget.textures ) { - renderer._renderContexts.dispose(); + if ( backend.has( texture ) ) backend.destroyTexture( texture ); } - xrRenderTarget.dispose(); + backend.delete( xrRenderTarget ); } @@ -1371,11 +1334,7 @@ class XRManager extends EventDispatcher { format: RGBAFormat, type: UnsignedByteType, colorSpace: renderer.outputColorSpace, - stencilBuffer: renderer.stencil, - resolveDepthBuffer: ( glBaseLayer.ignoreDepthValues === false ), - resolveStencilBuffer: ( glBaseLayer.ignoreDepthValues === false ), - storeMultisampledDepthBuffer: ( glBaseLayer.ignoreDepthValues === false ), - storeMultisampledStencilBuffer: ( glBaseLayer.ignoreDepthValues === false ), + stencilBuffer: renderer.stencil } ); @@ -1683,13 +1642,6 @@ function onSessionEnd() { this._currentDepthNear = null; this._currentDepthFar = null; - if ( this._currentSamples !== null ) { - - renderer._samples = this._currentSamples; - this._currentSamples = null; - - } - // restore framebuffer/rendering state renderer._resetXRState(); @@ -1985,6 +1937,14 @@ function onAnimationFrame( time, frame ) { renderer.setOutputRenderTarget( this._xrRenderTarget ); const frameBufferTarget = renderer._getFrameBufferTarget(); + + if ( webgpuViewData !== null ) { + + this._xrRenderTarget.samples = frameBufferTarget === null ? renderer.samples : 0; + this._xrRenderTarget.depthBuffer = frameBufferTarget === null; + + } + renderer.xr.foveateBoundTexture( frameBufferTarget ); } diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js index bd46d3c33cd154..d3240855849123 100644 --- a/src/renderers/webgpu/WebGPUBackend.js +++ b/src/renderers/webgpu/WebGPUBackend.js @@ -27,6 +27,7 @@ import GPURenderPassDepthStencilAttachment from './descriptors/GPURenderPassDept import GPURenderPassDescriptor from './descriptors/GPURenderPassDescriptor.js'; import GPURenderPassTimestampWrites from './descriptors/GPURenderPassTimestampWrites.js'; import GPUTexelCopyTextureInfo from './descriptors/GPUTexelCopyTextureInfo.js'; +import GPUTextureDescriptor from './descriptors/GPUTextureDescriptor.js'; import GPUTextureViewDescriptor from './descriptors/GPUTextureViewDescriptor.js'; import GPUExtent3D from './descriptors/GPUExtent3D.js'; @@ -39,6 +40,7 @@ const _shaderModuleDescriptor = new GPUShaderModuleDescriptor(); const _renderPassTimestampWrites = new GPURenderPassTimestampWrites(); const _texelCopyTextureInfoSrc = new GPUTexelCopyTextureInfo(); const _texelCopyTextureInfoDst = new GPUTexelCopyTextureInfo(); +const _textureDescriptor = new GPUTextureDescriptor(); const _viewDescriptor = new GPUTextureViewDescriptor(); const _extent3D = new GPUExtent3D(); @@ -306,13 +308,14 @@ class WebGPUBackend extends Backend { */ setXRRenderTargetTextures( renderTarget, colorTexture, viewDescriptors = null ) { - this.set( renderTarget.texture, { - texture: colorTexture, - format: colorTexture.format, - externalTexture: true, - xrViewDescriptors: viewDescriptors, - initialized: true - } ); + // Update the external XR texture without replacing the cached MSAA attachments. + const textureData = this.get( renderTarget.texture ); + + textureData.texture = colorTexture; + textureData.format = colorTexture.format; + textureData.externalTexture = true; + textureData.xrViewDescriptors = viewDescriptors; + textureData.initialized = true; } @@ -528,6 +531,81 @@ class WebGPUBackend extends Backend { } + /** + * Returns multisampled color textures for an external render target. + * + * @private + * @param {RenderContext} renderContext - The render context. + * @param {Object} textureData - The backend data for the external texture. + * @param {number} count - The number of textures to create. + * @return {?Array} The multisampled textures. + */ + _getExternalMSAATextures( renderContext, textureData, count ) { + + const samples = this.utils.getSampleCount( renderContext.sampleCount ); + + if ( samples === 1 ) { + + if ( textureData.msaaTextures !== undefined ) { + + for ( const texture of textureData.msaaTextures ) texture.destroy(); + + textureData.msaaTextures = undefined; + + } + + return null; + + } + + const renderTarget = renderContext.renderTarget; + const width = renderTarget.width; + const height = renderTarget.height; + const format = textureData.format; + + if ( textureData.msaaTextures === undefined || + textureData.msaaTextures.length !== count || + textureData.msaaWidth !== width || + textureData.msaaHeight !== height || + textureData.msaaSamples !== samples || + textureData.msaaFormat !== format ) { + + if ( textureData.msaaTextures !== undefined ) { + + for ( const texture of textureData.msaaTextures ) texture.destroy(); + + } + + _textureDescriptor.size.width = width; + _textureDescriptor.size.height = height; + _textureDescriptor.sampleCount = samples; + _textureDescriptor.format = format; + // Layered rendering can resume after a framebuffer copy, + // so these attachments must support loading. + _textureDescriptor.usage = GPUTextureUsage.RENDER_ATTACHMENT; + + textureData.msaaTextures = []; + + for ( let i = 0; i < count; i ++ ) { + + _textureDescriptor.label = renderTarget.texture.name + '-msaa-' + i; + textureData.msaaTextures.push( this.device.createTexture( _textureDescriptor ) ); + + } + + _textureDescriptor.reset(); + + textureData.msaaWidth = width; + textureData.msaaHeight = height; + textureData.msaaSamples = samples; + textureData.msaaFormat = format; + + } + + return textureData.msaaTextures; + + } + /** * Creates attachment views for an external texture render target. * @@ -540,14 +618,19 @@ class WebGPUBackend extends Backend { const textureViews = []; const camera = renderContext.camera; + const viewDescriptors = textureData.xrViewDescriptors; + const viewCount = Math.max( viewDescriptors?.length || 0, renderContext.activeCubeFace + 1, 1 ); + const msaaTextures = this._getExternalMSAATextures( renderContext, textureData, viewCount ); + + if ( viewDescriptors && camera !== null && camera.isArrayCamera === true ) { - if ( textureData.xrViewDescriptors && camera !== null && camera.isArrayCamera === true ) { + for ( let i = 0; i < viewDescriptors.length; i ++ ) { - for ( let i = 0; i < textureData.xrViewDescriptors.length; i ++ ) { + const textureView = textureData.texture.createView( viewDescriptors[ i ] ); textureViews.push( { - view: textureData.texture.createView( textureData.xrViewDescriptors[ i ] ), - resolveTarget: undefined, + view: msaaTextures !== null ? msaaTextures[ i ].createView() : textureView, + resolveTarget: msaaTextures !== null && renderContext.renderTarget.resolveColorBuffer === true ? textureView : undefined, depthSlice: undefined } ); @@ -555,13 +638,16 @@ class WebGPUBackend extends Backend { } else { + const layer = renderContext.activeCubeFace; + const textureView = textureData.texture.createView( { + dimension: GPUTextureViewDimension.TwoD, + baseArrayLayer: layer, + arrayLayerCount: 1 + } ); + textureViews.push( { - view: textureData.texture.createView( { - dimension: GPUTextureViewDimension.TwoD, - baseArrayLayer: renderContext.activeCubeFace, - arrayLayerCount: 1 - } ), - resolveTarget: undefined, + view: msaaTextures !== null ? msaaTextures[ layer ].createView() : textureView, + resolveTarget: msaaTextures !== null && renderContext.renderTarget.resolveColorBuffer === true ? textureView : undefined, depthSlice: undefined } ); @@ -649,9 +735,10 @@ class WebGPUBackend extends Backend { _viewDescriptor.dimension = GPUTextureViewDimension.TwoD; const textureView = textureData.texture.createView( _viewDescriptor ); + const msaaTexture = textureData.msaaTextures?.[ layer ]; textureViews.push( { - view: textureView, - resolveTarget: undefined, + view: msaaTexture !== undefined ? msaaTexture.createView() : textureView, + resolveTarget: msaaTexture !== undefined && renderTarget.resolveColorBuffer === true ? textureView : undefined, depthSlice: undefined } ); @@ -676,6 +763,11 @@ class WebGPUBackend extends Backend { view = textureData.msaaTexture.createView(); resolveTarget = renderTarget.resolveColorBuffer === true ? textureView : undefined; + } else if ( textureData.msaaTextures !== undefined ) { + + view = textureData.msaaTextures[ renderContext.activeCubeFace ].createView(); + resolveTarget = renderTarget.resolveColorBuffer === true ? textureView : undefined; + } else { view = textureView; @@ -727,7 +819,8 @@ class WebGPUBackend extends Backend { } const depthStencilAttachment = new GPURenderPassDepthStencilAttachment(); - depthStencilAttachment.view = depthTextureData.texture.createView( _viewDescriptor ); + const msaaDepthTexture = depthTextureData.msaaTextures?.[ renderContext.activeCubeFace ]; + depthStencilAttachment.view = msaaDepthTexture !== undefined ? msaaDepthTexture.createView() : depthTextureData.texture.createView( _viewDescriptor ); descriptorBase.depthStencilAttachment = depthStencilAttachment; _viewDescriptor.reset(); @@ -851,6 +944,12 @@ class WebGPUBackend extends Backend { const depthStencilAttachment = descriptor.depthStencilAttachment; const renderTarget = renderContext.renderTarget; + const discardColor = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledColorBuffer === false; + const discardDepth = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledDepthBuffer === false; + const discardStencil = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledStencilBuffer === false; + + // A fullscreen pass overwrites the external texture, so its previous contents do not need to be loaded. + const clearExternalColor = renderContext.fullscreenPass === true && this._hasExternalTexture( renderContext ); if ( renderContext.textures !== null ) { @@ -860,7 +959,7 @@ class WebGPUBackend extends Backend { const colorAttachment = colorAttachments[ i ]; - if ( renderContext.clearColor ) { + if ( renderContext.clearColor || discardColor || clearExternalColor ) { if ( i === 0 ) { @@ -885,7 +984,7 @@ class WebGPUBackend extends Backend { } - if ( renderContext.sampleCount > 1 && renderTarget?.storeMultisampledColorBuffer === false ) { + if ( discardColor ) { colorAttachment.storeOp = GPUStoreOp.Discard; @@ -920,7 +1019,7 @@ class WebGPUBackend extends Backend { if ( renderContext.depth ) { - if ( renderContext.clearDepth ) { + if ( renderContext.clearDepth || discardDepth ) { depthStencilAttachment.depthClearValue = renderContext.clearDepthValue; depthStencilAttachment.depthLoadOp = GPULoadOp.Clear; @@ -931,7 +1030,7 @@ class WebGPUBackend extends Backend { } - if ( renderContext.sampleCount > 1 && renderTarget?.storeMultisampledDepthBuffer === false ) { + if ( discardDepth ) { depthStencilAttachment.depthStoreOp = GPUStoreOp.Discard; @@ -945,7 +1044,7 @@ class WebGPUBackend extends Backend { if ( renderContext.stencil ) { - if ( renderContext.clearStencil ) { + if ( renderContext.clearStencil || discardStencil ) { depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue; depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear; @@ -956,7 +1055,7 @@ class WebGPUBackend extends Backend { } - if ( renderContext.sampleCount > 1 && renderTarget?.storeMultisampledStencilBuffer === false ) { + if ( discardStencil ) { depthStencilAttachment.stencilStoreOp = GPUStoreOp.Discard; @@ -986,7 +1085,7 @@ class WebGPUBackend extends Backend { } else { - this._updateArrayCameraLayerDescriptors( renderContext, renderContextData, cameras ); + this._updateArrayCameraLayerDescriptors( renderContext, renderContextData, descriptor, cameras ); } @@ -1109,11 +1208,12 @@ class WebGPUBackend extends Backend { for ( let i = 0; i < cameras.length; i ++ ) { const sourceAttachment = descriptor.colorAttachments[ 0 ]; + const layerAttachment = descriptor.colorAttachments[ i ]; const layerColorAttachment = new GPURenderPassColorAttachment(); - layerColorAttachment.view = descriptor.colorAttachments[ i ].view; - layerColorAttachment.depthSlice = sourceAttachment.depthSlice; - layerColorAttachment.resolveTarget = sourceAttachment.resolveTarget; + layerColorAttachment.view = layerAttachment.view; + layerColorAttachment.depthSlice = layerAttachment.depthSlice; + layerColorAttachment.resolveTarget = layerAttachment.resolveTarget; layerColorAttachment.loadOp = sourceAttachment.loadOp; layerColorAttachment.storeOp = sourceAttachment.storeOp; layerColorAttachment.clearValue = sourceAttachment.clearValue; @@ -1130,11 +1230,21 @@ class WebGPUBackend extends Backend { if ( ! depthTextureData.viewCache[ layerIndex ] ) { - _viewDescriptor.dimension = GPUTextureViewDimension.TwoD; - _viewDescriptor.baseArrayLayer = i; - _viewDescriptor.arrayLayerCount = 1; + const msaaTexture = depthTextureData.msaaTextures?.[ layerIndex ]; + + if ( msaaTexture !== undefined ) { + + depthTextureData.viewCache[ layerIndex ] = msaaTexture.createView(); + + } else { + + _viewDescriptor.dimension = GPUTextureViewDimension.TwoD; + _viewDescriptor.baseArrayLayer = i; + _viewDescriptor.arrayLayerCount = 1; - depthTextureData.viewCache[ layerIndex ] = depthTextureData.texture.createView( _viewDescriptor ); + depthTextureData.viewCache[ layerIndex ] = depthTextureData.texture.createView( _viewDescriptor ); + + } _viewDescriptor.reset(); @@ -1183,14 +1293,29 @@ class WebGPUBackend extends Backend { * * @param {RenderContext} renderContext - The render context. * @param {Object} renderContextData - The render context data. + * @param {Object} descriptor - The render pass descriptor. * @param {ArrayCamera} cameras - The array camera. * */ - _updateArrayCameraLayerDescriptors( renderContext, renderContextData, cameras ) { + _updateArrayCameraLayerDescriptors( renderContext, renderContextData, descriptor, cameras ) { + + const renderTarget = renderContext.renderTarget; + const discardDepth = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledDepthBuffer === false; + const discardStencil = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledStencilBuffer === false; for ( let i = 0; i < cameras.length; i ++ ) { const layerDescriptor = renderContextData.layerDescriptors[ i ]; + const sourceColorAttachment = descriptor.colorAttachments[ 0 ]; + const layerColorAttachment = descriptor.colorAttachments[ i ]; + const colorAttachment = layerDescriptor.colorAttachments[ 0 ]; + + colorAttachment.view = layerColorAttachment.view; + colorAttachment.resolveTarget = layerColorAttachment.resolveTarget; + colorAttachment.depthSlice = layerColorAttachment.depthSlice; + colorAttachment.loadOp = sourceColorAttachment.loadOp; + colorAttachment.storeOp = sourceColorAttachment.storeOp; + colorAttachment.clearValue = sourceColorAttachment.clearValue; if ( layerDescriptor.depthStencilAttachment ) { @@ -1198,7 +1323,7 @@ class WebGPUBackend extends Backend { if ( renderContext.depth ) { - if ( renderContext.clearDepth ) { + if ( renderContext.clearDepth || discardDepth ) { depthAttachment.depthClearValue = renderContext.clearDepthValue; depthAttachment.depthLoadOp = GPULoadOp.Clear; @@ -1213,7 +1338,7 @@ class WebGPUBackend extends Backend { if ( renderContext.stencil ) { - if ( renderContext.clearStencil ) { + if ( renderContext.clearStencil || discardStencil ) { depthAttachment.stencilClearValue = renderContext.clearStencilValue; depthAttachment.stencilLoadOp = GPULoadOp.Clear; @@ -1272,13 +1397,40 @@ class WebGPUBackend extends Backend { const layerDescriptor = renderContextData.layerDescriptors[ i ]; const colorLoadOps = layerDescriptor.colorAttachments.map( attachment => attachment.loadOp ); + const colorStoreOps = layerDescriptor.colorAttachments.map( attachment => attachment.storeOp ); const depthLoadOp = layerDescriptor.depthStencilAttachment?.depthLoadOp; + const depthStoreOp = layerDescriptor.depthStencilAttachment?.depthStoreOp; const stencilLoadOp = layerDescriptor.depthStencilAttachment?.stencilLoadOp; + const stencilStoreOp = layerDescriptor.depthStencilAttachment?.stencilStoreOp; for ( let stageIndex = 0; stageIndex < renderStages.length; stageIndex ++ ) { const renderStage = renderStages[ stageIndex ]; const bundle = renderStage.bundles[ i ]; + const isLastStage = stageIndex === renderStages.length - 1; + + for ( let j = 0; j < layerDescriptor.colorAttachments.length; j ++ ) { + + const attachment = layerDescriptor.colorAttachments[ j ]; + attachment.loadOp = stageIndex === 0 ? colorLoadOps[ j ] : GPULoadOp.Load; + attachment.storeOp = isLastStage ? colorStoreOps[ j ] : GPUStoreOp.Store; + + } + + if ( renderContext.depth ) { + + layerDescriptor.depthStencilAttachment.depthLoadOp = stageIndex === 0 ? depthLoadOp : GPULoadOp.Load; + layerDescriptor.depthStencilAttachment.depthStoreOp = isLastStage ? depthStoreOp : GPUStoreOp.Store; + + } + + if ( renderContext.stencil ) { + + layerDescriptor.depthStencilAttachment.stencilLoadOp = stageIndex === 0 ? stencilLoadOp : GPULoadOp.Load; + layerDescriptor.depthStencilAttachment.stencilStoreOp = isLastStage ? stencilStoreOp : GPUStoreOp.Store; + + } + const renderPass = encoder.beginRenderPass( layerDescriptor ); if ( renderContext.viewport ) { @@ -1307,25 +1459,28 @@ class WebGPUBackend extends Backend { } - if ( stageIndex < renderStages.length - 1 ) { - - for ( const attachment of layerDescriptor.colorAttachments ) attachment.loadOp = GPULoadOp.Load; + } - if ( renderContext.depth ) layerDescriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Load; - if ( renderContext.stencil ) layerDescriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load; + for ( let j = 0; j < layerDescriptor.colorAttachments.length; j ++ ) { - } + layerDescriptor.colorAttachments[ j ].loadOp = colorLoadOps[ j ]; + layerDescriptor.colorAttachments[ j ].storeOp = colorStoreOps[ j ]; } - for ( let j = 0; j < layerDescriptor.colorAttachments.length; j ++ ) { + if ( renderContext.depth ) { - layerDescriptor.colorAttachments[ j ].loadOp = colorLoadOps[ j ]; + layerDescriptor.depthStencilAttachment.depthLoadOp = depthLoadOp; + layerDescriptor.depthStencilAttachment.depthStoreOp = depthStoreOp; } - if ( renderContext.depth ) layerDescriptor.depthStencilAttachment.depthLoadOp = depthLoadOp; - if ( renderContext.stencil ) layerDescriptor.depthStencilAttachment.stencilLoadOp = stencilLoadOp; + if ( renderContext.stencil ) { + + layerDescriptor.depthStencilAttachment.stencilLoadOp = stencilLoadOp; + layerDescriptor.depthStencilAttachment.stencilStoreOp = stencilStoreOp; + + } } diff --git a/src/renderers/webgpu/utils/WebGPUTextureUtils.js b/src/renderers/webgpu/utils/WebGPUTextureUtils.js index ef037242008bd1..70425e6b9f2879 100644 --- a/src/renderers/webgpu/utils/WebGPUTextureUtils.js +++ b/src/renderers/webgpu/utils/WebGPUTextureUtils.js @@ -352,6 +352,20 @@ class WebGPUTextureUtils { textureData.format = format; const { samples, primarySamples, isMSAA } = backend.utils.getTextureSampleData( texture ); + const renderTarget = texture.renderTarget; + + // WebGPU multisampled 2D textures can only have a single array layer. + const useSeparateMSAATextures = samples > 1 && renderTarget !== null && depth > 1 && dimension === GPUTextureDimension.TwoD; + const supportsTransientAttachments = GPUTextureUsage.TRANSIENT_ATTACHMENT !== undefined; + // Layered rendering can resume after a framebuffer copy, so its attachments must support loading. + const useTransientAttachments = supportsTransientAttachments && useSeparateMSAATextures === false; + const useTransientDepthAttachment = texture.isDepthTexture === true && + useTransientAttachments && + renderTarget?.storeMultisampledDepthBuffer === false && + ( renderTarget.stencilBuffer === false || renderTarget.storeMultisampledStencilBuffer === false ); + const useTransientColorAttachment = texture.isDepthTexture !== true && + useTransientAttachments && + renderTarget?.storeMultisampledColorBuffer === false; let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC; @@ -367,17 +381,11 @@ class WebGPUTextureUtils { } - const renderTarget = texture.renderTarget; - // when the multisampled data are discarded, try to use a transient attachment if possible - if ( texture.isDepthTexture === true && primarySamples > 1 && GPUTextureUsage.TRANSIENT_ATTACHMENT !== undefined ) { - - if ( renderTarget?.storeMultisampledDepthBuffer === false && ( renderTarget.stencilBuffer === false || renderTarget.storeMultisampledStencilBuffer === false ) ) { + if ( primarySamples > 1 && useTransientDepthAttachment ) { - usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT; - - } + usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT; } @@ -421,7 +429,7 @@ class WebGPUTextureUtils { } - if ( isMSAA ) { + if ( isMSAA || useSeparateMSAATextures ) { const msaaTextureDescriptorGPU = Object.assign( {}, textureDescriptorGPU ); @@ -431,13 +439,30 @@ class WebGPUTextureUtils { // when the multisampled data are discarded, try to use a transient attachment if possible - if ( renderTarget?.storeMultisampledColorBuffer === false && GPUTextureUsage.TRANSIENT_ATTACHMENT !== undefined ) { + if ( useTransientDepthAttachment || useTransientColorAttachment ) { msaaTextureDescriptorGPU.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT; } - textureData.msaaTexture = backend.device.createTexture( msaaTextureDescriptorGPU ); + if ( useSeparateMSAATextures ) { + + msaaTextureDescriptorGPU.size = Object.assign( {}, msaaTextureDescriptorGPU.size, { depthOrArrayLayers: 1 } ); + + textureData.msaaTextures = []; + + for ( let i = 0; i < depth; i ++ ) { + + msaaTextureDescriptorGPU.label = textureDescriptorGPU.label + '-msaa-' + i; + textureData.msaaTextures.push( backend.device.createTexture( msaaTextureDescriptorGPU ) ); + + } + + } else { + + textureData.msaaTexture = backend.device.createTexture( msaaTextureDescriptorGPU ); + + } } @@ -458,10 +483,16 @@ class WebGPUTextureUtils { const backend = this.backend; const textureData = backend.get( texture ); - if ( textureData.texture !== undefined && isDefaultTexture === false && texture.isExternalTexture !== true ) textureData.texture.destroy(); + if ( textureData.texture !== undefined && isDefaultTexture === false && texture.isExternalTexture !== true && textureData.externalTexture !== true ) textureData.texture.destroy(); if ( textureData.msaaTexture !== undefined ) textureData.msaaTexture.destroy(); + if ( textureData.msaaTextures !== undefined ) { + + for ( const msaaTexture of textureData.msaaTextures ) msaaTexture.destroy(); + + } + backend.delete( texture ); } diff --git a/src/renderers/webgpu/utils/WebGPUUtils.js b/src/renderers/webgpu/utils/WebGPUUtils.js index 550969944fa9c4..419310e3b9fead 100644 --- a/src/renderers/webgpu/utils/WebGPUUtils.js +++ b/src/renderers/webgpu/utils/WebGPUUtils.js @@ -136,7 +136,8 @@ class WebGPUUtils { samples = this.getSampleCount( samples || 1 ); const isMSAA = samples > 1 && texture.renderTarget !== null && ( texture.isDepthTexture !== true && texture.isFramebufferTexture !== true ); - const primarySamples = isMSAA ? 1 : samples; + const isMSAAArrayDepthTexture = samples > 1 && texture.renderTarget !== null && texture.isDepthTexture === true && texture.isArrayTexture === true; + const primarySamples = isMSAA || isMSAAArrayDepthTexture ? 1 : samples; return { samples, primarySamples, isMSAA }; diff --git a/test/unit/src/loaders/ImageBitmapLoader.tests.js b/test/unit/src/loaders/ImageBitmapLoader.tests.js index cfa2b63cbcbf3c..6b46410caa6e25 100644 --- a/test/unit/src/loaders/ImageBitmapLoader.tests.js +++ b/test/unit/src/loaders/ImageBitmapLoader.tests.js @@ -1,5 +1,6 @@ import { ImageBitmapLoader } from '../../../../src/loaders/ImageBitmapLoader.js'; +import { Cache } from '../../../../src/loaders/Cache.js'; import { Loader } from '../../../../src/loaders/Loader.js'; import { CONSOLE_LEVEL } from '../../utils/console-wrapper.js'; @@ -70,6 +71,45 @@ export default QUnit.module( 'Loaders', () => { } ); + QUnit.test( 'load', async ( assert ) => { + + const canvas = document.createElement( 'canvas' ); + canvas.width = 8; + canvas.height = 8; + + const url = canvas.toDataURL( 'image/png' ); + + const enabled = Cache.enabled; + Cache.enabled = true; + + try { + + // concurrent requests for the same URL share the cached promise + + const [ first, second ] = await Promise.all( [ + new ImageBitmapLoader().loadAsync( url ), + new ImageBitmapLoader().loadAsync( url ) + ] ); + + assert.ok( + first instanceof ImageBitmap, + 'The first request resolves with an image bitmap.' + ); + + assert.ok( + second instanceof ImageBitmap, + 'The second request resolves with an image bitmap.' + ); + + } finally { + + Cache.remove( `image-bitmap:${url}` ); + Cache.enabled = enabled; + + } + + } ); + } ); } );