onejs-sl 0.1.0 → 0.1.2

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.2
4
+
5
+ The helper library has one source, `lib/*.hlsl`, translated at build time into GLSL ES, WGSL and the shared subset and copied into OneJS as its `.cginc` files. The web draws exactly what it drew; the Unity shader's text changes where Metal needed it to, and draws the same.
6
+
7
+ - `emitLibrary` prints the library functions a body calls, in the shared subset
8
+ - `emitBody` prints no scalar swizzles, and types every literal and scalar it passes to an intrinsic
9
+ - `atan2` of vectors is refused; no web backend compiled it
10
+ - `onejs-sl/goldens.json` exports the goldens
11
+ - The tests compile the shared subset as Metal on a Mac with Xcode
12
+
13
+ ## 0.1.1
14
+
15
+ A host can now supply its own frame for a program's body and get goldens for the pictures it should draw. Nothing an existing program draws changes, and OneJS's generated Unity shader is byte for byte what 0.1.0 printed.
16
+
17
+ - `onejs-sl/emit/hlsl-body`: `emitBody` and `BodyTarget`, the body in the HLSL and Metal shared subset
18
+ - The Unity shader is printed through `emitBody`
19
+ - `UniformDecl.colour` marks a uniform with a hex default, and `sl.uniform.colour` declares one
20
+ - `inputsUsed` lists the inputs a program reads
21
+ - `goldens/goldens.json`: every corpus fixture as a Linear game stores it, drawn by `npm run goldens`
22
+
3
23
  ## 0.1.0
4
24
 
5
25
  The shader language compiler as its own package, moved out of `onejs-unity` with its history and its tests. It behaves exactly as `onejs-unity` 0.5.15's `sl` did, at IR version 2 and VM wire version 2.
package/README.md CHANGED
@@ -40,11 +40,100 @@ only what it calls.
40
40
  | `onejs-sl/tables` | `BUILTINS`, `SL_HLSL`, `INPUTS`, `SL_SDF_SHAPES`, `SL_SDF_PARAMS`: what completion and highlighting read |
41
41
  | `onejs-sl/limits` | `vmFit(program)`: whether the VM runs it, and why not, without encoding |
42
42
  | `onejs-sl/vm` | `encode`, `SL_WIRE_VERSION` |
43
- | `onejs-sl/emit/unity` | `emitShader`: the `.shader` a Unity editor generates |
43
+ | `onejs-sl/emit/hlsl-body` | `emitBody`: a program as a function body for a host's own frame; `emitLibrary`: the library functions it calls |
44
+ | `onejs-sl/emit/unity` | `emitShader`: the `.shader` a Unity editor generates, a frame over `emitBody` |
44
45
  | `onejs-sl/emit/web` | `emitWGSL`, `emitGLSL`: OneJS's web frame |
46
+ | `onejs-sl/goldens.json` | the goldens (below), as data |
45
47
 
46
48
  `src/entries.test.ts` pins every name, since removing one breaks a host.
47
49
 
