gpu-atlas 0.1.0 → 0.2.1
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 +287 -248
- package/dist/gpu-atlas.js +188 -168
- package/dist/gpu-atlas.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/package.json +59 -59
package/README.md
CHANGED
|
@@ -1,248 +1,287 @@
|
|
|
1
|
-
# gpu-atlas
|
|
2
|
-
|
|
3
|
-
**What WebGPU actually does on a device — measured, not declared.**
|
|
4
|
-
|
|
5
|
-
**[Run it on your device →](https://ahnminjae08043-glitch.github.io/gpu-atlas/)**
|
|
6
|
-
Takes a few seconds. Nothing is uploaded; the profile stays in your browser
|
|
7
|
-
unless you save it yourself. It records the GPU and browser, not your user agent
|
|
8
|
-
string — a profile is meant to be shareable without handing over more than the
|
|
9
|
-
device description it is about.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## What measuring three devices turned up
|
|
14
|
-
|
|
15
|
-
| | desktop |
|
|
16
|
-
|---|---|---|---|
|
|
17
|
-
| GPU | NVIDIA Lovelace | Apple
|
|
18
|
-
| browser | Chrome 151 | Safari 26 | Samsung Internet 30 |
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
it
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
**
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
**
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
`
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
1
|
+
# gpu-atlas
|
|
2
|
+
|
|
3
|
+
**What WebGPU actually does on a device — measured, not declared.**
|
|
4
|
+
|
|
5
|
+
**[Run it on your device →](https://ahnminjae08043-glitch.github.io/gpu-atlas/)**
|
|
6
|
+
Takes a few seconds. Nothing is uploaded; the profile stays in your browser
|
|
7
|
+
unless you save it yourself. It records the GPU and browser, not your user agent
|
|
8
|
+
string — a profile is meant to be shareable without handing over more than the
|
|
9
|
+
device description it is about.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What measuring three devices turned up
|
|
14
|
+
|
|
15
|
+
| | desktop | tablet | phone |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| GPU | NVIDIA Lovelace | Apple | Adreno 7xx |
|
|
18
|
+
| browser | Chrome 151 | Safari 26.6 | Samsung Internet 30 |
|
|
19
|
+
| OS | Windows | iPadOS | Android 16 |
|
|
20
|
+
|
|
21
|
+
The tablet is an iPad, and finding that out took longer than it should have.
|
|
22
|
+
Since iPadOS 13 Safari sends a Macintosh user agent containing neither `iPad`
|
|
23
|
+
nor `Mobi`, so the probe filed it as a desktop and left `platform` empty — a
|
|
24
|
+
profile that could not say whether it came from a Mac or an iPad. Schema 5 uses
|
|
25
|
+
`maxTouchPoints` to tell them apart. Everything below was re-measured after the
|
|
26
|
+
fix; profiles at schema 4 and earlier cannot make the distinction at all.
|
|
27
|
+
|
|
28
|
+
### Performance does not scale by a single factor
|
|
29
|
+
|
|
30
|
+
| benchmark | desktop | iPad | Adreno | spread |
|
|
31
|
+
|---|---:|---:|---:|---:|
|
|
32
|
+
| triangle throughput | 4,246 MTri/s | 356 | 65 | **65x** |
|
|
33
|
+
| texture sampling | 254 GSample/s | 17.4 | 11.0 | 23x |
|
|
34
|
+
| fill rate | 127,117 MPixel/s | 36,263 | 6,522 | 20x |
|
|
35
|
+
| fragment ALU | 3,122 MPixel/s | 266 | 179 | 18x |
|
|
36
|
+
| bind group switching | 4,067,797 /s | 888,889 | 279,720 | 14x |
|
|
37
|
+
| pipeline switching | 1,904,762 /s | 1,200,000 | 181,818 | 11x |
|
|
38
|
+
| draw call overhead | 6,185,567 /s | 1,828,571 | 764,331 | 8x |
|
|
39
|
+
|
|
40
|
+
Geometry spreads nearly three times wider than the next-widest axis, and the
|
|
41
|
+
ordering is not uniform either. Against the iPad the desktop's geometry lead
|
|
42
|
+
(12x) is ordinary and its texture sampling lead (15x) is the largest; against
|
|
43
|
+
Adreno, geometry is the worst axis by a distance. Tile-based mobile GPUs pay
|
|
44
|
+
binning cost on vertices, so **on phones, cutting triangles buys far more than
|
|
45
|
+
cutting pixels** — the reverse of the desktop instinct.
|
|
46
|
+
|
|
47
|
+
Pipeline switching is the flattest axis of all: the iPad is within 1.6x of a
|
|
48
|
+
desktop discrete GPU there while being 12x behind on geometry. Any single "this
|
|
49
|
+
device is N times slower" number would be wrong in both directions at once.
|
|
50
|
+
|
|
51
|
+
Draw call overhead came back flagged `unreliable` on the desktop — the run
|
|
52
|
+
varied more than 15% between samples. The number is printed rather than hidden,
|
|
53
|
+
with the flag attached.
|
|
54
|
+
|
|
55
|
+
### Capability differences that break code
|
|
56
|
+
|
|
57
|
+
**`maxUniformBufferBindingSize` differs by 10,923x.** The iPad allows
|
|
58
|
+
715,827,880 bytes; Chrome and Samsung Internet cap at 65,536. Code developed
|
|
59
|
+
against large uniform buffers does not merely run slower elsewhere — it fails
|
|
60
|
+
outright. Treat the upper figure as one device's, not WebKit's: it is large
|
|
61
|
+
enough to look memory-derived, and no second Apple device has been measured.
|
|
62
|
+
|
|
63
|
+
**No compressed texture format works on all three.** Desktop Chrome has BC only
|
|
64
|
+
(6 formats). The iPad has ETC2 and ASTC but no BC (5). Adreno has all three
|
|
65
|
+
(11). Desktop Chrome and the iPad share *zero* — and across all three devices
|
|
66
|
+
the intersection is also empty, so there is no single compressed format to ship.
|
|
67
|
+
|
|
68
|
+
**`bgra8unorm` is storage-writable on desktop and on the iPad, but not on
|
|
69
|
+
Adreno.** It is also the preferred canvas format on those same two, while the
|
|
70
|
+
phone reports `rgba8unorm` — an easy thing to build on and have fail on phones.
|
|
71
|
+
|
|
72
|
+
`maxStorageBufferBindingSize` spans 16x (2.1GB / 716MB / 134MB) and
|
|
73
|
+
`maxBufferSize` 3x.
|
|
74
|
+
|
|
75
|
+
### Browsers quantize GPU timestamps, and by different amounts
|
|
76
|
+
|
|
77
|
+
`timestamp-query` results are rounded into buckets as a Spectre mitigation.
|
|
78
|
+
Measured rather than assumed:
|
|
79
|
+
|
|
80
|
+
| | GPU timer | `performance.now()` |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| Chrome 151 | 65,536 ns (2^16) | 0.1 ms |
|
|
83
|
+
| Samsung Internet 30 | 65,536 ns (2^16) | 0.1 ms |
|
|
84
|
+
| Safari 26.6 | no quantization detected | 1 ms |
|
|
85
|
+
|
|
86
|
+
Both Chromium browsers return exactly 2^16 despite different GPU vendors and
|
|
87
|
+
operating systems, while WebKit does not quantize the GPU timer at all — this is
|
|
88
|
+
browser policy, not hardware.
|
|
89
|
+
|
|
90
|
+
The practical consequence: **on Chromium, GPU work shorter than ~65 microseconds
|
|
91
|
+
cannot be measured.** It reports as zero or as a value indistinguishable from
|
|
92
|
+
unrelated work. Before accounting for this, two unrelated benchmarks here
|
|
93
|
+
reported byte-identical timings.
|
|
94
|
+
|
|
95
|
+
### A benchmark that measured nothing
|
|
96
|
+
|
|
97
|
+
Fragment work was originally created by stacking identical opaque fullscreen
|
|
98
|
+
draws. On the iPad that reported 112,524 MPixel/s — a 36x *advantage* over an
|
|
99
|
+
RTX 4060, which is not plausible. A tile-based deferred renderer discards
|
|
100
|
+
occluded opaque fragments before shading them, so every draw but the last was
|
|
101
|
+
being thrown away. Adreno, tile-based but not deferred to the same degree, did
|
|
102
|
+
not do this, so the same benchmark id was measuring different work per
|
|
103
|
+
architecture.
|
|
104
|
+
|
|
105
|
+
Additive blending fixes it, since each draw must contribute to the accumulated
|
|
106
|
+
result. The corrected figure is 266 MPixel/s — **423x lower**, and in line with
|
|
107
|
+
the rest of that device's numbers. Geometry throughput was unaffected, as its
|
|
108
|
+
triangles occupy distinct screen positions and never occluded one another, which
|
|
109
|
+
is the signature a real fix should have.
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Usage
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npm install gpu-atlas
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Or without installing anything, straight from a CDN:
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
import { probe } from 'https://esm.sh/gpu-atlas';
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
import { probe, breakingIssues, pickFormat } from 'gpu-atlas';
|
|
126
|
+
|
|
127
|
+
const profile = await probe();
|
|
128
|
+
|
|
129
|
+
// Anything that will break on this device
|
|
130
|
+
for (const issue of breakingIssues(profile)) {
|
|
131
|
+
console.warn(issue.subject, issue.detail);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Pick a format verified to work here, rather than one merely declared
|
|
135
|
+
const hdr = pickFormat(profile, ['rgba16float', 'rgb10a2unorm', 'rgba8unorm'], 'render');
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Comparing devices — this needs no GPU, so it also works in Node:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
import { compareProfiles, formatComparison } from 'gpu-atlas';
|
|
142
|
+
|
|
143
|
+
const comparison = compareProfiles([desktop, tablet, phone]);
|
|
144
|
+
console.log(formatComparison(comparison));
|
|
145
|
+
|
|
146
|
+
// Sorted by spread, so the worst portability risk is first
|
|
147
|
+
const worst = comparison.benchmarks[0];
|
|
148
|
+
console.log(worst.id, worst.ratio); // "triangle-throughput", 65.2
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Measurements flagged `unreliable` — quantized or unstable — are marked rather
|
|
152
|
+
than folded silently into a ratio.
|
|
153
|
+
|
|
154
|
+
## What it measures
|
|
155
|
+
|
|
156
|
+
**Texture formats.** 53 formats, each checked for six separate capabilities:
|
|
157
|
+
creation, shader sampling, render target, blending, storage binding, and 4x
|
|
158
|
+
MSAA. A format that creates fine but fails to bind is a real failure mode, and
|
|
159
|
+
it is invisible in the feature list.
|
|
160
|
+
|
|
161
|
+
**WGSL compilation.** Cases where implementations diverge: function pointers,
|
|
162
|
+
dynamic uniform indexing, struct alignment, override constants, workgroup
|
|
163
|
+
atomics, uniformity analysis. Chrome uses Dawn/Tint, Firefox uses wgpu/naga,
|
|
164
|
+
Safari has its own compiler, and each targets a different backend language.
|
|
165
|
+
Compile time is recorded too, since it drives first-frame stalls.
|
|
166
|
+
|
|
167
|
+
**Limits.** Declared values are requested for real, then bisected to find the
|
|
168
|
+
actual ceiling when a device refuses.
|
|
169
|
+
|
|
170
|
+
**Benchmarks.** Separated by axis rather than collapsed into a score, for the
|
|
171
|
+
reason the table above demonstrates.
|
|
172
|
+
|
|
173
|
+
## Measurement notes
|
|
174
|
+
|
|
175
|
+
Getting numbers is easy; getting numbers that mean anything was most of the work.
|
|
176
|
+
|
|
177
|
+
**Quantization is measured, not assumed** — and validated before it is believed.
|
|
178
|
+
Work below one bucket reports as zero, so a trivial workload is grown until
|
|
179
|
+
readings become non-zero. The smallest positive reading bounds the bucket, and
|
|
180
|
+
the smallest gap between distinct readings lands on it. That candidate then has
|
|
181
|
+
to *behave* like a bucket: under real quantization every reading is a multiple
|
|
182
|
+
of it. Without that check, a fine-grained timer looks identical to a quantized
|
|
183
|
+
one, and the same Safari machine reported a different timer on consecutive runs.
|
|
184
|
+
|
|
185
|
+
**Both clocks are measured.** `performance.now()` is quantized too, to a full
|
|
186
|
+
millisecond in Safari, and the draw-call benchmarks are wall-clock by necessity
|
|
187
|
+
— their cost lives in browser validation and driver calls, which barely register
|
|
188
|
+
on GPU timestamps. Each benchmark scales its repetitions until it spans enough
|
|
189
|
+
ticks of whichever clock timed it, and every result carries that tick count.
|
|
190
|
+
|
|
191
|
+
**This is the only way to read a variation of zero correctly.** Perfect
|
|
192
|
+
consistency and a timer that cannot resolve the work look identical otherwise.
|
|
193
|
+
Mobile turned out to be genuinely more reproducible than desktop — geometry
|
|
194
|
+
throughput repeated at exactly 65.1 MTri/s across runs weeks apart — but that
|
|
195
|
+
only became a claim worth making once ticks confirmed the measurement was not
|
|
196
|
+
sitting on the floor.
|
|
197
|
+
|
|
198
|
+
## Feature-aware baselines
|
|
199
|
+
|
|
200
|
+
WebGPU widens core capabilities through features — `texture-formats-tier1` adds
|
|
201
|
+
storage binding to a set of formats, `float32-blendable` adds blending to 32-bit
|
|
202
|
+
float targets. Comparing against a fixed core baseline produces a flood of false
|
|
203
|
+
"more permissive than spec" reports, so the baseline is raised to match what a
|
|
204
|
+
device declares before comparing. What survives is genuine divergence.
|
|
205
|
+
|
|
206
|
+
## The profile
|
|
207
|
+
|
|
208
|
+
`probe()` returns a JSON-serializable `AtlasProfile` keeping `declared` and
|
|
209
|
+
`verified` strictly separate, plus a `discrepancies` list where they disagree:
|
|
210
|
+
|
|
211
|
+
- `breaking` — code relying on the declared value will fail here
|
|
212
|
+
- `degraded` — it works, but slower or with reduced capability
|
|
213
|
+
- `note` — worth recording, not worth acting on
|
|
214
|
+
|
|
215
|
+
## Profile schema
|
|
216
|
+
|
|
217
|
+
Profiles are the point of this project, so a profile states which schema it was
|
|
218
|
+
captured under and `SCHEMA_VERSION` is bumped whenever a field is added,
|
|
219
|
+
removed, or changes meaning. The history is kept in `src/types.ts`.
|
|
220
|
+
|
|
221
|
+
Version 2 made measurement trustworthiness explicit — tick counts, quantization
|
|
222
|
+
flags, measured timer resolutions — and changed the overdraw benchmarks to blend
|
|
223
|
+
additively. Version 3 made errors structured rather than preformatted strings,
|
|
224
|
+
and widened the fingerprint from 32 bits to 128, since the narrow version
|
|
225
|
+
collided at a rate that mattered once profiles were being collected in bulk.
|
|
226
|
+
Version 4 dropped the raw user agent: browser, version, platform and mobile are
|
|
227
|
+
already parsed into their own fields, so keeping the original string added
|
|
228
|
+
identifying detail without adding information.
|
|
229
|
+
|
|
230
|
+
`compareProfiles` accepts older profiles and still compares their capability
|
|
231
|
+
data, but marks pre-v2 benchmark numbers `staleBenchmarks` and treats them as
|
|
232
|
+
unreliable, because a version 1 profile's silence about quantization means
|
|
233
|
+
"not recorded" rather than "fine".
|
|
234
|
+
|
|
235
|
+
## Contributing
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
npm install
|
|
239
|
+
npm test # comparison and quantization detection, no GPU needed
|
|
240
|
+
npm run dev # demo at /demo/
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The probe needs a real GPU, so it is verified by running the demo on actual
|
|
244
|
+
devices. Everything that does not — profile comparison, quantization detection,
|
|
245
|
+
discrepancy analysis, fingerprinting — is unit tested and runs in CI.
|
|
246
|
+
|
|
247
|
+
Automating the probe itself was attempted and does not currently work:
|
|
248
|
+
Playwright's bundled Chromium ships without WebGPU, and driving a system Chrome
|
|
249
|
+
through it leaves `navigator.gpu` undefined regardless of `--enable-unsafe-swiftshader`,
|
|
250
|
+
`--use-angle=swiftshader`, or headed mode. Deno's built-in WebGPU looks like the
|
|
251
|
+
more promising route for anyone who wants to try again.
|
|
252
|
+
|
|
253
|
+
## Status
|
|
254
|
+
|
|
255
|
+
Early, and honest about it. **Three devices is not a dataset.** The differences
|
|
256
|
+
above are facts about these three machines; whether they generalize needs many
|
|
257
|
+
more profiles. Firefox is entirely unmeasured, and so is any iPhone — the one
|
|
258
|
+
Apple device here is an iPad, which is not the same GPU class and not the same
|
|
259
|
+
browser build.
|
|
260
|
+
|
|
261
|
+
Worth stating plainly: the original premise — that browsers misreport their own
|
|
262
|
+
capabilities — has not held up. All three devices did exactly what they
|
|
263
|
+
declared, zero discrepancies each. The value turned out to be in the gaps
|
|
264
|
+
*between* devices, which is why comparison exists at all.
|
|
265
|
+
|
|
266
|
+
### Contributing a profile
|
|
267
|
+
|
|
268
|
+
The dataset is the bottleneck, and it takes about a minute to widen it.
|
|
269
|
+
|
|
270
|
+
1. Open the [demo](https://ahnminjae08043-glitch.github.io/gpu-atlas/) on any
|
|
271
|
+
device with WebGPU — a phone counts, and phones are underrepresented.
|
|
272
|
+
2. Run the probe, then press **Share profile**. That copies the JSON and opens a
|
|
273
|
+
prefilled issue.
|
|
274
|
+
3. Paste, and submit.
|
|
275
|
+
|
|
276
|
+
A profile records the GPU vendor and architecture the browser reports, which
|
|
277
|
+
formats and limits actually worked, timing numbers, and coarse environment facts
|
|
278
|
+
— browser and version, platform, core count, memory bucket, pixel ratio. The raw
|
|
279
|
+
user-agent string is deliberately not collected. Press **Copy profile** first if
|
|
280
|
+
you would rather read the whole thing before posting it.
|
|
281
|
+
|
|
282
|
+
Crashes are worth reporting too. If the probe fell over, it met something it was
|
|
283
|
+
not built for, and that is a finding.
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
MIT
|