==> src/pathtrace.c <==1/* programs/pathtrace's CPU reference (mesh-ref pathtrace.rs): tiles in Z, the raster, the PPM. */
2#include "mesh/ref/pathtrace.h"
3
4#include "pathtrace/z.h"
5
6#include <pthread.h>
7#include <stdatomic.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <unistd.h>
12
13bool mesh_pt_tile_render(const uint32_t *sc, uint32_t t, uint32_t passes, uint32_t op, mesh_pt_tile *out) {
14 memset(out, 0, sizeof *out);
15 out->t = t;
16 out->passes = passes;
17 out->part_digests = calloc(passes ? passes : 1, sizeof *out->part_digests);
18 pt_v *acc = calloc(PT_TILE_PIX, sizeof *acc), *part = calloc(PT_TILE_PIX, sizeof *part);
19 if (!out->part_digests || !acc || !part) {
20 free(out->part_digests), free(acc), free(part);
21 out->part_digests = NULL;
22 return false;
23 }
24 for (uint32_t p = 0; p < passes; p++) {
25 out->part_digests[p] = pt_tile_pass_z(sc, t, p, part);
26 pt_tile_fold_z(sc, p + 1, op, acc, part, out->rgb, &out->acc_digest, &out->rgb_digest);
27 }
28 for (uint32_t i = 0; i < PT_TILE_PIX; i++) {
29 out->acc[3 * i] = pt_to_bits(acc[i].x);
30 out->acc[3 * i + 1] = pt_to_bits(acc[i].y);
31 out->acc[3 * i + 2] = pt_to_bits(acc[i].z);
32 }
33 free(acc), free(part);
34 return true;
35}
36
37void mesh_pt_tile_free(mesh_pt_tile *tile) {
38 free(tile->part_digests);
39 tile->part_digests = NULL;
40}
41
42typedef struct {
43 const uint32_t *sc, *ts;
44 size_t n;
45 uint32_t passes, op;
46 mesh_pt_tile *out;
47 atomic_size_t next;
48 atomic_bool failed;
49} work;
50
51static void *worker(void *arg) {
52 work *w = arg;
53 for (;;) {
54 size_t i = atomic_fetch_add(&w->next, 1);
55 if (i >= w->n) return NULL;
56 if (!mesh_pt_tile_render(w->sc, w->ts[i], w->passes, w->op, &w->out[i])) atomic_store(&w->failed, true);
57 }
58}
59
60bool mesh_pt_tiles(const uint32_t *sc, const uint32_t *ts, size_t n, uint32_t passes, uint32_t op, mesh_pt_tile *out) {
61 work w = {sc, ts, n, passes, op, out, 0, false};
62 long cpus = sysconf(_SC_NPROCESSORS_ONLN);
63 size_t threads = cpus > 0 ? (size_t)cpus : 4;
64 if (threads > n) threads = n ? n : 1;
65 pthread_t tid[threads];
66 size_t started = 0;
67 for (size_t i = 0; i < threads; i++)
68 if (pthread_create(&tid[started], NULL, worker, &w) == 0) started++;
69 if (started == 0) worker(&w);
70 for (size_t i = 0; i < started; i++) pthread_join(tid[i], NULL);
71 if (atomic_load(&w.failed)) {
72 for (size_t i = 0; i < n; i++) mesh_pt_tile_free(&out[i]);
73 return false;
74 }
75 return true;
76}
77
78void mesh_pt_raster(const uint32_t *sc, const uint32_t *rgb, uint8_t *out) {
79 uint32_t w = sc[PT_H_WIDTH], h = sc[PT_H_HEIGHT], tx = w / PT_TILE_W;
80 for (uint32_t y = 0; y < h; y++)
81 for (uint32_t x = 0; x < w; x++) {
82 uint32_t t = (y / PT_TILE_H) * tx + x / PT_TILE_W;
83 uint32_t p = rgb[t * PT_TILE_PIX + (y % PT_TILE_H) * PT_TILE_W + x % PT_TILE_W];
84 size_t o = 3 * ((size_t)y * w + x);
85 out[o] = (uint8_t)p;
86 out[o + 1] = (uint8_t)(p >> 8);
87 out[o + 2] = (uint8_t)(p >> 16);
88 }
89}
90
91uint8_t *mesh_pt_ppm(uint32_t w, uint32_t h, const uint8_t *raster, size_t *len) {
92 char head[64];
93 int n = snprintf(head, sizeof head, "P6\n%u %u\n255\n", w, h);
94 size_t body = (size_t)w * h * 3;
95 uint8_t *b = malloc((size_t)n + body);
96 if (!b) return NULL;
97 memcpy(b, head, (size_t)n);
98 memcpy(b + n, raster, body);
99 *len = (size_t)n + body;
100 return b;
101}