(index: number, data: ReadonlyArray<DataPoint>, options?: Readonly<{ xOffset?: number }>)
| 131 | }; |
| 132 | |
| 133 | const setSeries = (index: number, data: ReadonlyArray<DataPoint>, options?: Readonly<{ xOffset?: number }>): void => { |
| 134 | assertNotDisposed(); |
| 135 | |
| 136 | const xOffset = options?.xOffset ?? 0; |
| 137 | const packed = xOffset === 0 ? packDataPoints(data) : packDataPointsWithXOffset(data, xOffset); |
| 138 | const pointCount = data.length; |
| 139 | const hash32 = hashFloat32ArrayBits(packed); |
| 140 | |
| 141 | const requiredBytes = roundUpToMultipleOf4(packed.byteLength); |
| 142 | const targetBytes = Math.max(MIN_BUFFER_BYTES, requiredBytes); |
| 143 | |
| 144 | const existing = series.get(index); |
| 145 | const unchanged = existing && existing.pointCount === pointCount && existing.hash32 === hash32; |
| 146 | if (unchanged) return; |
| 147 | |
| 148 | let buffer = existing?.buffer ?? null; |
| 149 | let capacityBytes = existing?.capacityBytes ?? 0; |
| 150 | |
| 151 | if (!buffer || targetBytes > capacityBytes) { |
| 152 | const maxBufferSize = device.limits.maxBufferSize; |
| 153 | if (targetBytes > maxBufferSize) { |
| 154 | throw new Error( |
| 155 | `DataStore.setSeries(${index}): required buffer size ${targetBytes} exceeds device.limits.maxBufferSize (${maxBufferSize}).` |
| 156 | ); |
| 157 | } |
| 158 | |
| 159 | if (buffer) { |
| 160 | try { |
| 161 | buffer.destroy(); |
| 162 | } catch { |
| 163 | // Ignore destroy errors; we are replacing the buffer anyway. |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | const grownCapacityBytes = computeGrownCapacityBytes(capacityBytes, targetBytes); |
| 168 | if (grownCapacityBytes > maxBufferSize) { |
| 169 | // If geometric growth would exceed the limit, fall back to the exact required size. |
| 170 | // (Still no shrink: if current capacity was already larger, we'd keep it above.) |
| 171 | // NOTE: targetBytes is already checked against maxBufferSize above. |
| 172 | capacityBytes = targetBytes; |
| 173 | } else { |
| 174 | capacityBytes = grownCapacityBytes; |
| 175 | } |
| 176 | |
| 177 | buffer = device.createBuffer({ |
| 178 | size: capacityBytes, |
| 179 | usage: GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, |
| 180 | }); |
| 181 | } |
| 182 | |
| 183 | // Avoid 0-byte writes (empty series). The buffer is still valid for binding. |
| 184 | if (packed.byteLength > 0) { |
| 185 | device.queue.writeBuffer(buffer, 0, packed.buffer); |
| 186 | } |
| 187 | |
| 188 | series.set(index, { |
| 189 | buffer, |
| 190 | capacityBytes, |
nothing calls this directly
no test coverage detected