==> tests/assemble.c <==1/* assemble.rs's tests: the ordered fold equals the CPU reference bit for bit, whatever order the blobs
2 * come in and however often, each (tile, pass) folded once, a wrong payload refused; a tone-map swap;
3 * the default scene's shape and job_scene's refusals. */
4#include "mesh/assemble.h"
5#include "mesh/assemble_bmp.h"
6#include "mesh/check.h"
7#include "mesh/ftz.h"
8#include "mesh/ref/pathtrace.h"
9#include "mesh/strbuf.h"
10#include "pathtrace/digest.h"
11#include "pathtrace/scene.h"
12#include "pathtrace/view.h"
13#include "pathtrace/z.h"
14
15#include <stdlib.h>
16#include <string.h>
17
18#define PX PT_TILE_PIX
19
20/* A small scene's tile pass as the programs compute it (Z: the wasm image's code). */
21static uint64_t payload(const uint32_t *sc, uint32_t t, uint32_t p, uint8_t *out) {
22 pt_v *part = mxalloc(PX * sizeof *part);
23 uint64_t d = pt_tile_pass_z(sc, t, p, part);
24 for (size_t i = 0; i < PX; i++) {
25 uint32_t w[3] = {mesh_ftz_bits(part[i].x), mesh_ftz_bits(part[i].y), mesh_ftz_bits(part[i].z)};
26 for (int k = 0; k < 3; k++)
27 for (int b = 0; b < 4; b++) out[12 * i + 4 * (size_t)k + (size_t)b] = (uint8_t)(w[k] >> 8 * b);
28 }
29 free(part);
30 return d;
31}
32
33static const mj_setup SMALL[] = {{5, 0}, {7, 0}, {7, 0}, {7, 0}, {7, 0}};
34
35static void small(mj_setup s[5]) {
36 memcpy(s, SMALL, sizeof SMALL);
37 s[1].arg = pt_set_arg(PT_H_WIDTH, 96);
38 s[2].arg = pt_set_arg(PT_H_HEIGHT, 48);
39 s[3].arg = pt_set_arg(PT_H_PASSES, 5);
40 s[4].arg = pt_set_arg(PT_H_SPP, 2);
41}
42
43static void ordered_fold(void) {
44 mj_setup setup[5];
45 small(setup);
46 uint32_t sc[PT_DEFAULT_SCENE_WORDS];
47 CHECK(masm_job_scene(setup, 5, sc, NULL));
48 uint32_t tiles = pt_tiles(sc), passes = sc[PT_H_PASSES];
49 CHECK(tiles == 6 && passes == 5);
50 size_t nitems = (size_t)tiles * passes;
51 uint8_t **bytes = mxalloc(nitems * sizeof *bytes);
52 uint64_t *dig = mxalloc(nitems * sizeof *dig);
53 for (size_t w = 0; w < nitems; w++) {
54 bytes[w] = mxalloc(PT_PAYLOAD);
55 dig[w] = payload(sc, (uint32_t)(w % tiles), (uint32_t)(w / tiles), bytes[w]);
56 }
57 uint32_t ts[6] = {0, 1, 2, 3, 4, 5};
58 mesh_pt_tile ref[6], alt[6];
59 CHECK(mesh_pt_tiles(sc, ts, tiles, passes, PT_TONEMAP_ACES, ref));
60 CHECK(mesh_pt_tiles(sc, ts, tiles, passes, PT_TONEMAP_REINHARD, alt));
61 uint32_t *ref_acc = mxalloc((size_t)tiles * 3 * PX * 4), *ref_rgb = mxalloc((size_t)tiles * PX * 4), *alt_rgb = mxalloc((size_t)tiles * PX * 4);
62 for (uint32_t t = 0; t < tiles; t++) {
63 memcpy(ref_acc + (size_t)t * 3 * PX, ref[t].acc, 3 * PX * 4);
64 memcpy(ref_rgb + (size_t)t * PX, ref[t].rgb, PX * 4);
65 memcpy(alt_rgb + (size_t)t * PX, alt[t].rgb, PX * 4);
66 }
67 for (uint64_t seed = 1; seed < 6; seed++) {
68 masm *a = masm_new(9, setup, 5, PT_TONEMAP_ACES, NULL);
69 size_t order[30];
70 for (size_t i = 0; i < nitems; i++) order[i] = i;
71 uint64_t r = seed;
72 for (size_t i = nitems - 1; i >= 1; i--) {
73 r ^= r << 13;
74 r ^= r >> 7;
75 r ^= r << 17;
76 size_t j = (size_t)(r % (i + 1));
77 size_t x = order[i];
78 order[i] = order[j];
79 order[j] = x;
80 }
81 /* Commits arrive in a random order too; every blob comes twice, some before their commit. */
82 for (size_t k = 0; k < nitems; k++) {
83 size_t i = order[k];
84 masm_commit(a, i, (masm_item){.len = PT_PAYLOAD, .holders = 1, .member = (uint32_t)(k % 4), .part = dig[i]});
85 masm_item junk = {.len = PT_PAYLOAD, .holders = 1, .member = 9, .part = 0};
86 memset(junk.hash.b, 1, 32);
87 masm_commit(a, i, junk);
88 if (k % 3 == 0) CHECK(masm_blob(a, i, bytes[i], PT_PAYLOAD, NULL) >= 0);
89 }
90 for (size_t k = nitems; k-- > 0;) {
91 size_t i = order[k];
92 CHECK(masm_blob(a, i, bytes[i], PT_PAYLOAD, NULL) >= 0);
93 CHECK(masm_blob(a, i, bytes[i], PT_PAYLOAD, NULL) >= 0);
94 }
95 uint64_t sum = 0, m;
96 for (const uint64_t *c = momap_first(masm_by_member(a), &m); c; c = momap_next(masm_by_member(a), &m, &m)) sum += *c;
97 CHECK(masm_complete(a) && masm_folded(a) == nitems && sum == masm_folded(a) && masm_bad_count(a) == 0);
98 bool acc_same = true;
99 for (uint32_t t = 0; t < tiles; t++) acc_same &= memcmp(masm_acc(a, t), ref_acc + (size_t)t * 3 * PX, 3 * PX * 4) == 0;
100 CHECK(acc_same);
101 CHECK(memcmp(masm_rgb(a), ref_rgb, (size_t)tiles * PX * 4) == 0);
102 uint64_t da, dp;
103 masm_digests(a, &da, &dp);
104 CHECK(da == pt_fnv_words(ref_acc, (size_t)tiles * 3 * PX) && dp == pt_fnv_words(ref_rgb, (size_t)tiles * PX));
105 masm_free(a);
106 }
107 /* A tone-map swap changes the pixels, never the accumulators; back again, the same bits. */
108 masm *a = masm_new(9, setup, 5, 0, NULL);
109 for (size_t w = 0; w < nitems; w++) {
110 masm_commit(a, w, (masm_item){.len = PT_PAYLOAD, .holders = 1, .member = 0, .part = dig[w]});
111 CHECK(masm_blob(a, w, bytes[w], PT_PAYLOAD, NULL) >= 0);
112 }
113 uint64_t a0, p0, a1, p1, a2, p2;
114 masm_digests(a, &a0, &p0);
115 masm_set_tone(a, PT_TONEMAP_REINHARD);
116 masm_digests(a, &a1, &p1);
117 CHECK(a1 == a0 && p1 == pt_fnv_words(alt_rgb, (size_t)tiles * PX) && p1 != p0);
118 masm_set_tone(a, PT_TONEMAP_ACES);
119 masm_digests(a, &a2, &p2);
120 CHECK(a2 == a0 && p2 == p0);
121 masm_free(a);
122 /* A payload whose digest is not its record's is refused, never folded. */
123 a = masm_new(9, setup, 5, 0, NULL);
124 masm_commit(a, 0, (masm_item){.len = PT_PAYLOAD, .holders = 1, .member = 2, .part = dig[0]});
125 uint8_t *b = mxalloc(PT_PAYLOAD);
126 memcpy(b, bytes[0], PT_PAYLOAD);
127 b[100] ^= 1;
128 char *err = NULL;
129 CHECK(masm_blob(a, 0, b, PT_PAYLOAD, &err) < 0 && masm_folded(a) == 0 && masm_bad_count(a) == 1);
130 static const char want[] = "item 0 (tile 0 pass 0, system 2): pass-sum digest ";
131 CHECK(err && strncmp(err, want, sizeof want - 1) == 0 && strstr(err, ", its record says ") != NULL);
132 free(err);
133 CHECK(masm_blob(a, 0, bytes[0], PT_PAYLOAD, NULL) >= 0 && masm_folded(a) == 1);
134 /* A payload of the wrong length. */
135 masm_commit(a, 1, (masm_item){.len = PT_PAYLOAD, .holders = 1, .member = 1, .part = dig[1]});
136 CHECK(masm_blob(a, 1, bytes[1], 100, &err) < 0);
137 CHECK_S(err, "item 1 (tile 1 pass 0, system 1): payload of 100 bytes, not 9216");
138 free(err);
139 masm_free(a);
140 free(b);
141 for (size_t w = 0; w < nitems; w++) free(bytes[w]);
142 free(bytes);
143 free(dig);
144 for (uint32_t t = 0; t < tiles; t++) {
145 mesh_pt_tile_free(&ref[t]);
146 mesh_pt_tile_free(&alt[t]);
147 }
148 free(ref_acc);
149 free(ref_rgb);
150 free(alt_rgb);
151}
152
153static void default_reference(void) {
154 masm *a = masm_new(1, NULL, 0, 0, NULL);
155 CHECK(masm_width(a) == 640 && masm_height(a) == 360 && masm_tiles(a) == 300 && masm_passes(a) == 32 && masm_spp(a) == 8);
156 masm_free(a);
157 uint32_t sc[PT_DEFAULT_SCENE_WORDS];
158 char *err = NULL;
159 CHECK(!masm_job_scene((const mj_setup[]){{3, 0}}, 1, sc, &err));
160 CHECK_S(err, "setup 3:0x0: not a scene setting (5:0, 7:<word << 24 | value>)");
161 free(err);
162 CHECK(!masm_job_scene((const mj_setup[]){{7, pt_set_arg(PT_H_WIDTH, 100)}}, 1, sc, &err));
163 free(err);
164 CHECK(masm_renders("pathtrace", PT_OP_PASS) && masm_renders("pathtrace-live", 254) && !masm_renders("pathtrace", 3) && !masm_renders("sha256d", 254));
165 uint64_t part;
166 uint8_t rec[32] = {0};
167 rec[8] = 0x11;
168 rec[15] = 0x88;
169 CHECK(masm_part_of(rec, 32, &part) && part == 0x8800000000000011u);
170 CHECK(!masm_part_of(rec, 15, &part));
171}
172
173int main(void) {
174 ordered_fold();
175 default_reference();
176 return mesh_check_done();
177}