==> src/assemble.c <==1/* assemble.rs's Assembly, part_of, job_scene and renders. */
2#include "mesh/assemble.h"
3#include "mesh/abi/ops.h"
4#include "mesh/assemble_bmp.h"
5#include "mesh/ftz.h"
6#include "mesh/ref/pathtrace.h"
7#include "mesh/ref/sha256d.h"
8#include "mesh/strbuf.h"
9#include "pathtrace/digest.h"
10#include "pathtrace/job_scene.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
20typedef struct {
21 uint64_t item;
22 char *why;
23} bad;
24
25struct masm {
26 mj_job_id job;
27 uint32_t sc[PT_DEFAULT_SCENE_WORDS];
28 uint32_t tiles, passes, tone;
29 uint32_t *next; /* per tile: the next pass to fold */
30 uint32_t **acc; /* per tile: the accumulators, once a pass is folded */
31 uint32_t *rgb; /* tiles x PX */
32 bool *dirty;
33 uint32_t *owner;
34 momap *pending; /* item -> masm_item: committed, not folded yet */
35 momap *ready; /* item -> uint32_t *: blobs fetched ahead of their turn */
36 momap *by_member;
37 uint64_t folded, committed;
38 bad *bad;
39 size_t nbad;
40};
41
42bool masm_part_of(const uint8_t *out, size_t n, uint64_t *part) {
43 size_t at = 4 * (size_t)PT_J_PART;
44 if (n < at + 8) return false;
45 uint64_t v = 0;
46 for (int i = 7; i >= 0; i--) v = v << 8 | out[at + (size_t)i];
47 *part = v;
48 return true;
49}
50
51bool masm_job_scene(const mj_setup *setup, size_t n, uint32_t *sc, char **err) {
52 pt_setup_task *t = n ? mxalloc(n * sizeof *t) : NULL;
53 for (size_t i = 0; i < n; i++) t[i] = (pt_setup_task){setup[i].op, setup[i].arg};
54 long bad_at = pt_job_scene(t, n, sc);
55 free(t);
56 if (bad_at >= 0) {
57 if (err)
58 *err = mfmt("setup %u:0x%x: not a scene setting (5:0, 7:<word << 24 | value>)", setup[bad_at].op, setup[bad_at].arg);
59 return false;
60 }
61 uint32_t rule = pt_validate_in(sc, PT_DEFAULT_SCENE_WORDS, PT_FLEET_MAX_TILES);
62 if (rule) {
63 if (err) *err = mfmt("the job's scene is not valid (rule %u)", rule);
64 return false;
65 }
66 return true;
67}
68
69bool masm_renders(const char *program, uint32_t op) {
70 return strncmp(program, "pathtrace", 9) == 0 && (op == PT_OP_PASS || op == MESH_OP_JOB);
71}
72
73static void ready_free(void *v) { free(*(uint32_t **)v); }
74
75masm *masm_new(mj_job_id job, const mj_setup *setup, size_t nsetup, uint32_t tone, char **err) {
76 masm *a = mxalloc(sizeof *a);
77 if (!masm_job_scene(setup, nsetup, a->sc, err)) {
78 free(a);
79 return NULL;
80 }
81 a->job = job;
82 a->tiles = pt_tiles(a->sc);
83 a->passes = a->sc[PT_H_PASSES];
84 a->tone = tone;
85 a->next = mxalloc(a->tiles * sizeof *a->next);
86 a->acc = mxalloc(a->tiles * sizeof *a->acc);
87 a->rgb = mxalloc((size_t)a->tiles * PX * sizeof *a->rgb);
88 a->dirty = mxalloc(a->tiles * sizeof *a->dirty);
89 a->owner = mxalloc(a->tiles * sizeof *a->owner);
90 for (uint32_t t = 0; t < a->tiles; t++) a->owner[t] = UINT32_MAX;
91 a->pending = momap_new(1, sizeof(masm_item), NULL, NULL);
92 a->ready = momap_new(1, sizeof(uint32_t *), ready_free, NULL);
93 a->by_member = momap_new(1, sizeof(uint64_t), NULL, NULL);
94 return a;
95}
96
97void masm_free(masm *a) {
98 if (!a) return;
99 for (uint32_t t = 0; t < a->tiles; t++) free(a->acc[t]);
100 free(a->acc);
101 free(a->next);
102 free(a->rgb);
103 free(a->dirty);
104 free(a->owner);
105 momap_free(a->pending);
106 momap_free(a->ready);
107 momap_free(a->by_member);
108 for (size_t i = 0; i < a->nbad; i++) free(a->bad[i].why);
109 free(a->bad);
110 free(a);
111}
112
113mj_job_id masm_job(const masm *a) { return a->job; }
114const uint32_t *masm_scene(const masm *a) { return a->sc; }
115uint32_t masm_tiles(const masm *a) { return a->tiles; }
116uint32_t masm_passes(const masm *a) { return a->passes; }
117uint32_t masm_tone(const masm *a) { return a->tone; }
118uint64_t masm_items(const masm *a) { return (uint64_t)a->tiles * a->passes; }
119uint32_t masm_width(const masm *a) { return a->sc[PT_H_WIDTH]; }
120uint32_t masm_height(const masm *a) { return a->sc[PT_H_HEIGHT]; }
121uint32_t masm_spp(const masm *a) { return a->sc[PT_H_SPP]; }
122bool masm_complete(const masm *a) { return a->folded == masm_items(a); }
123uint64_t masm_folded(const masm *a) { return a->folded; }
124uint64_t masm_committed(const masm *a) { return a->committed; }
125const momap *masm_by_member(const masm *a) { return a->by_member; }
126size_t masm_bad_count(const masm *a) { return a->nbad; }
127
128const char *masm_bad_at(const masm *a, size_t i, uint64_t *item) {
129 if (item) *item = a->bad[i].item;
130 return a->bad[i].why;
131}
132
133uint32_t masm_tiles_done(const masm *a) {
134 uint32_t n = 0;
135 for (uint32_t t = 0; t < a->tiles; t++) n += a->next[t] == a->passes;
136 return n;
137}
138
139void masm_tile(const masm *a, uint32_t t, uint32_t *passes, uint32_t *owner) {
140 *passes = a->next[t];
141 *owner = a->owner[t];
142}
143
144static void split(const masm *a, uint64_t item, uint32_t *t, uint32_t *p) {
145 *t = (uint32_t)(item % a->tiles);
146 *p = (uint32_t)(item / a->tiles);
147}
148
149static bool is_folded(const masm *a, uint64_t item) {
150 uint32_t t, p;
151 split(a, item, &t, &p);
152 return p < a->next[t];
153}
154
155void masm_commit(masm *a, uint64_t item, masm_item it) {
156 if (item >= masm_items(a) || is_folded(a, item) || momap_get1(a->pending, item)) return;
157 *(masm_item *)momap_put1(a->pending, item, NULL) = it;
158 a->committed++;
159}
160
161static bool is_bad(const masm *a, uint64_t item) {
162 for (size_t i = 0; i < a->nbad; i++)
163 if (a->bad[i].item == item) return true;
164 return false;
165}
166
167size_t masm_wants(const masm *a, bool (*asked)(void *ctx, uint64_t item), void *ctx, size_t budget, uint64_t *items, masm_item *its) {
168 /* Candidates by (pass, tile): every tile's window, collected then ordered. */
169 typedef struct {
170 uint32_t p, t;
171 uint64_t item;
172 masm_item it;
173 } cand;
174 cand *v = NULL;
175 size_t n = 0;
176 for (uint32_t t = 0; t < a->tiles; t++) {
177 uint32_t nx = a->next[t], end = nx + MESH_ASSEMBLE_WINDOW < a->passes ? nx + MESH_ASSEMBLE_WINDOW : a->passes;
178 for (uint32_t p = nx; p < end; p++) {
179 uint64_t item = (uint64_t)p * a->tiles + t;
180 const masm_item *it = momap_get1(a->pending, item);
181 if (it && !momap_get1(a->ready, item) && !(asked && asked(ctx, item)) && !is_bad(a, item)) {
182 v = mxrealloc(v, (n + 1) * sizeof *v);
183 v[n++] = (cand){p, t, item, *it};
184 }
185 }
186 }
187 /* Insertion into (p, t) order: the tiles loop already gives t ascending within each p. */
188 for (size_t i = 1; i < n; i++) {
189 cand x = v[i];
190 size_t j = i;
191 while (j > 0 && (v[j - 1].p > x.p || (v[j - 1].p == x.p && v[j - 1].t > x.t))) {
192 v[j] = v[j - 1];
193 j--;
194 }
195 v[j] = x;
196 }
197 size_t k = n < budget ? n : budget;
198 for (size_t i = 0; i < k; i++) {
199 items[i] = v[i].item;
200 if (its) its[i] = v[i].it;
201 }
202 free(v);
203 return k;
204}
205
206const masm_item *masm_wanted(const masm *a, uint64_t item) { return momap_get1(a->pending, item); }
207
208/* acc += P, word by word in the cluster float mode Z: pathtrace-core's fold_column add. */
209static void fold(masm *a, uint32_t t, const uint32_t *part) {
210 if (!a->acc[t]) a->acc[t] = mxalloc(3 * PX * sizeof(uint32_t));
211 uint32_t *acc = a->acc[t];
212 for (size_t i = 0; i < 3 * PX; i++) acc[i] = mesh_ftz_bits(mesh_ftz_add(mesh_ftz_from_bits(acc[i]), mesh_ftz_from_bits(part[i])));
213 a->dirty[t] = true;
214}
215
216long masm_blob(masm *a, uint64_t item, const uint8_t *bytes, size_t n, char **err) {
217 const masm_item *ip = momap_get1(a->pending, item);
218 if (!ip) return 0;
219 masm_item it = *ip;
220 char *why = NULL;
221 if (n != PT_PAYLOAD || it.len != PT_PAYLOAD) why = mfmt("payload of %zu bytes, not %u", n, (unsigned)PT_PAYLOAD);
222 uint32_t *words = NULL;
223 if (!why) {
224 words = mxalloc(n);
225 for (size_t i = 0; i < n / 4; i++)
226 words[i] = (uint32_t)bytes[4 * i] | (uint32_t)bytes[4 * i + 1] << 8 | (uint32_t)bytes[4 * i + 2] << 16 | (uint32_t)bytes[4 * i + 3] << 24;
227 uint64_t d = pt_payload_digest(words);
228 if (d != it.part) why = mfmt("pass-sum digest %016llx, its record says %016llx", (unsigned long long)d, (unsigned long long)it.part);
229 }
230 if (why) {
231 free(words);
232 uint32_t t, p;
233 split(a, item, &t, &p);
234 char *e = mfmt("item %llu (tile %u pass %u, system %u): %s", (unsigned long long)item, t, p, it.member, why);
235 free(why);
236 a->bad = mxrealloc(a->bad, (a->nbad + 1) * sizeof *a->bad);
237 a->bad[a->nbad++] = (bad){item, mstrdup(e)};
238 if (err) *err = e;
239 else free(e);
240 return -1;
241 }
242 uint32_t **slot = momap_put1(a->ready, item, NULL);
243 free(*slot);
244 *slot = words;
245 uint32_t t, p0;
246 split(a, item, &t, &p0);
247 long folded = 0;
248 for (;;) {
249 uint32_t p = a->next[t];
250 if (p >= a->passes) break;
251 uint64_t w = (uint64_t)p * a->tiles + t;
252 uint32_t *ws = NULL;
253 if (!momap_take(a->ready, &w, &ws)) break;
254 masm_item done;
255 momap_take(a->pending, &w, &done);
256 fold(a, t, ws);
257 free(ws);
258 a->next[t] = p + 1;
259 a->owner[t] = done.member;
260 (*(uint64_t *)momap_put1(a->by_member, done.member, NULL))++;
261 a->folded++;
262 folded++;
263 }
264 return folded;
265}
266
267void masm_set_tone(masm *a, uint32_t tone) {
268 if (tone != a->tone && tone < PT_TONEMAPS) {
269 a->tone = tone;
270 for (uint32_t t = 0; t < a->tiles; t++) a->dirty[t] = true;
271 }
272}
273
274const uint32_t *masm_rgb(masm *a) {
275 for (uint32_t t = 0; t < a->tiles; t++) {
276 bool d = a->dirty[t];
277 a->dirty[t] = false;
278 if (!d || !a->acc[t]) continue;
279 pt_v *v = mxalloc(PX * sizeof *v);
280 for (size_t i = 0; i < PX; i++)
281 v[i] = (pt_v){mesh_ftz_from_bits(a->acc[t][3 * i]), mesh_ftz_from_bits(a->acc[t][3 * i + 1]), mesh_ftz_from_bits(a->acc[t][3 * i + 2])};
282 pt_tile_retone_z(a->sc, a->next[t], a->tone, v, a->rgb + (size_t)t * PX);
283 free(v);
284 }
285 return a->rgb;
286}
287
288const uint32_t *masm_acc(const masm *a, uint32_t t) { return a->acc[t]; }
289
290void masm_digests(masm *a, uint64_t *acc, uint64_t *pixels) {
291 uint64_t h = PT_FNV_OFFSET;
292 for (uint32_t t = 0; t < a->tiles; t++)
293 for (size_t i = 0; i < 3 * PX; i++) h = pt_fnv(h, a->acc[t] ? a->acc[t][i] : 0);
294 *acc = h;
295 *pixels = pt_fnv_words(masm_rgb(a), (size_t)a->tiles * PX);
296}
297
298uint8_t *masm_raster(masm *a, size_t *len) {
299 size_t n = (size_t)masm_width(a) * masm_height(a) * 3;
300 uint8_t *r = mxalloc(n);
301 mesh_pt_raster(a->sc, masm_rgb(a), r);
302 *len = n;
303 return r;
304}
305
306uint8_t *masm_ppm(masm *a, size_t *len) {
307 size_t rn;
308 uint8_t *r = masm_raster(a, &rn);
309 uint8_t *p = mesh_pt_ppm(masm_width(a), masm_height(a), r, len);
310 free(r);
311 return p;
312}
313
314uint8_t *masm_bmp_of(masm *a, size_t *len) {
315 size_t rn;
316 uint8_t *r = masm_raster(a, &rn);
317 uint8_t *b = masm_bmp(masm_width(a), masm_height(a), r, len);
318 free(r);
319 return b;
320}
321
322char *masm_sha256(masm *a) {
323 size_t n;
324 uint8_t *p = masm_ppm(a, &n);
325 uint8_t h[32];
326 mesh_sha256(p, n, h);
327 free(p);
328 char *x = mxalloc(65);
329 mesh_sha_hex(h, 32, x);
330 return x;
331}