@luma.gl/webgpu 9.3.0-alpha.4 → 9.3.0-alpha.8

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 (125) 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 +31 -30
  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.map +1 -1
  21. package/dist/adapter/resources/webgpu-buffer.js +19 -3
  22. package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
  23. package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
  24. package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
  25. package/dist/adapter/resources/webgpu-command-encoder.d.ts +7 -16
  26. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
  27. package/dist/adapter/resources/webgpu-command-encoder.js +89 -32
  28. package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
  29. package/dist/adapter/resources/webgpu-compute-pass.d.ts +3 -3
  30. package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
  31. package/dist/adapter/resources/webgpu-compute-pass.js +30 -12
  32. package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
  33. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +7 -9
  34. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
  35. package/dist/adapter/resources/webgpu-compute-pipeline.js +30 -17
  36. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
  37. package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -1
  38. package/dist/adapter/resources/webgpu-fence.js +9 -1
  39. package/dist/adapter/resources/webgpu-fence.js.map +1 -1
  40. package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
  41. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
  42. package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
  43. package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
  44. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
  45. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
  46. package/dist/adapter/resources/webgpu-pipeline-layout.js +10 -16
  47. package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
  48. package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
  49. package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
  50. package/dist/adapter/resources/webgpu-query-set.js +145 -4
  51. package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
  52. package/dist/adapter/resources/webgpu-render-pass.d.ts +6 -3
  53. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
  54. package/dist/adapter/resources/webgpu-render-pass.js +78 -34
  55. package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
  56. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +14 -10
  57. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
  58. package/dist/adapter/resources/webgpu-render-pipeline.js +56 -35
  59. package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
  60. package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
  61. package/dist/adapter/resources/webgpu-sampler.js +4 -0
  62. package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
  63. package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
  64. package/dist/adapter/resources/webgpu-shader.js +17 -1
  65. package/dist/adapter/resources/webgpu-shader.js.map +1 -1
  66. package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
  67. package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
  68. package/dist/adapter/resources/webgpu-texture-view.js +47 -11
  69. package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
  70. package/dist/adapter/resources/webgpu-texture.d.ts +18 -5
  71. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  72. package/dist/adapter/resources/webgpu-texture.js +148 -97
  73. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  74. package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
  75. package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
  76. package/dist/adapter/webgpu-canvas-context.d.ts +2 -0
  77. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  78. package/dist/adapter/webgpu-canvas-context.js +78 -19
  79. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  80. package/dist/adapter/webgpu-device.d.ts +10 -2
  81. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  82. package/dist/adapter/webgpu-device.js +159 -13
  83. package/dist/adapter/webgpu-device.js.map +1 -1
  84. package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
  85. package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
  86. package/dist/adapter/webgpu-presentation-context.js +144 -0
  87. package/dist/adapter/webgpu-presentation-context.js.map +1 -0
  88. package/dist/dist.dev.js +3180 -1849
  89. package/dist/dist.min.js +168 -9
  90. package/dist/index.cjs +1640 -405
  91. package/dist/index.cjs.map +4 -4
  92. package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -1
  93. package/dist/wgsl/get-shader-layout-wgsl.js +8 -0
  94. package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -1
  95. package/package.json +5 -5
  96. package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
  97. package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
  98. package/src/adapter/helpers/get-bind-group.ts +42 -49
  99. package/src/adapter/helpers/get-vertex-buffer-layout.ts +31 -12
  100. package/src/adapter/helpers/webgpu-parameters.ts +2 -0
  101. package/src/adapter/resources/webgpu-buffer.ts +18 -3
  102. package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
  103. package/src/adapter/resources/webgpu-command-encoder.ts +129 -50
  104. package/src/adapter/resources/webgpu-compute-pass.ts +48 -13
  105. package/src/adapter/resources/webgpu-compute-pipeline.ts +49 -18
  106. package/src/adapter/resources/webgpu-fence.ts +11 -3
  107. package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
  108. package/src/adapter/resources/webgpu-pipeline-layout.ts +16 -14
  109. package/src/adapter/resources/webgpu-query-set.ts +185 -9
  110. package/src/adapter/resources/webgpu-render-pass.ts +92 -40
  111. package/src/adapter/resources/webgpu-render-pipeline.ts +83 -44
  112. package/src/adapter/resources/webgpu-sampler.ts +5 -0
  113. package/src/adapter/resources/webgpu-shader.ts +16 -1
  114. package/src/adapter/resources/webgpu-texture-view.ts +51 -11
  115. package/src/adapter/resources/webgpu-texture.ts +198 -132
  116. package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
  117. package/src/adapter/webgpu-canvas-context.ts +91 -26
  118. package/src/adapter/webgpu-device.ts +212 -17
  119. package/src/adapter/webgpu-presentation-context.ts +180 -0
  120. package/src/wgsl/get-shader-layout-wgsl.ts +9 -0
  121. package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
  122. package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
  123. package/dist/adapter/helpers/accessor-to-format.js +0 -105
  124. package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
  125. 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';
