@luma.gl/webgpu 9.2.5 → 9.3.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts +54 -0
  2. package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts.map +1 -0
  3. package/dist/adapter/helpers/cpu-hotspot-profiler.js +26 -0
  4. package/dist/adapter/helpers/cpu-hotspot-profiler.js.map +1 -0
  5. package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts +7 -0
  6. package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts.map +1 -0
  7. package/dist/adapter/helpers/generate-mipmaps-webgpu.js +490 -0
  8. package/dist/adapter/helpers/generate-mipmaps-webgpu.js.map +1 -0
  9. package/dist/adapter/helpers/get-bind-group.d.ts +4 -6
  10. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
  11. package/dist/adapter/helpers/get-bind-group.js +39 -32
  12. package/dist/adapter/helpers/get-bind-group.js.map +1 -1
  13. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts +3 -1
  14. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts.map +1 -1
  15. package/dist/adapter/helpers/get-vertex-buffer-layout.js +17 -12
  16. package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +1 -1
  17. package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -1
  18. package/dist/adapter/helpers/webgpu-parameters.js +1 -0
  19. package/dist/adapter/helpers/webgpu-parameters.js.map +1 -1
  20. package/dist/adapter/resources/webgpu-buffer.d.ts +7 -0
  21. package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
  22. package/dist/adapter/resources/webgpu-buffer.js +58 -15
  23. package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
  24. package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
  25. package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
  26. package/dist/adapter/resources/webgpu-command-encoder.d.ts +7 -16
  27. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
  28. package/dist/adapter/resources/webgpu-command-encoder.js +89 -32
  29. package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
  30. package/dist/adapter/resources/webgpu-compute-pass.d.ts +3 -3
  31. package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
  32. package/dist/adapter/resources/webgpu-compute-pass.js +30 -12
  33. package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
  34. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +7 -9
  35. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
  36. package/dist/adapter/resources/webgpu-compute-pipeline.js +30 -17
  37. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
  38. package/dist/adapter/resources/webgpu-fence.d.ts +13 -0
  39. package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -0
  40. package/dist/adapter/resources/webgpu-fence.js +33 -0
  41. package/dist/adapter/resources/webgpu-fence.js.map +1 -0
  42. package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
  43. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
  44. package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
  45. package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
  46. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
  47. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
  48. package/dist/adapter/resources/webgpu-pipeline-layout.js +11 -18
  49. package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
  50. package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
  51. package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
  52. package/dist/adapter/resources/webgpu-query-set.js +145 -4
  53. package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
  54. package/dist/adapter/resources/webgpu-render-pass.d.ts +6 -3
  55. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
  56. package/dist/adapter/resources/webgpu-render-pass.js +78 -34
  57. package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
  58. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +14 -10
  59. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
  60. package/dist/adapter/resources/webgpu-render-pipeline.js +56 -35
  61. package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
  62. package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
  63. package/dist/adapter/resources/webgpu-sampler.js +4 -0
  64. package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
  65. package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
  66. package/dist/adapter/resources/webgpu-shader.js +17 -1
  67. package/dist/adapter/resources/webgpu-shader.js.map +1 -1
  68. package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
  69. package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
  70. package/dist/adapter/resources/webgpu-texture-view.js +47 -11
  71. package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
  72. package/dist/adapter/resources/webgpu-texture.d.ts +25 -3
  73. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  74. package/dist/adapter/resources/webgpu-texture.js +211 -43
  75. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  76. package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
  77. package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
  78. package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
  79. package/dist/adapter/webgpu-adapter.js +34 -34
  80. package/dist/adapter/webgpu-adapter.js.map +1 -1
  81. package/dist/adapter/webgpu-canvas-context.d.ts +6 -3
  82. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  83. package/dist/adapter/webgpu-canvas-context.js +90 -30
  84. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  85. package/dist/adapter/webgpu-device.d.ts +12 -2
  86. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  87. package/dist/adapter/webgpu-device.js +173 -16
  88. package/dist/adapter/webgpu-device.js.map +1 -1
  89. package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
  90. package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
  91. package/dist/adapter/webgpu-presentation-context.js +144 -0
  92. package/dist/adapter/webgpu-presentation-context.js.map +1 -0
  93. package/dist/dist.dev.js +8070 -547
  94. package/dist/dist.min.js +169 -6
  95. package/dist/index.cjs +1929 -410
  96. package/dist/index.cjs.map +4 -4
  97. package/dist/index.d.ts +2 -0
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +2 -0
  100. package/dist/index.js.map +1 -1
  101. package/dist/wgsl/get-shader-layout-wgsl.d.ts +8 -0
  102. package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -0
  103. package/dist/wgsl/get-shader-layout-wgsl.js +144 -0
  104. package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -0
  105. package/package.json +6 -5
  106. package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
  107. package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
  108. package/src/adapter/helpers/get-bind-group.ts +52 -46
  109. package/src/adapter/helpers/get-vertex-buffer-layout.ts +31 -12
  110. package/src/adapter/helpers/webgpu-parameters.ts +2 -0
  111. package/src/adapter/resources/webgpu-buffer.ts +61 -15
  112. package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
  113. package/src/adapter/resources/webgpu-command-encoder.ts +129 -50
  114. package/src/adapter/resources/webgpu-compute-pass.ts +48 -13
  115. package/src/adapter/resources/webgpu-compute-pipeline.ts +49 -18
  116. package/src/adapter/resources/webgpu-fence.ts +38 -0
  117. package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
  118. package/src/adapter/resources/webgpu-pipeline-layout.ts +18 -17
  119. package/src/adapter/resources/webgpu-query-set.ts +185 -9
  120. package/src/adapter/resources/webgpu-render-pass.ts +92 -40
  121. package/src/adapter/resources/webgpu-render-pipeline.ts +83 -44
  122. package/src/adapter/resources/webgpu-sampler.ts +5 -0
  123. package/src/adapter/resources/webgpu-shader.ts +16 -1
  124. package/src/adapter/resources/webgpu-texture-view.ts +51 -11
  125. package/src/adapter/resources/webgpu-texture.ts +281 -101
  126. package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
  127. package/src/adapter/webgpu-adapter.ts +40 -42
  128. package/src/adapter/webgpu-canvas-context.ts +107 -40
  129. package/src/adapter/webgpu-device.ts +231 -21
  130. package/src/adapter/webgpu-presentation-context.ts +180 -0
  131. package/src/index.ts +3 -0
  132. package/src/wgsl/get-shader-layout-wgsl.ts +165 -0
  133. package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
  134. package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
  135. package/dist/adapter/helpers/accessor-to-format.js +0 -105
  136. package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
  137. package/src/adapter/helpers/accessor-to-format.ts +0 -104
