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

visgl / luma.gl / 27202880728

09 Jun 2026 11:24AM UTC coverage: 71.49% (+1.0%) from 70.51%
27202880728

Pull #2669

github

web-flow
Merge 4f9ed5f2a into 70f8e2f4b
Pull Request #2669: chore(arrow): Improve split between GPUTables and Arrow tables

9190 of 14517 branches covered (63.31%)

Branch coverage included in aggregate %.

618 of 865 new or added lines in 32 files covered. (71.45%)

18969 of 24872 relevant lines covered (76.27%)

4304.75 hits per line

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

40.51
/modules/arrow/src/arrow/engine/arrow-picking.ts
1
// luma.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import type {Device, RenderPass} from '@luma.gl/core';
6
import {
7
  indexPicking,
8
  getIndexPickingModule,
9
  PickingManager,
10
  supportsIndexPicking,
11
  type PickInfo,
12
  type PickingManagerProps,
13
  type ShaderInputs
14
} from '@luma.gl/engine';
15
import type {GPURecordBatchSourceInfo, GPUTable, GPUVector} from '@luma.gl/tables';
16
import {BufferType, Data, Uint32, Vector} from 'apache-arrow';
17
import {makeGPUVectorFromArrow} from '../gpu/arrow-gpu-table-adapters';
18

19
/** Arrow row identity resolved from a picking pass. */
20
export type ArrowPickingInfo = {
21
  /** Zero-based source batch index, or `null` when nothing is picked. */
22
  batchIndex: number | null;
23
  /** Zero-based source row index, or `null` when nothing is picked. */
24
  rowIndex: number | null;
25
  /** Zero-based row index inside the picked source batch, or `null` when unavailable. */
26
  batchRowIndex: number | null;
27
};
28

29
/** Options for creating a source-row index vector. */
30
export type ArrowRowIndexVectorOptions = {
31
  /** Number of source rows to encode. */
32
  rowCount: number;
33
  /** Source row index assigned to local row zero. Defaults to `0`. */
34
  rowIndexOffset?: number;
35
};
36

37
/** Options for creating a source-row index GPU vector. */
38
export type ArrowRowIndexGPUVectorOptions = ArrowRowIndexVectorOptions & {
39
  /** GPU vector/table column name. Defaults to `rowIndices`. */
40
  name?: string;
41
  /** Optional GPU buffer id. */
42
  id?: string;
43
};
44

45
/** Source metadata used to resolve picked Arrow rows. */
46
export type ArrowPickingSource =
47
  | {
48
      table?: Pick<GPUTable, 'batches'> | null;
49
      sourceInfos?: readonly (GPURecordBatchSourceInfo | null | undefined)[];
50
    }
51
  | Pick<GPUTable, 'batches'>
52
  | readonly (GPURecordBatchSourceInfo | null | undefined)[];
53

54
const NULL_PICK_INFO: PickInfo = {batchIndex: null, objectIndex: null};
22✔
55

56
/** Returns true when the device can render integer picking attachments. */
57
export function supportsArrowIndexPicking(device: Device): boolean {
58
  return supportsIndexPicking(device);
×
59
}
60

61
/** Returns the object-index picking shader module supported by this device. */
62
export function getArrowPickingModule(device: Device): typeof indexPicking {
NEW
63
  return getIndexPickingModule(device);
×
64
}
65

66
/** Returns picking modules for example model construction. */
67
export function getArrowPickingModules(device: Device): [typeof indexPicking] {
68
  return [getArrowPickingModule(device)];
×
69
}
70

71
/** Creates a PickingManager using Arrow examples' default auto backend selection. */
72
export function createArrowPickingManager(
73
  device: Device,
74
  props: PickingManagerProps
75
): PickingManager {
76
  return new PickingManager(device, {mode: 'auto', ...props});
1✔
77
}
78

79
/** Clears PickingManager state and optionally emits the standard null pick callback. */
80
export function clearArrowPickingState(
81
  picker: PickingManager,
82
  onObjectPicked?: (info: PickInfo) => void
83
): void {
84
  const hadPickedObject =
85
    picker.pickInfo.batchIndex !== null || picker.pickInfo.objectIndex !== null;
1✔
86
  picker.clearPickState();
1✔
87
  picker.pickInfo = NULL_PICK_INFO;
1✔
88
  if (hadPickedObject) {
1!
89
    onObjectPicked?.(NULL_PICK_INFO);
×
90
  }
91
}
92