@@ -39,10 +42,23 @@ export class WebGPUCanvasContext extends CanvasContext {
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
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 {
50
+ if (this.framebuffer) {
51
+ this.framebuffer.destroy();
52
+ this.framebuffer = null;
53
+ }
54
+ if (this.colorAttachment) {
55
+ this.colorAttachment.destroy();
56
+ this.colorAttachment = null;
57
+ }
58
+ if (this.depthStencilAttachment) {
59
+ this.depthStencilAttachment.destroy();
60
+ this.depthStencilAttachment = null;
61
+ }
46
62
  this.handle.unconfigure();
47
63
  super.destroy();
48
64
  }
@@ -65,6 +81,8 @@ export class WebGPUCanvasContext extends CanvasContext {
65
81
  colorSpace: this.props.colorSpace,
66
82
  alphaMode: this.props.alphaMode
67
83
  });
84
+
85
+ this._createDepthStencilAttachment(this.device.preferredDepthFormat);
68
86
  }
69
87
 
70
88
  /** Update framebuffer with properly resized "swap chain" texture views */
@@ -73,50 +91,97 @@ export class WebGPUCanvasContext extends CanvasContext {
73
91
  depthStencilFormat: 'depth24plus'
74
92
  }
75
93
  ): WebGPUFramebuffer {
76
- // Wrap the current canvas context texture in a luma.gl texture
77
- const currentColorAttachment = this._getCurrentTexture();
78
- // TODO - temporary debug code
79
- if (
80
- currentColorAttachment.width !== this.drawingBufferWidth ||
81
- currentColorAttachment.height !== this.drawingBufferHeight
82
- ) {
83
- const [oldWidth, oldHeight] = this.getDrawingBufferSize();
84
- this.drawingBufferWidth = currentColorAttachment.width;
85
- this.drawingBufferHeight = currentColorAttachment.height;
86
- log.log(
87
- 1,
88
- `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`
89
- )();
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;
90
100
  }
91
101
 
92
- // Resize the depth stencil attachment
93
- if (options?.depthStencilFormat) {
94
- this._createDepthStencilAttachment(options?.depthStencilFormat);
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
+ }
95
141
  }
96
-
97
- return new WebGPUFramebuffer(this.device, {
98
- colorAttachments: [currentColorAttachment],
99
- depthStencilAttachment: this.depthStencilAttachment
100
- });
101
142
  }
102
143
 
103
144
  // PRIMARY METHODS
104
145
 
105
146
  /** Wrap the current canvas context texture in a luma.gl texture */