@@ -10,6 +10,7 @@ import {CanvasContext, Texture, log} from '@luma.gl/core';
10
10
  import {WebGPUDevice} from './webgpu-device';
11
11
  import {WebGPUFramebuffer} from './resources/webgpu-framebuffer';
12
12
  import {WebGPUTexture} from './resources/webgpu-texture';
13
+ import {getCpuHotspotProfiler, getTimestamp} from './helpers/cpu-hotspot-profiler';
13
14
 
14
15
  /**
15
16
  * Holds a WebGPU Canvas Context
@@ -20,7 +21,9 @@ export class WebGPUCanvasContext extends CanvasContext {
20
21
  readonly device: WebGPUDevice;
21
22
  readonly handle: GPUCanvasContext;
22
23
 
24
+ private colorAttachment: WebGPUTexture | null = null;
23
25
  private depthStencilAttachment: WebGPUTexture | null = null;
26
+ private framebuffer: WebGPUFramebuffer | null = null;
24
27
 
25
28
  get [Symbol.toStringTag](): string {
26
29
  return 'WebGPUCanvasContext';
@@ -38,58 +41,38 @@ export class WebGPUCanvasContext extends CanvasContext {
38
41
 
39
42
  // Base class constructor cannot access derived methods/fields, so we need to call these functions in the subclass constructor
40
43
  this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
41
- this._updateDevice();
44
+ this._configureDevice();
45
+ this._startObservers();
42
46
  }
43
47
 
44
48
  /** Destroy any textures produced while configured and remove the context configuration. */
