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

visgl / luma.gl / 28704161937

04 Jul 2026 11:03AM UTC coverage: 72.194% (+0.008%) from 72.186%
28704161937

Pull #2732

github

web-flow
Merge 788000ead into 30d8c7ace
Pull Request #2732: [codex] Simplify text rendering behind TextRenderer

11280 of 17608 branches covered (64.06%)

Branch coverage included in aggregate %.

383 of 539 new or added lines in 17 files covered. (71.06%)

12 existing lines in 4 files now uncovered.

21846 of 28277 relevant lines covered (77.26%)

5604.61 hits per line

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

77.27
/modules/text/src/text-2d/gpu-text-data.ts
1
// luma.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import type {TextAttributeRenderProps, TextAttributeState} from './models/text-attribute-model';
6
import type {TextStorageRenderProps} from './models/text-storage-model';
7
import type {TextDictionaryRenderProps} from './models/text-dictionary-model';
8
import type {TextDictionaryState, TextStorageState} from './model-utils/text-storage-state';
9
import type {GPUTextResources} from './gpu-text-resources';
10

11
/** Prepared text representation selected by an adapter or forced through the experimental API. */
12
export type GPUTextStrategy = 'attribute' | 'storage' | 'storage-row-indexed' | 'dictionary';
13

14
/** Representation-independent preparation and memory statistics for {@link GPUTextData}. */
15
export type GPUTextStats = {
16
  /** Strategy used to prepare and render the data. */
17
  strategy: GPUTextStrategy;
18
  /** Number of source text rows. */
19
  rowCount: number;
20
  /** Number of visible glyph instances generated from all source rows. */
21
  glyphCount: number;
22
  /** Number of preserved source batches or chunks. */
23
  sourceBatchCount: number;
24
  /** Number of draw batches after applying device buffer-size limits. */
25
  renderBatchCount: number;
26
  /** CPU preparation time in milliseconds. */
27
  preparationTimeMs: number;
28
  /** Bytes retained by prepared glyph, row, atlas-definition, and control data. */
29
  retainedByteLength: number;
30
  /** Bytes used by temporary compute inputs that are released after preparation. */
31
  transientByteLength: number;
32
};
33

34
type GPUTextAttributeData = {
35
  strategy: 'attribute';
36
  modelProps: TextAttributeRenderProps;
37
  state: TextAttributeState;
38
};
39

40
type GPUTextStorageData = {
41
  strategy: 'storage' | 'storage-row-indexed';
42
  modelProps: TextStorageRenderProps;
43
  state: TextStorageState;
44
};
45

46
type GPUTextDictionaryData = {
47
  strategy: 'dictionary';
48
  modelProps: TextDictionaryRenderProps;
49
  state: TextDictionaryState;
50
};
51

52
/** @internal Strategy-specific prepared data accepted by the experimental factory. */
53
export type GPUTextDataProps = GPUTextAttributeData | GPUTextStorageData | GPUTextDictionaryData;
54

55
/**
56
 * Caller-owned prepared text data shared by render and picking models.
57
 *
58
 * The public surface intentionally exposes only representation-independent counts, statistics,
59
 * and destruction. Strategy-specific state is available only through the experimental entry
60
 * point. Destroy every borrowing renderer before destroying its data.
61
 */
62
export interface GPUTextData {
63
  /** Shared atlas resources borrowed by this batch. */
64
  readonly resources: GPUTextResources;
65
  /** Strategy selected while preparing this data. */
66
  readonly strategy: GPUTextStrategy;
67
  /** Source stream batch index. */
68
  readonly sourceBatchIndex: number;
69
  /** Global source-row index assigned to local row zero. */
70
  readonly rowIndexBase: number;
71
  /** Global glyph index assigned to local glyph zero. */
72
  readonly glyphIndexBase: number;
73
  /** Number of source text rows in this batch. */
74
  readonly rowCount: number;
75
  /** Number of visible glyph instances in this batch. */
76
  readonly glyphCount: number;
77
  /** Compact representation-independent preparation statistics. */
78
  readonly stats: GPUTextStats;
79
  /** Releases the owned source batch, generated buffers, and tables. Idempotent. */
80
  destroy(): void;
81
}
82