93
/** Runs a picking render pass and schedules the PickingManager readback/update. */
94
export function runArrowPickingPass({
95
  picker,
96
  mousePosition,
97
  shaderInputs,
98
  draw
99
}: {
100
  picker: PickingManager;
101
  mousePosition: number[] | null | undefined;
102
  shaderInputs?: ShaderInputs<any>;
103
  draw: (pickingPass: RenderPass) => boolean | void;
104
}): boolean {
105
  if (!picker.shouldPick(mousePosition as [number, number] | null)) {
×
106
    return false;
×
107
  }
108

109
  const pickingPass = picker.beginRenderPass();
×
110
  let shouldUpdatePickInfo = false;
×
111
  try {
×
112
    shouldUpdatePickInfo = draw(pickingPass) !== false;
×
113
  } finally {
114
    pickingPass.end();
×
115
    shaderInputs?.setProps({picking: {isActive: false}});
×
116
  }
117

118
  if (shouldUpdatePickInfo) {
×
119
    void picker.updatePickInfo(mousePosition as [number, number]);
×
120
  }
121
  return shouldUpdatePickInfo;
×
122
}
123

124
/** Creates a Uint32 Arrow vector containing global source row indices. */
125
export function makeArrowRowIndexVector({
126
  rowCount,
127
  rowIndexOffset = 0
×
128
}: ArrowRowIndexVectorOptions): Vector<Uint32> {
129
  const values = new Uint32Array(rowCount);
×
130
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
×
131
    values[rowIndex] = rowIndexOffset + rowIndex;
×
132
  }
133
  const data = new Data(new Uint32(), 0, values.length, 0, {
×
134
    [BufferType.DATA]: values
135
  }) as unknown as Data<Uint32>;
136
  return new Vector([data]);
×
137
}
138

139
/** Creates a GPU vector containing global source row indices. */
140
export function makeArrowRowIndexGPUVector(
141
  device: Device,
142
  {rowCount, rowIndexOffset = 0, name = 'rowIndices', id}: ArrowRowIndexGPUVectorOptions
×
143
): GPUVector<'uint32'> {
144
  return makeGPUVectorFromArrow(device, makeArrowRowIndexVector({rowCount, rowIndexOffset}), {
×
145
    name,
146
    id,
147
    format: 'uint32'
148
  });
149
}
150

151
/** Creates source-row metadata for a prepared Arrow GPU batch. */
152
export function makeArrowRecordBatchSourceInfo({
153
  sourceBatchIndex = 0,
3✔
154
  sourceRowIndexOffset = 0,
3✔
155
  sourceRowCount
156
}: {
157
  sourceBatchIndex?: number;
158
  sourceRowIndexOffset?: number;
159
  sourceRowCount: number;
160
}): GPURecordBatchSourceInfo {
161
  return {sourceBatchIndex, sourceRowIndexOffset, sourceRowCount};
3✔
162
}
163

164
/** Resolves a generic PickingManager result into Arrow source row identity. */
165
export function resolveArrowPickInfo(
166
  pickInfo: PickInfo,
167
  source?: ArrowPickingSource | null
168
): ArrowPickingInfo {
169
  if (pickInfo.batchIndex === null || pickInfo.objectIndex === null) {
1!
170
    return {batchIndex: null, rowIndex: null, batchRowIndex: null};
×
171
  }
172

173
  const sourceInfo = getArrowPickingSourceInfo(source, pickInfo.batchIndex);
1✔
174
  return {
1✔
175
    batchIndex: sourceInfo?.sourceBatchIndex ?? pickInfo.batchIndex,
1!
176
    rowIndex: pickInfo.objectIndex,
177
    batchRowIndex: getArrowBatchRowIndex(pickInfo.objectIndex, sourceInfo)
178
  };
179
}
180

181
/** Returns the source metadata for a picked render batch, when available. */
182
export function getArrowPickingSourceInfo(
183
  source: ArrowPickingSource | null | undefined,
184
  batchIndex: number
185
): GPURecordBatchSourceInfo | undefined {
186
  if (!source) {
1!
187
    return undefined;
×
188
  }
189
  if (Array.isArray(source)) {
1!
190
    return source[batchIndex] ?? undefined;
1!
191
  }
192
  if ('batches' in source) {
×
193
    return source.batches[batchIndex]?.sourceInfo;
×
194
  }
195
  const sourceObject = source as {
×
196
    table?: Pick<GPUTable, 'batches'> | null;
197
    sourceInfos?: readonly (GPURecordBatchSourceInfo | null | undefined)[];
198
  };
199
  return (
×
200
    sourceObject.sourceInfos?.[batchIndex] ?? sourceObject.table?.batches[batchIndex]?.sourceInfo
×
201
  );
202
}
203

204
function getArrowBatchRowIndex(
205
  rowIndex: number,
206
  sourceInfo: GPURecordBatchSourceInfo | undefined
207
): number | null {
208
  if (!sourceInfo) {
1!
209
    return null;
×
210
  }
211
  const batchRowIndex = rowIndex - sourceInfo.sourceRowIndexOffset;
1✔
212
  return batchRowIndex >= 0 && batchRowIndex < sourceInfo.sourceRowCount ? batchRowIndex : null;
1!
213
}
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