Commit graph

7 commits

Author SHA1 Message Date
2ad6edaae3 ludic syntax, and ludic-dev syntax: every grammar written from the compiler's vocabulary and checked against it
`ludic syntax [--json] [-o FILE]` prints what `ludicc --emit-syntax` does (a line
per entry, or the JSON). `ludic-dev syntax` writes, between "ludic-dev syntax:
begin" / "end" lines, the keyword, type, phase and attribute tables of
ludic_syntax.h, LudicVocabulary's sets (JetBrains), ludic-mode.el's lists and the
language server's word tests (is_keyword_word and the rest; is_contextual_word is
every word the parser does not reserve, and every declaring or modifying one),
and every TextMate pattern marked "comment": "ludic-dev syntax: <group>" (shared
and the VS Code copy). The grammars gain module uses port bind action reducer
dispatch registry def open component prop view alias friend unsafe numbers of as
from mut system; import and extern colour as declarations; the phase clause
knows Overlay; the bitwise pattern matches | and ^ on their own again.

`ludic-dev syntax --check` - and check-vocabulary, whose old parser comparison it
replaces, and the regression suite (syntax_cases, one line per file) - fails when
a written list is behind, when a grammar lacks a keyword, type or phase, when
docs/language has no page for a keyword, type, phase or attribute, or when the
parser (a scan of selfhost/frontend: is_id / text == words, a == / ann ==
attributes) tests a word or reads an attribute vocab.ludic lacks, or the reverse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:59:30 +03:00
49b5e80af5 ludic ui-preview: ludic.ui components previewed for a studio over stdio (R5)
A prebuilt tool, bin/ludic-ui-preview (built by ludic-dev build, shipped beside ludic-lsp, run as
`ludic ui-preview [--font DIR]`): ludic.ui with a backend that records every draw call as a line,
and mock component classes the studio describes (load / model / calls). frame t runs ui_show at
interface time t; text is measured on the CPU with the game's font.json metrics; locale goes
through ludic.i18n; tree / box / rules inspect the frame. No game code, no GPU, no network. The wire
protocol is frozen as tools/ui-preview/protocol-v1.md; smoke.txt is a transcript to run.

