• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visgl / luma.gl / 23981506010

04 Apr 2026 03:07PM UTC coverage: 74.219% (+0.03%) from 74.193%
23981506010

push

github

web-flow
fix: predraw() buffer overwrites (#2590)

5169 of 7877 branches covered (65.62%)

Branch coverage included in aggregate %.

45 of 59 new or added lines in 12 files covered. (76.27%)

1 existing line in 1 file now uncovered.

11692 of 14841 relevant lines covered (78.78%)

750.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

44.74
/modules/webgpu/src/adapter/resources/webgpu-command-encoder.ts
1
// luma.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import type {
6
  CommandBufferProps,
7
  RenderPassProps,
8
  ComputePassProps,
9
  CopyBufferToTextureOptions,
10
  CopyTextureToTextureOptions,
11
  CopyTextureToBufferOptions
12
} from '@luma.gl/core';
13
import {CommandEncoder, CommandEncoderProps, Buffer} from '@luma.gl/core';
14
import {WebGPUDevice} from '../webgpu-device';
15
import {WebGPUCommandBuffer} from './webgpu-command-buffer';
16
import {WebGPUBuffer} from './webgpu-buffer';
17
import {WebGPURenderPass} from './webgpu-render-pass';
18
import {WebGPUComputePass} from './webgpu-compute-pass';
19
import {WebGPUTexture} from './webgpu-texture';
20
import {WebGPUQuerySet} from './webgpu-query-set';
21

