@jbrowse/render-core 5.0.0-beta.8 → 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
|
@@ -1,120 +1,108 @@
|
|
|
1
1
|
/// <reference types="@webgpu/types" />
|
|
2
|
+
import { STANDARD_BLEND_STATE, stageVisibility, toGpuVertexFormat, } from '../webgpuUtils.js';
|
|
3
|
+
// A device's layouts and pipelines, shared by every WebGPUHal on it. Held per
|
|
4
|
+
// module copy rather than on the `globalThis` cell: a second bundled copy
|
|
5
|
+
// builds a duplicate set, never a wrong one, since WebGPU compares layouts
|
|
6
|
+
// structurally.
|
|
7
|
+
const perDevice = new WeakMap();
|
|
2
8
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* that linked it, and each display owns a context, so nothing there is
|
|
8
|
-
* shareable. WebGPU inverts that — `gpuDevice.ts` hands every display one
|
|
9
|
-
* device, and a `GPURenderPipeline` is a device object — and until this module
|
|
10
|
-
* the HAL kept building per-display copies anyway. Ten alignments tracks
|
|
11
|
-
* compiled 230 pipelines for 23 distinct programs.
|
|
12
|
-
*
|
|
13
|
-
* **The key is the descriptor object, and that is what makes it correct.** A
|
|
14
|
-
* plugin's pass list is a module-level const (`ALIGNMENTS_PASSES`) and
|
|
15
|
-
* `slangPass` reads `wgslSource` off a generated module const, so every display
|
|
16
|
-
* of a type hands the HAL the *same* `PipelineDescriptor` objects by reference.
|
|
17
|
-
* Identity therefore means "same shader, same layout, same blend, same
|
|
18
|
-
* topology" without comparing any of them, and two passes that merely share a
|
|
19
|
-
* `.slang` shape module (MAF and multi-row both draw `rowRect`) get separate
|
|
20
|
-
* entries because `slangPass` built them separate objects. The **sample count**
|
|
21
|
-
* is the one pipeline input the descriptor cannot express, so it is the inner
|
|
22
|
-
* key; see the `pipelines` field.
|
|
23
|
-
*
|
|
24
|
-
* The descriptor's `uniformByteSize` never reaches the pipeline, so it cannot
|
|
25
|
-
* make two HALs disagree here:
|
|
26
|
-
* `createUniformOnlyBindGroupLayout` declares no `minBindingSize`, so the
|
|
27
|
-
* layout — and thus the pipeline built against it — is the same whatever a
|
|
28
|
-
* display's `uniformByteSize` is. The size reaches the GPU through the bind
|
|
29
|
-
* group's `size` and the dynamic offset, both of which stay per-HAL.
|
|
30
|
-
*
|
|
31
|
-
* Per copy of this module rather than on the `globalThis` cell, for the reason
|
|
32
|
-
* `createHal.ts` gives for `warnedSoftwareRasterizer`: a second bundled copy
|
|
33
|
-
* builds a second set, which costs a duplicate and never a wrong answer.
|
|
34
|
-
* Layouts compare structurally in WebGPU, so a bind group from one copy is
|
|
35
|
-
* valid against a pipeline from the other.
|
|
9
|
+
* A render pass's bind-group layout, read off its shader's reflected table:
|
|
10
|
+
* each binding visible to exactly the stages that read it, so the layout
|
|
11
|
+
* cannot disagree with the shader about which stage reads what. The uniform
|
|
12
|
+
* block takes a dynamic offset into the HAL's ring.
|
|
36
13
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
binding: 2,
|
|
52
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
53
|
-
texture: { sampleType: 'float' },
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
binding: 3,
|
|
57
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
58
|
-
sampler: { type: 'filtering' },
|
|
59
|
-
},
|
|
60
|
-
],
|
|
14
|
+
export function bindGroupLayoutEntries(bindings) {
|
|
15
|
+
return bindings.map((b) => {
|
|
16
|
+
const at = { binding: b.index, visibility: stageVisibility(b.stages) };
|
|
17
|
+
switch (b.kind) {
|
|
18
|
+
case 'uniform':
|
|
19
|
+
return { ...at, buffer: { type: 'uniform', hasDynamicOffset: true } };
|
|
20
|
+
case 'texture':
|
|
21
|
+
return { ...at, texture: { sampleType: 'float' } };
|
|
22
|
+
case 'sampler':
|
|
23
|
+
return { ...at, sampler: { type: 'filtering' } };
|
|
24
|
+
default:
|
|
25
|
+
throw new Error(`a render pass binds no ${b.kind}, and '${b.name}' is one at ` +
|
|
26
|
+
`binding ${b.index}`);
|
|
27
|
+
}
|
|
61
28
|
});
|
|
29
|
+
}
|
|
30
|
+
function gpuBlendState(bs) {
|
|
31
|
+
// WebGPU rejects any factor but 'one' under max, which ignores them. Alpha
|
|
32
|
+
// accumulates as webgl2Hal's applyBlendState does.
|
|
33
|
+
const max = { srcFactor: 'one', dstFactor: 'one', operation: 'max' };
|
|
34
|
+
const behind = { srcFactor: 'one-minus-dst-alpha', dstFactor: 'one' };
|
|
35
|
+
return bs.op === 'max'
|
|
36
|
+
? { color: max, alpha: max }
|
|
37
|
+
: bs.op === 'behind'
|
|
38
|
+
? { color: behind, alpha: behind }
|
|
39
|
+
: {
|
|
40
|
+
color: { srcFactor: bs.srcFactor, dstFactor: bs.dstFactor },
|
|
41
|
+
alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha' },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function pipelineRecipe(desc, wgslSource, sampleCount) {
|
|
62
45
|
return {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
46
|
+
wgslSource,
|
|
47
|
+
bindGroupLayout: bindGroupLayoutEntries(desc.bindings),
|
|
48
|
+
vertexBuffer: {
|
|
49
|
+
arrayStride: desc.instanceStride,
|
|
50
|
+
stepMode: 'instance',
|
|
51
|
+
attributes: desc.vertexAttributes.map((attr, i) => ({
|
|
52
|
+
shaderLocation: i,
|
|
53
|
+
offset: attr.offsetBytes,
|
|
54
|
+
format: toGpuVertexFormat(attr),
|
|
55
|
+
})),
|
|
56
|
+
},
|
|
57
|
+
blend: desc.blend
|
|
58
|
+
? desc.blendState
|
|
59
|
+
? gpuBlendState(desc.blendState)
|
|
60
|
+
: STANDARD_BLEND_STATE
|
|
61
|
+
: undefined,
|
|
62
|
+
topology: desc.topology ?? 'triangle-list',
|
|
63
|
+
sampleCount,
|
|
71
64
|
};
|
|
72
65
|
}
|
|
73
66
|
function cacheFor(device) {
|
|
74
67
|
let entry = perDevice.get(device);
|
|
75
68
|
if (!entry) {
|
|
76
|
-
entry = { layouts:
|
|
69
|
+
entry = { layouts: new Map(), pipelines: new Map() };
|
|
77
70
|
perDevice.set(device, entry);
|
|
78
71
|
}
|
|
79
72
|
return entry;
|
|
80
73
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
74
|
+
export function getPassLayout(device, entries) {
|
|
75
|
+
const { layouts } = cacheFor(device);
|
|
76
|
+
const key = JSON.stringify(entries);
|
|
77
|
+
let layout = layouts.get(key);
|
|
78
|
+
if (!layout) {
|
|
79
|
+
const bindGroupLayout = device.createBindGroupLayout({ entries });
|
|
80
|
+
layout = {
|
|
81
|
+
bindGroupLayout,
|
|
82
|
+
pipelineLayout: device.createPipelineLayout({
|
|
83
|
+
bindGroupLayouts: [bindGroupLayout],
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
layouts.set(key, layout);
|
|
87
|
+
}
|
|
88
|
+
return layout;
|
|
91
89
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
export function getOrBuildPipeline(device, desc, sampleCount, build) {
|
|
100
|
-
const { layouts, pipelines } = cacheFor(device);
|
|
101
|
-
let bySampleCount = pipelines.get(desc);
|
|
102
|
-
if (!bySampleCount) {
|
|
103
|
-
bySampleCount = new Map();
|
|
104
|
-
pipelines.set(desc, bySampleCount);
|
|
90
|
+
export function getOrBuildPipeline(device, recipe, build) {
|
|
91
|
+
const { pipelines } = cacheFor(device);
|
|
92
|
+
const { wgslSource, ...rest } = recipe;
|
|
93
|
+
let bySource = pipelines.get(wgslSource);
|
|
94
|
+
if (!bySource) {
|
|
95
|
+
bySource = new Map();
|
|
96
|
+
pipelines.set(wgslSource, bySource);
|
|
105
97
|
}
|
|
106
|
-
|
|
98
|
+
const key = JSON.stringify(rest);
|
|
99
|
+
let pipeline = bySource.get(key);
|
|
107
100
|
if (!pipeline) {
|
|
108
|
-
pipeline = build(
|
|
109
|
-
|
|
101
|
+
pipeline = build(recipe);
|
|
102
|
+
bySource.set(key, pipeline);
|
|
110
103
|
}
|
|
111
104
|
return pipeline;
|
|
112
105
|
}
|
|
113
|
-
/**
|
|
114
|
-
* Drop every device's entry. Tests only — nothing in the app invalidates this
|
|
115
|
-
* cache, because losing the device is what invalidates it (see
|
|
116
|
-
* {@link getDeviceLayouts}).
|
|
117
|
-
*/
|
|
118
106
|
export function resetDeviceGpuCacheForTests(device) {
|
|
119
107
|
perDevice.delete(device);
|
|
120
108
|
}
|
package/esm/hal/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { createGpuHal } from './createHal.ts';
|
|
2
|
-
export { MockHal } from './mockHal.ts';
|
|
2
|
+
export { MOCK_SHADER_SOURCE, MockHal } from './mockHal.ts';
|
|
3
3
|
export { assertUniquePassIds } from './passIds.ts';
|
|
4
4
|
export { WebGL2Hal } from './webgl2Hal.ts';
|
|
5
5
|
export { WebGPUHal } from './webgpuHal.ts';
|
|
6
6
|
export type { GpuHalOptions } from './createHal.ts';
|
|
7
|
-
export type { BlendState, VertexAttributeLayout, GpuHal, PipelineDescriptor, SampleCount, ShaderBinding, TextureBinding, TextureSource, } from './types.ts';
|
|
7
|
+
export type { BlendState, VertexAttributeLayout, GpuHal, PipelineDescriptor, SampleCount, ShaderBinding, ShaderSource, ShaderStage, TextureBinding, TextureSource, } from './types.ts';
|
package/esm/hal/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createGpuHal } from './createHal.js';
|
|
2
|
-
export { MockHal } from './mockHal.js';
|
|
2
|
+
export { MOCK_SHADER_SOURCE, MockHal } from './mockHal.js';
|
|
3
3
|
export { assertUniquePassIds } from './passIds.js';
|
|
4
4
|
export { WebGL2Hal } from './webgl2Hal.js';
|
|
5
5
|
export { WebGPUHal } from './webgpuHal.js';
|
package/esm/hal/mockHal.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { GpuHalBase } from './gpuHalBase.ts';
|
|
2
|
-
import type { GpuHal, PipelineDescriptor, TextureBinding, TextureSource } from './types.ts';
|
|
2
|
+
import type { GpuHal, PipelineDescriptor, ShaderSource, TextureBinding, TextureSource } from './types.ts';
|
|
3
|
+
/** Text for a pass built only for a `MockHal`, which never loads it. */
|
|
4
|
+
export declare const MOCK_SHADER_SOURCE: ShaderSource;
|
|
3
5
|
export interface MockCall {
|
|
4
6
|
method: string;
|
|
5
7
|
args: unknown[];
|
|
@@ -17,7 +19,7 @@ export interface MockRect {
|
|
|
17
19
|
/**
|
|
18
20
|
* One draw, with the state that was in force when it was issued — the clip, and
|
|
19
21
|
* which `writeUniforms` it reads. `null` scissor/viewport means unclipped, the
|
|
20
|
-
* full canvas,
|
|
22
|
+
* full canvas, the state both HALs must restore after a `clearScissor` /
|
|
21
23
|
* `clearViewport`.
|
|
22
24
|
*
|
|
23
25
|
* `uniformWrite` indexes {@link MockHal.getUniformWritesF32}, or is `-1` when
|
|
@@ -32,8 +34,8 @@ export interface MockRect {
|
|
|
32
34
|
* does an immediate `bufferSubData` into one UBO. They agree only while a
|
|
33
35
|
* renderer writes then draws, which is the convention every renderer in tree
|
|
34
36
|
* follows and nothing enforces: batching the writes and then issuing the draws
|
|
35
|
-
* is right on WebGL2 and Canvas2D and
|
|
36
|
-
*
|
|
37
|
+
* is right on WebGL2 and Canvas2D and wrong on WebGPU, where the draws read the
|
|
38
|
+
* last write. `uniformWrite` lets a backend test pin its renderer's pairing.
|
|
37
39
|
*/
|
|
38
40
|
export interface MockDraw {
|
|
39
41
|
passId: string;
|
|
@@ -59,8 +61,8 @@ export declare class MockHal extends GpuHalBase<MockBuffer> implements GpuHal {
|
|
|
59
61
|
* rect without re-issuing a full-canvas one, so every draw after it went on
|
|
60
62
|
* being clipped to the previous block while WebGL2 drew them unclipped — right
|
|
61
63
|
* on Canvas2D, right on WebGL2, wrong on WebGPU alone. Nothing in tree clears
|
|
62
|
-
* mid-frame today,
|
|
63
|
-
*
|
|
64
|
+
* mid-frame today, so no test caught it. Recording the effective clip per draw
|
|
65
|
+
* makes that assertable.
|
|
64
66
|
*/
|
|
65
67
|
private scissor;
|
|
66
68
|
private viewport;
|
package/esm/hal/mockHal.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { getDpr } from '../canvas2dUtils.js';
|
|
2
2
|
import { GpuHalBase } from './gpuHalBase.js';
|
|
3
3
|
import { assertUniquePassIds } from './passIds.js';
|
|
4
|
+
/** Text for a pass built only for a `MockHal`, which never loads it. */
|
|
5
|
+
export const MOCK_SHADER_SOURCE = {
|
|
6
|
+
wgsl: () => Promise.resolve({ WGSL_SOURCE: '' }),
|
|
7
|
+
glsl: () => Promise.resolve({ GLSL_VERTEX: '', GLSL_FRAGMENT: '' }),
|
|
8
|
+
};
|
|
4
9
|
export class MockHal extends GpuHalBase {
|
|
5
10
|
calls = [];
|
|
6
11
|
// Every write of the frame, in order, not just the last. A renderer that
|
|
@@ -23,8 +28,8 @@ export class MockHal extends GpuHalBase {
|
|
|
23
28
|
* rect without re-issuing a full-canvas one, so every draw after it went on
|
|
24
29
|
* being clipped to the previous block while WebGL2 drew them unclipped — right
|
|
25
30
|
* on Canvas2D, right on WebGL2, wrong on WebGPU alone. Nothing in tree clears
|
|
26
|
-
* mid-frame today,
|
|
27
|
-
*
|
|
31
|
+
* mid-frame today, so no test caught it. Recording the effective clip per draw
|
|
32
|
+
* makes that assertable.
|
|
28
33
|
*/
|
|
29
34
|
scissor = null;
|
|
30
35
|
viewport = null;
|
package/esm/hal/passIds.d.ts
CHANGED
|
@@ -2,13 +2,14 @@ import type { PipelineDescriptor } from './types.ts';
|
|
|
2
2
|
/**
|
|
3
3
|
* Throw if two passes in one display's registry share an `id`.
|
|
4
4
|
*
|
|
5
|
-
* A pass id
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* backends while Canvas2D
|
|
5
|
+
* A pass id names a slot: the descriptor a draw of that id uses, its instance
|
|
6
|
+
* buffer per region, and its texture. Compiled pipelines and programs are keyed
|
|
7
|
+
* by content instead, so a second id over one shader costs no compile. A
|
|
8
|
+
* duplicate id collides the slot: the later descriptor answers every draw of
|
|
9
|
+
* it, both passes upload to one buffer so the last write wins, and whichever
|
|
10
|
+
* shader has the larger stride reads off the end of it. No validation error, no
|
|
11
|
+
* throw, no failing test — just wrong pixels on the GPU backends while Canvas2D
|
|
12
|
+
* keeps drawing correctly.
|
|
12
13
|
*
|
|
13
14
|
* Nothing structural stops it. A display's pass list is concatenated from
|
|
14
15
|
* several registries (`ALIGNMENTS_PASSES` merges three plus a standalone
|
package/esm/hal/passIds.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Throw if two passes in one display's registry share an `id`.
|
|
3
3
|
*
|
|
4
|
-
* A pass id
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* backends while Canvas2D
|
|
4
|
+
* A pass id names a slot: the descriptor a draw of that id uses, its instance
|
|
5
|
+
* buffer per region, and its texture. Compiled pipelines and programs are keyed
|
|
6
|
+
* by content instead, so a second id over one shader costs no compile. A
|
|
7
|
+
* duplicate id collides the slot: the later descriptor answers every draw of
|
|
8
|
+
* it, both passes upload to one buffer so the last write wins, and whichever
|
|
9
|
+
* shader has the larger stride reads off the end of it. No validation error, no
|
|
10
|
+
* throw, no failing test — just wrong pixels on the GPU backends while Canvas2D
|
|
11
|
+
* keeps drawing correctly.
|
|
11
12
|
*
|
|
12
13
|
* Nothing structural stops it. A display's pass list is concatenated from
|
|
13
14
|
* several registries (`ALIGNMENTS_PASSES` merges three plus a standalone
|
|
@@ -29,8 +30,8 @@ export function assertUniquePassIds(passes) {
|
|
|
29
30
|
}
|
|
30
31
|
if (duplicates.size > 0) {
|
|
31
32
|
throw new Error(`duplicate pass id(s) ${[...duplicates].map(id => `'${id}'`).join(', ')}` +
|
|
32
|
-
` — a pass id keys
|
|
33
|
-
` passes sharing one draw each other's instances
|
|
34
|
-
` shader. Rename one.`);
|
|
33
|
+
` — a pass id keys the descriptor, the instance buffer and the` +
|
|
34
|
+
` texture, so two passes sharing one draw each other's instances` +
|
|
35
|
+
` through one shader. Rename one.`);
|
|
35
36
|
}
|
|
36
37
|
}
|
package/esm/hal/types.d.ts
CHANGED
|
@@ -39,21 +39,25 @@ export type BlendState = {
|
|
|
39
39
|
dstFactor: BlendFactor;
|
|
40
40
|
} | {
|
|
41
41
|
op: 'max';
|
|
42
|
+
} | {
|
|
43
|
+
op: 'behind';
|
|
42
44
|
};
|
|
45
|
+
export type ShaderStage = 'vertex' | 'fragment' | 'compute';
|
|
43
46
|
/**
|
|
44
47
|
* One entry of a shader's GPU binding table, as the Slang codegen reflects it.
|
|
45
48
|
*
|
|
46
|
-
* `kind` is spelled the way WebGPU spells it
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* `kind` is spelled the way WebGPU spells it and `stages` names the entry
|
|
50
|
+
* points that read the binding, so a consumer builds a `createBindGroupLayout`
|
|
51
|
+
* entry from it directly, `visibility` included. The WebGPU HAL and the compute
|
|
52
|
+
* driver both do. `pnpm gen:shaders` refuses a render shader whose table is not
|
|
53
|
+
* one the HALs bind, and a `stages` that leaves out a stage the emitted WGSL
|
|
54
|
+
* reads the binding in.
|
|
52
55
|
*/
|
|
53
56
|
export interface ShaderBinding {
|
|
54
57
|
index: number;
|
|
55
58
|
kind: 'uniform' | 'texture' | 'sampler' | 'storage' | 'read-only-storage';
|
|
56
59
|
name: string;
|
|
60
|
+
stages: readonly ShaderStage[];
|
|
57
61
|
}
|
|
58
62
|
/**
|
|
59
63
|
* What a pass's texture is filled from: RGBA bytes, or a canvas whose pixels
|
|
@@ -63,12 +67,26 @@ export interface ShaderBinding {
|
|
|
63
67
|
*/
|
|
64
68
|
export type TextureSource = Uint8Array | HTMLCanvasElement | OffscreenCanvas;
|
|
65
69
|
export interface TextureBinding {
|
|
66
|
-
textureBinding: number;
|
|
67
|
-
samplerBinding: number;
|
|
68
70
|
glTextureUnit: number;
|
|
69
71
|
glUniformName: string;
|
|
70
72
|
filter: 'linear' | 'nearest';
|
|
71
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* A shader's compiled text, one loader per target. `pnpm gen:shaders` emits it
|
|
76
|
+
* as a shader module's `SOURCE`, each loader an `import()` of a module holding
|
|
77
|
+
* nothing but that target's strings, so no realm evaluates shader text until a
|
|
78
|
+
* HAL is built with the pass: WebGPU loads the WGSL, WebGL2 the GLSL, Canvas2D
|
|
79
|
+
* neither, and the RPC worker never builds one.
|
|
80
|
+
*/
|
|
81
|
+
export interface ShaderSource {
|
|
82
|
+
wgsl: () => Promise<{
|
|
83
|
+
WGSL_SOURCE: string;
|
|
84
|
+
}>;
|
|
85
|
+
glsl: () => Promise<{
|
|
86
|
+
GLSL_VERTEX: string;
|
|
87
|
+
GLSL_FRAGMENT: string;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
72
90
|
/**
|
|
73
91
|
* A **pipeline state object (PSO)**: shaders, vertex input layout, blend state,
|
|
74
92
|
* primitive topology and texture bindings, baked into one immutable
|
|
@@ -83,9 +101,7 @@ export interface TextureBinding {
|
|
|
83
101
|
*/
|
|
84
102
|
export interface PipelineDescriptor {
|
|
85
103
|
id: string;
|
|
86
|
-
|
|
87
|
-
glslVertex: string;
|
|
88
|
-
glslFragment: string;
|
|
104
|
+
source: ShaderSource;
|
|
89
105
|
instanceStride: number;
|
|
90
106
|
/** Bytes of the uniform block the shader binds; the HAL's slot is the largest over its passes. */
|
|
91
107
|
uniformByteSize: number;
|
|
@@ -96,7 +112,7 @@ export interface PipelineDescriptor {
|
|
|
96
112
|
topology?: 'triangle-list' | 'triangle-strip' | 'line-list';
|
|
97
113
|
coverage?: 'analytic';
|
|
98
114
|
textures?: readonly [TextureBinding, ...TextureBinding[]];
|
|
99
|
-
bindings
|
|
115
|
+
bindings: readonly ShaderBinding[];
|
|
100
116
|
}
|
|
101
117
|
export interface GpuHal {
|
|
102
118
|
/** The uniform slot size, the largest block any registered pass binds. */
|
package/esm/hal/webgl2Hal.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ export declare class WebGL2Hal extends GpuHalBase<RegionPassBuffer> implements G
|
|
|
8
8
|
private gl;
|
|
9
9
|
private canvas;
|
|
10
10
|
private passes;
|
|
11
|
+
private glsl;
|
|
12
|
+
private programs;
|
|
13
|
+
private passTextures;
|
|
11
14
|
private ubo;
|
|
12
15
|
private maxTextureDim;
|
|
13
16
|
private debug;
|
|
@@ -17,7 +20,12 @@ export declare class WebGL2Hal extends GpuHalBase<RegionPassBuffer> implements G
|
|
|
17
20
|
private contextLostListener;
|
|
18
21
|
private contextRestoredListener;
|
|
19
22
|
private checkGlError;
|
|
20
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Loads every pass's GLSL before the context is acquired, so a load that
|
|
25
|
+
* fails leaves the canvas free for Canvas2D.
|
|
26
|
+
*/
|
|
27
|
+
static create(canvas: HTMLCanvasElement, descriptors: PipelineDescriptor[]): Promise<WebGL2Hal>;
|
|
28
|
+
private constructor();
|
|
21
29
|
protected limits(): {
|
|
22
30
|
maxBufferBytes: number;
|
|
23
31
|
maxTextureDimensionPx: number;
|
|
@@ -27,7 +35,7 @@ export declare class WebGL2Hal extends GpuHalBase<RegionPassBuffer> implements G
|
|
|
27
35
|
count: number;
|
|
28
36
|
};
|
|
29
37
|
protected destroyBuffer(buf: RegionPassBuffer): void;
|
|
30
|
-
private
|
|
38
|
+
private link;
|
|
31
39
|
private getPass;
|
|
32
40
|
resize(width: number, height: number): import("./types.ts").CanvasScale;
|
|
33
41
|
protected createTexture(passId: string, binding: TextureBinding, data: TextureSource, width: number, height: number): void;
|
|
@@ -41,7 +49,7 @@ export declare class WebGL2Hal extends GpuHalBase<RegionPassBuffer> implements G
|
|
|
41
49
|
endFrame(): void;
|
|
42
50
|
protected releaseResources(): void;
|
|
43
51
|
private applyBlendState;
|
|
44
|
-
private
|
|
52
|
+
private bindTexture;
|
|
45
53
|
private bindAttributes;
|
|
46
54
|
}
|
|
47
55
|
export {};
|