Repository: playcanvas/engine
Pull request #4188 (2022-04-08 13:10:32)

enable support for retrying loading of container assets
While tracking down an issue with my project, I noticed that playcanvas was not automatically retrying loading of GLB files when there was a network failure, like it does for other assets like texture files for instance.

This PR updates the `ContainerHandler` class to support updating `maxRetries` and forward it to any configured parsers, including the `GlbParser`, to which it delegates the loading of the asset by default.


I confirm I have read the [contributing guidelines](https://github.com/playcanvas/engine/blob/master/.github/CONTRIBUTING.md) and signed the [Contributor License Agreement](https://docs.google.com/a/playcanvas.com/forms/d/1Ih69zQfJG-QDLIEpHr6CsaAs6fPORNOVnMv5nuo0cjk/viewform).



Patch:
diff --git a/examples/src/examples/user-interface/custom-shader.tsx b/examples/src/examples/user-interface/custom-shader.tsx
index 9d4c9d841..52902ea64 100644
--- a/examples/src/examples/user-interface/custom-shader.tsx
+++ b/examples/src/examples/user-interface/custom-shader.tsx
@@ -60,7 +60,7 @@ uniform float amount;
 void main(void)
 {
     vec4 color = texture2D(uDiffuseMap, vUv0);
-    vec3 roloc = vec3(1.0 - color.r, 1.0 - color.g, 1.0 - color.b);
+    vec3 roloc = 1.0 - color.rgb;
     gl_FragColor = vec4(mix(color.rgb, roloc, amount), color.a);
 }`
     };
diff --git a/package-lock.json b/package-lock.json
index 95a17e572..8a694e61b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
       "version": "1.54.0-dev",
       "license": "MIT",
       "devDependencies": {
-        "@babel/core": "^7.17.8",
+        "@babel/core": "^7.17.9",
         "@babel/eslint-parser": "^7.17.0",
         "@babel/preset-env": "^7.16.11",
         "@playcanvas/canvas-mock": "^1.0.1",
@@ -18,24 +18,24 @@
         "@rollup/plugin-babel": "^5.3.1",
         "@rollup/plugin-replace": "^4.0.0",
         "@rollup/plugin-strip": "^2.1.0",
-        "@rollup/pluginutils": "^4.2.0",
+        "@rollup/pluginutils": "^4.2.1",
         "c8": "^7.10.0",
         "chai": "^4.3.6",
-        "eslint": "^8.12.0",
+        "eslint": "^8.13.0",
         "jsdoc": "^3.6.10",
         "jsdoc-tsimport-plugin": "^1.0.5",
-        "karma": "^6.3.17",
+        "karma": "^6.3.18",
         "karma-chrome-launcher": "^3.1.1",
         "karma-mocha": "2.0.1",
-        "karma-spec-reporter": "^0.0.33",
+        "karma-spec-reporter": "^0.0.34",
         "mocha": "^9.2.2",
-        "rollup": "^2.70.1",
-        "rollup-plugin-dts": "^4.2.0",
+        "rollup": "^2.70.2",
+        "rollup-plugin-dts": "^4.2.1",
         "rollup-plugin-jscc": "2.0.0",
         "rollup-plugin-terser": "7.0.2",
         "rollup-plugin-visualizer": "^5.6.0",
         "serve": "^13.0.2",
-        "sinon": "^13.0.1",
+        "sinon": "^13.0.2",
         "typescript": "^4.6.3",
         "xhr2": "^0.2.1"
       }
@@ -74,25 +74,25 @@
       }
     },
     "node_modules/@babel/core": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz",
-      "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz",
+      "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==",
       "dev": true,
       "dependencies": {
         "@ampproject/remapping": "^2.1.0",
         "@babel/code-frame": "^7.16.7",
-        "@babel/generator": "^7.17.7",
+        "@babel/generator": "^7.17.9",
         "@babel/helper-compilation-targets": "^7.17.7",
         "@babel/helper-module-transforms": "^7.17.7",
-        "@babel/helpers": "^7.17.8",
-        "@babel/parser": "^7.17.8",
+        "@babel/helpers": "^7.17.9",
+        "@babel/parser": "^7.17.9",
         "@babel/template": "^7.16.7",
-        "@babel/traverse": "^7.17.3",
+        "@babel/traverse": "^7.17.9",
         "@babel/types": "^7.17.0",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
-        "json5": "^2.1.2",
+        "json5": "^2.2.1",
         "semver": "^6.3.0"
       },
       "engines": {
@@ -122,9 +122,9 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
-      "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz",
+      "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==",
       "dev": true,
       "dependencies": {
         "@babel/types": "^7.17.0",
@@ -259,26 +259,13 @@
       }
     },
     "node_modules/@babel/helper-function-name": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz",
-      "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz",
+      "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==",
       "dev": true,
       "dependencies": {
-        "@babel/helper-get-function-arity": "^7.16.7",
         "@babel/template": "^7.16.7",
-        "@babel/types": "^7.16.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-get-function-arity": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz",
-      "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.17.0"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -472,13 +459,13 @@
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz",
-      "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz",
+      "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==",
       "dev": true,
       "dependencies": {
         "@babel/template": "^7.16.7",
-        "@babel/traverse": "^7.17.3",
+        "@babel/traverse": "^7.17.9",
         "@babel/types": "^7.17.0"
       },
       "engines": {
@@ -500,9 +487,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz",
-      "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz",
+      "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==",
       "dev": true,
       "bin": {
         "parser": "bin/babel-parser.js"
@@ -1605,18 +1592,18 @@
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.17.3",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
-      "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz",
+      "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==",
       "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.16.7",
-        "@babel/generator": "^7.17.3",
+        "@babel/generator": "^7.17.9",
         "@babel/helper-environment-visitor": "^7.16.7",
-        "@babel/helper-function-name": "^7.16.7",
+        "@babel/helper-function-name": "^7.17.9",
         "@babel/helper-hoist-variables": "^7.16.7",
         "@babel/helper-split-export-declaration": "^7.16.7",
-        "@babel/parser": "^7.17.3",
+        "@babel/parser": "^7.17.9",
         "@babel/types": "^7.17.0",
         "debug": "^4.1.0",
         "globals": "^11.1.0"
@@ -1921,9 +1908,9 @@
       "dev": true
     },
     "node_modules/@rollup/pluginutils": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.0.tgz",
-      "integrity": "sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA==",
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
+      "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
       "dev": true,
       "dependencies": {
         "estree-walker": "^2.0.1",
@@ -1943,9 +1930,9 @@
       }
     },
     "node_modules/@sinonjs/fake-timers": {
-      "version": "9.1.1",
-      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.1.tgz",
-      "integrity": "sha512-Wp5vwlZ0lOqpSYGKqr53INws9HLkt6JDc/pDZcPf7bchQnrXJMXPns8CXx0hFikMSGSWfvtvvpb2gtMVfkWagA==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
+      "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
       "dev": true,
       "dependencies": {
         "@sinonjs/commons": "^1.7.0"
@@ -3131,9 +3118,9 @@
       }
     },
     "node_modules/eslint": {
-      "version": "8.12.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz",
-      "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==",
+      "version": "8.13.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz",
+      "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==",
       "dev": true,
       "dependencies": {
         "@eslint/eslintrc": "^1.2.1",
@@ -4434,13 +4421,10 @@
       "dev": true
     },
     "node_modules/json5": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
-      "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+      "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
       "dev": true,
-      "dependencies": {
-        "minimist": "^1.2.5"
-      },
       "bin": {
         "json5": "lib/cli.js"
       },
@@ -4467,9 +4451,9 @@
       "dev": true
     },
     "node_modules/karma": {
-      "version": "6.3.17",
-      "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.17.tgz",
-      "integrity": "sha512-2TfjHwrRExC8yHoWlPBULyaLwAFmXmxQrcuFImt/JsAsSZu1uOWTZ1ZsWjqQtWpHLiatJOHL5jFjXSJIgCd01g==",
+      "version": "6.3.18",
+      "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.18.tgz",
+      "integrity": "sha512-YEwXVHRILKWKN7uEW9IkgTPjnYGb3YA3MDvlp04xpSRAyrNPoRmsBayLDgHykKAwBm6/mAOckj4xi/1JdQfhzQ==",
       "dev": true,
       "dependencies": {
         "@colors/colors": "1.5.0",
@@ -4491,7 +4475,7 @@
         "qjobs": "^1.2.0",
         "range-parser": "^1.2.1",
         "rimraf": "^3.0.2",
-        "socket.io": "^4.2.0",
+        "socket.io": "^4.4.1",
         "source-map": "^0.6.1",
         "tmp": "^0.2.1",
         "ua-parser-js": "^0.7.30",
@@ -4535,9 +4519,9 @@
       }
     },
     "node_modules/karma-spec-reporter": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.33.tgz",
-      "integrity": "sha512-xRVevDUkiIVhKbDQ3CmeGEpyzA4b3HeVl95Sx5yJAvurpdKUSYF6ZEbQOqKJ7vrtDniABV1hyFez9KX9+7ruBA==",
+      "version": "0.0.34",
+      "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.34.tgz",
+      "integrity": "sha512-l5H/Nh9q4g2Ysx2CDU2m+NIPyLQpCVbk9c4V02BTZHw3NM6RO1dq3eRpKXCSSdPt4RGfhHk8jDt3XYkGp+5PWg==",
       "dev": true,
       "dependencies": {
         "colors": "1.4.0"
@@ -5672,9 +5656,9 @@
       }
     },
     "node_modules/rollup": {
-      "version": "2.70.1",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
-      "integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
+      "version": "2.70.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.2.tgz",
+      "integrity": "sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==",
       "dev": true,
       "bin": {
         "rollup": "dist/bin/rollup"
@@ -5687,15 +5671,15 @@
       }
     },
     "node_modules/rollup-plugin-dts": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.0.tgz",
-      "integrity": "sha512-lx6irWVhz/x4//tIqRhzk4FOqGQ0n37ZM2wpPCn4uafl/EmiV92om7ZdAsq7Bzho6C+Xh5GfsyuP9H+Udv72Lg==",
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.1.tgz",
+      "integrity": "sha512-eaxQZNUJ5iQcxNGlpJ1CUgG4OSVqWjDZ3nNSWBIoGrpcote2aNphSe1RJOaSYkb8dwn3o+rYm1vvld/5z3EGSQ==",
       "dev": true,
       "dependencies": {
         "magic-string": "^0.26.1"
       },
       "engines": {
-        "node": ">=v12.22.10"
+        "node": ">=v12.22.11"
       },
       "funding": {
         "url": "https://github.com/sponsors/Swatinem"
@@ -5704,20 +5688,8 @@
         "@babel/code-frame": "^7.16.7"
       },
       "peerDependencies": {
-        "rollup": "^2.55",
-        "typescript": "^4.1"
-      }
-    },
-    "node_modules/rollup-plugin-dts/node_modules/magic-string": {
-      "version": "0.26.1",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz",
-      "integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==",
-      "dev": true,
-      "dependencies": {
-        "sourcemap-codec": "^1.4.8"
-      },
-      "engines": {
-        "node": ">=12"
+        "rollup": "^2.70",
+        "typescript": "^4.6"
       }
     },
     "node_modules/rollup-plugin-dts/node_modules/magic-string": {
@@ -6010,13 +5982,13 @@
       "dev": true
     },
     "node_modules/sinon": {
-      "version": "13.0.1",
-      "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.1.tgz",
-      "integrity": "sha512-8yx2wIvkBjIq/MGY1D9h1LMraYW+z1X0mb648KZnKSdvLasvDu7maa0dFaNYdTDczFgbjNw2tOmWdTk9saVfwQ==",
+      "version": "13.0.2",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.2.tgz",
+      "integrity": "sha512-KvOrztAVqzSJWMDoxM4vM+GPys1df2VBoXm+YciyB/OLMamfS3VXh3oGh5WtrAGSzrgczNWFFY22oKb7Fi5eeA==",
       "dev": true,
       "dependencies": {
         "@sinonjs/commons": "^1.8.3",
-        "@sinonjs/fake-timers": "^9.0.0",
+        "@sinonjs/fake-timers": "^9.1.2",
         "@sinonjs/samsam": "^6.1.1",
         "diff": "^5.0.0",
         "nise": "^5.1.1",
@@ -6820,25 +6792,25 @@
       "dev": true
     },
     "@babel/core": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz",
-      "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz",
+      "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==",
       "dev": true,
       "requires": {
         "@ampproject/remapping": "^2.1.0",
         "@babel/code-frame": "^7.16.7",
-        "@babel/generator": "^7.17.7",
+        "@babel/generator": "^7.17.9",
         "@babel/helper-compilation-targets": "^7.17.7",
         "@babel/helper-module-transforms": "^7.17.7",
-        "@babel/helpers": "^7.17.8",
-        "@babel/parser": "^7.17.8",
+        "@babel/helpers": "^7.17.9",
+        "@babel/parser": "^7.17.9",
         "@babel/template": "^7.16.7",
-        "@babel/traverse": "^7.17.3",
+        "@babel/traverse": "^7.17.9",
         "@babel/types": "^7.17.0",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
-        "json5": "^2.1.2",
+        "json5": "^2.2.1",
         "semver": "^6.3.0"
       }
     },
@@ -6854,9 +6826,9 @@
       }
     },
     "@babel/generator": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
-      "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz",
+      "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==",
       "dev": true,
       "requires": {
         "@babel/types": "^7.17.0",
@@ -6955,23 +6927,13 @@
       }
     },
     "@babel/helper-function-name": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz",
-      "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz",
+      "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==",
       "dev": true,
       "requires": {
-        "@babel/helper-get-function-arity": "^7.16.7",
         "@babel/template": "^7.16.7",
-        "@babel/types": "^7.16.7"
-      }
-    },
-    "@babel/helper-get-function-arity": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz",
-      "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.17.0"
       }
     },
     "@babel/helper-hoist-variables": {
@@ -7119,13 +7081,13 @@
       }
     },
     "@babel/helpers": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz",
-      "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz",
+      "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==",
       "dev": true,
       "requires": {
         "@babel/template": "^7.16.7",
-        "@babel/traverse": "^7.17.3",
+        "@babel/traverse": "^7.17.9",
         "@babel/types": "^7.17.0"
       }
     },
@@ -7141,9 +7103,9 @@
       }
     },
     "@babel/parser": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz",
-      "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz",
+      "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==",
       "dev": true
     },
     "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
@@ -7880,18 +7842,18 @@
       }
     },
     "@babel/traverse": {
-      "version": "7.17.3",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
-      "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz",
+      "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.16.7",
-        "@babel/generator": "^7.17.3",
+        "@babel/generator": "^7.17.9",
         "@babel/helper-environment-visitor": "^7.16.7",
-        "@babel/helper-function-name": "^7.16.7",
+        "@babel/helper-function-name": "^7.17.9",
         "@babel/helper-hoist-variables": "^7.16.7",
         "@babel/helper-split-export-declaration": "^7.16.7",
-        "@babel/parser": "^7.17.3",
+        "@babel/parser": "^7.17.9",
         "@babel/types": "^7.17.0",
         "debug": "^4.1.0",
         "globals": "^11.1.0"
@@ -8130,9 +8092,9 @@
       }
     },
     "@rollup/pluginutils": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.0.tgz",
-      "integrity": "sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA==",
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
+      "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
       "dev": true,
       "requires": {
         "estree-walker": "^2.0.1",
@@ -8149,9 +8111,9 @@
       }
     },
     "@sinonjs/fake-timers": {
-      "version": "9.1.1",
-      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.1.tgz",
-      "integrity": "sha512-Wp5vwlZ0lOqpSYGKqr53INws9HLkt6JDc/pDZcPf7bchQnrXJMXPns8CXx0hFikMSGSWfvtvvpb2gtMVfkWagA==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
+      "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
       "dev": true,
       "requires": {
         "@sinonjs/commons": "^1.7.0"
@@ -9088,9 +9050,9 @@
       "dev": true
     },
     "eslint": {
-      "version": "8.12.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz",
-      "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==",
+      "version": "8.13.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz",
+      "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==",
       "dev": true,
       "requires": {
         "@eslint/eslintrc": "^1.2.1",
@@ -10063,13 +10025,10 @@
       "dev": true
     },
     "json5": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
-      "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
-      "dev": true,
-      "requires": {
-        "minimist": "^1.2.5"
-      }
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+      "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+      "dev": true
     },
     "jsonfile": {
       "version": "6.1.0",
@@ -10088,9 +10047,9 @@
       "dev": true
     },
     "karma": {
-      "version": "6.3.17",
-      "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.17.tgz",
-      "integrity": "sha512-2TfjHwrRExC8yHoWlPBULyaLwAFmXmxQrcuFImt/JsAsSZu1uOWTZ1ZsWjqQtWpHLiatJOHL5jFjXSJIgCd01g==",
+      "version": "6.3.18",
+      "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.18.tgz",
+      "integrity": "sha512-YEwXVHRILKWKN7uEW9IkgTPjnYGb3YA3MDvlp04xpSRAyrNPoRmsBayLDgHykKAwBm6/mAOckj4xi/1JdQfhzQ==",
       "dev": true,
       "requires": {
         "@colors/colors": "1.5.0",
@@ -10112,7 +10071,7 @@
         "qjobs": "^1.2.0",
         "range-parser": "^1.2.1",
         "rimraf": "^3.0.2",
-        "socket.io": "^4.2.0",
+        "socket.io": "^4.4.1",
         "source-map": "^0.6.1",
         "tmp": "^0.2.1",
         "ua-parser-js": "^0.7.30",
@@ -10166,9 +10125,9 @@
       }
     },
     "karma-spec-reporter": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.33.tgz",
-      "integrity": "sha512-xRVevDUkiIVhKbDQ3CmeGEpyzA4b3HeVl95Sx5yJAvurpdKUSYF6ZEbQOqKJ7vrtDniABV1hyFez9KX9+7ruBA==",
+      "version": "0.0.34",
+      "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.34.tgz",
+      "integrity": "sha512-l5H/Nh9q4g2Ysx2CDU2m+NIPyLQpCVbk9c4V02BTZHw3NM6RO1dq3eRpKXCSSdPt4RGfhHk8jDt3XYkGp+5PWg==",
       "dev": true,
       "requires": {
         "colors": "1.4.0"
@@ -11015,33 +10974,24 @@
       }
     },
     "rollup": {
-      "version": "2.70.1",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
-      "integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
+      "version": "2.70.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.2.tgz",
+      "integrity": "sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==",
       "dev": true,
       "requires": {
         "fsevents": "~2.3.2"
       }
     },
     "rollup-plugin-dts": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.0.tgz",
-      "integrity": "sha512-lx6irWVhz/x4//tIqRhzk4FOqGQ0n37ZM2wpPCn4uafl/EmiV92om7ZdAsq7Bzho6C+Xh5GfsyuP9H+Udv72Lg==",
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.1.tgz",
+      "integrity": "sha512-eaxQZNUJ5iQcxNGlpJ1CUgG4OSVqWjDZ3nNSWBIoGrpcote2aNphSe1RJOaSYkb8dwn3o+rYm1vvld/5z3EGSQ==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.16.7",
         "magic-string": "^0.26.1"
       },
       "dependencies": {
-        "magic-string": {
-          "version": "0.26.1",
-          "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz",
-          "integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==",
-          "dev": true,
-          "requires": {
-            "sourcemap-codec": "^1.4.8"
-          }
-        },
         "magic-string": {
           "version": "0.26.1",
           "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz",
@@ -11284,13 +11234,13 @@
       "dev": true
     },
     "sinon": {
-      "version": "13.0.1",
-      "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.1.tgz",
-      "integrity": "sha512-8yx2wIvkBjIq/MGY1D9h1LMraYW+z1X0mb648KZnKSdvLasvDu7maa0dFaNYdTDczFgbjNw2tOmWdTk9saVfwQ==",
+      "version": "13.0.2",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.2.tgz",
+      "integrity": "sha512-KvOrztAVqzSJWMDoxM4vM+GPys1df2VBoXm+YciyB/OLMamfS3VXh3oGh5WtrAGSzrgczNWFFY22oKb7Fi5eeA==",
       "dev": true,
       "requires": {
         "@sinonjs/commons": "^1.8.3",
-        "@sinonjs/fake-timers": "^9.0.0",
+        "@sinonjs/fake-timers": "^9.1.2",
         "@sinonjs/samsam": "^6.1.1",
         "diff": "^5.0.0",
         "nise": "^5.1.1",
diff --git a/package.json b/package.json
index bb5439ff2..0ff465bd9 100644
--- a/package.json
+++ b/package.json
@@ -67,7 +67,7 @@
     "README-zh.md"
   ],
   "devDependencies": {
-    "@babel/core": "^7.17.8",
+    "@babel/core": "^7.17.9",
     "@babel/eslint-parser": "^7.17.0",
     "@babel/preset-env": "^7.16.11",
     "@playcanvas/canvas-mock": "^1.0.1",
@@ -76,24 +76,24 @@
     "@rollup/plugin-babel": "^5.3.1",
     "@rollup/plugin-replace": "^4.0.0",
     "@rollup/plugin-strip": "^2.1.0",
-    "@rollup/pluginutils": "^4.2.0",
+    "@rollup/pluginutils": "^4.2.1",
     "c8": "^7.10.0",
     "chai": "^4.3.6",
-    "eslint": "^8.12.0",
+    "eslint": "^8.13.0",
     "jsdoc": "^3.6.10",
     "jsdoc-tsimport-plugin": "^1.0.5",
-    "karma": "^6.3.17",
+    "karma": "^6.3.18",
     "karma-chrome-launcher": "^3.1.1",
     "karma-mocha": "2.0.1",
-    "karma-spec-reporter": "^0.0.33",
+    "karma-spec-reporter": "^0.0.34",
     "mocha": "^9.2.2",
-    "rollup": "^2.70.1",
-    "rollup-plugin-dts": "^4.2.0",
+    "rollup": "^2.70.2",
+    "rollup-plugin-dts": "^4.2.1",
     "rollup-plugin-jscc": "2.0.0",
     "rollup-plugin-terser": "7.0.2",
     "rollup-plugin-visualizer": "^5.6.0",
     "serve": "^13.0.2",
-    "sinon": "^13.0.1",
+    "sinon": "^13.0.2",
     "typescript": "^4.6.3",
     "xhr2": "^0.2.1"
   },
diff --git a/src/framework/components/element/component.js b/src/framework/components/element/component.js
index 4b7c7787a..28eb1ede7 100644
--- a/src/framework/components/element/component.js
+++ b/src/framework/components/element/component.js
@@ -770,7 +770,7 @@ class ElementComponent extends Component {
             }
         } else {
             if (this._useInput === true) {
-                console.warn('Elements will not get any input events because this.system.app.elementInput is not created');
+                Debug.warn('Elements will not get any input events because this.system.app.elementInput is not created');
             }
         }
 
diff --git a/src/graphics/constants.js b/src/graphics/constants.js
index 5acddaf8b..e8d79970c 100644
--- a/src/graphics/constants.js
+++ b/src/graphics/constants.js
@@ -97,6 +97,34 @@ export const BLENDMODE_DST_ALPHA = 9;
  */
 export const BLENDMODE_ONE_MINUS_DST_ALPHA = 10;
 
+/**
+ * Multiplies all colors by a constant color.
+ *
+ * @type {number}
+ */
+export const BLENDMODE_CONSTANT_COLOR = 11;
+
+/**
+ * Multiplies all colors by 1 minus a constant color.
+ *
+ * @type {number}
+ */
+export const BLENDMODE_ONE_MINUS_CONSTANT_COLOR = 12;
+
+/**
+ * Multiplies all colors by a constant alpha value.
+ *
+ * @type {number}
+ */
+export const BLENDMODE_CONSTANT_ALPHA = 13;
+
+/**
+ * Multiplies all colors by 1 minus a constant alpha value.
+ *
+ * @type {number}
+ */
+export const BLENDMODE_ONE_MINUS_CONSTANT_ALPHA = 14;
+
 /**
  * Add the results of the source and destination fragment multiplies.
  *
diff --git a/src/graphics/env-lighting.js b/src/graphics/env-lighting.js
index d8f10df9c..1e51622ce 100644
--- a/src/graphics/env-lighting.js
+++ b/src/graphics/env-lighting.js
@@ -161,7 +161,7 @@ class EnvLighting {
 
         // generate mipmaps
         const rect = new Vec4(0, 0, 512 * s, 256 * s);
-        const levels = calcLevels(512);
+        const levels = calcLevels(256) - calcLevels(4);
         for (let i = 0; i < levels; ++i) {
             reprojectTexture(source, result, {
                 numSamples: 1,
diff --git a/src/graphics/program-lib/chunks/lit/reflectionEnv.frag.js b/src/graphics/program-lib/chunks/lit/reflectionEnv.frag.js
index ae45f05f2..2bd278755 100644
--- a/src/graphics/program-lib/chunks/lit/reflectionEnv.frag.js
+++ b/src/graphics/program-lib/chunks/lit/reflectionEnv.frag.js
@@ -18,7 +18,7 @@ float shinyMipLevel(vec2 uv) {
     // calculate min of both sets of dF to handle discontinuity at the azim edge
     float maxd = min(max(dot(dx, dx), dot(dy, dy)), max(dot(dx2, dx2), dot(dy2, dy2)));
 
-    return clamp(0.5 * log2(maxd) - 1.0 + textureBias, 0.0, 6.0);
+    return clamp(0.5 * log2(maxd) - 1.0 + textureBias, 0.0, 5.0);
 }
 
 vec3 calcReflection(vec3 tReflDirW, float tGlossiness) {
diff --git a/src/graphics/webgl/webgl-graphics-device.js b/src/graphics/webgl/webgl-graphics-device.js
index 83eaf2e44..428d96317 100644
--- a/src/graphics/webgl/webgl-graphics-device.js
+++ b/src/graphics/webgl/webgl-graphics-device.js
@@ -35,6 +35,7 @@ import { WebglIndexBuffer } from './webgl-index-buffer.js';
 import { WebglShader } from './webgl-shader.js';
 import { WebglTexture } from './webgl-texture.js';
 import { WebglRenderTarget } from './webgl-render-target.js';
+import { Color } from '../../math/color.js';
 
 /** @typedef {import('../index-buffer.js').IndexBuffer} IndexBuffer */
 /** @typedef {import('../shader.js').Shader} Shader */
@@ -338,7 +339,11 @@ class WebglGraphicsDevice extends GraphicsDevice {
             gl.SRC_ALPHA_SATURATE,
             gl.ONE_MINUS_SRC_ALPHA,
             gl.DST_ALPHA,
-            gl.ONE_MINUS_DST_ALPHA
+            gl.ONE_MINUS_DST_ALPHA,
+            gl.CONSTANT_COLOR,
+            gl.ONE_MINUS_CONSTANT_COLOR,
+            gl.CONSTANT_ALPHA,
+            gl.ONE_MINUS_CONSTANT_ALPHA
         ];
 
         this.glComparison = [
@@ -915,6 +920,9 @@ class WebglGraphicsDevice extends GraphicsDevice {
         gl.blendFunc(gl.ONE, gl.ZERO);
         gl.blendEquation(gl.FUNC_ADD);
 
+        this.blendColor = new Color(0, 0, 0, 0);
+        gl.blendColor(0, 0, 0, 0);
+
         this.writeRed = true;
         this.writeGreen = true;
         this.writeBlue = true;
@@ -963,10 +971,7 @@ class WebglGraphicsDevice extends GraphicsDevice {
         this.clearDepth = 1;
         gl.clearDepth(1);
 
-        this.clearRed = 0;
-        this.clearBlue = 0;
-        this.clearGreen = 0;
-        this.clearAlpha = 0;
+        this.clearColor = new Color(0, 0, 0, 0);
         gl.clearColor(0, 0, 0, 0);
 
         this.clearStencil = 0;
@@ -1879,12 +1884,10 @@ class WebglGraphicsDevice extends GraphicsDevice {
      * @ignore
      */
     setClearColor(r, g, b, a) {
-        if ((r !== this.clearRed) || (g !== this.clearGreen) || (b !== this.clearBlue) || (a !== this.clearAlpha)) {
+        const c = this.clearColor;
+        if ((r !== c.r) || (g !== c.g) || (b !== c.b) || (a !== c.a)) {
             this.gl.clearColor(r, g, b, a);
-            this.clearRed = r;
-            this.clearGreen = g;
-            this.clearBlue = b;
-            this.clearAlpha = a;
+            this.clearColor.set(r, g, b, a);
         }
     }
 
@@ -2355,6 +2358,10 @@ class WebglGraphicsDevice extends GraphicsDevice {
      * - {@link BLENDMODE_ONE_MINUS_SRC_ALPHA}
      * - {@link BLENDMODE_DST_ALPHA}
      * - {@link BLENDMODE_ONE_MINUS_DST_ALPHA}
+     * - {@link BLENDMODE_CONSTANT_COLOR}
+     * - {@link BLENDMODE_ONE_MINUS_CONSTANT_COLOR}
+     * - {@link BLENDMODE_CONSTANT_ALPHA}
+     * - {@link BLENDMODE_ONE_MINUS_CONSTANT_ALPHA}
      *
      * @param {number} blendSrc - The source blend function.
      * @param {number} blendDst - The destination blend function.
@@ -2448,6 +2455,23 @@ class WebglGraphicsDevice extends GraphicsDevice {
         }
     }
 
+    /**
+     * Set the source and destination blending factors.
+     *
+     * @param {number} r - The red component in the range of 0 to 1. Default value is 0.
+     * @param {number} g - The green component in the range of 0 to 1. Default value is 0.
+     * @param {number} b - The blue component in the range of 0 to 1. Default value is 0.
+     * @param {number} a - The alpha component in the range of 0 to 1. Default value is 0.
+     * @ignore
+     */
+    setBlendColor(r, g, b, a) {
+        const c = this.blendColor;
+        if ((r !== c.r) || (g !== c.g) || (b !== c.b) || (a !== c.a)) {
+            this.gl.blendColor(r, g, b, a);
+            c.set(r, g, b, a);
+        }
+    }
+
     /**
      * Controls how triangles are culled based on their face direction. The default cull mode is
      * {@link CULLFACE_BACK}.
diff --git a/src/resources/container.js b/src/resources/container.js
index 13d5323d1..74c9f11dd 100644
--- a/src/resources/container.js
+++ b/src/resources/container.js
@@ -118,6 +118,19 @@ class ContainerHandler {
         this.parsers = { };
     }
 
+    set maxRetries(value) {
+        this.glbParser.maxRetries = value;
+        for (const parser in this.parsers) {
+            if (this.parsers.hasOwnProperty(parser)) {
+                this.parsers[parser].maxRetries = value;
+            }
+        }
+    }
+
+    get maxRetries() {
+        return this.glbParser.maxRetries;
+    }
+
     /**
      * @param {string} url - The resource URL.
      * @returns {string} The URL with query parameters removed.
diff --git a/src/resources/parser/glb-parser.js b/src/resources/parser/glb-parser.js
index 01c95a217..97c131f42 100644
--- a/src/resources/parser/glb-parser.js
+++ b/src/resources/parser/glb-parser.js
@@ -2294,7 +2294,7 @@ class GlbParser {
         this._device = device;
         this._assets = assets;
         this._defaultMaterial = getDefaultMaterial(device);
-        this._maxRetries = maxRetries;
+        this.maxRetries = maxRetries;
     }
 
     _getUrlWithoutParams(url) {
@@ -2322,7 +2322,7 @@ class GlbParser {
                         }
                     });
             }
-        }, asset, this._maxRetries);
+        }, asset, this.maxRetries);
     }
 
     open(url, data, asset) {
diff --git a/src/sound/manager.js b/src/sound/manager.js
index f846abb67..04191605a 100644
--- a/src/sound/manager.js
+++ b/src/sound/manager.js
@@ -1,3 +1,5 @@
+import { Debug } from '../core/debug.js';
+
 import { platform } from '../core/platform.js';
 import { EventHandler } from '../core/event-handler.js';
 
@@ -62,7 +64,7 @@ class SoundManager extends EventHandler {
         if (hasAudioContext() || this._forceWebAudioApi) {
             this._addAudioContextUserInteractionListeners();
         } else {
-            console.warn('No support for 3D audio found');
+            Debug.warn('No support for 3D audio found');
         }
 
         this.listener = new Listener(this);


Test patch:
diff --git a/test/asset/asset-registry.test.mjs b/test/asset/asset-registry.test.mjs
index caca011a9..920073ccd 100644
--- a/test/asset/asset-registry.test.mjs
+++ b/test/asset/asset-registry.test.mjs
@@ -3,22 +3,29 @@ import { Asset } from '../../src/asset/asset.js';
 import { AssetRegistry } from '../../src/asset/asset-registry.js';
 import { GlbContainerResource } from '../../src/resources/parser/glb-container-resource.js';
 import { ResourceLoader } from '../../src/resources/loader.js';
+import { http, Http } from '../../src/net/http.js';
 
 import { HTMLCanvasElement } from '@playcanvas/canvas-mock';
 
 import { expect } from 'chai';
+import sinon from 'sinon';
 
 describe('AssetRegistry', function () {
 
     let app;
+    let retryDelay;
 
     beforeEach(function () {
+        retryDelay = Http.retryDelay;
+        Http.retryDelay = 1;
         const canvas = new HTMLCanvasElement(500, 500);
         app = new Application(canvas);
     });
 
     afterEach(function () {
         app.destroy();
+        Http.retryDelay = retryDelay;
+        sinon.restore();
     });
 
     describe('#constructor', function () {
@@ -178,6 +185,15 @@ describe('AssetRegistry', function () {
             });
         });
 
+        it('supports retry loading of container assets', function (done) {
+            sinon.spy(http, 'request');
+            app.loader.enableRetry(2);
+            app.assets.loadFromUrl(`${assetPath}someurl.glb`, 'container', function (err, asset) {
+                expect(http.request.callCount).to.equal(3);
+                done();
+            });
+        });
+
         it('loads css assets', function (done) {
             app.assets.loadFromUrl(`${assetPath}test.css`, 'css', function (err, asset) {
                 expect(err).to.be.null;