onejs-sl 0.9.0 → 0.9.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 +18 -0
- package/README.md +9 -3
- package/goldens/goldens.json +19 -2
- package/package.json +1 -1
- package/src/body.ts +34 -9
- package/src/structure.ts +13 -3
- package/src/web.ts +28 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.2
|
|
4
|
+
|
|
5
|
+
A fractional `tex2Dlod` level now weighs each level's colour by its alpha, so a transparent level no longer darkens the other. Every program keeps its hash; a host's generated shaders take the new text on their next build.
|
|
6
|
+
|
|
7
|
+
- A fractional `tex2Dlod` level blends the two levels premultiplied, so a transparent level fades the other out instead of pulling it toward black
|
|
8
|
+
- Opaque textures draw as they did, and so does colour under zero alpha
|
|
9
|
+
- Every program keeps its hash; one with a fractional `tex2Dlod` compiles to new shader text, which a host's generated shaders pick up on their next build
|
|
10
|
+
- The goldens anchor `lod-alpha.sl` to arithmetic, through a texture with alpha bound to a slot declared `edge`
|
|
11
|
+
|
|
12
|
+
## 0.9.1
|
|
13
|
+
|
|
14
|
+
`?:` now takes one side instead of blending the two, so a huge, infinite or NaN value on the side not taken no longer spoils the result. Every program keeps its hash; a host's generated shaders take the new text on their next build.
|
|
15
|
+
|
|
16
|
+
- `?:` chooses one side instead of blending the two, so a large, infinite or NaN value on the side not chosen no longer reaches the result
|
|
17
|
+
- A NaN condition picks the false side on every backend
|
|
18
|
+
- Every program keeps its hash; one with a `?:` on floats compiles to new shader text, which a host's generated shaders pick up on their next build
|
|
19
|
+
- The goldens anchor `select-extremes.sl` to arithmetic
|
|
20
|
+
|
|
3
21
|
## 0.9.0
|
|
4
22
|
|
|
5
23
|
A program can hold 64 uniforms and 8 textures, say how it samples a texture, and read a texture's size and its texels unfiltered. Every existing program keeps its hash and draws as it did; one that uses the new texture features is IR 6, which a host has to implement (below) to draw.
|
package/README.md
CHANGED
|
@@ -73,7 +73,10 @@ filtering and wrapping left to the host, which OneJS's hosts take from the
|
|
|
73
73
|
bound texture's own settings. Between mip levels the body decides instead: a
|
|
74
74
|
fractional `tex2Dlod` level blends the two levels either side by the fraction,
|
|
75
75
|
whatever the texture's filter mode, since the body asks for both whole levels
|
|
76
|
-
and blends them itself.
|
|
76
|
+
and blends them itself. The blend weighs each level's colour by its alpha, as
|
|
77
|
+
trilinear filtering of premultiplied texels does, so a level that reads
|
|
78
|
+
transparent fades the other out instead of darkening it; where both levels are
|
|
79
|
+
opaque, or both transparent, it is the plain blend. A level it can prove whole (a whole constant, a
|
|
77
80
|
`floor`, an int) costs one sample, and a level past the smallest reads the
|
|
78
81
|
smallest. A texture whose program sets its own sampler (`[Wrap(Repeat)]`,
|
|
79
82
|
`[Filter(Point)]`) goes to `sampleWith` instead, with both halves filled in,
|
|
@@ -162,11 +165,14 @@ over every pixel, and the anchors must match arithmetic, not each other:
|
|
|
162
165
|
stores as written), `texture.sl` (a texture's orientation and sRGB decode),
|
|
163
166
|
`lod.sl` (each mip level `tex2Dlod` reads, the texture's smaller levels being
|
|
164
167
|
solid colours), `lod-frac.sl` (a fractional level blends the two levels
|
|
165
|
-
either side, on a texture sampled nearest between levels), `
|
|
168
|
+
either side, on a texture sampled nearest between levels), `lod-alpha.sl` (the
|
|
169
|
+
same blend weighs each level's colour by its alpha, through a second texture,
|
|
170
|
+
bound to a slot declared `edge`, whose levels are transparent, opaque and half
|
|
171
|
+
transparent), `wrap-repeat.sl`
|
|
166
172
|
and `wrap-mirror.sl` (a program's own wrap over a texture the host binds
|
|
167
173
|
clamped, the filter's footprint wrapping with it), `filter-point.sl` (its own
|
|
168
174
|
point filter, to the byte, through `tex2D` and `tex2Dlod`) and `fetch.sl`
|
|
169
|
-
(`tex2Dfetch` held at the edges and `textureSize`, to the byte),
|
|
175
|
+
(`tex2Dfetch` held at the edges and `textureSize`, to the byte), `uniforms-64.sl` (every uniform slot arrives), and `select-extremes.sl` (`?:` chooses, so 1e9, an infinity or NaN on the side not chosen never reaches the one chosen). `sdf-values.sl`, `fbm-values.sl` and `ramp-values.sl` are black unless a
|
|
170
176
|
shape, a noise or a ramp draws differently with its parameters as values than
|
|
171
177
|
as constants.
|
|
172
178
|
A fixture that reads the previous frame, `frame` or `deltaTime` is stepped
|
package/goldens/goldens.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generatedBy": "onejs-sl goldens/run.mjs",
|
|
3
|
-
"package": "0.9.
|
|
3
|
+
"package": "0.9.2",
|
|
4
4
|
"ir": 6,
|
|
5
5
|
"target": "rgba8unorm-srgb: straight alpha, 8 bit, sRGB encoded, the stored bytes of a Linear OneJS game's element",
|
|
6
6
|
"size": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"historyExact": "a stepped fixture marked historyExact has to hold its history samples to the bit",
|
|
21
21
|
"samples": "a 16 x 16 grid at x = 2 + 4i, y = 2 + 4j, rows from the top, i fastest, RGBA each",
|
|
22
|
-
"texture": "every sampled slot: 8 x 8, rgba8 sRGB, linear filter, clamp to edge; texel (x, y) with y from the top = (32x + 16, 32y + 16, (x + y) even ? 200 : 40, 255); uv (0, 0) is the image's bottom left. Mip levels 1 to 3 (4 x 4, 2 x 2, 1 x 1) are each one colour, (224, 64, 32, 255), (32, 224, 64, 255), (64, 32, 224, 255), sampled nearest between levels",
|
|
22
|
+
"texture": "every sampled slot: 8 x 8, rgba8 sRGB, linear filter, clamp to edge; texel (x, y) with y from the top = (32x + 16, 32y + 16, (x + y) even ? 200 : 40, 255); uv (0, 0) is the image's bottom left. Mip levels 1 to 3 (4 x 4, 2 x 2, 1 x 1) are each one colour, (224, 64, 32, 255), (32, 224, 64, 255), (64, 32, 224, 255), sampled nearest between levels. A slot declared edge gets instead an 8 x 8 sampled the same way whose levels 0 to 3 are each one colour with straight alpha, (0, 0, 0, 0), (224, 64, 32, 255), (32, 224, 64, 128), (255, 255, 255, 0)",
|
|
23
23
|
"uniforms": "each program's declared defaults; a colour uniform's default is sRGB as written",
|
|
24
24
|
"drawnOn": {
|
|
25
25
|
"webgpu": "apple metal-3",
|
|
@@ -215,6 +215,14 @@
|
|
|
215
215
|
"1.25": [0,255,0,255,0,255,0,255,255,0,0,255,255,0,0,255,0,0,0,255,0,0,0,255,255,255,0,255,255,255,0,255,0,0,0,255,0,0,0,255,255,255,0,255,255,255,0,255,0,0,0,255,0,0,0,255,255,0,0,255,255,0,0,255,0,255,0,255,0,255,0,255,255,0,0,255,255,0,0,255,0,0,0,255,0,0,0,255,255,255,0,255,255,255,0,255,0,0,0,255,0,0,0,255,255,255,0,255,255,255,0,255,0,0,0,255,0,0,0,255,255,0,0,255,255,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,255,255,0,255,255,255,0,255,255,0,0,255,255,0,0,255,0,255,0,255,0,255,0,255,255,255,0,255,255,255,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,255,255,0,255,255,255,0,255,255,0,0,255,255,0,0,255,0,255,0,255,0,255,0,255,255,255,0,255,255,255,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,0,255,0,255,0,255,0,255,255,255,0,255,255,255,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,255,0,255,0,255,0,255,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,0,255,0,255,0,255,0,255,255,255,0,255,255,255,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,255,0,255,0,255,0,255,255,255,0,255,255,255,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,255,0,0,255,255,0,0,255,255,0,255,255,255,0,255,255,255,255,0,255,255,255,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,255,0,0,255,255,0,0,255,255,0,255,255,255,0,255,255,255,255,0,255,255,255,0,255,255,0,0,255,255,0,0,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,255,255,0,0,255,255,255,0,255,255,255,0,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,0,0,255,255,0,0,255,255,255,255,255,255,255,255,255,255,0,0,255,255,0,0,255,255,255,0,255,255,255,0,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,0,0,255,255,0,0,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,0,255,255,255,0,255,0,255,0,255,0,255,255,255,255,255,255,255,255,255,0,255,0,255,0,255,0,255,255,0,0,255,255,0,0,255,0,0,255,255,0,0,255,255,255,255,0,255,255,255,0,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,0,255,0,255,0,255,0,255,255,255,255,255,255,255,255,255,0,255,0,255,0,255,0,255,255,0,0,255,255,0,0,255,0,0,255,255,0,0,255,255,255,255,0,255,255,255,0,255,0,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255]
|
|
216
216
|
}
|
|
217
217
|
},
|
|
218
|
+
"lod-alpha.sl": {
|
|
219
|
+
"hash": "aaf5be77",
|
|
220
|
+
"source": "// An anchor for a fractional tex2Dlod level on a texture with alpha: the level\n// runs from 0 at the left edge to 3.5 at the right, through the goldens' edge\n// texture, whose levels are transparent black, opaque, half transparent and\n// transparent white. A fraction weighs each level's colour by its alpha, so a\n// transparent level fades the other out instead of pulling it toward the\n// colour it stores.\ntexture2D edge;\nfloat4 main() { return tex2Dlod(edge, uv, uv.x * 3.5); }\n",
|
|
221
|
+
"samples": {
|
|
222
|
+
"0": [224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0],
|
|
223
|
+
"1.25": [224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0,224,64,32,35,224,64,32,91,224,64,32,146,224,64,32,202,223,67,32,254,211,105,38,226,193,138,44,198,165,170,51,170,112,204,59,142,32,224,64,115,32,224,64,87,32,224,64,59,32,224,64,31,32,224,64,3,255,255,255,0,255,255,255,0]
|
|
224
|
+
}
|
|
225
|
+
},
|
|
218
226
|
"lod-frac.sl": {
|
|
219
227
|
"hash": "fd3f2852",
|
|
220
228
|
"source": "// An anchor for a fractional tex2Dlod level: the level runs from 0 at the left\n// edge to 4.5 at the right. A fraction blends the level below and the level\n// above by that fraction, whatever the texture's own filter between levels,\n// and past level 3, the smallest, both are level 3.\ntexture2D t;\nfloat4 main() { return tex2Dlod(t, uv, uv.x * 4.5); }\n",
|
|
@@ -306,6 +314,15 @@
|
|
|
306
314
|
"1.25": [0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255]
|
|
307
315
|
}
|
|
308
316
|
},
|
|
317
|
+
"select-extremes.sl": {
|
|
318
|
+
"hash": "2258fc10",
|
|
319
|
+
"exact": true,
|
|
320
|
+
"source": "// An anchor: ?: chooses one side and never blends the two, so what it does not\n// choose, however large, infinite or invalid, cannot reach what it does. Each\n// channel is 1 where the chosen value came through exactly, so this draws\n// white. A ?: that blends computes f + (t - f) * s, which rounds 0.75 away\n// beside 1e9 and is NaN beside an infinity: not white there.\n//\n// The conditions are comparisons written in place, which is the float choice;\n// a bool local as the condition is a choice that never blended. Each value is\n// both drawn (added up to 1) and compared, so a GPU that folds one reading\n// still shows the other. How a blend fared, 15 Oct 2026: the 1e9 cases wrong\n// in SwiftShader and right on Apple's GPU, the infinity and NaN cases wrong on\n// both, on WebGL2 and WebGPU alike for an infinity and on WebGPU for NaN.\nfloat4 main() {\n bool right = uv.x > 0.5;\n bool top = uv.y > 0.5;\n // 0 everywhere, from a value the compiler cannot fold\n float zero = floor(uv.x * 0.5);\n\n // Red: a ray tracer's \"no hit\" distance beside a hit\n float t = uv.x > 0.5 ? 0.75 : 1e9;\n float r = min(saturate(t + 0.25), right && t == 0.75 || !right && t == 1e9 ? 1 : 0);\n\n // Green: the same as three components\n float3 v = uv.y > 0.5 ? float3(0.1, 0.2, 0.3) : float3(1e9, -1e9, 1e9);\n float3 vSum = saturate(abs(v) + float3(0.9, 0.8, 0.7));\n bool vSame = top && v.x == 0.1 && v.y == 0.2 && v.z == 0.3 || !top && v.x == 1e9 && v.y == -1e9 && v.z == 1e9;\n float g = min(min(vSum.x, vSum.y), min(vSum.z, vSame ? 1 : 0));\n\n // Blue: an infinity beside the chosen value. Only the finite side is\n // checked, since a GPU need not keep an infinity it chose.\n float i = uv.x > 0.5 ? 0.25 : 1 / zero;\n float b = right ? min(saturate(i + 0.75), i == 0.25 ? 1 : 0) : 1;\n\n // Alpha: NaN beside the chosen value, checked the same way\n float n = uv.y > 0.5 ? zero / zero : 0.5;\n float a = top ? 1 : min(saturate(n + 0.5), n == 0.5 ? 1 : 0);\n\n return float4(r, g, b, a);\n}\n",
|
|
321
|
+
"samples": {
|
|
322
|
+
"0": [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255],
|
|
323
|
+
"1.25": [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]
|
|
324
|
+
}
|
|
325
|
+
},
|
|
309
326
|
"switch.sl": {
|
|
310
327
|
"hash": "1a535831",
|
|
311
328
|
"source": "// A switch over an int, as the if chain it means (Specs/SL_NEXT.md 3a): one\n// case per band, two labels stacked on one body, and a default.\n[Range(0, 3)] uniform int mode = 2;\nfloat4 main() {\n float t = 0;\n switch (int(uv.x * 5)) {\n case 0: t = uv.y; break;\n case 1: t = 1 - uv.y; break;\n case 2: case 3: t = length(uv - 0.5) * 1.4; break;\n default: t = frac(uv.y * 4); break;\n }\n float3 tint = float3(1, 1, 1);\n switch (mode) {\n case 0: tint = float3(1, 0.4, 0.2); break;\n case 2: tint = float3(0.2, 0.6, 1); break;\n default: break;\n }\n return float4(tint * t, 1);\n}\n",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onejs-sl",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "The OneJS shader language: a small typed language for per pixel programs, compiled to a portable IR and emitted as HLSL, WGSL or GLSL ES",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
package/src/body.ts
CHANGED
|
@@ -48,7 +48,8 @@ export interface BodyTarget {
|
|
|
48
48
|
* do with the GPU's sampler:
|
|
49
49
|
*
|
|
50
50
|
* - **Straight alpha**, not premultiplied, in and out: the body never
|
|
51
|
-
* multiplies or divides by alpha,
|
|
51
|
+
* multiplies or divides by alpha, except to blend two mip levels
|
|
52
|
+
* (`sampleLevel`), and its result is straight too.
|
|
52
53
|
* - **In the space `colour` names.** With `linear`, rgb is linear light, so
|
|
53
54
|
* an sRGB texture is decoded before it is filtered, as an sRGB sampler
|
|
54
55
|
* does; with `gamma`, rgb is as stored.
|
|
@@ -68,7 +69,11 @@ export interface BodyTarget {
|
|
|
68
69
|
* asks for whole levels, or for a level it cannot prove whole, floored and
|
|
69
70
|
* floored plus one, which it blends by the fraction itself; so a fraction
|
|
70
71
|
* draws the same whatever the texture's filter between levels, and a host
|
|
71
|
-
* has no blending to do.
|
|
72
|
+
* has no blending to do. The blend weighs each level's colour by its alpha,
|
|
73
|
+
* as trilinear filtering of premultiplied texels does, and divides alpha
|
|
74
|
+
* back out, so a level that reads transparent adds no colour; where both
|
|
75
|
+
* read transparent it is the plain blend. A level past the smallest reads
|
|
76
|
+
* the smallest.
|
|
72
77
|
* Otherwise the same contract as `sample`. `tex2Dlod` in a program; HLSL's
|
|
73
78
|
* `tex2Dlod(s, float4(uv, 0, lod))` and Metal's `sample(s, uv, level(lod))`.
|
|
74
79
|
*/
|
|
@@ -198,6 +203,8 @@ export function emitBody(p: Program, target: BodyTarget): Body {
|
|
|
198
203
|
const textures = new Set<number>()
|
|
199
204
|
const helpers = new Set<string>()
|
|
200
205
|
const helper = (fn: string) => { helpers.add(fn); return fn }
|
|
206
|
+
/** The statements a node prints ahead of its own line (`printBody`'s `before`). */
|
|
207
|
+
const ahead = new Map<NodeRef, string[]>()
|
|
201
208
|
/** A node at width `w`: a scalar repeated into a constructor, since HLSL refuses float3(x) and Metal x.xxx. */
|
|
202
209
|
/** A constant node's value, or null for anything computed. */
|
|
203
210
|
const constantOf = (ref: number): number[] | null => {
|
|
@@ -362,10 +369,16 @@ export function emitBody(p: Program, target: BodyTarget): Body {
|
|
|
362
369
|
case SLOP.MIX: return `lerp(${s[0]}, ${s[1]}, ${s[2]})`
|
|
363
370
|
case SLOP.STEP: return `step(${s[0]}, ${s[1]})`
|
|
364
371
|
case SLOP.SMOOTHSTEP: return `smoothstep(${s[0]}, ${s[1]}, ${s[2]})`
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
|
|
372
|
+
// A choice, never a blend: lerp(f, t, s) is f + (t - f) * s, which
|
|
373
|
+
// rounds t away beside 1e9 and is NaN beside an infinity. True from
|
|
374
|
+
// 0.5 up, as the web emitters read it. Spelled per component, so
|
|
375
|
+
// each ?: takes one bool: HLSL 2021 refuses a vector condition, and
|
|
376
|
+
// this subset compiles as Metal too.
|
|
377
|
+
case SLOP.SELECT: {
|
|
378
|
+
const part = (i: number, ch: number) => (valueAt(p.nodes, n.args[i]!).type === TYPE.FLOAT ? a[i] : `${a[i]}.${SWZ[ch]}`)
|
|
379
|
+
const pick = (ch: number) => `(${part(0, ch)} >= ${lit(0.5)} ? ${part(1, ch)} : ${part(2, ch)})`
|
|
380
|
+
return t === TYPE.FLOAT ? pick(0) : ctor(t, Array.from({ length: t }, (_, ch) => pick(ch)))
|
|
381
|
+
}
|
|
369
382
|
|
|
370
383
|
case SLOP.HSV2RGB: return `${helper("sl_hsv2rgb")}(${a[0]})`
|
|
371
384
|
case SLOP.NOISE: return `${helper("sl_valueNoise")}(${a[0]})`
|
|
@@ -398,10 +411,22 @@ export function emitBody(p: Program, target: BodyTarget): Body {
|
|
|
398
411
|
// A fraction blends the level below and the level above,
|
|
399
412
|
// whatever the texture's filter between levels, as the web
|
|
400
413
|
// emitters do (`isWhole`). Past the smallest level both read the
|
|
401
|
-
// smallest, as a whole level past it does.
|
|
414
|
+
// smallest, as a whole level past it does. The blend weighs
|
|
415
|
+
// each level's colour by its alpha, as trilinear filtering of
|
|
416
|
+
// premultiplied texels does: a level that reads transparent
|
|
417
|
+
// adds no colour, rather than pulling the other toward black.
|
|
418
|
+
// Where both are opaque it is the plain lerp, and where both
|
|
419
|
+
// are transparent too, so colour under zero alpha survives.
|
|
402
420
|
if (isWhole(p.nodes, n.args[1]!)) return level(slot, a[0]!, a[1]!)
|
|
403
421
|
const below = `floor(${a[1]})`
|
|
404
|
-
|
|
422
|
+
const [lo, hi, mixed] = ["lo", "hi", "pm"].map((s) => `${name(ref)}_${s}`)
|
|
423
|
+
const pm = (v: string) => `float4(${v}.rgb * ${v}.a, ${v}.a)`
|
|
424
|
+
ahead.set(ref, [
|
|
425
|
+
`float4 ${lo} = ${level(slot, a[0]!, below)};`,
|
|
426
|
+
`float4 ${hi} = ${level(slot, a[0]!, `${below} + 1.0`)};`,
|
|
427
|
+
`float4 ${mixed} = lerp(${pm(lo)}, ${pm(hi)}, frac(${a[1]}));`,
|
|
428
|
+
])
|
|
429
|
+
return `float4(${mixed}.a > 0.0 ? ${mixed}.rgb / ${mixed}.a : lerp(${lo}.rgb, ${hi}.rgb, frac(${a[1]})), ${mixed}.a)`
|
|
405
430
|
}
|
|
406
431
|
case SLOP.TEXTURE_SIZE: {
|
|
407
432
|
const slot = Math.round(imm[0] ?? 0)
|
|
@@ -443,7 +468,7 @@ export function emitBody(p: Program, target: BodyTarget): Body {
|
|
|
443
468
|
loopOpen: `${target.loopAttribute === undefined ? "" : target.loopAttribute + " "}for (;;) {`,
|
|
444
469
|
breakUnless: (c, turns, max) => `if (!(${c}) || ${turns} >= ${max}) break;`,
|
|
445
470
|
}
|
|
446
|
-
const lines = printBody(p, shape, syntax, expr, indent)
|
|
471
|
+
const lines = printBody(p, shape, syntax, expr, indent, (ref) => ahead.get(ref) ?? [])
|
|
447
472
|
lines.push(target.result === undefined
|
|
448
473
|
? `${indent}return ${name(p.result)};`
|
|
449
474
|
: `${indent}${target.result} = ${name(p.result)};`)
|
package/src/structure.ts
CHANGED
|
@@ -224,8 +224,15 @@ export const local = (ref: NodeRef): string => `n${ref}`
|
|
|
224
224
|
* Prints the body: every placed node in its region, each `if` and `loop` with
|
|
225
225
|
* its regions inside it. `expr` prints a value node's expression; the result is
|
|
226
226
|
* `local(p.result)` for the caller to return.
|
|
227
|
+
*
|
|
228
|
+
* `before` gives the statements a value node needs ahead of its own line,
|
|
229
|
+
* asked after `expr` has printed that node: locals of its own, named after
|
|
230
|
+
* `local(ref)`, for an expression that would otherwise repeat a texture read.
|
|
227
231
|
*/
|
|
228
|
-
export function printBody(
|
|
232
|
+
export function printBody(
|
|
233
|
+
p: Program, s: Structure, syntax: Syntax, expr: (ref: NodeRef) => string, indent: string,
|
|
234
|
+
before: (ref: NodeRef) => readonly string[] = () => [],
|
|
235
|
+
): string[] {
|
|
229
236
|
const lines: string[] = []
|
|
230
237
|
const loopRef = new Map<number, NodeRef>()
|
|
231
238
|
for (const [ref, n] of p.nodes.entries()) if (n.k === "loop") loopRef.set(n.id, ref)
|
|
@@ -270,8 +277,11 @@ export function printBody(p: Program, s: Structure, syntax: Syntax, expr: (ref:
|
|
|
270
277
|
lines.push(ind + syntax.value(ref, src.k === "loop" ? carried(n.src, n.index) : result(n.src, n.index)))
|
|
271
278
|
break
|
|
272
279
|
}
|
|
273
|
-
default:
|
|
274
|
-
|
|
280
|
+
default: {
|
|
281
|
+
const e = expr(ref)
|
|
282
|
+
for (const line of before(ref)) lines.push(ind + line)
|
|
283
|
+
lines.push(ind + syntax.value(ref, e))
|
|
284
|
+
}
|
|
275
285
|
}
|
|
276
286
|
}
|
|
277
287
|
}
|
package/src/web.ts
CHANGED
|
@@ -114,6 +114,8 @@ function emitWeb(p: Program, lang: WebLanguage): string {
|
|
|
114
114
|
const splat = (ref: number, t: SLType) => (typeOf(ref) === 1 && t > 1 ? `${T(t)}(n${ref})` : `n${ref}`)
|
|
115
115
|
/** A literal at width `t`. */
|
|
116
116
|
const k = (v: number, t: SLType) => (t > 1 ? `${T(t)}(${lit(v)})` : lit(v))
|
|
117
|
+
/** The statements a node prints ahead of its own line (`printBody`'s `before`). */
|
|
118
|
+
const ahead = new Map<NodeRef, string[]>()
|
|
117
119
|
|
|
118
120
|
const expr = (ref: NodeRef): string => {
|
|
119
121
|
const n = valueAt(p.nodes, ref)
|
|
@@ -259,11 +261,15 @@ function emitWeb(p: Program, lang: WebLanguage): string {
|
|
|
259
261
|
case SLOP.MIX: return `mix(${s[0]}, ${s[1]}, ${s[2]})`
|
|
260
262
|
case SLOP.STEP: return `step(${s[0]}, ${s[1]})`
|
|
261
263
|
case SLOP.SMOOTHSTEP: smoothsteps.add(t); return `sl_smoothstep${t}(${s[0]}, ${s[1]}, ${s[2]})`
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
+
// A choice, never a blend. mix(f, t, s) is f + (t - f) * s, which
|
|
265
|
+
// rounds t away beside 1e9 and is NaN beside an infinity, so the
|
|
266
|
+
// side not chosen reached the one that was. The condition holds
|
|
267
|
+
// from 0.5 up, as the step it replaces did, and a NaN condition
|
|
268
|
+
// picks the false side, as the HLSL emitter's does. A bool vector
|
|
269
|
+
// makes GLSL's mix choose rather than blend.
|
|
264
270
|
case SLOP.SELECT: {
|
|
265
|
-
const
|
|
266
|
-
return `
|
|
271
|
+
const on = t === 1 ? `(${s[0]} >= ${lit(0.5)})` : W ? `(${s[0]} >= ${k(0.5, t)})` : `greaterThanEqual(${s[0]}, ${k(0.5, t)})`
|
|
272
|
+
return W ? `select(${s[2]}, ${s[1]}, ${on})` : t === 1 ? `(${on} ? ${s[1]} : ${s[2]})` : `mix(${s[2]}, ${s[1]}, ${on})`
|
|
267
273
|
}
|
|
268
274
|
|
|
269
275
|
case SLOP.HSV2RGB: return `${lib("sl_hsv2rgb")}(${a[0]})`
|
|
@@ -293,11 +299,25 @@ function emitWeb(p: Program, lang: WebLanguage): string {
|
|
|
293
299
|
: `textureLod(sl_Tex${slot}, ${a[0]}, ${level})`
|
|
294
300
|
// A fraction blends the level below and the level above,
|
|
295
301
|
// whatever the texture's filter between levels, as the HLSL
|
|
296
|
-
// emitter does (`isWhole`)
|
|
297
|
-
// the
|
|
302
|
+
// emitter does (`isWhole`), each level's colour weighed by its
|
|
303
|
+
// alpha, the plain blend where both are transparent. Past the
|
|
304
|
+
// smallest level both read the smallest, as a whole level past
|
|
305
|
+
// it does.
|
|
298
306
|
if (isWhole(p.nodes, n.args[1]!)) return at(a[1]!)
|
|
299
307
|
const below = `floor(${a[1]})`
|
|
300
|
-
|
|
308
|
+
const [lo, hi, mixed] = ["lo", "hi", "pm"].map((s) => `${local(ref)}_${s}`)
|
|
309
|
+
const pm = (v: string) => `${T(4)}(${v}.rgb * ${v}.a, ${v}.a)`
|
|
310
|
+
const decl = (v: string, e: string) => (W ? `let ${v}: ${T(4)} = ${e};` : `${T(4)} ${v} = ${e};`)
|
|
311
|
+
ahead.set(ref, [
|
|
312
|
+
decl(lo, at(below)),
|
|
313
|
+
decl(hi, at(`${below} + 1.0`)),
|
|
314
|
+
decl(mixed, `mix(${pm(lo)}, ${pm(hi)}, fract(${a[1]}))`),
|
|
315
|
+
])
|
|
316
|
+
const plain = `mix(${lo}.rgb, ${hi}.rgb, fract(${a[1]}))`
|
|
317
|
+
const rgb = W
|
|
318
|
+
? `select(${plain}, ${mixed}.rgb / ${mixed}.a, ${mixed}.a > 0.0)`
|
|
319
|
+
: `(${mixed}.a > 0.0 ? ${mixed}.rgb / ${mixed}.a : ${plain})`
|
|
320
|
+
return `${T(4)}(${rgb}, ${mixed}.a)`
|
|
301
321
|
}
|
|
302
322
|
case SLOP.TEXTURE_SIZE: {
|
|
303
323
|
const slot = Math.round(imm[0] ?? 0)
|
|
@@ -401,7 +421,7 @@ function emitWeb(p: Program, lang: WebLanguage): string {
|
|
|
401
421
|
loopOpen: "for (;;) {",
|
|
402
422
|
breakUnless: (c, turns, max) => `if (!(${c}) || ${turns} >= ${max}) break;`,
|
|
403
423
|
}
|
|
404
|
-
const body = printBody(p, shape, syntax, expr, " ").join("\n")
|
|
424
|
+
const body = printBody(p, shape, syntax, expr, " ", (ref) => ahead.get(ref) ?? []).join("\n")
|
|
405
425
|
const library = [...[...smoothsteps].sort().map((w) => smoothstep(w, lang)), ...librarySource(need, lang)].join("\n")
|
|
406
426
|
const header = `// GENERATED from a shader language program (${p.hash}). Do not edit.`
|
|
407
427
|
// Only the textures the program reads, and in WGSL only the samplers it
|