feat(lang): L7 memory is safe unless it says unsafe
The typed buffers are slices: words/floats/fixeds/doubles/pointers(n) make
zeroed, bounds-checked []int/[]float/... and the type names mean them. buffer(n)
is a []byte, with text_of, Fs.read_bytes/write_bytes and view(xs, start, n).
bytes(), indexing a raw pointer or bytes, free, resize, Memory.*, raw file calls,
data_of and C externs are refused outside unsafe { } / unsafe function, and a
project's own files may write unsafe only with --unsafe; the runtime and packages
are the platform. A slice passed to an extern goes as its data.
What the change found: Sync's atomics on a slice header, words(n) uninitialised,
input's fixed axes in ints, truetype's fixed outlines as ints, skin matrices
typed int, gl_shader's source table made from raw bytes. render3d gets safe
entry points (safe_api.ludic). Rendering is byte-identical; a frame costs the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9259808f80
commit
b0b0b62bce
70 changed files with 69189 additions and 64569 deletions
41
packages/ludic.render3d/safe_api.ludic
Normal file
41
packages/ludic.render3d/safe_api.ludic
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# safe_api.ludic — the renderer's raw buffers, as a program may hold them (L7). The renderer reads
|
||||
# glTF accessors, the screen and PNG files into raw memory, which is its business; a game gets the
|
||||
# same data as slices, bounds-checked, and never frees anything. Each of these copies or hands over
|
||||
# at the boundary, so nothing raw crosses it.
|
||||
|
||||
# an accessor's float components (gltf_count elements of gltf_comps floats), as floats
|
||||
function gltf_accessor_floats(idx: int) -> floats {
|
||||
let p = gltf_accessor(idx)
|
||||
let n = gltf_count * gltf_comps
|
||||
let out = floats(n)
|
||||
for i in 0 .. n { out[i] = float_from_bits(mem_get_f32_bits(p, i)) }
|
||||
free(p)
|
||||
return out
|
||||
}
|
||||
# how many elements an accessor holds, without reading them
|
||||
function gltf_accessor_count(idx: int) -> int {
|
||||
let acc = value_at(value_get(gltf_doc, "accessors"), idx)
|
||||
return jint(acc, "count", 0)
|
||||
}
|
||||
# the presented frame as RGB8, bottom row first, into `out` (at least w * h * 3 bytes)
|
||||
function gpu_read_screen_bytes(w: int, h: int, out: []byte) -> bool {
|
||||
if out == null or len(out) < w * h * 3 { return false }
|
||||
gpu_read_screen(w, h, data_of(out))
|
||||
return true
|
||||
}
|
||||
# a PNG's samples (tex_w x tex_h x tex_channels, 8 or 16 bits): into `reuse` when it is large
|
||||
# enough - a map swap decodes the same size again - else into a new buffer. null if unreadable.
|
||||
function png_decode_bytes(path: string, reuse: []byte) -> []byte {
|
||||
let p = png_decode(path)
|
||||
if p == null { return null }
|
||||
let n = tex_w * tex_h * tex_channels * (tex_depth / 8)
|
||||
var out = reuse
|
||||
if out == null or len(out) < n { out = buffer(n) }
|
||||
for i in 0 .. n { out[i] = p[i] }
|
||||
free(p)
|
||||
return out
|
||||
}
|
||||
# upload samples laid out as the last decode left them (tex_w, tex_h, tex_channels, tex_depth)
|
||||
function tex_upload_bytes(px: []byte, srgb: bool, mips: bool) -> int {
|
||||
return tex_upload(data_of(px), srgb, mips)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue