Parallelise rendering

This commit is contained in:
2025-09-23 15:36:08 +01:00
parent 8827f22359
commit 75b646dd44
4 changed files with 93 additions and 39 deletions

2
demo.c
View File

@@ -24,8 +24,6 @@ static pix_t pixbuf[W * H];
int main()
{
rng_init();
img_t img = { .pix = pixbuf };
camera_t camera = camera_init(camera_pos, FOCAL_LEN, VIEWPORT_H, W, H);
camera_render(&camera, scene, NELEMS(scene), &img);

View File

@@ -3,9 +3,15 @@
#include "vec3.h"
void rng_init();
#include <stdint.h>
double rng_double();
vec3_t rng_vec3();
typedef struct {
uint32_t state;
} rng_t;
rng_t rng_init(unsigned seed);
double rng_double(rng_t *rng);
vec3_t rng_vec3(rng_t *rng);
#endif

View File

@@ -5,24 +5,36 @@
#include <float.h>
#include <stdio.h>
#include <threads.h>
#define MAX_ITER 10
#define MIN_T 1e-6
#define NSAMPLES 100
#define NTHREADS 40
typedef struct {
const camera_t *camera;
const obj_t *scene;
pix_t *pixels;
rng_t rng;
unsigned start_y, row_count, scene_count;
} work_slice_t;
static const vec3_t lightblue = { 0.4, 0.6, 1.0 };
static const vec3_t white = { 1.0, 1.0, 1.0 };
static const vec3_t black = { 0.0, 0.0, 0.0 };
static ray_t scatter(hit_t hit)
static ray_t scatter(hit_t hit, rng_t *rng)
{
const vec3_t rand = rng_vec3();
const vec3_t rand = rng_vec3(rng);
const vec3_t dir
= vec3_dot(rand, hit.normal) > 0.0 ? rand : vec3_scale(rand, -1.0);
return (ray_t) { .orig = hit.point, .dir = dir };
}
static vec3_t trace(ray_t ray, const obj_t *scene, unsigned scene_count)
static vec3_t
trace(ray_t ray, const obj_t *scene, unsigned scene_count, rng_t *rng)
{
double coeff = 1.0;
for (unsigned i = 0; i < MAX_ITER; ++i) {
@@ -37,7 +49,7 @@ static vec3_t trace(ray_t ray, const obj_t *scene, unsigned scene_count)
coeff);
}
ray = scatter(hit);
ray = scatter(hit, rng);
coeff *= 0.5;
}
@@ -52,6 +64,39 @@ static void setpix(vec3_t col, pix_t *out)
out->a = UINT16_MAX;
}
static int render_thread(void *arg)
{
work_slice_t *slice = (work_slice_t *)arg;
const camera_t *camera = slice->camera;
const uint32_t w = camera->img_width;
const unsigned stop_y = slice->start_y + slice->row_count;
for (unsigned y = slice->start_y; y < stop_y; ++y) {
const vec3_t row
= vec3_add(camera->pix_origin, vec3_scale(camera->y_step, y));
for (unsigned x = 0; x < w; ++x) {
const vec3_t pix = vec3_add(row, vec3_scale(camera->x_step, x));
const ray_t ray = {
.orig = camera->pos,
.dir = vec3_unit(pix),
};
vec3_t colour = black;
for (unsigned i = 0; i < NSAMPLES; ++i) {
const double weight = 1.0 / NSAMPLES;
const vec3_t sample = trace(
ray, slice->scene, slice->scene_count, &slice->rng);
colour = vec3_add(colour, vec3_scale(sample, weight));
}
setpix(colour, slice->pixels + (w * y + x));
}
}
return 0;
}
camera_t camera_init(
vec3_t pos, double focal_len, double viewport_height, uint32_t img_width,
uint32_t img_height)
@@ -83,32 +128,29 @@ void camera_render(
const camera_t *camera, const obj_t *scene, unsigned scene_count,
img_t *img_out)
{
const uint32_t w = img_out->w = camera->img_width;
const uint32_t h = img_out->h = camera->img_height;
img_out->w = camera->img_width;
img_out->h = camera->img_height;
for (unsigned y = 0; y < h; ++y) {
fprintf(stderr, "\r[%3d/%3d]", y, h);
fflush(stderr);
const unsigned rows_per_thread = img_out->h / NTHREADS;
const unsigned rem_rows = img_out->h % NTHREADS;
const vec3_t row
= vec3_add(camera->pix_origin, vec3_scale(camera->y_step, y));
for (unsigned x = 0; x < w; ++x) {
const vec3_t pix = vec3_add(row, vec3_scale(camera->x_step, x));
const ray_t ray = {
.orig = camera->pos,
.dir = vec3_unit(pix),
};
thrd_t threads[NTHREADS];
work_slice_t slices[NTHREADS];
for (unsigned i = 0; i < NTHREADS; ++i) {
slices[i].camera = camera;
slices[i].scene = scene;
slices[i].scene_count = scene_count;
slices[i].pixels = img_out->pix;
slices[i].rng = rng_init(i);
vec3_t colour = black;
for (unsigned i = 0; i < NSAMPLES; ++i) {
const double weight = 1.0 / NSAMPLES;
const vec3_t sample = trace(ray, scene, scene_count);
colour = vec3_add(colour, vec3_scale(sample, weight));
slices[i].start_y = i * rows_per_thread;
slices[i].row_count = rows_per_thread;
if (rem_rows != 0 && i == NTHREADS - 1)
slices[i].row_count += rem_rows;
thrd_create(threads + i, render_thread, slices + i);
}
setpix(colour, img_out->pix + (w * y + x));
}
}
fprintf(stderr, "\r[%3d/%3d]\n", h, h);
for (unsigned i = 0; i < NTHREADS; ++i)
thrd_join(threads[i], 0);
}

View File

@@ -1,23 +1,31 @@
#include "rng.h"
#include <limits.h>
#include <stdlib.h>
#include <sys/time.h>
void rng_init()
static uint32_t next(rng_t *rng)
{
uint32_t x = rng->state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return rng->state = x;
}
rng_t rng_init(unsigned seed)
{
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
gettimeofday(&tv, nullptr);
return (rng_t) { .state = tv.tv_usec + seed };
}
double rng_double()
double rng_double(rng_t *rng)
{
return 2.0 * (double)rand() / (double)INT_MAX - 1.0;
return 2.0 * (double)next(rng) / (double)UINT32_MAX - 1.0;
}
vec3_t rng_vec3()
vec3_t rng_vec3(rng_t *rng)
{
const vec3_t v = { rng_double(), rng_double(), rng_double() };
const vec3_t v = { rng_double(rng), rng_double(rng), rng_double(rng) };
return vec3_unit(v);
}