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

visgl / luma.gl / 23412316304

22 Mar 2026 08:56PM UTC coverage: 73.59% (-0.6%) from 74.227%
23412316304

push

github

web-flow
feat(engine): add async texture buffer read (#2439)

4597 of 7074 branches covered (64.98%)

Branch coverage included in aggregate %.

111 of 213 new or added lines in 20 files covered. (52.11%)

40 existing lines in 8 files now uncovered.

10525 of 13475 relevant lines covered (78.11%)

263.46 hits per line

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

41.51
/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

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

39
  override destroy(): void {
40
    this.destroyResource();
163✔
41
  }
42

43
  finish(props?: CommandBufferProps): WebGPUCommandBuffer {
44
    this.device.pushErrorScope('validation');
83✔
45
    const commandBuffer = new WebGPUCommandBuffer(this, {
83✔
46
      id: props?.id || 'unnamed-command-buffer'
164✔
47
    });
48
    this.device.popErrorScope((error: GPUError) => {
83✔
UNCOV
49
      const message = `${this} command encoding: ${error.message}. Maybe add depthWriteEnabled to your Model?`;
×
UNCOV
50
      this.device.reportError(new Error(message), this)();
×
UNCOV
51
      this.device.debug();
×
52
    });
53
    this.destroy();
83✔
54
    return commandBuffer;
83✔
55
  }
56

57
  /**
58
   * Allows a render pass to begin against a canvas context
59
   * @todo need to support a "Framebuffer" equivalent (aka preconfigured RenderPassDescriptors?).
60
   */
61
  beginRenderPass(props: RenderPassProps = {}): WebGPURenderPass {
76✔
62
    return new WebGPURenderPass(
76✔
63
      this.device,
64
      this._applyTimeProfilingToPassProps(props),
65
      this.handle
66
    );
67
  }
68

69
  beginComputePass(props: ComputePassProps = {}): WebGPUComputePass {
4✔
70
    return new WebGPUComputePass(
4✔
71
      this.device,
72
      this._applyTimeProfilingToPassProps(props),
73
      this.handle
74
    );
75
  }
76

77
  // beginRenderPass(GPURenderPassDescriptor descriptor): GPURenderPassEncoder;
78
  // beginComputePass(optional GPUComputePassDescriptor descriptor = {}): GPUComputePassEncoder;
79

80
  copyBufferToBuffer(options: {
81
    sourceBuffer: Buffer;
82
    sourceOffset?: number;
83
    destinationBuffer: Buffer;
84
    destinationOffset?: number;
85
    size?: number;
86
  }): void {
87
    const webgpuSourceBuffer = options.sourceBuffer as WebGPUBuffer;
2✔
88
    const webgpuDestinationBuffer = options.destinationBuffer as WebGPUBuffer;
2✔
89
    this.handle.copyBufferToBuffer(
2✔
90
      webgpuSourceBuffer.handle,
91
      options.sourceOffset ?? 0,
4✔
92
      webgpuDestinationBuffer.handle,
93
      options.destinationOffset ?? 0,
4✔
94
      options.size ?? 0
2!
95
    );
96
  }
97

98
  copyBufferToTexture(options: CopyBufferToTextureOptions): void {
UNCOV
99
    const webgpuSourceBuffer = options.sourceBuffer as WebGPUBuffer;
×
NEW
100
    const webgpuDestinationTexture = options.destinationTexture as WebGPUTexture;
×
NEW
101
    const copyOrigin = options.origin ?? [0, 0, 0];
×
NEW
102
    const copySize = options.size;
×
UNCOV
103
    this.handle.copyBufferToTexture(
×
104
      {
105
        buffer: webgpuSourceBuffer.handle,
106
        offset: options.byteOffset ?? 0,
×
107
        bytesPerRow: options.bytesPerRow,
108
        rowsPerImage: options.rowsPerImage
109
      },
110
      {
111
        texture: webgpuDestinationTexture.handle,
112
        mipLevel: options.mipLevel ?? 0,
×
113
        origin: {
114
          x: copyOrigin[0] ?? 0,
×
115
          y: copyOrigin[1] ?? 0,
×
116
          z: copyOrigin[2] ?? 0
×
117
        },
118
        aspect: options.aspect
119
      },
120
      {
121
        width: copySize[0],
122
        height: copySize[1],
123
        depthOrArrayLayers: copySize[2]
124
      }
125
    );
126
  }
127

128
  copyTextureToBuffer(options: CopyTextureToBufferOptions): void {
129
    const {
130
      sourceTexture,
131
      destinationBuffer,
132
      origin = [0, 0, 0],
×
133
      byteOffset = 0,
×
134
      width,
135
      height,
136
      depthOrArrayLayers,
137
      mipLevel,
138
      aspect
NEW
139
    } = options;
×
NEW
140
    const webgpuSourceTexture = sourceTexture as WebGPUTexture;
×
NEW
141
    webgpuSourceTexture.copyToBuffer(
×
142
      this.handle,
143
      {
144
        x: origin[0] ?? 0,
×
145
        y: origin[1] ?? 0,
×
146
        z: origin[2] ?? 0,
×
147
        width,
148
        height,
149
        depthOrArrayLayers,
150
        mipLevel,
151
        aspect,
152
        byteOffset,
153
        bytesPerRow: options.bytesPerRow,
154
        rowsPerImage: options.rowsPerImage
155
      },
156
      destinationBuffer
157
    );
158
  }
159

160
  copyTextureToTexture(options: CopyTextureToTextureOptions): void {
NEW
161
    const webgpuSourceTexture = options.sourceTexture as WebGPUTexture;
×
NEW
162
    const webgpuDestinationTexture = options.destinationTexture as WebGPUTexture;
×
NEW
163
    const sourceRegion = webgpuSourceTexture._normalizeTextureReadOptions({
×
164
      x: options.origin?.[0] ?? 0,
×
165
      y: options.origin?.[1] ?? 0,
×
166
      z: options.origin?.[2] ?? 0,
×
167
      width: options.width,
168
      height: options.height,
169
      depthOrArrayLayers: options.depthOrArrayLayers,
170
      mipLevel: options.mipLevel ?? 0,
×
171
      aspect: options.aspect ?? 'all'
×
172
    });
173

NEW
174
    this.handle.copyTextureToTexture(
×
175
      {
176
        texture: webgpuSourceTexture.handle,
177
        mipLevel: sourceRegion.mipLevel,
178
        origin: {
179
          x: sourceRegion.x,
180
          y: sourceRegion.y,
181
          z: sourceRegion.z
182
        },
183
        aspect: sourceRegion.aspect
184
      },
185
      {
186
        texture: webgpuDestinationTexture.handle,
187
        mipLevel: options.destinationMipLevel ?? 0,
×
188
        origin: {
189
          x: options.destinationOrigin?.[0] ?? 0,
×
190
          y: options.destinationOrigin?.[1] ?? 0,
×
191
          z: options.destinationOrigin?.[2] ?? 0
×
192
        },
193
        aspect: options.destinationAspect ?? sourceRegion.aspect
×
194
      },
195
      {
196
        width: sourceRegion.width,
197
        height: sourceRegion.height,
198
        depthOrArrayLayers: sourceRegion.depthOrArrayLayers
199
      }
200
    );
201
  }
202

203
  override pushDebugGroup(groupLabel: string): void {
204
    this.handle.pushDebugGroup(groupLabel);
×
205
  }
206

207
  override popDebugGroup(): void {
208
    this.handle.popDebugGroup();
×
209
  }
210

211
  override insertDebugMarker(markerLabel: string): void {
212
    this.handle.insertDebugMarker(markerLabel);
×
213
  }
214

215
  override resolveQuerySet(
216
    querySet: WebGPUQuerySet,
217
    destination: Buffer,
218
    options?: {
219
      firstQuery?: number;
220
      queryCount?: number;
221
      destinationOffset?: number;
222
    }
223
  ): void {
224
    const webgpuQuerySet = querySet;
2✔
225
    const webgpuBuffer = destination as WebGPUBuffer;
2✔
226
    this.handle.resolveQuerySet(
2✔
227
      webgpuQuerySet.handle,
228
      options?.firstQuery || 0,
4✔
229
      options?.queryCount || querySet.props.count - (options?.firstQuery || 0),
8✔
230
      webgpuBuffer.handle,
231
      options?.destinationOffset || 0
4✔
232
    );
233
  }
234

235
  writeTimestamp(querySet: WebGPUQuerySet, queryIndex: number): void {
236
    querySet._invalidateResults();
4✔
237
    const writeTimestamp = (
238
      this.handle as GPUCommandEncoder & {
4✔
239
        writeTimestamp?: (querySet: GPUQuerySet, queryIndex: number) => void;
240
      }
241
    ).writeTimestamp;
242

243
    if (writeTimestamp) {
4!
244
      writeTimestamp.call(this.handle, querySet.handle, queryIndex);
4✔
245
      return;
4✔
246
    }
247

248
    const computePass = this.handle.beginComputePass({
×
249
      timestampWrites: {
250
        querySet: querySet.handle,
251
        beginningOfPassWriteIndex: queryIndex
252
      }
253
    });
254
    computePass.end();
×
255
  }
256
}
257

258
/*
259
  // setDataFromTypedArray(data): this {
260
  //   const textureDataBuffer = this.device.handle.createBuffer({
261
  //     size: data.byteLength,
262
  //     usage: Buffer.COPY_DST | Buffer.COPY_SRC,
263
  //     mappedAtCreation: true
264
  //   });
265
  //   new Uint8Array(textureDataBuffer.getMappedRange()).set(data);
266
  //   textureDataBuffer.unmap();
267

268
  //   this.setBuffer(textureDataBuffer);
269

270
  //   textureDataBuffer.destroy();
271
  //   return this;
272
  // }
273

274
 */
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