(device: GPUDevice, options?: HighlightRendererOptions)
| 60 | 0.2126 * rgba[0] + 0.7152 * rgba[1] + 0.0722 * rgba[2]; |
| 61 | |
| 62 | export function createHighlightRenderer(device: GPUDevice, options?: HighlightRendererOptions): HighlightRenderer { |
| 63 | let disposed = false; |
| 64 | let visible = true; |
| 65 | |
| 66 | const targetFormat = options?.targetFormat ?? DEFAULT_TARGET_FORMAT; |
| 67 | |
| 68 | const bindGroupLayout = device.createBindGroupLayout({ |
| 69 | entries: [{ binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } }], |
| 70 | }); |
| 71 | |
| 72 | // Uniform layout (WGSL): |
| 73 | // center.xy, radius, thickness, color.rgba, outlineColor.rgba |
| 74 | // = 12 floats = 48 bytes |
| 75 | const uniformBuffer = createUniformBuffer(device, 48, { label: 'highlightRenderer/uniforms' }); |
| 76 | |
| 77 | const bindGroup = device.createBindGroup({ |
| 78 | layout: bindGroupLayout, |
| 79 | entries: [{ binding: 0, resource: { buffer: uniformBuffer } }], |
| 80 | }); |
| 81 | |
| 82 | const pipeline = createRenderPipeline(device, { |
| 83 | label: 'highlightRenderer/pipeline', |
| 84 | bindGroupLayouts: [bindGroupLayout], |
| 85 | vertex: { code: highlightWgsl, label: 'highlight.wgsl' }, |
| 86 | fragment: { |
| 87 | code: highlightWgsl, |
| 88 | label: 'highlight.wgsl', |
| 89 | formats: targetFormat, |
| 90 | blend: { |
| 91 | color: { operation: 'add', srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha' }, |
| 92 | alpha: { operation: 'add', srcFactor: 'one', dstFactor: 'one-minus-src-alpha' }, |
| 93 | }, |
| 94 | }, |
| 95 | primitive: { topology: 'triangle-list', cullMode: 'none' }, |
| 96 | multisample: { count: 1 }, |
| 97 | }); |
| 98 | |
| 99 | let lastCanvasWidth = 0; |
| 100 | let lastCanvasHeight = 0; |
| 101 | let lastScissor = { x: 0, y: 0, w: 0, h: 0 }; |
| 102 | let hasPrepared = false; |
| 103 | |
| 104 | const assertNotDisposed = (): void => { |
| 105 | if (disposed) throw new Error('HighlightRenderer is disposed.'); |
| 106 | }; |
| 107 | |
| 108 | const prepare: HighlightRenderer['prepare'] = (point, cssColor, sizeCssPx) => { |
| 109 | assertNotDisposed(); |
| 110 | |
| 111 | if (!Number.isFinite(point.centerDeviceX) || !Number.isFinite(point.centerDeviceY)) { |
| 112 | throw new Error('HighlightRenderer.prepare: point center must be finite.'); |
| 113 | } |
| 114 | if (!Number.isFinite(point.canvasWidth) || !Number.isFinite(point.canvasHeight) || point.canvasWidth <= 0 || point.canvasHeight <= 0) { |
| 115 | throw new Error('HighlightRenderer.prepare: canvasWidth/canvasHeight must be positive finite numbers.'); |
| 116 | } |
| 117 | if (!isFiniteScissor(point.scissor)) { |
| 118 | throw new Error('HighlightRenderer.prepare: scissor must be finite.'); |
| 119 | } |
no test coverage detected