(index: number, newPoints: ReadonlyArray<DataPoint>)
| 196 | }; |
| 197 | |
| 198 | const appendSeries = (index: number, newPoints: ReadonlyArray<DataPoint>): void => { |
| 199 | assertNotDisposed(); |
| 200 | if (!newPoints || newPoints.length === 0) return; |
| 201 | |
| 202 | const existing = getSeriesEntry(index); |
| 203 | const prevPointCount = existing.pointCount; |
| 204 | const nextPointCount = prevPointCount + newPoints.length; |
| 205 | |
| 206 | const appendPacked = |
| 207 | existing.xOffset === 0 ? packDataPoints(newPoints) : packDataPointsWithXOffset(newPoints, existing.xOffset); |
| 208 | const appendBytes = appendPacked.byteLength; |
| 209 | |
| 210 | // Each point is 2 floats (x, y) = 8 bytes. |
| 211 | const requiredBytes = roundUpToMultipleOf4(nextPointCount * 2 * 4); |
| 212 | const targetBytes = Math.max(MIN_BUFFER_BYTES, requiredBytes); |
| 213 | |
| 214 | let buffer = existing.buffer; |
| 215 | let capacityBytes = existing.capacityBytes; |
| 216 | |
| 217 | // Ensure the CPU-side store is updated regardless of GPU growth path. |
| 218 | const nextData = existing.data; |
| 219 | nextData.push(...newPoints); |
| 220 | |
| 221 | const maxBufferSize = device.limits.maxBufferSize; |
| 222 | |
| 223 | if (targetBytes > capacityBytes) { |
| 224 | if (targetBytes > maxBufferSize) { |
| 225 | throw new Error( |
| 226 | `DataStore.appendSeries(${index}): required buffer size ${targetBytes} exceeds device.limits.maxBufferSize (${maxBufferSize}).` |
| 227 | ); |
| 228 | } |
| 229 | |
| 230 | // Replace buffer (no shrink). This is the slow path; we re-upload the full series. |
| 231 | try { |
| 232 | buffer.destroy(); |
| 233 | } catch { |
| 234 | // Ignore destroy errors; we are replacing the buffer anyway. |
| 235 | } |
| 236 | |
| 237 | const grownCapacityBytes = computeGrownCapacityBytes(capacityBytes, targetBytes); |
| 238 | capacityBytes = grownCapacityBytes > maxBufferSize ? targetBytes : grownCapacityBytes; |
| 239 | |
| 240 | buffer = device.createBuffer({ |
| 241 | size: capacityBytes, |
| 242 | usage: GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, |
| 243 | }); |
| 244 | |
| 245 | const fullPacked = |
| 246 | existing.xOffset === 0 ? packDataPoints(nextData) : packDataPointsWithXOffset(nextData, existing.xOffset); |
| 247 | if (fullPacked.byteLength > 0) { |
| 248 | device.queue.writeBuffer(buffer, 0, fullPacked.buffer); |
| 249 | } |
| 250 | |
| 251 | series.set(index, { |
| 252 | buffer, |
| 253 | capacityBytes, |
| 254 | pointCount: nextPointCount, |
| 255 | hash32: hashFloat32ArrayBits(fullPacked), |
nothing calls this directly
no test coverage detected