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

visgl / luma.gl / 21063730492

16 Jan 2026 10:34AM UTC coverage: 76.381% (-0.003%) from 76.384%
21063730492

push

github

web-flow
fix(core): Auto-convert uint8 buffers to uint16 (#2486) (#2491)

2291 of 2972 branches covered (77.09%)

Branch coverage included in aggregate %.

9 of 15 new or added lines in 4 files covered. (60.0%)

51 existing lines in 2 files now uncovered.

28602 of 37474 relevant lines covered (76.32%)

70.36 hits per line

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

93.71
/modules/core/src/adapter/resources/buffer.ts
1
// luma.gl
1✔
2
// SPDX-License-Identifier: MIT
1✔
3
// Copyright (c) vis.gl contributors
1✔
4

1✔
5
import type {Device} from '../device';
1✔
6
import {Resource, ResourceProps} from './resource';
1✔
7

1✔
8
/** Callback for Buffer.mapAndReadAsync */
1✔
9
export type BufferMapCallback<T> = (arrayBuffer: ArrayBuffer, lifetime: 'mapped' | 'copied') => T;
1✔
10

1✔
11
export type BufferProps = ResourceProps & {
1✔
12
  /** Supply a handle to connect to an existing device-specific buffer */
1✔
13
  handle?: WebGLBuffer;
1✔
14
  /** Specifies how this buffer can be used */
1✔
15
  usage?: number;
1✔
16
  /** Length in bytes of memory to be allocated. If not specified, `byteLength` of  `props.data` will be used. */
1✔
17
  byteLength?: number;
1✔
18
  /** Byte offset into the newly created Buffer to store data at */
1✔
19
  byteOffset?: number;
1✔
20
  /** If props.usage includes Buffer.INDEX. Note: uint8 indices are automatically converted to uint16 for WebGPU compatibility */
1✔
21
  indexType?: 'uint8' | 'uint16' | 'uint32';
1✔
22
  /** Data to initialize the buffer with. */
1✔
23
  data?: ArrayBuffer | ArrayBufferView | null;
1✔
24
  /** Callback to initialize data without copy */
1✔
25
  onMapped?: BufferMapCallback<void>;
1✔
26
};
1✔
27

1✔
28
/** Abstract GPU buffer */
1✔
29
export abstract class Buffer extends Resource<BufferProps> {
1✔
30
  /** Index buffer */
1✔
31
  static INDEX = 0x0010;
1✔
32
  /** Vertex buffer */
1✔
33
  static VERTEX = 0x0020;
1✔
34
  /** Uniform buffer */
1✔
35
  static UNIFORM = 0x0040;
1✔
36
  /** Storage buffer */
1✔
37
  static STORAGE = 0x0080;
1✔
38
  static INDIRECT = 0x0100;
1✔
39
  static QUERY_RESOLVE = 0x0200;
1✔
40

1✔
41
  // Usage Flags
1✔
42
  static MAP_READ = 0x01;
1✔
43
  static MAP_WRITE = 0x02;
1✔
44
  static COPY_SRC = 0x0004;
1✔
45
  static COPY_DST = 0x0008;
1✔
46

1✔
47
  override get [Symbol.toStringTag](): string {
1✔
48
    return 'Buffer';
373✔
49
  }
373✔
50

1✔
51
  /** The usage with which this buffer was created */
1✔
52
  readonly usage: number;
1✔
53
  /** For index buffers, whether indices are 8, 16 or 32 bit. Note: uint8 indices are automatically converted to uint16 for WebGPU compatibility */
1✔
54
  readonly indexType?: 'uint8' | 'uint16' | 'uint32';
1✔
55
  /** Length of buffer in bytes */
1✔
56
  abstract byteLength: number;
1✔
57
  /** "Time" of last update, can be used to check if redraw is needed */
1✔
58
  updateTimestamp: number;
1✔
59

1✔
60
  constructor(device: Device, props: BufferProps) {
1✔
61
    const deducedProps = {...props};
101✔
62

101✔
63
    // Deduce indexType
101✔
64
    if ((props.usage || 0) & Buffer.INDEX && !props.indexType) {
101!
65
      if (props.data instanceof Uint32Array) {
×
66
        deducedProps.indexType = 'uint32';
×
67
      } else if (props.data instanceof Uint16Array) {
×
68
        deducedProps.indexType = 'uint16';
×
NEW
69
      } else if (props.data instanceof Uint8Array) {
×
NEW
70
        deducedProps.indexType = 'uint8';
×
71
      }
×
72
    }
×
73

101✔
74
    // Remove data from props before storing, we don't want to hold on to a big chunk of memory
101✔
75
    delete deducedProps.data;
101✔
76

101✔
77
    super(device, deducedProps, Buffer.defaultProps);
101✔
78

101✔
79
    this.usage = deducedProps.usage || 0;
101✔
80
    this.indexType = deducedProps.indexType;
101✔
81

101✔
82
    // TODO - perhaps this should be set on async write completion?
101✔
83
    this.updateTimestamp = device.incrementTimestamp();
101✔
84
  }
101✔
85

1✔
86
  /**
1✔
87
   * Create a copy of this Buffer with new byteLength, with same props but of the specified size.
1✔
88
   * @note Does not copy contents of the cloned Buffer.
1✔
89
   */
1✔
90
  clone(props: {byteLength: number}): Buffer {
1✔
91
    return this.device.createBuffer({...this.props, ...props});
×
92
  }
×
93

1✔
94
  /** Write data to buffer */
1✔
95
  abstract write(
1✔
96
    data: ArrayBufferLike | ArrayBufferView | SharedArrayBuffer,
1✔
97
    byteOffset?: number
1✔
98
  ): void;
1✔
99

1✔
100
  abstract mapAndWriteAsync(
1✔
101
    onMapped: BufferMapCallback<void | Promise<void>>,
1✔
102
    byteOffset?: number,
1✔
103
    byteLength?: number
1✔
104
  ): Promise<void>;
1✔
105

1✔
106
  /** Reads data asynchronously, returns a copy of the buffer data */
1✔
107
  abstract readAsync(byteOffset?: number, byteLength?: number): Promise<Uint8Array>;
1✔
108

1✔
109
  /** Maps buffer data to CPU memory. Mapped memory is only accessible in the callback */
1✔
110
  abstract mapAndReadAsync<T>(
1✔
111
    onMapped: BufferMapCallback<T>,
1✔
112
    byteOffset?: number,
1✔
113
    byteLength?: number
1✔
114
  ): Promise<T>;
1✔
115

1✔
116
  /** Read data synchronously. @note WebGL2 only */
1✔
117
  abstract readSyncWebGL(byteOffset?: number, byteLength?: number): Uint8Array;
1✔
118

1✔
119
  // PROTECTED METHODS (INTENDED FOR USE BY OTHER FRAMEWORK CODE ONLY)
1✔
120

1✔
121
  /** Max amount of debug data saved. Two vec4's */
1✔
122
  static DEBUG_DATA_MAX_LENGTH = 32;
1✔
123

1✔
124
  /** A partial CPU-side copy of the data in this buffer, for debugging purposes */
1✔
125
  debugData: ArrayBuffer = new ArrayBuffer(0);
1✔
126

1✔
127
  /** This doesn't handle partial non-zero offset updates correctly */
1✔
128
  protected _setDebugData(
1✔
129
    data: ArrayBufferView | ArrayBufferLike | null,
141✔
130
    byteOffset: number,
141✔
131
    byteLength: number
141✔
132
  ): void {
141✔
133
    const arrayBuffer: ArrayBufferLike | null = ArrayBuffer.isView(data) ? data.buffer : data;
141✔
134
    const debugDataLength = Math.min(
141✔
135
      data ? data.byteLength : byteLength,
141✔
136
      Buffer.DEBUG_DATA_MAX_LENGTH
141✔
137
    );
141✔
138
    if (arrayBuffer === null) {
141✔
139
      this.debugData = new ArrayBuffer(debugDataLength);
44✔
140
    } else if (byteOffset === 0 && byteLength === arrayBuffer.byteLength) {
141✔
141
      this.debugData = arrayBuffer.slice(0, debugDataLength);
86✔
142
    } else {
97✔
143
      this.debugData = arrayBuffer.slice(byteOffset, byteOffset + debugDataLength);
11✔
144
    }
11✔
145
  }
141✔
146

1✔
147
  static override defaultProps: Required<BufferProps> = {
1✔
148
    ...Resource.defaultProps,
1✔
149
    usage: 0, // Buffer.COPY_DST | Buffer.COPY_SRC
1✔
150
    byteLength: 0,
1✔
151
    byteOffset: 0,
1✔
152
    data: null,
1✔
153
    indexType: 'uint16',
1✔
154
    onMapped: undefined!
1✔
155
  };
1✔
156
}
1✔
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