From 7535aab751b65395063152a70e5c6e19a7a98678 Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Sat, 16 Dec 2023 00:32:21 -0800 Subject: [PATCH 1/6] tweak background --- examples/background.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/background.js b/examples/background.js index 5369d1c..df7bbd3 100644 --- a/examples/background.js +++ b/examples/background.js @@ -100,11 +100,11 @@ async function main() { let xy = vec2u(fsInput.position.xy); let color = textureLoad(colorTexture, xy, 0); let normal = textureLoad(normalTexture, xy, 0).xyz * 2.0 - 1.0; - let normalR = textureLoad(normalTexture, xy + vec2u(2, 0), 0).xyz * 2.0 - 1.0; - let normalL = textureLoad(normalTexture, xy + vec2u(0, 2), 0).xyz * 2.0 - 1.0; + let normalR = textureLoad(normalTexture, xy + vec2u(4, 0), 0).xyz * 2.0 - 1.0; + let normalL = textureLoad(normalTexture, xy + vec2u(0, 4), 0).xyz * 2.0 - 1.0; let depth = textureLoad(depthTexture, xy, 0).r; - let depthR = textureLoad(depthTexture, xy + vec2u(1, 0), 0).r; - let depthU = textureLoad(depthTexture, xy + vec2u(0, 1), 0).r; + let depthR = textureLoad(depthTexture, xy + vec2u(2, 0), 0).r; + let depthU = textureLoad(depthTexture, xy + vec2u(0, 2), 0).r; let maxDepthDiff = max(abs(depth - depthR), abs(depth - depthU)); let dotR = 1.0 - (dot(normal, normalR) * 0.5 + 0.5); let dotL = 1.0 - (dot(normal, normalL) * 0.5 + 0.5); @@ -177,7 +177,7 @@ async function main() { fsUniformValues.set({ lightDirection: vec3.normalize([1, 8, -10]), - color: cssColorToRGBA(hsl(i / numObjects * 0.2 + 0.4, 1, .3)), + color: cssColorToRGBA(hsl(i / numObjects * 0.2 + 0.6, 1, .6)), }); device.queue.writeBuffer(fsUniformBuffer, 0, fsUniformValues.arrayBuffer); From b4d489513857b24f262790954fc8e80188d9464a Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Sat, 16 Dec 2023 00:39:25 -0800 Subject: [PATCH 2/6] tweak background --- examples/background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/background.js b/examples/background.js index df7bbd3..b2d6835 100644 --- a/examples/background.js +++ b/examples/background.js @@ -109,7 +109,7 @@ async function main() { let dotR = 1.0 - (dot(normal, normalR) * 0.5 + 0.5); let dotL = 1.0 - (dot(normal, normalL) * 0.5 + 0.5); let maxDot = max(dotR, dotL); - let c = mix(color, vec4f(1), saturate(maxDot + maxDepthDiff)); // > 0.1 || maxDepthDiff > 0.15); + let c = mix(color, vec4f(1), saturate(pow(maxDot + 0.5, 5.0) + maxDepthDiff * 2.0)); // > 0.1 || maxDepthDiff > 0.15); return c; } `; From b493966c7b9ed45e0e9f86f496ae2f6d08b67055 Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Sat, 23 Dec 2023 14:19:19 -0800 Subject: [PATCH 3/6] Make start do both watch and server --- README.md | 8 +- build/tools/serve.js | 16 ++ package-lock.json | 405 ++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- rollup.config.js | 8 + 5 files changed, 431 insertions(+), 9 deletions(-) create mode 100644 build/tools/serve.js diff --git a/README.md b/README.md index d2fd5ce..0ef94d0 100644 --- a/README.md +++ b/README.md @@ -346,16 +346,12 @@ import { createTextureFromImage } from 'webgpu-utils'; ``` git clone https://github.com/greggman/webgpu-utils.git cd webgpu-utils -npm install +npm ci npm start ``` This will run rollup in watch mode, building from typescript into -`dist/1.x/webgpu-utils.js`. - -``` -npx servez -``` +`dist/1.x/webgpu-utils.js` and start a server Now open [`http://localhost:8080/test/`](http://localhost:8080/test/) to run tests. diff --git a/build/tools/serve.js b/build/tools/serve.js new file mode 100644 index 0000000..377844a --- /dev/null +++ b/build/tools/serve.js @@ -0,0 +1,16 @@ +import {spawn} from 'child_process'; + +spawn('npm', [ + 'run', + 'watch', +], { + shell: true, + stdio: 'inherit', +}); + +spawn('./node_modules/.bin/servez', [ +], { + shell: true, + stdio: 'inherit', +}); + diff --git a/package-lock.json b/package-lock.json index fcb5b56..927dd40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "mocha": "^10.2.0", "puppeteer": "^21.0.1", "rollup": "^3.27.1", + "servez": "^2.1.2", "tslib": "^2.6.1", "typedoc": "^0.24.8", "typescript": "^5.1.6", @@ -529,8 +530,16 @@ "version": "20.4.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.7.tgz", "integrity": "sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g==", + "dev": true + }, + "node_modules/@types/node-forge": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.10.tgz", + "integrity": "sha512-y6PJDYN4xYBxwd22l+OVH35N+1fCYWiuC3aiP2SlXVE6Lo7SS+rSx9r89hLxrP4pn6n1lBGhHJ12pj3F3Mpttw==", "dev": true, - "optional": true + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/resolve": { "version": "1.20.2", @@ -929,6 +938,24 @@ } ] }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/basic-ftp": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", @@ -938,6 +965,12 @@ "node": ">=10.0.0" } }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1204,6 +1237,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -1252,6 +1294,19 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", @@ -3003,6 +3058,15 @@ } } }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3012,6 +3076,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -3554,6 +3627,25 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -3617,6 +3709,84 @@ "randombytes": "^2.1.0" } }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -3632,6 +3802,52 @@ "node": ">= 0.8.0" } }, + "node_modules/server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "dev": true + }, + "node_modules/servez": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/servez/-/servez-2.1.2.tgz", + "integrity": "sha512-uJ/GlvogUfe52p9E5p9maEMQzycEMiz1doWkW8rke33WezHQ1P6/xPBqerOoeK9ze24Am1vJey7j/ddtySJTwg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "color-support": "^1.1.3", + "commander": "^11.0.0", + "servez-lib": "^2.8.1" + }, + "bin": { + "servez": "bin/servez" + } + }, + "node_modules/servez-lib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/servez-lib/-/servez-lib-2.8.1.tgz", + "integrity": "sha512-Wg/hvTxBPJ0ZqlQKTDktMDEAi/V5/wXdEaj4/O6SM1ZmqwWQoHDw0Yyg7l/G91znMQOCkyEdfAFPasOpjQWbNg==", + "dev": true, + "dependencies": { + "basic-auth": "^2.0.1", + "cors": "^2.8.5", + "debug": "^4.3.4", + "express": "^4.18.2", + "secure-compare": "^3.0.1", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "server-destroy": "^1.0.1" + } + }, + "node_modules/servez/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -4648,8 +4864,16 @@ "version": "20.4.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.7.tgz", "integrity": "sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g==", + "dev": true + }, + "@types/node-forge": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.10.tgz", + "integrity": "sha512-y6PJDYN4xYBxwd22l+OVH35N+1fCYWiuC3aiP2SlXVE6Lo7SS+rSx9r89hLxrP4pn6n1lBGhHJ12pj3F3Mpttw==", "dev": true, - "optional": true + "requires": { + "@types/node": "*" + } }, "@types/resolve": { "version": "1.20.2", @@ -4906,12 +5130,35 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, "basic-ftp": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==", "dev": true }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -5108,6 +5355,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -5147,6 +5400,16 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", @@ -6464,12 +6727,24 @@ "whatwg-url": "^5.0.0" } }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -6846,6 +7121,22 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true + }, + "selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "requires": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + } + }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -6904,6 +7195,74 @@ "randombytes": "^2.1.0" } }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + } + } + }, "serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -6916,6 +7275,48 @@ "send": "0.18.0" } }, + "server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "dev": true + }, + "servez": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/servez/-/servez-2.1.2.tgz", + "integrity": "sha512-uJ/GlvogUfe52p9E5p9maEMQzycEMiz1doWkW8rke33WezHQ1P6/xPBqerOoeK9ze24Am1vJey7j/ddtySJTwg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1", + "color-support": "^1.1.3", + "commander": "^11.0.0", + "servez-lib": "^2.8.1" + }, + "dependencies": { + "commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true + } + } + }, + "servez-lib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/servez-lib/-/servez-lib-2.8.1.tgz", + "integrity": "sha512-Wg/hvTxBPJ0ZqlQKTDktMDEAi/V5/wXdEaj4/O6SM1ZmqwWQoHDw0Yyg7l/G91znMQOCkyEdfAFPasOpjQWbNg==", + "dev": true, + "requires": { + "basic-auth": "^2.0.1", + "cors": "^2.8.5", + "debug": "^4.3.4", + "express": "^4.18.2", + "secure-compare": "^3.0.1", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "server-destroy": "^1.0.1" + } + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", diff --git a/package.json b/package.json index b32a3ea..b4e2d31 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "lint": "eslint \"src/**/*.{js,ts,tsx}\"", "pre-push": "npm run lint && npm run build && npm run test", "watch": "rollup -c -w", - "start": "rollup -c rollup.config.js -w", + "start": "node build/tools/serve.js", "test": "node test/puppeteer.js" }, "repository": { @@ -60,6 +60,7 @@ "mocha": "^10.2.0", "puppeteer": "^21.0.1", "rollup": "^3.27.1", + "servez": "^2.1.2", "tslib": "^2.6.1", "typedoc": "^0.24.8", "typescript": "^5.1.6", diff --git a/rollup.config.js b/rollup.config.js index 345c0ea..8200c75 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -12,6 +12,11 @@ const plugins = [ nodeResolve(), typescript({ tsconfig: './tsconfig.json' }), ]; +const shared = { + watch: { + clearScreen: false, + }, +}; export default [ { @@ -25,6 +30,7 @@ export default [ }, ], plugins, + ...shared, }, { input: 'src/webgpu-utils.ts', @@ -38,6 +44,7 @@ export default [ }, ], plugins, + ...shared, }, { input: 'src/webgpu-utils.ts', @@ -54,5 +61,6 @@ export default [ ...plugins, terser(), ], + ...shared, }, ]; From 20ca40d17ebb677a176bfea158e7c974cc145b8b Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Sat, 23 Dec 2023 13:57:18 -0800 Subject: [PATCH 4/6] Make generateMipmap compatible with compat mode --- src/generate-mipmap.ts | 180 +++++++++++++------ test/tests/generate-mipmap-test.js | 269 +++++++++++++++++------------ test/webgpu.js | 12 +- 3 files changed, 288 insertions(+), 173 deletions(-) diff --git a/src/generate-mipmap.ts b/src/generate-mipmap.ts index 4d1a056..565bccc 100644 --- a/src/generate-mipmap.ts +++ b/src/generate-mipmap.ts @@ -2,7 +2,7 @@ import { isTypedArray, } from './typed-arrays.js'; -function getViewDimensionForTexture(texture: GPUTexture): GPUTextureViewDimension { +function guessTextureBindingViewDimensionForTexture(texture: GPUTexture): GPUTextureViewDimension { switch (texture.dimension) { case '1d': return '1d'; @@ -10,7 +10,7 @@ function getViewDimensionForTexture(texture: GPUTexture): GPUTextureViewDimensio return '3d'; default: // to shut up TS case '2d': - return texture.depthOrArrayLayers > 1 ? '2d-array' : '2d'; + return texture.depthOrArrayLayers > 1 ? '2d-array' : '2d'; } } @@ -48,40 +48,51 @@ export function numMipLevels(size: GPUExtent3D, dimension?: GPUTextureDimension) return 1 + Math.log2(maxSize) | 0; } -// Use a WeakMap so the device can be destroyed and/or lost -const byDevice = new WeakMap(); +function getMipmapGenerationWGSL(textureBindingViewDimension: GPUTextureViewDimension) { + let textureSnippet; + let sampleSnippet; + switch (textureBindingViewDimension) { + case '2d': + textureSnippet = 'texture_2d'; + sampleSnippet = 'textureSample(ourTexture, ourSampler, fsInput.texcoord)'; + break; + case '2d-array': + textureSnippet = 'texture_2d_array'; + sampleSnippet = ` + textureSample( + ourTexture, + ourSampler, + fsInput.texcoord, + uni.layer)`; + break; + case 'cube': + textureSnippet = 'texture_cube'; + sampleSnippet = ` + textureSample( + ourTexture, + ourSampler, + faceMat[uni.layer] * vec3f(fract(fsInput.texcoord), 1))`; + break; + case 'cube-array': + textureSnippet = 'texture_cube_array'; + sampleSnippet = ` + textureSample( + ourTexture, + ourSampler, + faceMat[uni.layer] * vec3f(fract(fsInput.texcoord), 1), uni.layer)`; + break; + default: + throw new Error(`unsupported view: ${textureBindingViewDimension}`); + } + return ` + const faceMat = array( + mat3x3f( 0, 0, -2, 0, -2, 0, 1, 1, 1), // pos-x + mat3x3f( 0, 0, 2, 0, -2, 0, -1, 1, -1), // neg-x + mat3x3f( 2, 0, 0, 0, 0, 2, -1, 1, -1), // pos-y + mat3x3f( 2, 0, 0, 0, 0, -2, -1, -1, 1), // neg-y + mat3x3f( 2, 0, 0, 0, -2, 0, -1, 1, 1), // pos-z + mat3x3f(-2, 0, 0, 0, -2, 0, 1, 1, -1)); // neg-z -/** - * Generates mip levels from level 0 to the last mip for an existing texture - * - * The texture must have been created with TEXTURE_BINDING and - * RENDER_ATTACHMENT and been created with mip levels - * - * @param device - * @param texture - */ -export function generateMipmap(device: GPUDevice, texture: GPUTexture) { - let perDeviceInfo = byDevice.get(device); - if (!perDeviceInfo) { - perDeviceInfo = { - pipelineByFormat: {}, - moduleByView: {}, - }; - byDevice.set(device, perDeviceInfo); - } - let { - sampler, - } = perDeviceInfo; - const { - pipelineByFormat, - moduleByView, - } = perDeviceInfo; - const view = getViewDimensionForTexture(texture); - let module = moduleByView[view]; - if (!module) { - module = device.createShaderModule({ - label: `mip level generation for ${view}`, - code: ` struct VSOutput { @builtin(position) position: vec4f, @location(0) texcoord: vec2f, @@ -103,29 +114,85 @@ export function generateMipmap(device: GPUDevice, texture: GPUTexture) { return vsOutput; } + struct Uniforms { + layer: u32, + }; + @group(0) @binding(0) var ourSampler: sampler; - @group(0) @binding(1) var ourTexture: texture_2d; + @group(0) @binding(1) var ourTexture: ${textureSnippet}; + @group(0) @binding(2) var uni: Uniforms; @fragment fn fs(fsInput: VSOutput) -> @location(0) vec4f { - return textureSample(ourTexture, ourSampler, fsInput.texcoord); + _ = uni.layer; // make sure this is used so all pipelines have the same bindings + return ${sampleSnippet}; } - `, + `; +} + +// Use a WeakMap so the device can be destroyed and/or lost +const byDevice = new WeakMap(); + +/** + * Generates mip levels from level 0 to the last mip for an existing texture + * + * The texture must have been created with TEXTURE_BINDING and RENDER_ATTACHMENT + * and been created with mip levels + * + * @param device A GPUDevice + * @param texture The texture to create mips for + * @param textureBindingViewDimension This is only needed in compatibility mode + * and it is only needed when the texture is going to be used as a cube map. + */ +export function generateMipmap( + device: GPUDevice, + texture: GPUTexture, + textureBindingViewDimension?: GPUTextureViewDimension) { + let perDeviceInfo = byDevice.get(device); + if (!perDeviceInfo) { + perDeviceInfo = { + pipelineByFormatAndView: {}, + moduleByViewType: {}, + }; + byDevice.set(device, perDeviceInfo); + } + let { + sampler, + uniformBuffer, + uniformValues, + } = perDeviceInfo; + const { + pipelineByFormatAndView, + moduleByViewType, + } = perDeviceInfo; + textureBindingViewDimension = textureBindingViewDimension || guessTextureBindingViewDimensionForTexture(texture); + let module = moduleByViewType[textureBindingViewDimension]; + if (!module) { + const code = getMipmapGenerationWGSL(textureBindingViewDimension); + module = device.createShaderModule({ + label: `mip level generation for ${textureBindingViewDimension}`, + code, }); - moduleByView[view] = module; + moduleByViewType[textureBindingViewDimension] = module; } if (!sampler) { sampler = device.createSampler({ minFilter: 'linear', + magFilter: 'linear', + }); + uniformBuffer = device.createBuffer({ + size: 16, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, }); - perDeviceInfo.sampler = sampler; + uniformValues = new Uint32Array(1); + Object.assign(perDeviceInfo, { sampler, uniformBuffer, uniformValues }); } - const id = `${texture.format}`; + const id = `${texture.format}.${textureBindingViewDimension}`; - if (!pipelineByFormat[id]) { - pipelineByFormat[id] = device.createRenderPipeline({ - label: `mip level generator pipeline for ${view}`, + if (!pipelineByFormatAndView[id]) { + pipelineByFormatAndView[id] = device.createRenderPipeline({ + label: `mip level generator pipeline for ${textureBindingViewDimension}`, layout: 'auto', vertex: { module, @@ -138,14 +205,13 @@ export function generateMipmap(device: GPUDevice, texture: GPUTexture) { }, }); } - const pipeline = pipelineByFormat[id]; - - const encoder = device.createCommandEncoder({ - label: 'mip gen encoder', - }); + const pipeline = pipelineByFormatAndView[id]; for (let baseMipLevel = 1; baseMipLevel < texture.mipLevelCount; ++baseMipLevel) { for (let baseArrayLayer = 0; baseArrayLayer < texture.depthOrArrayLayers; ++baseArrayLayer) { + uniformValues[0] = baseArrayLayer; + device.queue.writeBuffer(uniformBuffer, 0, uniformValues); + const bindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [ @@ -153,13 +219,12 @@ export function generateMipmap(device: GPUDevice, texture: GPUTexture) { { binding: 1, resource: texture.createView({ - dimension: '2d', + dimension: textureBindingViewDimension, baseMipLevel: baseMipLevel - 1, mipLevelCount: 1, - baseArrayLayer, - arrayLayerCount: 1, }), }, + { binding: 2, resource: { buffer: uniformBuffer }}, ], }); @@ -168,6 +233,7 @@ export function generateMipmap(device: GPUDevice, texture: GPUTexture) { colorAttachments: [ { view: texture.createView({ + dimension: '2d', baseMipLevel, mipLevelCount: 1, baseArrayLayer, @@ -179,14 +245,18 @@ export function generateMipmap(device: GPUDevice, texture: GPUTexture) { ], }; + const encoder = device.createCommandEncoder({ + label: 'mip gen encoder', + }); + const pass = encoder.beginRenderPass(renderPassDescriptor); pass.setPipeline(pipeline); pass.setBindGroup(0, bindGroup); pass.draw(3); pass.end(); + + const commandBuffer = encoder.finish(); + device.queue.submit([commandBuffer]); } } - - const commandBuffer = encoder.finish(); - device.queue.submit([commandBuffer]); } \ No newline at end of file diff --git a/test/tests/generate-mipmap-test.js b/test/tests/generate-mipmap-test.js index 529e5f7..4e2e40c 100644 --- a/test/tests/generate-mipmap-test.js +++ b/test/tests/generate-mipmap-test.js @@ -4,7 +4,7 @@ import { numMipLevels, } from '../../dist/1.x/webgpu-utils.module.js'; import { assertArrayEqualApproximately, assertEqual } from '../assert.js'; -import { readTextureUnpadded, testWithDevice } from '../webgpu.js'; +import { readTextureUnpadded, testWithDeviceWithOptions } from '../webgpu.js'; // prevent global document // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -14,102 +14,72 @@ const document = undefined; describe('generate-mipmap tests', () => { - it('returns correct number of mip levels', () => { - assertEqual(numMipLevels([1]), 1); - assertEqual(numMipLevels([2]), 2); - assertEqual(numMipLevels([3]), 2); - assertEqual(numMipLevels([4]), 3); - assertEqual(numMipLevels([4]), 3); - - assertEqual(numMipLevels([1, 1]), 1); - assertEqual(numMipLevels([1, 2]), 2); - assertEqual(numMipLevels([1, 3]), 2); - assertEqual(numMipLevels([1, 4]), 3); - assertEqual(numMipLevels([1, 4]), 3); - - assertEqual(numMipLevels([1, 1, 1]), 1); - assertEqual(numMipLevels([1, 1, 2]), 1); - assertEqual(numMipLevels([1, 1, 3]), 1); - assertEqual(numMipLevels([1, 1, 4]), 1); - assertEqual(numMipLevels([1, 1, 4]), 1); - - assertEqual(numMipLevels([1, 1, 1], '3d'), 1); - assertEqual(numMipLevels([1, 1, 2], '3d'), 2); - assertEqual(numMipLevels([1, 1, 3], '3d'), 2); - assertEqual(numMipLevels([1, 1, 4], '3d'), 3); - assertEqual(numMipLevels([1, 1, 4], '3d'), 3); - - assertEqual(numMipLevels({width: 1}), 1); - assertEqual(numMipLevels({width: 2}), 2); - assertEqual(numMipLevels({width: 3}), 2); - assertEqual(numMipLevels({width: 4}), 3); - assertEqual(numMipLevels({width: 4}), 3); - - assertEqual(numMipLevels({width: 1, height: 1}), 1); - assertEqual(numMipLevels({width: 1, height: 2}), 2); - assertEqual(numMipLevels({width: 1, height: 3}), 2); - assertEqual(numMipLevels({width: 1, height: 4}), 3); - assertEqual(numMipLevels({width: 1, height: 4}), 3); - - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 1}, '3d'), 1); - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 2}, '3d'), 2); - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 3}, '3d'), 2); - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 4}, '3d'), 3); - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 4}, '3d'), 3); - - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 1}), 1); - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 2}), 1); - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 3}), 1); - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 4}), 1); - assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 4}), 1); + it('returns correct number of mip levels', () => { + assertEqual(numMipLevels([1]), 1); + assertEqual(numMipLevels([2]), 2); + assertEqual(numMipLevels([3]), 2); + assertEqual(numMipLevels([4]), 3); + assertEqual(numMipLevels([4]), 3); + + assertEqual(numMipLevels([1, 1]), 1); + assertEqual(numMipLevels([1, 2]), 2); + assertEqual(numMipLevels([1, 3]), 2); + assertEqual(numMipLevels([1, 4]), 3); + assertEqual(numMipLevels([1, 4]), 3); + + assertEqual(numMipLevels([1, 1, 1]), 1); + assertEqual(numMipLevels([1, 1, 2]), 1); + assertEqual(numMipLevels([1, 1, 3]), 1); + assertEqual(numMipLevels([1, 1, 4]), 1); + assertEqual(numMipLevels([1, 1, 4]), 1); + + assertEqual(numMipLevels([1, 1, 1], '3d'), 1); + assertEqual(numMipLevels([1, 1, 2], '3d'), 2); + assertEqual(numMipLevels([1, 1, 3], '3d'), 2); + assertEqual(numMipLevels([1, 1, 4], '3d'), 3); + assertEqual(numMipLevels([1, 1, 4], '3d'), 3); + + assertEqual(numMipLevels({width: 1}), 1); + assertEqual(numMipLevels({width: 2}), 2); + assertEqual(numMipLevels({width: 3}), 2); + assertEqual(numMipLevels({width: 4}), 3); + assertEqual(numMipLevels({width: 4}), 3); + + assertEqual(numMipLevels({width: 1, height: 1}), 1); + assertEqual(numMipLevels({width: 1, height: 2}), 2); + assertEqual(numMipLevels({width: 1, height: 3}), 2); + assertEqual(numMipLevels({width: 1, height: 4}), 3); + assertEqual(numMipLevels({width: 1, height: 4}), 3); + + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 1}, '3d'), 1); + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 2}, '3d'), 2); + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 3}, '3d'), 2); + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 4}, '3d'), 3); + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 4}, '3d'), 3); + + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 1}), 1); + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 2}), 1); + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 3}), 1); + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 4}), 1); + assertEqual(numMipLevels({width: 1, depthOrArrayLayers: 4}), 1); + + }); + + function test(compatibilityMode) { + const options = { + compatibilityMode, + }; + + describe(compatibilityMode ? 'test compatibility mode' : 'test normal WebGPU', () => { - }); - - it('generates mipmaps 1 layer', testWithDevice(async device => { - const kTextureWidth = 4; - const kTextureHeight = 4; - const r = [255, 0, 0, 255]; - const b = [0, 0, 255, 255]; - const textureData = new Uint8Array([ - r, r, b, b, - r, r, b, b, - b, b, r, r, - b, b, r, r, - ].flat()); - - const size = [kTextureWidth, kTextureHeight]; - const texture = device.createTexture({ - size, - mipLevelCount: numMipLevels(size), - format: 'rgba8unorm', - usage: GPUTextureUsage.TEXTURE_BINDING | - GPUTextureUsage.RENDER_ATTACHMENT | - GPUTextureUsage.COPY_DST | - GPUTextureUsage.COPY_SRC, - }); - - device.queue.writeTexture( - { texture }, - textureData, - { bytesPerRow: kTextureWidth * 4 }, - { width: kTextureWidth, height: kTextureHeight }, - ); - generateMipmap(device, texture); - - const result = await readTextureUnpadded(device, texture, 2); - assertArrayEqualApproximately(result, [128, 0, 128, 255], 1); - })); - - it('generates mipmaps 3 layers', testWithDevice(async device => { - const kTextureWidth = 4; - const kTextureHeight = 4; const r = [255, 0, 0, 255]; const g = [0, 255, 0, 255]; const b = [0, 0, 255, 255]; const y = [255, 255, 0, 255]; - // const c = [0, 255, 255, 255]; + const c = [0, 255, 255, 255]; const m = [255, 0, 255, 255]; - const textureData = [ + + const layerData = [ { src: new Uint8Array([ r, r, b, b, @@ -137,34 +107,103 @@ describe('generate-mipmap tests', () => { ].flat()), expected: [255, 128, 128, 255], }, + { + src: new Uint8Array([ + c, c, m, m, + c, c, m, m, + m, m, c, c, + m, m, c, c, + ].flat()), + expected: [128, 128, 255, 255], + }, + { + src: new Uint8Array([ + b, b, y, y, + b, b, y, y, + y, y, b, b, + y, y, b, b, + ].flat()), + expected: [128, 128, 128, 255], + }, + { + src: new Uint8Array([ + g, g, r, r, + g, g, r, r, + r, r, g, g, + r, r, g, g, + ].flat()), + expected: [128, 128, 0, 255], + }, ]; - const size = [kTextureWidth, kTextureHeight, textureData.length]; - const texture = device.createTexture({ - size, - mipLevelCount: numMipLevels(size), - format: 'rgba8unorm', - usage: GPUTextureUsage.TEXTURE_BINDING | - GPUTextureUsage.RENDER_ATTACHMENT | - GPUTextureUsage.COPY_DST | - GPUTextureUsage.COPY_SRC, - }); - - textureData.forEach(({src}, layer) => { - device.queue.writeTexture( - { texture, origin: [0, 0, layer] }, - src, - { bytesPerRow: kTextureWidth * 4 }, - { width: kTextureWidth, height: kTextureHeight }, + async function testGenerateMipmap(device, textureData, textureOptions = {}) { + const kTextureWidth = 4; + const kTextureHeight = 4; + const size = [kTextureWidth, kTextureHeight, textureData.length]; + const texture = device.createTexture({ + ...textureOptions, + size, + mipLevelCount: numMipLevels(size), + format: 'rgba8unorm', + usage: GPUTextureUsage.TEXTURE_BINDING | + GPUTextureUsage.RENDER_ATTACHMENT | + GPUTextureUsage.COPY_DST | + GPUTextureUsage.COPY_SRC, + }); + + textureData.forEach(({src}, layer) => { + device.queue.writeTexture( + { texture, origin: [0, 0, layer] }, + src, + { bytesPerRow: kTextureWidth * 4 }, + { width: kTextureWidth, height: kTextureHeight }, + ); + }); + generateMipmap(device, texture, textureOptions.textureBindingViewDimension === 'cube' ? 'cube' : undefined); + + const results = await Promise.all(textureData.map((_, layer) => readTextureUnpadded(device, texture, 2, layer))); + + textureData.forEach(({expected}, layer) => { + assertArrayEqualApproximately(results[layer], expected, 1, `for layer: ${layer}`); + }); + + } + + it('generates mipmaps 1 layer', testWithDeviceWithOptions(options, async device => { + await testGenerateMipmap(device, layerData.slice(0, 1)); + })); + + it('generates mipmaps 3 layers', testWithDeviceWithOptions(options, async device => { + await testGenerateMipmap(device, layerData.slice(0, 3)); + })); + + it('generates mipmaps 6 layers (cube)', testWithDeviceWithOptions(options, async device => { + await testGenerateMipmap(device, layerData.slice(0, 6), { textureBindingViewDimension: 'cube' }); + })); + + it('generates mipmaps 6 layers (2d-array)', testWithDeviceWithOptions(options, async device => { + await testGenerateMipmap(device, layerData.slice(0, 6), { textureBindingViewDimension: '2d-array' }); + })); + + it('generates mipmaps 12 layers (cube-array)', testWithDeviceWithOptions(options, async device => { + if (options.compatibilityMode) { + // no cube-array in compat + return; + } + await testGenerateMipmap.call(this, + device, + [ + ...layerData.slice(0, 6), + ...layerData.slice(0, 6).reverse(), + ], ); - }); - generateMipmap(device, texture); + })); + + }); + } - const results = await Promise.all(textureData.map((_, layer) => readTextureUnpadded(device, texture, 2, layer))); + test(false); + test(true); - textureData.forEach(({expected}, layer) => { - assertArrayEqualApproximately(results[layer], expected, 1, `for layer: ${layer}`); - }); - })); }); diff --git a/test/webgpu.js b/test/webgpu.js index 0bb6040..0295ab1 100644 --- a/test/webgpu.js +++ b/test/webgpu.js @@ -30,9 +30,9 @@ GPUDevice.prototype.createBuffer = (function (origFn) { }; })(GPUDevice.prototype.createBuffer); -export function testWithDevice(fn, ...args) { +export function testWithDeviceWithOptions(options, fn, ...args) { return async function () { - const adapter = await globalThis?.navigator?.gpu?.requestAdapter(); + const adapter = await globalThis?.navigator?.gpu?.requestAdapter(options); const device = await adapter?.requestDevice(); if (!device) { this.skip(); @@ -42,7 +42,7 @@ export function testWithDevice(fn, ...args) { let caughtErr; try { - await fn(device, ...args); + await fn.call(this, device, ...args); } catch (e) { caughtErr = e; } finally { @@ -57,6 +57,12 @@ export function testWithDevice(fn, ...args) { }; } +export function testWithDevice(fn, ...args) { + return async function () { + await testWithDeviceWithOptions({}, fn, ...args).call(this); + }; +} + export function testWithDeviceAndDocument(fn) { return async function () { if (typeof document === 'undefined') { From 5b2f7f70cddb88dcbb28ac252ad23bc284aeca1b Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Sun, 24 Dec 2023 20:07:46 -0800 Subject: [PATCH 5/6] CHANGELIST --- CHANGELIST.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELIST.md b/CHANGELIST.md index eb4bec2..dcc9fa6 100644 --- a/CHANGELIST.md +++ b/CHANGELIST.md @@ -1,5 +1,9 @@ # Change List +### 1.1.0 + +* Make `generateMipmap` support compatibility mode + ### 1.0.0 * switch primitive functions to use named parameters. From de2bd90d3c7d06fe90adc0a4a17ef0d51bbdfeac Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Sun, 24 Dec 2023 20:07:53 -0800 Subject: [PATCH 6/6] 1.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 927dd40..2bfadc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "webgpu-utils", - "version": "1.0.2", + "version": "1.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "webgpu-utils", - "version": "1.0.2", + "version": "1.1.0", "license": "MIT", "devDependencies": { "@rollup/plugin-node-resolve": "^15.2.3", diff --git a/package.json b/package.json index b4e2d31..8d992f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webgpu-utils", - "version": "1.0.2", + "version": "1.1.0", "description": "webgpu utilities", "main": "dist/1.x/webgpu-utils.module.js", "module": "dist/1.x/webgpu-utils.module.js",