83
/** @internal */
84
export class GPUTextDataImpl implements GPUTextData {
85
  readonly resources: GPUTextResources;
86
  readonly strategy: GPUTextStrategy;
87
  readonly sourceBatchIndex: number;
88
  readonly rowIndexBase: number;
89
  readonly glyphIndexBase: number;
90
  readonly rowCount: number;
91
  readonly glyphCount: number;
92
  readonly stats: GPUTextStats;
93
  private readonly props: GPUTextDataProps;
94
  private readonly destroySource?: () => void;
95
  private destroyed = false;
15✔
96

97
  constructor(
98
    props: GPUTextDataProps,
99
    options: {
100
      resources: GPUTextResources;
101
      sourceBatchIndex: number;
102
      rowIndexBase: number;
103
      glyphIndexBase: number;
104
      rowCount: number;
105
      destroySource?: () => void;
106
    }
107
  ) {
108
    this.props = props;
15✔
109
    this.resources = options.resources;
15✔
110
    this.strategy = props.strategy;
15✔
111
    this.sourceBatchIndex = options.sourceBatchIndex;
15✔
112
    this.rowIndexBase = options.rowIndexBase;
15✔
113
    this.glyphIndexBase = options.glyphIndexBase;
15✔
114
    this.rowCount = options.rowCount;
15✔
115
    this.destroySource = options.destroySource;
15✔
116
    this.glyphCount =
15✔
117
      props.strategy === 'attribute' ? props.state.glyphLayout.glyphCount : props.state.glyphCount;
15!
118
    this.stats = makeGPUTextStats(props, options.rowCount);
15✔
119
  }
120

121
  /** Releases all prepared and source resources owned by this data object exactly once. */
122
  destroy(): void {
123
    if (this.destroyed) {
16✔
124
      return;
1✔
125
    }
126
    this.destroyed = true;
15✔
127
    destroyGPUTextState(this.props);
15✔
128
    this.destroySource?.();
15✔
129
  }
130

131
  getInternalProps(): GPUTextDataProps {
132
    if (this.destroyed) {
13!
NEW
133
      throw new Error('GPUTextData has been destroyed');
×
134
    }
135
    return this.props;
13✔
136
  }
137
}
138

139
/** @internal */
140
export function createGPUTextData(
141
  props: GPUTextDataProps,
142
  options: {
143
    resources: GPUTextResources;
144
    sourceBatchIndex?: number;
145
    rowIndexBase?: number;
146
    glyphIndexBase?: number;
147
    rowCount: number;
148
    destroySource?: () => void;
149
  }
150
): GPUTextData {
151
  return new GPUTextDataImpl(props, {
15✔
152
    sourceBatchIndex: 0,
153
    rowIndexBase: 0,
154
    glyphIndexBase: 0,
155
    ...options
156
  });
157
}
158

159
/** @internal */
160
export function getGPUTextDataProps(data: GPUTextData): GPUTextDataProps {
161
  if (!(data instanceof GPUTextDataImpl)) {
13!
NEW
162
    throw new Error('TextRenderer requires GPUTextData created by @luma.gl/text');
×
163
  }
164
  return data.getInternalProps();
13✔
165
}
166

167
function makeGPUTextStats(props: GPUTextDataProps, rowCount: number): GPUTextStats {
168
  if (props.strategy === 'attribute') {
15!
169
    const state = props.state;
15✔
170
    return {
15✔
171
      strategy: props.strategy,
172
      rowCount,
173
      glyphCount: state.glyphLayout.glyphCount,
174
      sourceBatchCount: 1,
175
      renderBatchCount: state.renderBatches.length,
176
      preparationTimeMs: state.glyphAttributeBuildTimeMs,
177
      retainedByteLength: state.glyphAttributeByteLength,
178
      transientByteLength: 0
179
    };
180
  }
NEW
181
  const state = props.state;
×
NEW
182
  return {
×
183
    strategy: props.strategy,
184
    rowCount,
185
    glyphCount: state.glyphCount,
186
    sourceBatchCount: 1,
187
    renderBatchCount: state.renderBatches.length,
188
    preparationTimeMs: state.glyphAttributeBuildTimeMs + state.compactStreamBuildTimeMs,
189
    retainedByteLength:
190
      state.glyphAttributeByteLength +
191
      state.compactStreamByteLength +
192
      state.rowStorageByteLength +
193
      state.glyphDefinitionStorageByteLength,
194
    transientByteLength: state.transientComputeInputByteLength
195
  };
196
}
197

198
function destroyGPUTextState(props: GPUTextDataProps): void {
199
  if (props.strategy === 'attribute') {
15!
200
    props.state.destroy();
15✔
201
    return;
15✔
202
  }
NEW
203
  props.state.destroy();
×
204
}
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