Workers now operate on columns of the image
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
*.exe
|
*.exe
|
||||||
*.exr
|
*.exr
|
||||||
|
path_trace
|
||||||
@@ -7,7 +7,7 @@ else
|
|||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
ifeq ($(UNAME_S),Linux)
|
ifeq ($(UNAME_S),Linux)
|
||||||
EXT =
|
EXT =
|
||||||
CFLAGS = -O2 -ggdb -I3p/glad/include #-fsanitize=address,undefined
|
CFLAGS = -O2 -ggdb -I3p/glad/include -fsanitize=thread,undefined
|
||||||
LDFLAGS = -lglfw -lm
|
LDFLAGS = -lglfw -lm
|
||||||
endif
|
endif
|
||||||
ifeq ($(UNAME_S),Darwin)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
|||||||
+13
@@ -25,6 +25,9 @@
|
|||||||
* For more information, please refer to <http://unlicense.org/>
|
* For more information, please refer to <http://unlicense.org/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define _POSIX_C_SOURCE 1999309L
|
||||||
|
|
||||||
#include "clock.h"
|
#include "clock.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -32,6 +35,7 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#else
|
#else
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -100,3 +104,12 @@ uint64_t get_relative_time_ns(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sleep_ms(float ms)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
Sleep(ms);
|
||||||
|
#else
|
||||||
|
usleep(ms*1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -29,3 +29,4 @@
|
|||||||
|
|
||||||
uint64_t get_absolute_time_us(void);
|
uint64_t get_absolute_time_us(void);
|
||||||
uint64_t get_relative_time_ns(void);
|
uint64_t get_relative_time_ns(void);
|
||||||
|
void sleep_ms(float ms);
|
||||||
+138
-107
@@ -3,14 +3,17 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
#include <float.h> // FLT_MAX
|
#include <float.h> // FLT_MAX
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
//#define GLFW_INCLUDE_NONE
|
//#define GLFW_INCLUDE_NONE
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <x86intrin.h>
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#include "clock.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
@@ -600,7 +603,9 @@ Vector3 pixel(float x, float y, float aspect_ratio)
|
|||||||
|
|
||||||
Vector3 contrib = {1, 1, 1};
|
Vector3 contrib = {1, 1, 1};
|
||||||
Vector3 result = {0, 0, 0};
|
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);
|
HitInfo hit = trace_ray(in_ray);
|
||||||
if (hit.object == -1) {
|
if (hit.object == -1) {
|
||||||
@@ -681,113 +686,137 @@ Vector3 pixel(float x, float y, float aspect_ratio)
|
|||||||
return result;
|
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 *accum = NULL;
|
||||||
Vector3 *frame = NULL;
|
Vector3 *frame = NULL;
|
||||||
int frame_w = 0;
|
int frame_w = 0;
|
||||||
int frame_h = 0;
|
int frame_h = 0;
|
||||||
unsigned int frame_texture;
|
unsigned int frame_texture;
|
||||||
float accum_count = 0;
|
float accum_counts[NUM_COLUMNS] = {0};
|
||||||
os_mutex_t frame_mutex;
|
os_mutex_t frame_mutex;
|
||||||
|
os_condvar_t accum_conds[NUM_COLUMNS];
|
||||||
|
|
||||||
os_threadreturn worker(void *arg)
|
float render_to_column(Vector3 *data, int scale_, int column_w, int column_i, int frame_w, int frame_h, uint64_t cached_generation)
|
||||||
{
|
{
|
||||||
uint32_t local_accum_generation = 0;
|
float scale2inv = 1.0f / (scale_ * scale_);
|
||||||
Vector3 *local_accum = NULL;
|
|
||||||
float local_accum_count = 0;
|
|
||||||
int local_frame_w = 0;
|
|
||||||
int local_frame_h = 0;
|
|
||||||
|
|
||||||
int init_scale = (int) arg;
|
int column_x = column_w * column_i;
|
||||||
int scale = init_scale;
|
float aspect_ratio = (float) frame_w / frame_h;
|
||||||
|
|
||||||
for (;;) {
|
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_;
|
||||||
|
|
||||||
bool resize = false;
|
for (int j = 0; j < lowres_frame_h; j++) {
|
||||||
|
for (int i = 0; i < lowres_column_w; i++) {
|
||||||
os_mutex_lock(&frame_mutex);
|
float u = (float) (lowres_column_x + i) / (lowres_frame_w - 1);
|
||||||
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)
|
|
||||||
resize = true;
|
|
||||||
local_accum_generation = accum_generation;
|
|
||||||
local_frame_w = frame_w;
|
|
||||||
local_frame_h = frame_h;
|
|
||||||
local_accum_count = 0;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (local_accum) {
|
|
||||||
|
|
||||||
float scale2inv = 1.0f / (scale * scale);
|
|
||||||
|
|
||||||
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);
|
float v = (float) j / (lowres_frame_h - 1);
|
||||||
u = 1 - u;
|
u = 1 - u;
|
||||||
v = 1 - v;
|
v = 1 - v;
|
||||||
|
|
||||||
int tile_w = scale;
|
int tile_w = scale_;
|
||||||
int tile_h = scale;
|
int tile_h = scale_;
|
||||||
if (tile_w > local_frame_w - i * scale) tile_w = local_frame_w - i * scale;
|
if (tile_w > column_w - i * scale_)
|
||||||
if (tile_h > local_frame_h - j * scale) tile_h = local_frame_h - j * scale;
|
tile_w = column_w - i * scale_;
|
||||||
|
|
||||||
Vector3 color = pixel(u, v, aspect_ratio);
|
Vector3 color = pixel(u, v, aspect_ratio);
|
||||||
for (int g = 0; g < tile_h; g++)
|
for (int g = 0; g < tile_h; g++)
|
||||||
for (int t = 0; t < tile_w; t++) {
|
for (int t = 0; t < tile_w; t++) {
|
||||||
int real_i = i * scale + t;
|
int pixel_index = (j * scale_ + g) * column_w + (i * scale_ + t);
|
||||||
int real_j = j * scale + g;
|
assert(pixel_index >= 0 && pixel_index < column_w * frame_h);
|
||||||
int pixel_index = real_j * frame_w + real_i;
|
data[pixel_index] = scale(color, 1);
|
||||||
local_accum[pixel_index] = combine(local_accum[pixel_index], color, 1, scale2inv);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
local_accum_count += scale2inv;
|
if (cached_generation != atomic_load(&accum_generation))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return scale2inv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os_threadreturn worker(void *arg)
|
||||||
|
{
|
||||||
|
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 = 16;
|
||||||
|
int scale_ = init_scale;
|
||||||
|
|
||||||
|
os_mutex_lock(&frame_mutex);
|
||||||
|
while (!stop_workers) {
|
||||||
|
bool resize = false;
|
||||||
|
|
||||||
|
if (column_data == NULL || cached_generation != atomic_load(&accum_generation))
|
||||||
|
resize = true;
|
||||||
|
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) {
|
||||||
|
free(column_data);
|
||||||
|
column_data = malloc(sizeof(Vector3) * column_w * cached_frame_h);
|
||||||
|
if (!column_data) abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
column_data_weight += render_to_column(column_data, scale_, column_w, column_i, cached_frame_w, cached_frame_h, cached_generation);
|
||||||
|
|
||||||
|
os_mutex_lock(&frame_mutex);
|
||||||
|
|
||||||
|
// 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_));
|
||||||
|
}
|
||||||
|
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)
|
void invalidate_accumulation(void)
|
||||||
{
|
{
|
||||||
os_mutex_lock(&frame_mutex);
|
os_mutex_lock(&frame_mutex);
|
||||||
accum_count = 0;
|
for (int i = 0; i < NUM_COLUMNS; i++)
|
||||||
accum_generation++;
|
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);
|
os_mutex_unlock(&frame_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_frame_texture(float s)
|
void update_frame_texture(void)
|
||||||
{
|
{
|
||||||
os_mutex_lock(&frame_mutex);
|
os_mutex_lock(&frame_mutex);
|
||||||
|
|
||||||
if (frame_w != s * screen_w || frame_h != s * screen_h) {
|
if (frame_w != screen_w || frame_h != screen_h) {
|
||||||
frame_w = s * screen_w;
|
|
||||||
frame_h = s * screen_h;
|
frame_w = screen_w;
|
||||||
|
frame_h = screen_h;
|
||||||
|
|
||||||
if (frame) free(frame);
|
if (frame) free(frame);
|
||||||
if (accum) free(accum);
|
if (accum) free(accum);
|
||||||
@@ -798,33 +827,20 @@ void update_frame_texture(float s)
|
|||||||
accum = malloc(sizeof(Vector3) * frame_w * frame_h);
|
accum = malloc(sizeof(Vector3) * frame_w * frame_h);
|
||||||
if (!accum) { printf("OUT OF MEMORY\n"); abort(); }
|
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;
|
for (int i = 0; i < NUM_COLUMNS; i++) {
|
||||||
float scale2inv = 1.0f / (scale_ * scale_);
|
while (accum_counts[i] < 0.0001)
|
||||||
|
os_condvar_wait(&accum_conds[i], &frame_mutex, -1);
|
||||||
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 j = 0; j < frame_h; j++)
|
for (int j = 0; j < frame_h; j++)
|
||||||
@@ -836,7 +852,7 @@ void update_frame_texture(float s)
|
|||||||
v = 1 - v;
|
v = 1 - v;
|
||||||
|
|
||||||
int pixel_index = j * frame_w + i;
|
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);
|
glBindTexture(GL_TEXTURE_2D, frame_texture);
|
||||||
@@ -1132,15 +1148,12 @@ int main(void)
|
|||||||
os_mutex_create(&frame_mutex);
|
os_mutex_create(&frame_mutex);
|
||||||
|
|
||||||
os_thread workers[16];
|
os_thread workers[16];
|
||||||
int num_workers = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < NUM_COLUMNS; i++)
|
||||||
int init_scale = 1 << i;
|
os_condvar_create(&accum_conds[i]);
|
||||||
if (init_scale > 16)
|
|
||||||
init_scale = 1;
|
for (int i = 0; i < NUM_COLUMNS; i++)
|
||||||
os_thread_create(&workers[i], (void*) init_scale, worker);
|
os_thread_create(&workers[i], (void*) i, worker);
|
||||||
num_workers++;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int screen_program = compile_shader("assets/screen.vs", "assets/screen.fs");
|
unsigned int screen_program = compile_shader("assets/screen.vs", "assets/screen.fs");
|
||||||
if (!screen_program) { printf("Couldn't compile program\n"); return -1; }
|
if (!screen_program) { printf("Couldn't compile program\n"); return -1; }
|
||||||
@@ -1185,6 +1198,8 @@ int main(void)
|
|||||||
|
|
||||||
while (!glfwWindowShouldClose(window)) {
|
while (!glfwWindowShouldClose(window)) {
|
||||||
|
|
||||||
|
double startTime = glfwGetTime();
|
||||||
|
|
||||||
glfwGetWindowSize(window, &screen_w, &screen_h);
|
glfwGetWindowSize(window, &screen_w, &screen_h);
|
||||||
|
|
||||||
float speed = 0.5;
|
float speed = 0.5;
|
||||||
@@ -1195,7 +1210,7 @@ int main(void)
|
|||||||
|
|
||||||
Vector3 clear_color = {1, 1, 1};
|
Vector3 clear_color = {1, 1, 1};
|
||||||
|
|
||||||
update_frame_texture(1);
|
update_frame_texture();
|
||||||
|
|
||||||
glViewport(0, 0, screen_w, screen_h);
|
glViewport(0, 0, screen_w, screen_h);
|
||||||
glClearColor(clear_color.x, clear_color.y, clear_color.z, 1.0f);
|
glClearColor(clear_color.x, clear_color.y, clear_color.z, 1.0f);
|
||||||
@@ -1211,8 +1226,24 @@ int main(void)
|
|||||||
|
|
||||||
glfwSwapBuffers(window);
|
glfwSwapBuffers(window);
|
||||||
glfwPollEvents();
|
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);
|
free_cubemap(&skybox);
|
||||||
glfwDestroyWindow(window);
|
glfwDestroyWindow(window);
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
|
|||||||
Reference in New Issue
Block a user