diff --git a/examples/webgpu/hello-triangle/app.ts b/examples/webgpu/hello-triangle/app.ts
index 775d56a1f..2f9fe1317 100644
--- a/examples/webgpu/hello-triangle/app.ts
+++ b/examples/webgpu/hello-triangle/app.ts
@@ -1,4 +1,4 @@
-///
+///
import {Model, WebGPUDevice} from '@luma.gl/webgpu';
@@ -16,7 +16,7 @@ void main() {
}
`,
- fragment: `#version 450
+ fragment: `#version 450
layout(location = 0) out vec4 outColor;
void main() {
@@ -52,8 +52,20 @@ export async function init(canvas: HTMLCanvasElement, language: 'glsl' | 'wgsl')
const model = new Model(device, {
vs: SHADERS[language].vertex,
fs: SHADERS[language].fragment,
- topology: "triangle-list",
- vertexCount: 3
+ topology: 'triangle-list',
+ vertexCount: 3,
+ parameters: {
+ // Enable depth testing so that the fragment closest to the camera
+ // is rendered in front.
+ depthWriteEnabled: true,
+ depthCompare: 'less',
+ depthFormat: 'depth24plus',
+
+ // Backface culling since the cube is solid piece of geometry.
+ // Faces pointing away from the camera will be occluded by faces
+ // pointing toward the camera.
+ cullMode: 'back',
+ }
});
function frame() {
diff --git a/examples/webgpu/hello-triangle/package.json b/examples/webgpu/hello-triangle/package.json
index 065c90ad1..a01ef17d1 100644
--- a/examples/webgpu/hello-triangle/package.json
+++ b/examples/webgpu/hello-triangle/package.json
@@ -1,5 +1,5 @@
{
- "name": "luma.gl-examples-hello-triangle-webgpu",
+ "name": "luma.gl-examples-webgpu-hello-triangle",
"version": "1.0.0",
"private": true,
"scripts": {
diff --git a/examples/webgpu/rotating-cube/app.ts b/examples/webgpu/rotating-cube/app.ts
new file mode 100644
index 000000000..b04adcd3a
--- /dev/null
+++ b/examples/webgpu/rotating-cube/app.ts
@@ -0,0 +1,166 @@
+import {Model, WebGPUDevice} from '@luma.gl/webgpu';
+import {Matrix4} from '@math.gl/core';
+
+import {
+ cubePositions,
+ cubeUVs,
+ cubeVertexCount
+} from './cube';
+
+export const title = 'Rotating Cube';
+export const description = 'Shows rendering a basic triangle.';
+
+/** Provide both GLSL and WGSL shaders */
+const SHADERS = {
+ wgsl: {
+ vertex: `
+struct Uniforms {
+ modelViewProjectionMatrix : mat4x4;
+};
+[[binding(0), group(0)]] var uniforms : Uniforms;
+
+struct VertexOutput {
+ [[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 {
+ var output : VertexOutput;
+ output.Position = uniforms.modelViewProjectionMatrix * position;
+ output.fragUV = uv;
+ output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0));
+ return output;
+}
+ `,
+ fragment: `
+[[stage(fragment)]]
+fn main([[location(0)]] fragUV: vec2,
+ [[location(1)]] fragPosition: vec4) -> [[location(0)]] vec4 {
+ return fragPosition;
+}
+ `
+ }
+};
+
+// const vertexBufferLayout = {
+// arrayStride: cubeVertexSize,
+// attributes: [
+// {
+// // position
+// shaderLocation: 0,
+// offset: cubePositionOffset,
+// format: 'float32x4',
+// },
+// {
+// // uv
+// shaderLocation: 1,
+// offset: cubeUVOffset,
+// format: 'float32x2',
+// },
+// ],
+// };
+
+// const uniformBindGroup = device.handle.createBindGroup({
+// layout: pipeline.handle.getBindGroupLayout(0),
+// entries: [
+// {
+// binding: 0,
+// resource: {
+// buffer: uniformBuffer.handle,
+// },
+// },
+// ],
+// });
+
+const UNIFORM_BUFFER_SIZE = 4 * 16; // 4x4 matrix
+
+export async function init(canvas: HTMLCanvasElement, language: 'glsl' | 'wgsl') {
+ const device = await WebGPUDevice.create({canvas});
+
+ // Create a vertex buffer from the cube data.
+ const positionBuffer = device.createBuffer({id: 'cube-positions', data: cubePositions});
+ const uvBuffer = device.createBuffer({id: 'cube-uvs', data: cubeUVs});
+
+
+ const uniformBuffer = device.createBuffer({
+ byteLength: UNIFORM_BUFFER_SIZE,
+ // TODO - use API constants instead of WebGPU constants
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
+ });
+
+ const model = new Model(device, {
+ id: 'cube',
+ vs: SHADERS[language].vertex,
+ fs: SHADERS[language].fragment,
+ topology: 'triangle-list',
+ attributeLayouts: [
+ {name: 'position', location: 0, accessor: {format: 'float32x4'}},
+ {name: 'uv', location: 1, accessor: {format: 'float32x2'}}
+ ],
+ attributeBuffers: [positionBuffer, uvBuffer],
+ bindings: [uniformBuffer],
+ vertexCount: cubeVertexCount,
+ parameters: {
+ // Enable depth testing so that the fragment closest to the camera
+ // is rendered in front.
+ depthWriteEnabled: true,
+ depthCompare: 'less',
+ depthFormat: 'depth24plus',
+
+ // Backface culling since the cube is solid piece of geometry.
+ // Faces pointing away from the camera will be occluded by faces
+ // pointing toward the camera.
+ cullMode: 'back',
+ },
+ });
+
+ const projectionMatrix = new Matrix4();
+ const viewMatrix = new Matrix4();
+ const modelViewProjectionMatrix = new Matrix4();
+
+ function frame() {
+ const aspect = canvas.width / canvas.height;
+ const now = Date.now() / 1000;
+
+ viewMatrix.identity().translate([0, 0, -4]).rotateAxis(1, [Math.sin(now), Math.cos(now), 0]);
+ projectionMatrix.perspective({fov: (2 * Math.PI) / 5, aspect, near: 1, far: 100.0});
+ modelViewProjectionMatrix.copy(viewMatrix).multiplyLeft(projectionMatrix);
+ uniformBuffer.write(new Float32Array(modelViewProjectionMatrix));
+
+ device.beginRenderPass();
+ model.draw();
+
+ device.submit();
+ requestAnimationFrame(frame);
+ }
+
+ requestAnimationFrame(frame);
+}
+
+init(document.getElementById('canvas') as HTMLCanvasElement, 'wgsl');
+
+
+/*
+const projectionMatrix = mat4.create();
+mat4.perspective(projectionMatrix, );
+
+function getTransformationMatrix() {
+ const viewMatrix = mat4.create();
+ mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -4));
+ const now = Date.now() / 1000;
+ mat4.rotate(
+ viewMatrix,
+ viewMatrix,
+ 1,
+ vec3.fromValues(Math.sin(now), Math.cos(now), 0)
+ );
+
+ const modelViewProjectionMatrix = mat4.create();
+ mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
+
+ return modelViewProjectionMatrix as Float32Array;
+}
+*/
diff --git a/examples/webgpu/rotating-cube/cube.ts b/examples/webgpu/rotating-cube/cube.ts
new file mode 100644
index 000000000..4aff0493a
--- /dev/null
+++ b/examples/webgpu/rotating-cube/cube.ts
@@ -0,0 +1,142 @@
+export const cubeVertexSize = 4 * 10; // Byte size of one cube vertex.
+export const cubePositionOffset = 0;
+export const cubeColorOffset = 4 * 4; // Byte offset of cube vertex color attribute.
+export const cubeUVOffset = 4 * 8;
+export const cubeVertexCount = 36;
+
+// float4 position
+// prettier-ignore
+export const cubePositions = new Float32Array([
+ 1, -1, 1, 1,
+ -1, -1, 1, 1,
+ -1, -1, -1, 1,
+ 1, -1, -1, 1,
+ 1, -1, 1, 1,
+ -1, -1, -1, 1,
+
+ 1, 1, 1, 1,
+ 1, -1, 1, 1,
+ 1, -1, -1, 1,
+ 1, 1, -1, 1,
+ 1, 1, 1, 1,
+ 1, -1, -1, 1,
+
+ -1, 1, 1, 1,
+ 1, 1, 1, 1,
+ 1, 1, -1, 1,
+ -1, 1, -1, 1,
+ -1, 1, 1, 1,
+ 1, 1, -1, 1,
+
+ -1, -1, 1, 1,
+ -1, 1, 1, 1,
+ -1, 1, -1, 1,
+ -1, -1, -1, 1,
+ -1, -1, 1, 1,
+ -1, 1, -1, 1,
+
+ 1, 1, 1, 1,
+ -1, 1, 1, 1,
+ -1, -1, 1, 1,
+ -1, -1, 1, 1,
+ 1, -1, 1, 1,
+ 1, 1, 1, 1,
+
+ 1, -1, -1, 1,
+ -1, -1, -1, 1,
+ -1, 1, -1, 1,
+ 1, 1, -1, 1,
+ 1, -1, -1, 1,
+ -1, 1, -1, 1,
+]);
+
+// float2 uv,
+// prettier-ignore
+export const cubeUVs = new Float32Array([
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 0,
+
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 0,
+
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 0,
+
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 0,
+
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 0,
+]);
+
+export const cubeColors = new Float32Array([
+ // float4 position, float4 color, float2 uv,
+ 1, 0, 1, 1,
+ 0, 0, 1, 1,
+ 0, 0, 0, 1,
+ 1, 0, 0, 1,
+ 1, 0, 1, 1,
+ 0, 0, 0, 1,
+
+ 1, 1, 1, 1,
+ 1, 0, 1, 1,
+ 1, 0, 0, 1,
+ 1, 1, 0, 1,
+ 1, 1, 1, 1,
+ 1, 0, 0, 1,
+
+ 0, 1, 1, 1,
+ 1, 1, 1, 1,
+ 1, 1, 0, 1,
+ 0, 1, 0, 1,
+ 0, 1, 1, 1,
+ 1, 1, 0, 1,
+
+ 0, 0, 1, 1,
+ 0, 1, 1, 1,
+ 0, 1, 0, 1,
+ 0, 0, 0, 1,
+ 0, 0, 1, 1,
+ 0, 1, 0, 1,
+
+ 1, 1, 1, 1,
+ 0, 1, 1, 1,
+ 0, 0, 1, 1,
+ 0, 0, 1, 1,
+ 1, 0, 1, 1,
+ 1, 1, 1, 1,
+
+ 1, 0, 0, 1,
+ 0, 0, 0, 1,
+ 0, 1, 0, 1,
+ 1, 1, 0, 1,
+ 1, 0, 0, 1,
+ 0, 1, 0, 1,
+]);
diff --git a/examples/webgpu/rotating-cube/index.html b/examples/webgpu/rotating-cube/index.html
new file mode 100644
index 000000000..c155a807d
--- /dev/null
+++ b/examples/webgpu/rotating-cube/index.html
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/examples/webgpu/rotating-cube/package.json b/examples/webgpu/rotating-cube/package.json
new file mode 100644
index 000000000..ef828eba8
--- /dev/null
+++ b/examples/webgpu/rotating-cube/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "luma.gl-examples-webgpu-rotating-cube",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "start": "vite",
+ "build": "tsc && vite build",
+ "serve": "vite preview"
+ },
+ "dependencies": {
+ "@luma.gl/engine": "8.6.0-alpha.5",
+ "@luma.gl/shadertools": "8.6.0-alpha.5",
+ "@luma.gl/webgpu": "8.6.0-alpha.5",
+ "@math.gl/core": "^3.5.0"
+ },
+ "devDependencies": {
+ "typescript": "^4.3.2",
+ "vite": "^2.6.4"
+ }
+}
diff --git a/examples/webgpu/rotating-cube/vite.config.ts b/examples/webgpu/rotating-cube/vite.config.ts
new file mode 100644
index 000000000..46bb698c1
--- /dev/null
+++ b/examples/webgpu/rotating-cube/vite.config.ts
@@ -0,0 +1,18 @@
+import fs from 'fs/promises';
+import { defineConfig } from 'vite'
+
+/** @see https://vitejs.dev/config/ */
+export default defineConfig(async () => ({
+ resolve: {alias: await getAliases('@luma.gl', `${__dirname}/../../..`)},
+ server: {open: true}
+}));
+
+/** Run against local source */
+const getAliases = async (frameworkName, frameworkRootDir) => {
+ const modules = await fs.readdir(`${frameworkRootDir}/modules`)
+ const aliases = {}
+ for (const module of modules) {
+ aliases[`${frameworkName}/${module}`] = `${frameworkRootDir}/modules/${module}/src`
+ }
+ return aliases
+}
diff --git a/modules/api/src/adapter/device.ts b/modules/api/src/adapter/device.ts
index e40c29c12..7763c6246 100644
--- a/modules/api/src/adapter/device.ts
+++ b/modules/api/src/adapter/device.ts
@@ -1,8 +1,8 @@
// luma.gl, MIT license
import StatsManager, {lumaStats} from '../utils/stats-manager';
-import type {default as Buffer, BufferProps} from './buffer';
-import type {default as Texture, TextureProps} from './texture';
-import type {default as Shader, ShaderProps} from './shader';
+import type {default as Buffer, BufferProps} from './resources/buffer';
+import type {default as Texture, TextureProps} from './resources/texture';
+import type {default as Shader, ShaderProps} from './resources/shader';
// import type {RenderPipeline, RenderPipelineProps, ComputePipeline, ComputePipelineProps} from './pipeline';
export type ShadingLanguage = 'glsl' | 'wgsl';
diff --git a/modules/api/src/adapter/buffer.ts b/modules/api/src/adapter/resources/buffer.ts
similarity index 94%
rename from modules/api/src/adapter/buffer.ts
rename to modules/api/src/adapter/resources/buffer.ts
index 822dddb34..6cd8dc4ef 100644
--- a/modules/api/src/adapter/buffer.ts
+++ b/modules/api/src/adapter/resources/buffer.ts
@@ -1,5 +1,5 @@
// luma.gl, MIT license
-import type Device from './device';
+import type Device from '../device';
import Resource, {ResourceProps, DEFAULT_RESOURCE_PROPS} from './resource';
export type BufferProps = ResourceProps & {
@@ -30,7 +30,7 @@ const DEFAULT_BUFFER_PROPS: Required = {
byteLength: 0,
data: undefined,
usage: undefined, // GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
- mappedAtCreation: true
+ mappedAtCreation: false
};
/** Abstract GPU buffer */
@@ -53,16 +53,7 @@ export default abstract class Buffer extends Resource {
super(device, props, DEFAULT_BUFFER_PROPS);
}
- // Mapped API (WebGPU)
-
- /** Maps the memory so that it can be read */
- // abstract mapAsync(mode, byteOffset, byteLength): Promise
-
- /** Get the mapped range of data for reading or writing */
- // abstract getMappedRange(byteOffset, byteLength): ArrayBuffer;
-
- /** unmap makes the contents of the buffer available to the GPU again */
- // abstract unmap(): void;
+ write(data: ArrayBufferView, byteOffset: number = 0): void { throw new Error('not implemented'); }
// Convenience API
@@ -103,4 +94,15 @@ export default abstract class Buffer extends Resource {
}
}
*/
+
+ // Mapped API (WebGPU)
+
+ /** Maps the memory so that it can be read */
+ // abstract mapAsync(mode, byteOffset, byteLength): Promise
+
+ /** Get the mapped range of data for reading or writing */
+ // abstract getMappedRange(byteOffset, byteLength): ArrayBuffer;
+
+ /** unmap makes the contents of the buffer available to the GPU again */
+ // abstract unmap(): void;
}
diff --git a/modules/api/src/adapter/render-pipeline.ts b/modules/api/src/adapter/resources/render-pipeline.ts
similarity index 80%
rename from modules/api/src/adapter/render-pipeline.ts
rename to modules/api/src/adapter/resources/render-pipeline.ts
index eafbe724a..2cb20896c 100644
--- a/modules/api/src/adapter/render-pipeline.ts
+++ b/modules/api/src/adapter/resources/render-pipeline.ts
@@ -1,9 +1,10 @@
// luma.gl, MIT license
-import type Device from './device';
+import type Device from '../device';
import Resource, {ResourceProps, DEFAULT_RESOURCE_PROPS} from './resource';
import type {default as Shader} from './shader';
-import {RenderPipelineParameters} from './parameters';
-import type {BindingLayout} from './types';
+import type {RenderPipelineParameters} from '../types/parameters';
+import type {BindingLayout} from '../types/types';
+import type {AttributeBinding} from '../types/program-bindings';
export type RenderPipelineProps = ResourceProps & {
vertexShader: Shader;
@@ -16,6 +17,7 @@ export type RenderPipelineProps = ResourceProps & {
topology?: 'point-list' | 'line-list' | 'line-strip' | 'triangle-list' | 'triangle-strip';
parameters?: RenderPipelineParameters;
+ attributeLayouts?: AttributeBinding[];
layout?: BindingLayout[];
};
diff --git a/modules/api/src/adapter/resource.ts b/modules/api/src/adapter/resources/resource.ts
similarity index 80%
rename from modules/api/src/adapter/resource.ts
rename to modules/api/src/adapter/resources/resource.ts
index fe255e6a9..6571aaaf4 100644
--- a/modules/api/src/adapter/resource.ts
+++ b/modules/api/src/adapter/resources/resource.ts
@@ -1,6 +1,6 @@
-//
-import Device from './device';
-// import {uid} from '../utils';
+// luma.gl, MIT license
+import type Device from '../device';
+import {uid} from '../../utils/utils';
export type ResourceProps = {
id?: string;
@@ -38,8 +38,10 @@ export default abstract class Resource {
throw new Error('no device');
}
this._device = device;
- this.props = this.initializeProps(props, defaultProps);
- this.id = this.props.id || 'no-id'; // TODO uid(this[Symbol.toStringTag] || this.constructor.name);
+ this.props = selectivelyMerge(props, defaultProps);
+ this.props.id = this.props.id || uid(this[Symbol.toStringTag]);
+
+ this.id = this.props.id;
this.userData = this.props.userData || {};
this.addStats();
}
@@ -105,19 +107,20 @@ export default abstract class Resource {
stats.get(`${name}s Active`).incrementCount();
}
- /**
- * Combines a map of user props and default props, only including props from defaultProps
- * @param props
- * @param defaultProps
- * @returns returns a map of overridden default props
- */
- private initializeProps(props: Props, defaultProps: Required): Required {
- const mergedProps = {...defaultProps};
- for (const key in props) {
- if (props[key] !== undefined) {
- mergedProps[key] = props[key];
- }
- }
- return mergedProps;
- }
+}
+
+/**
+ * Combines a map of user props and default props, only including props from defaultProps
+ * @param props
+ * @param defaultProps
+ * @returns returns a map of overridden default props
+ */
+ function selectivelyMerge(props: Props, defaultProps: Required): Required {
+ const mergedProps = {...defaultProps};
+ for (const key in props) {
+ if (props[key] !== undefined) {
+ mergedProps[key] = props[key];
+ }
+ }
+ return mergedProps;
}
diff --git a/modules/api/src/adapter/sampler.ts b/modules/api/src/adapter/resources/sampler.ts
similarity index 93%
rename from modules/api/src/adapter/sampler.ts
rename to modules/api/src/adapter/resources/sampler.ts
index 33f863fbe..86dcc86c7 100644
--- a/modules/api/src/adapter/sampler.ts
+++ b/modules/api/src/adapter/resources/sampler.ts
@@ -1,6 +1,6 @@
-import {CompareFunction} from './parameters';
+import type Device from '../device';
+import {CompareFunction} from '../types/parameters';
import Resource, {ResourceProps, DEFAULT_RESOURCE_PROPS} from './resource';
-import type Device from './device';
export type SamplerAddressMode = 'clamp-to-edge' | 'repeat' | 'mirror-repeat';
export type SamplerFilterMode = 'nearest' | 'linear';
diff --git a/modules/api/src/adapter/shader.ts b/modules/api/src/adapter/resources/shader.ts
similarity index 97%
rename from modules/api/src/adapter/shader.ts
rename to modules/api/src/adapter/resources/shader.ts
index f81ccc80f..032aa4b30 100644
--- a/modules/api/src/adapter/shader.ts
+++ b/modules/api/src/adapter/resources/shader.ts
@@ -1,5 +1,5 @@
// luma.gl, MIT license
-import type Device from './device';
+import type Device from '../device';
import Resource, {ResourceProps, DEFAULT_RESOURCE_PROPS} from './resource';
export type CompilerMessageType = 'error' | 'warning' | 'info';
diff --git a/modules/api/src/adapter/texture.ts b/modules/api/src/adapter/resources/texture.ts
similarity index 69%
rename from modules/api/src/adapter/texture.ts
rename to modules/api/src/adapter/resources/texture.ts
index 3ac7c64c4..0b1b00a47 100644
--- a/modules/api/src/adapter/texture.ts
+++ b/modules/api/src/adapter/resources/texture.ts
@@ -1,6 +1,7 @@
// luma.gl, MIT license
+import type Device from '../device';
+import type {TextureFormat} from '../types/types';
import Resource, {ResourceProps, DEFAULT_RESOURCE_PROPS} from './resource';
-import type Device from './device';
// required GPUExtent3D size;
// GPUIntegerCoordinate mipLevelCount = 1;
@@ -11,35 +12,42 @@ import type Device from './device';
/** Abstract Texture interface */
export type TextureProps = ResourceProps & {
- data?: any;
+ format?: TextureFormat | number;
+ dimension?: '1d' | '2d' | '3d';
width?: number;
height?: number;
depth?: number;
+ usage?: number;
+
+ data?: any;
+ mipmaps?: boolean;
+ parameters?: object;
- pixels?: any;
- format?: number;
- dataFormat?: number;
- border?: number;
- recreate?: boolean;
type?: number;
compressed?: boolean;
- mipmaps?: boolean;
- parameters?: object;
+ /** @deprecated use data */
+ pixels?: any;
+ /** @deprecated use format */
+ dataFormat?: number;
+ /** @deprecated rarely supported */
+ border?: number;
+ /** @deprecated WebGL only. */
pixelStore?: object;
+ /** @deprecated WebGL only. */
textureUnit?: number;
-
+ /** @deprecated WebGL only. Use dimension. */
target?: number;
+ /** @deprecated not supported */
+ recreate?: boolean;
};
export type WebGPUTextureProps = ResourceProps & {
- dimension?: '1d' | '2d' | '3d';
width: number;
height: number;
depth?: number;
mipLevels?: number;
format?: string;
- usage?: number;
};
export type TextureViewProps = {
@@ -48,22 +56,35 @@ export type TextureViewProps = {
aspect?: 'all', 'stencil-only', 'depth-only';
arrayLayerCount: number;
baseArrayLayer?: number;
- mipLevelCount: number;
+ mipLevels?: number;
baseMipLevel?: number;
};
+// @ts-expect-error
const DEFAULT_TEXTURE_PROPS: Required = {
...DEFAULT_RESOURCE_PROPS,
+ data: undefined,
dimension: '2d',
width: 1,
height: 1,
depth: 1,
- mipLevels: 1,
- // @ts-expect-error
- format: 'unorm8',
+ mipmaps: false,
+ parameters: {},
+ type: undefined,
+ compressed: false,
+ // mipLevels: 1,
+ format: 'rgba8unorm',
usage: 0
};
+// const DEFAULT_TEXTURE_PROPS: Required = {
+// handle: undefined,
+// id: undefined,
+// depth: 1,
+// format: 'rgba8unorm',
+// usage: GPUTextureUsage.COPY_DST
+// };
+
/**
* Abstract Texture interface
* Texture Object
diff --git a/modules/api/src/adapter/types.ts b/modules/api/src/adapter/types.ts
deleted file mode 100644
index 97c1f8757..000000000
--- a/modules/api/src/adapter/types.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-// luma.gl, MIT license
-enum TextureFormat {
- // 8-bit formats
- 'r8unorm',
- 'r8snorm',
- 'r8uint',
- 'r8sint',
-
- // 16-bit formats
- 'r16uint',
- 'r16sint',
- 'r16float',
- 'rg8unorm',
- 'rg8snorm',
- 'rg8uint',
- 'rg8sint',
-
- // 32-bit formats
- 'r32uint',
- 'r32sint',
- 'r32float',
- 'rg16uint',
- 'rg16sint',
- 'rg16float',
- 'rgba8unorm',
- 'rgba8unorm-srgb',
- 'rgba8snorm',
- 'rgba8uint',
- 'rgba8sint',
- 'bgra8unorm',
- 'bgra8unorm-srgb',
- // Packed 32-bit formats
- 'rgb9e5ufloat',
- 'rgb10a2unorm',
- 'rg11b10ufloat',
-
- // 64-bit formats
- 'rg32uint',
- 'rg32sint',
- 'rg32float',
- 'rgba16uint',
- 'rgba16sint',
- 'rgba16float',
-
- // 128-bit formats
- 'rgba32uint',
- 'rgba32sint',
- 'rgba32float',
-
- // Depth and stencil formats
- 'stencil8',
- 'depth16unorm',
- 'depth24plus',
- 'depth24plus-stencil8',
- 'depth32float',
-
- // BC compressed formats usable if 'texture-compression-bc' is both
- // supported by the device/user agent and enabled in requestDevice.
- 'bc1-rgba-unorm',
- 'bc1-rgba-unorm-srgb',
- 'bc2-rgba-unorm',
- 'bc2-rgba-unorm-srgb',
- 'bc3-rgba-unorm',
- 'bc3-rgba-unorm-srgb',
- 'bc4-r-unorm',
- 'bc4-r-snorm',
- 'bc5-rg-unorm',
- 'bc5-rg-snorm',
- 'bc6h-rgb-ufloat',
- 'bc6h-rgb-float',
- 'bc7-rgba-unorm',
- 'bc7-rgba-unorm-srgb',
-
- // 'depth24unorm-stencil8' feature
- 'depth24unorm-stencil8',
-
- // 'depth32float-stencil8' feature
- 'depth32float-stencil8',
-};
-
-// BINDING LAYOUTS
-
-type BufferBindingLayout = {
- location?: number;
- visibility: number;
- type: 'uniform' | 'storage' | 'read-only-storage';
- hasDynamicOffset?: boolean;
- minBindingSize?: number;
-}
-
-type TextureBindingLayout = {
- location?: number;
- visibility: number;
- viewDimension?: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d';
- sampleType?: 'float' | 'unfilterable-float' | 'depth' | 'sint' | 'uint';
- multisampled?: boolean;
-};
-
-type StorageTextureBindingLayout = {
- location?: number;
- visibility: number;
- access?: 'write-only';
- format: TextureFormat;
- viewDimension?: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d';
-};
-
-export type BindingLayout = BufferBindingLayout | TextureBindingLayout | StorageTextureBindingLayout;
-
-// BINDINGS
-
-import type Buffer from './buffer';
-import type Texture from './texture'; // TextureView...
-
-export type Binding = Texture | Buffer | {buffer: Buffer, offset?: number, size?: number};
-
-// Attachments
-
-export type ColorAttachment = {
- // attachment: GPUTextureView;
- // resolveTarget?: GPUTextureView;
- // loadValue: GPULoadOp | GPUColor;
- // storeOp?: GPUStoreOp;
-};
-
-export type DepthStencilAttachment = {
- // attachment: GPUTextureView;
-
- // depthLoadValue: GPULoadOp | number;
- // depthStoreOp: GPUStoreOp;
- // depthReadOnly?: boolean;
-
- // stencilLoadValue: GPULoadOp | number;
- // stencilStoreOp: GPUStoreOp;
- // stencilReadOnly?: boolean;
-};
diff --git a/modules/api/src/adapter/accessor.ts b/modules/api/src/adapter/types/accessor.ts
similarity index 56%
rename from modules/api/src/adapter/accessor.ts
rename to modules/api/src/adapter/types/accessor.ts
index a87e2a199..2759ea96f 100644
--- a/modules/api/src/adapter/accessor.ts
+++ b/modules/api/src/adapter/types/accessor.ts
@@ -1,30 +1,38 @@
// luma.gl, MIT license
+import type {VertexFormat} from './types';
+
// ACCESSORS
/**
* Attribute descriptor object
*/
export type Accessor = {
- format?: string;
+ format: VertexFormat;
offset?: number;
- location?: number;
-
// can now be described with single WebGPU-style `format` string
- type?: number;
- size?: number;
- normalized?: boolean;
- integer?: boolean;
- // deprecated - now shared between accessors in the surrounding BufferAccessor object
+ //
stride?: number;
+ stepMode?: 'vertex' | 'instance';
+
+ /** @deprecated - Use accessor.stepMode */
divisor?: number;
+
+ /** @deprecated - Infer from format */
+ type?: number;
+ /** @deprecated - Infer from format */
+ size?: number;
+ /** @deprecated - Infer from format */
+ normalized?: boolean;
+ /** @deprecated - Infer from format */
+ integer?: boolean;
};
/**
* List of attribute descriptors for one interleaved buffer
*/
-export type BufferAccessors = {
+export type InterleavedAccessors = {
stride?: number;
stepMode?: 'vertex' | 'instance';
attributes: Accessor[];
-}
+};
diff --git a/modules/api/src/adapter/parameters.ts b/modules/api/src/adapter/types/parameters.ts
similarity index 92%
rename from modules/api/src/adapter/parameters.ts
rename to modules/api/src/adapter/types/parameters.ts
index ecb5ef650..b5c538cb3 100644
--- a/modules/api/src/adapter/parameters.ts
+++ b/modules/api/src/adapter/types/parameters.ts
@@ -1,3 +1,5 @@
+import {DepthOrStencilTextureFormat} from './types';
+
export type CompareFunction =
'never' |
'less' |
@@ -53,6 +55,7 @@ export type StencilOperation =
export type DepthStencilParameters = {
depthWriteEnabled?: boolean;
depthCompare?: CompareFunction;
+ depthFormat?: DepthOrStencilTextureFormat;
stencilReadMask?: number;
stencilWriteMask?: number;
@@ -156,8 +159,8 @@ export type RenderPipelineParameters =
export type Parameters =
_RenderParameters &
DepthStencilParameters &
- ColorParameters;
- // MultisampleParameters;
+ ColorParameters &
+ MultisampleParameters;
// export const DEFAULT_PARAMETERS: Parameters;
@@ -167,15 +170,19 @@ export const DEFAULT_PARAMETERS: Required = {
cullMode: 'none',
frontFace: 'ccw',
+
+ // Depth Parameters
+
+ depthWriteEnabled: false,
+ depthCompare: 'always',
+ depthFormat: 'depth24plus',
+
depthClamp: false,
depthBias: 0,
depthBiasSlopeScale: 0,
depthBiasClamp: 0,
- // Depth Stencil Parameters
-
- depthWriteEnabled: false,
- depthCompare: 'always',
+ // Stencil parameters
stencilReadMask: 0xFFFFFFFF,
stencilWriteMask: 0xFFFFFFFF,
@@ -185,6 +192,11 @@ export const DEFAULT_PARAMETERS: Required = {
stencilFailOperation: 'keep',
stencilDepthFailOperation: 'keep',
+ // Multisample parameters
+ sampleCount: 0,
+ sampleMask: 0xFFFFFFFF,
+ sampleAlphaToCoverageEnabled: false,
+
// Color and blend parameters
blendColorOperation: 'add',
diff --git a/modules/webgl/src/helpers/program-bindings.ts b/modules/api/src/adapter/types/program-bindings.ts
similarity index 100%
rename from modules/webgl/src/helpers/program-bindings.ts
rename to modules/api/src/adapter/types/program-bindings.ts
diff --git a/modules/api/src/adapter/types/types.ts b/modules/api/src/adapter/types/types.ts
new file mode 100644
index 000000000..442b03a46
--- /dev/null
+++ b/modules/api/src/adapter/types/types.ts
@@ -0,0 +1,173 @@
+// luma.gl, MIT license
+
+/** Depth and stencil texture formats */
+export type DepthOrStencilTextureFormat =
+ 'stencil8' |
+ 'depth16unorm' |
+ 'depth24plus' |
+ 'depth24plus-stencil8' |
+ 'depth32float' |
+ // device.features.has('depth24unorm-stencil8')
+ 'depth24unorm-stencil8' |
+ // device.features.has('depth32float-stencil8')
+ 'depth32float-stencil8';
+
+/** Texture formats */
+export type TextureFormat = DepthOrStencilTextureFormat |
+ // 8-bit formats
+ 'r8unorm' |
+ 'r8snorm' |
+ 'r8uint' |
+ 'r8sint' |
+
+ // 16-bit formats
+ 'r16uint' |
+ 'r16sint' |
+ 'r16float' |
+ 'rg8unorm' |
+ 'rg8snorm' |
+ 'rg8uint' |
+ 'rg8sint' |
+
+ // 32-bit formats
+ 'r32uint' |
+ 'r32sint' |
+ 'r32float' |
+ 'rg16uint' |
+ 'rg16sint' |
+ 'rg16float' |
+ 'rgba8unorm' |
+ 'rgba8unorm-srgb' |
+ 'rgba8snorm' |
+ 'rgba8uint' |
+ 'rgba8sint' |
+ 'bgra8unorm' |
+ 'bgra8unorm-srgb' |
+ // Packed 32-bit formats
+ 'rgb9e5ufloat' |
+ 'rgb10a2unorm' |
+ 'rg11b10ufloat' |
+
+ // 64-bit formats
+ 'rg32uint' |
+ 'rg32sint' |
+ 'rg32float' |
+ 'rgba16uint' |
+ 'rgba16sint' |
+ 'rgba16float' |
+
+ // 128-bit formats
+ 'rgba32uint' |
+ 'rgba32sint' |
+ 'rgba32float' |
+
+ // BC compressed formats usable if 'texture-compression-bc' is both
+ // supported by the device/user agent and enabled in requestDevice.
+ 'bc1-rgba-unorm' |
+ 'bc1-rgba-unorm-srgb' |
+ 'bc2-rgba-unorm' |
+ 'bc2-rgba-unorm-srgb' |
+ 'bc3-rgba-unorm' |
+ 'bc3-rgba-unorm-srgb' |
+ 'bc4-r-unorm' |
+ 'bc4-r-snorm' |
+ 'bc5-rg-unorm' |
+ 'bc5-rg-snorm' |
+ 'bc6h-rgb-ufloat' |
+ 'bc6h-rgb-float' |
+ 'bc7-rgba-unorm' |
+ 'bc7-rgba-unorm-srgb';
+
+/** Attribute formats */
+export type VertexFormat =
+ 'uint8x2' |
+ 'uint8x4' |
+ 'sint8x2' |
+ 'sint8x4' |
+ 'unorm8x2' |
+ 'unorm8x4' |
+ 'snorm8x2' |
+ 'snorm8x4' |
+ 'uint16x2' |
+ 'uint16x4' |
+ 'sint16x2' |
+ 'sint16x4' |
+ 'unorm16x2' |
+ 'unorm16x4' |
+ 'snorm16x2' |
+ 'snorm16x4' |
+ 'float16x2' |
+ 'float16x4' |
+ 'float32' |
+ 'float32x2' |
+ 'float32x3' |
+ 'float32x4' |
+ 'uint32' |
+ 'uint32x2' |
+ 'uint32x3' |
+ 'uint32x4' |
+ 'sint32' |
+ 'sint32x2' |
+ 'sint32x3' |
+ 'sint32x4';
+
+// BINDING LAYOUTS
+
+type BufferBindingLayout = {
+ location?: number;
+ visibility: number;
+ type: 'uniform' | 'storage' | 'read-only-storage';
+ hasDynamicOffset?: boolean;
+ minBindingSize?: number;
+}
+
+type TextureBindingLayout = {
+ location?: number;
+ visibility: number;
+ viewDimension?: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d';
+ sampleType?: 'float' | 'unfilterable-float' | 'depth' | 'sint' | 'uint';
+ multisampled?: boolean;
+};
+
+type StorageTextureBindingLayout = {
+ location?: number;
+ visibility: number;
+ access?: 'write-only';
+ format: TextureFormat;
+ viewDimension?: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d';
+};
+
+export type BindingLayout = BufferBindingLayout | TextureBindingLayout | StorageTextureBindingLayout;
+
+// BINDINGS
+
+import type Buffer from '../resources/buffer';
+import type Texture from '../resources/texture'; // TextureView...
+
+export type Binding = Texture | Buffer | {buffer: Buffer, offset?: number, size?: number};
+
+// ATTRIBUTE LAYOUTS
+
+
+
+
+// Attachments
+
+export type ColorAttachment = {
+ // attachment: GPUTextureView;
+ // resolveTarget?: GPUTextureView;
+ // loadValue: GPULoadOp | GPUColor;
+ // storeOp?: GPUStoreOp;
+};
+
+export type DepthStencilAttachment = {
+ // attachment: GPUTextureView;
+
+ // depthLoadValue: GPULoadOp | number;
+ // depthStoreOp: GPUStoreOp;
+ // depthReadOnly?: boolean;
+
+ // stencilLoadValue: GPULoadOp | number;
+ // stencilStoreOp: GPUStoreOp;
+ // stencilReadOnly?: boolean;
+};
diff --git a/modules/api/src/index.ts b/modules/api/src/index.ts
index 7bf1934ea..3977f41f2 100644
--- a/modules/api/src/index.ts
+++ b/modules/api/src/index.ts
@@ -1,24 +1,27 @@
+// Initialize any global state
+import './init';
+
// MAIN API ACCESS POINTS
export {default as luma} from './lib/luma';
export type {DeviceLimits, DeviceInfo} from './adapter/device';
export {default as Device} from './adapter/device';
// GPU RESOURCES
-export type {ResourceProps} from './adapter/resource';
-export {default as Resource} from './adapter/resource';
-export type {BufferProps} from './adapter/buffer';
-export {default as Buffer} from './adapter/buffer';
-export type {TextureProps} from './adapter/texture';
-export {default as Texture} from './adapter/texture';
-export type {ShaderProps, CompilerMessage} from './adapter/shader';
-export {default as Shader} from './adapter/shader';
-export type {SamplerProps} from './adapter/sampler';
-export {default as Sampler} from './adapter/sampler';
-export type {RenderPipelineProps} from './adapter/render-pipeline';
-export {default as RenderPipeline} from './adapter/render-pipeline';
+export type {ResourceProps} from './adapter/resources/resource';
+export {default as Resource} from './adapter/resources/resource';
+export type {BufferProps} from './adapter/resources/buffer';
+export {default as Buffer} from './adapter/resources/buffer';
+export type {TextureProps} from './adapter/resources/texture';
+export {default as Texture} from './adapter/resources/texture';
+export type {ShaderProps, CompilerMessage} from './adapter/resources/shader';
+export {default as Shader} from './adapter/resources/shader';
+export type {SamplerProps} from './adapter/resources/sampler';
+export {default as Sampler} from './adapter/resources/sampler';
+export type {RenderPipelineProps} from './adapter/resources/render-pipeline';
+export {default as RenderPipeline} from './adapter/resources/render-pipeline';
// API TYPES
-export type {Accessor, BufferAccessors} from './adapter/accessor';
+export type {Accessor, InterleavedAccessors} from './adapter/types/accessor';
export type {
Parameters,
PrimitiveTopology,
@@ -35,9 +38,24 @@ export type {
MultisampleParameters,
RenderPassParameters,
RenderPipelineParameters
-} from './adapter/parameters';
+} from './adapter/types/parameters';
-export type {BindingLayout, Binding, ColorAttachment, DepthStencilAttachment} from './adapter/types';
+export type {
+ TextureFormat,
+ VertexFormat,
+ BindingLayout,
+ Binding,
+ ColorAttachment,
+ DepthStencilAttachment
+} from './adapter/types/types';
+
+export type {
+ ProgramBindings,
+ AttributeBinding,
+ UniformBinding,
+ UniformBlockBinding,
+ VaryingBinding
+} from './adapter/types/program-bindings';
// UTILS
export type {TypedArray, NumberArray} from './types';
diff --git a/modules/api/src/init.ts b/modules/api/src/init.ts
new file mode 100644
index 000000000..16cc33a38
--- /dev/null
+++ b/modules/api/src/init.ts
@@ -0,0 +1,35 @@
+import {isBrowser} from '@probe.gl/env';
+import {log} from './utils/log';
+import {lumaStats} from './utils/stats-manager';
+
+// Version detection using babel plugin
+// @ts-expect-error
+const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'untranspiled source';
+
+const STARTUP_MESSAGE = 'set luma.log.level=1 (or higher) to trace rendering';
+// Assign luma.log.level in console to control logging: \
+// 0: none, 1: minimal, 2: verbose, 3: attribute/uniforms, 4: gl logs
+// luma.log.break[], set to gl funcs, luma.log.profile[] set to model names`;
+
+if (globalThis.luma && globalThis.luma.VERSION !== VERSION) {
+ throw new Error(`luma.gl - multiple VERSIONs detected: ${globalThis.luma.VERSION} vs ${VERSION}`);
+}
+
+if (!globalThis.luma) {
+ if (isBrowser()) {
+ log.log(1, `luma.gl ${VERSION} - ${STARTUP_MESSAGE}`)();
+ }
+
+ globalThis.luma = globalThis.luma || {
+ VERSION,
+ version: VERSION,
+ log,
+
+ // A global stats object that various components can add information to
+ // E.g. see webgl/resource.js
+ stats: lumaStats,
+ };
+}
+
+export {lumaStats};
+export default globalThis.luma;
diff --git a/modules/engine/test/lib/model.spec.js b/modules/engine/test/lib/model.spec.js
index fc49153ff..d1cd2c15b 100644
--- a/modules/engine/test/lib/model.spec.js
+++ b/modules/engine/test/lib/model.spec.js
@@ -2,7 +2,7 @@ import test from 'tape-promise/tape';
import {webgl1TestDevice} from '@luma.gl/test-utils';
import GL from '@luma.gl/constants';
-import luma from '@luma.gl/webgl/init';
+import {luma} from '@luma.gl/api';
// TODO - Model test should not depend on Cube
import {Model, ProgramManager} from '@luma.gl/engine';
import {Buffer} from '@luma.gl/webgl';
diff --git a/modules/webgl/src/classes/program-configuration.ts b/modules/webgl/src/classes/program-configuration.ts
index 2c7415349..d6394c950 100644
--- a/modules/webgl/src/classes/program-configuration.ts
+++ b/modules/webgl/src/classes/program-configuration.ts
@@ -1,9 +1,9 @@
// Contains metadata describing attribute configurations for a program's shaders
// Much of this is automatically extracted from shaders after program linking
// import Accessor from './accessor';
-import type Program from './program';
-import type {AttributeBinding, VaryingBinding} from '../helpers/program-bindings';
+import type {AttributeBinding, VaryingBinding} from '@luma.gl/api';
import {getProgramBindings} from '../helpers/get-program-bindings';
+import type Program from './program';
/**
* is a mechanism for taking a program object and querying information
diff --git a/modules/webgl/src/classes/texture-2d.ts b/modules/webgl/src/classes/texture-2d.ts
index 01d0247f5..bc24f2c30 100644
--- a/modules/webgl/src/classes/texture-2d.ts
+++ b/modules/webgl/src/classes/texture-2d.ts
@@ -4,6 +4,7 @@ import Texture, {TextureProps, TextureSupportOptions} from './texture';
export type Texture2DProps = TextureProps & {
+ format?: number;
};
export default class Texture2D extends Texture {
diff --git a/modules/webgl/src/classes/vertex-array.ts b/modules/webgl/src/classes/vertex-array.ts
index 47f7dcede..319b5b111 100644
--- a/modules/webgl/src/classes/vertex-array.ts
+++ b/modules/webgl/src/classes/vertex-array.ts
@@ -1,9 +1,8 @@
-import {log, assert} from '@luma.gl/api';
+import {log, assert, AttributeBinding} from '@luma.gl/api';
import GL from '@luma.gl/constants';
import Accessor from './accessor';
import Buffer from './webgl-buffer';
import Program from './program';
-import type {AttributeBinding} from '../helpers/program-bindings'
import ProgramConfiguration from './program-configuration';
import VertexArrayObject, {VertexArrayObjectProps} from './vertex-array-object';
diff --git a/modules/webgl/src/helpers/get-program-bindings.ts b/modules/webgl/src/helpers/get-program-bindings.ts
index a2ca0393f..40fcbe5ae 100644
--- a/modules/webgl/src/helpers/get-program-bindings.ts
+++ b/modules/webgl/src/helpers/get-program-bindings.ts
@@ -1,6 +1,6 @@
+import {ProgramBindings, AttributeBinding, UniformBinding, UniformBlockBinding, VaryingBinding} from '@luma.gl/api';
import GL from '@luma.gl/constants';
import {isWebGL2} from '../context/context/webgl-checks';
-import {ProgramBindings, AttributeBinding, UniformBinding, UniformBlockBinding, VaryingBinding} from './program-bindings'
import Accessor from '../classes/accessor';
import {decomposeCompositeGLType} from '../webgl-utils/attribute-utils';
@@ -44,6 +44,7 @@ function readAttributeBindings(gl: WebGL2RenderingContext, program: WebGLProgram
inferProperties(location, name, accessor);
const attributeInfo = {location, name, accessor: new Accessor(accessor)}; // Base values
+ // @ts-expect-error
attributes.push(attributeInfo);
}
}
diff --git a/modules/webgl/src/index.ts b/modules/webgl/src/index.ts
index cbb83d202..fd7f9a4eb 100644
--- a/modules/webgl/src/index.ts
+++ b/modules/webgl/src/index.ts
@@ -5,7 +5,8 @@
// Higher level abstractions can be built on these classes
// Initialize any global state
-import './init';
+import '@luma.gl/api';
+import './init'
// export type {WebGLDeviceProps, WebGLDeviceInfo, WebGPUDeviceLimits} from './lib/webgl-device';
export type {WebGLDeviceProps} from './adapter/webgl-device';
@@ -116,7 +117,7 @@ export {getProgramBindings} from './helpers/get-program-bindings';
// DEPRECATED
// Deprecated re-exports
-export {lumaStats} from './init';
+export {lumaStats} from '@luma.gl/api';
export {log, assert, uid, isObjectEmpty} from '@luma.gl/api';
export {setPathPrefix, loadFile, loadImage} from '@luma.gl/api';
diff --git a/modules/webgl/src/init.ts b/modules/webgl/src/init.ts
index 762439eff..7f65a7678 100644
--- a/modules/webgl/src/init.ts
+++ b/modules/webgl/src/init.ts
@@ -1,48 +1,4 @@
-import {log} from '@luma.gl/api';
-import {isBrowser} from '@probe.gl/env';
-import {luma, lumaStats} from '@luma.gl/api';
+import {luma} from '@luma.gl/api';
import WebGLDevice from './adapter/webgl-device';
-// Version detection using babel plugin
-// @ts-expect-error
-const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'untranspiled source';
-
-const STARTUP_MESSAGE = 'set luma.log.level=1 (or higher) to trace rendering';
-// Assign luma.log.level in console to control logging: \
-// 0: none, 1: minimal, 2: verbose, 3: attribute/uniforms, 4: gl logs
-// luma.log.break[], set to gl funcs, luma.log.profile[] set to model names`;
-
-if (globalThis.luma && globalThis.luma.VERSION !== VERSION) {
- throw new Error(`luma.gl - multiple VERSIONs detected: ${globalThis.luma.VERSION} vs ${VERSION}`);
-}
-
-if (!globalThis.luma) {
- luma.registerDevices([WebGLDevice]);
-
- if (isBrowser()) {
- log.log(1, `luma.gl ${VERSION} - ${STARTUP_MESSAGE}`)();
- }
-
- globalThis.luma = globalThis.luma || {
- VERSION,
- version: VERSION,
- log,
-
- // A global stats object that various components can add information to
- // E.g. see webgl/resource.js
- stats: lumaStats,
-
- // Keep some luma globals in a sub-object
- // This allows us to dynamically detect if certain modules have been
- // included (such as IO and headless) and enable related functionality,
- // without unconditionally requiring and thus bundling big dependencies
- // into the app.
- globals: {
- modules: {},
- nodeIO: {}
- }
- };
-}
-
-export {lumaStats};
-export default globalThis.luma;
+luma.registerDevices([WebGLDevice]);
diff --git a/modules/webgpu/src/adapter/helpers/accessor-to-format.ts b/modules/webgpu/src/adapter/helpers/accessor-to-format.ts
new file mode 100644
index 000000000..edaf8ca20
--- /dev/null
+++ b/modules/webgpu/src/adapter/helpers/accessor-to-format.ts
@@ -0,0 +1,101 @@
+/*
+import {assert} from '@luma.gl/api';
+import GL from '@luma.gl/constants';
+
+type Accessor = Record;
+
+const FORMAT_TO_ACCESSOR: Record = {
+ uchar2: {type: 'uchar', size: 2},
+ uchar4: {type: 'uchar', size: 4},
+ char2: {type: 'char', size: 2},
+ char4: {type: 'char', size: 4},
+ uchar2norm: {type: 'uchar', size: 2, normalized: true},
+ uchar4norm: {type: 'uchar', size: 4, normalized: true},
+ char2norm: {type: 'char', size: 2, normalized: true},
+ char4norm: {type: 'char', size: 4, normalized: true},
+ ushort2: {type: 'ushort', size: 2},
+ ushort4: {type: 'ushort', size: 4},
+ short2: {type: 'short', size: 2},
+ short4: {type: 'short', size: 4},
+ ushort2norm: {type: 'ushort', size: 2, normalized: true},
+ ushort4norm: {type: 'ushort', size: 4, normalized: true},
+ short2norm: {type: 'short', size: 1, normalized: true},
+ short4norm: {type: 'short', size: 1, normalized: true},
+ half2: {type: 'half', size: 2},
+ half4: {type: 'half', size: 4},
+ float: {type: 'float', size: 1},
+ float2: {type: 'float', size: 2},
+ float3: {type: 'float', size: 3},
+ float4: {type: 'float', size: 4},
+ uint: {type: 'uint', size: 1, integer: true},
+ uint2: {type: 'uint', size: 2, integer: true},
+ uint3: {type: 'uint', size: 3, integer: true},
+ uint4: {type: 'uint', size: 4, integer: true},
+ int: {type: 'int', size: 1, integer: true},
+ int2: {type: 'int', size: 2, integer: true},
+ int3: {type: 'int', size: 3, integer: true},
+ int4: {type: 'int', size: 4, integer: true}
+};
+
+/**
+ * Convert from WebGPU attribute format strings to accessor {type, size, normalized, integer}
+ * @param {*} format
+ *
+export function mapWebGPUFormatToAccessor(format) {
+ const accessorDefinition = FORMAT_TO_ACCESSOR[format];
+ assert(accessorDefinition, 'invalid attribute format');
+ return Object.freeze(accessorDefinition);
+}
+
+/**
+ * Convert from accessor {type, size, normalized, integer} to WebGPU attribute format strings
+ * @param {*} format
+ *
+export function mapAccessorToWebGPUFormat(accessor) {
+ const {type = GL.FLOAT, size = 1, normalized = false, integer = false} = accessor;
+ assert(size >=1 && size <=4);
+ // `norm` suffix (uchar4norm)
+ const norm = normalized ? 'norm' : '';
+ // size 1 is ommitted in format names (float vs float2)
+ const count = size === 1 ? '' : size;
+ switch (type) {
+ case GL.UNSIGNED_BYTE:
+ switch (size) {
+ case 2:
+ case 4:
+ return `uchar${count}${norm}`;
+ }
+ case GL.BYTE:
+ switch (size) {
+ case 2:
+ case 4:
+ return `char${count}${norm}`;
+ }
+ case GL.UNSIGNED_SHORT:
+ switch (size) {
+ case 2:
+ case 4:
+ return `ushort${count}${norm}`;
+ }
+ case GL.SHORT:
+ switch (size) {
+ case 2:
+ case 4:
+ return `short${count}${norm}`;
+ }
+ case GL.HALF_FLOAT:
+ switch (size) {
+ case 2:
+ case 4:
+ return `half${count}`;
+ }
+ case GL.FLOAT:
+ return `float${count}`;
+ case GL.UNSIGNED_INT:
+ return `uint${count}`;
+ case GL.INT:
+ return `int${count}`;
+ }
+ throw new Error('illegal accessor');
+}
+*/
\ No newline at end of file
diff --git a/modules/webgpu/src/adapter/helpers/get-vertex-buffer-layout.ts b/modules/webgpu/src/adapter/helpers/get-vertex-buffer-layout.ts
new file mode 100644
index 000000000..06ccd7d0a
--- /dev/null
+++ b/modules/webgpu/src/adapter/helpers/get-vertex-buffer-layout.ts
@@ -0,0 +1,107 @@
+import {VertexFormat, AttributeBinding, assert} from '@luma.gl/api';
+
+export type Attribute = {
+ name: string;
+ location: number;
+ accessor: {
+ format: VertexFormat;
+ offset?: number;
+ stride?: number;
+ stepMode?: 'vertex' | 'instance';
+ divisor?: number;
+ }
+};
+
+// type InterleavedAttributes = {
+// byteStride?: number;
+// stepMode?:
+// attributes: Attribute[];
+// }
+
+export function convertAttributesVertexBufferToLayout(attributes: Attribute[]): GPUVertexBufferLayout[] {
+ const vertexBufferLayouts: GPUVertexBufferLayout[] = [];
+
+ for (const attribute of attributes) {
+ const arrayStride = attribute.accessor.stride || getVertexFormatBytes(attribute.accessor.format);
+ const stepMode = attribute.accessor.stepMode || (attribute.accessor.divisor ? 'instance' : 'vertex');
+ vertexBufferLayouts.push({
+ arrayStride,
+ stepMode,
+ attributes: [
+ {
+ format: attribute.accessor.format,
+ offset: attribute.accessor.offset || 0,
+ shaderLocation: attribute.location
+ }
+ ]
+ });
+ }
+
+ return vertexBufferLayouts;
+}
+
+function getVertexFormatBytes(format: GPUVertexFormat): number {
+ const [type, count] = format.split('x');
+ const bytes = getTypeBytes(type);
+ assert(bytes);
+ return bytes * parseInt(count);
+}
+
+const TYPE_SIZES = {
+ uint8: 1,
+ sint8: 1,
+ unorm8: 1,
+ snorm8: 1,
+ uint16: 2,
+ sint16: 2,
+ unorm16: 2,
+ snorm16: 2,
+ float16: 2,
+ float32: 4,
+ uint32: 4,
+ sint32: 4,
+};
+
+function getTypeBytes(type: string): number {
+ const bytes = TYPE_SIZES[type];
+ assert(bytes);
+ return bytes;
+}
+
+/**
+ * Attempt to convert legacy luma.gl accessors to attribute infos
+ * @param bufferAccessors
+ *
+ function getVertexBuffers(bufferAccessors: BufferAccessors[]) {
+ const vertexBuffers = [];
+
+ for (const buffer of bufferAccessors) {
+ let stride = null;
+ let divisor = null;
+ const attributes = [];
+
+ for (const accessor of buffer.attributes) {
+ if ('stride' in accessor) {
+ stride = accessor.stride;
+ }
+ if ('divisor' in accessor) {
+ divisor = accessor.divisor;
+ }
+
+ attributes.push({
+ format: accessor.format || mapAccessorToWebGPUFormat(accessor),
+ offset: accessor.offset || 0,
+ location: accessor.location
+ });
+ }
+
+ vertexBuffers.push({
+ stride: buffer.stride || stride || 0,
+ stepMode: buffer.stepMode || (divisor ? 'instance' : 'vertex'),
+ attributes
+ });
+ }
+
+ return vertexBuffers;
+}
+*/
\ No newline at end of file
diff --git a/modules/webgpu/src/adapter/helpers/make-bind-group-layout.ts b/modules/webgpu/src/adapter/helpers/make-bind-group-layout.ts
index 6cee4c414..a9c087743 100644
--- a/modules/webgpu/src/adapter/helpers/make-bind-group-layout.ts
+++ b/modules/webgpu/src/adapter/helpers/make-bind-group-layout.ts
@@ -1,8 +1,8 @@
//
import {Binding, Buffer, Sampler, Texture, cast} from '@luma.gl/api';
-import type WebGPUBuffer from '../webgpu-buffer';
-import type WebGPUSampler from '../webgpu-sampler';
-import type WebGPUTexture from '../webgpu-texture';
+import type WebGPUBuffer from '../resources/webgpu-buffer';
+import type WebGPUSampler from '../resources/webgpu-sampler';
+import type WebGPUTexture from '../resources/webgpu-texture';
/**
* Create a WebGPU bind group layout from an array of luma.gl bindings
diff --git a/modules/webgpu/src/adapter/helpers/make-bind-group.ts b/modules/webgpu/src/adapter/helpers/make-bind-group.ts
index 250bbe28e..a60cd200e 100644
--- a/modules/webgpu/src/adapter/helpers/make-bind-group.ts
+++ b/modules/webgpu/src/adapter/helpers/make-bind-group.ts
@@ -1,8 +1,8 @@
//
import {Binding, Buffer, Sampler, Texture, cast} from '@luma.gl/api';
-import type WebGPUBuffer from '../webgpu-buffer';
-import type WebGPUSampler from '../webgpu-sampler';
-import type WebGPUTexture from '../webgpu-texture';
+import type WebGPUBuffer from '../resources/webgpu-buffer';
+import type WebGPUSampler from '../resources/webgpu-sampler';
+import type WebGPUTexture from '../resources/webgpu-texture';
/**
* Create a WebGPU bind group from an array of luma.gl bindings
diff --git a/modules/webgpu/src/adapter/helpers/webgpu-parameters.ts b/modules/webgpu/src/adapter/helpers/webgpu-parameters.ts
index da790088e..5e559aab1 100644
--- a/modules/webgpu/src/adapter/helpers/webgpu-parameters.ts
+++ b/modules/webgpu/src/adapter/helpers/webgpu-parameters.ts
@@ -2,131 +2,178 @@ import {Parameters} from '@luma.gl/api';
type PipelineDescriptor = Omit;
+
+function addDepthStencil(descriptor: GPURenderPipelineDescriptor): void {
+ descriptor.depthStencil = descriptor.depthStencil || {
+ // required, set something
+ format: 'depth24plus',
+ stencilFront: {},
+ stencilBack: {},
+ };
+}
+
/**
* Supports for luma.gl's flat parameter space
* Populates the corresponding sub-objects in a PipelineDescriptor
*/
-export const PARAMETER_TABLE = {
+// @ts-expect-error
+export const PARAMETER_TABLE: Record = {
// RASTERIZATION PARAMETERS
- /*
cullMode: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.rasterizationState.cullMode = value;
+ descriptor.primitive.cullMode = value;
},
- cullFace: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.rasterizationState.frontFace = value;
- },
-
- depthBias: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.rasterizationState.depthBias = value;
- },
-
- depthBiasSlopeScale: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.rasterizationState.depthBiasSlopeScale = value;
- },
-
- depthBiasClamp: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.rasterizationState.depthBiasClamp = value;
- },
-
- // STENCIL
-
- stencilReadMask: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.depthStencilState.stencilReadMask = value;
- },
-
- stencilWriteMask: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.depthStencilState.stencilWriteMask = value;
- },
-
- stencilCompare: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.depthStencilState.stencilFront.compare = value;
- descriptor.depthStencilState.stencilBack.compare = value;
- },
-
- stencilPassOperation: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.depthStencilState.stencilFront.passOp = value;
- descriptor.depthStencilState.stencilBack.passOp = value;
- },
-
- stencilFailOperation: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.depthStencilState.stencilFront.failOp = value;
- descriptor.depthStencilState.stencilBack.failOp = value;
- },
-
- stencilDepthFailOperation: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.depthStencilState.stencilFront.depthFailOp = value;
- descriptor.depthStencilState.stencilBack.depthFailOp = value;
+
+ frontFace: (parameter, value, descriptor: PipelineDescriptor) => {
+ descriptor.primitive.frontFace = value;
},
// DEPTH
depthWriteEnabled: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.depthStencilState.depthWriteEnabled = value;
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.depthWriteEnabled = value;
},
depthCompare: (parameter, value, descriptor: PipelineDescriptor) => {
- descriptor.depthStencilState.depthCompare = value;
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.depthCompare = value;
},
+
+ depthFormat: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.format = value;
+ },
+
+ depthBias: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.depthBias = value;
+ },
+
+ depthBiasSlopeScale: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.depthBiasSlopeScale = value;
+ },
+
+ depthBiasClamp: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.depthBiasClamp = value;
+ },
+
+ // STENCIL
+
+ stencilReadMask: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.stencilReadMask = value;
+ },
+
+ stencilWriteMask: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.stencilWriteMask = value;
+ },
+
+ stencilCompare: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.stencilFront.compare = value;
+ descriptor.depthStencil.stencilBack.compare = value;
+ },
+
+ stencilPassOperation: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.stencilFront.passOp = value;
+ descriptor.depthStencil.stencilBack.passOp = value;
+ },
+
+ stencilFailOperation: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.stencilFront.failOp = value;
+ descriptor.depthStencil.stencilBack.failOp = value;
+ },
+
+ stencilDepthFailOperation: (parameter, value, descriptor: PipelineDescriptor) => {
+ addDepthStencil(descriptor);
+ descriptor.depthStencil.stencilFront.depthFailOp = value;
+ descriptor.depthStencil.stencilBack.depthFailOp = value;
+ },
+
+ // MULTISAMPLE
+
+ sampleCount: (parameter, value, descriptor: PipelineDescriptor) => {
+ descriptor.multisample = descriptor.multisample || {};
+ descriptor.multisample.count = value;
+ },
+
+ sampleMask: (parameter, value, descriptor: PipelineDescriptor) => {
+ descriptor.multisample = descriptor.multisample || {};
+ descriptor.multisample.mask = value;
+ },
+
+ sampleAlphaToCoverageEnabled: (parameter, value, descriptor: PipelineDescriptor) => {
+ descriptor.multisample = descriptor.multisample || {};
+ descriptor.multisample.alphaToCoverageEnabled = value;
+ },
+
+ // COLOR
colorMask: (parameter, value, descriptor: PipelineDescriptor) => {
addColorState(descriptor);
- descriptor.colorStates[0].writeMask = value;
+ descriptor.fragment.targets[0].writeMask = value;
},
blendColorOperation: (parameter, value, descriptor: PipelineDescriptor) => {
addColorState(descriptor);
- descriptor.colorStates[0].blend = descriptor.colorStates[0].blend || {};
- descriptor.colorStates[0].blend.color = descriptor.colorStates[0].blend.color || {};
- descriptor.colorStates[0].blend.color.operation = value;
+ descriptor.fragment.targets[0].blend = descriptor.fragment.targets[0].blend || {};
+ descriptor.fragment.targets[0].blend.color = descriptor.fragment.targets[0].blend.color || {};
+ descriptor.fragment.targets[0].blend.color.operation = value;
},
+ /*
blendColorSrcTarget: (parameter, value, descriptor: PipelineDescriptor) => {
addColorState(descriptor);
- descriptor.colorStates[0].blend = descriptor.colorStates[0].blend || {};
- descriptor.colorStates[0].blend.color = descriptor.colorStates[0].blend.color || {};
- descriptor.colorStates[0].blend.color.srcTarget = value;
+ descriptor.fragment.targets[0].blend = descriptor.fragment.targets[0].blend || {};
+ descriptor.fragment.targets[0].blend.color = descriptor.fragment.targets[0].blend.color || {};
+ descriptor.fragment.targets[0].blend.color.srcTarget = value;
},
blendColorDstTarget: (parameter, value, descriptor: PipelineDescriptor) => {
addColorState(descriptor);
- descriptor.colorStates[0].blend = descriptor.colorStates[0].blend || {};
- descriptor.colorStates[0].blend.color = descriptor.colorStates[0].blend.color || {};
- descriptor.colorStates[0].blend.color.dstTarget = value;
+ descriptor.fragment.targets[0].blend = descriptor.fragment.targets[0].blend || {};
+ descriptor.fragment.targets[0].blend.color = descriptor.fragment.targets[0].blend.color || {};
+ descriptor.fragment.targets[0].blend.color.dstTarget = value;
},
blendAlphaOperation: (parameter, value, descriptor: PipelineDescriptor) => {
addColorState(descriptor);
- descriptor.colorStates[0].blend = descriptor.colorStates[0].blend || {};
- descriptor.colorStates[0].blend.alpha = descriptor.colorStates[0].blend.alpha || {};
- descriptor.colorStates[0].blend.alpha.operation = value;
+ descriptor.fragment.targets[0].blend = descriptor.fragment.targets[0].blend || {};
+ descriptor.fragment.targets[0].blend.alpha = descriptor.fragment.targets[0].blend.alpha || {};
+ descriptor.fragment.targets[0].blend.alpha.operation = value;
},
blendAlphaSrcTarget: (parameter, value, descriptor: PipelineDescriptor) => {
addColorState(descriptor);
- descriptor.colorStates[0].blend = descriptor.colorStates[0].blend || {};
- descriptor.colorStates[0].blend.alpha = descriptor.colorStates[0].blend.alpha || {};
- descriptor.colorStates[0].blend.alpha.srcTarget = value;
+ descriptor.fragment.targets[0].blend = descriptor.fragment.targets[0].blend || {};
+ descriptor.fragment.targets[0].blend.alpha = descriptor.fragment.targets[0].blend.alpha || {};
+ descriptor.fragment.targets[0].blend.alpha.srcTarget = value;
},
blendAlphaDstTarget: (parameter, value, descriptor: PipelineDescriptor) => {
addColorState(descriptor);
- descriptor.colorStates[0].blend = descriptor.colorStates[0].blend || {};
- descriptor.colorStates[0].blend.alpha = descriptor.colorStates[0].blend.alpha || {};
- descriptor.colorStates[0].blend.alpha.dstTarget = value;
+ descriptor.fragment.targets[0].blend = descriptor.fragment.targets[0].blend || {};
+ descriptor.fragment.targets[0].blend.alpha = descriptor.fragment.targets[0].blend.alpha || {};
+ descriptor.fragment.targets[0].blend.alpha.dstTarget = value;
},
*/
};
const DEFAULT_PIPELINE_DESCRIPTOR: PipelineDescriptor = {
- depthStencil: {
- stencilFront: {},
- stencilBack: {},
- depthWriteEnabled: true,
- depthCompare: 'less',
- format: 'depth24plus-stencil8',
- },
+ // depthStencil: {
+ // stencilFront: {},
+ // stencilBack: {},
+ // // depthWriteEnabled: true,
+ // // depthCompare: 'less',
+ // // format: 'depth24plus-stencil8',
+ // },
primitive: {
cullMode: 'back',
@@ -142,35 +189,18 @@ const DEFAULT_PIPELINE_DESCRIPTOR: PipelineDescriptor = {
module: undefined,
entryPoint: 'main',
targets: [
+ // { format: props.color0Format || 'bgra8unorm' }
]
}
}
-export function getRenderPipelineDescriptor(parameters: Parameters = {}, props = {}): PipelineDescriptor {
- const pipelineDescriptor: PipelineDescriptor = {...DEFAULT_PIPELINE_DESCRIPTOR};
- // pipelineDescriptor.
- // rasterizationState: {
- // cullMode: 'back',
- // },
-
- // depthStencilState: {
- // depthWriteEnabled: true,
- // depthCompare: 'less',
- // format: props.depthStencilFormat || 'depth24plus-stencil8',
- // },
-
- // colorStates: [
- // {
- // format: props.color0Format || 'bgra8unorm',
- // }
- // ],
-
+export function applyParametersToRenderPipelineDescriptor(pipelineDescriptor, parameters: Parameters = {}): PipelineDescriptor {
+ pipelineDescriptor = {...DEFAULT_PIPELINE_DESCRIPTOR, ...pipelineDescriptor};
setParameters(pipelineDescriptor, parameters);
-
return pipelineDescriptor;
}
- // Apply any supplied parameters
+// Apply any supplied parameters
function setParameters(pipelineDescriptor: PipelineDescriptor, parameters: Parameters): void {
for (const key in parameters) {
const value = parameters[key];
@@ -182,11 +212,11 @@ function setParameters(pipelineDescriptor: PipelineDescriptor, parameters: Param
}
}
-// function addColorState(descriptor: PipelineDescriptor): void {
-// descriptor.colorStates = descriptor.colorStates || [];
-// // @ts-expect-error
-// if (descriptor.colorStates.length === 0) {
-// // @ts-expect-error
-// descriptor.colorStates.push({});
-// }
-// }
+function addColorState(descriptor: PipelineDescriptor): void {
+ descriptor.fragment.targets = descriptor.fragment.targets || [];
+ // @ts-expect-error
+ if (descriptor.fragment.targets.length === 0) {
+ // @ts-expect-error
+ descriptor.fragment.targets.push({});
+ }
+}
diff --git a/modules/webgpu/src/adapter/resources/webgpu-buffer.ts b/modules/webgpu/src/adapter/resources/webgpu-buffer.ts
new file mode 100644
index 000000000..a2cba4035
--- /dev/null
+++ b/modules/webgpu/src/adapter/resources/webgpu-buffer.ts
@@ -0,0 +1,77 @@
+// WEBGPU Buffer implementation
+import {Buffer, BufferProps, assert} from '@luma.gl/api';
+import type WebGPUDevice from '../webgpu-device';
+
+function getByteLength(props: BufferProps): number {
+ return props.byteLength >= 0 ? props.byteLength : props.data.byteLength;
+}
+
+export default class WebGPUBuffer extends Buffer {
+ readonly device: WebGPUDevice;
+ readonly handle: GPUBuffer;
+ readonly byteLength: number;
+
+ constructor(device: WebGPUDevice, props: BufferProps) {
+ super(device, props);
+ this.device = device;
+
+ this.byteLength = getByteLength(props);
+ const mapBuffer = Boolean(props.data);
+
+ this.handle = this.props.handle || this.createHandle(mapBuffer);
+ this.handle.label = this.props.id;
+
+ if (props.data) {
+ this._writeMapped(props.data);
+ // this.handle.writeAsync({data: props.data, map: false, unmap: false});
+ }
+
+ if (mapBuffer && !props.mappedAtCreation) {
+ this.handle.unmap();
+ }
+ }
+
+ protected createHandle(mapBuffer: boolean): GPUBuffer {
+ return this.device.handle.createBuffer({
+ size: this.byteLength,
+ // usage defaults to vertex
+ usage: this.props.usage || (GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST),
+ mappedAtCreation: this.props.mappedAtCreation || mapBuffer
+ });
+ }
+
+ destroy(): void {
+ this.handle.destroy();
+ }
+
+ // WebGPU provides multiple ways to write a buffer...
+ write(data: ArrayBufferView, byteOffset = 0) {
+ this.device.handle.queue.writeBuffer(
+ this.handle,
+ byteOffset,
+ data.buffer,
+ data.byteOffset,
+ data.byteLength
+ );
+ }
+
+ _writeMapped(typedArray: TypedArray): void {
+ const arrayBuffer = this.handle.getMappedRange();
+ // @ts-expect-error
+ new typedArray.constructor(arrayBuffer).set(typedArray);
+ }
+
+ // WEBGPU API
+
+ mapAsync(mode: number, offset: number = 0, size?: number): Promise {
+ return this.handle.mapAsync(mode, offset, size);
+ }
+
+ getMappedRange(offset: number = 0, size?: number): ArrayBuffer {
+ return this.handle.getMappedRange(offset, size);
+ }
+
+ unmap(): void {
+ this.handle.unmap();
+ }
+}
diff --git a/modules/webgpu/src/adapter/resources/webgpu-framebuffer.ts b/modules/webgpu/src/adapter/resources/webgpu-framebuffer.ts
new file mode 100644
index 000000000..65f2fcc25
--- /dev/null
+++ b/modules/webgpu/src/adapter/resources/webgpu-framebuffer.ts
@@ -0,0 +1,51 @@
+import WebGPUDevice from "../webgpu-device";
+
+export type FramebufferProps = { // ResourceProps & {
+ width: number;
+ height: number;
+ attachments?: Record;
+ readBuffer?: number;
+ drawBuffers?: number[];
+ check?: boolean;
+};
+
+export class WebGPUFramebuffer {
+ readonly device: WebGPUDevice;
+ renderPassDescriptor: GPURenderPassDescriptor;
+
+ constructor(device: WebGPUDevice, props: FramebufferProps) {
+ this.device = device;
+
+ const depthTexture = this.device.createTexture({
+ id: "depth-stencil",
+ width: props.width,
+ height: props.height,
+ depth: 1,
+ format: "depth24plus",
+ usage: GPUTextureUsage.RENDER_ATTACHMENT
+ });
+
+ const depthStencilAttachment = depthTexture.handle.createView();
+ depthStencilAttachment.label = 'depth-stencil-attachment';
+
+ const renderPassDescriptor: GPURenderPassDescriptor = {
+ colorAttachments: [{
+ // @ts-expect-error
+ attachment: undefined, // Assigned later
+ loadValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },
+ }],
+
+ depthStencil: {
+ attachment: depthStencilAttachment,
+ depthLoadValue: 1.0,
+ depthStoreOp: "store",
+ // stencilLoadValue: 0,
+ // stencilStoreOp: "store",
+ }
+ };
+
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/modules/webgpu/src/adapter/webgpu-render-pipeline.ts b/modules/webgpu/src/adapter/resources/webgpu-render-pipeline.ts
similarity index 82%
rename from modules/webgpu/src/adapter/webgpu-render-pipeline.ts
rename to modules/webgpu/src/adapter/resources/webgpu-render-pipeline.ts
index 10ef74770..7ad37e58c 100644
--- a/modules/webgpu/src/adapter/webgpu-render-pipeline.ts
+++ b/modules/webgpu/src/adapter/resources/webgpu-render-pipeline.ts
@@ -1,12 +1,13 @@
///
-import {RenderPipeline, RenderPipelineProps, cast } from '@luma.gl/api';
+import {RenderPipeline, RenderPipelineProps, cast, log} from '@luma.gl/api';
-import WebGPUDevice from './webgpu-device';
+import type WebGPUDevice from '../webgpu-device';
-import {getRenderPipelineDescriptor} from './helpers/webgpu-parameters';
+import {applyParametersToRenderPipelineDescriptor} from '../helpers/webgpu-parameters';
+import {convertAttributesVertexBufferToLayout} from '../helpers/get-vertex-buffer-layout';
// import {mapAccessorToWebGPUFormat} from './helpers/accessor-to-format';
-import {WebGPUShader} from '..';
+import WebGPUShader from './webgpu-shader';
// import type {BufferAccessors} from './webgpu-pipeline';
// BIND GROUP LAYOUTS
@@ -57,7 +58,8 @@ export default class WebGPURenderPipeline extends RenderPipeline {
const vertex: GPUVertexState = {
module: cast(this.props.vertexShader).handle,
- entryPoint: this.props.vertexShaderEntryPoint || 'main'
+ entryPoint: this.props.vertexShaderEntryPoint || 'main',
+ buffers: convertAttributesVertexBufferToLayout(this.props.attributeLayouts)
};
let fragment: GPUFragmentState | undefined;
@@ -73,27 +75,19 @@ export default class WebGPURenderPipeline extends RenderPipeline {
};
}
- const descriptor: GPURenderPipelineDescriptor = {
+ let descriptor: GPURenderPipelineDescriptor = {
vertex,
fragment,
primitive: {
topology: this.props.topology
}
-
- // WebGPU spec seems updated
- // primitive: {
- // topology: this.props.topology
- // },
-
};
- // const ceDescriptor: GPUCommandEncoderDescriptor;
- // const commandEncoder = this.device.handle.createCommandEncoder({
- // // label
- // // measureExecutionTime
- // });
+ descriptor = applyParametersToRenderPipelineDescriptor(descriptor, this.props.parameters);
- getRenderPipelineDescriptor(this.props.parameters, descriptor);
+ log.groupCollapsed(1, 'RenderPipeline.GPURenderPipelineDescriptor')();
+ log.log(1, JSON.stringify(descriptor, null, 2))();
+ log.groupEnd(1)();
const renderPipeline = this.device.handle.createRenderPipeline(descriptor);
return renderPipeline;
diff --git a/modules/webgpu/src/adapter/webgpu-sampler.ts b/modules/webgpu/src/adapter/resources/webgpu-sampler.ts
similarity index 91%
rename from modules/webgpu/src/adapter/webgpu-sampler.ts
rename to modules/webgpu/src/adapter/resources/webgpu-sampler.ts
index a397154a9..0da3f763b 100644
--- a/modules/webgpu/src/adapter/webgpu-sampler.ts
+++ b/modules/webgpu/src/adapter/resources/webgpu-sampler.ts
@@ -1,5 +1,5 @@
import {Sampler, SamplerProps} from '@luma.gl/api';
-import type WebGPUDevice from './webgpu-device';
+import type WebGPUDevice from '../webgpu-device';
export type WebGPUSamplerProps = SamplerProps & {
handle?: GPUSampler;
diff --git a/modules/webgpu/src/adapter/webgpu-shader.ts b/modules/webgpu/src/adapter/resources/webgpu-shader.ts
similarity index 96%
rename from modules/webgpu/src/adapter/webgpu-shader.ts
rename to modules/webgpu/src/adapter/resources/webgpu-shader.ts
index b2d7587f0..7b4f333b4 100644
--- a/modules/webgpu/src/adapter/webgpu-shader.ts
+++ b/modules/webgpu/src/adapter/resources/webgpu-shader.ts
@@ -1,5 +1,5 @@
import {Shader, ShaderProps, CompilerMessage} from '@luma.gl/api';
-import type WebGPUDevice from './webgpu-device';
+import type WebGPUDevice from '../webgpu-device';
export type WebGPUShaderProps = ShaderProps & {
handle?: GPUShaderModule;
@@ -14,7 +14,6 @@ export default class WebGPUShader extends Shader {
constructor(device: WebGPUDevice, props: WebGPUShaderProps) {
super(device, props);
-
this.device = device;
this.handle = this.props.handle || this.createHandle();
diff --git a/modules/webgpu/src/adapter/webgpu-texture.ts b/modules/webgpu/src/adapter/resources/webgpu-texture.ts
similarity index 89%
rename from modules/webgpu/src/adapter/webgpu-texture.ts
rename to modules/webgpu/src/adapter/resources/webgpu-texture.ts
index cc9848654..a65a0581e 100644
--- a/modules/webgpu/src/adapter/webgpu-texture.ts
+++ b/modules/webgpu/src/adapter/resources/webgpu-texture.ts
@@ -1,18 +1,10 @@
// Inspired by webgpu samples at https://github.com/austinEng/webgpu-samples/blob/master/src/glslang.ts
// under BSD 3-clause license
///
-import {Resource, Texture, TextureProps, Device, Sampler, SamplerProps} from '@luma.gl/api';
-import type WebGPUDevice from './webgpu-device';
+import {Texture, TextureProps, Sampler, SamplerProps, assert} from '@luma.gl/api';
+import type WebGPUDevice from '../webgpu-device';
import WebGPUSampler from './webgpu-sampler';
-// const DEFAULT_TEXTURE_PROPS: Required = {
-// handle: undefined,
-// id: undefined,
-// depth: 1,
-// format: 'rgba8unorm',
-// usage: GPUTextureUsage.COPY_DST
-// };
-
export default class WebGPUTexture extends Texture {
readonly device: WebGPUDevice;
readonly handle: GPUTexture;
@@ -28,26 +20,26 @@ export default class WebGPUTexture extends Texture {
// static createFromImage(img, usage = 0) {
// return new WebGPUTexture({width: img.width, height:img.height, usage}).setImage(image, usage);
// }
+
constructor(device: WebGPUDevice, props: TextureProps) {
super(device, props);
-
this.device = device;
this.handle = this.props.handle || this.createHandle();
-
this.sampler = null;
}
protected createHandle(): GPUTexture {
+ if (typeof this.props.format === 'number') {
+ throw new Error('number format');
+ }
return this.device.handle.createTexture({
size: {
width: this.props.width,
height: this.props.height,
depthOrArrayLayers: this.props.depth
},
- // @ts-expect-error
format: this.props.format,
- // @ts-expect-error
- usage: GPUTextureUsage.COPY_DST | this.props.usage
+ usage: this.props.usage
});
}
diff --git a/modules/webgpu/src/adapter/webgpu-buffer.ts b/modules/webgpu/src/adapter/webgpu-buffer.ts
deleted file mode 100644
index 575824f65..000000000
--- a/modules/webgpu/src/adapter/webgpu-buffer.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// WEBGPU Buffer implementation
-import {Buffer, BufferProps} from '@luma.gl/api';
-import WebGPUDevice from './webgpu-device';
-
-export default class WebGPUBuffer extends Buffer {
- readonly device: WebGPUDevice;
- readonly handle: GPUBuffer;
-
- constructor(device: WebGPUDevice, props: BufferProps) {
- super(device, props);
-
- this.handle = this.props.handle || this.createHandle();
- this.handle.label = this.props.id;
-
- if (props.data) {
- // this.handle.writeAsync({data: props.data, map: false, unmap: false});
- }
-
- if (!props.mappedAtCreation) {
- this.handle.unmap();
- }
- }
-
- protected createHandle(): GPUBuffer {
- return this.device.handle.createBuffer({
- size: this.props.byteLength,
- usage: this.props.usage || (GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC),
- mappedAtCreation: this.props.mappedAtCreation
- });
- }
-
- destroy(): void {
- this.handle.destroy();
- }
-
- mapAsync(mode: number, offset: number = 0, size?: number): Promise {
- return this.handle.mapAsync(mode, offset, size);
- }
-
- getMappedRange(offset: number = 0, size?: number): ArrayBuffer {
- return this.handle.getMappedRange(offset, size);
- }
-
- unmap(): void {
- this.handle.unmap();
- }
-}
diff --git a/modules/webgpu/src/adapter/webgpu-device.ts b/modules/webgpu/src/adapter/webgpu-device.ts
index 167e904a8..2c71cb70a 100644
--- a/modules/webgpu/src/adapter/webgpu-device.ts
+++ b/modules/webgpu/src/adapter/webgpu-device.ts
@@ -9,7 +9,8 @@ import {
TextureProps,
RenderPipelineProps,
// ComputePipelineProps,
- assert
+ assert,
+ log
} from '@luma.gl/api';
// import type {
// CopyBufferToBufferOptions,
@@ -17,11 +18,11 @@ import {
// CopyTextureToBufferOptions,
// CopyTextureToTextureOptions
// } from '@luma.gl/api';
-import WebGPUBuffer from './webgpu-buffer';
-import WebGPUTexture from './webgpu-texture';
-import WebGPUSampler from './webgpu-sampler';
-import WebGPUShader from './webgpu-shader';
-import WebGPURenderPipeline from './webgpu-render-pipeline';
+import WebGPUBuffer from './resources/webgpu-buffer';
+import WebGPUTexture from './resources/webgpu-texture';
+import WebGPUSampler from './resources/webgpu-sampler';
+import WebGPUShader from './resources/webgpu-shader';
+import WebGPURenderPipeline from './resources/webgpu-render-pipeline';
// import WebGPUComputePipeline from './webgpu-compute-pipeline';
// import {loadGlslangModule} from '../glsl/glslang';
@@ -41,6 +42,7 @@ export default class WebGPUDevice extends Device {
readonly presentationFormat: GPUTextureFormat;
presentationSize = [1, 1];
private _renderPassDescriptor: GPURenderPassDescriptor;
+ private _info: DeviceInfo;
static isSupported(): boolean {
return true;
@@ -48,13 +50,19 @@ export default class WebGPUDevice extends Device {
static async create(props) {
if (!navigator.gpu) {
- throw new Error('WebGPU not available');
+ throw new Error('WebGPU not available. Use Chrome Canary and turn on chrome://flags/#enable-unsafe-webgpu');
}
+ log.groupCollapsed(1, 'Creating device')();
const adapter = await navigator.gpu.requestAdapter({
powerPreference: "high-performance"
});
- const device = await adapter.requestDevice();
- return new WebGPUDevice(device, adapter, props);
+ log.log(1, "Adapter available")();
+ const gpuDevice = await adapter.requestDevice();
+ log.log(1, "GPUDevice available")();
+ const device = new WebGPUDevice(gpuDevice, adapter, props);
+ log.log(1, "Device created", device.info)();
+ log.groupEnd(1)();
+ return device;
}
constructor(device: GPUDevice, adapter: GPUAdapter, props: DeviceProps) {
@@ -62,6 +70,21 @@ export default class WebGPUDevice extends Device {
this.handle = device;
this.adapter = adapter;
+ this._info = {
+ type: 'webgpu',
+ vendor: this.adapter.name,
+ renderer: '',
+ version: '',
+ gpuVendor: 'UNKNOWN', // 'NVIDIA' | 'AMD' | 'INTEL' | 'APPLE' | 'UNKNOWN',
+ shadingLanguages: ['glsl', 'wgsl'],
+ shadingLanguageVersions: {
+ glsl: '450',
+ wgsl: '100'
+ },
+ vendorMasked: '',
+ rendererMasked: ''
+ };
+
// Configure swap chain
assert(props.canvas);
this.context = props.canvas.getContext('webgpu') as GPUCanvasContext;
@@ -78,7 +101,6 @@ export default class WebGPUDevice extends Device {
format: this.presentationFormat,
size: this.presentationSize,
});
- this._initializeRenderPassDescriptor(props.canvas)
}
destroy() {
@@ -86,20 +108,7 @@ export default class WebGPUDevice extends Device {
}
get info(): DeviceInfo {
- return {
- type: 'webgpu',
- vendor: '',
- renderer: '',
- version: '',
- gpuVendor: 'UNKNOWN', // 'NVIDIA' | 'AMD' | 'INTEL' | 'APPLE' | 'UNKNOWN',
- shadingLanguages: ['glsl', 'wgsl'],
- shadingLanguageVersions: {
- glsl: '450',
- wgsl: '100'
- },
- vendorMasked: '',
- rendererMasked: ''
- };
+ return this._info;
}
get features(): Set {
@@ -136,18 +145,7 @@ export default class WebGPUDevice extends Device {
beginRenderPass(): GPURenderPassEncoder {
if (!this.renderPass) {
this.commandEncoder = this.handle.createCommandEncoder();
-
- const textureView = this.context.getCurrentTexture().createView();
- const renderPassDescriptor: GPURenderPassDescriptor = {
- colorAttachments: [
- {
- view: textureView,
- loadValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
- storeOp: 'store',
- },
- ],
- };
-
+ const renderPassDescriptor = this._updateRenderPassDescriptor();
this.renderPass = this.commandEncoder.beginRenderPass(renderPassDescriptor);
}
return this.renderPass;
@@ -165,25 +163,27 @@ export default class WebGPUDevice extends Device {
}
// TODO: Possible to support multiple canvases with one device?
- _initializeRenderPassDescriptor(canvas) {
+ _initializeRenderPassDescriptor() {
const depthTexture = this.createTexture({
- width: canvas.width,
- height: canvas.height,
+ width: this.presentationSize[0],
+ height: this.presentationSize[1],
depth: 1,
- // @ts-expect-error
- format: "depth24plus-stencil8",
+ format: "depth24plus",
usage: GPUTextureUsage.RENDER_ATTACHMENT
});
- const renderPassDescriptor: GPURenderPassDescriptor = {
+ const depthStencilAttachment = depthTexture.handle.createView();
+ depthStencilAttachment.label = 'depth-stencil-attachment';
+
+ this._renderPassDescriptor = {
colorAttachments: [{
- // @ts-expect-error
- attachment: undefined, // Assigned later
+ view: undefined, // Assigned later
loadValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },
+ storeOp: 'store'
}],
- depthStencil: {
- attachment: depthTexture.handle.createView(),
+ depthStencilAttachment: {
+ view: depthStencilAttachment,
depthLoadValue: 1.0,
depthStoreOp: "store",
stencilLoadValue: 0,
@@ -191,7 +191,18 @@ export default class WebGPUDevice extends Device {
}
};
- this._renderPassDescriptor = renderPassDescriptor;
+ log.groupCollapsed(1, 'Device.GPURenderPassDescriptor')();
+ log.log(1, JSON.stringify(this._renderPassDescriptor, null, 2))();
+ log.groupEnd(1)();
+ }
+
+ _updateRenderPassDescriptor() {
+ if (!this._renderPassDescriptor) {
+ this._initializeRenderPassDescriptor();
+ }
+ const textureView = this.context.getCurrentTexture().createView();
+ this._renderPassDescriptor.colorAttachments[0].view = textureView;
+ return this._renderPassDescriptor;
}
}
diff --git a/modules/webgpu/src/engine/webgpu-model.ts b/modules/webgpu/src/engine/webgpu-model.ts
index d0868e442..aefc5902b 100644
--- a/modules/webgpu/src/engine/webgpu-model.ts
+++ b/modules/webgpu/src/engine/webgpu-model.ts
@@ -1,81 +1,112 @@
-
-import {Shader, RenderPipeline, PrimitiveTopology, assert} from '@luma.gl/api';
+import {
+ Buffer,
+ Shader,
+ RenderPipeline,
+ RenderPipelineParameters,
+ PrimitiveTopology,
+ assert,
+ AttributeBinding,
+ Binding
+} from '@luma.gl/api';
import {cast} from '@luma.gl/api';
-import {VertexShader} from '@luma.gl/webgl/';
import {WebGPUShader} from '..';
import WebGPUDevice from '../adapter/webgpu-device';
-import WebGPURenderPipeline from '../adapter/webgpu-render-pipeline';
-// import glslangModule from '../glslang';
+import WebGPUBuffer from '../adapter/resources/webgpu-buffer';
+import WebGPURenderPipeline from '../adapter/resources/webgpu-render-pipeline';
+import {makeBindGroup} from '../adapter/helpers/make-bind-group';
export type ModelProps = {
id?: string;
pipeline?: RenderPipeline;
- vertex?: string | Shader;
- fragment?: string | Shader;
- topology: PrimitiveTopology;
- vertexCount: number;
- instanceCount?: number;
-
- // backwards compatibility
vs?: string | Shader;
fs?: string | Shader;
-}
+ topology: PrimitiveTopology;
+ attributeLayouts?: AttributeBinding[];
+ vertexCount: number;
+ instanceCount?: number;
+ parameters?: RenderPipelineParameters;
+
+ attributeBuffers?: Buffer[]; // | Record
+ bindings?: Binding[];
+};
const DEFAULT_MODEL_PROPS: Required = {
id: 'unnamed',
- vertex: undefined,
- fragment: undefined,
- pipeline: undefined,
- topology: 'triangle-list',
- vertexCount: 0,
- instanceCount: 1,
vs: undefined,
fs: undefined,
+ pipeline: undefined,
+ topology: 'triangle-list',
+ attributeLayouts: [],
+ vertexCount: 0,
+ instanceCount: 1,
+ parameters: {},
+
+ attributeBuffers: [],
+ bindings: []
};
export default class Model {
device: WebGPUDevice;
pipeline: WebGPURenderPipeline;
- vertex: WebGPUShader;
- fragment: WebGPUShader | undefined;
+ vs: WebGPUShader;
+ fs: WebGPUShader | undefined;
props: Required;
+ _bindGroup: GPUBindGroup;
+
constructor(device: WebGPUDevice, props: ModelProps) {
this.props = {...DEFAULT_MODEL_PROPS, ...props};
props = this.props;
-
this.device = device;
+
+ // Create the pipeline
if (props.pipeline) {
this.pipeline = cast(props.pipeline);
} else {
- const vertex = props.vertex || props.vs;
- const fragment = props.fragment || props.fs;
+ const vertex = props.vs;
+ const fragment = props.fs;
assert(vertex);
- this.vertex = (typeof vertex === 'string')
- ? new WebGPUShader(device, {stage: 'vertex', source: vertex})
- : cast(vertex);
-
- if (fragment) {
- this.fragment = (typeof fragment === 'string')
- ? new WebGPUShader(device, {stage: 'fragment', source: fragment})
- : cast(fragment);
- }
+ this.vs =
+ typeof vertex === 'string'
+ ? new WebGPUShader(device, {stage: 'vertex', source: vertex})
+ : cast(vertex);
+ if (fragment) {
+ this.fs =
+ typeof fragment === 'string'
+ ? new WebGPUShader(device, {stage: 'fragment', source: fragment})
+ : cast(fragment);
+ }
+
this.pipeline = device.createRenderPipeline({
- vertexShader: this.vertex,
- fragmentShader: this.fragment,
- topology: props.topology
+ vertexShader: this.vs,
+ fragmentShader: this.fs,
+ topology: props.topology,
+ parameters: props.parameters,
// Geometry in the vertex shader!
+ attributeLayouts: props.attributeLayouts
});
}
+
+ // Set up the bindings
+ this._bindGroup = makeBindGroup(this.device.handle, this.pipeline._getBindGroupLayout(), this.props.bindings);
}
-
+
draw(renderPass?: GPURenderPassEncoder) {
renderPass = renderPass || this.device.getActiveRenderPass();
renderPass.setPipeline(this.pipeline.handle);
- // renderPass.setBindGroup(0, this.uniformBindGroup);
- // renderPass.setVertexBuffer(0, this.verticesBuffer);
+
+ // Set up attributes
+ for (let i = 0; i < this.props.attributeBuffers.length; ++i) {
+ const buffer = cast(this.props.attributeBuffers[i]);
+ const location = this.props.attributeLayouts[i].location;
+ renderPass.setVertexBuffer(location, buffer.handle);
+ }
+
+ // Set up bindings (uniform buffers, textures etc)
+ renderPass.setBindGroup(0, this._bindGroup);
+
renderPass.draw(this.props.vertexCount, this.props.instanceCount, 0, 0); // firstVertex, firstInstance);
}
@@ -225,11 +256,8 @@ export default class Model {
});
}
*/
-
}
-
-
/*
private:
void _initializeVertexState(utils::ComboVertexStateDescriptor* descriptor,
@@ -444,7 +472,7 @@ export default class Model {
this.userData = {};
this.needsRedraw = true;
- // Attributes and buffers
+ // Attributes and attributeBuffers
// Model manages auto Buffer creation from typed arrays
this._attributes = {}; // All attributes
this.attributes = {}; // User defined attributes
@@ -467,7 +495,7 @@ export default class Model {
this.drawMode = props.drawMode !== undefined ? props.drawMode : GL.TRIANGLES;
this.vertexCount = props.vertexCount || 0;
- // Track buffers created by setGeometry
+ // Track attributeBuffers created by setGeometry
this.geometryBuffers = {};
// geometry might have set drawMode and vertexCount
diff --git a/modules/webgpu/src/index.ts b/modules/webgpu/src/index.ts
index fe73a49de..6be31d0cc 100644
--- a/modules/webgpu/src/index.ts
+++ b/modules/webgpu/src/index.ts
@@ -1,12 +1,17 @@
+
+// Initialize any global state
+import '@luma.gl/api';
+import './init'
+
// WEBGPU ADAPTER
export {default as WebGPUDevice} from './adapter/webgpu-device';
-// WEBGPU CLASSES
-export {default as WebGPUBuffer} from './adapter/webgpu-buffer';
-export {default as WebGPUTexture} from './adapter/webgpu-texture';
-export {default as WebGPUSampler} from './adapter/webgpu-sampler';
-export {default as WebGPUShader} from './adapter/webgpu-shader';
+// WEBGPU CLASSES (typically not accessed directly)
+export {default as WebGPUBuffer} from './adapter/resources/webgpu-buffer';
+export {default as WebGPUTexture} from './adapter/resources/webgpu-texture';
+export {default as WebGPUSampler} from './adapter/resources/webgpu-sampler';
+export {default as WebGPUShader} from './adapter/resources/webgpu-shader';
-// WEBGPU ENGINE CLASSES
+// WEBGPU ENGINE CLASSES (until we can make the engine module truly platform independent)
export type {ModelProps} from './engine/webgpu-model';
export {default as Model} from './engine/webgpu-model';
diff --git a/modules/webgpu/src/init.ts b/modules/webgpu/src/init.ts
new file mode 100644
index 000000000..aa4d10c78
--- /dev/null
+++ b/modules/webgpu/src/init.ts
@@ -0,0 +1,4 @@
+import {luma} from '@luma.gl/api';
+import WebGPUDevice from './adapter/webgpu-device';
+
+luma.registerDevices([WebGPUDevice]);
diff --git a/scripts/bundle.config.js b/scripts/bundle.config.js
index d5fd0bdd6..f791c3e6d 100644
--- a/scripts/bundle.config.js
+++ b/scripts/bundle.config.js
@@ -102,7 +102,7 @@ const config = {
externals: getExternals(PACKAGE_INFO),
plugins: [
- // This is used to define the __VERSION__ constant in core/lib/init.js
+ // This is used to define the __VERSION__ constant in api/src/init.js
// babel-plugin-version-inline uses the package version from the working directory
// Therefore we need to manually import the correct version from the core
// This is called in prepublishOnly, after lerna bumps the package versions