106
147
  _getCurrentTexture(): WebGPUTexture {
148
+ const profiler = getCpuHotspotProfiler(this.device);
149
+ const currentTextureStartTime = profiler ? getTimestamp() : 0;
107
150
  const handle = this.handle.getCurrentTexture();
108
- return this.device.createTexture({
109
- 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, {
110
168
  handle,
111
169
  format: this.device.preferredColorFormat,
112
170
  width: handle.width,
113
171
  height: handle.height
114
172
  });
173
+ return this.colorAttachment;
115
174
  }
116
175
 
117
176
  /** We build render targets on demand (i.e. not when size changes but when about to render) */
118
177
  _createDepthStencilAttachment(depthStencilFormat: TextureFormatDepthStencil): WebGPUTexture {
119
- 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();
120
185
  this.depthStencilAttachment = this.device.createTexture({
121
186
  id: `${this.id}#depth-stencil-texture`,
122
187
  usage: Texture.RENDER_ATTACHMENT,
@@ -125,6 +190,6 @@ export class WebGPUCanvasContext extends CanvasContext {
125
190
  height: this.drawingBufferHeight
126
191
  });
127
192
  }
128
- return this.depthStencilAttachment;
193
+ return this.depthStencilAttachment!;
129
194
  }
130
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,6 +49,7 @@ 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';
@@ -48,6 +57,13 @@ import {WebGPUPipelineLayout} from './resources/webgpu-pipeline-layout';
48
57
  import {WebGPUFence} from './resources/webgpu-fence';
49
58
 
50
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';
51
67
 
52
68
  /** WebGPU Device implementation */
53
69
  export class WebGPUDevice extends Device {
@@ -75,6 +91,7 @@ export class WebGPUDevice extends Device {
75
91
  override canvasContext: WebGPUCanvasContext | null = null;
76
92
 
77
93
  private _isLost: boolean = false;
94
+ private _defaultSampler: WebGPUSampler | null = null;
78
95
  commandEncoder: WebGPUCommandEncoder;
79
96
 
80
97
  override get [Symbol.toStringTag](): string {
@@ -132,6 +149,9 @@ export class WebGPUDevice extends Device {
132
149
  // this.glslang = glsl && await loadGlslangModule();
133
150
 
134
151
  destroy(): void {
152
+ this.commandEncoder?.destroy();
153
+ this._defaultSampler?.destroy();
154
+ this._defaultSampler = null;
135
155
  this.handle.destroy();
136
156
  }
137
157
 
@@ -169,6 +189,13 @@ export class WebGPUDevice extends Device {
169
189
  return new WebGPUSampler(this, props);
170
190
  }
171
191
 
192
+ getDefaultSampler(): WebGPUSampler {
193
+ this._defaultSampler ||= new WebGPUSampler(this, {
194
+ id: `${this.id}-default-sampler`
195
+ });
196
+ return this._defaultSampler;
197
+ }
198
+
172
199
  createRenderPipeline(props: RenderPipelineProps): WebGPURenderPipeline {
173
200
  return new WebGPURenderPipeline(this, props);
174
201
  }
@@ -207,37 +234,172 @@ export class WebGPUDevice extends Device {
207
234
  return new WebGPUCanvasContext(this, this.adapter, props);
208
235
  }
209
236
 
237
+ createPresentationContext(props?: PresentationContextProps): PresentationContext {
238
+ return new WebGPUPresentationContext(this, props);
239
+ }
240
+
210
241
  createPipelineLayout(props: PipelineLayoutProps): WebGPUPipelineLayout {
211
242
  return new WebGPUPipelineLayout(this, props);
212
243
  }
213
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
+
214
274
  submit(commandBuffer?: WebGPUCommandBuffer): void {
275
+ let submittedCommandEncoder: WebGPUCommandEncoder | null = null;
215
276
  if (!commandBuffer) {
216
- commandBuffer = this.commandEncoder.finish();
217
- this.commandEncoder.destroy();
218
- this.commandEncoder = this.createCommandEncoder({id: `${this.id}-default-encoder`});
277
+ ({submittedCommandEncoder, commandBuffer} = this._finalizeDefaultCommandEncoderForSubmit());
219
278
  }
220
279
 
221
- this.pushErrorScope('validation');
222
- this.handle.queue.submit([commandBuffer.handle]);
223
- this.popErrorScope((error: GPUError) => {
224
- this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
225
- 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()
226
357
  });
358
+
359
+ return {submittedCommandEncoder, commandBuffer};
227
360
  }
228
361
 
229
362
  // WebGPU specific
230
363
 
231
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;
232
370
  this.handle.pushErrorScope(scope);
371
+ if (profiler) {
372
+ profiler.errorScopePushCount = (profiler.errorScopePushCount || 0) + 1;
373
+ profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
374
+ }
233
375
  }
234
376
 
235
377
  popErrorScope(handler: (error: GPUError) => void): void {
236
- this.handle.popErrorScope().then((error: GPUError | null) => {
237
- if (error) {
238
- handler(error);
239
- }
240
- });
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
+ }
241
403
  }
242
404
 
243
405
  // PRIVATE METHODS
@@ -249,11 +411,22 @@ export class WebGPUDevice extends Device {
249
411
  const vendor = this.adapterInfo.vendor || this.adapter.__brand || 'unknown';
250
412
  const renderer = driver || '';
251
413
  const version = driverVersion || '';
252
-
253
- 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',
254
425
  const gpuArchitecture = this.adapterInfo.architecture || 'unknown';
255
426
  const gpuBackend = (this.adapterInfo as any).backend || 'unknown';
256
- 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');
257
430
 
258
431
  return {
259
432
  type: 'webgpu',
@@ -264,11 +437,24 @@ export class WebGPUDevice extends Device {
264
437
  gpuType,
265
438
  gpuBackend,
266
439
  gpuArchitecture,
440
+ fallback,
267
441
  shadingLanguage: 'wgsl',
268
442
  shadingLanguageVersion: 100
269
443
  };
270
444
  }
271
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
+
272
458
  protected _getFeatures(): DeviceFeatures {
273
459
  // Initialize with actual WebGPU Features (note that unknown features may not be in DeviceFeature type)
274
460
  const features = new Set<DeviceFeature>(this.handle.features as Set<DeviceFeature>);
@@ -294,7 +480,6 @@ export class WebGPUDevice extends Device {
294
480
  }
295
481
 
296
482
  const WEBGPU_ALWAYS_FEATURES: DeviceFeature[] = [
297
- 'timer-query-webgl',
298
483
  'compilation-status-async-webgl',
299
484
  'float32-renderable-webgl',
300
485
  'float16-renderable-webgl',
@@ -320,3 +505,13 @@ export class WebGPUDevice extends Device {
320
505
  return capabilities;
321
506
  }
322
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
+ }