50
+ ## A host's own frame: `emitBody`
51
+
52
+ `emitBody(program, target)` prints the body only: one local per node, in the
53
+ HLSL and Metal shared subset (HLSL spelling, `fmod`, no `mul`, no `static`, no
54
+ derivatives, no swizzle of a scalar, and every literal and scalar in an
55
+ intrinsic at its exact type, since Metal overloads where HLSL converts). The `BodyTarget` says what differs between hosts: an expression
56
+ for each input, the float4 holding a uniform slot, a texture sample, whether
57
+ `toLinear` is real (`colour: "linear"`) or the identity (`"gamma"`), and
58
+ optionally a local to assign the result to. It returns the uniform and texture
59
+ slots the body uses and the library functions it calls. OneJS's Unity shader is
60
+ one frame over it (`hlsl.ts`); Magerie's compute kernel is another, and its
61
+ target is in `src/body.test.ts` so an opcode cannot change without the text
62
+ Magerie compiles changing in front of a test.
63
+
64
+ `emitLibrary(body.uses.helpers, colour)` prints the library functions the body
65
+ calls, and everything they call, in the same subset and dependency order, with
66
+ the colour branch asked for: the text a host puts ahead of its frame. It is a
67
+ separate call because OneJS's own frame never needs it (the Unity shader
68
+ includes `SLCommon.cginc`), and a bundle that prints only Unity shaders should
69
+ not carry the library too. On a Mac with Xcode, `src/metal.test.ts` compiles
70
+ the whole library and a body for every corpus program and every builtin at
71
+ every width as Metal, behind the two defines a host's compatibility header
72
+ supplies (`frac`, `lerp`).
73
+
74
+ A uniform declared with a hex default (`uniform float4 tint = #ff8040;`), or
75
+ with `sl.uniform.colour`, is marked `colour: true`: its value is sRGB as
76
+ written, which is what a colour picker shows, and its reads convert. `inputsUsed`
77
+ says which inputs the result depends on, dead nodes aside, so a host knows
78
+ whether a program animates.
79
+
80
+ ## The helper library: one source
81
+
82
+ Value and simplex noise, the octave kinds, voronoi, hsv2rgb, `toLinear` and the
83
+ 42 distance shapes are written once, in HLSL, in `lib/sdf2d.hlsl`,
84
+ `lib/noise2d.hlsl` and `lib/common.hlsl`. Everything else is printed from them
85
+ by `npm run lib`, and nobody edits the copies:
86
+
87
+ - **OneJS's `.cginc` files** (`SDF2D`, `Noise2D`, `SLCommon` in
88
+ `Resources/OneJS/`) are the source with an include guard and a generated
89
+ header around it. The VM, the generated Unity shaders and `fx` include them.
90
+ `npm run lib` writes them when the package sits in the OneJS container.
91
+ - **`src/lib/`**: `table.ts` (every function, its signature, what it calls, and
92
+ the shape table read from `sl_sdfDistance`'s switch), and the text of each
93
+ function as GLSL ES (`glsl.ts`), WGSL (`wgsl.ts`) and the shared subset
94
+ (`hlsl.ts`). The web emitters and `emitLibrary` take only what a program calls.
95
+
96
+ `lib/translate.ts` does the printing, at build time only. It reads a subset of
97
+ HLSL, listed at its top (functions, locals, `if`, bounded `for`, `switch`,
98
+ intrinsics, `mul(v, float2x2(...))`, and one preprocessor switch,
99
+ `UNITY_COLORSPACE_GAMMA`, which becomes the target's colour), and anything
100
+ outside it is an error with a file and line rather than a guess. HLSL converts
101
+ where GLSL, WGSL and Metal refuse, so it makes every conversion explicit first;
102
+ the printers then differ in spelling, plus what WGSL lacks (overloading,
103
+ ternaries, swizzle assignment, assignable parameters). On the web the colour
104
+ switch stays a runtime branch on the frame's `opt.x`, since a web build does not
105
+ know the project's colour space when it prints.
106
+
107
+ `src/lib/lib.test.ts` regenerates everything and fails on any byte of
108
+ difference, the `.cginc` copies included in the container, and OneJS's
109
+ `SLSharedLibraryTests` checks them from the other side. What proves the
110
+ translations draw the same picture: the goldens compile the whole library on
111
+ WebGPU and WebGL2 and draw every fixture; OneJS's parity harness holds the web
112
+ output to the VM; `SLSharedLibraryTests` draws every corpus program in Unity
113
+ through `SLCommon.cginc` and through the translated shared subset and requires
114
+ them to agree; and the Metal check above compiles the shared subset.
115
+
116
+ ## Goldens
117
+
118
+ `goldens/goldens.json` is every corpus fixture drawn as a Linear OneJS game
119
+ stores it: the package's WGSL and GLSL ES in OneJS's web frame, into an
120
+ `rgba8unorm-srgb` target (the format the element's render texture has), read
121
+ back raw, with no Unity, no UI Toolkit and no browser colour management. A
122
+ second host's own backend is checked against it; OneJS's parity harness is what
123
+ proves the web emitters equal the VM in the first place.
124
+
125
+ `npm run goldens` draws it on WebGPU and WebGL2 in a Chrome with its own
126
+ profile (set `CHROME` to choose one). The two backends must agree within 1/255
127
+ over every pixel, and three anchors must match arithmetic, not each other:
128
+ `orient.sl` (orientation, and the linear to sRGB store), `hex.sl` (a hex colour
129
+ stores as written) and `texture.sl` (a texture's orientation and sRGB decode).
130
+ The whole translated library must also compile on both backends, including
131
+ the functions no fixture reaches. The file describes the sampling grid, the
132
+ times and the texture every sampled slot gets. A host imports it as
133
+ `onejs-sl/goldens.json`. No CI runner here has a GPU, so `src/goldens.test.ts` checks instead
134
+ that the file still covers the corpus at today's hashes; a change that moves a
135
+ hash fails there until the goldens are drawn again.
136
+
48
137
  ## Runs anywhere ES2020 runs
49
138
 
50
139
  The source reaches for no host API: no `fs`, `process`, `Buffer`,
@@ -56,7 +145,7 @@ and QuickJS (Magerie). Two checks hold it to that:
56
145
  - `npm run test:quickjs` bundles `quickjs/corpus.ts` as one ES2020 IIFE, the
57
146
  way Magerie bundles a script, runs it in QuickJS-ng and in a bare Node
58
147
  context, and fails unless the two results agree byte for byte. The corpus
59
- parses, encodes, fits and emits every program in `quickjs/corpus/`, every
148
+ parses, encodes, fits and emits every program in `corpus/`, every
60
149
  shape at its full parameter count, and the error paths. `npm test` runs it
61
150
  after vitest.
62
151
 
@@ -90,8 +179,8 @@ would rather write its programs out at build time.
90
179
 
91
180
  **A third and fourth backend, for the browser.** A player cannot compile a
92
181
  shader, but the page it runs in can. `web.ts` prints every program as WGSL and
93
- as GLSL ES 3.00 (`weblib.ts` holds the noise, colour and 42 distance functions
94
- ported from `SLCommon.cginc`, `Noise2D.cginc` and `SDF2D.cginc`), and OneJS's
182
+ as GLSL ES 3.00 (carrying the library functions it calls, translated from
183
+ `lib/*.hlsl`), and OneJS's
95
184
  `Plugins/WebGL/OneJSSLWeb.jslib` compiles whichever one Unity's device speaks
96
185
  and draws it into the element's target in place of the VM. A `.sl` import
97
186
  carries both strings, printed at build time; an `encode()` result has them as
@@ -253,13 +342,13 @@ linear light. `sl.ramp` mixes its stops in sRGB, which is what reads as an even
253
342
  ramp, and converts the result once through `TO_LINEAR`; `sl.color("#hex")` is
254
343
  `parseColor` plus that conversion, and `sl.toLinear` is the conversion on its
255
344
  own for a vec4 built from raw components. Both backends implement it gamma
256
- aware (`sl_toLinear` in `SLCommon.cginc`), so a Gamma project gets the value as
257
- written. Alpha is coverage and is never converted. Same rule as `fx`.
345
+ aware (`sl_toLinear` in `lib/common.hlsl`), so a Gamma project gets the value
346
+ as written. Alpha is coverage and is never converted. Same rule as `fx`.
258
347
 
259
348
  ## Noise
260
349
 
261
350
  `sl.noise`, `sl.simplex`, `sl.fbm(p, octaves, base)`, `sl.turbulence` and
262
- `sl.ridged` are the fields `fx.noise` draws, from the same `Noise2D.cginc`, so
351
+ `sl.ridged` are the fields `fx.noise` draws, from the same `lib/noise2d.hlsl`, so
263
352
  a simplex here is the simplex there. Octaves are 1 to 4. A program has no seed;
264
353
  offset the input for a different field. `sl.simplex` used to be value noise on
265
354
  a rotated lattice, and `sl.fbm` had its own value noise; both changed on