diff options
| author | user <user@clank> | 2026-08-14 22:39:18 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-14 22:39:18 +0200 |
| commit | c0b88416fc9543fa348b63be07905f7a33fdbcc3 (patch) | |
| tree | f669fdf1066b2ffeed7d61baae572f4834b7ce62 /Makefile | |
| parent | sl0ppty: a tab is something you can name and move (diff) | |
| download | slosh-c0b88416fc9543fa348b63be07905f7a33fdbcc3.tar.gz slosh-c0b88416fc9543fa348b63be07905f7a33fdbcc3.tar.xz slosh-c0b88416fc9543fa348b63be07905f7a33fdbcc3.zip | |
sl0ppty: a shader's strength can be an expression
Adding an effect meant writing C -- in the binary, or since D15 in a shared
library. Both are a lot of ceremony for "dim every other row".
So: `amount` may be an expression, compiled to bytecode when the config is
read and evaluated per cell.
shaders {
dim amount="(y % 2) * 40" // scanlines
dim amount="(x > cols - 10) * 120" // a right margin
tint amount="255 - dist(x, y, curx, cury) * 12" color="#ff5fd7"
}
The scope is the argument, and that is the whole design. Every built-in here
is already colour_op(amount) with the amount a pure function of position --
vignette, zebra, ruler, margin and spotlight differ only in that function --
so the part worth handing to a config is the *number*, not the colour maths.
Three things follow at once: the mixing stays in C where it is fast and
correct; an expression cannot produce an invalid cell, because it produces
one byte; and that byte is cacheable.
The cache is what makes an interpreter affordable, and the numbers are why
this exists at all. Measured on a 200x50 pane, one pass:
native vignette 6.1 ns/cell
constant expression, folded 5.6 ns/cell
positional expression, mapped 5.2 ns/cell
animated (reads the clock) 35.0 ns/cell
A shader you wrote in your config costs what a compiled one does. A program
that does not read the clock is evaluated once per cell into a byte map and
reused until something it reads changes; one that does read the clock is
interpreted per cell, per frame, which is the honest price of animation and
is opt-in. (The earlier measurement of 56ns for interpretation was per
evaluation -- correct, and the reason the map exists rather than an argument
against the language.)
What a program reads is derived by the compiler rather than declared, and
that dependency set *is* the cache key. A stale map is therefore
unrepresentable rather than merely unlikely -- the same rule the layout
follows, arrived at from the other direction. A unit test caught the first
violation of it before anything else did: `cols` and `rows` carried no
dependency flag, on the theory that the key always contains the rect, and so
a program reading only the size looked constant and was folded at compile
time, where every size is zero.
The language is deliberately total. No loops, no recursion, no jumps: even
`?:` compiles to evaluating both sides and selecting, because nothing in it
has an effect. Evaluation is O(program length), so a config cannot spin, and
there is nothing to sandbox. Division by zero is 0 rather than a trap: an
expression that is wrong should look wrong, not take the session with it. One
that does not compile drops that one shader with a warning -- not the config,
and not the half-strength version of what you asked for, which is what the
number path did with garbage and is now also fixed.
shader_t gained the compiled program, so the plugin ABI goes to 2. A plugin
built against 1 is refused with "shader ABI 1, we speak 2" and needs nothing
but a rebuild -- the check earning its keep the first time it was asked to.
Diffstat (limited to 'Makefile')
| -rw-r--r-- | Makefile | 14 |
1 files changed, 10 insertions, 4 deletions
@@ -66,10 +66,15 @@ KDL_TEST := build/kdl_test $(KDL_TEST): tests/kdl_test.c src/kdl.c | build $(CC) $(CFLAGS) tests/kdl_test.c src/kdl.c -o $@ +EXPR_TEST := build/expr_test + +$(EXPR_TEST): tests/expr_test.c src/expr.c | build + $(CC) $(CFLAGS) tests/expr_test.c src/expr.c -o $@ + SHADER_TEST := build/shader_test -$(SHADER_TEST): tests/shader_test.c src/shader.c src/screen.c src/json.c $(VT_LIB) | build - $(CC) $(CFLAGS) tests/shader_test.c src/shader.c src/screen.c src/json.c $(VT_LIB) -o $@ +$(SHADER_TEST): tests/shader_test.c src/shader.c src/screen.c src/json.c src/expr.c $(VT_LIB) | build + $(CC) $(CFLAGS) tests/shader_test.c src/shader.c src/screen.c src/json.c src/expr.c $(VT_LIB) -o $@ # Shader plugins, for test_shader_plugin.py: a good one, one that announces an # ABI we do not speak, and the example we ship in contrib -- which is built @@ -94,9 +99,10 @@ build/.pass-%: tests/%.py tests/harness.py $(BIN) | build @printf ' ok %-24s %s\n' "$(notdir $<)" "$$(grep -c '^ok' build/.log-$* 2>/dev/null || echo ?) checks" @touch $@ -test: $(BIN) $(TEST_BIN) $(KDL_TEST) $(SHADER_TEST) ## unit + headless checks (fast) +test: $(BIN) $(TEST_BIN) $(KDL_TEST) $(SHADER_TEST) $(EXPR_TEST) ## unit + headless checks (fast) @./$(TEST_BIN) >/dev/null && ./$(KDL_TEST) >/dev/null && ./$(SHADER_TEST) >/dev/null \ - && printf ' ok %-24s %s\n' "C unit tests" "3 binaries" + && ./$(EXPR_TEST) >/dev/null \ + && printf ' ok %-24s %s\n' "C unit tests" "4 binaries" @$(MAKE) --no-print-directory -j$(JOBS) $(PY_STAMPS) @printf '\nall green\n' |
