==> tests/doom.c <==1/* kcheck's doom checks on the simulator (3, 3b, and the OP_FOLLOW part of 3c):
2 * the map and start pose after OP_INIT; seven poses, each frame (4 tickets
3 * over 4 nodes, the arg-0 one releasing the pose) against the CPU raycaster
4 * (@ajax/mesh-ref-doom) bitwise; keys through the input queue applied with
5 * wall collision, the pose bitwise == the reference's every frame; a follow
6 * renders at the view's pose and leaves it, the frame and the queue alone.
7 * Ported from alpha-rust-mesh crates/mesh-kcheck/src/pchecks.rs. */
8#include "mesh/check.h"
9#include "mesh/ref/doom.h"
10#include "mesh/rt/sim_mailbox.h"
11
12#include "../src/doom.c"
13
14static uint8_t *mb, *view;
15static uint32_t ref_map[256];
16
17static void set_pose(const float p[6]) { for (int k = 0; k < 6; k++) mesh_sim_set_f32(view + MESH_DOOM_D_POS_X + 4 * k, p[k]); }
18static void get_pose(float p[6]) { for (int k = 0; k < 6; k++) p[k] = mesh_sim_f32(view + MESH_DOOM_D_POS_X + 4 * k); }
19
20/* One frame (4 consecutive tickets), the hits summed. */
21static uint32_t frame(uint32_t op) {
22 uint32_t t[4], sum = 0;
23 for (uint32_t b = 0; b < 4; b++) t[b] = mesh_sim_publish(mb, 0, op, b);
24 for (uint32_t b = 0; b < 4; b++) {
25 uint32_t r = 0;
26 CHECK(mesh_sim_wait(mb, 0, t[b], &r, 60000));
27 sum += r;
28 }
29 return sum;
30}
31
32/* A frame whose arg-0 task drains keys: on the simulator 64 collective pops
33 * can outlast the program's POSE_WAIT_NS under load (on a GPU they take
34 * microseconds), so arg 0 completes before args 1..3 are published. Their
35 * tickets are still the next three: the pose hand-off is the same. */
36static uint32_t frame_keys(void) {
37 uint32_t r = 0, sum = 0, t = mesh_sim_publish(mb, 0, MESH_OP_TASK, 0);
38 CHECK(mesh_sim_wait(mb, 0, t, &r, 60000));
39 sum += r;
40 uint32_t ts[3];
41 for (uint32_t b = 1; b < 4; b++) ts[b - 1] = mesh_sim_publish(mb, 0, MESH_OP_TASK, b);
42 for (uint32_t b = 0; b < 3; b++) {
43 CHECK(mesh_sim_wait(mb, 0, ts[b], &r, 60000));
44 sum += r;
45 }
46 return sum;
47}
48
49/* The frame in the view against the CPU raycaster at pose: mismatching columns (+1 for the hit count). */
50static uint32_t frame_bad(const float *pose, uint32_t hits) {
51 uint32_t bad = 0, want_hits = 0;
52 for (uint32_t c = 0; c < MESH_DOOM_WIDTH; c++) {
53 uint32_t side;
54 float dist;
55 uint32_t k = mesh_doom_cast(ref_map, pose[0], pose[1], pose[2], pose[3], pose[4], pose[5], c, MESH_DOOM_MAX_STEPS, &side, &dist);
56 float h = mesh_doom_height(k, dist);
57 want_hits += k != 0;
58 if (mesh_sim_u32(view + MESH_DOOM_D_KIND + 4 * c) != k || mesh_sim_u32(view + MESH_DOOM_D_SIDE + 4 * c) != side ||
59 mesh_sim_u32(view + MESH_DOOM_D_DIST + 4 * c) != mesh_f32_bits(dist) ||
60 mesh_sim_u32(view + MESH_DOOM_D_HEIGHT + 4 * c) != mesh_f32_bits(h)) {
61 if (bad < 3) fprintf(stderr, "doom col %u: sim (%u,%u,%g,%g) cpu (%u,%u,%g,%g)\n", c, mesh_sim_u32(view + MESH_DOOM_D_KIND + 4 * c),
62 mesh_sim_u32(view + MESH_DOOM_D_SIDE + 4 * c), (double)mesh_sim_f32(view + MESH_DOOM_D_DIST + 4 * c),
63 (double)mesh_sim_f32(view + MESH_DOOM_D_HEIGHT + 4 * c), k, side, (double)dist, (double)h);
64 bad++;
65 }
66 }
67 return bad + (hits != want_hits);
68}
69
70int main(void) {
71 mesh_doom_map(ref_map);
72 mb = mesh_sim_mailbox();
73 /* The manifest's `input = 64`: the queue at the end of the view, 16-aligned. */
74 uint32_t input_off = (MESH_DOOM_VIEW_BYTES - (MESH_IQ_DATA + MESH_DOOM_INPUT_BYTES)) & ~15u;
75 mesh_sim_prog p = {.name = "doom", .view_bytes = MESH_DOOM_VIEW_BYTES, .nodes = 4, .input_off = input_off, .input_len = MESH_DOOM_INPUT_BYTES};
76 view = mesh_sim_install(mb, 0, &p);
77 uint8_t *ctl = mesh_sim_ctl(mb, 0);
78 mesh_sim_launch *l = mesh_sim_launch_prog(mb, 0, &p, mesh_main);
79 CHECK_U(mesh_sim_call(mb, 0, MESH_OP_INIT, 0), MESH_R_NONE);
80 /* 3: init and seven poses. */
81 for (uint32_t c = 0; c < 256; c++) CHECK_U(mesh_sim_u32(view + MESH_DOOM_D_MAP + 4 * c), ref_map[c]);
82 float g[6];
83 get_pose(g);
84 for (int k = 0; k < 6; k++) CHECK_U(mesh_f32_bits(g[k]), mesh_f32_bits(MESH_DOOM_START[k]));
85 CHECK_U(mesh_sim_u32(view + MESH_DOOM_D_ROWS), mesh_f32_bits(MESH_DOOM_ROWS));
86 CHECK_U(mesh_sim_u32(view + MESH_DOOM_D_MAX_STEPS), MESH_DOOM_MAX_STEPS);
87 const float poses[7][6] = {
88 {8.5f, 10.5f, 0.0f, -1.0f, 0.66f, 0.0f}, {8.5f, 8.5f, 0.0f, 1.0f, -0.66f, 0.0f},
89 {1.5f, 1.5f, 0.7071f, 0.7071f, -0.4667f, 0.4667f}, {14.5f, 14.5f, -0.7071f, -0.7071f, 0.4667f, -0.4667f},
90 {1.21f, 7.5f, -1.0f, 0.0f, 0.0f, -0.66f}, {14.79f, 1.21f, 0.6f, -0.8f, 0.528f, 0.396f},
91 {5.5f, 7.5f, 0.0f, -1.0f, 0.66f, 0.0f},
92 };
93 uint32_t hits_total = 0;
94 for (int i = 0; i < 7; i++) {
95 set_pose(poses[i]);
96 uint32_t hits = frame(MESH_OP_TASK);
97 hits_total += hits;
98 get_pose(g);
99 for (int k = 0; k < 6; k++) CHECK_U(mesh_f32_bits(g[k]), mesh_f32_bits(poses[i][k])); /* no keys: unchanged */
100 CHECK_U(frame_bad(poses[i], hits), 0);
101 }
102 CHECK(hits_total > 0);
103 /* On a GPU no frame stalls (kcheck checks 0). The simulator on an
104 * oversubscribed machine can run an arg-0 task later than POSE_WAIT_NS:
105 * the frame above was still exact (the pose did not change), so a stall
106 * here is noted, not failed. The keys frames below must not stall. */
107 uint32_t stalls0 = mesh_sim_u32(ctl + MESH_P_COUNTERS + 4 * MESH_DOOM_C_STALLS);
108 if (stalls0) fprintf(stderr, "note: %u pose waits outlasted POSE_WAIT_NS on this loaded machine\n", stalls0);
109 /* 3b: keys through the input queue, one burst a frame (the last fills the whole queue). */
110 mesh_doom_pose cpu = mesh_doom_pose_start();
111 set_pose(cpu.p);
112 uint32_t frames0 = mesh_sim_u32(ctl + MESH_P_COUNTERS + 4 * MESH_DOOM_C_FRAMES), keys = 0, nframes = 0;
113 uint8_t full[64];
114 memset(full, 'w', sizeof full);
115 const char *script[11] = {"w", "wwwwwwwwww", "dddd", "wwwwwwwwwwwwww", "qqqqqqqqqq", "eeeeeeeeeeeeeeeeeeee", "aaaaaaaaa", "wwwwsssswwwwdwdwdwdw", "xyz", "", "ssssssssssssssssssss"};
116 for (int i = 0; i < 12; i++) {
117 const uint8_t *burst = i < 11 ? (const uint8_t *)script[i] : full;
118 uint32_t n = i < 11 ? (uint32_t)strlen(script[i]) : 64;
119 CHECK(n == 0 || mesh_sim_input(mb, 0, burst, n));
120 for (uint32_t k = 0; k < n; k++) mesh_doom_pose_apply(&cpu, burst[k]);
121 keys += n;
122 uint32_t hits = frame_keys();
123 nframes++;
124 get_pose(g);
125 for (int k = 0; k < 6; k++) CHECK_U(mesh_f32_bits(g[k]), mesh_f32_bits(cpu.p[k]));
126 CHECK(mesh_doom_walkable(g[0], g[1]));
127 CHECK_U(frame_bad(cpu.p, hits), 0);
128 }
129 uint8_t *q = view + input_off;
130 CHECK_U(mesh_sim_u32(q + MESH_IQ_HEAD), mesh_sim_u32(q + MESH_IQ_TAIL));
131 CHECK_U(mesh_sim_u32(ctl + MESH_P_COUNTERS + 4 * MESH_DOOM_C_FRAMES) - frames0, nframes);
132 CHECK_U(mesh_sim_u32(ctl + MESH_P_COUNTERS + 4 * MESH_DOOM_C_KEYS), keys);
133 CHECK_U(mesh_sim_u32(view + MESH_DOOM_D_KEYS), keys);
134 CHECK_U(mesh_sim_u32(ctl + MESH_P_COUNTERS + 4 * MESH_DOOM_C_STALLS), stalls0);
135 /* A follow: a queued key, the frame number and the pose stay as they are. */
136 uint32_t frame_no = mesh_sim_u32(view + MESH_DOOM_D_FRAME);
137 CHECK(mesh_sim_input(mb, 0, "w", 1));
138 uint32_t hits = frame(MESH_OP_FOLLOW);
139 get_pose(g);
140 for (int k = 0; k < 6; k++) CHECK_U(mesh_f32_bits(g[k]), mesh_f32_bits(cpu.p[k]));
141 CHECK_U(frame_bad(cpu.p, hits), 0);
142 CHECK_U(mesh_sim_u32(view + MESH_DOOM_D_FRAME), frame_no);
143 CHECK_U(mesh_sim_u32(q + MESH_IQ_HEAD) - mesh_sim_u32(q + MESH_IQ_TAIL), 1);
144 CHECK_U(mesh_sim_u32(ctl + MESH_P_COUNTERS + 4 * MESH_DOOM_C_FOLLOWS), 4);
145 CHECK_U(mesh_sim_call(mb, 0, MESH_OP_FOLLOW, 4), MESH_R_BAD_OP);
146 /* A lone arg-1 ticket (its arg-0 lost): a bounded wait, then a stall. */
147 CHECK_U(mesh_sim_u32(ctl + MESH_P_FAULT), 0);
148 mesh_sim_halt(mb, 0, l);
149 mesh_sim_mailbox_free(mb);
150 return mesh_check_done();
151}