Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8dfdee8
docs(brief): add m0.6 milestone brief
guysenpai Jun 3, 2026
89b160a
docs(brief): confirm specs read for m0.6
guysenpai Jun 3, 2026
65e7b63
docs(brief): activate m0.6
guysenpai Jun 3, 2026
7e98a03
docs(brief): log E1 frozen-header-size blocker
guysenpai Jun 3, 2026
2696fed
docs(brief): patch .bin header to 40 bytes, aligned (recorded deviation)
guysenpai Jun 3, 2026
c3184cd
feat(assets): add frozen on-disk format surfaces
guysenpai Jun 3, 2026
a23bafa
feat(assets): add AssetHandle and generation-checked registry
guysenpai Jun 3, 2026
ab68def
feat(assets): wire asset_pipeline module and E1 handle test
guysenpai Jun 3, 2026
41b5285
docs(brief): journal update
guysenpai Jun 3, 2026
51e15ee
feat(simd): add foundation/simd skeleton and adler32 kernel
guysenpai Jun 3, 2026
35ae269
feat(assets): add native RFC 1951 inflate and zlib wrapper
guysenpai Jun 3, 2026
39b4b35
feat(assets): wire foundation dep, codec, and E2 tests
guysenpai Jun 3, 2026
1745982
docs(brief): journal update
guysenpai Jun 3, 2026
aa2dcec
feat(simd): add paeth_filter_decode kernel
guysenpai Jun 3, 2026
8d73449
feat(assets): add PNG, glTF static, and WAV decoders
guysenpai Jun 3, 2026
64e311e
docs(brief): journal update
guysenpai Jun 3, 2026
cda2f63
feat(assets): align intermediate format to normative schema
guysenpai Jun 4, 2026
442d52f
feat(assets): add importers, cookers, BLAKE3 hashing, and cache
guysenpai Jun 4, 2026
f3886ee
feat(assets): add round-trip tests, fixtures, and offline cook
guysenpai Jun 4, 2026
a5ab2aa
docs(brief): journal update
guysenpai Jun 4, 2026
67ff3d2
feat(assets): add stable UUIDv7 identity to intermediate format
guysenpai Jun 4, 2026
6c6c929
docs(brief): journal update
guysenpai Jun 4, 2026
52c47da
docs(brief): record cache_diff gate ruling (recorded deviation)
guysenpai Jun 4, 2026
fda3c17
fix(assets): reject inflate length-repeat past total
guysenpai Jun 4, 2026
c7d320a
test(assets): add deflate negative-guard vectors
guysenpai Jun 4, 2026
2b502af
docs(assets): correct adler32 nmax and hash comments
guysenpai Jun 4, 2026
87d0b28
feat(assets): add async runtime loader and lifecycle
guysenpai Jun 4, 2026
a771152
docs(brief): journal update
guysenpai Jun 4, 2026
34bb3e6
docs(brief): close m0.6
guysenpai Jun 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 248 additions & 0 deletions briefs/m0.6-assets.md

Large diffs are not rendered by default.

139 changes: 139 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,36 @@ pub fn build(b: *std.Build) void {
});
render_module.addImport("weld_core", core_module);

// M0.6 — `weld_asset_pipeline` module: the Tier 1 Asset Pipeline. E1
// ships the day-1-frozen on-disk surfaces (intermediate
// `<type>.asset.etch` schema + runtime `.<type>.bin` 40-byte header),
// the `AssetHandle`, and the slot registry (refcount + generation
// invalidation). Depends on `weld_core` (the E5 async loader consumes the
// Tier 0 job system); the `foundation` (SIMD) import wires in at E2 when
// the `adler32` / `paeth` kernels land. No `weld_etch` dependency
// (brief §Out-of-scope).
// M0.6 / E2 — `foundation` module: transversal sibling submodules
// (math, simd). Ships `simd` (batched-SIMD kernels; `adler32` inaugural).
// Imports nothing but std (engine-simd.md §4). Consumed by
// `asset_pipeline` (zlib ADLER32 trailer check), the simd tests, and the
// adler32 bench.
const foundation_module = b.addModule("foundation", .{
.root_source_file = b.path("src/foundation/root.zig"),
.target = target,
.optimize = optimize,
});

