gputex 0.3.4 → 0.4.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 +52 -62
- package/dist/index.d.ts +8 -41
- package/dist/index.js +123 -186
- package/dist/three.d.ts +3 -15
- package/dist/three.js +128 -195
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,14 +46,14 @@ alpha or a normal map.
|
|
|
46
46
|
|
|
47
47
|
## WebGL fallback
|
|
48
48
|
|
|
49
|
-
WebGPU is the primary path. When it's unavailable (older Safari, Firefox without WebGPU, locked-down environments) `compressTexture()` automatically falls back to a **WebGL2** path that runs the same family of block encoders as fragment shaders — each 4×4 block is computed in one fragment, written to an `RGBA32UI` render target, and read back. The two backends are not byte-identical (the WebGPU
|
|
49
|
+
WebGPU is the primary path. When it's unavailable (older Safari, Firefox without WebGPU, locked-down environments) `compressTexture()` automatically falls back to a **WebGL2** path that runs the same family of block encoders as fragment shaders — each 4×4 block is computed in one fragment, written to an `RGBA32UI` render target, and read back. The two backends are not byte-identical (the WebGPU shaders use f16 where available), but they implement the same algorithms at the same quality level and the resulting `CompressedTexture` looks the same under either renderer.
|
|
50
50
|
|
|
51
51
|
The fallback chain is **WebGPU → WebGL2 → uncompressed RGBA8**. The `backend` field on the result (`'webgpu' | 'webgl' | 'none'`) tells you which path ran.
|
|
52
52
|
|
|
53
53
|
Notes on the WebGL path:
|
|
54
54
|
|
|
55
55
|
- It needs the matching WebGL2 compressed-texture extension to be sampleable: `EXT_texture_compression_bptc` (BC7), `EXT_texture_compression_rgtc` (BC5), `WEBGL_compressed_texture_astc` (ASTC), or `WEBGL_compressed_texture_s3tc` (BC1). Selection mirrors the WebGPU side, with BC1 added as a broadly-available last resort for **opaque** colour when neither BPTC nor ASTC is present.
|
|
56
|
-
-
|
|
56
|
+
- The `device` / `adapter` options apply to the WebGPU path only.
|
|
57
57
|
- All encoding happens on one shared, off-screen WebGL2 context; nothing is drawn to a visible canvas.
|
|
58
58
|
|
|
59
59
|
## Usage
|
|
@@ -67,38 +67,33 @@ const { texture, format } = await compressTexture('/cobblestone.avif', {
|
|
|
67
67
|
hint: 'color', // 'color' | 'colorWithAlpha' | 'normal'
|
|
68
68
|
colorSpace: 'srgb',
|
|
69
69
|
mipmaps: true,
|
|
70
|
-
quality: 'fast', // 'fast' (default) | 'high'
|
|
71
70
|
})
|
|
72
71
|
|
|
73
72
|
material.map = texture
|
|
74
73
|
```
|
|
75
74
|
|
|
76
|
-
####
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
- **`'high'`** — exhaustive endpoint search (farthest-pair seed, full nearest
|
|
99
|
-
search, p-bit search); matches the CPU reference encoders block-for-block
|
|
100
|
-
(byte-identical on >96% of blocks; the rest are equal-error FP tie-breaks,
|
|
101
|
-
enforced by the GPU test suite).
|
|
75
|
+
#### The encoding algorithm
|
|
76
|
+
|
|
77
|
+
There is a single encode mode, built to be both fast and high quality: a
|
|
78
|
+
principal-axis endpoint seed (per-block covariance power-iteration — unlike
|
|
79
|
+
a bbox diagonal it follows anti-correlated channels, worth **+2–4 dB on
|
|
80
|
+
normal-map-like content**) plus projection-based index assignment (each
|
|
81
|
+
pixel is projected onto the colinear endpoint line in O(1) instead of
|
|
82
|
+
searching every palette entry), and the block bits packed with
|
|
83
|
+
straight-line constant shifts. The formats with coarse 4-level palettes
|
|
84
|
+
(BC1, ASTC) add up to two least-squares endpoint refit rounds accepted per
|
|
85
|
+
block only when they lower the error, and BC5 one; BC7's 16-level mode-6
|
|
86
|
+
palette makes the refit redundant on a principal-axis seed (≤0.05 dB), so
|
|
87
|
+
it skips it and stays the cheapest per pixel. On GPUs that report the
|
|
88
|
+
`shader-f16` feature everything runs in f16 — the f32 shaders are the
|
|
89
|
+
automatic fallback.
|
|
90
|
+
|
|
91
|
+
On the repo's test cards this lands within **≤0.1 dB** of the exhaustive
|
|
92
|
+
per-block reference encoders (BC5 matches the reference exactly; ASTC and
|
|
93
|
+
BC1-on-normal-maps measure slightly above it), trailing only on adversarial
|
|
94
|
+
high-frequency noise, where any single-line seed loses to an exhaustive
|
|
95
|
+
search — while encoding an order of magnitude faster. See the benchmark
|
|
96
|
+
table below.
|
|
102
97
|
|
|
103
98
|
#### SVG sources
|
|
104
99
|
|
|
@@ -265,20 +260,16 @@ buffers, bind group) across encodes, so repeated encodes — including mip
|
|
|
265
260
|
chains — skip per-call allocation: in an interleaved A/B this cuts BC7
|
|
266
261
|
end-to-end wall time by ~10% at 512², ~20% at 1024–2048² and ~35% at 4096².
|
|
267
262
|
|
|
268
|
-
| Format |
|
|
269
|
-
| -------- |
|
|
270
|
-
| BC1 |
|
|
271
|
-
| BC1 |
|
|
272
|
-
| BC5 |
|
|
273
|
-
| BC5 |
|
|
274
|
-
| BC7 |
|
|
275
|
-
| BC7 |
|
|
276
|
-
| ASTC 4×4 |
|
|
277
|
-
| ASTC 4×4 |
|
|
278
|
-
| BC1 | high | f32 | 2.7 ms |
|
|
279
|
-
| BC5 | high | f32 | 1.0 ms |
|
|
280
|
-
| BC7 | high | f32 | 10.4 ms |
|
|
281
|
-
| ASTC 4×4 | high | f32 | 1.6 ms |
|
|
263
|
+
| Format | Shader | GPU pass |
|
|
264
|
+
| -------- | ------------- | ----------- |
|
|
265
|
+
| BC1 | f16 (default) | **0.26 ms** |
|
|
266
|
+
| BC1 | f32 | 0.46 ms |
|
|
267
|
+
| BC5 | f16 (default) | **0.26 ms** |
|
|
268
|
+
| BC5 | f32 | 0.26 ms |
|
|
269
|
+
| BC7 | f16 (default) | **0.26 ms** |
|
|
270
|
+
| BC7 | f32 | 0.59 ms |
|
|
271
|
+
| ASTC 4×4 | f16 (default) | **0.26 ms** |
|
|
272
|
+
| ASTC 4×4 | f32 | 0.56 ms |
|
|
282
273
|
|
|
283
274
|
Timestamps are quantised to 100 µs by Chrome and Apple GPU clock states swing
|
|
284
275
|
timings by ~2×, so sub-millisecond figures are indicative (±0.1 ms); compare
|
|
@@ -297,32 +288,31 @@ cd example && bunx next dev # then open http://localhost:3000/test
|
|
|
297
288
|
```
|
|
298
289
|
|
|
299
290
|
A second page, `/bench`, measures median end-to-end `encodeToBytes()` wall
|
|
300
|
-
time per format across image sizes (256²–4096²)
|
|
301
|
-
|
|
302
|
-
|
|
291
|
+
time per format across image sizes (256²–4096²) — the numbers that matter
|
|
292
|
+
for runtime streaming, where host overhead dominates small textures
|
|
293
|
+
(results on `window.__GPUTEX_BENCH__`).
|
|
303
294
|
|
|
304
295
|
The page runs three groups against the live WebGPU device and renders
|
|
305
296
|
PASS/FAIL tables (machine-readable copy on `window.__GPUTEX_TESTS__`):
|
|
306
297
|
|
|
307
|
-
- **Correctness** —
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
aggregate PSNR
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
encodes near-losslessly) must not exceed `'high'`'s error by more than a
|
|
320
|
-
small per-format limit.
|
|
298
|
+
- **Correctness** — determinism (same input twice → identical bytes) and the
|
|
299
|
+
clamp-to-edge padding path: a non-multiple-of-4 image must land within a
|
|
300
|
+
couple of dB of the exhaustive CPU reference encode (`gputex/testing`) — a
|
|
301
|
+
padding bug craters it.
|
|
302
|
+
- **Quality** — GPU output is CPU-decoded and validated on the FULL 1024²
|
|
303
|
+
test cards (every tile stresses a different failure mode) with two gates,
|
|
304
|
+
for both the f16 and (force-disabled-f16) f32 shaders: aggregate PSNR must
|
|
305
|
+
beat per-format thresholds pinned ~0.15 dB under the measured baseline,
|
|
306
|
+
and — because a handful of catastrophically wrong blocks barely moves
|
|
307
|
+
aggregate PSNR — the worst _easy_ block (one the exhaustive CPU reference
|
|
308
|
+
encodes near-losslessly) must not exceed the reference's error by more
|
|
309
|
+
than a small per-format limit.
|
|
321
310
|
- **Performance** — the benchmark table above: wall + GPU-pass time per
|
|
322
|
-
format ×
|
|
311
|
+
format × shader variant.
|
|
323
312
|
|
|
324
313
|
The `gputex/testing` entry point exports the CPU reference
|
|
325
|
-
encoders/decoders (`encodeBC7Mode6Block`, `decodeASTC4x4Block`, …)
|
|
314
|
+
encoders/decoders (`encodeBC7Mode6Block`, `decodeASTC4x4Block`, …) — the
|
|
315
|
+
exhaustive per-block yardstick the GPU shaders are gated against — so any
|
|
326
316
|
consumer can run the same validation.
|
|
327
317
|
|
|
328
318
|
## Requirements
|
package/dist/index.d.ts
CHANGED
|
@@ -49,19 +49,9 @@ interface EncoderOptions {
|
|
|
49
49
|
*/
|
|
50
50
|
disableF16?: boolean;
|
|
51
51
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Encoder quality level. 'fast' (default) uses the projection-based paths in
|
|
54
|
-
* the shaders — an order of magnitude faster for a ≤0.65 dB PSNR cost. 'high'
|
|
55
|
-
* runs the exhaustive search, matching the CPU reference encoders
|
|
56
|
-
* block-for-block (byte-identical up to FP tie-breaks with equal error).
|
|
57
|
-
* BC1's 'high' adds a principal-axis endpoint seed and iterative refit.
|
|
58
|
-
*/
|
|
59
|
-
type EncodeQuality = 'fast' | 'high';
|
|
60
52
|
interface EncodeCallOptions {
|
|
61
53
|
/** Tags the output color space. Forced 'linear' for encoders with supportsSrgb=false. */
|
|
62
54
|
colorSpace?: 'srgb' | 'linear';
|
|
63
|
-
/** Encode quality / speed trade-off. Default 'fast'. */
|
|
64
|
-
quality?: EncodeQuality;
|
|
65
55
|
}
|
|
66
56
|
/**
|
|
67
57
|
* Result of a raw bytes-only encode. This is the encoder's native output: the
|
|
@@ -126,11 +116,7 @@ declare abstract class Encoder {
|
|
|
126
116
|
readonly adapter?: GPUAdapter;
|
|
127
117
|
readonly ownsDevice: boolean;
|
|
128
118
|
readonly disableF16: boolean;
|
|
129
|
-
protected _module: GPUShaderModule | null;
|
|
130
|
-
protected _moduleF16: GPUShaderModule | null;
|
|
131
|
-
protected _pipelineF16: GPUComputePipeline | null;
|
|
132
119
|
protected _pipeline: GPUComputePipeline;
|
|
133
|
-
protected _pipelineCache: Map<EncodeQuality, GPUComputePipeline>;
|
|
134
120
|
private _cachedSrcTex;
|
|
135
121
|
private _cachedSrcW;
|
|
136
122
|
private _cachedSrcH;
|
|
@@ -138,18 +124,10 @@ declare abstract class Encoder {
|
|
|
138
124
|
private _cachedStaging;
|
|
139
125
|
private _cachedParams;
|
|
140
126
|
private _lastParams;
|
|
141
|
-
private
|
|
127
|
+
private _cachedBindGroup;
|
|
142
128
|
private _resourcesBusy;
|
|
143
129
|
constructor({ device, adapter, ownsDevice, disableF16 }: EncoderOptions);
|
|
144
130
|
protected _buildPipeline(): void;
|
|
145
|
-
/** The f32 module, parsed on first use (see `_module`). */
|
|
146
|
-
protected _ensureModule(): GPUShaderModule;
|
|
147
|
-
/**
|
|
148
|
-
* Pipeline for a given quality level. Encoders that don't declare a
|
|
149
|
-
* `QUALITY_HIGH` override (`supportsQuality === false`, e.g. BC1) ignore the
|
|
150
|
-
* argument and reuse the single pipeline. Specialised pipelines are cached.
|
|
151
|
-
*/
|
|
152
|
-
protected _getPipeline(quality: EncodeQuality): GPUComputePipeline;
|
|
153
131
|
destroy(): void;
|
|
154
132
|
/** Short lowercase identifier used in GPU object labels and errors. */
|
|
155
133
|
abstract get label(): string;
|
|
@@ -160,20 +138,14 @@ declare abstract class Encoder {
|
|
|
160
138
|
/** Whether this format has an sRGB variant. Default true. */
|
|
161
139
|
get supportsSrgb(): boolean;
|
|
162
140
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*/
|
|
167
|
-
get supportsQuality(): boolean;
|
|
168
|
-
/**
|
|
169
|
-
* Optional f16 WGSL for the 'fast' path. Used only when the device reports the
|
|
170
|
-
* `shader-f16` feature; the format's f32 `wgslSource()` is the fallback and
|
|
171
|
-
* `'high'` always uses it. Returns null when there's no f16 variant.
|
|
141
|
+
* Optional f16 WGSL variant. Used only when the device reports the
|
|
142
|
+
* `shader-f16` feature; the format's f32 `wgslSource()` is the automatic
|
|
143
|
+
* fallback. Returns null when there's no f16 variant.
|
|
172
144
|
*/
|
|
173
145
|
wgslSourceFastF16(): string | null;
|
|
174
|
-
/** Whether the f16
|
|
146
|
+
/** Whether the f16 shader is both available and supported on this device. */
|
|
175
147
|
protected get _useF16(): boolean;
|
|
176
|
-
/** WGSL compute-shader source. */
|
|
148
|
+
/** WGSL compute-shader source (f32; the fallback when f16 is unavailable). */
|
|
177
149
|
abstract wgslSource(): string;
|
|
178
150
|
/** e.g. 'bc1-rgba-unorm-srgb'. */
|
|
179
151
|
abstract gpuTextureFormat(opts: FormatVariant): GPUTextureFormat;
|
|
@@ -190,9 +162,8 @@ declare abstract class Encoder {
|
|
|
190
162
|
* bytes into a `CompressedTexture`; callers targeting another engine feed
|
|
191
163
|
* `data` into that engine's compressed-texture upload directly.
|
|
192
164
|
*/
|
|
193
|
-
encodeToBytes(source: EncoderImageSource, { flipY,
|
|
165
|
+
encodeToBytes(source: EncoderImageSource, { flipY, withGpuTime }?: {
|
|
194
166
|
flipY?: boolean;
|
|
195
|
-
quality?: EncodeQuality;
|
|
196
167
|
withGpuTime?: boolean;
|
|
197
168
|
}): Promise<EncodeBytesResult>;
|
|
198
169
|
}
|
|
@@ -203,7 +174,6 @@ declare class BC1Encoder extends Encoder {
|
|
|
203
174
|
get label(): string;
|
|
204
175
|
get bytesPerBlock(): number;
|
|
205
176
|
get supportsSrgb(): boolean;
|
|
206
|
-
get supportsQuality(): boolean;
|
|
207
177
|
wgslSource(): string;
|
|
208
178
|
wgslSourceFastF16(): string | null;
|
|
209
179
|
gpuTextureFormat({ colorSpace }: FormatVariant): GPUTextureFormat;
|
|
@@ -215,7 +185,6 @@ declare class BC5Encoder extends Encoder {
|
|
|
215
185
|
get label(): string;
|
|
216
186
|
get bytesPerBlock(): number;
|
|
217
187
|
get supportsSrgb(): boolean;
|
|
218
|
-
get supportsQuality(): boolean;
|
|
219
188
|
wgslSource(): string;
|
|
220
189
|
wgslSourceFastF16(): string;
|
|
221
190
|
gpuTextureFormat(): GPUTextureFormat;
|
|
@@ -227,7 +196,6 @@ declare class BC7Encoder extends Encoder {
|
|
|
227
196
|
get label(): string;
|
|
228
197
|
get bytesPerBlock(): number;
|
|
229
198
|
get supportsSrgb(): boolean;
|
|
230
|
-
get supportsQuality(): boolean;
|
|
231
199
|
wgslSource(): string;
|
|
232
200
|
wgslSourceFastF16(): string;
|
|
233
201
|
gpuTextureFormat({ colorSpace }: FormatVariant): GPUTextureFormat;
|
|
@@ -239,7 +207,6 @@ declare class ASTC4x4Encoder extends Encoder {
|
|
|
239
207
|
get label(): string;
|
|
240
208
|
get bytesPerBlock(): number;
|
|
241
209
|
get supportsSrgb(): boolean;
|
|
242
|
-
get supportsQuality(): boolean;
|
|
243
210
|
wgslSource(): string;
|
|
244
211
|
wgslSourceFastF16(): string;
|
|
245
212
|
gpuTextureFormat({ colorSpace }: FormatVariant): GPUTextureFormat;
|
|
@@ -474,4 +441,4 @@ interface RasterizeSvgOptions {
|
|
|
474
441
|
*/
|
|
475
442
|
declare function rasterizeSvg(source: string | Blob, options?: RasterizeSvgOptions): Promise<ImageBitmap>;
|
|
476
443
|
|
|
477
|
-
export { ASTC4x4Encoder, ASTC4x4WebGLEncoder, BC1Encoder, BC1WebGLEncoder, BC5Encoder, BC5WebGLEncoder, BC7Encoder, BC7WebGLEncoder, type Capabilities, type EncodeBytesResult, type EncodeCallOptions,
|
|
444
|
+
export { ASTC4x4Encoder, ASTC4x4WebGLEncoder, BC1Encoder, BC1WebGLEncoder, BC5Encoder, BC5WebGLEncoder, BC7Encoder, BC7WebGLEncoder, type Capabilities, type EncodeBytesResult, type EncodeCallOptions, Encoder, type EncoderConstructor, type EncoderImageSource, type EncoderOptions, type ExtensionProvider, type FeatureProvider, type FormatSelection, type FormatVariant, type MipLevel, type PreferredFormat, type RasterizeSvgOptions, type RawPixelSource, type SelectFormatOptions, type SvgRasterSize, TextureFormat, type TextureHint, WebGLBlockEncoder, type WebGLCapabilities, type WebGLEncodeBytesResult, type WebGLEncoderConstructor, type WebGLEncoderImageSource, type WebGLEncoderOptions, type WebGLFormatSelection, WebGPUFeature, createWebGLContext, detectCapabilities, detectWebGLCapabilities, generateMipChain, getSharedWebGLContext, isWebGLAvailable, padToBlockMultiple, rasterizeSvg, selectFormat, selectWebGLFormat };
|