gputex 0.6.0 → 0.7.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 +63 -37
- package/dist/index.d.ts +15 -7
- package/dist/index.js +533 -587
- package/dist/testing.d.ts +8 -5
- package/dist/testing.js +190 -34
- package/dist/three.js +533 -587
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,13 +83,19 @@ a bbox diagonal it follows anti-correlated channels, worth **+2–4 dB on
|
|
|
83
83
|
normal-map-like content**) plus projection-based index assignment (each
|
|
84
84
|
pixel is projected onto the colinear endpoint line in O(1) instead of
|
|
85
85
|
searching every palette entry), and the block bits packed with
|
|
86
|
-
straight-line constant shifts.
|
|
87
|
-
|
|
88
|
-
block only when they lower the error
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
straight-line constant shifts. BC1's coarse 4-level palette adds up to two
|
|
87
|
+
least-squares endpoint refit rounds, solved from per-pass projection
|
|
88
|
+
moments and accepted per block only when they lower the error; its
|
|
89
|
+
near-flat blocks skip the line fit and take the endpoint pair whose ⅔/⅓
|
|
90
|
+
interpolant lands nearest the block colour (direct 565 rounding is up to 4
|
|
91
|
+
levels off — worth up to +3.9 dB on maps with flat regions). BC5 refits
|
|
92
|
+
once; BC7's 16-level mode-6 palette makes the refit redundant on a
|
|
93
|
+
principal-axis seed (≤0.05 dB). ASTC spends every one of its 128 bits: a
|
|
94
|
+
wide-span opaque block gets 16 weight levels with 192-level (trit-coded)
|
|
95
|
+
endpoints, a small-span one exact 8-bit endpoints with 8 levels,
|
|
96
|
+
exactly-grayscale blocks a luminance-only mode with 32 levels. On GPUs that
|
|
97
|
+
report the `shader-f16` feature everything runs in f16 — the f32 shaders
|
|
98
|
+
are the automatic fallback.
|
|
93
99
|
|
|
94
100
|
ETC2 is the exception to the endpoint-line story: its blocks are per-subblock
|
|
95
101
|
base colours shifted by scalar modifier tables. The encoder exploits the
|
|
@@ -109,12 +115,12 @@ stay f32 (they overflow f16), so the two modules produce byte-identical
|
|
|
109
115
|
output — f16 buys register pressure on mobile GPUs, not different
|
|
110
116
|
results.
|
|
111
117
|
|
|
112
|
-
On the repo's test
|
|
113
|
-
per-block reference encoders (
|
|
114
|
-
BC1
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
table below.
|
|
118
|
+
On the repo's test textures this lands within a few tenths of a dB of the
|
|
119
|
+
per-block CPU reference encoders (`gputex/testing`) and above them on
|
|
120
|
+
several (BC5 matches exactly; BC1 on flat content, BC7 and ASTC on some
|
|
121
|
+
maps measure above), trailing only on adversarial high-frequency noise,
|
|
122
|
+
where any single-line seed loses to an exhaustive search — while encoding
|
|
123
|
+
an order of magnitude faster. See the benchmark table below.
|
|
118
124
|
|
|
119
125
|
#### SVG sources
|
|
120
126
|
|
|
@@ -331,39 +337,46 @@ readback) rather than a round trip per level.
|
|
|
331
337
|
|
|
332
338
|
## Benchmarks
|
|
333
339
|
|
|
334
|
-
Measured
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
buffers, bind group) across encodes, so repeated encodes — including mip
|
|
340
|
-
chains — skip per-call allocation: in an interleaved A/B this cuts BC7
|
|
341
|
-
end-to-end wall time by ~10% at 512², ~20% at 1024–2048² and ~35% at 4096².
|
|
340
|
+
Measured on an Apple Silicon GPU (`metal-3`, M3) in Chrome with the `/eval`
|
|
341
|
+
dev page: per-dispatch compute time from timestamp queries over batches of
|
|
342
|
+
back-to-back dispatches, all variants interleaved in one session, encoding
|
|
343
|
+
the procedural 2048×2048 benchmark image. **GPU pass** is the compute
|
|
344
|
+
shader alone.
|
|
342
345
|
|
|
343
346
|
| Format | Shader | GPU pass |
|
|
344
347
|
| -------- | ------------- | ----------- |
|
|
345
|
-
| BC1 | f16 (default) | **0.
|
|
346
|
-
| BC1 | f32 | 0.
|
|
347
|
-
| BC5 | f16 (default) | **0.
|
|
348
|
-
| BC5 | f32 | 0.
|
|
349
|
-
| BC7 | f16 (default) | **0.
|
|
350
|
-
| BC7 | f32 | 0.
|
|
351
|
-
| ASTC 4×4 | f16 (default) | **0.
|
|
352
|
-
| ASTC 4×4 | f32 | 0.
|
|
348
|
+
| BC1 | f16 (default) | **0.33 ms** |
|
|
349
|
+
| BC1 | f32 | 0.50 ms |
|
|
350
|
+
| BC5 | f16 (default) | **0.14 ms** |
|
|
351
|
+
| BC5 | f32 | 0.15 ms |
|
|
352
|
+
| BC7 | f16 (default) | **0.19 ms** |
|
|
353
|
+
| BC7 | f32 | 0.52 ms |
|
|
354
|
+
| ASTC 4×4 | f16 (default) | **0.17 ms** |
|
|
355
|
+
| ASTC 4×4 | f32 | 0.28 ms |
|
|
353
356
|
| ETC2 | f16 + f32 | 0.20 ms |
|
|
354
357
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
358
|
+
End-to-end `encodeToBytes()` wall time adds the upload and the readback.
|
|
359
|
+
Each encoder caches its GPU resources (source texture, output/staging
|
|
360
|
+
buffers, bind groups) across encodes, and outputs above ~3 MB are encoded
|
|
361
|
+
in row bands — one submission and staging buffer per ~2 MB of output, so
|
|
362
|
+
the readback of one band (a GPU-process copy that dominated large encodes)
|
|
363
|
+
overlaps the compute of the next — with the result array pre-faulted while
|
|
364
|
+
the GPU works. In an interleaved A/B against the single-submission
|
|
365
|
+
readback this cuts wall time by 23–33% at 4096² and 5–25% at 2048²
|
|
366
|
+
(bytes identical); a fresh 4096² encode is then dominated by the ~8 ms
|
|
367
|
+
`copyExternalImageToTexture` upload.
|
|
368
|
+
|
|
369
|
+
On a 100 GB/s part just reading the 2048² RGBA8 source costs ~0.15 ms, so
|
|
370
|
+
BC5/BC7/ASTC/ETC2 sit within ~1.3× of simply touching the bytes; BC1's
|
|
371
|
+
refit rounds keep it ALU-bound. Two faster ETC2 variants live in git
|
|
372
|
+
history and were deliberately not shipped: a two-pass 2 B/px prepared source (encode pass
|
|
360
373
|
0.115 ms, but the prep pass is also bandwidth-bound and cannot overlap, so
|
|
361
374
|
the per-texture total regressed) and an O(1) hedged table pick (−3% for
|
|
362
375
|
−0.5 dB — a poor trade against the scored search).
|
|
363
376
|
|
|
364
|
-
|
|
365
|
-
timings by ~2
|
|
366
|
-
|
|
377
|
+
Single-dispatch timestamps are coarse and Apple GPU clock states swing
|
|
378
|
+
timings by up to ~2× across page loads, so compare variants only within a
|
|
379
|
+
single session, interleaved (as `/eval` and `/ab` do).
|
|
367
380
|
|
|
368
381
|
## Testing
|
|
369
382
|
|
|
@@ -382,6 +395,19 @@ time per format across image sizes (256²–4096²) — the numbers that matter
|
|
|
382
395
|
for runtime streaming, where host overhead dominates small textures
|
|
383
396
|
(results on `window.__GPUTEX_BENCH__`).
|
|
384
397
|
|
|
398
|
+
For shader work, `/eval` compares WGSL variants on speed AND quality in one
|
|
399
|
+
session: `example/scripts/ab-sync.sh` snapshots the working-tree shaders
|
|
400
|
+
(`<fmt>_work`) and git HEAD's (`<fmt>_head`) into `example/public/ab/`, and
|
|
401
|
+
`/eval?shaders=bc7_f16_head,bc7_f16_work` times them interleaved and scores
|
|
402
|
+
each image through the GPU's own hardware decoder (the encoded blocks are
|
|
403
|
+
copied into a real compressed texture and sampled), reporting PSNR, blocks
|
|
404
|
+
that got worse/better, the worst regression on blocks the first variant
|
|
405
|
+
encodes near-losslessly, and byte-identical coverage — over the full
|
|
406
|
+
1K/2K/4K texture corpus in seconds (`window.__GPUTEX_EVAL__`). For host-side
|
|
407
|
+
changes, `example/scripts/ab-lib.sh` builds git HEAD's library and the
|
|
408
|
+
working tree side by side (`/ab/gputex_head.js`, `/ab/gputex_work.js`) so
|
|
409
|
+
both can be driven in one page with alternating calls.
|
|
410
|
+
|
|
385
411
|
The page runs three groups against the live WebGPU device and renders
|
|
386
412
|
PASS/FAIL tables (machine-readable copy on `window.__GPUTEX_TESTS__`):
|
|
387
413
|
|
package/dist/index.d.ts
CHANGED
|
@@ -165,10 +165,10 @@ declare abstract class Encoder {
|
|
|
165
165
|
private _cachedSrcSource;
|
|
166
166
|
private _cachedSrcFlipY;
|
|
167
167
|
private _cachedDst;
|
|
168
|
-
private
|
|
168
|
+
private _cachedStagings;
|
|
169
169
|
private _cachedParams;
|
|
170
|
-
private
|
|
171
|
-
private
|
|
170
|
+
private _lastParamsKey;
|
|
171
|
+
private _cachedBindGroups;
|
|
172
172
|
private _cachedPrepPlanes;
|
|
173
173
|
private _cachedPrepBindGroup;
|
|
174
174
|
private _resourcesBusy;
|
|
@@ -182,8 +182,9 @@ declare abstract class Encoder {
|
|
|
182
182
|
private _chainPrepBindGroups;
|
|
183
183
|
private _chainParams;
|
|
184
184
|
private _chainBindGroups;
|
|
185
|
+
private _chainBandBindGroups;
|
|
185
186
|
private _chainDst;
|
|
186
|
-
private
|
|
187
|
+
private _chainStagings;
|
|
187
188
|
private _chainBusy;
|
|
188
189
|
constructor({ device, adapter, ownsDevice, disableF16 }: EncoderOptions);
|
|
189
190
|
protected _buildPipeline(): void;
|
|
@@ -295,10 +296,17 @@ declare abstract class Encoder {
|
|
|
295
296
|
/** Block-grid geometry + packed output offsets for a chain of levels.
|
|
296
297
|
* `byteSpan` is both the dst buffer size and the readback copy size (a
|
|
297
298
|
* multiple of 4: byteLen is a multiple of bytesPerBlock ≥ 8, offsets are
|
|
298
|
-
* CHAIN_ALIGN-ed). */
|
|
299
|
+
* CHAIN_ALIGN-ed). `bands` splits the base level into row bands. */
|
|
299
300
|
private _chainGeometry;
|
|
300
|
-
/**
|
|
301
|
-
*
|
|
301
|
+
/** One uniform buffer, one write: level i's { blocksX, blocksY, width,
|
|
302
|
+
* height, y0 = 0 } at slot i, then base-level band b at slot
|
|
303
|
+
* levels + b (y0 = the band's first block row). Slots are CHAIN_ALIGN
|
|
304
|
+
* apart. */
|
|
305
|
+
private _createChainParams;
|
|
306
|
+
/** Shared chain-encode tail: one submission per base-level row band —
|
|
307
|
+
* band b dispatches its rows of level 0, the last band also every tail
|
|
308
|
+
* level — each read back through its own staging buffer as it lands,
|
|
309
|
+
* then sliced into per-level byte arrays. */
|
|
302
310
|
private _submitChainAndRead;
|
|
303
311
|
/** Create the query set + resolve/staging buffers for one timed
|
|
304
312
|
* submission, or null when the device lacks 'timestamp-query'. */
|