const asset_pipeline_module = b.createModule(.{
.root_source_file = b.path("src/modules/asset_pipeline/root.zig"),
.target = target,
.optimize = optimize,
});
asset_pipeline_module.addImport("weld_core", core_module);
// M0.6 / E2 — `foundation` dep wires in now that simd exists (deferred
// from E1): the DEFLATE/zlib codec verifies the ADLER32 trailer via
// `foundation.simd.adler32`.
asset_pipeline_module.addImport("foundation", foundation_module);

// M0.2 / E6 — plugin loader ABI module shared with the stub
// plugin sub-projects under `tests/core/plugin_loader/stub_plugin/`.
// Exposes the C ABI types from `desc.zig` (no `WeldAPI` itself,
Expand Down Expand Up @@ -209,6 +239,18 @@ pub fn build(b: *std.Build) void {
const etch_tests = b.addTest(.{ .root_module = etch_module });
test_step.dependOn(&b.addRunArtifact(etch_tests).step);

// M0.6 — inline tests inside src/modules/asset_pipeline/**. The module
// root re-exports format/ and registry/, so every sub-file is reachable
// and its inline tests run (engine-zig-conventions.md §13).
const asset_pipeline_tests = b.addTest(.{ .root_module = asset_pipeline_module });
test_step.dependOn(&b.addRunArtifact(asset_pipeline_tests).step);

// M0.6 / E2 — inline tests inside src/foundation/** (traits + kernels).
// simd.zig re-exports traits/portable/dispatch/kernels, so they are all
// reachable and analysed (engine-zig-conventions.md §13).
const foundation_tests = b.addTest(.{ .root_module = foundation_module });
test_step.dependOn(&b.addRunArtifact(foundation_tests).step);

// Out-of-tree tests. Each file is its own root_module and imports
// `weld_core` to reach the engine internals.
// Out-of-tree bindings tests need to reach files that live outside
Expand Down Expand Up @@ -276,6 +318,10 @@ pub fn build(b: *std.Build) void {
/// M0.4 — when set, imports the `weld_render` module (GAL public
/// surface + Null backend, Vulkan backend wires in later).
render: bool = false,
/// M0.6 — when set, imports the `weld_asset_pipeline` module.
asset_pipeline: bool = false,
/// M0.6 / E2 — when set, imports the `foundation` module (simd).
foundation: bool = false,
/// M0.4 stabilization — when set, create a dedicated `zig build
/// <name>` step that runs ONLY this test. Used by the CI
/// runtime-smoke-test job to gate strictly on the capture PSNR
Expand Down Expand Up @@ -390,6 +436,22 @@ pub fn build(b: *std.Build) void {
// M0.4 — vk_gen *Raw variants emission (3 targets emitted, others
// not emitted).
.{ .path = "tests/vk_gen/raw_variants.zig" },
// M0.6 / E1 — asset registry stale-handle (generation) acceptance.
.{ .path = "tests/assets/handle_generation.zig", .asset_pipeline = true },
// M0.6 / E5 — async loader + lifecycle (internal 5 s watchdog).
.{ .path = "tests/assets/loader_async.zig", .asset_pipeline = true },
// M0.6 / E2 — DEFLATE/zlib inflate known-vector acceptance.
.{ .path = "tests/assets/deflate_vectors.zig", .asset_pipeline = true },
// M0.6 / E2 — adler32 kernel known vectors + cross-variant correctness.
.{ .path = "src/foundation/simd/tests/adler32_test.zig", .foundation = true },
.{ .path = "src/foundation/simd/tests/correctness.zig", .foundation = true },
// M0.6 / E3 — paeth_filter_decode kernel portable == reference.
.{ .path = "src/foundation/simd/tests/paeth_test.zig", .foundation = true },
// M0.6 / E4 — import → cook → load round-trips + cache differential.
.{ .path = "tests/assets/png_roundtrip.zig", .asset_pipeline = true },
.{ .path = "tests/assets/gltf_static_roundtrip.zig", .asset_pipeline = true },
.{ .path = "tests/assets/wav_roundtrip.zig", .asset_pipeline = true },
.{ .path = "tests/assets/cache_diff.zig", .asset_pipeline = true },
};
for (test_specs) |spec| {
const t_mod = b.createModule(.{
Expand Down Expand Up @@ -417,6 +479,12 @@ pub fn build(b: *std.Build) void {
if (spec.render) {
t_mod.addImport("weld_render", render_module);
}
if (spec.asset_pipeline) {
t_mod.addImport("weld_asset_pipeline", asset_pipeline_module);
}
if (spec.foundation) {
t_mod.addImport("foundation", foundation_module);
}
const t = b.addTest(.{ .root_module = t_mod });
const t_run = b.addRunArtifact(t);
if (spec.needs_stub_plugins) {
Expand Down Expand Up @@ -691,6 +759,77 @@ pub fn build(b: *std.Build) void {
);
render_bench_step.dependOn(&render_bench_run.step);

// ------------------------------------------- M0.6 adler32 baseline bench --
//
// Inaugural foundation/simd kernel throughput baseline. No parity target
// (cold path — runs once at cook time). `zig build bench-adler32`.
const adler32_bench_module = b.createModule(.{
.root_source_file = b.path("src/foundation/simd/bench/adler32_bench.zig"),
.target = target,
.optimize = optimize,
});
adler32_bench_module.addImport("foundation", foundation_module);
const adler32_bench_exe = b.addExecutable(.{
.name = "adler32-bench",
.root_module = adler32_bench_module,
});
b.installArtifact(adler32_bench_exe);
const adler32_bench_run = b.addRunArtifact(adler32_bench_exe);
adler32_bench_run.step.dependOn(b.getInstallStep());
if (b.args) |args| adler32_bench_run.addArgs(args);
const adler32_bench_step = b.step(
"bench-adler32",
"Run the M0.6 adler32 throughput baseline (pass `-- --smoke` for a CI sanity run)",
);
adler32_bench_step.dependOn(&adler32_bench_run.step);

// ------------------------------------------- M0.6 paeth baseline bench ----
//
// Second foundation/simd kernel throughput baseline. No parity target.
const paeth_bench_module = b.createModule(.{
.root_source_file = b.path("src/foundation/simd/bench/paeth_bench.zig"),
.target = target,
.optimize = optimize,
});
paeth_bench_module.addImport("foundation", foundation_module);
const paeth_bench_exe = b.addExecutable(.{
.name = "paeth-bench",
.root_module = paeth_bench_module,
});
b.installArtifact(paeth_bench_exe);
const paeth_bench_run = b.addRunArtifact(paeth_bench_exe);
paeth_bench_run.step.dependOn(b.getInstallStep());
if (b.args) |args| paeth_bench_run.addArgs(args);
const paeth_bench_step = b.step(
"bench-paeth",
"Run the M0.6 paeth_filter_decode throughput baseline (pass `-- --smoke` for a CI sanity run)",
);
paeth_bench_step.dependOn(&paeth_bench_run.step);

// ----------------------------------- M0.6 thin offline asset cook demo ----
//
// `zig build cook-demo` cooks the three M0.6 fixtures end-to-end through
// the cache and logs hits (brief §Observable behavior). The user-facing
// `weld cook` CLI is Phase 1.
const asset_cook_module = b.createModule(.{
.root_source_file = b.path("tools/asset_cook/main.zig"),
.target = target,
.optimize = optimize,
});
asset_cook_module.addImport("weld_asset_pipeline", asset_pipeline_module);
const asset_cook_exe = b.addExecutable(.{
.name = "asset_cook",
.root_module = asset_cook_module,
});
b.installArtifact(asset_cook_exe);
const asset_cook_run = b.addRunArtifact(asset_cook_exe);
if (b.args) |args| asset_cook_run.addArgs(args);
const asset_cook_step = b.step(
"cook-demo",
"Cook the M0.6 fixtures end-to-end (import → cook → cache; logs hits)",
);
asset_cook_step.dependOn(&asset_cook_run.step);

// -------------------------------------------- Fixture facade (S4 demo) --

// `@embedFile` cannot escape the package root of the module that
Expand Down
12 changes: 12 additions & 0 deletions src/foundation/root.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Foundation — transversal sibling submodules consumed across the engine.
//!
//! Per `engine-spec.md` §3.5 and `engine-simd.md` §4, `math` and `simd` are
//! sibling submodules with no mutual dependency. M0.6 ships `simd` (the
//! batched-kernel module); `math` is added when its first consumer lands.

/// Batched-SIMD kernels (adler32 in M0.6; audio mix, skinning, … later).
pub const simd = @import("simd/simd.zig");

comptime {
_ = simd;
}
63 changes: 63 additions & 0 deletions src/foundation/simd/bench/adler32_bench.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! ADLER32 throughput baseline (brief §Acceptance ▸ Benchmarks).
//!
//! Records the portable `@Vector` and scalar-reference throughput on a fixed
//! buffer. **Baseline only — no parity target.** ADLER32 sits on a cold
//! path (it runs once at cook time; the runtime mmaps the cooked `.bin`), so
//! a zlib-ng parity target is explicitly out of scope (brief §Notes —
//! cold-path principle). The number exists to track gross regressions, not
//! to chase a reference.
//!
//! Run: `zig build bench-adler32` (add `-- --smoke` for a tiny CI sanity run).

const std = @import("std");
const simd = @import("foundation").simd;

pub fn main(init: std.process.Init) !void {
const gpa = init.gpa;
const io = init.io;
const args = try init.minimal.args.toSlice(init.arena.allocator());

var smoke = false;
for (args[1..]) |a| {
if (std.mem.eql(u8, a, "--smoke")) smoke = true;
}

const buf_len: usize = if (smoke) 64 * 1024 else 8 * 1024 * 1024;
const iterations: usize = if (smoke) 4 else 64;

const buf = try gpa.alloc(u8, buf_len);
defer gpa.free(buf);
for (buf, 0..) |*b, i| b.* = @truncate(i *% 2_654_435_761);

var stdout_buf: [4096]u8 = undefined;
var stdout_w = std.Io.File.stdout().writer(io, &stdout_buf);
const out = &stdout_w.interface;

const vec_mbps = benchOne(simd.kernels.adler32.vectorized, buf, iterations, io);
const ref_mbps = benchOne(simd.kernels.adler32.reference, buf, iterations, io);

try out.print("## adler32 — bench baseline\n\n", .{});
try out.print("Buffer: {d} bytes, {d} iterations\n\n", .{ buf_len, iterations });
try out.print("| Variant | Throughput (MB/s) |\n", .{});
try out.print("|-----------|-------------------|\n", .{});
try out.print("| portable | {d:.1} |\n", .{vec_mbps});
try out.print("| reference | {d:.1} |\n", .{ref_mbps});
try out.print("\n(baseline only — no parity target; cold path)\n", .{});
try out.flush();
}

fn benchOne(comptime kernel: fn ([]const u8) u32, buf: []const u8, iterations: usize, io: std.Io) f64 {
const start = std.Io.Clock.Timestamp.now(io, .awake);
var sink: u32 = 0;
var it: usize = 0;
while (it < iterations) : (it += 1) {
sink ^= kernel(buf);
}
const elapsed_ns: i96 = start.untilNow(io).raw.nanoseconds;
std.mem.doNotOptimizeAway(sink);

if (elapsed_ns <= 0) return 0;
const total_bytes: f64 = @floatFromInt(buf.len * iterations);
const seconds: f64 = @as(f64, @floatFromInt(@as(i64, @intCast(elapsed_ns)))) / 1_000_000_000.0;
return total_bytes / seconds / 1_000_000.0;
}
69 changes: 69 additions & 0 deletions src/foundation/simd/bench/paeth_bench.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! Paeth-filter-decode throughput baseline (brief §Acceptance ▸ Benchmarks).
//!
//! **Baseline only — no parity target** (cold path; PNG defiltering runs
//! once at cook time). Tracks gross regressions, nothing more.
//!
//! Run: `zig build bench-paeth` (add `-- --smoke` for a tiny CI sanity run).

const std = @import("std");
const simd = @import("foundation").simd;

pub fn main(init: std.process.Init) !void {
const gpa = init.gpa;
const io = init.io;
const args = try init.minimal.args.toSlice(init.arena.allocator());

var smoke = false;
for (args[1..]) |a| {
if (std.mem.eql(u8, a, "--smoke")) smoke = true;
}

const bpp: u8 = 4; // RGBA8 scanline
const row_len: usize = if (smoke) 4 * 1024 else 4 * 1024 * 1024;
const iterations: usize = if (smoke) 4 else 64;

const prev = try gpa.alloc(u8, row_len);
defer gpa.free(prev);
const curr = try gpa.alloc(u8, row_len);
defer gpa.free(curr);
for (prev, 0..) |*b, i| b.* = @truncate(i *% 40_503);

var stdout_buf: [4096]u8 = undefined;
var stdout_w = std.Io.File.stdout().writer(io, &stdout_buf);
const out = &stdout_w.interface;

const vec_mbps = benchOne(simd.kernels.paeth.vectorized, prev, curr, bpp, iterations, io);
const ref_mbps = benchOne(simd.kernels.paeth.reference, prev, curr, bpp, iterations, io);

try out.print("## paeth_filter_decode — bench baseline\n\n", .{});
try out.print("Scanline: {d} bytes (bpp={d}), {d} iterations\n\n", .{ row_len, bpp, iterations });
try out.print("| Variant | Throughput (MB/s) |\n", .{});
try out.print("|-----------|-------------------|\n", .{});
try out.print("| portable | {d:.1} |\n", .{vec_mbps});
try out.print("| reference | {d:.1} |\n", .{ref_mbps});
try out.print("\n(baseline only — no parity target; cold path)\n", .{});
try out.flush();
}

fn benchOne(
comptime kernel: fn ([]const u8, []u8, u8) void,
prev: []const u8,
curr: []u8,
bpp: u8,
iterations: usize,
io: std.Io,
) f64 {
const start = std.Io.Clock.Timestamp.now(io, .awake);
var it: usize = 0;
while (it < iterations) : (it += 1) {
@memcpy(curr, prev); // reset the filtered scanline each pass
kernel(prev, curr, bpp);
}
const elapsed_ns: i96 = start.untilNow(io).raw.nanoseconds;
std.mem.doNotOptimizeAway(curr.ptr);

if (elapsed_ns <= 0) return 0;
const total_bytes: f64 = @floatFromInt(curr.len * iterations);
const seconds: f64 = @as(f64, @floatFromInt(@as(i64, @intCast(elapsed_ns)))) / 1_000_000_000.0;
return total_bytes / seconds / 1_000_000.0;
}
29 changes: 29 additions & 0 deletions src/foundation/simd/dispatch.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! Comptime variant dispatch. Each kernel resolves to the best
//! implementation available for the build target, decided at comptime so
//! the call site (`simd.adler32(data)`) carries no runtime branch.
//!
//! M0.6 ships portable `@Vector` kernels only, so every kernel resolves to
//! its portable variant. Arch variants (`arch_x86_64/`, `arch_aarch64/`)
//! slot into the `select*` functions below behind `traits.current` checks
//! in a later phase without touching any call site.

const portable = @import("portable.zig");

/// ADLER32 entry point selected for the build target.
pub const adler32 = selectAdler32();

fn selectAdler32() fn (data: []const u8) u32 {
// No ISA-specific variants in M0.6 (brief §Notes — `@Vector` only).
// Future shape:
// if (traits.current.has_avx2) return @import("arch_x86_64/avx2.zig").adler32;
// if (traits.current.has_neon) return @import("arch_aarch64/neon.zig").adler32;
return portable.adler32;
}

/// PNG Paeth-filter decode entry point selected for the build target.
pub const paeth_filter_decode = selectPaeth();

fn selectPaeth() fn (prev: []const u8, curr: []u8, bpp: u8) void {
// Portable-only in M0.6; arch variants slot in here later.
return portable.paeth_filter_decode;
}
Loading
Loading