wgblas 2.0.0 → 2.1.0
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/README.md +18 -18
- package/dist/wgblas.browser.js +1273 -1239
- package/index.d.mts +38 -6
- package/package.json +2 -1
- package/src/classes/GpuMatrix.mjs +17 -10
- package/src/classes/GpuVector.mjs +28 -10
- package/src/dasum/dasum.d.mts +4 -4
- package/src/dasum/dasum.mjs +19 -17
- package/src/devdocs.mjs +13 -0
- package/src/idamax/idamax.d.mts +20 -2
- package/src/idamax/idamax.mjs +18 -16
- package/src/init.mjs +114 -56
- package/src/isamax/isamax.d.mts +20 -2
- package/src/isamax/isamax.mjs +16 -14
- package/src/random/random.d.mts +1 -0
- package/src/sasum/sasum.d.mts +2 -2
- package/src/sasum/sasum.mjs +15 -13
- package/src/saxpy/saxpy.d.mts +2 -2
- package/src/saxpy/saxpy.mjs +10 -8
- package/src/scopy/scopy.d.mts +2 -2
- package/src/scopy/scopy.mjs +10 -8
- package/src/sdot/sdot.d.mts +2 -2
- package/src/sdot/sdot.mjs +16 -14
- package/src/sgemm/sgemm.mjs +28 -15
- package/src/sgemmtr/sgemmtr.mjs +16 -15
- package/src/sgemv/sgemv.mjs +38 -26
- package/src/sger/sger.mjs +10 -8
- package/src/shaders/index.mjs +164 -14
- package/src/shaders/reduction/scaledSum.wgsl +65 -0
- package/src/shaders/sgemm_large.wgsl +107 -18
- package/src/shaders/sgemm_small.wgsl +115 -15
- package/src/shaders/sgemmtr_large.wgsl +4 -1
- package/src/shaders/sgemmtr_small.wgsl +4 -1
- package/src/shaders/sgemv_n.wgsl +3 -1
- package/src/shaders/sgemv_t.wgsl +3 -1
- package/src/shaders/snrm2.wgsl +72 -23
- package/src/shaders/ssymv.wgsl +3 -1
- package/src/snrm2/snrm2.d.mts +2 -2
- package/src/snrm2/snrm2.mjs +33 -21
- package/src/srot/srot.d.mts +2 -4
- package/src/srot/srot.mjs +11 -9
- package/src/srotm/srotm.d.mts +2 -4
- package/src/srotm/srotm.mjs +19 -10
- package/src/sscal/sscal.d.mts +3 -3
- package/src/sscal/sscal.mjs +12 -10
- package/src/sswap/sswap.d.mts +2 -2
- package/src/sswap/sswap.mjs +11 -9
- package/src/ssymm/ssymm.mjs +31 -22
- package/src/ssymv/ssymv.mjs +10 -8
- package/src/ssyr/ssyr.mjs +9 -7
- package/src/ssyr2/ssyr2.mjs +10 -8
- package/src/ssyr2k/ssyr2k.mjs +18 -17
- package/src/ssyrk/ssyrk.mjs +18 -17
- package/src/strmm/strmm.mjs +47 -32
- package/src/strmv/strmv.mjs +10 -8
- package/src/strsm/strsm.mjs +54 -36
- package/src/strsv/strsv.mjs +16 -12
- package/src/util/benchmark.mjs +4 -6
- package/src/util/bindgroup.mjs +1 -3
- package/src/util/buffer.mjs +113 -19
- package/src/util/compute.mjs +6 -9
- package/src/util/constants.mjs +57 -0
- package/src/util/device.mjs +34 -0
- package/src/util/pipeline.mjs +5 -6
- package/src/util/workgroup.mjs +55 -7
- package/src/shaders/browser-shaders.mjs +0 -81
package/src/strsm/strsm.mjs
CHANGED
|
@@ -4,19 +4,19 @@ import {
|
|
|
4
4
|
createStorageBuffer,
|
|
5
5
|
stageReadback,
|
|
6
6
|
destroyBuffers,
|
|
7
|
+
vec4ViewBinding,
|
|
7
8
|
} from "../util/buffer.mjs";
|
|
8
9
|
import { createBindGroup } from "../util/bindgroup.mjs";
|
|
9
10
|
import { beginTimedEncoder, encodePass, submit } from "../util/compute.mjs";
|
|
10
11
|
import { extractResult } from "../util/result.mjs";
|
|
11
12
|
import { resolveTimestamp, extractTimestamp } from "../util/benchmark.mjs";
|
|
12
13
|
import { getPipeline } from "../util/pipeline.mjs";
|
|
13
|
-
import { calcWorkgroups } from "../util/workgroup.mjs";
|
|
14
|
+
import { calcWorkgroups, requireWorkgroups, requireWorkgroupCount } from "../util/workgroup.mjs";
|
|
14
15
|
import { GpuMatrix } from "../classes/GpuMatrix.mjs";
|
|
16
|
+
import { BM_SMALL, BN_SMALL, BM_LARGE, BN_LARGE, LARGE_TILE_WORKGROUP_THRESHOLD } from "../util/constants.mjs";
|
|
17
|
+
import { BLOCK_SIZE } from "../util/constants.mjs";
|
|
18
|
+
import { requireSameDevice } from "../util/device.mjs";
|
|
15
19
|
|
|
16
|
-
const BLOCK_SIZE = 64; // must match strsv_invert_block.wgsl's own constant
|
|
17
|
-
const BM_SMALL = 32, BN_SMALL = 32; // sgemm_small.wgsl's block tile
|
|
18
|
-
const BM_LARGE = 64, BN_LARGE = 64; // sgemm_large.wgsl's block tile
|
|
19
|
-
const LARGE_TILE_WORKGROUP_THRESHOLD = 36; // same threshold sgemm/ssymm/strmm use
|
|
20
20
|
|
|
21
21
|
// strsm: B := alpha*op(A)^-1*B (side='left') or alpha*B*op(A)^-1 (side='right'),
|
|
22
22
|
// A triangular. Blocked substitution (strsv's own technique, generalized to
|
|
@@ -31,6 +31,7 @@ export async function strsm(
|
|
|
31
31
|
|
|
32
32
|
if (!(device instanceof GPUDevice))
|
|
33
33
|
throw new Error("device must be a GPUDevice.");
|
|
34
|
+
requireSameDevice(device, "strsm", { A, B });
|
|
34
35
|
if (side !== "left" && side !== "right")
|
|
35
36
|
throw new Error("side must be 'left' or 'right'.");
|
|
36
37
|
if (uplo !== "lower" && uplo !== "upper")
|
|
@@ -103,19 +104,22 @@ export async function strsm(
|
|
|
103
104
|
const transferPipeline = await getPipeline(device, "block_transfer");
|
|
104
105
|
const scalarPipeline = await getPipeline(device, "sscal");
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
// Null-init here and allocate inside the try below, so a throw partway
|
|
108
|
+
// through the sequence still reaches finally with every handle visible
|
|
109
|
+
// (strsv.mjs is the reference for this pattern).
|
|
110
|
+
let ABuffer = null;
|
|
111
|
+
let BBuffer = null;
|
|
112
|
+
let AinvBuffer = null;
|
|
109
113
|
|
|
110
114
|
const paramsBuffers = [];
|
|
111
115
|
const scratchBuffers = [];
|
|
112
116
|
function scratch(size, label) {
|
|
113
|
-
const buf = createStorageBuffer(size, label);
|
|
117
|
+
const buf = createStorageBuffer(device, size, label);
|
|
114
118
|
scratchBuffers.push(buf);
|
|
115
119
|
return buf;
|
|
116
120
|
}
|
|
117
121
|
function params(entries, label) {
|
|
118
|
-
const buf = createParamsBuffer(entries, label);
|
|
122
|
+
const buf = createParamsBuffer(device, entries, label);
|
|
119
123
|
paramsBuffers.push(buf);
|
|
120
124
|
return buf;
|
|
121
125
|
}
|
|
@@ -126,6 +130,10 @@ export async function strsm(
|
|
|
126
130
|
const bScaleLen = (bOuter - 1) * ldb + bInner;
|
|
127
131
|
|
|
128
132
|
try {
|
|
133
|
+
ABuffer = AIsGpu ? A._buf : uploadBuffer(device, A, "strsm-A", false);
|
|
134
|
+
BBuffer = BIsGpu ? B._buf : uploadBuffer(device, B, "strsm-B", true);
|
|
135
|
+
AinvBuffer = createStorageBuffer(device, numBlocks * BLOCK_SIZE * BLOCK_SIZE * 4, "strsm-Ainv");
|
|
136
|
+
|
|
129
137
|
// Pre-scale B by alpha once (reuses sscal, so no per-block alpha handling).
|
|
130
138
|
let preScaleBindGroup = null;
|
|
131
139
|
if (alpha !== 1.0) {
|
|
@@ -137,7 +145,7 @@ export async function strsm(
|
|
|
137
145
|
],
|
|
138
146
|
"strsm-scale-params",
|
|
139
147
|
);
|
|
140
|
-
preScaleBindGroup = createBindGroup(scalarPipeline.getBindGroupLayout(0), [BBuffer, scaleParams]);
|
|
148
|
+
preScaleBindGroup = createBindGroup(device, scalarPipeline.getBindGroupLayout(0), [BBuffer, scaleParams]);
|
|
141
149
|
}
|
|
142
150
|
|
|
143
151
|
// Every diagonal block's inverse, fully parallel, one dispatch, unchanged.
|
|
@@ -151,7 +159,7 @@ export async function strsm(
|
|
|
151
159
|
],
|
|
152
160
|
"strsm-invert-params",
|
|
153
161
|
);
|
|
154
|
-
const invertBindGroup = createBindGroup(invertPipeline.getBindGroupLayout(0), [ABuffer, AinvBuffer, invertParams]);
|
|
162
|
+
const invertBindGroup = createBindGroup(device, invertPipeline.getBindGroupLayout(0), [ABuffer, AinvBuffer, invertParams]);
|
|
155
163
|
|
|
156
164
|
// Reusable scratch buffers, sized for the worst case, bound at offset 0.
|
|
157
165
|
const Bblock = scratch(BLOCK_SIZE * otherLen * 4, "strsm-Bblock");
|
|
@@ -159,15 +167,15 @@ export async function strsm(
|
|
|
159
167
|
const Aoff = scratch(aOrder * BLOCK_SIZE * 4, "strsm-Aoff");
|
|
160
168
|
const delta = scratch(aOrder * otherLen * 4, "strsm-delta");
|
|
161
169
|
|
|
162
|
-
const { commandEncoder, querySet } = beginTimedEncoder();
|
|
170
|
+
const { commandEncoder, querySet } = beginTimedEncoder(device);
|
|
163
171
|
|
|
164
172
|
if (alpha === 0) {
|
|
165
173
|
// BLAS: alpha=0 means A is not referenced — skip straight to B:=0.
|
|
166
174
|
const zeroDesc = querySet ? { timestampWrites: { querySet, beginningOfPassWriteIndex: 0, endOfPassWriteIndex: 1 } } : undefined;
|
|
167
|
-
encodePass(commandEncoder, scalarPipeline, preScaleBindGroup, calcWorkgroups(bScaleLen), zeroDesc);
|
|
175
|
+
encodePass(commandEncoder, scalarPipeline, preScaleBindGroup, calcWorkgroups(device, bScaleLen), zeroDesc);
|
|
168
176
|
} else {
|
|
169
177
|
if (preScaleBindGroup) {
|
|
170
|
-
encodePass(commandEncoder, scalarPipeline, preScaleBindGroup, calcWorkgroups(bScaleLen));
|
|
178
|
+
encodePass(commandEncoder, scalarPipeline, preScaleBindGroup, calcWorkgroups(device, bScaleLen));
|
|
171
179
|
}
|
|
172
180
|
const invertDesc = querySet ? { timestampWrites: { querySet, beginningOfPassWriteIndex: 0 } } : undefined;
|
|
173
181
|
encodePass(commandEncoder, invertPipeline, invertBindGroup, { x: BLOCK_SIZE, y: numBlocks }, invertDesc);
|
|
@@ -193,8 +201,8 @@ export async function strsm(
|
|
|
193
201
|
],
|
|
194
202
|
"strsm-gather-B-params",
|
|
195
203
|
);
|
|
196
|
-
const gatherBBindGroup = createBindGroup(transferPipeline.getBindGroupLayout(0), [Bblock, BBuffer, gatherBParams]);
|
|
197
|
-
encodePass(commandEncoder, transferPipeline, gatherBBindGroup,
|
|
204
|
+
const gatherBBindGroup = createBindGroup(device, transferPipeline.getBindGroupLayout(0), [Bblock, BBuffer, gatherBParams]);
|
|
205
|
+
encodePass(commandEncoder, transferPipeline, gatherBBindGroup, requireWorkgroups(device, "strsm", blockLen, otherLen));
|
|
198
206
|
|
|
199
207
|
// 2) apply: Xblock := op(Ainv_block) @ Bblock (side='left'), or the
|
|
200
208
|
// transpose-trick equivalent for side='right' (same trick strmm uses).
|
|
@@ -218,15 +226,18 @@ export async function strsm(
|
|
|
218
226
|
],
|
|
219
227
|
"strsm-apply-params",
|
|
220
228
|
);
|
|
221
|
-
const
|
|
222
|
-
|
|
229
|
+
const ainvBlock = { buffer: AinvBuffer, offset: blockIndex * BLOCK_SIZE * BLOCK_SIZE * 4, size: BLOCK_SIZE * BLOCK_SIZE * 4 };
|
|
230
|
+
const applyBindGroup = createBindGroup(device, gemmPipeline.getBindGroupLayout(0), [
|
|
231
|
+
ainvBlock,
|
|
232
|
+
vec4ViewBinding(device, ainvBlock),
|
|
223
233
|
Bblock,
|
|
234
|
+
vec4ViewBinding(device, Bblock),
|
|
224
235
|
Xblock,
|
|
225
236
|
applyParams,
|
|
226
237
|
]);
|
|
227
238
|
const wg = useLarge
|
|
228
|
-
? { x:
|
|
229
|
-
: { x:
|
|
239
|
+
? { x: requireWorkgroupCount(device, largeWgX, "strsm", "x"), y: requireWorkgroupCount(device, largeWgY, "strsm", "y") }
|
|
240
|
+
: { x: requireWorkgroupCount(device, Math.ceil(ng / BN_SMALL), "strsm", "x"), y: requireWorkgroupCount(device, Math.ceil(mg / BM_SMALL), "strsm", "y") };
|
|
230
241
|
encodePass(commandEncoder, gemmPipeline, applyBindGroup, wg);
|
|
231
242
|
}
|
|
232
243
|
|
|
@@ -247,9 +258,9 @@ export async function strsm(
|
|
|
247
258
|
],
|
|
248
259
|
"strsm-scatter-params",
|
|
249
260
|
);
|
|
250
|
-
const scatterBindGroup = createBindGroup(transferPipeline.getBindGroupLayout(0), [Xblock, BBuffer, scatterParams]);
|
|
261
|
+
const scatterBindGroup = createBindGroup(device, transferPipeline.getBindGroupLayout(0), [Xblock, BBuffer, scatterParams]);
|
|
251
262
|
const scatterDesc = isLastPass && !hasRemaining && querySet ? { timestampWrites: { querySet, endOfPassWriteIndex: 1 } } : undefined;
|
|
252
|
-
encodePass(commandEncoder, transferPipeline, scatterBindGroup,
|
|
263
|
+
encodePass(commandEncoder, transferPipeline, scatterBindGroup, requireWorkgroups(device, "strsm", blockLen, otherLen), scatterDesc);
|
|
253
264
|
|
|
254
265
|
// 4) trailing update: subtract this block's contribution from B.
|
|
255
266
|
if (!hasRemaining) continue;
|
|
@@ -268,8 +279,8 @@ export async function strsm(
|
|
|
268
279
|
],
|
|
269
280
|
"strsm-gather-A-params",
|
|
270
281
|
);
|
|
271
|
-
const gatherABindGroup = createBindGroup(transferPipeline.getBindGroupLayout(0), [Aoff, ABuffer, gatherAParams]);
|
|
272
|
-
encodePass(commandEncoder, transferPipeline, gatherABindGroup,
|
|
282
|
+
const gatherABindGroup = createBindGroup(device, transferPipeline.getBindGroupLayout(0), [Aoff, ABuffer, gatherAParams]);
|
|
283
|
+
encodePass(commandEncoder, transferPipeline, gatherABindGroup, requireWorkgroups(device, "strsm", remCount, blockLen));
|
|
273
284
|
|
|
274
285
|
{
|
|
275
286
|
const mg = remCount, ng = otherLen, kg = blockLen;
|
|
@@ -291,10 +302,17 @@ export async function strsm(
|
|
|
291
302
|
],
|
|
292
303
|
"strsm-update-params",
|
|
293
304
|
);
|
|
294
|
-
const updateBindGroup = createBindGroup(gemmPipeline.getBindGroupLayout(0), [
|
|
305
|
+
const updateBindGroup = createBindGroup(device, gemmPipeline.getBindGroupLayout(0), [
|
|
306
|
+
Aoff,
|
|
307
|
+
vec4ViewBinding(device, Aoff),
|
|
308
|
+
Xblock,
|
|
309
|
+
vec4ViewBinding(device, Xblock),
|
|
310
|
+
delta,
|
|
311
|
+
updateParams,
|
|
312
|
+
]);
|
|
295
313
|
const wg = useLarge
|
|
296
|
-
? { x:
|
|
297
|
-
: { x:
|
|
314
|
+
? { x: requireWorkgroupCount(device, largeWgX, "strsm", "x"), y: requireWorkgroupCount(device, largeWgY, "strsm", "y") }
|
|
315
|
+
: { x: requireWorkgroupCount(device, Math.ceil(ng / BN_SMALL), "strsm", "x"), y: requireWorkgroupCount(device, Math.ceil(mg / BM_SMALL), "strsm", "y") };
|
|
298
316
|
encodePass(commandEncoder, gemmPipeline, updateBindGroup, wg);
|
|
299
317
|
}
|
|
300
318
|
|
|
@@ -311,16 +329,16 @@ export async function strsm(
|
|
|
311
329
|
],
|
|
312
330
|
"strsm-scatter-sub-params",
|
|
313
331
|
);
|
|
314
|
-
const scatterSubBindGroup = createBindGroup(transferPipeline.getBindGroupLayout(0), [delta, BBuffer, scatterSubParams]);
|
|
332
|
+
const scatterSubBindGroup = createBindGroup(device, transferPipeline.getBindGroupLayout(0), [delta, BBuffer, scatterSubParams]);
|
|
315
333
|
const subDesc = isLastPass && querySet ? { timestampWrites: { querySet, endOfPassWriteIndex: 1 } } : undefined;
|
|
316
|
-
encodePass(commandEncoder, transferPipeline, scatterSubBindGroup,
|
|
334
|
+
encodePass(commandEncoder, transferPipeline, scatterSubBindGroup, requireWorkgroups(device, "strsm", remCount, otherLen), subDesc);
|
|
317
335
|
}
|
|
318
336
|
}
|
|
319
337
|
|
|
320
|
-
const ts = resolveTimestamp(commandEncoder, querySet);
|
|
321
|
-
const readBuffer = BIsGpu ? null : stageReadback(commandEncoder, BBuffer);
|
|
338
|
+
const ts = resolveTimestamp(device, commandEncoder, querySet);
|
|
339
|
+
const readBuffer = BIsGpu ? null : stageReadback(device, commandEncoder, BBuffer);
|
|
322
340
|
|
|
323
|
-
submit(commandEncoder);
|
|
341
|
+
submit(device, commandEncoder);
|
|
324
342
|
|
|
325
343
|
const gpuTimeMs = await extractTimestamp(ts);
|
|
326
344
|
|
|
@@ -333,9 +351,9 @@ export async function strsm(
|
|
|
333
351
|
if (gpuTimeMs !== undefined) return { B: result, gpuTimeMs };
|
|
334
352
|
return { B: result };
|
|
335
353
|
} finally {
|
|
336
|
-
if (!AIsGpu) destroyBuffers(ABuffer);
|
|
337
|
-
if (!BIsGpu) destroyBuffers(BBuffer);
|
|
338
|
-
destroyBuffers(AinvBuffer);
|
|
354
|
+
if (!AIsGpu && ABuffer) destroyBuffers(ABuffer);
|
|
355
|
+
if (!BIsGpu && BBuffer) destroyBuffers(BBuffer);
|
|
356
|
+
if (AinvBuffer) destroyBuffers(AinvBuffer);
|
|
339
357
|
destroyBuffers(scratchBuffers);
|
|
340
358
|
destroyBuffers(paramsBuffers);
|
|
341
359
|
}
|
package/src/strsv/strsv.mjs
CHANGED
|
@@ -12,9 +12,10 @@ import { resolveTimestamp, extractTimestamp } from "../util/benchmark.mjs";
|
|
|
12
12
|
import { getPipeline } from "../util/pipeline.mjs";
|
|
13
13
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
14
14
|
import { GpuMatrix } from "../classes/GpuMatrix.mjs";
|
|
15
|
+
import { BLOCK_SIZE } from "../util/constants.mjs";
|
|
16
|
+
import { requireSameDevice } from "../util/device.mjs";
|
|
15
17
|
|
|
16
18
|
// Blocked triangular solve via explicit block inversion (invert/apply/update passes) instead of barrier-per-row substitution.
|
|
17
|
-
const BLOCK_SIZE = 64;
|
|
18
19
|
|
|
19
20
|
// One shared buffer holds all blocks' params (offset blockIndex*stride) instead of one buffer per block — avoids the O(numBlocks) createBuffer/writeBuffer calls that dominated CPU time.
|
|
20
21
|
function packBlockParams(numBlocks, stride, fieldsPerBlock) {
|
|
@@ -45,6 +46,7 @@ export async function strsv(device, uplo, trans, diag, n, A, lda, x, incx, layou
|
|
|
45
46
|
|
|
46
47
|
if (!(device instanceof GPUDevice))
|
|
47
48
|
throw new Error("device must be a GPUDevice.");
|
|
49
|
+
requireSameDevice(device, "strsv", { A, x });
|
|
48
50
|
if (uplo !== "lower" && uplo !== "upper")
|
|
49
51
|
throw new Error("uplo must be 'lower' or 'upper'.");
|
|
50
52
|
if (trans !== "no-transpose" && trans !== "transpose")
|
|
@@ -65,6 +67,8 @@ export async function strsv(device, uplo, trans, diag, n, A, lda, x, incx, layou
|
|
|
65
67
|
throw new Error("A must be a GpuMatrix when x is a GpuVector.");
|
|
66
68
|
if (AIsGpu && !xIsGpu)
|
|
67
69
|
throw new Error("x must be a GpuVector when A is a GpuMatrix.");
|
|
70
|
+
if (AIsGpu && xIsGpu && A._buf === x._buf)
|
|
71
|
+
throw new Error("A and x must not reference the same GPU buffer.");
|
|
68
72
|
if (AIsGpu && lda !== A.lda)
|
|
69
73
|
throw new Error("lda must match A.lda when A is a GpuMatrix.");
|
|
70
74
|
if (AIsGpu && (A.rows < n || A.cols < n))
|
|
@@ -109,11 +113,11 @@ export async function strsv(device, uplo, trans, diag, n, A, lda, x, incx, layou
|
|
|
109
113
|
let invertParams = null;
|
|
110
114
|
|
|
111
115
|
try {
|
|
112
|
-
ABuffer = AIsGpu ? A._buf : uploadBuffer(A, "strsv-A", false);
|
|
113
|
-
xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "strsv-x", true);
|
|
116
|
+
ABuffer = AIsGpu ? A._buf : uploadBuffer(device, A, "strsv-A", false);
|
|
117
|
+
xBuffer = xIsGpu ? x._buf : uploadBuffer(device, x, "strsv-x", true);
|
|
114
118
|
// One BLOCK_SIZE x BLOCK_SIZE dense region per block (row-major), even
|
|
115
119
|
// though only a triangular half is ever nonzero — see strsv_invert_block.wgsl.
|
|
116
|
-
AinvBuffer = createStorageBuffer(
|
|
120
|
+
AinvBuffer = createStorageBuffer(device,
|
|
117
121
|
numBlocks * BLOCK_SIZE * BLOCK_SIZE * 4,
|
|
118
122
|
"strsv-Ainv",
|
|
119
123
|
);
|
|
@@ -135,10 +139,10 @@ export async function strsv(device, uplo, trans, diag, n, A, lda, x, incx, layou
|
|
|
135
139
|
});
|
|
136
140
|
updateParamsBuffer = createSharedParamsBuffer(device, updateData, "strsv-update-params");
|
|
137
141
|
|
|
138
|
-
const { commandEncoder, querySet } = beginTimedEncoder();
|
|
142
|
+
const { commandEncoder, querySet } = beginTimedEncoder(device);
|
|
139
143
|
|
|
140
144
|
// Pre-pass: every block's inverse, fully parallel, one dispatch.
|
|
141
|
-
invertParams = createParamsBuffer(
|
|
145
|
+
invertParams = createParamsBuffer(device,
|
|
142
146
|
[
|
|
143
147
|
{ value: n, type: "u32" },
|
|
144
148
|
{ value: lda, type: "u32" },
|
|
@@ -148,7 +152,7 @@ export async function strsv(device, uplo, trans, diag, n, A, lda, x, incx, layou
|
|
|
148
152
|
],
|
|
149
153
|
"strsv-invert-params",
|
|
150
154
|
);
|
|
151
|
-
const invertBindGroup = createBindGroup(invertPipeline.getBindGroupLayout(0), [
|
|
155
|
+
const invertBindGroup = createBindGroup(device, invertPipeline.getBindGroupLayout(0), [
|
|
152
156
|
ABuffer, AinvBuffer, invertParams,
|
|
153
157
|
]);
|
|
154
158
|
const invertDesc = querySet
|
|
@@ -163,7 +167,7 @@ export async function strsv(device, uplo, trans, diag, n, A, lda, x, incx, layou
|
|
|
163
167
|
const isLastPass = bi === blockStarts.length - 1;
|
|
164
168
|
const paramsOffset = blockIndex * stride;
|
|
165
169
|
|
|
166
|
-
const applyBindGroup = createBindGroup(applyPipeline.getBindGroupLayout(0), [
|
|
170
|
+
const applyBindGroup = createBindGroup(device, applyPipeline.getBindGroupLayout(0), [
|
|
167
171
|
AinvBuffer, xBuffer, { buffer: applyParamsBuffer, offset: paramsOffset, size: 16 },
|
|
168
172
|
]);
|
|
169
173
|
|
|
@@ -175,7 +179,7 @@ export async function strsv(device, uplo, trans, diag, n, A, lda, x, incx, layou
|
|
|
175
179
|
const remaining = forward ? n - blockEnd : blockStart;
|
|
176
180
|
if (remaining === 0) continue;
|
|
177
181
|
|
|
178
|
-
const updateBindGroup = createBindGroup(updatePipeline.getBindGroupLayout(0), [
|
|
182
|
+
const updateBindGroup = createBindGroup(device, updatePipeline.getBindGroupLayout(0), [
|
|
179
183
|
ABuffer, xBuffer, { buffer: updateParamsBuffer, offset: paramsOffset, size: 32 },
|
|
180
184
|
]);
|
|
181
185
|
|
|
@@ -183,10 +187,10 @@ export async function strsv(device, uplo, trans, diag, n, A, lda, x, incx, layou
|
|
|
183
187
|
encodePass(commandEncoder, updatePipeline, updateBindGroup, wgCount);
|
|
184
188
|
}
|
|
185
189
|
|
|
186
|
-
const ts = resolveTimestamp(commandEncoder, querySet);
|
|
187
|
-
const readBuffer = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
|
|
190
|
+
const ts = resolveTimestamp(device, commandEncoder, querySet);
|
|
191
|
+
const readBuffer = xIsGpu ? null : stageReadback(device, commandEncoder, xBuffer);
|
|
188
192
|
|
|
189
|
-
submit(commandEncoder);
|
|
193
|
+
submit(device, commandEncoder);
|
|
190
194
|
|
|
191
195
|
const gpuTimeMs = await extractTimestamp(ts);
|
|
192
196
|
|
package/src/util/benchmark.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @module devdocs/utility-functions/benchmark */
|
|
2
|
-
import {
|
|
2
|
+
import { isBenchmarkEnabled } from "../init.mjs";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Returns the `requestDevice` descriptor to pass to `adapter.requestDevice()`.
|
|
@@ -29,10 +29,9 @@ export function benchmarkMode(adapter, enabled) {
|
|
|
29
29
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUQuerySet GPUQuerySet}
|
|
30
30
|
* @see {@link https://developer.chrome.com/blog/new-in-webgpu-121 Chrome 121 — timestamp queries (querySet + timestampWrites pattern, quantization caveat)}
|
|
31
31
|
*/
|
|
32
|
-
export function beginTimestamp() {
|
|
33
|
-
if (!isBenchmarkEnabled())
|
|
32
|
+
export function beginTimestamp(device) {
|
|
33
|
+
if (!isBenchmarkEnabled(device))
|
|
34
34
|
return { querySet: null, passDescriptor: undefined };
|
|
35
|
-
const device = getDevice();
|
|
36
35
|
// Two slots: index 0 written when the pass begins, index 1 when it ends.
|
|
37
36
|
const querySet = device.createQuerySet({ type: "timestamp", count: 2 });
|
|
38
37
|
const passDescriptor = {
|
|
@@ -55,9 +54,8 @@ export function beginTimestamp() {
|
|
|
55
54
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/resolveQuerySet GPUCommandEncoder.resolveQuerySet()}
|
|
56
55
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer GPUCommandEncoder.copyBufferToBuffer()}
|
|
57
56
|
*/
|
|
58
|
-
export function resolveTimestamp(commandEncoder, querySet) {
|
|
57
|
+
export function resolveTimestamp(device, commandEncoder, querySet) {
|
|
59
58
|
if (!querySet) return null;
|
|
60
|
-
const device = getDevice();
|
|
61
59
|
// QUERY_RESOLVE and MAP_READ cannot be combined — two buffers are required.
|
|
62
60
|
// resolveBuffer: GPU writes resolved nanosecond timestamps here.
|
|
63
61
|
const resolveBuffer = device.createBuffer({
|
package/src/util/bindgroup.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/** @module devdocs/utility-functions/bindgroup */
|
|
2
|
-
import { getDevice } from "../init.mjs";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Creates a `GPUBindGroup` by mapping each buffer to sequential binding indices
|
|
@@ -16,8 +15,7 @@ import { getDevice } from "../init.mjs";
|
|
|
16
15
|
* @returns {GPUBindGroup}
|
|
17
16
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createBindGroup GPUDevice.createBindGroup()}
|
|
18
17
|
*/
|
|
19
|
-
export function createBindGroup(layout, buffers, startBinding = 0) {
|
|
20
|
-
const device = getDevice();
|
|
18
|
+
export function createBindGroup(device, layout, buffers, startBinding = 0) {
|
|
21
19
|
const entries = buffers.map((buffer, i) => ({
|
|
22
20
|
binding: startBinding + i,
|
|
23
21
|
resource: buffer instanceof GPUBuffer ? { buffer } : buffer,
|
package/src/util/buffer.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/** @module devdocs/utility-functions/buffer */
|
|
2
|
-
import { getDevice } from "../init.mjs";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Destroys one or more GPU buffers. Accepts individual buffers or arrays of buffers.
|
|
@@ -10,12 +9,38 @@ export function destroyBuffers(...buffers) {
|
|
|
10
9
|
buffers.flat().forEach((b) => b.destroy());
|
|
11
10
|
}
|
|
12
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Throws if `byteSize` is more than this device can bind as a storage buffer.
|
|
14
|
+
*
|
|
15
|
+
* Every storage buffer the library creates goes through here. WebGPU accepts an
|
|
16
|
+
* oversized `createBuffer` and only rejects it later, when it is bound — as a
|
|
17
|
+
* `GPUValidationError` naming a bind group index rather than an operand, which
|
|
18
|
+
* gives no clue which allocation was at fault. Failing at creation, with the
|
|
19
|
+
* buffer's own label, points straight at it.
|
|
20
|
+
*
|
|
21
|
+
* @param {GPUDevice} device
|
|
22
|
+
* @param {number} byteSize
|
|
23
|
+
* @param {string} label - the buffer's debug label, quoted in the error
|
|
24
|
+
* @throws {Error} if `byteSize` exceeds `maxStorageBufferBindingSize`
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
function requireStorageSize(device, byteSize, label) {
|
|
28
|
+
const maxSize = device.limits.maxStorageBufferBindingSize;
|
|
29
|
+
if (byteSize > maxSize) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Buffer "${label}" needs ${byteSize} bytes, exceeding this device's ` +
|
|
32
|
+
`maxStorageBufferBindingSize (${maxSize} bytes). The operands are too large for this device.`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
13
37
|
/**
|
|
14
38
|
* Creates a GPU storage buffer and uploads `data` into it via mapped-at-creation.
|
|
15
39
|
* The mapped view is constructed from `data`'s own typed-array constructor, so
|
|
16
40
|
* bits are copied as-is regardless of element type (e.g. a Uint32Array's raw
|
|
17
41
|
* bit patterns are preserved — critical for dasum's aux half, which must
|
|
18
42
|
* never pass through a Float32Array view and risk NaN-bit-pattern canonicalization).
|
|
43
|
+
* @param {GPUDevice} device
|
|
19
44
|
* @param {Float32Array|Uint32Array|Int32Array} data
|
|
20
45
|
* @param {string} [label] - debug label visible in browser DevTools GPU inspection
|
|
21
46
|
* @param {boolean} [readback=false] - add `COPY_SRC` so the buffer can be copied to a readback buffer
|
|
@@ -26,17 +51,9 @@ export function destroyBuffers(...buffers) {
|
|
|
26
51
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/unmap GPUBuffer.unmap()}
|
|
27
52
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUSupportedLimits GPUSupportedLimits} (`maxStorageBufferBindingSize`)
|
|
28
53
|
*/
|
|
29
|
-
export function uploadBuffer(data, label = "blas-input", readback = false) {
|
|
30
|
-
const device = getDevice();
|
|
31
|
-
|
|
32
|
-
// User-facing boundary: give a clear error instead of a cryptic GPUValidationError.
|
|
33
|
-
const maxSize = device.limits.maxStorageBufferBindingSize;
|
|
54
|
+
export function uploadBuffer(device, data, label = "blas-input", readback = false) {
|
|
34
55
|
const byteSize = data.byteLength;
|
|
35
|
-
|
|
36
|
-
throw new Error(
|
|
37
|
-
`Buffer size ${byteSize} bytes exceeds device limit of ${maxSize} bytes.`,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
56
|
+
requireStorageSize(device, byteSize, label);
|
|
40
57
|
|
|
41
58
|
const usage = readback
|
|
42
59
|
? GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
|
|
@@ -60,15 +77,17 @@ export function uploadBuffer(data, label = "blas-input", readback = false) {
|
|
|
60
77
|
/**
|
|
61
78
|
* Creates an uninitialised GPU storage buffer. Used for intermediate buffers
|
|
62
79
|
* that are written by a shader before being read.
|
|
80
|
+
* @param {GPUDevice} device
|
|
63
81
|
* @param {number} size - byte size
|
|
64
82
|
* @param {string} [label] - debug label visible in browser DevTools GPU inspection
|
|
65
83
|
* @param {number} [extraUsage=0] - additional `GPUBufferUsage` flags OR'd in alongside `STORAGE`
|
|
66
84
|
* (e.g. `GPUBufferUsage.COPY_DST` for a buffer that's also a `copyBufferToBuffer` destination)
|
|
67
85
|
* @returns {GPUBuffer}
|
|
86
|
+
* @throws {Error} if `size` exceeds the device's `maxStorageBufferBindingSize`
|
|
68
87
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createBuffer GPUDevice.createBuffer()}
|
|
69
88
|
*/
|
|
70
|
-
export function createStorageBuffer(size, label = "blas-storage", extraUsage = 0) {
|
|
71
|
-
|
|
89
|
+
export function createStorageBuffer(device, size, label = "blas-storage", extraUsage = 0) {
|
|
90
|
+
requireStorageSize(device, size, label);
|
|
72
91
|
return device.createBuffer({
|
|
73
92
|
label,
|
|
74
93
|
size,
|
|
@@ -79,13 +98,15 @@ export function createStorageBuffer(size, label = "blas-storage", extraUsage = 0
|
|
|
79
98
|
/**
|
|
80
99
|
* Creates a GPU storage buffer with `COPY_SRC` so its contents can be
|
|
81
100
|
* copied to a CPU-readable readback buffer after the shader runs.
|
|
101
|
+
* @param {GPUDevice} device
|
|
82
102
|
* @param {number} size - byte size
|
|
83
103
|
* @param {string} [label] - debug label visible in browser DevTools GPU inspection
|
|
84
104
|
* @returns {GPUBuffer}
|
|
105
|
+
* @throws {Error} if `size` exceeds the device's `maxStorageBufferBindingSize`
|
|
85
106
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createBuffer GPUDevice.createBuffer()}
|
|
86
107
|
*/
|
|
87
|
-
export function createResultBuffer(size, label = "blas-result") {
|
|
88
|
-
|
|
108
|
+
export function createResultBuffer(device, size, label = "blas-result") {
|
|
109
|
+
requireStorageSize(device, size, label);
|
|
89
110
|
return device.createBuffer({
|
|
90
111
|
label,
|
|
91
112
|
size,
|
|
@@ -97,13 +118,13 @@ export function createResultBuffer(size, label = "blas-result") {
|
|
|
97
118
|
* Appends a `copyBufferToBuffer` command to `commandEncoder` that copies
|
|
98
119
|
* `sourceBuffer` into a new `MAP_READ` buffer. Returns that readback buffer;
|
|
99
120
|
* call `readBuffer.mapAsync(GPUMapMode.READ)` after submitting the encoder.
|
|
121
|
+
* @param {GPUDevice} device
|
|
100
122
|
* @param {GPUCommandEncoder} commandEncoder
|
|
101
123
|
* @param {GPUBuffer} sourceBuffer
|
|
102
124
|
* @returns {GPUBuffer}
|
|
103
125
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/copyBufferToBuffer GPUCommandEncoder.copyBufferToBuffer()}
|
|
104
126
|
*/
|
|
105
|
-
export function stageReadback(commandEncoder, sourceBuffer) {
|
|
106
|
-
const device = getDevice();
|
|
127
|
+
export function stageReadback(device, commandEncoder, sourceBuffer) {
|
|
107
128
|
|
|
108
129
|
// COPY_DST: receives the copyBufferToBuffer transfer; MAP_READ: lets the CPU map and read it back.
|
|
109
130
|
const readBuffer = device.createBuffer({
|
|
@@ -121,17 +142,90 @@ export function stageReadback(commandEncoder, sourceBuffer) {
|
|
|
121
142
|
return readBuffer;
|
|
122
143
|
}
|
|
123
144
|
|
|
145
|
+
// Minimum bindable size for an array<vec4<f32>> view: one 16-byte element.
|
|
146
|
+
const VEC4_ELEM_BYTES = 16;
|
|
147
|
+
|
|
148
|
+
// Dummy STORAGE buffer bound into a kernel's unused vec4-view slot when the
|
|
149
|
+
// real operand can't host even one vec4 (tiny-matrix edge cases, where the
|
|
150
|
+
// stride check forces the scalar path anyway). Cached per device.
|
|
151
|
+
const _vec4Fallbacks = new WeakMap();
|
|
152
|
+
function vec4FallbackBuffer(device) {
|
|
153
|
+
let b = _vec4Fallbacks.get(device);
|
|
154
|
+
if (!b) {
|
|
155
|
+
b = device.createBuffer({
|
|
156
|
+
label: "blas-vec4-fallback",
|
|
157
|
+
size: VEC4_ELEM_BYTES,
|
|
158
|
+
usage: GPUBufferUsage.STORAGE,
|
|
159
|
+
});
|
|
160
|
+
_vec4Fallbacks.set(device, b);
|
|
161
|
+
}
|
|
162
|
+
return b;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Bind-group entry exposing `buffer`'s bytes as an `array<vec4<f32>>` view —
|
|
167
|
+
* the twin binding that lets a shader issue 16-byte vector loads alongside
|
|
168
|
+
* scalar access of the same storage (bind the same GPUBuffer twice: once as
|
|
169
|
+
* `array<f32>`, once through this). The view's size is rounded down to a
|
|
170
|
+
* multiple of 16 because some backends reject non-multiple-of-16 ranges for
|
|
171
|
+
* vec4 arrays; whenever a kernel's vector path is usable (stride % 4 == 0)
|
|
172
|
+
* the buffer size is itself a multiple of 16, so the rounding never truncates
|
|
173
|
+
* a component the vector path would actually read. Buffers smaller than one
|
|
174
|
+
* vec4 element get a shared dummy storage buffer bound instead — the shader
|
|
175
|
+
* never dereferences it on those shapes.
|
|
176
|
+
* @param {GPUBuffer|{buffer: GPUBuffer, offset?: number, size?: number}} entry - whole buffer or sub-range, matching what the scalar slot binds
|
|
177
|
+
* @returns {{buffer: GPUBuffer, offset: number, size: number}}
|
|
178
|
+
*/
|
|
179
|
+
export function vec4ViewBinding(device, entry) {
|
|
180
|
+
const buffer = entry instanceof GPUBuffer ? entry : entry.buffer;
|
|
181
|
+
const offset = entry instanceof GPUBuffer ? 0 : (entry.offset ?? 0);
|
|
182
|
+
const avail = (entry instanceof GPUBuffer ? entry.size : (entry.size ?? buffer.size - offset));
|
|
183
|
+
const size = Math.floor(avail / VEC4_ELEM_BYTES) * VEC4_ELEM_BYTES;
|
|
184
|
+
if (size < VEC4_ELEM_BYTES) {
|
|
185
|
+
return { buffer: vec4FallbackBuffer(device), offset: 0, size: VEC4_ELEM_BYTES };
|
|
186
|
+
}
|
|
187
|
+
return { buffer, offset, size };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Whether every in-bounds element of a matrix operand is reachable through
|
|
192
|
+
* the `array<vec4<f32>>` view that {@link vec4ViewBinding} produces for it.
|
|
193
|
+
* The view truncates the binding down to a multiple of 16 bytes, so a
|
|
194
|
+
* tightly-uploaded array can hold valid matrix elements past the view's end
|
|
195
|
+
* even while its stride is 4-aligned (e.g. a column-major m×1 operand with
|
|
196
|
+
* padded lda uploaded without padding cells). Call this with the kernel-side
|
|
197
|
+
* dimensions and only take a shader's vectorized path when it returns true;
|
|
198
|
+
* the scalar fallback reads the full storage and is always correct.
|
|
199
|
+
* @param {GPUBuffer|{buffer: GPUBuffer, offset?: number, size?: number}} entry - what the scalar slot binds
|
|
200
|
+
* @param {number} stride - the operand's leading dimension as seen by the kernel
|
|
201
|
+
* @param {number} outerCount - extent of the stride-multiplied dimension
|
|
202
|
+
* @param {number} innerCount - extent of the contiguous dimension
|
|
203
|
+
* @returns {boolean}
|
|
204
|
+
*/
|
|
205
|
+
export function vec4Usable(entry, stride, outerCount, innerCount) {
|
|
206
|
+
if (stride % 4 !== 0) return false;
|
|
207
|
+
const buffer = entry instanceof GPUBuffer ? entry : entry.buffer;
|
|
208
|
+
const offset = entry instanceof GPUBuffer ? 0 : (entry.offset ?? 0);
|
|
209
|
+
const avail = (entry instanceof GPUBuffer ? buffer.size : (entry.size ?? buffer.size - offset));
|
|
210
|
+
const viewFloats = Math.floor(avail / VEC4_ELEM_BYTES) * 4;
|
|
211
|
+
if (viewFloats <= 0) return false;
|
|
212
|
+
// Highest flat index any masked-in component can touch; usable iff its
|
|
213
|
+
// containing vec4 ends within the view.
|
|
214
|
+
const maxFlat = (Math.max(outerCount, 1) - 1) * stride + (Math.max(innerCount, 1) - 1);
|
|
215
|
+
return Math.floor(maxFlat / 4) * 4 + 4 <= viewFloats;
|
|
216
|
+
}
|
|
217
|
+
|
|
124
218
|
/**
|
|
125
219
|
* Packs an array of typed scalar values into a uniform buffer aligned to 16 bytes.
|
|
126
220
|
* Each entry specifies the value and its WGSL type (`"f32"`, `"u32"`, or `"i32"`).
|
|
127
221
|
* The order of entries must match the field order in the shader's `Params` struct.
|
|
222
|
+
* @param {GPUDevice} device
|
|
128
223
|
* @param {{ value: number, type: "f32"|"u32"|"i32" }[]} params
|
|
129
224
|
* @param {string} [label] - debug label visible in browser DevTools GPU inspection
|
|
130
225
|
* @returns {GPUBuffer}
|
|
131
226
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/writeBuffer GPUQueue.writeBuffer()}
|
|
132
227
|
*/
|
|
133
|
-
export function createParamsBuffer(params, label = "blas-params") {
|
|
134
|
-
const device = getDevice();
|
|
228
|
+
export function createParamsBuffer(device, params, label = "blas-params") {
|
|
135
229
|
|
|
136
230
|
const rawSize = params.length * 4;
|
|
137
231
|
const size = Math.ceil(rawSize / 16) * 16;
|
package/src/util/compute.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/** @module devdocs/utility-functions/compute */
|
|
2
|
-
import { getDevice } from "../init.mjs";
|
|
3
2
|
import { beginTimestamp, resolveTimestamp } from "./benchmark.mjs";
|
|
4
3
|
|
|
5
4
|
// Anchors the pass encoder to its command encoder to prevent premature GC.
|
|
@@ -10,8 +9,7 @@ const _passEncoders = new WeakMap();
|
|
|
10
9
|
* @param {GPUCommandEncoder} commandEncoder
|
|
11
10
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUQueue/submit GPUQueue.submit()}
|
|
12
11
|
*/
|
|
13
|
-
export function submit(commandEncoder) {
|
|
14
|
-
const device = getDevice();
|
|
12
|
+
export function submit(device, commandEncoder) {
|
|
15
13
|
device.queue.submit([commandEncoder.finish()]);
|
|
16
14
|
}
|
|
17
15
|
|
|
@@ -23,9 +21,8 @@ export function submit(commandEncoder) {
|
|
|
23
21
|
* @returns {{ commandEncoder: GPUCommandEncoder, querySet: GPUQuerySet|null, passDescriptor: object|undefined }}
|
|
24
22
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createCommandEncoder GPUDevice.createCommandEncoder()}
|
|
25
23
|
*/
|
|
26
|
-
export function beginTimedEncoder() {
|
|
27
|
-
const
|
|
28
|
-
const { querySet, passDescriptor } = beginTimestamp();
|
|
24
|
+
export function beginTimedEncoder(device) {
|
|
25
|
+
const { querySet, passDescriptor } = beginTimestamp(device);
|
|
29
26
|
const commandEncoder = device.createCommandEncoder();
|
|
30
27
|
return { commandEncoder, querySet, passDescriptor };
|
|
31
28
|
}
|
|
@@ -71,11 +68,11 @@ export function encodePass(commandEncoder, pipeline, bindGroup, workgroups, pass
|
|
|
71
68
|
* @returns {{ commandEncoder: GPUCommandEncoder, ts: any }} encoded commands and timestamp handle
|
|
72
69
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createCommandEncoder GPUDevice.createCommandEncoder()}
|
|
73
70
|
*/
|
|
74
|
-
export function runComputePass(pipeline, bindGroup, workgroups) {
|
|
75
|
-
const { commandEncoder, querySet, passDescriptor } = beginTimedEncoder();
|
|
71
|
+
export function runComputePass(device, pipeline, bindGroup, workgroups) {
|
|
72
|
+
const { commandEncoder, querySet, passDescriptor } = beginTimedEncoder(device);
|
|
76
73
|
encodePass(commandEncoder, pipeline, bindGroup, workgroups, passDescriptor);
|
|
77
74
|
|
|
78
|
-
const ts = resolveTimestamp(commandEncoder, querySet);
|
|
75
|
+
const ts = resolveTimestamp(device, commandEncoder, querySet);
|
|
79
76
|
|
|
80
77
|
return { commandEncoder, ts };
|
|
81
78
|
}
|