display: the compositor service — claim, double-buffer, present (D2)
Stand up /system/services/display: a ring-3 process that claims the framebuffer D1 seeded, maps it write-combining as the front buffer, allocates a cacheable back buffer, and presents composed frames. The GUI track's compositor, reached by name over ServiceId.display (= 9). - protocol.zig: the display wire protocol (info/create_layer/configure_layer/ destroy_layer/fill_rect/blit_tile/damage/present). `info` and a whole-screen `present` are live; the layer ops fail-stub until D3. - display.zig: enumerate -> claim -> mmio_map(WC) the LFB, mmap a cacheable back buffer, clear it and present it (proving the double-buffer path), then serve. - runtime.display + barrel exports (display, display_protocol): a cached `.display` client with info()/present(), the runtime.block shape. - init spawns "display" in boot_services; build.zig wires the protocol onto the runtime, builds the exe, packs it into the initial-ramdisk, installs it. mmap fix the back buffer forced: systemMmap was capped at 256 pages (1 MiB) by a fixed kernel-stack scratch array. Rewrote it to map page-by-page with rollback (no array) and raised the cap to 8192 pages (32 MiB) — enough for a 4K back buffer. A real limitation met. Gate: `python3 test/qemu_test.py display-service` matches the service's own serial heartbeats (display: online WxH / presented frame 0), printed only after the full claim -> WC-map -> back-buffer -> present chain. Regression-checked usermem, heap, init, and D1's display.
This commit is contained in:
@@ -97,6 +97,8 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void {
|
||||
ioPortTest();
|
||||
} else if (eql(case, "display")) {
|
||||
displayTest(boot_information);
|
||||
} else if (eql(case, "display-service")) {
|
||||
displayServiceTest(boot_information);
|
||||
} else if (eql(case, "clock")) {
|
||||
clockTest();
|
||||
} else if (eql(case, "smp")) {
|
||||
@@ -2307,6 +2309,41 @@ fn inputTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
}
|
||||
|
||||
/// D2 — the display service comes up. Spawn it from the initial_ramdisk; it claims the
|
||||
/// framebuffer the kernel seeded (D1), maps it write-combining, allocates a cacheable
|
||||
/// back buffer, and proves the double-buffer path by clearing that buffer and presenting
|
||||
/// it. Its `display: online WxH` + `display: presented frame 0` heartbeats are the
|
||||
/// markers — seeing them proves a user-space compositor took the framebuffer and pushed
|
||||
/// a whole composed frame to the screen, without ever drawing straight to the LFB.
|
||||
fn displayServiceTest(boot_information: *const BootInformation) void {
|
||||
log("DANOS-TEST-BEGIN: display-service\n", .{});
|
||||
if (boot_information.initial_ramdisk_len == 0) {
|
||||
check("bootloader handed over an initial_ramdisk", false);
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
return;
|
||||
};
|
||||
|
||||
// Spawn the compositor and hand it the core. Its own serial heartbeats — `display:
|
||||
// online WxH` and `display: presented frame 0` — are what the harness matches (it
|
||||
// reads serial directly, like the fault cases). We don't poll for them in-kernel: a
|
||||
// single service that comes up and blocks doesn't reschedule this bring-up context
|
||||
// (there is no other runnable task to bounce control back through), so the honest
|
||||
// observation point is the service's output itself, not a check() proxy here.
|
||||
if (!spawnNamed(rd, "display")) {
|
||||
log("display-service: could not spawn the display service\n", .{});
|
||||
result();
|
||||
return;
|
||||
}
|
||||
scheduler.setPriority(1); // below the service, so it runs and comes up first
|
||||
while (true) scheduler.yield();
|
||||
}
|
||||
|
||||
/// Process arguments, end to end: spawn args-echo bare (its argv[0] is the
|
||||
/// initial-ramdisk name). Instance 1 sees argc == 1 and respawns itself through
|
||||
/// `system_spawn` with the extra arguments "alpha beta-42" — the syscall argument
|
||||
|
||||
Reference in New Issue
Block a user