skip to content
==> tests/golden.c <==
1/* Golden digests from the Rust (pathtrace-core, R = f32): four tiles of the default
2 * scene over two passes (pass digests, fold digests, pixels, accumulators, a re-tone), the scalar
3 * functions (sqrt, cis, quantize, tonemap) over 200 000 inputs, and 4000 camera samples. */
4#include "pathtrace/f32.h"
5
6#include "pathtrace/digest.h"
7
8#include "pathtrace/default_scene.h"
9
10#include <stdio.h>
11#include <stdlib.h>
12
13static int failures;
14
15static uint64_t fh(uint64_t h, uint64_t w) { return (h ^ w) * 0x100000001b3ull; }
16
17static uint32_t mix(uint32_t x) {
18    x ^= x >> 16;
19    x *= 0x7feb352du;
20    x ^= x >> 15;
21    x *= 0x846ca68bu;
22    return x ^ (x >> 16);
23}
24
25/* The operand generator of the golden program (mesh-ftz's tests have the same). */
26static uint32_t gen(uint32_t m, uint32_t i) {
27    uint32_t h = mix((i * 0x9e3779b9u) ^ (m * 0x85ebca6bu));
28    uint32_t h2 = mix(h ^ 0x27d4eb2fu);
29    if (m == 0) return h;
30    if (m == 1) return (h & 0x80000000u) | ((h2 % 41) << 23) | (h & 0x007fffffu);
31    return (h & 0x80000000u) | ((50 + h2 % 31) << 23) | (h & 0x007fffffu);
32}
33
34static void want(const char *what, uint64_t got, uint64_t want) {
35    if (got != want) {
36        fprintf(stderr, "%s: %#018llx, want %#018llx\n", what, (unsigned long long)got, (unsigned long long)want);
37        failures++;
38    }
39}
40
41static void tiles(const uint32_t *sc) {
42    uint64_t h = 0xcbf29ce484222325ull;
43    const uint32_t ts[4] = {0, 42, 157, 299};
44    pt_v *acc = malloc(PT_TILE_PIX * sizeof *acc), *part = malloc(PT_TILE_PIX * sizeof *part);
45    uint32_t *rgb = malloc(PT_TILE_PIX * sizeof *rgb);
46    for (int i = 0; i < 4; i++) {
47        for (uint32_t p = 0; p < PT_TILE_PIX; p++) acc[p] = (pt_v){0, 0, 0}, rgb[p] = 0;
48        for (uint32_t p = 0; p < 2; p++) {
49            h = fh(h, pt_tile_pass_f32(sc, ts[i], p, part));
50            uint64_t da, dr;
51            pt_tile_fold_f32(sc, p + 1, p % 2, acc, part, rgb, &da, &dr);
52            h = fh(fh(h, da), dr);
53        }
54        for (uint32_t p = 0; p < PT_TILE_PIX; p++) h = fh(h, rgb[p]);
55        for (uint32_t p = 0; p < PT_TILE_PIX; p++)
56            h = fh(fh(fh(h, pt_to_bits(acc[p].x)), pt_to_bits(acc[p].y)), pt_to_bits(acc[p].z));
57        h = fh(h, pt_tile_retone_f32(sc, 2, 1, acc, rgb));
58    }
59    free(acc), free(part), free(rgb);
60    want("tiles", h, 0x0ebdc45fd60c40a7ull);
61}
62
63static void scalars(void) {
64    uint64_t h = 0xcbf29ce484222325ull;
65    for (uint32_t i = 0; i < 200000; i++) {
66        float x = pt_from_bits(gen(i % 3, i));
67        h = fh(h, pt_to_bits(pt_sqrt_f32(x)));
68        float c, s;
69        pt_cis_f32(mix(i), &c, &s);
70        h = fh(fh(h, pt_to_bits(c)), pt_to_bits(s));
71        h = fh(h, pt_quantize_f32(x));
72        h = fh(h, pt_quantize_f32(pt_from_bits(mix(i) >> 2)));
73        pt_v v = {x, pt_from_bits(mix(i) >> 1), pt_from_bits(0x3f000000u ^ (i & 0xffff))};
74        h = fh(h, pt_tonemap_f32(v, 1 + i % 64, pt_from_bits(0x3f800000u), i % 2));
75    }
76    want("scalars", h, 0xcd7759b2f49657b3ull);
77}
78
79static void samples(const uint32_t *sc) {
80    pt_layout l = pt_layout_of(sc);
81    uint64_t h = 0xcbf29ce484222325ull;
82    for (uint32_t i = 0; i < 4000; i++) {
83        pt_v v = pt_sample_f32(sc, &l, mix(i) % 640, mix(i ^ 0xabcdefu) % 360, i);
84        h = fh(fh(fh(h, pt_to_bits(v.x)), pt_to_bits(v.y)), pt_to_bits(v.z));
85    }
86    want("samples", h, 0x15444c3b8761be83ull);
87}
88
89/* pathtrace-core's sqrt_and_cis_are_close and quantize_is_srgb. The references are computed in
90 * double here (Newton for sqrt, Taylor series for cos and sin), so the test needs no libm. */
91static double dsqrt(double x) {
92    double y = x > 1 ? x : 1;
93    for (int i = 0; i < 100; i++) y = 0.5 * (y + x / y);
94    return y;
95}
96
97static void dcis(double a, double *c, double *s) {
98    const double tau = 6.283185307179586;
99    while (a > 3.141592653589793) a -= tau;
100    double t = a, cs = 1, sn = a, a2 = a * a;
101    for (int k = 1; k < 30; k++) {
102        t = -t * a2 / ((2 * k) * (2 * k + 1));
103        sn += t;
104    }
105    t = 1;
106    for (int k = 1; k < 30; k++) {
107        t = -t * a2 / ((2 * k - 1) * (2 * k));
108        cs += t;
109    }
110    *c = cs, *s = sn;
111}
112
113static double dabs(double x) { return x < 0 ? -x : x; }
114
115static void sqrt_and_cis_are_close(void) {
116    const float xs[7] = {1e-6f, 0.01f, 0.5f, 1.0f, 2.0f, 3.0f, 1e6f};
117    for (int i = 0; i < 7; i++) {
118        double r = dsqrt(xs[i]);
119        if (dabs(pt_sqrt_f32(xs[i]) - (float)r) > (float)r * 2e-7) failures++;
120    }
121    const uint32_t hs[6] = {0, 1u << 29, 1u << 30, 3u << 30, 0x12345678u, 0xffffffffu};
122    for (int i = 0; i < 6; i++) {
123        double a = 2.0 * 3.141592653589793 * ((double)(hs[i] >> 6) / (double)(1ull << 26)), rc, rs;
124        dcis(a, &rc, &rs);
125        float c, s;
126        pt_cis_f32(hs[i], &c, &s);
127        if (dabs(c - rc) >= 1e-6 || dabs(s - rs) >= 1e-6) {
128            fprintf(stderr, "cis %#x: %g %g vs %g %g\n", hs[i], c, s, rc, rs);
129            failures++;
130        }
131    }
132}
133
134static void quantize_is_srgb(void) {
135    if (pt_quantize_f32(0.0f) != 0 || pt_quantize_f32(1.0f) != 255 || pt_quantize_f32(0.2122f) != 127 ||
136        pt_quantize_f32(pt_from_bits(0x7fc00000u)) != 0)
137        failures++;
138}
139
140/* A payload's digest (from its words, as meshd's assembler checks it) is the item's pass digest. */
141static void payload_digest(void) {
142    pt_v *part = malloc(PT_TILE_PIX * sizeof *part);
143    uint32_t *words = malloc(3 * PT_TILE_PIX * sizeof *words);
144    uint64_t d = pt_tile_pass_f32(pt_default_scene, 42, 3, part);
145    for (uint32_t p = 0; p < PT_TILE_PIX; p++) {
146        words[3 * p] = pt_to_bits(part[p].x);
147        words[3 * p + 1] = pt_to_bits(part[p].y);
148        words[3 * p + 2] = pt_to_bits(part[p].z);
149    }
150    want("payload", d, 0xbb4c3b272139e454ull);
151    want("payload digest", pt_payload_digest(words), d);
152    free(part), free(words);
153}
154
155int main(void) {
156    tiles(pt_default_scene);
157    scalars();
158    samples(pt_default_scene);
159    sqrt_and_cis_are_close();
160    quantize_is_srgb();
161    payload_digest();
162    return failures != 0;
163}