From a85b5eae4b3b5a84624e3fc12a86ea5cfa632ebc Mon Sep 17 00:00:00 2001 From: Francesco Cozzuto Date: Tue, 8 Oct 2024 23:53:14 +0200 Subject: [PATCH] Workers now operate on columns of the image --- .gitignore | 3 +- Makefile | 2 +- src/clock.c | 13 +++ src/clock.h | 3 +- src/main.c | 247 +++++++++++++++++++++++++++++----------------------- 5 files changed, 157 insertions(+), 111 deletions(-) diff --git a/.gitignore b/.gitignore index c98ac3e..252a043 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.exe -*.exr \ No newline at end of file +*.exr +path_trace \ No newline at end of file diff --git a/Makefile b/Makefile index ebd8eca..7498488 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ else UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Linux) EXT = - CFLAGS = -O2 -ggdb -I3p/glad/include #-fsanitize=address,undefined + CFLAGS = -O2 -ggdb -I3p/glad/include -fsanitize=thread,undefined LDFLAGS = -lglfw -lm endif ifeq ($(UNAME_S),Darwin) diff --git a/src/clock.c b/src/clock.c index 772f5e2..50b6c61 100644 --- a/src/clock.c +++ b/src/clock.c @@ -25,6 +25,9 @@ * For more information, please refer to */ +#define _GNU_SOURCE +#define _POSIX_C_SOURCE 1999309L + #include "clock.h" #ifdef _WIN32 @@ -32,6 +35,7 @@ #include #else #include +#include #include #endif @@ -99,4 +103,13 @@ uint64_t get_relative_time_ns(void) return res; } #endif +} + +void sleep_ms(float ms) +{ +#ifdef _WIN32 + Sleep(ms); +#else + usleep(ms*1000); +#endif } \ No newline at end of file diff --git a/src/clock.h b/src/clock.h index 6acf231..7446146 100644 --- a/src/clock.h +++ b/src/clock.h @@ -28,4 +28,5 @@ #include uint64_t get_absolute_time_us(void); -uint64_t get_relative_time_ns(void); \ No newline at end of file +uint64_t get_relative_time_ns(void); +void sleep_ms(float ms); \ No newline at end of file diff --git a/src/main.c b/src/main.c index d4c4e2b..f4352de 100644 --- a/src/main.c +++ b/src/main.c @@ -3,14 +3,17 @@ #include #include #include +#include #include // FLT_MAX #include //#define GLFW_INCLUDE_NONE #include +#include #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" +#include "clock.h" #include "utils.h" #include "camera.h" #include "vector.h" @@ -600,7 +603,9 @@ Vector3 pixel(float x, float y, float aspect_ratio) Vector3 contrib = {1, 1, 1}; Vector3 result = {0, 0, 0}; - for (int i = 0; i < 5; i++) { + + int bounces = 2; + for (int i = 0; i < bounces; i++) { HitInfo hit = trace_ray(in_ray); if (hit.object == -1) { @@ -681,113 +686,137 @@ Vector3 pixel(float x, float y, float aspect_ratio) return result; } -uint32_t accum_generation = 0; +#define NUM_COLUMNS 16 + +bool stop_workers = false; +_Atomic uint32_t accum_generation = 0; Vector3 *accum = NULL; Vector3 *frame = NULL; int frame_w = 0; int frame_h = 0; unsigned int frame_texture; -float accum_count = 0; +float accum_counts[NUM_COLUMNS] = {0}; os_mutex_t frame_mutex; +os_condvar_t accum_conds[NUM_COLUMNS]; + +float render_to_column(Vector3 *data, int scale_, int column_w, int column_i, int frame_w, int frame_h, uint64_t cached_generation) +{ + float scale2inv = 1.0f / (scale_ * scale_); + + int column_x = column_w * column_i; + float aspect_ratio = (float) frame_w / frame_h; + + int lowres_frame_w = frame_w / scale_; + int lowres_frame_h = frame_h / scale_; + int lowres_column_w = column_w / scale_ + 1; + int lowres_column_x = column_x / scale_; + + for (int j = 0; j < lowres_frame_h; j++) { + for (int i = 0; i < lowres_column_w; i++) { + float u = (float) (lowres_column_x + i) / (lowres_frame_w - 1); + float v = (float) j / (lowres_frame_h - 1); + u = 1 - u; + v = 1 - v; + + int tile_w = scale_; + int tile_h = scale_; + if (tile_w > column_w - i * scale_) + tile_w = column_w - i * scale_; + + Vector3 color = pixel(u, v, aspect_ratio); + for (int g = 0; g < tile_h; g++) + for (int t = 0; t < tile_w; t++) { + int pixel_index = (j * scale_ + g) * column_w + (i * scale_ + t); + assert(pixel_index >= 0 && pixel_index < column_w * frame_h); + data[pixel_index] = scale(color, 1); + } + } + + if (cached_generation != atomic_load(&accum_generation)) + break; + } + + return scale2inv; +} os_threadreturn worker(void *arg) { - uint32_t local_accum_generation = 0; - Vector3 *local_accum = NULL; - float local_accum_count = 0; - int local_frame_w = 0; - int local_frame_h = 0; + float column_data_weight = 0; + Vector3 *column_data = NULL; + int column_i = (int) arg; + int column_w = 0; + int cached_frame_w; + int cached_frame_h; + uint64_t cached_generation; - int init_scale = (int) arg; - int scale = init_scale; - - for (;;) { + int init_scale = 16; + int scale_ = init_scale; + os_mutex_lock(&frame_mutex); + while (!stop_workers) { bool resize = false; - - os_mutex_lock(&frame_mutex); - if (accum != NULL && local_accum != NULL && local_accum_generation == accum_generation) { - for (int i = 0; i < frame_w * frame_h; i++) - accum[i] = combine(accum[i], local_accum[i], 1, 1); - accum_count += local_accum_count; - //if (scale > 1) - // scale >>= 1; - } else { - //scale = init_scale; - } - memset(local_accum, 0, sizeof(Vector3) * local_frame_w * local_frame_h); - if (local_frame_w != frame_w || local_frame_h != frame_h) + + if (column_data == NULL || cached_generation != atomic_load(&accum_generation)) resize = true; - local_accum_generation = accum_generation; - local_frame_w = frame_w; - local_frame_h = frame_h; - local_accum_count = 0; + column_w = frame_w / NUM_COLUMNS; + cached_frame_w = frame_w; + cached_frame_h = frame_h; + cached_generation = atomic_load(&accum_generation); os_mutex_unlock(&frame_mutex); if (resize) { - - if (local_accum) - free(local_accum); - - local_accum = malloc(sizeof(Vector3) * local_frame_w * local_frame_h); - if (!local_accum) { - printf("OUT OF MEMORY\n"); - abort(); - } - - memset(local_accum, 0, sizeof(Vector3) * local_frame_w * local_frame_h); + free(column_data); + column_data = malloc(sizeof(Vector3) * column_w * cached_frame_h); + if (!column_data) abort(); } - if (local_accum) { + column_data_weight += render_to_column(column_data, scale_, column_w, column_i, cached_frame_w, cached_frame_h, cached_generation); - float scale2inv = 1.0f / (scale * scale); + os_mutex_lock(&frame_mutex); - int lowres_frame_h = local_frame_h / scale; - int lowres_frame_w = local_frame_w / scale; - float aspect_ratio = (float) local_frame_w / local_frame_h; - - for (int j = 0; j < lowres_frame_h; j++) - for (int i = 0; i < lowres_frame_w; i++) { - float u = (float) i / (lowres_frame_w - 1); - float v = (float) j / (lowres_frame_h - 1); - u = 1 - u; - v = 1 - v; - - int tile_w = scale; - int tile_h = scale; - if (tile_w > local_frame_w - i * scale) tile_w = local_frame_w - i * scale; - if (tile_h > local_frame_h - j * scale) tile_h = local_frame_h - j * scale; - - Vector3 color = pixel(u, v, aspect_ratio); - for (int g = 0; g < tile_h; g++) - for (int t = 0; t < tile_w; t++) { - int real_i = i * scale + t; - int real_j = j * scale + g; - int pixel_index = real_j * frame_w + real_i; - local_accum[pixel_index] = combine(local_accum[pixel_index], color, 1, scale2inv); - } + // Publish changes + if (cached_generation == atomic_load(&accum_generation)) { + for (int j = 0; j < frame_h; j++) + for (int i = 0; i < column_w; i++) { + int column_x = column_w * column_i; + int src_index = j * column_w + i; + int dst_index = j * frame_w + (i + column_x); + assert(src_index >= 0 && src_index < column_w * cached_frame_h); + assert(dst_index >= 0 && dst_index < cached_frame_w * cached_frame_h); + accum[dst_index] = combine(accum[dst_index], column_data[src_index], 1, 1.0f / (scale_ * scale_)); } - - local_accum_count += scale2inv; + os_condvar_signal(&accum_conds[column_i]); + accum_counts[column_i] += column_data_weight; + if (scale_ > 1) + scale_ >>= 1; + } else { + scale_ = init_scale; } + + column_data_weight = 0; } + os_mutex_unlock(&frame_mutex); } void invalidate_accumulation(void) { os_mutex_lock(&frame_mutex); - accum_count = 0; - accum_generation++; + for (int i = 0; i < NUM_COLUMNS; i++) + accum_counts[i] = 0; + atomic_fetch_add(&accum_generation, 1); + memset(accum, 0, sizeof(Vector3) * frame_w * frame_h); + memset(frame, 0, sizeof(Vector3) * frame_w * frame_h); os_mutex_unlock(&frame_mutex); } -void update_frame_texture(float s) +void update_frame_texture(void) { os_mutex_lock(&frame_mutex); - if (frame_w != s * screen_w || frame_h != s * screen_h) { - frame_w = s * screen_w; - frame_h = s * screen_h; + if (frame_w != screen_w || frame_h != screen_h) { + + frame_w = screen_w; + frame_h = screen_h; if (frame) free(frame); if (accum) free(accum); @@ -798,33 +827,20 @@ void update_frame_texture(float s) accum = malloc(sizeof(Vector3) * frame_w * frame_h); if (!accum) { printf("OUT OF MEMORY\n"); abort(); } - accum_count = 0; + for (int i = 0; i < NUM_COLUMNS; i++) + accum_counts[i] = 0; + + memset(accum, 0, sizeof(Vector3) * frame_w * frame_h); + memset(frame, 0, sizeof(Vector3) * frame_w * frame_h); + + atomic_fetch_add(&accum_generation, 1); } - if (accum_count == 0) { + int column_w = frame_w / NUM_COLUMNS; - int scale_ = 16; - float scale2inv = 1.0f / (scale_ * scale_); - - int fake_frame_w = frame_w / scale_; - int fake_frame_h = frame_h / scale_; - float aspect_ratio = (float) fake_frame_w/fake_frame_h; - - for (int j = 0; j < fake_frame_h; j++) - for (int i = 0; i < fake_frame_w; i++) { - float u = (float) i / (fake_frame_w - 1); - float v = (float) j / (fake_frame_h - 1); - u = 1 - u; - v = 1 - v; - Vector3 color = pixel(u, v, aspect_ratio); - for (int g = 0; g < scale_; g++) - for (int t = 0; t < scale_; t++) { - int pixel_index = (j * scale_ + g) * frame_w + (i * scale_ + t); - accum[pixel_index] = scale(color, scale2inv); - } - } - - accum_count += scale2inv; + for (int i = 0; i < NUM_COLUMNS; i++) { + while (accum_counts[i] < 0.0001) + os_condvar_wait(&accum_conds[i], &frame_mutex, -1); } for (int j = 0; j < frame_h; j++) @@ -836,7 +852,7 @@ void update_frame_texture(float s) v = 1 - v; int pixel_index = j * frame_w + i; - frame[pixel_index] = scale(accum[pixel_index], 1.0f / accum_count); + frame[pixel_index] = scale(accum[pixel_index], 1.0f / accum_counts[i / column_w]); } glBindTexture(GL_TEXTURE_2D, frame_texture); @@ -1132,15 +1148,12 @@ int main(void) os_mutex_create(&frame_mutex); os_thread workers[16]; - int num_workers = 0; - for (int i = 0; i < 16; i++) { - int init_scale = 1 << i; - if (init_scale > 16) - init_scale = 1; - os_thread_create(&workers[i], (void*) init_scale, worker); - num_workers++; - } + for (int i = 0; i < NUM_COLUMNS; i++) + os_condvar_create(&accum_conds[i]); + + for (int i = 0; i < NUM_COLUMNS; i++) + os_thread_create(&workers[i], (void*) i, worker); unsigned int screen_program = compile_shader("assets/screen.vs", "assets/screen.fs"); if (!screen_program) { printf("Couldn't compile program\n"); return -1; } @@ -1185,6 +1198,8 @@ int main(void) while (!glfwWindowShouldClose(window)) { + double startTime = glfwGetTime(); + glfwGetWindowSize(window, &screen_w, &screen_h); float speed = 0.5; @@ -1195,7 +1210,7 @@ int main(void) Vector3 clear_color = {1, 1, 1}; - update_frame_texture(1); + update_frame_texture(); glViewport(0, 0, screen_w, screen_h); glClearColor(clear_color.x, clear_color.y, clear_color.z, 1.0f); @@ -1211,8 +1226,24 @@ int main(void) glfwSwapBuffers(window); glfwPollEvents(); + + double fps = 30; + double elapsedTime = glfwGetTime() - startTime; + double remaining = 1.0f/fps - elapsedTime; + printf("remain = %f ms\n", remaining * 1000); + if (remaining > 0) + sleep_ms(remaining * 1000); } + os_mutex_lock(&frame_mutex); + stop_workers = true; + os_mutex_unlock(&frame_mutex); + for (int i = 0; i < NUM_COLUMNS; i++) + os_thread_join(workers[i]); + + for (int i = 0; i < NUM_COLUMNS; i++) + os_condvar_delete(&accum_conds[i]); + free_cubemap(&skybox); glfwDestroyWindow(window); glfwTerminate();