diff --git a/dev-modules/babel-plugin-inline-webgl-constants/package.json b/dev-modules/babel-plugin-inline-webgl-constants/package.json index be09823cb8..8fcb6815eb 100644 --- a/dev-modules/babel-plugin-inline-webgl-constants/package.json +++ b/dev-modules/babel-plugin-inline-webgl-constants/package.json @@ -24,6 +24,6 @@ "require": "./dist/index.cjs" }, "dependencies": { - "@luma.gl/constants": "9.1.0-alpha.17" + "@luma.gl/constants": "9.1.0-alpha.19" } } diff --git a/docs/api-guide/gpu/gpu-bindings.md b/docs/api-guide/gpu/gpu-bindings.md index fdf4c275c0..7257d8d89b 100644 --- a/docs/api-guide/gpu/gpu-bindings.md +++ b/docs/api-guide/gpu/gpu-bindings.md @@ -143,17 +143,17 @@ WGSL vertex shader struct Uniforms { modelViewProjectionMatrix : mat4x4; }; -[[binding(0), group(0)]] var uniforms : Uniforms; // BINDING 0 +@binding(0), @group(0) var uniforms : Uniforms; // BINDING 0 struct VertexOutput { - [[builtin(position)]] Position : vec4; - [[location(0)]] fragUV : vec2; - [[location(1)]] fragPosition: vec4; + @builtin(position) Position : vec4; + @location(0) fragUV : vec2; + @location(1) fragPosition: vec4; }; -[[stage(vertex)]] -fn main([[location(0)]] position : vec4, - [[location(1)]] uv : vec2) -> VertexOutput { +@stage(vertex) +fn main(@location(0) position : vec4, + @location(1) uv : vec2) -> VertexOutput { var output : VertexOutput; output.Position = uniforms.modelViewProjectionMatrix * position; output.fragUV = uv; @@ -165,12 +165,12 @@ fn main([[location(0)]] position : vec4, WGSL Fragment Shader ```rust -[[group(0), binding(1)]] var mySampler: sampler; // BINDING 1 -[[group(0), binding(2)]] var myTexture: texture_2d; // BINDING 2 +@group(0), binding(1) var mySampler: sampler; // BINDING 1 +@group(0), binding(2) var myTexture: texture_2d; // BINDING 2 -[[stage(fragment)]] -fn main([[location(0)]] fragUV: vec2, - [[location(1)]] fragPosition: vec4) -> [[location(0)]] vec4 { +@stage(fragment) +fn main(@location(0) fragUV: vec2, + @location(1) fragPosition: vec4) -> [[location(0)]] vec4 { return textureSample(myTexture, mySampler, fragUV) * fragPosition; } ``` diff --git a/docs/api-reference/core/bindings.md b/docs/api-reference/core/bindings.md index 0224711f85..775093ee76 100644 --- a/docs/api-reference/core/bindings.md +++ b/docs/api-reference/core/bindings.md @@ -115,17 +115,17 @@ WGSL vertex shader struct Uniforms { modelViewProjectionMatrix : mat4x4; }; -[[binding(0), group(0)]] var uniforms : Uniforms; // BINDING 0 +@binding(0), @group(0) var uniforms : Uniforms; // BINDING 0 struct VertexOutput { - [[builtin(position)]] Position : vec4; - [[location(0)]] fragUV : vec2; - [[location(1)]] fragPosition: vec4; + @builtin(position) Position : vec4; + @location(0) fragUV : vec2; + @location(1) fragPosition: vec4; }; -[[stage(vertex)]] -fn main([[location(0)]] position : vec4, - [[location(1)]] uv : vec2) -> VertexOutput { +@stage(vertex) +fn main(@location(0) position : vec4, + @location(1) uv : vec2) -> VertexOutput { var output : VertexOutput; output.Position = uniforms.modelViewProjectionMatrix * position; output.fragUV = uv; @@ -137,12 +137,12 @@ fn main([[location(0)]] position : vec4, WGSL FRAGMENT SHADER ```rust -[[group(0), binding(1)]] var mySampler: sampler; // BINDING 1 -[[group(0), binding(2)]] var myTexture: texture_2d; // BINDING 2 +@group(0), @binding(1) var mySampler: sampler; // BINDING 1 +@group(0), @binding(2) var myTexture: texture_2d; // BINDING 2 -[[stage(fragment)]] -fn main([[location(0)]] fragUV: vec2, - [[location(1)]] fragPosition: vec4) -> [[location(0)]] vec4 { +@stage(fragment) +fn main(@location(0) fragUV: vec2, + @location(1) fragPosition: vec4) -> @location(0) vec4 { return textureSample(myTexture, mySampler, fragUV) * fragPosition; } ``` diff --git a/docs/tutorials/README.mdx b/docs/tutorials/README.mdx index ca9b01efd4..f26527dae7 100644 --- a/docs/tutorials/README.mdx +++ b/docs/tutorials/README.mdx @@ -103,8 +103,8 @@ Open the file `package.json` (created when we initialized npm), and add the foll "serve": "vite preview" }, "dependencies": { - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", }, "devDependencies": { "typescript": "^5.5.0", @@ -129,8 +129,8 @@ The full contents of the `package.json` should be the following (dependency vers "serve": "vite preview" }, "dependencies": { - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", }, "devDependencies": { "typescript": "^5.5.0", diff --git a/examples/api/animation/app.ts b/examples/api/animation/app.ts index e71dbdb0f2..1f8d0993a3 100644 --- a/examples/api/animation/app.ts +++ b/examples/api/animation/app.ts @@ -280,7 +280,6 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate { .translate(cube.translation) .rotateXYZ([rotationX, rotationY, rotationZ]); - cube.model.setUniforms({}); cube.uniformStore.setUniforms({ app: { uModel: modelMatrix diff --git a/examples/api/animation/package.json b/examples/api/animation/package.json index ec3df6d697..8c1629de50 100644 --- a/examples/api/animation/package.json +++ b/examples/api/animation/package.json @@ -8,10 +8,10 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/api/cubemap/package.json b/examples/api/cubemap/package.json index 50c082e6c3..b3b4dbbb9a 100644 --- a/examples/api/cubemap/package.json +++ b/examples/api/cubemap/package.json @@ -8,12 +8,12 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/constants": "9.1.0-alpha.17", - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/constants": "9.1.0-alpha.19", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/api/texture-3d/package.json b/examples/api/texture-3d/package.json index 717c9bf0e9..77355cc3db 100644 --- a/examples/api/texture-3d/package.json +++ b/examples/api/texture-3d/package.json @@ -8,11 +8,11 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/showcase/instancing/package.json b/examples/showcase/instancing/package.json index 28100af516..7a827d9339 100644 --- a/examples/showcase/instancing/package.json +++ b/examples/showcase/instancing/package.json @@ -9,11 +9,11 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/showcase/persistence/app.ts b/examples/showcase/persistence/app.ts index f5091b2928..40cc4db125 100644 --- a/examples/showcase/persistence/app.ts +++ b/examples/showcase/persistence/app.ts @@ -75,7 +75,7 @@ fn vertexMain(inputs: VertexInputs) -> FragmentInputs { } @fragment -fn fragmentMain(inputs: FragmentInputs) -> [[location(0)]] vec4 { +fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4 { let attenuation = 1.0; if (sphere.lighting) { light = normalize(vec3(1,1,2)); diff --git a/examples/showcase/persistence/package.json b/examples/showcase/persistence/package.json index 0ac5d22ba1..041b88adc0 100644 --- a/examples/showcase/persistence/package.json +++ b/examples/showcase/persistence/package.json @@ -8,11 +8,11 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/showcase/postprocessing/index.html b/examples/showcase/postprocessing/index.html index e1fc25e3c0..148feb07b3 100644 --- a/examples/showcase/postprocessing/index.html +++ b/examples/showcase/postprocessing/index.html @@ -25,7 +25,7 @@ import {webgpuAdapter} from '@luma.gl/webgpu'; import {makeAnimationLoop} from '@luma.gl/engine'; import AnimationLoopTemplate from './app.ts'; - const animationLoop = makeAnimationLoop(AnimationLoopTemplate, {adapters: [/* webgpuAdapter, */ webgl2Adapter]}); + const animationLoop = makeAnimationLoop(AnimationLoopTemplate, {adapters: [webgpuAdapter, webgl2Adapter]}); animationLoop.start(); diff --git a/examples/showcase/postprocessing/package.json b/examples/showcase/postprocessing/package.json index c91a26a6e3..a5e98d6564 100644 --- a/examples/showcase/postprocessing/package.json +++ b/examples/showcase/postprocessing/package.json @@ -10,12 +10,12 @@ "dependencies": { "@loaders.gl/core": "^4.2.0", "@loaders.gl/gltf": "^4.2.0", - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/gltf": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/gltf": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/tutorials/hello-cube/app.ts b/examples/tutorials/hello-cube/app.ts index e61dc0933e..0021c4c4b7 100644 --- a/examples/tutorials/hello-cube/app.ts +++ b/examples/tutorials/hello-cube/app.ts @@ -22,7 +22,9 @@ struct Uniforms { modelViewProjectionMatrix : mat4x4, }; -@binding(0) @group(0) var app : Uniforms; +@group(0) @binding(0) var app : Uniforms; +// @group(0) @binding(1) var uTexture : texture_2d; +// @group(0) @binding(2) var uTextureSampler : sampler; struct VertexInputs { // CUBE GEOMETRY @@ -48,6 +50,7 @@ fn vertexMain(inputs: VertexInputs) -> FragmentInputs { @fragment fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4 { return inputs.fragPosition; + // return textureSample(uTexture, uTextureSampler, inputs.fragUV); } `; @@ -125,9 +128,9 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate { data: loadImageBitmap('vis-logo.png'), mipmaps: true, sampler: device.createSampler({ - minFilter: 'linear', - magFilter: 'linear', - mipmapFilter: 'linear' + minFilter: 'nearest', + magFilter: 'nearest', + mipmapFilter: 'nearest' }) }); diff --git a/examples/tutorials/hello-cube/package.json b/examples/tutorials/hello-cube/package.json index a6c7ec5cba..9f187c34f1 100644 --- a/examples/tutorials/hello-cube/package.json +++ b/examples/tutorials/hello-cube/package.json @@ -8,10 +8,10 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/tutorials/hello-cube/vite.config.ts b/examples/tutorials/hello-cube/vite.config.ts index 43053081f0..a53c7e410f 100644 --- a/examples/tutorials/hello-cube/vite.config.ts +++ b/examples/tutorials/hello-cube/vite.config.ts @@ -1,15 +1,11 @@ import {defineConfig} from 'vite'; +import {readdirSync} from 'fs'; -const alias = { - '@luma.gl/constants': `${__dirname}/../../../modules/constants/src`, - '@luma.gl/core': `${__dirname}/../../../modules/core/src`, - '@luma.gl/engine': `${__dirname}/../../../modules/engine/src`, - '@luma.gl/experimental': `${__dirname}/../../../modules/experimental/src`, - '@luma.gl/webgl-legacy': `${__dirname}/../../../modules/gltf/src`, - '@luma.gl/shadertools': `${__dirname}/../../../modules/shadertools/src`, - '@luma.gl/test-utils': `${__dirname}/../../../modules/test-utils/src`, - '@luma.gl/webgl': `${__dirname}/../../../modules/webgl/src` -}; +const MODULES_DIR = `${__dirname}/../../../modules`; +const modules = readdirSync(MODULES_DIR); +const alias = Object.fromEntries( + modules.map(module => [`@luma.gl/${module}`, `${MODULES_DIR}/${module}/src`]) +); // https://vitejs.dev/config/ export default defineConfig({ diff --git a/examples/tutorials/hello-gltf/package.json b/examples/tutorials/hello-gltf/package.json index 8e3533beba..5df3ce8483 100644 --- a/examples/tutorials/hello-gltf/package.json +++ b/examples/tutorials/hello-gltf/package.json @@ -10,12 +10,12 @@ "dependencies": { "@loaders.gl/core": "^4.2.0", "@loaders.gl/gltf": "^4.2.0", - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/gltf": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/gltf": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/tutorials/hello-instanced-cubes/package.json b/examples/tutorials/hello-instanced-cubes/package.json index 22ff027e4c..899c95a5a5 100644 --- a/examples/tutorials/hello-instanced-cubes/package.json +++ b/examples/tutorials/hello-instanced-cubes/package.json @@ -8,11 +8,11 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/tutorials/hello-instancing/package.json b/examples/tutorials/hello-instancing/package.json index dcd1f0719a..50cdf25f86 100644 --- a/examples/tutorials/hello-instancing/package.json +++ b/examples/tutorials/hello-instancing/package.json @@ -8,10 +8,10 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19" }, "devDependencies": { "typescript": "^5.5.0", diff --git a/examples/tutorials/hello-triangle-geometry/app.ts b/examples/tutorials/hello-triangle-geometry/app.ts index 64524f9618..9e47ac5547 100644 --- a/examples/tutorials/hello-triangle-geometry/app.ts +++ b/examples/tutorials/hello-triangle-geometry/app.ts @@ -5,7 +5,27 @@ const INFO_HTML = ` Have to start somewhere... `; -const vs = `\ +export const source = /* wgsl */`\ +struct VertexOutput { + @builtin(position) Position : vec4, + @location(0) fragColor : vec3 +}; + +@vertex +fn vertexMain(@location(0) position : vec2, @location(1) color : vec3) -> VertexOutput { + var output : VertexOutput; + output.Position = vec4(position.x, position.y, 0.0, 1.0); + output.fragColor = color; + return output; +} + +@fragment +fn fragmentMain(@location(0) fragColor: vec3) -> @location(0) vec4 { + return vec4(fragColor, 1.0); +} +`; + +const vs = /* glsl */ `\ #version 300 es in vec2 position; @@ -29,28 +49,6 @@ void main() { } `; -// export const vs_wgsl = /* WGSL */`\ -// struct VertexOutput { -// @builtin(position) Position : vec4; -// @location(0) fragColor : vec3; -// }; - -// @vertex -// fn main(@location(0) position : vec2, @location(1) color : vec3) -> VertexOutput { -// var output : VertexOutput; -// output.Position = uniforms.modelViewProjectionMatrix * position; -// output.fragColor = color; -// return output; -// } -// `; - -// export const fs_wgsl = /* WGSL */`\ -// @fragment -// fn main(@location(0) fragColor: vec3) -> @location(0) vec4 { -// return vec4(fragColor, 1.0); -// } -// `; - export default class AppAnimationLoopTemplate extends AnimationLoopTemplate { static info = INFO_HTML; @@ -75,6 +73,7 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate { this.model = new Model(device, { id: 'triangle', + source, vs, fs, bufferLayout: [ diff --git a/examples/tutorials/hello-triangle-geometry/package.json b/examples/tutorials/hello-triangle-geometry/package.json index 383bda9b34..28d62c5d46 100644 --- a/examples/tutorials/hello-triangle-geometry/package.json +++ b/examples/tutorials/hello-triangle-geometry/package.json @@ -8,11 +8,11 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19" }, "devDependencies": { "typescript": "^5.5.0", diff --git a/examples/tutorials/hello-triangle/package.json b/examples/tutorials/hello-triangle/package.json index 28b604dc48..ddd7aa5663 100644 --- a/examples/tutorials/hello-triangle/package.json +++ b/examples/tutorials/hello-triangle/package.json @@ -8,10 +8,10 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/tutorials/hello-two-cubes/package.json b/examples/tutorials/hello-two-cubes/package.json index 6b08d7c33d..7736505cf3 100644 --- a/examples/tutorials/hello-two-cubes/package.json +++ b/examples/tutorials/hello-two-cubes/package.json @@ -8,10 +8,10 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/tutorials/lighting/package.json b/examples/tutorials/lighting/package.json index eba91f665a..7a513686b9 100644 --- a/examples/tutorials/lighting/package.json +++ b/examples/tutorials/lighting/package.json @@ -8,10 +8,10 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", "@math.gl/core": "4.1.0-alpha.3" }, "devDependencies": { diff --git a/examples/tutorials/shader-hooks/package.json b/examples/tutorials/shader-hooks/package.json index 23ee7bbeb7..87c0c8a53e 100644 --- a/examples/tutorials/shader-hooks/package.json +++ b/examples/tutorials/shader-hooks/package.json @@ -8,10 +8,10 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19" }, "devDependencies": { "typescript": "^5.5.0", diff --git a/examples/tutorials/shader-modules/package.json b/examples/tutorials/shader-modules/package.json index f779a87592..d391488602 100644 --- a/examples/tutorials/shader-modules/package.json +++ b/examples/tutorials/shader-modules/package.json @@ -8,10 +8,10 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19" }, "devDependencies": { "typescript": "^5.5.0", diff --git a/examples/tutorials/transform-feedback/package.json b/examples/tutorials/transform-feedback/package.json index d06855aa47..290ab8ad7e 100644 --- a/examples/tutorials/transform-feedback/package.json +++ b/examples/tutorials/transform-feedback/package.json @@ -8,10 +8,10 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19" }, "devDependencies": { "typescript": "^5.1.6", diff --git a/examples/tutorials/transform/package.json b/examples/tutorials/transform/package.json index 90bb832c0a..42a8f7891b 100644 --- a/examples/tutorials/transform/package.json +++ b/examples/tutorials/transform/package.json @@ -8,9 +8,9 @@ "serve": "vite preview" }, "dependencies": { - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", "@probe.gl/log": "^4.0.8" }, "devDependencies": { diff --git a/modules/core/src/adapter/canvas-context.ts b/modules/core/src/adapter/canvas-context.ts index a438fc1b7b..e2b595e7ab 100644 --- a/modules/core/src/adapter/canvas-context.ts +++ b/modules/core/src/adapter/canvas-context.ts @@ -6,6 +6,7 @@ import {isBrowser} from '@probe.gl/env'; import type {Device} from './device'; import type {Framebuffer} from './resources/framebuffer'; import {log} from '../utils/log'; +import {uid} from '../utils/uid'; import type {TextureFormat} from '../gpu-type-utils/texture-formats'; /** Properties for a CanvasContext */ @@ -71,6 +72,12 @@ export abstract class CanvasContext { /** State used by luma.gl classes: TODO - move to canvasContext*/ readonly _canvasSizeInfo = {clientWidth: 0, clientHeight: 0, devicePixelRatio: 1}; + abstract get [Symbol.toStringTag](): string; + + toString(): string { + return `${this[Symbol.toStringTag]}(${this.id})`; + } + constructor(props?: CanvasContextProps) { this.props = {...CanvasContext.defaultProps, ...props}; props = this.props; @@ -338,7 +345,7 @@ function getCanvasFromDOM(canvasId: string): HTMLCanvasElement { function createCanvas(props: CanvasContextProps) { const {width, height} = props; const targetCanvas = document.createElement('canvas'); - targetCanvas.id = 'lumagl-auto-created-canvas'; + targetCanvas.id = uid('lumagl-auto-created-canvas'); targetCanvas.width = width || 1; targetCanvas.height = height || 1; targetCanvas.style.width = Number.isFinite(width) ? `${width}px` : '100%'; diff --git a/modules/core/src/adapter/device.ts b/modules/core/src/adapter/device.ts index 6348a8e5d7..31e235ecdc 100644 --- a/modules/core/src/adapter/device.ts +++ b/modules/core/src/adapter/device.ts @@ -278,7 +278,7 @@ export abstract class Device { createCanvasContext: undefined!, // Callbacks - onError: (error: Error) => log.error(error.message), + onError: (error: Error) => log.error(error.message)(), _requestMaxLimits: true, _factoryDestroyPolicy: 'unused', @@ -370,8 +370,8 @@ export abstract class Device { return false; } - /** Report error (normally for unhandled device errors) */ - error(error: Error): void { + /** Report error (normally called for unhandled device errors) */ + reportError(error: Error): void { this.props.onError(error); } diff --git a/modules/core/src/adapter/resources/resource.ts b/modules/core/src/adapter/resources/resource.ts index 7a9db205dd..ae31e4a1dc 100644 --- a/modules/core/src/adapter/resources/resource.ts +++ b/modules/core/src/adapter/resources/resource.ts @@ -27,6 +27,10 @@ export abstract class Resource { abstract get [Symbol.toStringTag](): string; + toString(): string { + return `${this[Symbol.toStringTag] || this.constructor.name}:"${this.id}"`; + } + /** props.id, for debugging. */ id: string; readonly props: Required; @@ -73,10 +77,6 @@ export abstract class Resource { return this; } - toString(): string { - return `${this[Symbol.toStringTag] || this.constructor.name}(${this.id})`; - } - /** * Combines a map of user props and default props, only including props from defaultProps * @returns returns a map of overridden default props diff --git a/modules/core/src/adapter/types/shader-layout.ts b/modules/core/src/adapter/types/shader-layout.ts index 4294e841c9..fb802153f7 100644 --- a/modules/core/src/adapter/types/shader-layout.ts +++ b/modules/core/src/adapter/types/shader-layout.ts @@ -61,7 +61,7 @@ export type AttributeDeclaration = { stepMode?: 'vertex' | 'instance'; }; -// BINDING LAYOUTS +// BINDING LAYOUT TYPES /** ShaderLayout for bindings */ export type BindingDeclaration = @@ -73,11 +73,17 @@ export type BindingDeclaration = export type UniformBufferBindingLayout = { type: 'uniform'; + /** Name of the binding. Used by luma to map bindings by name */ name: string; + /** Bind group index. Always 0 in WebGL */ + group: number; + /** Binding index within the bind group */ location: number; + /** Which shader stages can access this binding */ visibility?: number; hasDynamicOffset?: boolean; minBindingSize?: number; + /** The uniforms in this uniform buffer */ uniforms?: UniformInfo[]; }; @@ -92,8 +98,13 @@ export type UniformInfo = { export type StorageBufferBindingLayout = { type: 'storage' | 'read-only-storage'; + /** Name of the binding. Used by luma to map bindings by name */ name: string; + /** Bind group index. Always 0 in WebGL */ + group: number; + /** Binding index within the bind group */ location: number; + /** Which shader stages can access this binding */ visibility?: number; hasDynamicOffset?: boolean; minBindingSize?: number; @@ -101,8 +112,13 @@ export type StorageBufferBindingLayout = { type TextureBindingLayout = { type: 'texture'; + /** Name of the binding. Used by luma to map bindings by name */ name: string; + /** Bind group index. Always 0 in WebGL */ + group: number; + /** Binding index within the bind group */ location: number; + /** Which shader stages can access this binding */ visibility?: number; viewDimension?: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d'; // default: '2d' sampleType?: 'float' | 'unfilterable-float' | 'depth' | 'sint' | 'uint'; // default: 'float' @@ -111,23 +127,33 @@ type TextureBindingLayout = { type SamplerBindingLayout = { type: 'sampler'; + /** Name of the binding. Used by luma to map bindings by name */ name: string; + /** Bind group index. Always 0 in WebGL */ + group: number; + /** Binding index within the bind group */ location: number; + /** Which shader stages can access this binding */ visibility?: number; samplerType?: 'filtering' | 'non-filtering' | 'comparison'; // default: filtering }; type StorageTextureBindingLayout = { type: 'storage'; + /** Name of the binding. Used by luma to map bindings by name */ name: string; + /** Bind group index. Always 0 in WebGL */ + group: number; + /** Binding index within the bind group */ location: number; + /** Which shader stages can access this binding */ visibility?: number; access?: 'write-only'; format: TextureFormat; viewDimension?: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d'; }; -// BINDINGS +// BINDING VALUE TYPES /** Binding value */ export type Binding = diff --git a/modules/core/test/adapter/resources/compute-pipeline.spec.ts b/modules/core/test/adapter/resources/compute-pipeline.spec.ts index d93423039e..e40601ebfc 100644 --- a/modules/core/test/adapter/resources/compute-pipeline.spec.ts +++ b/modules/core/test/adapter/resources/compute-pipeline.spec.ts @@ -38,7 +38,7 @@ test('ComputePipeline compute', async t => { const computePipeline = webgpuDevice.createComputePipeline({ shader, shaderLayout: { - bindings: [{name: 'data', type: 'storage', location: 0}] + bindings: [{name: 'data', type: 'storage', group: 0, location: 0}] } }); diff --git a/modules/engine/package.json b/modules/engine/package.json index c0d70b818c..ee5d51c1be 100644 --- a/modules/engine/package.json +++ b/modules/engine/package.json @@ -40,8 +40,8 @@ "prepublishOnly": "npm run build-minified-bundle && npm run build-dev-bundle" }, "peerDependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19" }, "dependencies": { "@math.gl/core": "4.1.0-alpha.3", diff --git a/modules/engine/src/async-texture/async-texture.ts b/modules/engine/src/async-texture/async-texture.ts index 52b8abc5ee..73f2a54dff 100644 --- a/modules/engine/src/async-texture/async-texture.ts +++ b/modules/engine/src/async-texture/async-texture.ts @@ -16,6 +16,7 @@ import type { } from '@luma.gl/core'; import {loadImageBitmap} from '../application-utils/load-file'; +import {uid} from '../utils/uid'; export type AsyncTextureProps = Omit & AsyncTextureDataProps; @@ -54,6 +55,7 @@ type AsyncTextureData = AsyncTextureProps['data']; */ export class AsyncTexture { readonly device: Device; + readonly id: string; // TODO - should we type these as possibly `null`? It will make usage harder? // @ts-expect-error @@ -70,8 +72,18 @@ export class AsyncTexture { protected resolveReady: () => void = () => {}; protected rejectReady: (error: Error) => void = () => {}; + get [Symbol.toStringTag]() { + return 'AsyncTexture'; + } + + toString(): string { + return `AsyncTexture:"${this.id}"(${this.isReady ? 'ready' : 'loading'})`; + } + constructor(device: Device, props: AsyncTextureProps) { this.device = device; + this.id = props.id || uid('async-texture'); + // this.id = typeof props?.data === 'string' ? props.data.slice(-20) : uid('async-texture'); // Signature: new AsyncTexture(device, {data: url}) if (typeof props?.data === 'string' && props.dimension === '2d') { diff --git a/modules/engine/src/compute/swap.ts b/modules/engine/src/compute/swap.ts index 8a7133e5a6..c74d5fef97 100644 --- a/modules/engine/src/compute/swap.ts +++ b/modules/engine/src/compute/swap.ts @@ -3,7 +3,7 @@ // Copyright (c) vis.gl contributors import type {BufferProps, FramebufferProps} from '@luma.gl/core'; -import {Device, Resource, Buffer, Framebuffer} from '@luma.gl/core'; +import {Device, Resource, Buffer, Framebuffer, Texture} from '@luma.gl/core'; /** * Helper class for working with repeated transformations / computations @@ -39,7 +39,31 @@ export class Swap> { /** Helper for managing double-buffered framebuffers */ export class SwapFramebuffers extends Swap { constructor(device: Device, props: FramebufferProps) { - super({current: device.createFramebuffer(props), next: device.createFramebuffer(props)}); + props = {...props}; + + let colorAttachments = props.colorAttachments?.map(colorAttachment => + typeof colorAttachment !== 'string' + ? colorAttachment + : device.createTexture({ + format: colorAttachment, + usage: Texture.COPY_DST | Texture.RENDER_ATTACHMENT + }) + ); + + const current = device.createFramebuffer({...props, colorAttachments}); + + colorAttachments = props.colorAttachments?.map(colorAttachment => + typeof colorAttachment !== 'string' + ? colorAttachment + : device.createTexture({ + format: colorAttachment, + usage: Texture.COPY_DST | Texture.RENDER_ATTACHMENT + }) + ); + + const next = device.createFramebuffer({...props, colorAttachments}); + + super({current, next}); } /** diff --git a/modules/engine/src/model/model.ts b/modules/engine/src/model/model.ts index 86ca7228bf..83e77bffd0 100644 --- a/modules/engine/src/model/model.ts +++ b/modules/engine/src/model/model.ts @@ -221,6 +221,14 @@ export class Model { /** "Time" of last draw. Monotonically increasing timestamp */ _lastDrawTimestamp: number = -1; + get [Symbol.toStringTag](): string { + return 'Model'; + } + + toString(): string { + return `Model(${this.id})`; + } + constructor(device: Device, props: ModelProps) { this.props = {...Model.defaultProps, ...props}; props = this.props; @@ -327,11 +335,11 @@ export class Model { this.setBindings(props.bindings); } if (props.uniforms) { - this.setUniforms(props.uniforms); + this.setUniformsWebGL(props.uniforms); } if (props.moduleSettings) { // log.warn('Model.props.moduleSettings is deprecated. Use Model.shaderInputs.setProps()')(); - this.updateModuleSettings(props.moduleSettings); + this.updateModuleSettingsWebGL(props.moduleSettings); } if (props.transformFeedback) { this.transformFeedback = props.transformFeedback; @@ -380,10 +388,22 @@ export class Model { } draw(renderPass: RenderPass): boolean { - this.predraw(); + const loadingBinding = this._areBindingsLoading(); + if (loadingBinding) { + log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)(); + return false; + } + + try { + renderPass.pushDebugGroup(`${this}.predraw(${renderPass})`); + this.predraw(); + } finally { + renderPass.popDebugGroup(); + } let drawSuccess: boolean; try { + renderPass.pushDebugGroup(`${this}.draw(${renderPass})`); this._logDrawCallStart(); // Update the pipeline if invalidated @@ -394,6 +414,7 @@ export class Model { // Set pipeline state, we may be sharing a pipeline so we need to set all state on every draw // Any caching needs to be done inside the pipeline functions // TODO this is a busy initialized check for all bindings every frame + const syncBindings = this._getBindings(); this.pipeline.setBindings(syncBindings, { disableWarnings: this.props.disableWarnings @@ -422,6 +443,7 @@ export class Model { topology: this.topology }); } finally { + renderPass.popDebugGroup(); this._logDrawCallEnd(); } this._logFramebuffer(renderPass); @@ -656,7 +678,7 @@ export class Model { * @deprecated WebGL only, use uniform buffers for portability * @param uniforms */ - setUniforms(uniforms: Record): void { + setUniformsWebGL(uniforms: Record): void { if (!isObjectEmpty(uniforms)) { this.pipeline.setUniformsWebGL(uniforms); Object.assign(this.uniforms, uniforms); @@ -667,7 +689,7 @@ export class Model { /** * @deprecated Updates shader module settings (which results in uniforms being set) */ - updateModuleSettings(props: Record): void { + updateModuleSettingsWebGL(props: Record): void { // log.warn('Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()')(); const {bindings, uniforms} = splitUniformsAndBindings(this._getModuleUniforms(props)); Object.assign(this.bindings, bindings); @@ -677,19 +699,32 @@ export class Model { // Internal methods - /** Get texture / texture view from any async textures */ + /** Check that bindings are loaded. Returns id of first binding that is still loading. */ + _areBindingsLoading(): string | false { + for (const binding of Object.values(this.bindings)) { + if (binding instanceof AsyncTexture && !binding.isReady) { + return binding.id; + } + } + return false; + } + + /** Extracts texture view from loaded async textures. Returns null if any textures have not yet been loaded. */ _getBindings(): Record { - // Extract actual textures from async textures. If not loaded, null - return Object.entries(this.bindings).reduce>((acc, [name, binding]) => { + const validBindings: Record = {}; + + for (const [name, binding] of Object.entries(this.bindings)) { if (binding instanceof AsyncTexture) { + // Check that async textures are loaded if (binding.isReady) { - acc[name] = binding.texture; + validBindings[name] = binding.texture; } } else { - acc[name] = binding; + validBindings[name] = binding; } - return acc; - }, {}); + } + + return validBindings; } /** Get the timestamp of the latest updated bound GPU memory resource (buffer/texture). */ diff --git a/modules/engine/src/models/billboard-texture-model.ts b/modules/engine/src/models/billboard-texture-model.ts index 2ef1ea37fa..ee9c245cb8 100644 --- a/modules/engine/src/models/billboard-texture-model.ts +++ b/modules/engine/src/models/billboard-texture-model.ts @@ -6,15 +6,33 @@ import {Device, Texture} from '@luma.gl/core'; import {AsyncTexture} from '../async-texture/async-texture'; import {ClipSpace} from './clip-space'; +const BACKGROUND_FS_WGSL = /* wgsl */ `\ +@group(0) @binding(0) var backgroundTexture: texture_2d; +@group(0) @binding(1) var backgroundTextureSampler: sampler; + +fn billboardTexture_getTextureUV(coordinates: vec2) -> vec2 { + let iTexSize: vec2 = textureDimensions(backgroundTexture, 0) * 2; + let texSize: vec2 = vec2(f32(iTexSize.x), f32(iTexSize.y)); + var position: vec2 = coordinates.xy / texSize; + return position; +} + +@fragment +fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4 { + let position: vec2 = billboardTexture_getTextureUV(inputs.coordinate); + return textureSample(backgroundTexture, backgroundTextureSampler, position); +} +`; + const BACKGROUND_FS = /* glsl */ `\ #version 300 es - precision highp float; + uniform sampler2D backgroundTexture; out vec4 fragColor; vec2 billboardTexture_getTextureUV() { - ivec2 iTexSize = textureSize(backgroundTexture, 0) * 2; + ivec2 iTexSize = textureDimensions(backgroundTexture, 0) * 2; vec2 texSize = vec2(float(iTexSize.x), float(iTexSize.y)); vec2 position = gl_FragCoord.xy / texSize; return position; @@ -45,6 +63,7 @@ export class BackgroundTextureModel extends ClipSpace { constructor(device: Device, props: BackgroundTextureModelProps) { super(device, { id: props.id || 'background-texture-model', + source: BACKGROUND_FS_WGSL, fs: BACKGROUND_FS, parameters: { depthWriteEnabled: false, diff --git a/modules/engine/src/models/clip-space.ts b/modules/engine/src/models/clip-space.ts index b3e5f384fa..aba5fddf84 100644 --- a/modules/engine/src/models/clip-space.ts +++ b/modules/engine/src/models/clip-space.ts @@ -6,46 +6,48 @@ import {Device} from '@luma.gl/core'; import {Model, ModelProps} from '../model/model'; import {Geometry} from '../geometry/geometry'; +import {uid} from '../utils/uid'; const CLIPSPACE_VERTEX_SHADER_WGSL = /* wgsl */ `\ -struct VertexInput { - aClipSpacePosition: vec2; - aTexCoord: vec2; - aCoordinate: vec2; +struct VertexInputs { + @location(0) clipSpacePosition: vec2, + @location(1) texCoord: vec2, + @location(2) coordinate: vec2 } -struct FragmentInput { - @builtin(position) Position : vec4; - @location(0) position : vec2; - @location(1) coordinate : vec2; - @location(2) uv : vec2; +struct FragmentInputs { + @builtin(position) Position : vec4, + @location(0) position : vec2, + @location(1) coordinate : vec2, + @location(2) uv : vec2 }; -@stage(vertex) -fn vertexMain(input: VertexInput) -> FragmentInput { - var output: FragmentInput; - output.Position = vec4(aClipSpacePosition, 0., 1.); - output.position = input.aClipSpacePosition; - output.coordinate = input.aCoordinate; - output.uv = aTexCoord; +@vertex +fn vertexMain(inputs: VertexInputs) -> FragmentInputs { + var outputs: FragmentInputs; + outputs.Position = vec4(inputs.clipSpacePosition, 0., 1.); + outputs.position = inputs.clipSpacePosition; + outputs.coordinate = inputs.coordinate; + outputs.uv = inputs.texCoord; + return outputs; } `; const CLIPSPACE_VERTEX_SHADER = /* glsl */ `\ #version 300 es -in vec2 aClipSpacePosition; -in vec2 aTexCoord; -in vec2 aCoordinate; +in vec2 clipSpacePosition; +in vec2 texCoord; +in vec2 coordinate; out vec2 position; out vec2 coordinate; out vec2 uv; void main(void) { - gl_Position = vec4(aClipSpacePosition, 0., 1.); - position = aClipSpacePosition; - coordinate = aCoordinate; - uv = aTexCoord; + gl_Position = vec4(clipSpacePosition, 0., 1.); + position = clipSpacePosition; + coordinate = coordinate; + uv = texCoord; } `; @@ -64,21 +66,21 @@ export class ClipSpace extends Model { // For WGSL we need to append the supplied fragment shader to the default vertex shader source if (props.source) { - props = {...props, source: `${CLIPSPACE_VERTEX_SHADER_WGSL}\m${props.source}`}; + props = {...props, source: `${CLIPSPACE_VERTEX_SHADER_WGSL}\n${props.source}`}; } super(device, { + id: props.id || uid('clip-space'), ...props, - source: CLIPSPACE_VERTEX_SHADER_WGSL, vs: CLIPSPACE_VERTEX_SHADER, vertexCount: 4, geometry: new Geometry({ topology: 'triangle-strip', vertexCount: 4, attributes: { - aClipSpacePosition: {size: 2, value: new Float32Array(POSITIONS)}, - aTexCoord: {size: 2, value: new Float32Array(TEX_COORDS)}, - aCoordinate: {size: 2, value: new Float32Array(TEX_COORDS)} + clipSpacePosition: {size: 2, value: new Float32Array(POSITIONS)}, + texCoord: {size: 2, value: new Float32Array(TEX_COORDS)}, + coordinate: {size: 2, value: new Float32Array(TEX_COORDS)} } }) }); diff --git a/modules/engine/src/passes/shader-pass-renderer.ts b/modules/engine/src/passes/shader-pass-renderer.ts index 5a51cf8f7e..d0014ffed1 100644 --- a/modules/engine/src/passes/shader-pass-renderer.ts +++ b/modules/engine/src/passes/shader-pass-renderer.ts @@ -56,7 +56,18 @@ export class ShaderPassRenderer { }); this.clipSpace = new ClipSpace(device, { - fs: `\ + source: /* wgsl */ `\ + @group(0) @binding(0) var sourceTexture: texture_2d; + @group(0) @binding(1) var sourceTextureSampler: sampler; + +@fragment +fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4 { + let texCoord: vec2 = inputs.coordinate; + return textureSample(sourceTexture, sourceTextureSampler, texCoord); +} +`, + + fs: /* glsl */ `\ #version 300 es uniform sampler2D sourceTexture; diff --git a/modules/engine/test/compute/computation.spec.ts b/modules/engine/test/compute/computation.spec.ts index cdfb2b768e..4268ca3a8c 100644 --- a/modules/engine/test/compute/computation.spec.ts +++ b/modules/engine/test/compute/computation.spec.ts @@ -37,7 +37,7 @@ test('ComputePipeline compute', async t => { const computation = new Computation(webgpuDevice, { source, shaderLayout: { - bindings: [{name: 'data', type: 'storage', location: 0}] + bindings: [{name: 'data', type: 'storage', group: 0, location: 0}] } }); diff --git a/modules/gltf/package.json b/modules/gltf/package.json index 2882d1fc83..2494c9328b 100644 --- a/modules/gltf/package.json +++ b/modules/gltf/package.json @@ -40,9 +40,9 @@ "prepublishOnly": "npm run build-minified-bundle && npm run build-dev-bundle" }, "peerDependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19" }, "dependencies": { "@loaders.gl/core": "^4.2.0", diff --git a/modules/shadertools/package.json b/modules/shadertools/package.json index 8f349b20b6..82c7c7d296 100644 --- a/modules/shadertools/package.json +++ b/modules/shadertools/package.json @@ -46,7 +46,7 @@ "prepublishOnly": "npm run build-minified-bundle && npm run build-dev-bundle" }, "peerDependencies": { - "@luma.gl/core": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19" }, "dependencies": { "@math.gl/core": "4.1.0-alpha.3", diff --git a/modules/shadertools/src/lib/wgsl/get-shader-layout-wgsl.ts b/modules/shadertools/src/lib/wgsl/get-shader-layout-wgsl.ts index 491d9e9b77..d85c84a6aa 100644 --- a/modules/shadertools/src/lib/wgsl/get-shader-layout-wgsl.ts +++ b/modules/shadertools/src/lib/wgsl/get-shader-layout-wgsl.ts @@ -33,13 +33,31 @@ export function getShaderLayoutFromWGSL(source: string): ShaderLayout { shaderLayout.bindings.push({ type: 'uniform', name: uniform.name, - location: uniform.binding, - // @ts-expect-error group: uniform.group, + location: uniform.binding, + // @ts-expect-error TODO - unused for now but needs fixing members }); } + for (const texture of parsedWGSL.textures) { + shaderLayout.bindings.push({ + type: 'texture', + name: texture.name, + group: texture.group, + location: texture.binding + }); + } + + for (const sampler of parsedWGSL.samplers) { + shaderLayout.bindings.push({ + type: 'sampler', + name: sampler.name, + group: sampler.group, + location: sampler.binding + }); + } + const vertex = parsedWGSL.entry.vertex[0]; // "main" // Vertex shader inputs diff --git a/modules/shadertools/src/passes/postprocessing/image-adjust-filters/denoise.ts b/modules/shadertools/src/passes/postprocessing/image-adjust-filters/denoise.ts index 9e44d3b2b7..3ac8d612e2 100644 --- a/modules/shadertools/src/passes/postprocessing/image-adjust-filters/denoise.ts +++ b/modules/shadertools/src/passes/postprocessing/image-adjust-filters/denoise.ts @@ -5,6 +5,32 @@ import {ShaderPass} from '../../../lib/shader-module/shader-pass'; // Do a 9x9 bilateral box filter +const source = /* wgsl */ `\ + +@group(?), @binding(?) var denoiseUniforms { strength: f32 } noise; + +fn denoise_sampleColor(source: sampler2D, texSize: vec2, texCoord: vec2) -> vec4 { + let adjustedExponent: f32 = 3. + 200. * pow(1. - noise.strength, 4.); + let center: vec4 = sample_texture(BUFFER_source, texCoord); + var color: vec4 = vec4(0.); + var total: f32 = 0.; + + for (var x: f32 = -4.; x <= 4.; x = x + (1.)) { + + for (var y: f32 = -4.; y <= 4.; y = y + (1.)) { + let offsetColor: vec4 = sample_texture(BUFFER_source, texCoord + vec2(x, y) / texSize); + var weight: f32 = 1. - abs(dot(offsetColor.rgb - center.rgb, vec3(0.25))); + weight = pow(weight, adjustedExponent); + color = color + (offsetColor * weight); + total = total + (weight); + } + + } + + return color / total; +} +`; + const fs = /* glsl */ `\ uniform denoiseUniforms { float strength; @@ -63,6 +89,9 @@ export const denoise = { strength: {format: 'f32', value: 0.5, min: 0, max: 1} // strength: {..., adjust: (strength: number): number => 0.53 + 200 * Math.pow(1 - strength, 4) // TODO - JS preprocessing }, + + source, fs, + passes: [{sampler: true}, {sampler: true}] } as const satisfies ShaderPass; diff --git a/modules/shadertools/src/passes/postprocessing/image-adjust-filters/huesaturation.ts b/modules/shadertools/src/passes/postprocessing/image-adjust-filters/huesaturation.ts index 672009b306..8b0bdca08a 100644 --- a/modules/shadertools/src/passes/postprocessing/image-adjust-filters/huesaturation.ts +++ b/modules/shadertools/src/passes/postprocessing/image-adjust-filters/huesaturation.ts @@ -4,6 +4,47 @@ import {ShaderPass} from '../../../lib/shader-module/shader-pass'; +const source = /* wgsl */ `\ +@group(?), @binding(?) +var hueSaturationUniforms { hue: f32, + + saturation: f32, + +}hueSaturation; + +fn hueSaturation_filterColor(color: vec4) -> vec4 { + let angle: f32 = hueSaturation.hue * 3.1415927; + let s: f32 = sin(angle); + let c: f32 = cos(angle); + let weights: vec3 = (vec3(2. * c, -sqrt(3.) * s - c, sqrt(3.) * s - c) + 1.) / 3.; + let len: f32 = length(color.rgb); + var colorrgb = color.rgb; + colorrgb = vec3(dot(color.rgb, weights.xyz), dot(color.rgb, weights.zxy), dot(color.rgb, weights.yzx)); + color.r = colorrgb.x; + color.g = colorrgb.y; + color.b = colorrgb.z; + let average: f32 = (color.r + color.g + color.b) / 3.; + if (hueSaturation.saturation > 0.) { + var colorrgb = color.rgb; + colorrgb = color.rgb + ((average - color.rgb) * (1. - 1. / (1.001 - hueSaturation.saturation))); + color.r = colorrgb.x; + color.g = colorrgb.y; + color.b = colorrgb.z; + } else { + var colorrgb = color.rgb; + colorrgb = color.rgb + ((average - color.rgb) * -hueSaturation.saturation); + color.r = colorrgb.x; + color.g = colorrgb.y; + color.b = colorrgb.z; + } + return color; +} + +fn hueSaturation_filterColor_ext(color: vec4, texSize: vec2, texCoord: vec2) -> vec4 { + return hueSaturation_filterColor(color); +} +`; + const fs = /* glsl */ `\ uniform hueSaturationUniforms { float hue; @@ -64,6 +105,7 @@ export const hueSaturation = { props: {} as HueSaturationProps, name: 'hueSaturation', + source, fs, uniformTypes: { diff --git a/modules/shadertools/src/passes/postprocessing/image-adjust-filters/noise.ts b/modules/shadertools/src/passes/postprocessing/image-adjust-filters/noise.ts index 96b54d958e..411ab7d9ad 100644 --- a/modules/shadertools/src/passes/postprocessing/image-adjust-filters/noise.ts +++ b/modules/shadertools/src/passes/postprocessing/image-adjust-filters/noise.ts @@ -4,6 +4,26 @@ import {ShaderPass} from '../../../lib/shader-module/shader-pass'; +const source = /* wgsl */ `\ +struct noiseUniforms { + amount: f32 +}; + +@group(0), @binding(0) var noise: NoiseUniforms; + +fn rand(co: vec2) -> f32 { + return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.547); +} + +fn noise_filterColor_ext(color: vec4, texSize: vec2, texCoord: vec2) -> vec4 { + let diff: f32 = (rand(texCoord) - 0.5) * noise.amount; + color.r = color.r + (diff); + color.g = color.g + (diff); + color.b = color.b + (diff); + return color; +} +`; + const fs = /* glsl */ `\ uniform noiseUniforms { float amount; @@ -48,5 +68,6 @@ export const noise = { amount: {value: 0.5, min: 0, max: 1} }, fs, + source, passes: [{filter: true}] } as const satisfies ShaderPass; diff --git a/modules/shadertools/test/lib/wgsl/get-shader-layout-wgsl.spec.ts b/modules/shadertools/test/lib/wgsl/get-shader-layout-wgsl.spec.ts index 96b3e0e57e..0788ed5206 100644 --- a/modules/shadertools/test/lib/wgsl/get-shader-layout-wgsl.spec.ts +++ b/modules/shadertools/test/lib/wgsl/get-shader-layout-wgsl.spec.ts @@ -65,9 +65,9 @@ const TEST_CASES: {title?: string; wgsl: string; shaderLayout: ShaderLayout}[] = { type: 'uniform', name: 'uniforms', + group: 0, location: 0, // @ts-expect-error - group: 0, members: [ { name: 'modelViewProjectionMatrix', diff --git a/modules/shadertools/test/modules-webgl1/fp64/fp64-test-utils-transform.ts b/modules/shadertools/test/modules-webgl1/fp64/fp64-test-utils-transform.ts index 516c57099c..99cb617b10 100644 --- a/modules/shadertools/test/modules-webgl1/fp64/fp64-test-utils-transform.ts +++ b/modules/shadertools/test/modules-webgl1/fp64/fp64-test-utils-transform.ts @@ -104,7 +104,7 @@ export async function runTests( testCases }); - transform.model.setUniforms(fp64arithmetic.getUniforms()); + transform.model.setUniformsWebGL(fp64arithmetic.getUniforms()); transform.run(); const {buffer, byteOffset, byteLength} = await transform.readAsync('result'); diff --git a/modules/test-utils/package.json b/modules/test-utils/package.json index 9f462d6247..13c0366b03 100644 --- a/modules/test-utils/package.json +++ b/modules/test-utils/package.json @@ -36,11 +36,11 @@ "pre-build": "echo test utils has no bundle" }, "peerDependencies": { - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19" }, "dependencies": { "@probe.gl/env": "^4.0.8", diff --git a/modules/test-utils/src/null-device/null-canvas-context.ts b/modules/test-utils/src/null-device/null-canvas-context.ts index b539a8fe31..1bfdc4e069 100644 --- a/modules/test-utils/src/null-device/null-canvas-context.ts +++ b/modules/test-utils/src/null-device/null-canvas-context.ts @@ -18,6 +18,10 @@ export class NullCanvasContext extends CanvasContext { presentationSize: [number, number]; private _framebuffer: NullFramebuffer | null = null; + get [Symbol.toStringTag]() { + return 'NullCanvasContext'; + } + constructor(device: NullDevice, props: CanvasContextProps) { // Note: Base class creates / looks up the canvas (unless under Node.js) super(props); diff --git a/modules/webgl/package.json b/modules/webgl/package.json index 04ab51519d..e8f4a5aa2d 100644 --- a/modules/webgl/package.json +++ b/modules/webgl/package.json @@ -40,7 +40,7 @@ "prepublishOnly": "npm run build-minified-bundle && npm run build-dev-bundle" }, "peerDependencies": { - "@luma.gl/core": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19" }, "dependencies": { "@luma.gl/constants": "9.1.0-alpha.19", diff --git a/modules/webgl/src/adapter/helpers/get-shader-layout.ts b/modules/webgl/src/adapter/helpers/get-shader-layout.ts index f465ae9dba..41fee4ff4e 100644 --- a/modules/webgl/src/adapter/helpers/get-shader-layout.ts +++ b/modules/webgl/src/adapter/helpers/get-shader-layout.ts @@ -18,7 +18,10 @@ import {decodeGLUniformType, decodeGLAttributeType, isSamplerUniform} from './de * Note: `linkProgram()` needs to have been called * (although linking does not need to have been successful). */ -export function getShaderLayout(gl: WebGL2RenderingContext, program: WebGLProgram): ShaderLayout { +export function getShaderLayoutFromGLSL( + gl: WebGL2RenderingContext, + program: WebGLProgram +): ShaderLayout { const shaderLayout: ShaderLayout = { attributes: [], bindings: [] @@ -39,6 +42,7 @@ export function getShaderLayout(gl: WebGL2RenderingContext, program: WebGLProgra shaderLayout.bindings.push({ type: 'uniform', name: uniformBlock.name, + group: 0, location: uniformBlock.location, visibility: (uniformBlock.vertex ? 0x1 : 0) & (uniformBlock.fragment ? 0x2 : 0), minBindingSize: uniformBlock.byteLength, @@ -54,6 +58,7 @@ export function getShaderLayout(gl: WebGL2RenderingContext, program: WebGLProgra shaderLayout.bindings.push({ type: 'texture', name: uniform.name, + group: 0, location: textureUnit, viewDimension, sampleType diff --git a/modules/webgl/src/adapter/resources/webgl-render-pipeline.ts b/modules/webgl/src/adapter/resources/webgl-render-pipeline.ts index 15e64f3111..5c9b2d5526 100644 --- a/modules/webgl/src/adapter/resources/webgl-render-pipeline.ts +++ b/modules/webgl/src/adapter/resources/webgl-render-pipeline.ts @@ -16,7 +16,7 @@ import {RenderPipeline, log} from '@luma.gl/core'; // import {getAttributeInfosFromLayouts} from '@luma.gl/core'; import {GL} from '@luma.gl/constants'; -import {getShaderLayout} from '../helpers/get-shader-layout'; +import {getShaderLayoutFromGLSL} from '../helpers/get-shader-layout'; import {withDeviceAndGLParameters} from '../converters/device-parameters'; import {setUniform} from '../helpers/set-uniform'; import {splitUniformsAndBindings} from '../../utils/split-uniforms-and-bindings'; @@ -80,7 +80,7 @@ export class WEBGLRenderPipeline extends RenderPipeline { this._linkShaders(); log.time(1, `RenderPipeline ${this.id} - shaderLayout introspection`)(); - this.introspectedLayout = getShaderLayout(this.device.gl, this.handle); + this.introspectedLayout = getShaderLayoutFromGLSL(this.device.gl, this.handle); log.timeEnd(1, `RenderPipeline ${this.id} - shaderLayout introspection`)(); // Merge provided layout with introspected layout diff --git a/modules/webgl/src/adapter/webgl-canvas-context.ts b/modules/webgl/src/adapter/webgl-canvas-context.ts index 05c2d55f67..046387c17f 100644 --- a/modules/webgl/src/adapter/webgl-canvas-context.ts +++ b/modules/webgl/src/adapter/webgl-canvas-context.ts @@ -18,6 +18,10 @@ export class WebGLCanvasContext extends CanvasContext { presentationSize: [number, number]; private _framebuffer: WEBGLFramebuffer | null = null; + get [Symbol.toStringTag](): string { + return 'WebGLCanvasContext'; + } + constructor(device: WebGLDevice, props: CanvasContextProps) { // Note: Base class creates / looks up the canvas (unless under Node.js) super(props); diff --git a/modules/webgl/src/index.ts b/modules/webgl/src/index.ts index 6e614eccac..c58b292931 100644 --- a/modules/webgl/src/index.ts +++ b/modules/webgl/src/index.ts @@ -46,7 +46,7 @@ export type {AccessorObject} from './types'; export {setDeviceParameters, withDeviceParameters} from './adapter/converters/device-parameters'; // HELPERS - EXPERIMENTAL -export {getShaderLayout} from './adapter/helpers/get-shader-layout'; +export {getShaderLayoutFromGLSL} from './adapter/helpers/get-shader-layout'; export {WebGLStateTracker} from './context/state-tracker/webgl-state-tracker'; // TEST EXPORTS diff --git a/modules/webgpu/package.json b/modules/webgpu/package.json index 931e2b8fec..eee722f3ec 100644 --- a/modules/webgpu/package.json +++ b/modules/webgpu/package.json @@ -37,7 +37,7 @@ "prepublishOnly": "npm run build-minified-bundle && npm run build-dev-bundle" }, "peerDependencies": { - "@luma.gl/core": "9.1.0-alpha.17" + "@luma.gl/core": "9.1.0-alpha.19" }, "dependencies": { "@probe.gl/env": "^4.0.8", diff --git a/modules/webgpu/src/adapter/helpers/get-bind-group.ts b/modules/webgpu/src/adapter/helpers/get-bind-group.ts index 1fc71b60a5..a475149dc9 100644 --- a/modules/webgpu/src/adapter/helpers/get-bind-group.ts +++ b/modules/webgpu/src/adapter/helpers/get-bind-group.ts @@ -65,16 +65,26 @@ function getBindGroupEntries( const entries: GPUBindGroupEntry[] = []; for (const [bindingName, value] of Object.entries(bindings)) { - const bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName); + let bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName); if (bindingLayout) { entries.push(getBindGroupEntry(value, bindingLayout.location)); } + + // TODO - hack to automatically bind samplers to supplied texture default samplers + bindingLayout = getShaderLayoutBinding(shaderLayout, `${bindingName}Sampler`); + if (bindingLayout) { + entries.push(getBindGroupEntry(value, bindingLayout.location, {sampler: true})); + } } return entries; } -function getBindGroupEntry(binding: Binding, index: number): GPUBindGroupEntry { +function getBindGroupEntry( + binding: Binding, + index: number, + options?: {sampler?: boolean} +): GPUBindGroupEntry { if (binding instanceof Buffer) { return { binding: index, @@ -89,6 +99,12 @@ function getBindGroupEntry(binding: Binding, index: number): GPUBindGroupEntry { resource: (binding as WebGPUSampler).handle }; } else if (binding instanceof Texture) { + if (options?.sampler) { + return { + binding: index, + resource: (binding as WebGPUTexture).sampler.handle + }; + } return { binding: index, resource: (binding as WebGPUTexture).handle.createView({label: 'bind-group-auto-created'}) diff --git a/modules/webgpu/src/adapter/resources/webgpu-render-pass.ts b/modules/webgpu/src/adapter/resources/webgpu-render-pass.ts index f75928c341..d8115d6e87 100644 --- a/modules/webgpu/src/adapter/resources/webgpu-render-pass.ts +++ b/modules/webgpu/src/adapter/resources/webgpu-render-pass.ts @@ -46,7 +46,13 @@ export class WebGPURenderPass extends RenderPass { throw new Error('commandEncoder not available'); } + this.device.handle.pushErrorScope('validation'); this.handle = this.props.handle || device.commandEncoder.beginRenderPass(renderPassDescriptor); + this.device.handle.popErrorScope().then((error: GPUError | null) => { + if (error) { + log.error(`${this} creation failed:\n"${error.message}"`, this)(); + } + }); this.handle.label = this.props.id; log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)(); log.probe(3, JSON.stringify(renderPassDescriptor, null, 2))(); diff --git a/modules/webgpu/src/adapter/resources/webgpu-render-pipeline.ts b/modules/webgpu/src/adapter/resources/webgpu-render-pipeline.ts index 477e8f7926..f28b774ce9 100644 --- a/modules/webgpu/src/adapter/resources/webgpu-render-pipeline.ts +++ b/modules/webgpu/src/adapter/resources/webgpu-render-pipeline.ts @@ -39,10 +39,18 @@ export class WebGPURenderPipeline extends RenderPipeline { log.groupCollapsed(1, `new WebGPURenderPipeline(${this.id})`)(); log.probe(1, JSON.stringify(descriptor, null, 2))(); log.groupEnd(1)(); + + this.device.handle.pushErrorScope('validation'); this.handle = this.device.handle.createRenderPipeline(descriptor); + this.device.handle.popErrorScope().then((error: GPUError | null) => { + if (error) { + log.error(`${this} creation failed:\n"${error.message}"`, this, this.props.vs?.source)(); + } + }); } this.handle.label = this.props.id; + // Note: Often the same shader in WebGPU this.vs = props.vs as WebGPUShader; this.fs = props.fs as WebGPUShader; @@ -78,7 +86,13 @@ export class WebGPURenderPipeline extends RenderPipeline { const webgpuRenderPass = options.renderPass as WebGPURenderPass; // Set pipeline + this.device.handle.pushErrorScope('validation'); webgpuRenderPass.handle.setPipeline(this.handle); + this.device.handle.popErrorScope().then((error: GPUError | null) => { + if (error) { + log.error(`${this} setPipeline failed:\n"${error.message}"`, this)(); + } + }); // Set bindings (uniform buffers, textures etc) const bindGroup = this._getBindGroup(); diff --git a/modules/webgpu/src/adapter/resources/webgpu-shader.ts b/modules/webgpu/src/adapter/resources/webgpu-shader.ts index 9516d6392c..6a5aeae6d6 100644 --- a/modules/webgpu/src/adapter/resources/webgpu-shader.ts +++ b/modules/webgpu/src/adapter/resources/webgpu-shader.ts @@ -17,29 +17,38 @@ export class WebGPUShader extends Shader { super(device, props); this.device = device; + const isGLSL = props.source.includes('#version'); + if (this.props.language === 'glsl' || isGLSL) { + throw new Error('GLSL shaders are not supported in WebGPU'); + } + this.device.handle.pushErrorScope('validation'); + this.handle = this.props.handle || this.device.handle.createShaderModule({code: props.source}); + this.device.handle.popErrorScope().then((error: GPUError | null) => { + if (error) { + log.error(`${this} creation failed:\n"${error.message}"`, this, this.props.source)(); + } + }); - this.handle = this.props.handle || this.createHandle(); this.handle.label = this.props.id; - - this._checkCompilationError(this.device.handle.popErrorScope()); + this._checkCompilationError(); } get asyncCompilationStatus(): Promise { return this.getCompilationInfo().then(() => this.compilationStatus); } - async _checkCompilationError(errorScope: Promise): Promise { - const error = (await errorScope) as GPUValidationError; - if (error) { - // The `Shader` base class will determine if debug window should be opened based on props - this.debugShader(); + async _checkCompilationError(): Promise { + const shaderLog = await this.getCompilationInfo(); + const hasErrors = Boolean(shaderLog.find(msg => msg.type === 'error')); + this.compilationStatus = hasErrors ? 'error' : 'success'; + this.debugShader(); - const shaderLog = await this.getCompilationInfo(); - log.error(`Shader compilation error: ${error.message}`, shaderLog)(); + if (this.compilationStatus === 'error') { + log.error(`Shader compilation error`, shaderLog)(); // Note: Even though this error is asynchronous and thrown after the constructor completes, // it will result in a useful stack trace leading back to the constructor - throw new Error(`Shader compilation error: ${error.message}`); + // throw new Error(`Shader compilation error`); } } @@ -55,17 +64,4 @@ export class WebGPUShader extends Shader { const compilationInfo = await this.handle.getCompilationInfo(); return compilationInfo.messages; } - - // PRIVATE METHODS - - protected createHandle(): GPUShaderModule { - const {source} = this.props; - - const isGLSL = source.includes('#version'); - if (this.props.language === 'glsl' || isGLSL) { - throw new Error('GLSL shaders are not supported in WebGPU'); - } - - return this.device.handle.createShaderModule({code: source}); - } } diff --git a/modules/webgpu/src/adapter/resources/webgpu-vertex-array.ts b/modules/webgpu/src/adapter/resources/webgpu-vertex-array.ts index 0850ec2b80..0dd8d08ebc 100644 --- a/modules/webgpu/src/adapter/resources/webgpu-vertex-array.ts +++ b/modules/webgpu/src/adapter/resources/webgpu-vertex-array.ts @@ -57,7 +57,12 @@ export class WebGPUVertexArray extends VertexArray { const webgpuIndexBuffer = this.indexBuffer as WebGPUBuffer; if (webgpuIndexBuffer?.handle) { // Note we can't unset an index buffer - log.warn('setting index buffer', webgpuIndexBuffer?.handle, webgpuIndexBuffer?.indexType)(); + log.info( + 3, + 'setting index buffer', + webgpuIndexBuffer?.handle, + webgpuIndexBuffer?.indexType + )(); webgpuRenderPass.handle.setIndexBuffer( webgpuIndexBuffer?.handle, // @ts-expect-error TODO - we must enforce type @@ -67,7 +72,7 @@ export class WebGPUVertexArray extends VertexArray { for (let location = 0; location < this.maxVertexAttributes; location++) { const webgpuBuffer = this.attributes[location] as WebGPUBuffer; if (webgpuBuffer?.handle) { - log.warn(`setting vertex buffer ${location}`, webgpuBuffer?.handle)(); + log.info(3, `setting vertex buffer ${location}`, webgpuBuffer?.handle)(); webgpuRenderPass.handle.setVertexBuffer(location, webgpuBuffer?.handle); } } diff --git a/modules/webgpu/src/adapter/webgpu-canvas-context.ts b/modules/webgpu/src/adapter/webgpu-canvas-context.ts index 1157eb15be..bcdb9ce677 100644 --- a/modules/webgpu/src/adapter/webgpu-canvas-context.ts +++ b/modules/webgpu/src/adapter/webgpu-canvas-context.ts @@ -26,10 +26,14 @@ export class WebGPUCanvasContext extends CanvasContext { private depthStencilAttachment: Texture | null = null; + get [Symbol.toStringTag](): string { + return 'WebGPUCanvasContext'; + } + constructor(device: WebGPUDevice, adapter: GPUAdapter, props: CanvasContextProps) { super(props); this.device = device; - // TODO - hack to trigger resize? + // TODO - ugly hack to trigger first resize this.width = -1; this.height = -1; @@ -76,13 +80,15 @@ export class WebGPUCanvasContext extends CanvasContext { /** Resizes and updates render targets if necessary */ update() { - const [width, height] = this.getPixelSize(); + const oldWidth = this.width; + const oldHeight = this.height; + const [newWidth, newHeight] = this.getPixelSize(); - const sizeChanged = width !== this.width || height !== this.height; + const sizeChanged = newWidth !== oldWidth || newHeight !== oldHeight; if (sizeChanged) { - this.width = width; - this.height = height; + this.width = newWidth; + this.height = newHeight; if (this.depthStencilAttachment) { this.depthStencilAttachment.destroy(); @@ -100,7 +106,7 @@ export class WebGPUCanvasContext extends CanvasContext { alphaMode: this.props.alphaMode }); - log.log(1, `Resized to ${this.width}x${this.height}px`)(); + log.log(1, `${this} Resized ${oldWidth}x${oldHeight} => ${newWidth}x${newHeight}px`)(); } } diff --git a/modules/webgpu/src/adapter/webgpu-device.ts b/modules/webgpu/src/adapter/webgpu-device.ts index 6d577709e6..7a6d6c1eee 100644 --- a/modules/webgpu/src/adapter/webgpu-device.ts +++ b/modules/webgpu/src/adapter/webgpu-device.ts @@ -89,8 +89,13 @@ export class WebGPUDevice extends Device { device.addEventListener('uncapturederror', (event: Event) => { // TODO is this the right way to make sure the error is an Error instance? const errorMessage = - event instanceof GPUUncapturedErrorEvent ? event.error.message : 'Unknown error'; - this.error(new Error(errorMessage)); + event instanceof GPUUncapturedErrorEvent ? event.error.message : 'Unknown WebGPU error'; + this.reportError(new Error(errorMessage)); + if (this.props.debug) { + // eslint-disable-next-line no-debugger + debugger; + } + event.preventDefault(); }); // "Context" loss handling @@ -209,13 +214,17 @@ export class WebGPUDevice extends Device { } submit(): void { - // this.renderPass?.end(); const commandBuffer = this.commandEncoder?.finish(); if (commandBuffer) { + this.handle.pushErrorScope('validation'); this.handle.queue.submit([commandBuffer]); + this.handle.popErrorScope().then((error: GPUError | null) => { + if (error) { + this.reportError(new Error(`WebGPU command submission failed: ${error.message}`)); + } + }); } this.commandEncoder = null; - // this.renderPass = null; } // PRIVATE METHODS diff --git a/website/package.json b/website/package.json index 1089a29e46..c2f2e8a852 100644 --- a/website/package.json +++ b/website/package.json @@ -21,13 +21,13 @@ "@algolia/autocomplete-js": "^1.8.3", "@docusaurus/core": "^3.4.0", "@docusaurus/preset-classic": "^3.4.0", - "@luma.gl/constants": "9.1.0-alpha.17", - "@luma.gl/core": "9.1.0-alpha.17", - "@luma.gl/engine": "9.1.0-alpha.17", - "@luma.gl/gltf": "9.1.0-alpha.17", - "@luma.gl/shadertools": "9.1.0-alpha.17", - "@luma.gl/webgl": "9.1.0-alpha.17", - "@luma.gl/webgpu": "9.1.0-alpha.17", + "@luma.gl/constants": "9.1.0-alpha.19", + "@luma.gl/core": "9.1.0-alpha.19", + "@luma.gl/engine": "9.1.0-alpha.19", + "@luma.gl/gltf": "9.1.0-alpha.19", + "@luma.gl/shadertools": "9.1.0-alpha.19", + "@luma.gl/webgl": "9.1.0-alpha.19", + "@luma.gl/webgpu": "9.1.0-alpha.19", "@mdx-js/react": "^3.0.0", "clsx": "^1.1.1", "react": "^18.2.0", diff --git a/website/yarn.lock b/website/yarn.lock index 4d7402a05e..976ef1029b 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -3711,91 +3711,91 @@ __metadata: languageName: node linkType: hard -"@luma.gl/constants@npm:9.1.0-alpha.17": - version: 9.1.0-alpha.17 - resolution: "@luma.gl/constants@npm:9.1.0-alpha.17" - checksum: 10c0/7e645ee0d31cc7bb5b2b4898c7374edfa55387b44e61cd33b45e8e56946789c70ba7449b57dc53c59bd6a7b2a80fa1a6858dc1dce9689e2340a07ecf8d017652 +"@luma.gl/constants@npm:9.1.0-alpha.19": + version: 9.1.0-alpha.19 + resolution: "@luma.gl/constants@npm:9.1.0-alpha.19" + checksum: 10c0/d66e6f38bdc7a5b3441d2dd9b8c9516726efe9c32462c3c0c256f8885b42ef51bf0101e50dbd44a989f73cc3968ccfc12e9c79e53e21e987a0faf8b684635415 languageName: node linkType: hard -"@luma.gl/core@npm:9.1.0-alpha.17": - version: 9.1.0-alpha.17 - resolution: "@luma.gl/core@npm:9.1.0-alpha.17" +"@luma.gl/core@npm:9.1.0-alpha.19": + version: 9.1.0-alpha.19 + resolution: "@luma.gl/core@npm:9.1.0-alpha.19" dependencies: "@math.gl/types": "npm:4.1.0-alpha.3" "@probe.gl/env": "npm:^4.0.8" "@probe.gl/log": "npm:^4.0.8" "@probe.gl/stats": "npm:^4.0.8" "@types/offscreencanvas": "npm:^2019.6.4" - checksum: 10c0/757b910ce30517e8ac15d06033df9c53a9759a83c3f58cb2bfee2812b4520e42c71d148c266c0175c4a22b768e7eff681c2b32594e28be1be56b3eb475575ecb + checksum: 10c0/113c2399b40c85d6a9dfdebfcd48f077dc775817e8739173ddcb96c0446338b07b9b2200ed37712d176e19f931361fa0e79871989a33d33012c2728d9917404a languageName: node linkType: hard -"@luma.gl/engine@npm:9.1.0-alpha.17": - version: 9.1.0-alpha.17 - resolution: "@luma.gl/engine@npm:9.1.0-alpha.17" +"@luma.gl/engine@npm:9.1.0-alpha.19": + version: 9.1.0-alpha.19 + resolution: "@luma.gl/engine@npm:9.1.0-alpha.19" dependencies: "@math.gl/core": "npm:4.1.0-alpha.3" "@math.gl/types": "npm:4.1.0-alpha.3" "@probe.gl/log": "npm:^4.0.8" "@probe.gl/stats": "npm:^4.0.8" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.16 - "@luma.gl/shadertools": 9.1.0-alpha.16 - checksum: 10c0/e389401b52500874904885a74e962398afa53b923f1d57feacb5310a0026d9b505ea38fd1123d91bec22e4b8be84aa7011ceb2ca7a8919c98ab786b209dc81fa + "@luma.gl/core": 9.1.0-alpha.17 + "@luma.gl/shadertools": 9.1.0-alpha.17 + checksum: 10c0/a0ad31de058d71bc8622b4ab37dbb89291fbefd67a7f97b16360d4dd17d4283e86fa751e6eb7c9840f8f4c71998e6ff6bd86ca8a59f3436b10fe5f7837214a36 languageName: node linkType: hard -"@luma.gl/gltf@npm:9.1.0-alpha.17": - version: 9.1.0-alpha.17 - resolution: "@luma.gl/gltf@npm:9.1.0-alpha.17" +"@luma.gl/gltf@npm:9.1.0-alpha.19": + version: 9.1.0-alpha.19 + resolution: "@luma.gl/gltf@npm:9.1.0-alpha.19" dependencies: "@loaders.gl/core": "npm:^4.2.0" "@loaders.gl/textures": "npm:^4.2.0" "@math.gl/core": "npm:4.1.0-alpha.3" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.16 - "@luma.gl/engine": 9.1.0-alpha.16 - "@luma.gl/shadertools": 9.1.0-alpha.16 - checksum: 10c0/4e852617d47a3d54328a50e2d78ac2bd1b690c971da0f39ff2c1e29de2dc4760f33bd74ffe91ca93a0540e4c0960a154f6daae6e891148835fd697f4904d6ed1 + "@luma.gl/core": 9.1.0-alpha.17 + "@luma.gl/engine": 9.1.0-alpha.17 + "@luma.gl/shadertools": 9.1.0-alpha.17 + checksum: 10c0/26eed519b2a3cab453bcd874c64e8c07a10f2a1e0ad8a1c49007488f2344a9608613ca7d2e19d9cf0ff057af7b6a4396b30468ffd2bb0641ba2507dc26b1a160 languageName: node linkType: hard -"@luma.gl/shadertools@npm:9.1.0-alpha.17": - version: 9.1.0-alpha.17 - resolution: "@luma.gl/shadertools@npm:9.1.0-alpha.17" +"@luma.gl/shadertools@npm:9.1.0-alpha.19": + version: 9.1.0-alpha.19 + resolution: "@luma.gl/shadertools@npm:9.1.0-alpha.19" dependencies: "@math.gl/core": "npm:4.1.0-alpha.3" "@math.gl/types": "npm:4.1.0-alpha.3" wgsl_reflect: "npm:^1.0.1" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.16 - checksum: 10c0/3230b669f4a2fac95b598e05e7d8562a9bd085971095119b210890e31fb238c700044a17acdb942f094f8ca8fb66ebccd18f3e2e862b2e7ed37ea3e96f10131f + "@luma.gl/core": 9.1.0-alpha.17 + checksum: 10c0/01071c7fbb4669c60b30713a370e60a0cb77b8eb38c327d26cbf90c293d2a023be8cbdcbdcd1bc93afdb878ab4129fd2b8025a56afd02689bd36aa46879601b9 languageName: node linkType: hard -"@luma.gl/webgl@npm:9.1.0-alpha.17": - version: 9.1.0-alpha.17 - resolution: "@luma.gl/webgl@npm:9.1.0-alpha.17" +"@luma.gl/webgl@npm:9.1.0-alpha.19": + version: 9.1.0-alpha.19 + resolution: "@luma.gl/webgl@npm:9.1.0-alpha.19" dependencies: - "@luma.gl/constants": "npm:9.1.0-alpha.17" + "@luma.gl/constants": "npm:9.1.0-alpha.19" "@math.gl/types": "npm:4.1.0-alpha.3" "@probe.gl/env": "npm:^4.0.8" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.16 - checksum: 10c0/0e4f5ab37863550c99144b8a95a227d3706ffaa6a11919aec2105d1483e508738513accaf2ba22a2e26d358d5dbdeb5099e18a4683144e56abc93fe163cc9ac9 + "@luma.gl/core": 9.1.0-alpha.17 + checksum: 10c0/f1381a0cf103fd308902e5f370e1e3c3cf74ffac2578ffb875f1d9c60cd172b952c3075fb2afeb2b4cc8b2ec2eb05aeafa636b479bdd0861dd5f8ef8a54dc518 languageName: node linkType: hard -"@luma.gl/webgpu@npm:9.1.0-alpha.17": - version: 9.1.0-alpha.17 - resolution: "@luma.gl/webgpu@npm:9.1.0-alpha.17" +"@luma.gl/webgpu@npm:9.1.0-alpha.19": + version: 9.1.0-alpha.19 + resolution: "@luma.gl/webgpu@npm:9.1.0-alpha.19" dependencies: "@probe.gl/env": "npm:^4.0.8" "@webgpu/types": "npm:^0.1.34" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.16 - checksum: 10c0/9e2f9e0eb9520347ca1c43fc33f04b1fad4eb90b2b0898fea02a70137166e818440d4e34a0ee671aeb4f5cd7a67f5a0f83762ca4f5f4e8aa655d6705dd7e9754 + "@luma.gl/core": 9.1.0-alpha.17 + checksum: 10c0/f0def6797f396269de2ff4728cbb18a08ec5ba20b026c5014bd2eec008cce9d0ed67bb4a32ae5ba446639432165d3a386345eb75911fa4543ef1bf237f84e705 languageName: node linkType: hard @@ -14266,13 +14266,13 @@ __metadata: "@docusaurus/plugin-content-docs": "npm:^3.4.0" "@docusaurus/preset-classic": "npm:^3.4.0" "@docusaurus/tsconfig": "npm:^3.4.0" - "@luma.gl/constants": "npm:9.1.0-alpha.17" - "@luma.gl/core": "npm:9.1.0-alpha.17" - "@luma.gl/engine": "npm:9.1.0-alpha.17" - "@luma.gl/gltf": "npm:9.1.0-alpha.17" - "@luma.gl/shadertools": "npm:9.1.0-alpha.17" - "@luma.gl/webgl": "npm:9.1.0-alpha.17" - "@luma.gl/webgpu": "npm:9.1.0-alpha.17" + "@luma.gl/constants": "npm:9.1.0-alpha.19" + "@luma.gl/core": "npm:9.1.0-alpha.19" + "@luma.gl/engine": "npm:9.1.0-alpha.19" + "@luma.gl/gltf": "npm:9.1.0-alpha.19" + "@luma.gl/shadertools": "npm:9.1.0-alpha.19" + "@luma.gl/webgl": "npm:9.1.0-alpha.19" + "@luma.gl/webgpu": "npm:9.1.0-alpha.19" "@mdx-js/react": "npm:^3.0.0" babel-plugin-styled-components: "npm:^2.0.0" clsx: "npm:^1.1.1" diff --git a/yarn.lock b/yarn.lock index d04df6a220..394e5caa34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2311,8 +2311,8 @@ __metadata: "@probe.gl/log": "npm:^4.0.8" "@probe.gl/stats": "npm:^4.0.8" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.17 - "@luma.gl/shadertools": 9.1.0-alpha.17 + "@luma.gl/core": 9.1.0-alpha.19 + "@luma.gl/shadertools": 9.1.0-alpha.19 languageName: unknown linkType: soft @@ -2324,9 +2324,9 @@ __metadata: "@loaders.gl/textures": "npm:^4.2.0" "@math.gl/core": "npm:4.1.0-alpha.3" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.17 - "@luma.gl/engine": 9.1.0-alpha.17 - "@luma.gl/shadertools": 9.1.0-alpha.17 + "@luma.gl/core": 9.1.0-alpha.19 + "@luma.gl/engine": 9.1.0-alpha.19 + "@luma.gl/shadertools": 9.1.0-alpha.19 languageName: unknown linkType: soft @@ -2338,7 +2338,7 @@ __metadata: "@math.gl/types": "npm:4.1.0-alpha.3" wgsl_reflect: "npm:^1.0.1" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.17 + "@luma.gl/core": 9.1.0-alpha.19 languageName: unknown linkType: soft @@ -2349,11 +2349,11 @@ __metadata: "@probe.gl/env": "npm:^4.0.8" "@probe.gl/stats": "npm:^4.0.8" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.17 - "@luma.gl/engine": 9.1.0-alpha.17 - "@luma.gl/shadertools": 9.1.0-alpha.17 - "@luma.gl/webgl": 9.1.0-alpha.17 - "@luma.gl/webgpu": 9.1.0-alpha.17 + "@luma.gl/core": 9.1.0-alpha.19 + "@luma.gl/engine": 9.1.0-alpha.19 + "@luma.gl/shadertools": 9.1.0-alpha.19 + "@luma.gl/webgl": 9.1.0-alpha.19 + "@luma.gl/webgpu": 9.1.0-alpha.19 languageName: unknown linkType: soft @@ -2365,7 +2365,7 @@ __metadata: "@math.gl/types": "npm:4.1.0-alpha.3" "@probe.gl/env": "npm:^4.0.8" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.17 + "@luma.gl/core": 9.1.0-alpha.19 languageName: unknown linkType: soft @@ -2376,7 +2376,7 @@ __metadata: "@probe.gl/env": "npm:^4.0.8" "@webgpu/types": "npm:^0.1.34" peerDependencies: - "@luma.gl/core": 9.1.0-alpha.17 + "@luma.gl/core": 9.1.0-alpha.19 languageName: unknown linkType: soft