45
49
  override destroy(): void {
46
- this.handle.unconfigure();
47
- super.destroy();
48
- }
49
-
50
- /** Update framebuffer with properly resized "swap chain" texture views */
51
- getCurrentFramebuffer(
52
- options: {depthStencilFormat?: TextureFormatDepthStencil | false} = {
53
- depthStencilFormat: 'depth24plus'
50
+ if (this.framebuffer) {
51
+ this.framebuffer.destroy();
52
+ this.framebuffer = null;
54
53
  }
55
- ): WebGPUFramebuffer {
56
- // Wrap the current canvas context texture in a luma.gl texture
57
- const currentColorAttachment = this.getCurrentTexture();
58
- // TODO - temporary debug code
59
- if (
60
- currentColorAttachment.width !== this.drawingBufferWidth ||
61
- currentColorAttachment.height !== this.drawingBufferHeight
62
- ) {
63
- const [oldWidth, oldHeight] = this.getDrawingBufferSize();
64
- this.drawingBufferWidth = currentColorAttachment.width;
65
- this.drawingBufferHeight = currentColorAttachment.height;
66
- log.log(
67
- 1,
68
- `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`
69
- )();
54
+ if (this.colorAttachment) {
55
+ this.colorAttachment.destroy();
56
+ this.colorAttachment = null;
70
57
  }
71
-
72
- // Resize the depth stencil attachment
73
- if (options?.depthStencilFormat) {
74
- this._createDepthStencilAttachment(options?.depthStencilFormat);
58
+ if (this.depthStencilAttachment) {
59
+ this.depthStencilAttachment.destroy();
60
+ this.depthStencilAttachment = null;
75
61
  }
76
-
77
- return new WebGPUFramebuffer(this.device, {
78
- colorAttachments: [currentColorAttachment],
79
- depthStencilAttachment: this.depthStencilAttachment
80
- });
62
+ this.handle.unconfigure();
63
+ super.destroy();
81
64
  }
82
65
 
83
66
  // IMPLEMENTATION OF ABSTRACT METHODS
84
67
 
85
- _updateDevice(): void {
68
+ /** @see https://www.w3.org/TR/webgpu/#canvas-configuration */
69
+ _configureDevice(): void {
86
70
  if (this.depthStencilAttachment) {
87
71
  this.depthStencilAttachment.destroy();
88
72
  this.depthStencilAttachment = null;
89
73
  }
90
74
 
91
75
  // Reconfigure the canvas size.
92
- // https://www.w3.org/TR/webgpu/#canvas-configuration
93
76
  this.handle.configure({
94
77
  device: this.device.handle,
95
78
  format: this.device.preferredColorFormat,
@@ -98,23 +81,107 @@ export class WebGPUCanvasContext extends CanvasContext {
98
81
  colorSpace: this.props.colorSpace,
99
82
  alphaMode: this.props.alphaMode
100
83
  });
84
+
85
+ this._createDepthStencilAttachment(this.device.preferredDepthFormat);
101
86
  }
102
87
 
88
+ /** Update framebuffer with properly resized "swap chain" texture views */
89
+ _getCurrentFramebuffer(
90
+ options: {depthStencilFormat?: TextureFormatDepthStencil | false} = {
91
+ depthStencilFormat: 'depth24plus'
92
+ }
93
+ ): WebGPUFramebuffer {
94
+ const profiler = getCpuHotspotProfiler(this.device);
95
+ const startTime = profiler ? getTimestamp() : 0;
96
+ if (profiler) {
97
+ profiler.framebufferAcquireCount = (profiler.framebufferAcquireCount || 0) + 1;
98
+ profiler.activeDefaultFramebufferAcquireDepth =
99
+ (profiler.activeDefaultFramebufferAcquireDepth || 0) + 1;
100
+ }
101
+
102
+ try {
103
+ // Wrap the current canvas context texture in a luma.gl texture
104
+ const currentColorAttachment = this._getCurrentTexture();
105
+ // TODO - temporary debug code
106
+ if (
107
+ currentColorAttachment.width !== this.drawingBufferWidth ||
108
+ currentColorAttachment.height !== this.drawingBufferHeight
109
+ ) {
110
+ const [oldWidth, oldHeight] = this.getDrawingBufferSize();
111
+ this.drawingBufferWidth = currentColorAttachment.width;
112
+ this.drawingBufferHeight = currentColorAttachment.height;
113
+ log.log(
114
+ 1,
115
+ `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`
116
+ )();
117
+ }
118
+
119
+ // Resize the depth stencil attachment
120
+ if (options?.depthStencilFormat) {
121
+ this._createDepthStencilAttachment(options?.depthStencilFormat);
122
+ }
123
+
124
+ this.framebuffer ||= new WebGPUFramebuffer(this.device, {
125
+ id: `${this.id}#framebuffer`,
126
+ colorAttachments: [currentColorAttachment],
127
+ depthStencilAttachment: null
128
+ });
129
+ this.framebuffer._reinitialize(
130
+ currentColorAttachment.view,
131
+ options?.depthStencilFormat ? this.depthStencilAttachment?.view || null : null
132
+ );
133
+ return this.framebuffer;
134
+ } finally {
135
+ if (profiler) {
136
+ profiler.activeDefaultFramebufferAcquireDepth =
137
+ (profiler.activeDefaultFramebufferAcquireDepth || 1) - 1;
138
+ profiler.framebufferAcquireTimeMs =
139
+ (profiler.framebufferAcquireTimeMs || 0) + (getTimestamp() - startTime);
140
+ }
141
+ }
142
+ }
143
+
144
+ // PRIMARY METHODS
145
+
103
146
  /** Wrap the current canvas context texture in a luma.gl texture */
104
- getCurrentTexture(): WebGPUTexture {
147
+ _getCurrentTexture(): WebGPUTexture {
148
+ const profiler = getCpuHotspotProfiler(this.device);
149
+ const currentTextureStartTime = profiler ? getTimestamp() : 0;
105
150
  const handle = this.handle.getCurrentTexture();
106
- return this.device.createTexture({
107
- id: `${this.id}#color-texture`,
151
+ if (profiler) {
152
+ profiler.currentTextureAcquireCount = (profiler.currentTextureAcquireCount || 0) + 1;
153
+ profiler.currentTextureAcquireTimeMs =
154
+ (profiler.currentTextureAcquireTimeMs || 0) + (getTimestamp() - currentTextureStartTime);
155
+ }
156
+ if (!this.colorAttachment) {
157
+ this.colorAttachment = this.device.createTexture({
158
+ id: `${this.id}#color-texture`,
159
+ handle,
160
+ format: this.device.preferredColorFormat,
161
+ width: handle.width,
162
+ height: handle.height
163
+ });
164
+ return this.colorAttachment;
165
+ }
166
+
167
+ this.colorAttachment._reinitialize(handle, {
108
168
  handle,
109
169
  format: this.device.preferredColorFormat,
110
170
  width: handle.width,
111
171
  height: handle.height
112
172
  });
173
+ return this.colorAttachment;
113
174
  }
114
175
 
115
176
  /** We build render targets on demand (i.e. not when size changes but when about to render) */
116
177
  _createDepthStencilAttachment(depthStencilFormat: TextureFormatDepthStencil): WebGPUTexture {
117
- if (!this.depthStencilAttachment) {
178
+ const needsNewDepthStencilAttachment =
179
+ !this.depthStencilAttachment ||
180
+ this.depthStencilAttachment.width !== this.drawingBufferWidth ||
181
+ this.depthStencilAttachment.height !== this.drawingBufferHeight ||
182
+ this.depthStencilAttachment.format !== depthStencilFormat;
183
+ if (needsNewDepthStencilAttachment) {
184
+ this.depthStencilAttachment?.destroy();
118
185
  this.depthStencilAttachment = this.device.createTexture({
119
186
  id: `${this.id}#depth-stencil-texture`,
120
187
  usage: Texture.RENDER_ATTACHMENT,
@@ -123,6 +190,6 @@ export class WebGPUCanvasContext extends CanvasContext {
123
190
  height: this.drawingBufferHeight
124
191
  });
125
192
  }
126
- return this.depthStencilAttachment;
193
+ return this.depthStencilAttachment!;
127
194
  }
128
195
  }
@@ -6,16 +6,22 @@
6
6
  // / <reference types="@webgpu/types" />
7
7
 
8
8
  import type {
9
+ Bindings,
10
+ ComputePipeline,
11
+ ComputeShaderLayout,
9
12
  DeviceInfo,
10
13
  DeviceLimits,
11
14
  DeviceFeature,
12
15
  DeviceTextureFormatCapabilities,
13
16
  VertexFormat,
14
17
  CanvasContextProps,
18
+ PresentationContextProps,
19
+ PresentationContext,
15
20
  BufferProps,
16
21
  SamplerProps,
17
22
  ShaderProps,
18
23
  TextureProps,
24
+ Texture,
19
25
  ExternalTextureProps,
20
26
  FramebufferProps,
21
27
  RenderPipelineProps,
@@ -28,6 +34,8 @@ import type {
28
34
  DeviceProps,
29
35
  CommandEncoderProps,
30
36
  PipelineLayoutProps,
37
+ RenderPipeline,
38
+ ShaderLayout
31
39
  } from '@luma.gl/core';
32
40
  import {Device, DeviceFeatures} from '@luma.gl/core';
33
41
  import {WebGPUBuffer} from './resources/webgpu-buffer';
@@ -41,10 +49,21 @@ import {WebGPUComputePipeline} from './resources/webgpu-compute-pipeline';
41
49
  import {WebGPUVertexArray} from './resources/webgpu-vertex-array';
42
50
 
43
51
  import {WebGPUCanvasContext} from './webgpu-canvas-context';
52
+ import {WebGPUPresentationContext} from './webgpu-presentation-context';
44
53
  import {WebGPUCommandEncoder} from './resources/webgpu-command-encoder';
45
54
  import {WebGPUCommandBuffer} from './resources/webgpu-command-buffer';
46
55
  import {WebGPUQuerySet} from './resources/webgpu-query-set';
47
56
  import {WebGPUPipelineLayout} from './resources/webgpu-pipeline-layout';
57
+ import {WebGPUFence} from './resources/webgpu-fence';
58
+
59
+ import {getShaderLayoutFromWGSL} from '../wgsl/get-shader-layout-wgsl';
60
+ import {generateMipmapsWebGPU} from './helpers/generate-mipmaps-webgpu';
61
+ import {getBindGroup} from './helpers/get-bind-group';
62
+ import {
63
+ getCpuHotspotProfiler as getWebGPUCpuHotspotProfiler,
64
+ getCpuHotspotSubmitReason as getWebGPUCpuHotspotSubmitReason,
65
+ getTimestamp
66
+ } from './helpers/cpu-hotspot-profiler';
48
67
 
49
68
  /** WebGPU Device implementation */
50
69
  export class WebGPUDevice extends Device {
@@ -72,6 +91,7 @@ export class WebGPUDevice extends Device {
72
91
  override canvasContext: WebGPUCanvasContext | null = null;
73
92
 
74
93
  private _isLost: boolean = false;
94
+ private _defaultSampler: WebGPUSampler | null = null;
75
95
  commandEncoder: WebGPUCommandEncoder;
76
96
 
77
97
  override get [Symbol.toStringTag](): string {
@@ -129,6 +149,9 @@ export class WebGPUDevice extends Device {
129
149
  // this.glslang = glsl && await loadGlslangModule();
130
150
 
131
151
  destroy(): void {
152
+ this.commandEncoder?.destroy();
153
+ this._defaultSampler?.destroy();
154
+ this._defaultSampler = null;
132
155
  this.handle.destroy();
133
156
  }
134
157
 
@@ -136,15 +159,15 @@ export class WebGPUDevice extends Device {
136
159
  return this._isLost;
137
160
  }
138
161
 
162
+ getShaderLayout(source: string) {
163
+ return getShaderLayoutFromWGSL(source);
164
+ }
165
+
139
166
  override isVertexFormatSupported(format: VertexFormat): boolean {
140
167
  const info = this.getVertexFormatInfo(format);
141
168
  return !info.webglOnly;
142
169
  }
143
170
 
144
- getTextureByteAlignment(): number {
145
- return 1;
146
- }
147
-
148
171
  createBuffer(props: BufferProps | ArrayBuffer | ArrayBufferView): WebGPUBuffer {
149
172
  const newProps = this._normalizeBufferProps(props);
150
173
  return new WebGPUBuffer(this, newProps);
@@ -166,6 +189,13 @@ export class WebGPUDevice extends Device {
166
189
  return new WebGPUSampler(this, props);
167
190
  }
168
191
 
192
+ getDefaultSampler(): WebGPUSampler {
193
+ this._defaultSampler ||= new WebGPUSampler(this, {
194
+ id: `${this.id}-default-sampler`
195
+ });
196
+ return this._defaultSampler;
197
+ }
198
+
169
199
  createRenderPipeline(props: RenderPipelineProps): WebGPURenderPipeline {
170
200
  return new WebGPURenderPipeline(this, props);
171
201
  }
@@ -196,41 +226,180 @@ export class WebGPUDevice extends Device {
196
226
  return new WebGPUQuerySet(this, props);
197
227
  }
198
228
 
229
+ override createFence(): WebGPUFence {
230
+ return new WebGPUFence(this);
231
+ }
232
+
199
233
  createCanvasContext(props: CanvasContextProps): WebGPUCanvasContext {
200
234
  return new WebGPUCanvasContext(this, this.adapter, props);
201
235
  }
202
236
 
237
+ createPresentationContext(props?: PresentationContextProps): PresentationContext {
238
+ return new WebGPUPresentationContext(this, props);
239
+ }
240
+
203
241
  createPipelineLayout(props: PipelineLayoutProps): WebGPUPipelineLayout {
204
242
  return new WebGPUPipelineLayout(this, props);
205
243
  }
206
244
 
245
+ override generateMipmapsWebGPU(texture: Texture): void {
246
+ generateMipmapsWebGPU(this, texture);
247
+ }
248
+
249
+ override _createBindGroupLayoutWebGPU(
250
+ pipeline: RenderPipeline | ComputePipeline,
251
+ group: number
252
+ ): GPUBindGroupLayout {
253
+ return (pipeline as WebGPURenderPipeline | WebGPUComputePipeline).handle.getBindGroupLayout(
254
+ group
255
+ );
256
+ }
257
+
258
+ override _createBindGroupWebGPU(
259
+ bindGroupLayout: unknown,
260
+ shaderLayout: ShaderLayout | ComputeShaderLayout,
261
+ bindings: Bindings,
262
+ group: number
263
+ ): GPUBindGroup | null {
264
+ if (Object.keys(bindings).length === 0) {
265
+ return this.handle.createBindGroup({
266
+ layout: bindGroupLayout as GPUBindGroupLayout,
267
+ entries: []
268
+ });
269
+ }
270
+
271
+ return getBindGroup(this, bindGroupLayout as GPUBindGroupLayout, shaderLayout, bindings, group);
272
+ }
273
+
207
274
  submit(commandBuffer?: WebGPUCommandBuffer): void {
275
+ let submittedCommandEncoder: WebGPUCommandEncoder | null = null;
208
276
  if (!commandBuffer) {
209
- commandBuffer = this.commandEncoder.finish();
210
- this.commandEncoder.destroy();
211
- this.commandEncoder = this.createCommandEncoder({id: `${this.id}-default-encoder`});
277
+ ({submittedCommandEncoder, commandBuffer} = this._finalizeDefaultCommandEncoderForSubmit());
212
278
  }
213
279
 
214
- this.pushErrorScope('validation');
215
- this.handle.queue.submit([commandBuffer.handle]);
216
- this.popErrorScope((error: GPUError) => {
217
- this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
218
- this.debug();
280
+ const profiler = getWebGPUCpuHotspotProfiler(this);
281
+ const startTime = profiler ? getTimestamp() : 0;
282
+ const submitReason = getWebGPUCpuHotspotSubmitReason(this);
283
+ try {
284
+ this.pushErrorScope('validation');
285
+ const queueSubmitStartTime = profiler ? getTimestamp() : 0;
286
+ this.handle.queue.submit([commandBuffer.handle]);
287
+ if (profiler) {
288
+ profiler.queueSubmitCount = (profiler.queueSubmitCount || 0) + 1;
289
+ profiler.queueSubmitTimeMs =
290
+ (profiler.queueSubmitTimeMs || 0) + (getTimestamp() - queueSubmitStartTime);
291
+ }
292
+ this.popErrorScope((error: GPUError) => {
293
+ this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
294
+ this.debug();
295
+ });
296
+
297
+ if (submittedCommandEncoder) {
298
+ const submitResolveKickoffStartTime = profiler ? getTimestamp() : 0;
299
+ scheduleMicrotask(() => {
300
+ submittedCommandEncoder
301
+ .resolveTimeProfilingQuerySet()
302
+ .then(() => {
303
+ this.commandEncoder._gpuTimeMs = submittedCommandEncoder._gpuTimeMs;
304
+ })
305
+ .catch(() => {});
306
+ });
307
+ if (profiler) {
308
+ profiler.submitResolveKickoffCount = (profiler.submitResolveKickoffCount || 0) + 1;
309
+ profiler.submitResolveKickoffTimeMs =
310
+ (profiler.submitResolveKickoffTimeMs || 0) +
311
+ (getTimestamp() - submitResolveKickoffStartTime);
312
+ }
313
+ }
314
+ } finally {
315
+ if (profiler) {
316
+ profiler.submitCount = (profiler.submitCount || 0) + 1;
317
+ profiler.submitTimeMs = (profiler.submitTimeMs || 0) + (getTimestamp() - startTime);
318
+ const reasonCountKey =
319
+ submitReason === 'query-readback' ? 'queryReadbackSubmitCount' : 'defaultSubmitCount';
320
+ const reasonTimeKey =
321
+ submitReason === 'query-readback' ? 'queryReadbackSubmitTimeMs' : 'defaultSubmitTimeMs';
322
+ profiler[reasonCountKey] = (profiler[reasonCountKey] || 0) + 1;
323
+ profiler[reasonTimeKey] = (profiler[reasonTimeKey] || 0) + (getTimestamp() - startTime);
324
+ }
325
+ const commandBufferDestroyStartTime = profiler ? getTimestamp() : 0;
326
+ commandBuffer.destroy();
327
+ if (profiler) {
328
+ profiler.commandBufferDestroyCount = (profiler.commandBufferDestroyCount || 0) + 1;
329
+ profiler.commandBufferDestroyTimeMs =
330
+ (profiler.commandBufferDestroyTimeMs || 0) +
331
+ (getTimestamp() - commandBufferDestroyStartTime);
332
+ }
333
+ }
334
+ }
335
+
336
+ private _finalizeDefaultCommandEncoderForSubmit(): {
337
+ submittedCommandEncoder: WebGPUCommandEncoder;
338
+ commandBuffer: WebGPUCommandBuffer;
339
+ } {
340
+ const submittedCommandEncoder = this.commandEncoder;
341
+ if (
342
+ submittedCommandEncoder.getTimeProfilingSlotCount() > 0 &&
343
+ submittedCommandEncoder.getTimeProfilingQuerySet() instanceof WebGPUQuerySet
344
+ ) {
345
+ const querySet = submittedCommandEncoder.getTimeProfilingQuerySet() as WebGPUQuerySet;
346
+ querySet._encodeResolveToReadBuffer(submittedCommandEncoder, {
347
+ firstQuery: 0,
348
+ queryCount: submittedCommandEncoder.getTimeProfilingSlotCount()
349
+ });
350
+ }
351
+
352
+ const commandBuffer = submittedCommandEncoder.finish();
353
+ this.commandEncoder.destroy();
354
+ this.commandEncoder = this.createCommandEncoder({
355
+ id: submittedCommandEncoder.props.id,
356
+ timeProfilingQuerySet: submittedCommandEncoder.getTimeProfilingQuerySet()
219
357
  });
358
+
359
+ return {submittedCommandEncoder, commandBuffer};
220
360
  }
221
361
 
222
362
  // WebGPU specific
223
363
 
224
364
  pushErrorScope(scope: 'validation' | 'out-of-memory'): void {
365
+ if (!this.props.debug) {
366
+ return;
367
+ }
368
+ const profiler = getWebGPUCpuHotspotProfiler(this);
369
+ const startTime = profiler ? getTimestamp() : 0;
225
370
  this.handle.pushErrorScope(scope);
371
+ if (profiler) {
372
+ profiler.errorScopePushCount = (profiler.errorScopePushCount || 0) + 1;
373
+ profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
374
+ }
226
375
  }
227
376
 
228
377
  popErrorScope(handler: (error: GPUError) => void): void {
229
- this.handle.popErrorScope().then((error: GPUError | null) => {
230
- if (error) {
231
- handler(error);
232
- }
233
- });
378
+ if (!this.props.debug) {
379
+ return;
380
+ }
381
+ const profiler = getWebGPUCpuHotspotProfiler(this);
382
+ const startTime = profiler ? getTimestamp() : 0;
383
+ this.handle
384
+ .popErrorScope()
385
+ .then((error: GPUError | null) => {
386
+ if (error) {
387
+ handler(error);
388
+ }
389
+ })
390
+ .catch((error: unknown) => {
391
+ if (this.shouldIgnoreDroppedInstanceError(error, 'popErrorScope')) {
392
+ return;
393
+ }
394
+
395
+ const errorMessage = error instanceof Error ? error.message : String(error);
396
+ this.reportError(new Error(`${this} popErrorScope failed: ${errorMessage}`), this)();
397
+ this.debug();
398
+ });
399
+ if (profiler) {
400
+ profiler.errorScopePopCount = (profiler.errorScopePopCount || 0) + 1;
401
+ profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
402
+ }
234
403
  }
235
404
 
236
405
  // PRIVATE METHODS
@@ -242,11 +411,22 @@ export class WebGPUDevice extends Device {
242
411
  const vendor = this.adapterInfo.vendor || this.adapter.__brand || 'unknown';
243
412
  const renderer = driver || '';
244
413
  const version = driverVersion || '';
245
-
246
- const gpu = vendor === 'apple' ? 'apple' : 'unknown'; // 'nvidia' | 'amd' | 'intel' | 'apple' | 'unknown',
414
+ const fallback = Boolean(
415
+ (this.adapterInfo as any).isFallbackAdapter ??
416
+ (this.adapter as any).isFallbackAdapter ??
417
+ false
418
+ );
419
+ const softwareRenderer = /SwiftShader/i.test(
420
+ `${vendor} ${renderer} ${this.adapterInfo.architecture || ''}`
421
+ );
422
+
423
+ const gpu =
424
+ vendor === 'apple' ? 'apple' : softwareRenderer || fallback ? 'software' : 'unknown'; // 'nvidia' | 'amd' | 'intel' | 'apple' | 'unknown',
247
425
  const gpuArchitecture = this.adapterInfo.architecture || 'unknown';
248
426
  const gpuBackend = (this.adapterInfo as any).backend || 'unknown';
249
- const gpuType = ((this.adapterInfo as any).type || '').split(' ')[0].toLowerCase() || 'unknown';
427
+ const gpuType =
428
+ ((this.adapterInfo as any).type || '').split(' ')[0].toLowerCase() ||
429
+ (softwareRenderer || fallback ? 'cpu' : 'unknown');
250
430
 
251
431
  return {
252
432
  type: 'webgpu',
@@ -257,11 +437,24 @@ export class WebGPUDevice extends Device {
257
437
  gpuType,
258
438
  gpuBackend,
259
439
  gpuArchitecture,
440
+ fallback,
260
441
  shadingLanguage: 'wgsl',
261
442
  shadingLanguageVersion: 100
262
443
  };
263
444
  }
264
445
 
446
+ shouldIgnoreDroppedInstanceError(error: unknown, operation?: string): boolean {
447
+ const errorMessage = error instanceof Error ? error.message : String(error);
448
+ return (
449
+ errorMessage.includes('Instance dropped') &&
450
+ (!operation || errorMessage.includes(operation)) &&
451
+ (this._isLost ||
452
+ this.info.gpu === 'software' ||
453
+ this.info.gpuType === 'cpu' ||
454
+ Boolean(this.info.fallback))
455
+ );
456
+ }
457
+
265
458
  protected _getFeatures(): DeviceFeatures {
266
459
  // Initialize with actual WebGPU Features (note that unknown features may not be in DeviceFeature type)
267
460
  const features = new Set<DeviceFeature>(this.handle.features as Set<DeviceFeature>);
@@ -278,8 +471,15 @@ export class WebGPUDevice extends Device {
278
471
  features.add('texture-compression-bc5-webgl');
279
472
  }
280
473
 
474
+ if (this.handle.features.has('chromium-experimental-norm16-texture-formats')) {
475
+ features.add('norm16-renderable-webgl');
476
+ }
477
+
478
+ if (this.handle.features.has('chromium-experimental-snorm16-texture-formats')) {
479
+ features.add('snorm16-renderable-webgl');
480
+ }
481
+
281
482
  const WEBGPU_ALWAYS_FEATURES: DeviceFeature[] = [
282
- 'timer-query-webgl',
283
483
  'compilation-status-async-webgl',
284
484
  'float32-renderable-webgl',
285
485
  'float16-renderable-webgl',
@@ -305,3 +505,13 @@ export class WebGPUDevice extends Device {
305
505
  return capabilities;
306
506
  }
307
507
  }
508
+
509
+ function scheduleMicrotask(callback: () => void): void {
510
+ if (globalThis.queueMicrotask) {
511
+ globalThis.queueMicrotask(callback);
512
+ return;
513
+ }
514
+ Promise.resolve()
515
+ .then(callback)
516
+ .catch(() => {});
517
+ }