22
export class WebGPUCommandEncoder extends CommandEncoder {
23
  readonly device: WebGPUDevice;
24
  readonly handle: GPUCommandEncoder;
25
  private _transientUploadBuffers: WebGPUBuffer[] = [];
115✔
26

27
  constructor(device: WebGPUDevice, props: CommandEncoderProps = {}) {
115✔
28
    super(device, props);
115✔
29
    this.device = device;
115✔
30
    this.handle =
115✔
31
      props.handle ||
230✔
32
      this.device.handle.createCommandEncoder({
33
        label: this.props.id
34
        // TODO was this removed in standard?
35
        // measureExecutionTime: this.props.measureExecutionTime
36
      });
37
    this.handle.label = this.props.id;
115✔
38
  }
39

40
  override destroy(): void {
41
    for (const uploadBuffer of this._transientUploadBuffers) {
166✔
NEW
42
      uploadBuffer.destroy();
×
43
    }
44
    this._transientUploadBuffers = [];
166✔
45
    this.destroyResource();
166✔
46
  }
47

48
  finish(props?: CommandBufferProps): WebGPUCommandBuffer {
49
    this.device.pushErrorScope('validation');
85✔
50
    const commandBuffer = new WebGPUCommandBuffer(this, {
85✔
51
      id: props?.id || 'unnamed-command-buffer'
168✔
52
    });
53
    this.device.popErrorScope((error: GPUError) => {
85✔
54
      const message = `${this} command encoding: ${error.message}. Maybe add depthWriteEnabled to your Model?`;
×
55
      this.device.reportError(new Error(message), this)();
×
56
      this.device.debug();
×
57
    });
58
    this.destroy();
85✔
59
    return commandBuffer;
85✔
60
  }
61

62
  /** Retains staging uploads until the finished command buffer has been submitted. */
63
  trackTransientUploadBuffer(buffer: WebGPUBuffer): void {
64
    this._transientUploadBuffers.push(buffer);
1✔
65
  }
66

67
  /** Transfers staging-upload ownership to the finished command buffer. */
68
  takeTransientUploadBuffers(): WebGPUBuffer[] {
69
    const transientUploadBuffers = this._transientUploadBuffers;
85✔
70
    this._transientUploadBuffers = [];
85✔
71
    return transientUploadBuffers;
85✔
72
  }
73

74
  /**
75
   * Allows a render pass to begin against a canvas context
76
   * @todo need to support a "Framebuffer" equivalent (aka preconfigured RenderPassDescriptors?).
77
   */
78
  beginRenderPass(props: RenderPassProps = {}): WebGPURenderPass {
79✔
79
    return new WebGPURenderPass(
79✔
80
      this.device,
81
      this._applyTimeProfilingToPassProps(props),
82
      this.handle
83
    );
84
  }
85

86
  beginComputePass(props: ComputePassProps = {}): WebGPUComputePass {
4✔
87
    return new WebGPUComputePass(
4✔
88
      this.device,
89
      this._applyTimeProfilingToPassProps(props),
90
      this.handle
91
    );
92
  }
93

94
  // beginRenderPass(GPURenderPassDescriptor descriptor): GPURenderPassEncoder;
95
  // beginComputePass(optional GPUComputePassDescriptor descriptor = {}): GPUComputePassEncoder;
96

97
  copyBufferToBuffer(options: {
98
    sourceBuffer: Buffer;
99
    sourceOffset?: number;
100
    destinationBuffer: Buffer;
101
    destinationOffset?: number;
102
    size?: number;
103
  }): void {
104
    const webgpuSourceBuffer = options.sourceBuffer as WebGPUBuffer;
3✔
105
    const webgpuDestinationBuffer = options.destinationBuffer as WebGPUBuffer;
3✔
106
    this.handle.copyBufferToBuffer(
3✔
107
      webgpuSourceBuffer.handle,
108
      options.sourceOffset ?? 0,
6✔
109
      webgpuDestinationBuffer.handle,
110
      options.destinationOffset ?? 0,
5✔
111
      options.size ?? 0
3!
112
    );
113
  }
114

115
  copyBufferToTexture(options: CopyBufferToTextureOptions): void {
116
    const webgpuSourceBuffer = options.sourceBuffer as WebGPUBuffer;
×
117
    const webgpuDestinationTexture = options.destinationTexture as WebGPUTexture;
×
118
    const copyOrigin = options.origin ?? [0, 0, 0];
×
119
    const copySize = options.size;
×
120
    this.handle.copyBufferToTexture(
×
121
      {
122
        buffer: webgpuSourceBuffer.handle,
123
        offset: options.byteOffset ?? 0,
×
124
        bytesPerRow: options.bytesPerRow,
125
        rowsPerImage: options.rowsPerImage
126
      },
127
      {
128
        texture: webgpuDestinationTexture.handle,
129
        mipLevel: options.mipLevel ?? 0,
×
130
        origin: {
131
          x: copyOrigin[0] ?? 0,
×
132
          y: copyOrigin[1] ?? 0,
×
133
          z: copyOrigin[2] ?? 0
×
134
        },
135
        aspect: options.aspect
136
      },
137
      {
138
        width: copySize[0],
139
        height: copySize[1],
140
        depthOrArrayLayers: copySize[2]
141
      }
142
    );
143
  }
144

145
  copyTextureToBuffer(options: CopyTextureToBufferOptions): void {
146
    const {
147
      sourceTexture,
148
      destinationBuffer,
149
      origin = [0, 0, 0],
×
150
      byteOffset = 0,
×
151
      width,
152
      height,
153
      depthOrArrayLayers,
154
      mipLevel,
155
      aspect
156
    } = options;
×
157
    const webgpuSourceTexture = sourceTexture as WebGPUTexture;
×
158
    webgpuSourceTexture.copyToBuffer(
×
159
      this.handle,
160
      {
161
        x: origin[0] ?? 0,
×
162
        y: origin[1] ?? 0,
×
163
        z: origin[2] ?? 0,
×
164
        width,
165
        height,
166
        depthOrArrayLayers,
167
        mipLevel,
168
        aspect,
169
        byteOffset,
170
        bytesPerRow: options.bytesPerRow,
171
        rowsPerImage: options.rowsPerImage
172
      },
173
      destinationBuffer
174
    );
175
  }
176

177
  copyTextureToTexture(options: CopyTextureToTextureOptions): void {
178
    const webgpuSourceTexture = options.sourceTexture as WebGPUTexture;
×
179
    const webgpuDestinationTexture = options.destinationTexture as WebGPUTexture;
×
180
    const sourceRegion = webgpuSourceTexture._normalizeTextureReadOptions({
×
181
      x: options.origin?.[0] ?? 0,
×
182
      y: options.origin?.[1] ?? 0,
×
183
      z: options.origin?.[2] ?? 0,
×
184
      width: options.width,
185
      height: options.height,
186
      depthOrArrayLayers: options.depthOrArrayLayers,
187
      mipLevel: options.mipLevel ?? 0,
×
188
      aspect: options.aspect ?? 'all'
×
189
    });
190

191
    this.handle.copyTextureToTexture(
×
192
      {
193
        texture: webgpuSourceTexture.handle,
194
        mipLevel: sourceRegion.mipLevel,
195
        origin: {
196
          x: sourceRegion.x,
197
          y: sourceRegion.y,
198
          z: sourceRegion.z
199
        },
200
        aspect: sourceRegion.aspect
201
      },
202
      {
203
        texture: webgpuDestinationTexture.handle,
204
        mipLevel: options.destinationMipLevel ?? 0,
×
205
        origin: {
206
          x: options.destinationOrigin?.[0] ?? 0,
×
207
          y: options.destinationOrigin?.[1] ?? 0,
×
208
          z: options.destinationOrigin?.[2] ?? 0
×
209
        },
210
        aspect: options.destinationAspect ?? sourceRegion.aspect
×
211
      },
212
      {
213
        width: sourceRegion.width,
214
        height: sourceRegion.height,
215
        depthOrArrayLayers: sourceRegion.depthOrArrayLayers
216
      }
217
    );
218
  }
219

220
  override pushDebugGroup(groupLabel: string): void {
221
    this.handle.pushDebugGroup(groupLabel);
×
222
  }
223

224
  override popDebugGroup(): void {
225
    this.handle.popDebugGroup();
×
226
  }
227

228
  override insertDebugMarker(markerLabel: string): void {
229
    this.handle.insertDebugMarker(markerLabel);
×
230
  }
231

232
  override resolveQuerySet(
233
    querySet: WebGPUQuerySet,
234
    destination: Buffer,
235
    options?: {
236
      firstQuery?: number;
237
      queryCount?: number;
238
      destinationOffset?: number;
239
    }
240
  ): void {
241
    const webgpuQuerySet = querySet;
2✔
242
    const webgpuBuffer = destination as WebGPUBuffer;
2✔
243
    this.handle.resolveQuerySet(
2✔
244
      webgpuQuerySet.handle,
245
      options?.firstQuery || 0,
4✔
246
      options?.queryCount || querySet.props.count - (options?.firstQuery || 0),
8✔
247
      webgpuBuffer.handle,
248
      options?.destinationOffset || 0
4✔
249
    );
250
  }
251

252
  writeTimestamp(querySet: WebGPUQuerySet, queryIndex: number): void {
253
    querySet._invalidateResults();
4✔
254
    const writeTimestamp = (
255
      this.handle as GPUCommandEncoder & {
4✔
256
        writeTimestamp?: (querySet: GPUQuerySet, queryIndex: number) => void;
257
      }
258
    ).writeTimestamp;
259

260
    if (writeTimestamp) {
4!
261
      writeTimestamp.call(this.handle, querySet.handle, queryIndex);
4✔
262
      return;
4✔
263
    }
264

265
    const computePass = this.handle.beginComputePass({
×
266
      timestampWrites: {
267
        querySet: querySet.handle,
268
        beginningOfPassWriteIndex: queryIndex
269
      }
270
    });
271
    computePass.end();
×
272
  }
273
}
274

275
/*
276
  // setDataFromTypedArray(data): this {
277
  //   const textureDataBuffer = this.device.handle.createBuffer({
278
  //     size: data.byteLength,
279
  //     usage: Buffer.COPY_DST | Buffer.COPY_SRC,
280
  //     mappedAtCreation: true
281
  //   });
282
  //   new Uint8Array(textureDataBuffer.getMappedRange()).set(data);
283
  //   textureDataBuffer.unmap();
284

285
  //   this.setBuffer(textureDataBuffer);
286

287
  //   textureDataBuffer.destroy();
288
  //   return this;
289
  // }
290

291
 */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc