@jbrowse/render-core 5.0.0-beta.7 → 5.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/RenderCanvas.d.ts +2 -2
- package/esm/RenderLifecycleMixin.d.ts +7 -6
- package/esm/RenderLifecycleMixin.js +7 -6
- package/esm/canvas2dUtils.d.ts +58 -17
- package/esm/canvas2dUtils.js +82 -30
- package/esm/computePipeline.d.ts +4 -3
- package/esm/computePipeline.js +21 -14
- package/esm/coverageBand.d.ts +3 -3
- package/esm/coverageBand.js +2 -2
- package/esm/coverageBandBuffers.d.ts +2 -3
- package/esm/createRenderingBackend.d.ts +1 -1
- package/esm/displayPhase.d.ts +79 -109
- package/esm/displayPhase.js +42 -83
- package/esm/gpuDevice.d.ts +11 -10
- package/esm/gpuDevice.js +10 -11
- package/esm/graphicsCapabilities.d.ts +6 -7
- package/esm/graphicsCapabilities.js +3 -4
- package/esm/hal/createHal.js +1 -1
- package/esm/hal/deviceGpuCache.d.ts +25 -30
- package/esm/hal/deviceGpuCache.js +85 -97
- package/esm/hal/index.d.ts +2 -2
- package/esm/hal/index.js +1 -1
- package/esm/hal/mockHal.d.ts +8 -6
- package/esm/hal/mockHal.js +7 -2
- package/esm/hal/passIds.d.ts +8 -7
- package/esm/hal/passIds.js +11 -10
- package/esm/hal/types.d.ts +28 -12
- package/esm/hal/webgl2Hal.d.ts +11 -3
- package/esm/hal/webgl2Hal.js +116 -145
- package/esm/hal/webgpuHal.d.ts +8 -8
- package/esm/hal/webgpuHal.js +97 -124
- package/esm/instancePass.d.ts +2 -1
- package/esm/mapUploadSync.d.ts +2 -2
- package/esm/mapUploadSync.js +2 -2
- package/esm/marks/barMark.d.ts +6 -3
- package/esm/marks/barMark.js +19 -13
- package/esm/marks/drawAgainstHit.d.ts +58 -39
- package/esm/marks/drawAgainstHit.js +136 -96
- package/esm/marks/glyphPaint.d.ts +14 -2
- package/esm/marks/glyphPaint.js +16 -2
- package/esm/marks/index.d.ts +4 -2
- package/esm/marks/index.js +3 -2
- package/esm/marks/markBackend.d.ts +2 -2
- package/esm/marks/markBackend.js +2 -2
- package/esm/marks/markHit.d.ts +2 -2
- package/esm/marks/markHit.js +2 -2
- package/esm/marks/markPlan.d.ts +2 -2
- package/esm/marks/markPlan.js +2 -2
- package/esm/marks/markRamp.d.ts +1 -3
- package/esm/marks/markRamp.js +1 -5
- package/esm/marks/nearestMarkHit.d.ts +49 -0
- package/esm/marks/nearestMarkHit.js +126 -0
- package/esm/marks/pointMark.d.ts +9 -2
- package/esm/marks/pointMark.js +36 -31
- package/esm/marks/rowLane.d.ts +17 -0
- package/esm/marks/rowLane.js +11 -0
- package/esm/marks/spanMark.d.ts +6 -26
- package/esm/marks/spanMark.js +34 -31
- package/esm/marks/types.d.ts +51 -181
- package/esm/marks/types.js +91 -131
- package/esm/namedReactions.d.ts +3 -4
- package/esm/namedReactions.js +3 -4
- package/esm/scoreScale.d.ts +20 -0
- package/esm/scoreScale.js +77 -0
- package/esm/shaders/barMark.generated.d.ts +2 -3
- package/esm/shaders/barMark.generated.js +4 -3
- package/esm/shaders/barMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/barMark.glsl.generated.js +4 -0
- package/esm/shaders/barMark.iface.generated.d.ts +12 -6
- package/esm/shaders/barMark.iface.generated.js +27 -15
- package/esm/shaders/barMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/barMark.wgsl.generated.js +3 -0
- package/esm/shaders/coverageBar.generated.d.ts +2 -3
- package/esm/shaders/coverageBar.generated.js +4 -3
- package/esm/shaders/coverageBar.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageBar.glsl.generated.js +4 -0
- package/esm/shaders/coverageBar.iface.generated.js +1 -1
- package/esm/shaders/coverageBar.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageBar.wgsl.generated.js +3 -0
- package/esm/shaders/coverageIndicator.generated.d.ts +2 -3
- package/esm/shaders/coverageIndicator.generated.js +4 -3
- package/esm/shaders/coverageIndicator.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageIndicator.glsl.generated.js +4 -0
- package/esm/shaders/coverageIndicator.iface.generated.js +1 -1
- package/esm/shaders/coverageIndicator.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageIndicator.wgsl.generated.js +3 -0
- package/esm/shaders/coverageInterbase.generated.d.ts +2 -3
- package/esm/shaders/coverageInterbase.generated.js +4 -3
- package/esm/shaders/coverageInterbase.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageInterbase.glsl.generated.js +4 -0
- package/esm/shaders/coverageInterbase.iface.generated.js +1 -1
- package/esm/shaders/coverageInterbase.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageInterbase.wgsl.generated.js +3 -0
- package/esm/shaders/coverageMod.generated.d.ts +2 -3
- package/esm/shaders/coverageMod.generated.js +4 -3
- package/esm/shaders/coverageMod.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageMod.glsl.generated.js +4 -0
- package/esm/shaders/coverageMod.iface.generated.js +1 -1
- package/esm/shaders/coverageMod.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageMod.wgsl.generated.js +3 -0
- package/esm/shaders/coverageSnp.generated.d.ts +2 -3
- package/esm/shaders/coverageSnp.generated.js +4 -3
- package/esm/shaders/coverageSnp.glsl.generated.d.ts +2 -0
- package/esm/shaders/coverageSnp.glsl.generated.js +4 -0
- package/esm/shaders/coverageSnp.iface.generated.js +1 -1
- package/esm/shaders/coverageSnp.wgsl.generated.d.ts +1 -0
- package/esm/shaders/coverageSnp.wgsl.generated.js +3 -0
- package/esm/shaders/pointMark.generated.d.ts +2 -3
- package/esm/shaders/pointMark.generated.js +4 -3
- package/esm/shaders/pointMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/pointMark.glsl.generated.js +4 -0
- package/esm/shaders/pointMark.iface.generated.d.ts +11 -3
- package/esm/shaders/pointMark.iface.generated.js +23 -9
- package/esm/shaders/pointMark.js.generated.d.ts +1 -1
- package/esm/shaders/pointMark.js.generated.js +51 -29
- package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/pointMark.wgsl.generated.js +3 -0
- package/esm/shaders/scoreScale.js.generated.js +46 -22
- package/esm/shaders/spanMark.generated.d.ts +2 -3
- package/esm/shaders/spanMark.generated.js +4 -3
- package/esm/shaders/spanMark.glsl.generated.d.ts +2 -0
- package/esm/shaders/spanMark.glsl.generated.js +4 -0
- package/esm/shaders/spanMark.iface.generated.js +1 -1
- package/esm/shaders/spanMark.wgsl.generated.d.ts +1 -0
- package/esm/shaders/spanMark.wgsl.generated.js +3 -0
- package/esm/slangPass.d.ts +9 -10
- package/esm/slangPass.js +9 -3
- package/esm/useGraphicsCapabilities.d.ts +1 -1
- package/esm/useGraphicsCapabilities.js +1 -1
- package/esm/webgpuUtils.d.ts +3 -11
- package/esm/webgpuUtils.js +12 -33
- package/package.json +3 -2
- package/src/shaders/barMark.generated.ts +6 -5
- package/src/shaders/barMark.glsl.generated.ts +6 -0
- package/src/shaders/barMark.iface.generated.ts +31 -15
- package/src/shaders/barMark.slang +11 -3
- package/src/shaders/barMark.wgsl.generated.ts +4 -0
- package/src/shaders/colorPack.slang +3 -2
- package/src/shaders/coverageBand.slang +5 -11
- package/src/shaders/coverageBar.generated.ts +6 -5
- package/src/shaders/coverageBar.glsl.generated.ts +6 -0
- package/src/shaders/coverageBar.iface.generated.ts +1 -1
- package/src/shaders/coverageBar.slang +1 -1
- package/src/shaders/coverageBar.wgsl.generated.ts +4 -0
- package/src/shaders/coverageIndicator.generated.ts +6 -5
- package/src/shaders/coverageIndicator.glsl.generated.ts +6 -0
- package/src/shaders/coverageIndicator.iface.generated.ts +1 -1
- package/src/shaders/coverageIndicator.wgsl.generated.ts +4 -0
- package/src/shaders/coverageInterbase.generated.ts +6 -5
- package/src/shaders/coverageInterbase.glsl.generated.ts +6 -0
- package/src/shaders/coverageInterbase.iface.generated.ts +1 -1
- package/src/shaders/coverageInterbase.wgsl.generated.ts +4 -0
- package/src/shaders/coverageMod.generated.ts +6 -5
- package/src/shaders/coverageMod.glsl.generated.ts +6 -0
- package/src/shaders/coverageMod.iface.generated.ts +1 -1
- package/src/shaders/coverageMod.wgsl.generated.ts +4 -0
- package/src/shaders/coverageSnp.generated.ts +6 -5
- package/src/shaders/coverageSnp.glsl.generated.ts +6 -0
- package/src/shaders/coverageSnp.iface.generated.ts +1 -1
- package/src/shaders/coverageSnp.wgsl.generated.ts +4 -0
- package/src/shaders/hpmath.slang +18 -6
- package/src/shaders/pointMark.generated.ts +6 -6
- package/src/shaders/pointMark.glsl.generated.ts +6 -0
- package/src/shaders/pointMark.iface.generated.ts +28 -9
- package/src/shaders/pointMark.js.generated.ts +48 -29
- package/src/shaders/pointMark.slang +18 -5
- package/src/shaders/pointMark.wgsl.generated.ts +4 -0
- package/src/shaders/scoreScale.js.generated.ts +43 -22
- package/src/shaders/scoreScale.slang +37 -53
- package/src/shaders/spanMark.generated.ts +6 -5
- package/src/shaders/spanMark.glsl.generated.ts +6 -0
- package/src/shaders/spanMark.iface.generated.ts +1 -1
- package/src/shaders/spanMark.wgsl.generated.ts +4 -0
- package/src/shaders/valueScale.slang +8 -45
package/esm/hal/webgpuHal.js
CHANGED
|
@@ -3,8 +3,8 @@ import { syncCanvasSize } from '../canvas2dUtils.js';
|
|
|
3
3
|
import { canvasConfiguredBy, canvasContextError, noteCanvasConfigured, noteCanvasContext, } from '../canvasContext.js';
|
|
4
4
|
import { createGpuSurfaceLostError } from '../gpuContextLostError.js';
|
|
5
5
|
import { getGpuDevice } from '../gpuDevice.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { createVertexBuffer } from '../webgpuUtils.js';
|
|
7
|
+
import { bindGroupLayoutEntries, getOrBuildPipeline, getPassLayout, pipelineRecipe, } from './deviceGpuCache.js';
|
|
8
8
|
import { GpuHalBase } from './gpuHalBase.js';
|
|
9
9
|
class ShaderCompileError extends Error {
|
|
10
10
|
constructor(passId, details) {
|
|
@@ -13,22 +13,16 @@ class ShaderCompileError extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
// Maximum number of writeUniforms() calls per frame. Each call occupies one
|
|
16
|
-
// aligned slot in the uniform ring
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// minimum alignment pays 512 KB each side; alignments' 640-byte uniform aligns
|
|
22
|
-
// to 768, so 1.5 MiB each side, per track. A frame is measured using 2-4 of
|
|
23
|
-
// those slots, which is what makes the count above the oversized term rather
|
|
24
|
-
// than any struct:
|
|
25
|
-
// agent-docs/ideas/size-the-uniform-ring-to-its-measured-occupancy.md.
|
|
16
|
+
// aligned slot in the uniform ring, allocated eagerly per display as a GPU
|
|
17
|
+
// buffer and a CPU staging array. A frame is measured using 2-4 slots, so the
|
|
18
|
+
// count rather than any struct is the oversized term:
|
|
19
|
+
// agent-docs/reference/ARCHITECTURAL_LIMITS.md §"The uniform ring is allocated
|
|
20
|
+
// at 2048 slots".
|
|
26
21
|
//
|
|
27
22
|
// Exhausting it does not throw: the write is dropped and its draws render
|
|
28
|
-
// against
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
// again.
|
|
23
|
+
// against another batch's uniforms. If we ever hit the cap, grow the buffer and
|
|
24
|
+
// recreate every bind group in `passBindGroups` rather than bumping the
|
|
25
|
+
// constant again.
|
|
32
26
|
const MAX_UNIFORM_SLOTS = 2048;
|
|
33
27
|
// Warn while there is still headroom, because the cap itself is not a place to
|
|
34
28
|
// find out. A renderer's per-frame write count is rarely one number: alignments
|
|
@@ -38,58 +32,22 @@ const MAX_UNIFORM_SLOTS = 2048;
|
|
|
38
32
|
// the "~50 writes/frame" this file used to claim, and nothing reported the
|
|
39
33
|
// difference between 50 and 1900.
|
|
40
34
|
const UNIFORM_SLOT_WARN_AT = MAX_UNIFORM_SLOTS / 2;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// BlendOperation::Max"). Same blend as webgl2Hal's bare glBlendEquation(MAX).
|
|
46
|
-
const max = { srcFactor: 'one', dstFactor: 'one', operation: 'max' };
|
|
47
|
-
// Otherwise RGB uses the caller-supplied factors and alpha always accumulates
|
|
48
|
-
// through ONE / ONE_MINUS_SRC_ALPHA (matches webgl2Hal.applyBlendState).
|
|
49
|
-
return bs.op === 'max'
|
|
50
|
-
? { color: max, alpha: max }
|
|
51
|
-
: {
|
|
52
|
-
color: { srcFactor: bs.srcFactor, dstFactor: bs.dstFactor },
|
|
53
|
-
alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha' },
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
// Every pass reads per-instance data from a vertex buffer (no storage binding
|
|
57
|
-
// at 0) because Slang-generated shaders cross-compile to GLSL ES, which has no
|
|
58
|
-
// SSBOs. The two @group(0) layouts a pass is built against — uniform-only, and
|
|
59
|
-
// uniform + texture + sampler — belong to the device, not to this HAL, and live
|
|
60
|
-
// in `deviceGpuCache.ts` with the pipelines.
|
|
61
|
-
async function buildPipeline(device, desc, layouts, sampleCount) {
|
|
62
|
-
const module = device.createShaderModule({ code: desc.wgslSource });
|
|
35
|
+
// Every pass reads per-instance data from a vertex buffer, because Slang's
|
|
36
|
+
// GLSL ES target has no SSBOs.
|
|
37
|
+
async function buildPipeline(device, recipe, passId) {
|
|
38
|
+
const module = device.createShaderModule({ code: recipe.wgslSource });
|
|
63
39
|
const info = await module.getCompilationInfo();
|
|
64
40
|
const errors = info.messages.filter(m => m.type === 'error');
|
|
65
41
|
if (errors.length > 0) {
|
|
66
42
|
const details = errors
|
|
67
43
|
.map(m => `line ${m.lineNum}: ${m.message}`)
|
|
68
44
|
.join('; ');
|
|
69
|
-
throw new ShaderCompileError(
|
|
70
|
-
}
|
|
71
|
-
const blend =
|
|
72
|
-
? desc.blendState
|
|
73
|
-
? gpuBlendState(desc.blendState)
|
|
74
|
-
: STANDARD_BLEND_STATE
|
|
75
|
-
: undefined;
|
|
76
|
-
// Every pass feeds @location(N) inputs from a bound vertex buffer.
|
|
77
|
-
const vertexBuffers = [
|
|
78
|
-
{
|
|
79
|
-
arrayStride: desc.instanceStride,
|
|
80
|
-
stepMode: 'instance',
|
|
81
|
-
attributes: desc.vertexAttributes.map((attr, i) => ({
|
|
82
|
-
shaderLocation: i,
|
|
83
|
-
offset: attr.offsetBytes,
|
|
84
|
-
format: toGpuVertexFormat(attr),
|
|
85
|
-
})),
|
|
86
|
-
},
|
|
87
|
-
];
|
|
45
|
+
throw new ShaderCompileError(passId, details);
|
|
46
|
+
}
|
|
47
|
+
const { blend } = recipe;
|
|
88
48
|
return device.createRenderPipelineAsync({
|
|
89
|
-
layout:
|
|
90
|
-
|
|
91
|
-
: layouts.uniformOnlyPipelineLayout,
|
|
92
|
-
vertex: { module, entryPoint: 'vs_main', buffers: vertexBuffers },
|
|
49
|
+
layout: getPassLayout(device, recipe.bindGroupLayout).pipelineLayout,
|
|
50
|
+
vertex: { module, entryPoint: 'vs_main', buffers: [recipe.vertexBuffer] },
|
|
93
51
|
fragment: {
|
|
94
52
|
module,
|
|
95
53
|
entryPoint: 'fs_main',
|
|
@@ -100,17 +58,18 @@ async function buildPipeline(device, desc, layouts, sampleCount) {
|
|
|
100
58
|
},
|
|
101
59
|
],
|
|
102
60
|
},
|
|
103
|
-
primitive: { topology:
|
|
104
|
-
multisample: sampleCount === 1 ? undefined : { count: sampleCount },
|
|
61
|
+
primitive: { topology: recipe.topology },
|
|
62
|
+
multisample: recipe.sampleCount === 1 ? undefined : { count: recipe.sampleCount },
|
|
105
63
|
});
|
|
106
64
|
}
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
// (`getPass`) and this side does not; see ARCHITECTURAL_LIMITS.md §"Every
|
|
111
|
-
// WebGPU display resolves its whole pass list before it can paint".
|
|
65
|
+
// Every declared pass up front, unlike WebGL2's first-draw link; see
|
|
66
|
+
// ARCHITECTURAL_LIMITS.md §"Every WebGPU display resolves its whole pass list
|
|
67
|
+
// before it can paint". Each pass compiles as soon as its own WGSL arrives.
|
|
112
68
|
async function resolvePipelines(device, descriptors, sampleCount) {
|
|
113
|
-
const built = await Promise.all(descriptors.map(
|
|
69
|
+
const built = await Promise.all(descriptors.map(async (desc) => {
|
|
70
|
+
const { WGSL_SOURCE } = await desc.source.wgsl();
|
|
71
|
+
return getOrBuildPipeline(device, pipelineRecipe(desc, WGSL_SOURCE, sampleCount), recipe => buildPipeline(device, recipe, desc.id));
|
|
72
|
+
}));
|
|
114
73
|
return new Map(descriptors.map((desc, i) => [desc.id, built[i]]));
|
|
115
74
|
}
|
|
116
75
|
// Behavioral parity with WebGL2Hal is enforced by tests, not by this file:
|
|
@@ -128,14 +87,13 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
128
87
|
context;
|
|
129
88
|
pipelines;
|
|
130
89
|
passTextures = new Map();
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
90
|
+
// Every pass's bind group, by the pass drawn. A pass that binds no texture
|
|
91
|
+
// has one from construction, shared with each pass over the same layout; a
|
|
92
|
+
// textured pass gets its own when its texture arrives, dropped when
|
|
93
|
+
// `uploadTexture` replaces the texture. The layouts are the device's, shared
|
|
94
|
+
// with every display on it; the groups are this HAL's, since they name its
|
|
95
|
+
// uniform ring buffer.
|
|
134
96
|
passBindGroups = new Map();
|
|
135
|
-
// The device's layouts, not this HAL's — shared with every other display on
|
|
136
|
-
// the same device. The bind GROUPS above stay per-HAL: they reference this
|
|
137
|
-
// HAL's uniform ring buffer.
|
|
138
|
-
layouts;
|
|
139
97
|
// Uniform ring buffer: holds up to MAX_UNIFORM_SLOTS sets of uniforms so
|
|
140
98
|
// that all draw calls in a frame can reference different uniform data via
|
|
141
99
|
// dynamic offsets, enabling a single command encoder + submit per frame.
|
|
@@ -147,10 +105,6 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
147
105
|
// console the developer stops reading, and the fact is about the renderer
|
|
148
106
|
// rather than about this frame.
|
|
149
107
|
warnedUniformSlots = false;
|
|
150
|
-
// Shared bind group for every uniform-only pass — only references
|
|
151
|
-
// `uniformRingBuffer` (via dynamic offset at draw time), so one instance
|
|
152
|
-
// serves all passes/regions instead of allocating a fresh one per upload.
|
|
153
|
-
uniformOnlyBindGroup;
|
|
154
108
|
// Samples per pixel this display renders at, stated by whoever built it (see
|
|
155
109
|
// `RenderingBackendOptions.sampleCount`). Every render-pass, texture and
|
|
156
110
|
// pipeline decision below reads it, so the two shapes cannot mismatch — and
|
|
@@ -178,7 +132,7 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
178
132
|
* selection spanning two sections re-uploads `OVERLAY_REGION` once per
|
|
179
133
|
* section, inside the block loop.
|
|
180
134
|
*
|
|
181
|
-
* Deferring
|
|
135
|
+
* Deferring the destroy makes mid-frame replacement legal instead of merely
|
|
182
136
|
* warned about; synteny's `ensureUploaded` has always deleted a pass mid-frame
|
|
183
137
|
* that the open pass never referenced, and that case was never the bug.
|
|
184
138
|
*/
|
|
@@ -200,13 +154,12 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
200
154
|
warnedSwapChainLoss = false;
|
|
201
155
|
swapChainUnrecoverable = false;
|
|
202
156
|
reportedSurfaceFailure = false;
|
|
203
|
-
constructor(device, canvas, context, descriptors, sampleCount, pipelines
|
|
157
|
+
constructor(device, canvas, context, descriptors, sampleCount, pipelines) {
|
|
204
158
|
super(descriptors, 'WebGPUHal');
|
|
205
159
|
this.device = device;
|
|
206
160
|
this.canvas = canvas;
|
|
207
161
|
this.context = context;
|
|
208
162
|
this.pipelines = pipelines;
|
|
209
|
-
this.layouts = layouts;
|
|
210
163
|
this.sampleCount = sampleCount;
|
|
211
164
|
// Align uniform slots to device requirements for dynamic offsets
|
|
212
165
|
const alignment = device.limits.minUniformBufferOffsetAlignment;
|
|
@@ -218,9 +171,51 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
218
171
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
219
172
|
});
|
|
220
173
|
this.uniformStaging = new Uint8Array(ringSize);
|
|
221
|
-
|
|
174
|
+
const byLayout = new Map();
|
|
175
|
+
for (const desc of descriptors) {
|
|
176
|
+
if (!desc.bindings.some(b => b.kind === 'texture')) {
|
|
177
|
+
const layout = this.bindGroupLayoutOf(desc.bindings);
|
|
178
|
+
let group = byLayout.get(layout);
|
|
179
|
+
if (!group) {
|
|
180
|
+
group = this.createBindGroup(desc.bindings, layout);
|
|
181
|
+
byLayout.set(layout, group);
|
|
182
|
+
}
|
|
183
|
+
this.passBindGroups.set(desc.id, group);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
222
186
|
this.configureContext();
|
|
223
187
|
}
|
|
188
|
+
bindGroupLayoutOf(bindings) {
|
|
189
|
+
return getPassLayout(this.device, bindGroupLayoutEntries(bindings))
|
|
190
|
+
.bindGroupLayout;
|
|
191
|
+
}
|
|
192
|
+
// One entry per binding in the pass's table, the same table its layout was
|
|
193
|
+
// built from, so the group cannot name a binding the layout lacks.
|
|
194
|
+
createBindGroup(bindings, layout, texture) {
|
|
195
|
+
return this.device.createBindGroup({
|
|
196
|
+
layout,
|
|
197
|
+
entries: bindings.map(b => ({
|
|
198
|
+
binding: b.index,
|
|
199
|
+
resource: this.bindingResource(b, texture),
|
|
200
|
+
})),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
bindingResource(b, texture) {
|
|
204
|
+
if (b.kind === 'uniform') {
|
|
205
|
+
return {
|
|
206
|
+
buffer: this.uniformRingBuffer,
|
|
207
|
+
offset: 0,
|
|
208
|
+
size: this.alignedUniformSize,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (texture && b.kind === 'texture') {
|
|
212
|
+
return texture.texture.createView();
|
|
213
|
+
}
|
|
214
|
+
if (texture && b.kind === 'sampler') {
|
|
215
|
+
return texture.sampler;
|
|
216
|
+
}
|
|
217
|
+
throw new Error(`[WebGPUHal] nothing to bind for the ${b.kind} '${b.name}' at binding ${b.index}`);
|
|
218
|
+
}
|
|
224
219
|
limits() {
|
|
225
220
|
const { maxBufferSize, maxTextureDimension2D } = this.device.limits;
|
|
226
221
|
return {
|
|
@@ -256,11 +251,10 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
256
251
|
return null;
|
|
257
252
|
}
|
|
258
253
|
// Resolve pipelines BEFORE acquiring the canvas's webgpu context. A canvas's
|
|
259
|
-
// context type is permanent once acquired, so if shader
|
|
260
|
-
// here the canvas stays pristine and createGpuHal's WebGL2 fallback
|
|
261
|
-
// still claim it — otherwise a partial WebGPU init would drop us all the
|
|
262
|
-
// to Canvas2D on a WebGL2-capable machine.
|
|
263
|
-
const layouts = getDeviceLayouts(device);
|
|
254
|
+
// context type is permanent once acquired, so if a shader fails to load or
|
|
255
|
+
// compile here the canvas stays pristine and createGpuHal's WebGL2 fallback
|
|
256
|
+
// can still claim it — otherwise a partial WebGPU init would drop us all the
|
|
257
|
+
// way to Canvas2D on a WebGL2-capable machine.
|
|
264
258
|
const pipelines = await resolvePipelines(device, descriptors, sampleCount);
|
|
265
259
|
const context = canvas.getContext('webgpu');
|
|
266
260
|
if (!context) {
|
|
@@ -271,7 +265,7 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
271
265
|
throw canvasContextError(canvas, 'webgpu');
|
|
272
266
|
}
|
|
273
267
|
noteCanvasContext(canvas, 'webgpu');
|
|
274
|
-
return new WebGPUHal(device, canvas, context, descriptors, sampleCount, pipelines
|
|
268
|
+
return new WebGPUHal(device, canvas, context, descriptors, sampleCount, pipelines);
|
|
275
269
|
}
|
|
276
270
|
resize(width, height) {
|
|
277
271
|
const { changed, scale } = syncCanvasSize(this.canvas, width, height);
|
|
@@ -314,43 +308,22 @@ export class WebGPUHal extends GpuHalBase {
|
|
|
314
308
|
* (and a dropped draw) the moment one side samples a texture.
|
|
315
309
|
*
|
|
316
310
|
* Nothing in a bind group varies per region either: it references the
|
|
317
|
-
* HAL-wide uniform ring buffer plus the pass's own texture/sampler.
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* Returns undefined when a pass needs a texture that hasn't arrived yet;
|
|
321
|
-
* drawPass skips those.
|
|
311
|
+
* HAL-wide uniform ring buffer plus the pass's own texture/sampler. Returns
|
|
312
|
+
* undefined when a pass needs a texture that hasn't arrived yet; drawPass
|
|
313
|
+
* skips those.
|
|
322
314
|
*/
|
|
323
315
|
getBindGroup(passId) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
layout: this.layouts.texturedBindGroupLayout,
|
|
332
|
-
entries: [
|
|
333
|
-
{
|
|
334
|
-
binding: 1,
|
|
335
|
-
resource: {
|
|
336
|
-
buffer: this.uniformRingBuffer,
|
|
337
|
-
offset: 0,
|
|
338
|
-
size: this.alignedUniformSize,
|
|
339
|
-
},
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
binding: tb.textureBinding,
|
|
343
|
-
resource: texState.texture.createView(),
|
|
344
|
-
},
|
|
345
|
-
{ binding: tb.samplerBinding, resource: texState.sampler },
|
|
346
|
-
],
|
|
347
|
-
});
|
|
348
|
-
this.passBindGroups.set(passId, bindGroup);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return bindGroup;
|
|
316
|
+
return this.passBindGroups.get(passId) ?? this.bindTexture(passId);
|
|
317
|
+
}
|
|
318
|
+
bindTexture(passId) {
|
|
319
|
+
const texture = this.passTextures.get(passId);
|
|
320
|
+
const desc = this.descriptors.get(passId);
|
|
321
|
+
if (!texture || !desc) {
|
|
322
|
+
return undefined;
|
|
352
323
|
}
|
|
353
|
-
|
|
324
|
+
const group = this.createBindGroup(desc.bindings, this.bindGroupLayoutOf(desc.bindings), texture);
|
|
325
|
+
this.passBindGroups.set(passId, group);
|
|
326
|
+
return group;
|
|
354
327
|
}
|
|
355
328
|
/**
|
|
356
329
|
* The one place a GPU resource this HAL owns is released — see
|
package/esm/instancePass.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ import type { GpuHal, PipelineDescriptor } from './hal';
|
|
|
5
5
|
* A pass id, the packer that fills its buffer, and the upload that joins them
|
|
6
6
|
* are three statements of one fact, and renderers kept them in three places —
|
|
7
7
|
* so a pass could be registered and drawn while nothing uploaded to it, which
|
|
8
|
-
* paints nothing,
|
|
8
|
+
* paints nothing, on the GPU backend only. `InstancePass`
|
|
9
|
+
* makes them one object:
|
|
9
10
|
*
|
|
10
11
|
* ```ts
|
|
11
12
|
* export const GAP_PASS = {
|
package/esm/mapUploadSync.d.ts
CHANGED
|
@@ -9,8 +9,8 @@ export interface MapUploadTarget<K, T, B> {
|
|
|
9
9
|
* re-uploads), and re-uploads everything when the backend identity changes —
|
|
10
10
|
* a context-loss recovery hands a fresh backend with empty GPU buffers.
|
|
11
11
|
*
|
|
12
|
-
* Returns whether anything reached the backend,
|
|
13
|
-
*
|
|
12
|
+
* Returns whether anything reached the backend, so the upload autorun can skip
|
|
13
|
+
* the redraw it would otherwise force after every run.
|
|
14
14
|
*
|
|
15
15
|
* For the skip to fire, the map must keep **stable references** for unchanged
|
|
16
16
|
* entries across recomputes. With an always-fresh map every entry re-uploads,
|
package/esm/mapUploadSync.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* re-uploads), and re-uploads everything when the backend identity changes —
|
|
6
6
|
* a context-loss recovery hands a fresh backend with empty GPU buffers.
|
|
7
7
|
*
|
|
8
|
-
* Returns whether anything reached the backend,
|
|
9
|
-
*
|
|
8
|
+
* Returns whether anything reached the backend, so the upload autorun can skip
|
|
9
|
+
* the redraw it would otherwise force after every run.
|
|
10
10
|
*
|
|
11
11
|
* For the skip to fire, the map must keep **stable references** for unchanged
|
|
12
12
|
* entries across recomputes. With an always-fresh map every entry re-uploads,
|
package/esm/marks/barMark.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { ColorChannel } from './markRamp.ts';
|
|
2
|
+
import type { RowChannel, RowParams } from './rowLane.ts';
|
|
2
3
|
import type { MarkRamp, MarkShape, MarkValueScaleType } from './types.ts';
|
|
3
4
|
/**
|
|
4
5
|
* The `bar` shape's channels: a rectangle from `x` to `x2` standing between
|
|
5
|
-
* the baseline and `y` on the value scale
|
|
6
|
+
* the baseline and `y` on the value scale, in the band of its `row`.
|
|
6
7
|
*/
|
|
7
|
-
export interface BarChannels extends ColorChannel {
|
|
8
|
+
export interface BarChannels extends ColorChannel, RowChannel {
|
|
8
9
|
x: Uint32Array;
|
|
9
10
|
x2: Uint32Array;
|
|
10
11
|
y: Float32Array;
|
|
11
12
|
count: number;
|
|
12
13
|
}
|
|
13
|
-
export interface BarParams {
|
|
14
|
+
export interface BarParams extends RowParams {
|
|
14
15
|
/** `[min, max]` `y` and `origin` are read through. */
|
|
15
16
|
domain: [number, number];
|
|
16
17
|
/** How that domain is read; linear when absent. */
|
|
@@ -24,5 +25,7 @@ export interface BarParams {
|
|
|
24
25
|
* a no-op for a caller whose bars tile.
|
|
25
26
|
*/
|
|
26
27
|
minWidthPx: number;
|
|
28
|
+
/** CSS px the painter alone adds to the drawn width; see `SpanParams.seamPx`. */
|
|
29
|
+
seamPx: number;
|
|
27
30
|
}
|
|
28
31
|
export declare const barMark: MarkShape<BarChannels, BarParams>;
|
package/esm/marks/barMark.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { bpRangeXTuple } from '../blockClipUtils.js';
|
|
2
2
|
import { getDpr, makeBpMapper, spanLeft } from '../canvas2dUtils.js';
|
|
3
|
+
import { scaleTypeCode } from '../scoreScale.js';
|
|
3
4
|
import * as shader from '../shaders/barMark.generated.js';
|
|
4
5
|
import { valueToYPxScaled } from '../shaders/pointMark.js.generated.js';
|
|
5
6
|
import { slangPass } from '../slangPass.js';
|
|
6
7
|
import { makeAbgrFill } from './colorFill.js';
|
|
7
|
-
import { colorBits, paintColors, rampUniforms
|
|
8
|
-
import {
|
|
8
|
+
import { colorBits, paintColors, rampUniforms } from './markRamp.js';
|
|
9
|
+
import { valueWindow } from './nearestMarkHit.js';
|
|
10
|
+
import { bandHeightPx, bandTopPx, rowLane } from './rowLane.js';
|
|
9
11
|
// The rect one instance paints, in the frame's CSS px, or undefined for a bar
|
|
10
12
|
// with no height — which draws nothing and so cannot be hovered.
|
|
11
|
-
function barRect(bpToPx, x, x2, y,
|
|
13
|
+
function barRect(bpToPx, x, x2, y, bandTop, band, params) {
|
|
12
14
|
const xa = bpToPx(x);
|
|
13
15
|
const xb = bpToPx(x2);
|
|
14
16
|
const width = Math.max(params.minWidthPx, Math.abs(xb - xa));
|
|
15
17
|
const [domainMin, domainMax] = params.domain;
|
|
16
|
-
const st =
|
|
17
|
-
const valueY = valueToYPxScaled(y, domainMin, domainMax,
|
|
18
|
-
const originY = valueToYPxScaled(params.origin, domainMin, domainMax,
|
|
18
|
+
const st = scaleTypeCode(params.scaleType);
|
|
19
|
+
const valueY = bandTop + valueToYPxScaled(y, domainMin, domainMax, band, st);
|
|
20
|
+
const originY = bandTop + valueToYPxScaled(params.origin, domainMin, domainMax, band, st);
|
|
19
21
|
const top = Math.min(valueY, originY);
|
|
20
22
|
const height = Math.abs(valueY - originY);
|
|
21
23
|
return height === 0
|
|
@@ -26,7 +28,7 @@ export const barMark = {
|
|
|
26
28
|
id: 'bar',
|
|
27
29
|
pass: {
|
|
28
30
|
...slangPass({ id: 'bar', mod: shader }),
|
|
29
|
-
pack: c => shader.packInstances({ ...c, color: colorBits(c) }, c.count),
|
|
31
|
+
pack: c => shader.packInstances({ ...c, color: colorBits(c), row: rowLane(c.row, c.count) }, c.count),
|
|
30
32
|
},
|
|
31
33
|
writeUniforms(scratch, clip, block, frame, params) {
|
|
32
34
|
shader.writeUniforms(scratch, {
|
|
@@ -34,9 +36,10 @@ export const barMark = {
|
|
|
34
36
|
canvasHeight: frame.canvasHeight,
|
|
35
37
|
domainMin: params.domain[0],
|
|
36
38
|
domainMax: params.domain[1],
|
|
37
|
-
valueScaleType:
|
|
39
|
+
valueScaleType: scaleTypeCode(params.scaleType),
|
|
38
40
|
...rampUniforms(params.ramp),
|
|
39
41
|
origin: params.origin,
|
|
42
|
+
rowHeight: bandHeightPx(params, frame.canvasHeight),
|
|
40
43
|
zero: 0,
|
|
41
44
|
minCellDenomPx: clip.scissorW,
|
|
42
45
|
minWidthPx: params.minWidthPx,
|
|
@@ -44,20 +47,23 @@ export const barMark = {
|
|
|
44
47
|
});
|
|
45
48
|
},
|
|
46
49
|
paintBlock(ctx, channels, block, frame, params) {
|
|
47
|
-
const { x, x2, y, count } = channels;
|
|
50
|
+
const { x, x2, y, row, count } = channels;
|
|
48
51
|
const color = paintColors(channels, count, params.ramp);
|
|
49
52
|
const bpToPx = makeBpMapper(block);
|
|
50
53
|
const setFill = makeAbgrFill(ctx);
|
|
54
|
+
const band = bandHeightPx(params, frame.canvasHeight);
|
|
51
55
|
for (let i = 0; i < count; i++) {
|
|
52
|
-
const r = barRect(bpToPx, x[i], x2[i], y[i],
|
|
56
|
+
const r = barRect(bpToPx, x[i], x2[i], y[i], bandTopPx(row, i, band), band, params);
|
|
53
57
|
if (r) {
|
|
54
58
|
setFill(color[i]);
|
|
55
|
-
ctx.fillRect(r.left, r.top, r.width, r.height);
|
|
59
|
+
ctx.fillRect(r.left, r.top, r.width + params.seamPx, r.height);
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
62
|
},
|
|
59
63
|
ink(channels, block, frame, params, i) {
|
|
60
|
-
const { x, x2, y } = channels;
|
|
61
|
-
|
|
64
|
+
const { x, x2, y, row } = channels;
|
|
65
|
+
const band = bandHeightPx(params, frame.canvasHeight);
|
|
66
|
+
return barRect(makeBpMapper(block), x[i], x2[i], y[i], bandTopPx(row, i, band), band, params);
|
|
62
67
|
},
|
|
68
|
+
valueWindow,
|
|
63
69
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RenderBlock } from '../renderBlock.ts';
|
|
2
|
-
import type { MarkContext2D, MarkFrame
|
|
2
|
+
import type { Mark, MarkContext2D, MarkFrame } from './types.ts';
|
|
3
3
|
export interface RecordedRect {
|
|
4
4
|
x: number;
|
|
5
5
|
y: number;
|
|
@@ -12,8 +12,8 @@ export interface RecordedRect {
|
|
|
12
12
|
* in CANVAS coordinates — `fillRect` and path `rect` alike, so a shape that
|
|
13
13
|
* batches by colour records the same list as one that fills per instance. A
|
|
14
14
|
* path traced with `moveTo`/`lineTo`/`bezierCurveTo` and filled records its
|
|
15
|
-
* bounding box,
|
|
16
|
-
* (the variant inversion triangle) is
|
|
15
|
+
* bounding box, the extent a shape whose hit test answers a polygon's box
|
|
16
|
+
* (the variant inversion triangle) is checked against; a curve contributes its control
|
|
17
17
|
* points, whose hull contains it.
|
|
18
18
|
*
|
|
19
19
|
* A **stroked** path records one rect per `moveTo`/`lineTo` edge, each the
|
|
@@ -60,59 +60,78 @@ export declare function recordingContext(): {
|
|
|
60
60
|
};
|
|
61
61
|
calls: RecordedRect[];
|
|
62
62
|
};
|
|
63
|
+
/** The extent of a painting, in canvas px. */
|
|
64
|
+
export interface Box {
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
w: number;
|
|
68
|
+
h: number;
|
|
69
|
+
}
|
|
70
|
+
interface Counted {
|
|
71
|
+
count: number;
|
|
72
|
+
}
|
|
73
|
+
export interface SweepOptions<C> {
|
|
74
|
+
maxDistSq?: number;
|
|
75
|
+
step?: number;
|
|
76
|
+
sliceOne?: (channels: C, index: number) => C;
|
|
77
|
+
inkSlackPx?: number;
|
|
78
|
+
contains?: (index: number, xPx: number, yPx: number) => boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The ink clause of {@link sweepMarkAgainstHit} alone, for a mark with no
|
|
82
|
+
* `hitNearest`: `boxes[i]` is what the painter recorded for instance `i`.
|
|
83
|
+
*/
|
|
84
|
+
export declare function inkViolations<C, S extends MarkFrame>(mark: Mark<C, S>, channels: C, block: RenderBlock, state: S, boxes: readonly (Box | undefined)[], inkSlackPx: number): string[];
|
|
63
85
|
/**
|
|
64
86
|
* The draw-against-hit gate: paint the block, then walk it in `step` px and
|
|
65
87
|
* hold `hitNearest` to what the painter actually put on the canvas.
|
|
66
88
|
*
|
|
67
89
|
* **What the painter drew for instance `i` has to be attributed to `i`,** and
|
|
68
|
-
* there are two ways to get that. A
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
90
|
+
* there are two ways to get that. A mark whose every instance is one `fillRect`
|
|
91
|
+
* gets it positionally, which is the default and needs nothing. A mark that
|
|
92
|
+
* batches a colour run into one path (`point`'s glyphs), or that skips an
|
|
93
|
+
* instance the view has scrolled past (`cell`'s off-canvas rows), cannot be read
|
|
94
|
+
* that way at all — the batch has fewer rects than instances, or the wrong ones.
|
|
95
|
+
* `sliceOne` is the way in for those: hand back one instance's channels and the
|
|
96
|
+
* sweep paints it alone, so the attribution holds by construction and a culled
|
|
97
|
+
* instance is visibly an empty painting.
|
|
76
98
|
*
|
|
77
|
-
* Four claims, all of them one-directional and true of every
|
|
99
|
+
* Four claims, all of them one-directional and true of every mark:
|
|
78
100
|
*
|
|
79
101
|
* - an instance that painted nothing is never the answer,
|
|
80
|
-
* - `hit.x`/`hit.y` — where the
|
|
102
|
+
* - `hit.x`/`hit.y` — where the mark says its ink is — lies inside the box the
|
|
81
103
|
* painter drew for the instance the hit names,
|
|
82
104
|
* - `hit.distSq` is no smaller than the distance to that box, since the box
|
|
83
105
|
* contains the ink and cannot be farther than it,
|
|
84
106
|
* - `hit.distSq` IS the distance to `hit.x`/`hit.y`, which is `MarkHit`'s own
|
|
85
|
-
* contract rather than a fact about the painting: a
|
|
107
|
+
* contract rather than a fact about the painting: a mark that answers a
|
|
86
108
|
* distance its own reported point does not support has two spellings of where
|
|
87
109
|
* its ink is, and the nearest-wins walk is resolved by the one the caller
|
|
88
110
|
* cannot see.
|
|
89
111
|
*
|
|
90
|
-
* The
|
|
91
|
-
*
|
|
92
|
-
* sweep hands candidates back to front.
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
112
|
+
* The fifth is the containment rule: **a point the rule contains answers the
|
|
113
|
+
* nearest painted instance it contains**, the last-painted where tied, since the
|
|
114
|
+
* sweep hands candidates back to front. The default rule is the painted box, so
|
|
115
|
+
* a point on a box answers that box, and off every box a hit target wider than
|
|
116
|
+
* its ink may still answer. The default holds only where the box IS the hit
|
|
117
|
+
* target — every `fillRect` mark, and `cell`'s inversion triangle, which answers
|
|
118
|
+
* as its bounding box on purpose — and not for a glyph, whose box is a superset
|
|
119
|
+
* of its ink, so it runs exactly when the batch painted one rect per instance. A
|
|
120
|
+
* mark whose hit rule is not its box (the pileup's bp containment) passes
|
|
121
|
+
* `contains`, which is then the whole rule: the sweep covers the whole canvas,
|
|
122
|
+
* and a point the rule contains nowhere answers nothing.
|
|
97
123
|
*
|
|
98
|
-
* A
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
124
|
+
* A mark declaring `ink` is held to one more clause, per instance: the box the
|
|
125
|
+
* painter recorded for `i` lies inside `ink(i)`, and each edge of `ink(i)` lies
|
|
126
|
+
* within `inkSlackPx` (default 1) of the painting's — so the rect is tight to
|
|
127
|
+
* the ink within a pixel on every side, which admits a seam overdraw or a pixel
|
|
128
|
+
* snap and rejects a rect that is the wrong row, the wrong width or the whole
|
|
129
|
+
* canvas. An instance that painted nothing must answer no ink, and one with ink
|
|
130
|
+
* must have painted.
|
|
105
131
|
*
|
|
106
132
|
* Returns the violations rather than asserting, so a test reads as
|
|
107
|
-
* `expect(sweep(...)).toEqual([])`. `maxDistSq` is the
|
|
108
|
-
*
|
|
109
|
-
* admits the whole rect.
|
|
133
|
+
* `expect(sweep(...)).toEqual([])`. `maxDistSq` is the bound the hit test is
|
|
134
|
+
* asked with, `Infinity` unless the caller's differs.
|
|
110
135
|
*/
|
|
111
|
-
export declare function
|
|
112
|
-
|
|
113
|
-
}, P>(shape: MarkShape<C, P>, channels: C, block: RenderBlock, frame: MarkFrame, params: P, { maxDistSq, step, sliceOne, inkSlackPx, }: {
|
|
114
|
-
maxDistSq: number;
|
|
115
|
-
step?: number;
|
|
116
|
-
sliceOne?: (channels: C, index: number) => C;
|
|
117
|
-
inkSlackPx?: number;
|
|
118
|
-
}): string[];
|
|
136
|
+
export declare function sweepMarkAgainstHit<C extends Counted, S extends MarkFrame>(mark: Mark<NoInfer<C>, S>, channels: C, block: RenderBlock, state: S, { maxDistSq, step, sliceOne, inkSlackPx, contains: rule, }?: SweepOptions<C>): string[];
|
|
137
|
+
export {};
|