skip to content
==> 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}