ludic.ui gains, all additive: UiClass.make_of, UiBackend.said / emitted, UiRule.text / at (with
comment line breaks kept so rule lines count true, and a class's styles read under its .lss path),
and ui_root, ui_find, ui_rules_of, ui_rule_value, ui_building, ui_class, ui_class_load,
ui_file_forget, ui_errors_clear; ui_nine_cuts_into exported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:53:26 +03:00
9cc3f24c56 ludic build on every core: the IR split with llvm-split and compiled by a clang per part at once
Most of a build was one clang -O2 on one .ll (the game: 32 s of a 41 s headless build, one core).
Both paths that assemble - the CLI's (build.ludic: ludicc --emit-llvm, then clang) and ludicc's own
(-o, which ludic bundle and the examples use) - now cut the program's IR into N parts with llvm-split
(externalizing what the parts share), compile them with one clang each in parallel (-x ir -O<opt>
-mmacosx-version-min=11.0, the link's own clang taking the objects where it took the .ll), and remove
the parts and objects after. N is $LUDIC_JOBS, else min(cores, free GB / 1.5).

It needs an llvm-split and a clang of the same LLVM (Homebrew's LLVM 22 writes attributes Apple's
clang 17 cannot read): $LUDIC_LLVM, else /opt/homebrew/opt/llvm/bin. With either missing, on Windows
(its shell cannot run the parts at once yet), with LUDIC_SPLIT=0, or when a part fails, it compiles the
.ll whole as before.

$LUDIC_OPT=1 is a developer's faster build; ludic bundle sets LUDIC_OPT=2 for its compile whatever the
shell says.

Measured before the compile-only rule (this Mac, 12 cores, one build at a time):
- the game headless: 37-41 s -> 13-15.5 s (8 parts / by free memory), peak 2.1 GB -> 1.0-1.1 GB;
- the lab headless: 43.1 s -> 12.7 s, peak 2.4 GB -> 1.0 GB;
- the game at LUDIC_OPT=1, split: 11.8 s (fps cost not measured).
Both built and linked clean; the goldens and a headless shot of the result are not run here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:12:27 +03:00
3fb1b7cd87 feat(render3d): every shader variant as Vulkan SPIR-V, built ahead of time
Vulkan cannot compile GLSL when the game starts, so the programs render3d builds are listed
(shaders/variants.list, 45 of them, collected with R3D_PROGRAMS_LOG across the self-tests, the
screens, the viewpoints and the debug switches) and `ludic-dev shaders` compiles each into
shaders/spv/<id>.vert.spv and .frag.spv with a manifest of what the backend needs: each
stage's uniform block and member offsets, the samplers' bindings, the vertex inputs.

The GLSL is the renderer's own, assembled as programs.ludic assembles it, through glslang's
relaxed Vulkan mode, so gpu_uniform / u_* can write the same uniforms into a block on Vulkan.
Bindings are assigned by the tool (glslang's own numbering put several samplers of one stage
on binding 0): the vertex block is 0, the fragment block 1, samplers from 2 in name order,
shared across both stages. Every stage passes spirv-val; `ludic-dev test` rebuilds and compares
wherever the Vulkan SDK is installed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 10:11:31 +03:00
208cad7ca1 feat(vk): Vk.* - Vulkan 1.0-1.4 generated from the registry, loaded at run time
ludic-dev vkgen reads vk.xml into runtime/native/vk_api.ludic (constants, every struct's
<Struct>_sizeof and <Struct>_<field> offsets, one extern per command) and vk_thunks.ll.
Every size and offset was compiled against the SDK's C headers; `ludic-dev test` checks the
tracked files against the registry wherever the Vulkan SDK is installed.

vk_win.ll (vulkan-1.dll) and vk_mac.ll (libvulkan.1.dylib, MoltenVK) open the loader at run
time, so a program built with Vk.* starts on a machine without Vulkan. ludicc and ludic
build link both for any program that uses Vk.*. The seeds are regenerated for the new
compiler.

vk_probe reports what a machine's Vulkan can do; vk_compute dispatches a Slang compute
shader and reads the picture back, clean under the validation layer on an RTX 3070 Ti and
on an M4 Pro through MoltenVK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 09:52:12 +03:00
f25289db20 feat(gl): OpenGL 4.1 and the ludic.render3d renderer
Some checks failed
ci / build-and-test (push) Waiting to run
commit-lint / conventional-commits (push) Waiting to run
bootstrap / cfree-fixpoint (push) Has been cancelled
docs / build-and-deploy (push) Successful in 34s
`Gl.*` binds the whole OpenGL 4.1 core API — every entry point of the
platform gl3.h with every GL_* constant, generated by `ludic-dev glgen`
with per-call ABI thunks. Windowed builds get an NSOpenGLContext on the
existing window at Retina resolution; headless builds render into an
offscreen CGL context, so a program that uses Gl.* renders and
screenshots identically under the test harness. It links gl.ll, the
thunks and OpenGL.framework only when used; every other build stays
byte-identical.

packages/ludic.render3d is a physically based renderer written on that
surface: HDRI image-based lighting, GPU-generated terrain with scanned
PBR materials, CDLOD, cascaded shadows, glTF with skinning, instanced
vegetation with impostors, procedural grass, water, SSAO, and an HDR
pipeline with bloom, auto-exposure and ACES.

It also carries this session's work on it: the terrain at half its cost
(10.3 -> 5.4 ms of frame), the streaming hitch that got worse the longer
you played, a resize that emptied the world, and the packaging that lets
a game use the renderer from its own repository — `ludic assets`, the
material manifest shipping with the package, and shader lookup falling
back to the install root. See changes/ for each, with its numbers.

The camping game that drove all of it has moved out to its own
repository, Maroon Lake; examples/rendering/smooth.ludic stays as the
renderer's example here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 03:31:12 +03:00
e175619543 refactor(cli)!: split the contributor tool out of the ludic CLI
`ludic help` ended with a section titled "contributing to the toolchain itself",
listing bootstrap, reseed, docs-gen and release tasks. None of that is available
to someone who installed the language — those tasks need the repository — so the
shipped tool was advertising work its user cannot do, in a namespace they have to
read past to find `new` and `run`.

The tasks move to a second program, dev.ludic -> bin/ludic-dev, built from a
checkout and excluded from every release artifact. `ludic` keeps the project and
package commands and nothing else; `ludic dev …` now explains where the tasks
went instead of failing as an unknown command.

What this shook out: the two programs share prelude/build/project/pkg, so the
helpers each had accreted in whichever file first needed them — cc(),
ensure_ludicc, the string functions, title_case, cmd_version — moved to where
both can see them. The argument-shift indirection added for the `dev` namespace
is gone with the namespace, so commands read argv directly again.

`ludic-dev test` asserts the split rather than trusting it: the staged install
must build a project, and `ludic dev build` there must fail while naming
ludic-dev. install.sh keeps building older tags, whose bootstrap goes through
main.ludic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:15:12 +03:00