(device: GPUDevice, options?: ScatterRendererOptions)
| 155 | }; |
| 156 | |
| 157 | export function createScatterRenderer(device: GPUDevice, options?: ScatterRendererOptions): ScatterRenderer { |
| 158 | let disposed = false; |
| 159 | const targetFormat = options?.targetFormat ?? DEFAULT_TARGET_FORMAT; |
| 160 | |
| 161 | const bindGroupLayout = device.createBindGroupLayout({ |
| 162 | entries: [ |
| 163 | { binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: 'uniform' } }, |
| 164 | { binding: 1, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } }, |
| 165 | ], |
| 166 | }); |
| 167 | |
| 168 | // VSUniforms: mat4x4 (64) + viewportPx vec2 (8) + pad vec2 (8) = 80 bytes. |
| 169 | const vsUniformBuffer = createUniformBuffer(device, 80, { label: 'scatterRenderer/vsUniforms' }); |
| 170 | const fsUniformBuffer = createUniformBuffer(device, 16, { label: 'scatterRenderer/fsUniforms' }); |
| 171 | |
| 172 | // Reused CPU-side staging for uniform writes (avoid per-frame allocations). |
| 173 | const vsUniformScratchBuffer = new ArrayBuffer(80); |
| 174 | const vsUniformScratchF32 = new Float32Array(vsUniformScratchBuffer); |
| 175 | const fsUniformScratchF32 = new Float32Array(4); |
| 176 | |
| 177 | const bindGroup = device.createBindGroup({ |
| 178 | layout: bindGroupLayout, |
| 179 | entries: [ |
| 180 | { binding: 0, resource: { buffer: vsUniformBuffer } }, |
| 181 | { binding: 1, resource: { buffer: fsUniformBuffer } }, |
| 182 | ], |
| 183 | }); |
| 184 | |
| 185 | const pipeline = createRenderPipeline(device, { |
| 186 | label: 'scatterRenderer/pipeline', |
| 187 | bindGroupLayouts: [bindGroupLayout], |
| 188 | vertex: { |
| 189 | code: scatterWgsl, |
| 190 | label: 'scatter.wgsl', |
| 191 | buffers: [ |
| 192 | { |
| 193 | arrayStride: INSTANCE_STRIDE_BYTES, |
| 194 | stepMode: 'instance', |
| 195 | attributes: [ |
| 196 | { shaderLocation: 0, format: 'float32x2', offset: 0 }, |
| 197 | { shaderLocation: 1, format: 'float32', offset: 8 }, |
| 198 | ], |
| 199 | }, |
| 200 | ], |
| 201 | }, |
| 202 | fragment: { |
| 203 | code: scatterWgsl, |
| 204 | label: 'scatter.wgsl', |
| 205 | formats: targetFormat, |
| 206 | // Standard alpha blending (circle AA uses alpha, and series color may be translucent). |
| 207 | blend: { |
| 208 | color: { operation: 'add', srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha' }, |
| 209 | alpha: { operation: 'add', srcFactor: 'one', dstFactor: 'one-minus-src-alpha' }, |
| 210 | }, |
| 211 | }, |
| 212 | primitive: { topology: 'triangle-list', cullMode: 'none' }, |
| 213 | multisample: { count: 1 }, |
| 214 | }); |
no test coverage detected