onejs-sl 0.6.0 → 0.8.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/CHANGELOG.md +22 -0
- package/README.md +65 -13
- package/goldens/goldens.json +80 -5
- package/package.json +2 -2
- package/src/body.ts +51 -9
- package/src/compile.ts +11 -1
- package/src/core.ts +4 -4
- package/src/hlsl.ts +21 -3
- package/src/ir.ts +108 -12
- package/src/lang/README.md +23 -12
- package/src/lang/check.ts +39 -7
- package/src/lang/classify.ts +2 -2
- package/src/lang/glsl.ts +2 -2
- package/src/lang/lower.ts +12 -3
- package/src/lang/parser.ts +1 -1
- package/src/ops.ts +7 -3
- package/src/params.ts +6 -3
- package/src/serial.ts +8 -4
- package/src/sl.ts +39 -5
- package/src/structure.ts +2 -2
- package/src/tables.ts +2 -2
- package/src/web.ts +42 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
A fractional `tex2Dlod` level now blends the two levels either side on every backend. No existing program's hash changes, but a host now gets two whole-level samples and a blend where it got one fractional sample, so a host that blended levels itself would blend twice, which makes this a minor.
|
|
6
|
+
|
|
7
|
+
- A fractional `tex2Dlod` level blends the two levels either side, whatever the texture's filter mode
|
|
8
|
+
|
|
9
|
+
## 0.7.0
|
|
10
|
+
|
|
11
|
+
A program can read what it drew the frame before, and count frames (`Specs/SL_NEXT.md` 4). Every existing program keeps its hash and its shader; one that reads the new names is IR 5. A host has to forward the new `reads` to draw such a program, which makes this a minor.
|
|
12
|
+
|
|
13
|
+
- `previous`, the frame this program drew last, read as `tex2D(previous, uv)`; `previous.sample(uv)` in `sl.program`
|
|
14
|
+
- `frame`, an int counting frames since `previous` was last cleared, and `deltaTime`, the seconds since the frame before
|
|
15
|
+
- A file that already used one of the three names keeps its own
|
|
16
|
+
- `readsOf(program)` and `compile(p).reads` say which of the three a host has to keep between frames
|
|
17
|
+
- `BodyTarget` takes `inputs.frame`, `inputs.deltaTime` and `previous(uv)`, all optional; `emitBody` names the one a program needs and a target lacks
|
|
18
|
+
- The Unity shader reads the step from `_Res.zw` and the previous frame from `_Prev`; the web frame from `sl_Res.w`, `sl_Opt.z` and its own binding
|
|
19
|
+
- IR 5: `SAMPLE_PREVIOUS` and the two inputs
|
|
20
|
+
- `fromGLSL` turns `iFrame` and `iTimeDelta` into `frame` and `deltaTime`
|
|
21
|
+
- The goldens step the programs that read the three, and hold the history to the bit
|
|
22
|
+
- The web emitters refuse an input they have no mapping for
|
|
23
|
+
- The shipped source is typechecked against ES2020 with no Node types, which found the parser's `Array.prototype.at`
|
|
24
|
+
|
|
3
25
|
## 0.6.0
|
|
4
26
|
|
|
5
27
|
An int uniform says it is one, and uint constants fold as int ones do. A `.sl` program whose uint constants now fold gets a new hash and shader, which a build regenerates; every other program keeps its hash and its shader, and a program built in code keeps both, since the fold is the `.sl` file's. That hash change and `sl.uniform.int` make this a minor.
|
package/README.md
CHANGED
|
@@ -27,6 +27,11 @@ const plasma = sl.program(({ uv, time }) => {
|
|
|
27
27
|
})
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
`npm install onejs-sl`. The package ships TypeScript source (its `exports`
|
|
31
|
+
point at `src/*.ts`): bundle it (esbuild, Vite) or run it with a TypeScript
|
|
32
|
+
loader (`npx tsx script.ts`). Plain `node` cannot load `.ts` from
|
|
33
|
+
`node_modules`.
|
|
34
|
+
|
|
30
35
|
## Entry points
|
|
31
36
|
|
|
32
37
|
`"sideEffects": false`, and each backend is its own entry, so a host bundles
|
|
@@ -43,7 +48,9 @@ only what it calls.
|
|
|
43
48
|
| `onejs-sl/emit/web` | `emitWGSL`, `emitGLSL`: OneJS's web frame |
|
|
44
49
|
| `onejs-sl/goldens.json` | the goldens (below), as data |
|
|
45
50
|
|
|
46
|
-
`src/entries.test.ts`
|
|
51
|
+
[`src/entries.test.ts`](https://github.com/Singtaa/onejs-sl/blob/main/src/entries.test.ts)
|
|
52
|
+
(in the repository, not the npm package) pins every name, since removing one
|
|
53
|
+
breaks a host.
|
|
47
54
|
|
|
48
55
|
## A host's own frame: `emitBody`
|
|
49
56
|
|
|
@@ -54,13 +61,21 @@ intrinsic at its exact type, since Metal overloads where HLSL converts). The `Bo
|
|
|
54
61
|
for each input, the float4 holding a uniform slot, a texture sample and a
|
|
55
62
|
sample at a mip level (`sampleLevel`, for `tex2Dlod`), whether
|
|
56
63
|
`toLinear` is real (`colour: "linear"`) or the identity (`"gamma"`), and
|
|
57
|
-
optionally a local to assign the result to.
|
|
64
|
+
optionally a local to assign the result to. A host that steps a program frame
|
|
65
|
+
by frame adds `frame` and `deltaTime` to its inputs and a `previous` read (below,
|
|
66
|
+
"What a program is given"); a target without one cannot draw a program that
|
|
67
|
+
reads it, and `emitBody` says which is missing rather than print a shader that
|
|
68
|
+
stands still. It returns the uniform and texture
|
|
58
69
|
slots the body uses and the library functions it calls. The sample's contract
|
|
59
70
|
is on `BodyTarget.sample`: straight alpha in and out, rgb in the space `colour`
|
|
60
71
|
names (an sRGB texture decoded before filtering when it is `linear`), and
|
|
61
72
|
filtering and wrapping left to the host, which OneJS's hosts take from the
|
|
62
|
-
bound texture's own settings.
|
|
63
|
-
|
|
73
|
+
bound texture's own settings. Between mip levels the body decides instead: a
|
|
74
|
+
fractional `tex2Dlod` level blends the two levels either side by the fraction,
|
|
75
|
+
whatever the texture's filter mode, since the body asks for both whole levels
|
|
76
|
+
and blends them itself. A level it can prove whole (a whole constant, a
|
|
77
|
+
`floor`, an int) costs one sample, and a level past the smallest reads the
|
|
78
|
+
smallest. OneJS's Unity shader is one frame over it (`hlsl.ts`); Magerie's compute kernel is another, and its
|
|
64
79
|
target is in `src/body.test.ts` so an opcode cannot change without the text
|
|
65
80
|
Magerie compiles changing in front of a test.
|
|
66
81
|
|
|
@@ -83,7 +98,8 @@ program reads it as one, so a host shows a whole number field; a float uniform
|
|
|
83
98
|
has no `kind`. Neither mark is in the hash, since the reads' conversion already
|
|
84
99
|
is. `inputsUsed`
|
|
85
100
|
says which inputs the result depends on, dead nodes aside, so a host knows
|
|
86
|
-
whether a program animates.
|
|
101
|
+
whether a program animates, and `readsOf` (also `compile(p).reads`) says which
|
|
102
|
+
of the previous frame, `frame` and `deltaTime` it needs kept between frames.
|
|
87
103
|
|
|
88
104
|
## The helper library: one source
|
|
89
105
|
|
|
@@ -137,13 +153,23 @@ what a WebGL player draws.
|
|
|
137
153
|
|
|
138
154
|
`npm run goldens` draws it on WebGPU and WebGL2 in a Chrome with its own
|
|
139
155
|
profile (set `CHROME` to choose one). The two backends must agree within 1/255
|
|
140
|
-
over every pixel, and
|
|
156
|
+
over every pixel, and eight anchors must match arithmetic, not each other:
|
|
141
157
|
`orient.sl` (orientation, and the linear to sRGB store), `hex.sl` (a hex colour
|
|
142
|
-
stores as written), `texture.sl` (a texture's orientation and sRGB decode)
|
|
158
|
+
stores as written), `texture.sl` (a texture's orientation and sRGB decode),
|
|
143
159
|
`lod.sl` (each mip level `tex2Dlod` reads, the texture's smaller levels being
|
|
144
|
-
solid colours)
|
|
160
|
+
solid colours) and `lod-frac.sl` (a fractional level blends the two levels
|
|
161
|
+
either side, on a texture sampled nearest between levels). `sdf-values.sl`, `fbm-values.sl` and `ramp-values.sl` are black unless a
|
|
145
162
|
shape, a noise or a ramp draws differently with its parameters as values than
|
|
146
163
|
as constants.
|
|
164
|
+
A fixture that reads the previous frame, `frame` or `deltaTime` is stepped
|
|
165
|
+
instead of drawn at times: eight frames at 1/30 of a second, into an
|
|
166
|
+
`rgba16float` history pair that starts clear, each frame then copied into the
|
|
167
|
+
target as an element shows it. goldens.json holds its samples at frames 0, 1
|
|
168
|
+
and 7, as shown and as the raw history. Three are anchors on the history, to
|
|
169
|
+
the bit: `counter.sl` (a step taken twice, a missed swap or 8 bit history),
|
|
170
|
+
`drift.sl` (the previous frame read upside down) and `frame-delta.sl` (`frame`
|
|
171
|
+
and `deltaTime` as a host hands them over); `trail.sl` is the fading trail the
|
|
172
|
+
docs show.
|
|
147
173
|
The whole translated library must also compile on both backends, including
|
|
148
174
|
the functions no fixture reaches. The file describes the sampling grid, the
|
|
149
175
|
times and the texture every sampled slot gets. A host imports it as
|
|
@@ -156,7 +182,8 @@ bits per pixel, and `corpus/probe-hash.sl` does the same through a program;
|
|
|
156
182
|
every pixel must match `goldens/reference.mjs`, which computes the hashes in
|
|
157
183
|
JavaScript. goldens.json ships each probe under its name, as HLSL, WGSL and
|
|
158
184
|
GLSL ES and with the bits it must draw, for a host to run on its own compiler,
|
|
159
|
-
and marks the fixtures compared at 0/255 `exact
|
|
185
|
+
and marks the fixtures compared at 0/255 `exact`, and the stepped fixtures
|
|
186
|
+
whose history is compared to the bit `historyExact`.
|
|
160
187
|
|
|
161
188
|
`npm run goldens -- --check` draws everything again and compares it with
|
|
162
189
|
goldens.json instead of writing it: within 1/255, and exact fixtures and probes
|
|
@@ -170,9 +197,12 @@ hash fails there until the goldens are drawn again.
|
|
|
170
197
|
The source reaches for no host API: no `fs`, `process`, `Buffer`,
|
|
171
198
|
`TextEncoder`, `performance`, `structuredClone` or `Intl`. It runs in a
|
|
172
199
|
browser (the Play editor's diagnostics), a Cloudflare worker (the Play build)
|
|
173
|
-
and QuickJS (Magerie).
|
|
200
|
+
and QuickJS (Magerie). Three checks hold it to that:
|
|
174
201
|
|
|
175
202
|
- lint refuses those globals and any Node module in `src/`;
|
|
203
|
+
- `npm run typecheck` also checks the shipped source alone against ES2020 with
|
|
204
|
+
no Node types (`tsconfig.lib.json`), since `@types/node` adds later methods
|
|
205
|
+
such as `Array.prototype.at`, which 0.5.0 used by mistake;
|
|
176
206
|
- `npm run test:quickjs` bundles `quickjs/corpus.ts` as one ES2020 IIFE, the
|
|
177
207
|
way Magerie bundles a script, runs it in QuickJS-ng and in a bare Node
|
|
178
208
|
context, and fails unless the two results agree byte for byte. The corpus
|
|
@@ -225,8 +255,9 @@ drew what had no shader yet; OneJS 3.8 deleted it.) `hlsl.ts` feeds OneJS's
|
|
|
225
255
|
draws a program asks the compiled program for its
|
|
226
256
|
`hlsl` (a lazy getter, never read in Play), records it into
|
|
227
257
|
`Assets/OneJS/Recorded.sl.json`, generates the shader and
|
|
228
|
-
moves the live material onto it. `manifest()
|
|
229
|
-
|
|
258
|
+
moves the live material onto it. `manifest()`, in `onejs-unity/sl` and
|
|
259
|
+
`onejs-unity/sl/compiler` rather than this package, is still there for an app
|
|
260
|
+
that would rather write its programs out at build time.
|
|
230
261
|
|
|
231
262
|
**A third and fourth backend, for the browser.** A player cannot compile a
|
|
232
263
|
shader, but the page it runs in can. `web.ts` prints every program as WGSL and
|
|
@@ -350,7 +381,9 @@ number. `fromJSON` moves an older file's immediates to operands. IR 4 added
|
|
|
350
381
|
control flow (the `if` and `loop` nodes, a loop's `param`s and each result's
|
|
351
382
|
`proj`) and int, uint and bool values (a node's `kind`). A program that uses
|
|
352
383
|
none of them is still IR 3 and keeps its hash; one with an `if` statement is
|
|
353
|
-
IR 4, since the statement is now a real branch rather than a select.
|
|
384
|
+
IR 4, since the statement is now a real branch rather than a select. IR 5 added
|
|
385
|
+
the previous frame (`SAMPLE_PREVIOUS`) and the `frame` and `deltaTime` inputs;
|
|
386
|
+
a program that reads none of them keeps the version and the hash it had.
|
|
354
387
|
|
|
355
388
|
## Control flow
|
|
356
389
|
|
|
@@ -405,6 +438,25 @@ program that does not compiles to the bytes it always did, and a program with a
|
|
|
405
438
|
`texel` of its own keeps it: a local, a parameter or a uniform of that name
|
|
406
439
|
shadows the input.
|
|
407
440
|
|
|
441
|
+
Three are for a program drawn frame after frame, and a host hands them over
|
|
442
|
+
only to a program that reads them (`readsOf`):
|
|
443
|
+
|
|
444
|
+
- `previous`, a texture: what this program drew the frame before, read as
|
|
445
|
+
`tex2D(previous, uv)`. It is the program's result exactly, linear and
|
|
446
|
+
straight alpha, kept at 16 bits a channel, filtered bilinear and clamped at
|
|
447
|
+
the edges, with one level (so `tex2Dlod` refuses it). A read at a pixel's own
|
|
448
|
+
`uv` is that pixel.
|
|
449
|
+
- `frame`, an int: frames drawn since `previous` was last cleared, from 0.
|
|
450
|
+
- `deltaTime`: seconds since the frame before, 0 on the first.
|
|
451
|
+
|
|
452
|
+
A host advances a frame when it draws with `time` moved forward: the last
|
|
453
|
+
result becomes `previous`, `frame` goes up by one and `deltaTime` is the step.
|
|
454
|
+
A draw at the same time draws the same frame again. It clears (`previous`
|
|
455
|
+
transparent black, `frame` and `deltaTime` 0) on the first draw, a new size, a
|
|
456
|
+
new program, a seek and time going back, so `frame` is 0 exactly when
|
|
457
|
+
`previous` is clear. A file that already used one of the three names for its
|
|
458
|
+
own value, texture or function keeps it.
|
|
459
|
+
|
|
408
460
|
Why `resolution` and `aspect` are the target's: a program is drawn with
|
|
409
461
|
`Graphics.Blit` into the element's own render texture, and Unity sets `_ScreenParams` per camera and
|
|
410
462
|
leaves it alone for a blit: reading it from a 64x256 target answers with the
|