/* Isolated benchmark for the sensor-bloom model in src/sensor_bloom.c. * * This performs no ray tracing, catalog lookup, or PSF splatting; it fills a * synthetic HDR framebuffer and times the post-processing model alone. The * default 512x288 size keeps a full sweep bounded, and the optional size * arguments are clamped so the benchmark cannot be turned into a production * render. It is a non-default target and is not part of `make test`. */ #include "sensor_bloom.h" #include #include #include #include #include #include #define DEFAULT_WIDTH 512 #define DEFAULT_HEIGHT 288 #define MAX_DIMENSION 1024 #define RESPONSE_LIMIT 1.0 static uint32_t next_random(uint32_t *state) { *state = *state * 1664525u + 1013904223u; return *state; } static uint64_t checksum(const double *hdr, size_t count) { const unsigned char *bytes = (const unsigned char *)hdr; uint64_t hash = 1469598103934665603ull; for (size_t i = 0; i < count * sizeof(double); ++i) { hash ^= bytes[i]; hash *= 1099511628211ull; } return hash; } static void fill_central(double *hdr, int width, int height) { const size_t index = ((size_t)(height / 2) * width + width / 2) * 3; hdr[index + 0] = 20.0; hdr[index + 1] = 20.0; hdr[index + 2] = 20.0; } static void fill_scattered(double *hdr, int width, int height) { int placed = 0; for (int y = 0; y < height && placed < 64; y += height > 8 ? height / 8 : 1) { for (int x = 0; x < width && placed < 64; x += width > 8 ? width / 8 : 1) { const size_t index = ((size_t)y * width + x) * 3; hdr[index + 0] = 20.0; hdr[index + 1] = 16.0; hdr[index + 2] = 12.0; ++placed; } } } static void fill_random_fraction(double *hdr, int width, int height) { const size_t count = (size_t)width * height * 3; uint32_t state = 20260927u; for (size_t i = 0; i < count; ++i) hdr[i] = 1.5 + (double)(next_random(&state) % 2500u) / 1000.0; for (size_t i = 0; i < count; ++i) if (next_random(&state) % 100u >= 5u) hdr[i] = 0.0; } typedef void (*FillFunction)(double *, int, int); static void run_case(const char *name, FillFunction fill, int width, int height, double transfer) { const size_t count = (size_t)width * height * 3; double *hdr = calloc(count, sizeof *hdr); if (hdr == NULL) { fprintf(stderr, "allocation failed for %s\n", name); exit(EXIT_FAILURE); } fill(hdr, width, height); const SensorBloomSettings settings = {RESPONSE_LIMIT, transfer}; SensorBloomStats stats; if (sensor_bloom_apply(hdr, width, height, &settings, &stats)) { fprintf(stderr, "%s e=%.2f failed\n", name, transfer); free(hdr); exit(EXIT_FAILURE); } /* Report the model's own guard-inclusive conservative cap, not a duplicate * of the bound formula. */ printf("%-10s e=%.2f size=%dx%d saturated=%zu predicted=%zu actual=%zu " "bbox=%.4f elapsed=%.6fs checksum=%016llx\n", name, transfer, width, height, stats.initially_saturated_channels, stats.predicted_iterations, stats.iterations, stats.bbox_coverage, stats.elapsed_seconds, (unsigned long long)checksum(hdr, count)); free(hdr); } int main(int argc, char **argv) { int width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT; if (argc >= 2) width = atoi(argv[1]); if (argc >= 3) height = atoi(argv[2]); if (width < 1) width = 1; if (height < 1) height = 1; if (width > MAX_DIMENSION) width = MAX_DIMENSION; if (height > MAX_DIMENSION) height = MAX_DIMENSION; const int workers = omp_get_max_threads(); printf("sensor-bloom benchmark: size=%dx%d threads=%d limit=%.3f\n", width, height, workers, RESPONSE_LIMIT); const double transfers[] = {0.0, 0.5, 0.9}; for (size_t e = 0; e < sizeof transfers / sizeof transfers[0]; ++e) { run_case("central", fill_central, width, height, transfers[e]); run_case("scattered", fill_scattered, width, height, transfers[e]); run_case("random5pct", fill_random_fraction, width, height, transfers[e]); } return EXIT_SUCCESS; }