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

visgl / luma.gl / 25990278810

17 May 2026 12:01PM UTC coverage: 75.092% (+0.2%) from 74.881%
25990278810

push

github

web-flow
feat: Columnar GPU-data stack (#2616)

6711 of 10084 branches covered (66.55%)

Branch coverage included in aggregate %.

625 of 865 new or added lines in 22 files covered. (72.25%)

1 existing line in 1 file now uncovered.

14631 of 18337 relevant lines covered (79.79%)

792.86 hits per line

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

94.59
/modules/engine/src/utils/buffer-schema.ts
1
// luma.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import type {BufferLayout, VertexFormat} from '@luma.gl/core';
6

7
/**
8
 * One named view into a logical GPU buffer record.
9
 *
10
 * `recordOffset` advances whole records. `elementOffset` advances scalar
11
 * values within one record. The current helper lowers fields into vertex
12
 * `BufferLayout` attributes, while keeping the declaration itself record-oriented.
13
 */
14
export type BufferField = {
15
  /** Data format exposed through the generated buffer layout field. */
16
  format: VertexFormat;
17
  /** Whole-record offset relative to the current record. */
18
  recordOffset?: number;
19
  /** Scalar element offset inside the logical buffer record. */
20
  elementOffset?: number;
21
};
22

23
/** Named fields that describe one shared logical GPU buffer record. */
24
export type BufferSchema = Record<string, BufferField>;
25

26
/** Options for lowering one logical buffer schema into a vertex buffer layout. */
27
export type AttributeLayoutFromBufferSchemaOptions = {
28
  /** Name used to bind the shared logical buffer. */
29
  name: string;
30
  /** Byte stride between consecutive logical records. */
31
  byteStride: number;
32
  /** Byte size of one scalar element used by `elementOffset`. */
33
  bytesPerElement: number;
34
  /** Record schema shared by the logical buffer and future storage-friendly lowering paths. */
35
  schema: BufferSchema;
36
  /** Step mode shared by all generated vertex attribute views. */
37
  stepMode?: 'vertex' | 'instance';
38
};
39

40
/**
41
 * Lowers a record-oriented buffer schema into the canonical vertex `BufferLayout` shape.
42
 *
43
 * Each emitted attribute shares the same logical buffer and stride. Offsets follow:
44
 * `byteOffset = recordOffset * byteStride + elementOffset * bytesPerElement`.
45
 */
46
export function getAttributeLayoutFromBufferSchema(
47
  options: AttributeLayoutFromBufferSchemaOptions
48
): BufferLayout {
49
  const {name, byteStride, bytesPerElement, schema, stepMode} = options;
6✔
50
  assertNonNegativeInteger(byteStride, 'byteStride');
6✔
51
  assertPositiveInteger(bytesPerElement, 'bytesPerElement');
6✔
52

53
  const fieldEntries = Object.entries(schema);
6✔
54
  if (fieldEntries.length === 0) {
6✔
55
    throw new Error('schema must declare at least one buffer field');
1✔
56
  }
57

58
  return {
5✔
59
    name,
60
    byteStride,
61
    ...(stepMode ? {stepMode} : {}),
5✔
62
    attributes: fieldEntries.map(([attribute, field]) =>
63
      getResolvedBufferFieldLayout(attribute, field, byteStride, bytesPerElement)
11✔
64
    )
65
  };
66
}
67

68
function getResolvedBufferFieldLayout(
69
  attribute: string,
70
  field: BufferField,
71
  byteStride: number,
72
  bytesPerElement: number
73
): NonNullable<BufferLayout['attributes']>[number] {
74
  if (!field?.format) {
11✔
75
    throw new Error(`schema.${attribute} must declare a format`);
1✔
76
  }
77

78
  const recordOffset = field.recordOffset ?? 0;
10✔
79
  const elementOffset = field.elementOffset ?? 0;
11✔
80
  assertNonNegativeInteger(recordOffset, `schema.${attribute}.recordOffset`);
11✔
81
  assertNonNegativeInteger(elementOffset, `schema.${attribute}.elementOffset`);
11✔
82

83
  return {
11✔
84
    attribute,
85
    format: field.format,
86
    byteOffset: recordOffset * byteStride + elementOffset * bytesPerElement
87
  };
88
}
89

90
function assertPositiveInteger(value: number, label: string): void {
91
  if (!Number.isInteger(value) || value <= 0) {
6!
NEW
92
    throw new Error(`${label} must be a positive integer`);
×
93
  }
94
}
95

96
function assertNonNegativeInteger(value: number, label: string): void {
97
  if (!Number.isInteger(value) || value < 0) {
25✔
98
    throw new Error(`${label} must be a non-negative integer`);
1✔
99
  }
100
}
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