Add low pass filter
This commit is contained in:
+338
-181
@@ -39,6 +39,13 @@ typedef struct {
|
|||||||
int column_i;
|
int column_i;
|
||||||
} WorkerConfig;
|
} WorkerConfig;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float depth;
|
||||||
|
Vector3 color;
|
||||||
|
Vector3 normal;
|
||||||
|
float roughness;
|
||||||
|
} PixelInfo;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
/// GLOBAL VARIABLES ///
|
/// GLOBAL VARIABLES ///
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -53,21 +60,12 @@ int num_columns;
|
|||||||
Scene scene;
|
Scene scene;
|
||||||
Cubemap skybox;
|
Cubemap skybox;
|
||||||
|
|
||||||
// Any time the accumulation buffer is reset or
|
int color_buffer_frames = 0;
|
||||||
// resized, this is incremented.
|
Vector3 *color_buffer;
|
||||||
_Atomic uint32_t accum_generation = 0;
|
float *depth_buffer;
|
||||||
|
Vector3 *normal_buffer;
|
||||||
// This is the "accumulation buffer". Workers evaluate
|
Vector3 *filter_buffer;
|
||||||
// pixel colors in parallel and sum their results in here.
|
float *roughness_buffer;
|
||||||
// When the main thread needs to draw a new frame it takes
|
|
||||||
// these values and divides them by the frame count, averaging
|
|
||||||
// the results of multiple frames.
|
|
||||||
Vector3 *accum = NULL;
|
|
||||||
|
|
||||||
// This is the "frame buffer". It's only accessed by the
|
|
||||||
// main buffer to store the averaged values of the accumulation
|
|
||||||
// buffer before sending them to the GPU.
|
|
||||||
Vector3 *frame = NULL;
|
|
||||||
|
|
||||||
// Size of the accumulation and frame buffers
|
// Size of the accumulation and frame buffers
|
||||||
int frame_w = 0;
|
int frame_w = 0;
|
||||||
@@ -76,21 +74,20 @@ int frame_h = 0;
|
|||||||
// This guards the critical section around the accumulation buffer.
|
// This guards the critical section around the accumulation buffer.
|
||||||
os_mutex_t frame_mutex;
|
os_mutex_t frame_mutex;
|
||||||
|
|
||||||
// One condition variable per column. Any time new information
|
int completed = 0;
|
||||||
// is added to the accumulation buffer the condition of the
|
os_condvar_t start_work;
|
||||||
// associated column is signaled
|
os_condvar_t completed_work;
|
||||||
os_condvar_t accum_conds[MAX_COLUMNS];
|
|
||||||
|
|
||||||
// Counters that indicate how much information each column
|
|
||||||
// is storing. An integer value of N means N full frames have
|
|
||||||
// been accumulated. Lower resolution frames contribute lower
|
|
||||||
// values (half resolution weighs 0.25).
|
|
||||||
float accum_counts[MAX_COLUMNS];
|
|
||||||
|
|
||||||
uint64_t loop_cycles = 0;
|
uint64_t loop_cycles = 0;
|
||||||
uint64_t loop_count = 0;
|
uint64_t loop_count = 0;
|
||||||
uint64_t frame_cycles = 0;
|
|
||||||
uint64_t frame_count = 0;
|
uint64_t global_frame_index = 0;
|
||||||
|
|
||||||
|
uint64_t global_cycle_pixel_sum = 0;
|
||||||
|
uint64_t global_pixel_count = 0;
|
||||||
|
|
||||||
|
uint64_t smooth_index = 0;
|
||||||
|
uint64_t smooth_limit = 0;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
/// FUNCTION PROTOTYPES ///
|
/// FUNCTION PROTOTYPES ///
|
||||||
@@ -103,9 +100,9 @@ bool quitting(void);
|
|||||||
void screenshot(void);
|
void screenshot(void);
|
||||||
void parse_arguments_or_exit(int argc, char **argv, int *num_columns, char **scene_file);
|
void parse_arguments_or_exit(int argc, char **argv, int *num_columns, char **scene_file);
|
||||||
|
|
||||||
Vector3 pixel(float x, float y, float aspect_ratio);
|
PixelInfo pixel(float x, float y, float aspect_ratio);
|
||||||
void update_frame(void);
|
void update_frame(void);
|
||||||
void invalidate_accumulation(void);
|
void invalidate_accumulation(void);
|
||||||
|
|
||||||
os_threadreturn worker(void *arg);
|
os_threadreturn worker(void *arg);
|
||||||
|
|
||||||
@@ -113,16 +110,16 @@ os_threadreturn worker(void *arg);
|
|||||||
/// IMPLEMENTATION ///
|
/// IMPLEMENTATION ///
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Resets the current frame and accumulation buffers and tells
|
|
||||||
// every worker to drop what they are doing and start again.
|
|
||||||
void invalidate_accumulation(void)
|
void invalidate_accumulation(void)
|
||||||
{
|
{
|
||||||
os_mutex_lock(&frame_mutex);
|
os_mutex_lock(&frame_mutex);
|
||||||
for (int i = 0; i < num_columns; i++)
|
memset(color_buffer, 0, sizeof(Vector3) * frame_w * frame_h);
|
||||||
accum_counts[i] = 0;
|
memset(depth_buffer, 0, sizeof(float) * frame_w * frame_h);
|
||||||
atomic_fetch_add(&accum_generation, 1);
|
memset(normal_buffer, 0, sizeof(Vector3) * frame_w * frame_h);
|
||||||
memset(accum, 0, sizeof(Vector3) * frame_w * frame_h);
|
memset(roughness_buffer, 0, sizeof(float) * frame_w * frame_h);
|
||||||
memset(frame, 0, sizeof(Vector3) * frame_w * frame_h);
|
color_buffer_frames = 0;
|
||||||
|
smooth_index = 0;
|
||||||
|
smooth_limit = 0;
|
||||||
os_mutex_unlock(&frame_mutex);
|
os_mutex_unlock(&frame_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,13 +140,8 @@ static uint64_t wyhash64(void) {
|
|||||||
return m2;
|
return m2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _Thread_local uint64_t local_pixel_cycles;
|
PixelInfo pixel_inner(Ray in_ray)
|
||||||
static _Thread_local uint64_t local_pixel_count;
|
|
||||||
|
|
||||||
Vector3 pixel_inner(Ray in_ray)
|
|
||||||
{
|
{
|
||||||
uint64_t start_time = __rdtsc();
|
|
||||||
|
|
||||||
// Find a light source. This is kind of lazy as we should
|
// Find a light source. This is kind of lazy as we should
|
||||||
// sample every light source in the scene.
|
// sample every light source in the scene.
|
||||||
float light_sample_weight = 0.05;
|
float light_sample_weight = 0.05;
|
||||||
@@ -178,6 +170,12 @@ Vector3 pixel_inner(Ray in_ray)
|
|||||||
// Maximum number of bounces of the ray
|
// Maximum number of bounces of the ray
|
||||||
int bounces = 5;
|
int bounces = 5;
|
||||||
|
|
||||||
|
PixelInfo info;
|
||||||
|
info.depth = 1000000;
|
||||||
|
info.color = (Vector3) {0, 0, 0};
|
||||||
|
info.normal = (Vector3) {-in_ray.direction.x, -in_ray.direction.y, -in_ray.direction.z};
|
||||||
|
info.roughness = 0;
|
||||||
|
|
||||||
for (int i = 0; i < bounces; i++) {
|
for (int i = 0; i < bounces; i++) {
|
||||||
|
|
||||||
// Find the next collision
|
// Find the next collision
|
||||||
@@ -197,6 +195,12 @@ Vector3 pixel_inner(Ray in_ray)
|
|||||||
|
|
||||||
Material material = scene.objects[hit.object].material;
|
Material material = scene.objects[hit.object].material;
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
info.depth = hit.distance;
|
||||||
|
info.normal = hit.normal;
|
||||||
|
info.roughness = material.roughness;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t rand_bucket_0 = wyhash64();
|
uint64_t rand_bucket_0 = wyhash64();
|
||||||
|
|
||||||
Vector3 v = in_ray.direction;
|
Vector3 v = in_ray.direction;
|
||||||
@@ -332,19 +336,11 @@ Vector3 pixel_inner(Ray in_ray)
|
|||||||
in_ray = out_ray;
|
in_ray = out_ray;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
info.color = result;
|
||||||
// Saturate the result so it's a valid color
|
return info;
|
||||||
result.x = clamp(result.x, 0, 1);
|
|
||||||
result.y = clamp(result.y, 0, 1);
|
|
||||||
result.z = clamp(result.z, 0, 1);
|
|
||||||
*/
|
|
||||||
local_pixel_cycles += __rdtsc() - start_time;
|
|
||||||
local_pixel_count++;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 pixel(float x, float y, float aspect_ratio)
|
PixelInfo pixel(float x, float y, float aspect_ratio)
|
||||||
{
|
{
|
||||||
assert(!isnan(aspect_ratio));
|
assert(!isnan(aspect_ratio));
|
||||||
|
|
||||||
@@ -356,11 +352,11 @@ Vector3 pixel(float x, float y, float aspect_ratio)
|
|||||||
|
|
||||||
os_threadreturn worker(void *arg)
|
os_threadreturn worker(void *arg)
|
||||||
{
|
{
|
||||||
// How many information is contained in the column buffer
|
|
||||||
float column_data_weight = 0;
|
|
||||||
|
|
||||||
// The actual pixels
|
// The actual pixels
|
||||||
Vector3 *column_data = NULL;
|
Vector3 *local_color_buffer = NULL;
|
||||||
|
float *local_depth_buffer = NULL;
|
||||||
|
Vector3 *local_normal_buffer = NULL;
|
||||||
|
float *local_roughness_buffer = NULL;
|
||||||
|
|
||||||
// The screen is divided in "num_columns" columns
|
// The screen is divided in "num_columns" columns
|
||||||
int column_i = (int) arg;
|
int column_i = (int) arg;
|
||||||
@@ -372,40 +368,56 @@ os_threadreturn worker(void *arg)
|
|||||||
int cached_frame_w;
|
int cached_frame_w;
|
||||||
int cached_frame_h;
|
int cached_frame_h;
|
||||||
|
|
||||||
// Generation counter of the frame buffer when the worker
|
uint64_t local_frame_index = 0;
|
||||||
// started producing a new frame. If the camera moves in the
|
|
||||||
// or something else causing the frame buffer to be reset, this
|
|
||||||
// will let the worker know the information needs to be thrown
|
|
||||||
// away.
|
|
||||||
uint64_t cached_generation;
|
|
||||||
|
|
||||||
os_mutex_lock(&frame_mutex);
|
os_mutex_lock(&frame_mutex);
|
||||||
while (!quitting()) {
|
for (;;) {
|
||||||
|
|
||||||
|
while (local_frame_index == global_frame_index && !quitting())
|
||||||
|
os_condvar_wait(&start_work, &frame_mutex, -1);
|
||||||
|
local_frame_index = global_frame_index;
|
||||||
|
|
||||||
|
if (quitting()) {
|
||||||
|
completed++;
|
||||||
|
os_condvar_signal(&completed_work);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Cache data and check if we need to resize the column buffer
|
// Cache data and check if we need to resize the column buffer
|
||||||
bool resize = false;
|
bool resize = false;
|
||||||
if (column_data == NULL || cached_generation != atomic_load(&accum_generation))
|
if (local_color_buffer == NULL)
|
||||||
resize = true;
|
resize = true;
|
||||||
column_w = frame_w / num_columns;
|
column_w = frame_w / num_columns;
|
||||||
cached_frame_w = frame_w;
|
cached_frame_w = frame_w;
|
||||||
cached_frame_h = frame_h;
|
cached_frame_h = frame_h;
|
||||||
cached_generation = atomic_load(&accum_generation);
|
|
||||||
os_mutex_unlock(&frame_mutex);
|
os_mutex_unlock(&frame_mutex);
|
||||||
|
|
||||||
// We need to resize
|
// We need to resize
|
||||||
if (resize) {
|
if (resize) {
|
||||||
free(column_data);
|
|
||||||
column_data = malloc(sizeof(Vector3) * column_w * cached_frame_h);
|
free(local_color_buffer);
|
||||||
if (!column_data) abort();
|
local_color_buffer = malloc(sizeof(Vector3) * column_w * cached_frame_h);
|
||||||
|
if (!local_color_buffer) abort();
|
||||||
|
|
||||||
|
free(local_depth_buffer);
|
||||||
|
local_depth_buffer = malloc(sizeof(float) * column_w * cached_frame_h);
|
||||||
|
if (!local_depth_buffer) abort();
|
||||||
|
|
||||||
|
free(local_normal_buffer);
|
||||||
|
local_normal_buffer = malloc(sizeof(Vector3) * column_w * cached_frame_h);
|
||||||
|
if (!local_normal_buffer) abort();
|
||||||
|
|
||||||
|
free(local_roughness_buffer);
|
||||||
|
local_roughness_buffer = malloc(sizeof(float) * column_w * cached_frame_h);
|
||||||
|
if (!local_roughness_buffer) abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
int column_x = column_w * column_i;
|
int column_x = column_w * column_i;
|
||||||
float aspect_ratio = (float) frame_w / frame_h;
|
float aspect_ratio = (float) frame_w / frame_h;
|
||||||
|
|
||||||
local_pixel_cycles = 0;
|
uint64_t local_cycle_pixel_sum = 0;
|
||||||
local_pixel_count = 0;
|
uint64_t local_pixel_count = 0;
|
||||||
|
|
||||||
uint64_t frame_start = __rdtsc();
|
|
||||||
|
|
||||||
// Iterate over each low resolution pixel
|
// Iterate over each low resolution pixel
|
||||||
for (int j = 0; j < frame_h; j++) {
|
for (int j = 0; j < frame_h; j++) {
|
||||||
@@ -416,85 +428,41 @@ os_threadreturn worker(void *arg)
|
|||||||
u = 1 - u;
|
u = 1 - u;
|
||||||
v = 1 - v;
|
v = 1 - v;
|
||||||
|
|
||||||
Vector3 color = pixel(u, v, aspect_ratio);
|
uint64_t start_time = __rdtsc();
|
||||||
|
PixelInfo info = pixel(u, v, aspect_ratio);
|
||||||
|
local_cycle_pixel_sum += __rdtsc() - start_time;
|
||||||
|
local_pixel_count++;
|
||||||
|
|
||||||
column_data[j * column_w + i] = color;
|
local_color_buffer[j * column_w + i] = info.color;
|
||||||
|
local_depth_buffer[j * column_w + i] = info.depth;
|
||||||
|
local_normal_buffer[j * column_w + i] = info.normal;
|
||||||
|
local_roughness_buffer[j * column_w + i] = info.roughness;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cached_generation != atomic_load(&accum_generation)) break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t frame_delta = __rdtsc() - frame_start;
|
|
||||||
/*
|
|
||||||
for (int j = 1; j < frame_h-2; j++)
|
|
||||||
for (int i = 1; i < column_w-2; i++) {
|
|
||||||
|
|
||||||
data[j * column_w + i].x
|
|
||||||
= data[(j - 1) * column_w + (i - 1)].x
|
|
||||||
+ data[(j - 1) * column_w + (i + 0)].x
|
|
||||||
+ data[(j - 1) * column_w + (i + 1)].x
|
|
||||||
+ data[(j + 0) * column_w + (i - 1)].x
|
|
||||||
+ data[(j + 0) * column_w + (i + 0)].x
|
|
||||||
+ data[(j + 0) * column_w + (i + 1)].x
|
|
||||||
+ data[(j + 1) * column_w + (i - 1)].x
|
|
||||||
+ data[(j + 1) * column_w + (i + 0)].x
|
|
||||||
+ data[(j + 1) * column_w + (i + 1)].x;
|
|
||||||
data[j * column_w + i].x /= 9;
|
|
||||||
|
|
||||||
data[j * column_w + i].y
|
|
||||||
= data[(j - 1) * column_w + (i - 1)].y
|
|
||||||
+ data[(j - 1) * column_w + (i + 0)].y
|
|
||||||
+ data[(j - 1) * column_w + (i + 1)].y
|
|
||||||
+ data[(j + 0) * column_w + (i - 1)].y
|
|
||||||
+ data[(j + 0) * column_w + (i + 0)].y
|
|
||||||
+ data[(j + 0) * column_w + (i + 1)].y
|
|
||||||
+ data[(j + 1) * column_w + (i - 1)].y
|
|
||||||
+ data[(j + 1) * column_w + (i + 0)].y
|
|
||||||
+ data[(j + 1) * column_w + (i + 1)].y;
|
|
||||||
data[j * column_w + i].y /= 9;
|
|
||||||
|
|
||||||
data[j * column_w + i].z
|
|
||||||
= data[(j - 1) * column_w + (i - 1)].z
|
|
||||||
+ data[(j - 1) * column_w + (i + 0)].z
|
|
||||||
+ data[(j - 1) * column_w + (i + 1)].z
|
|
||||||
+ data[(j + 0) * column_w + (i - 1)].z
|
|
||||||
+ data[(j + 0) * column_w + (i + 0)].z
|
|
||||||
+ data[(j + 0) * column_w + (i + 1)].z
|
|
||||||
+ data[(j + 1) * column_w + (i - 1)].z
|
|
||||||
+ data[(j + 1) * column_w + (i + 0)].z
|
|
||||||
+ data[(j + 1) * column_w + (i + 1)].z;
|
|
||||||
data[j * column_w + i].z /= 9;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// Now we try publishing the changes
|
// Now we try publishing the changes
|
||||||
os_mutex_lock(&frame_mutex);
|
os_mutex_lock(&frame_mutex);
|
||||||
|
|
||||||
loop_cycles += local_pixel_cycles;
|
global_cycle_pixel_sum += local_cycle_pixel_sum;
|
||||||
loop_count += local_pixel_count;
|
global_pixel_count += local_pixel_count;
|
||||||
|
|
||||||
frame_cycles += frame_delta;
|
for (int j = 0; j < frame_h; j++)
|
||||||
frame_count++;
|
for (int i = 0; i < column_w; i++) {
|
||||||
|
|
||||||
if (cached_generation == atomic_load(&accum_generation)) {
|
int src_index = j * column_w + i;
|
||||||
// Frame didn't change its size while we were evaluating the column
|
int dst_index = j * frame_w + (i + column_x);
|
||||||
|
|
||||||
// This loop basically copies the pixel colors from the column buffer to
|
color_buffer[dst_index].x = color_buffer[dst_index].x * color_buffer_frames / (color_buffer_frames + 1) + local_color_buffer[src_index].x / (color_buffer_frames + 1);
|
||||||
// the frame buffer.
|
color_buffer[dst_index].y = color_buffer[dst_index].y * color_buffer_frames / (color_buffer_frames + 1) + local_color_buffer[src_index].y / (color_buffer_frames + 1);
|
||||||
for (int j = 0; j < frame_h; j++)
|
color_buffer[dst_index].z = color_buffer[dst_index].z * color_buffer_frames / (color_buffer_frames + 1) + local_color_buffer[src_index].z / (color_buffer_frames + 1);
|
||||||
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);
|
|
||||||
}
|
|
||||||
accum_counts[column_i]++;
|
|
||||||
|
|
||||||
// Let the main thread know there are new pixels
|
depth_buffer[dst_index] = local_depth_buffer[src_index];
|
||||||
os_condvar_signal(&accum_conds[column_i]);
|
normal_buffer[dst_index] = local_normal_buffer[src_index];
|
||||||
|
roughness_buffer[dst_index] = local_roughness_buffer[src_index];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
completed++;
|
||||||
|
os_condvar_signal(&completed_work);
|
||||||
}
|
}
|
||||||
os_mutex_unlock(&frame_mutex);
|
os_mutex_unlock(&frame_mutex);
|
||||||
}
|
}
|
||||||
@@ -504,28 +472,46 @@ void realloc_frame_buffer(void)
|
|||||||
frame_w = get_screen_w();
|
frame_w = get_screen_w();
|
||||||
frame_h = get_screen_h();
|
frame_h = get_screen_h();
|
||||||
|
|
||||||
if (frame) free(frame);
|
free(color_buffer);
|
||||||
if (accum) free(accum);
|
color_buffer = malloc(sizeof(Vector3) * frame_w * frame_h);
|
||||||
|
if (!color_buffer) {
|
||||||
frame = malloc(sizeof(Vector3) * frame_w * frame_h);
|
|
||||||
if (!frame) {
|
|
||||||
printf("OUT OF MEMORY\n");
|
printf("OUT OF MEMORY\n");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
accum = malloc(sizeof(Vector3) * frame_w * frame_h);
|
free(depth_buffer);
|
||||||
if (!accum) {
|
depth_buffer = malloc(sizeof(float) * frame_w * frame_h);
|
||||||
|
if (!depth_buffer) {
|
||||||
printf("OUT OF MEMORY\n");
|
printf("OUT OF MEMORY\n");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < num_columns; i++)
|
free(normal_buffer);
|
||||||
accum_counts[i] = 0;
|
normal_buffer = malloc(sizeof(Vector3) * frame_w * frame_h);
|
||||||
|
if (!normal_buffer) {
|
||||||
|
printf("OUT OF MEMORY\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
memset(accum, 0, sizeof(Vector3) * frame_w * frame_h);
|
free(roughness_buffer);
|
||||||
memset(frame, 0, sizeof(Vector3) * frame_w * frame_h);
|
roughness_buffer = malloc(sizeof(float) * frame_w * frame_h);
|
||||||
|
if (!roughness_buffer) {
|
||||||
|
printf("OUT OF MEMORY\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
free(filter_buffer);
|
||||||
|
filter_buffer = malloc(sizeof(Vector3) * frame_w * frame_h);
|
||||||
|
if (!filter_buffer) {
|
||||||
|
printf("OUT OF MEMORY\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(color_buffer, 0, sizeof(Vector3) * frame_w * frame_h);
|
||||||
|
memset(depth_buffer, 0, sizeof(float) * frame_w * frame_h);
|
||||||
|
memset(normal_buffer, 0, sizeof(Vector3) * frame_w * frame_h);
|
||||||
|
memset(roughness_buffer, 0, sizeof(float) * frame_w * frame_h);
|
||||||
|
|
||||||
atomic_fetch_add(&accum_generation, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool frame_buffer_size_doesnt_match_window(void)
|
bool frame_buffer_size_doesnt_match_window(void)
|
||||||
@@ -533,8 +519,152 @@ bool frame_buffer_size_doesnt_match_window(void)
|
|||||||
return frame_w != get_screen_w() || frame_h != get_screen_h();
|
return frame_w != get_screen_w() || frame_h != get_screen_h();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int compare_vector_luminosity(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
Vector3 u = *(Vector3*) a;
|
||||||
|
Vector3 v = *(Vector3*) b;
|
||||||
|
float x = avgv(u);
|
||||||
|
float y = avgv(v);
|
||||||
|
if (x < y)
|
||||||
|
return 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void median_filter()
|
||||||
|
{
|
||||||
|
for (int j = 0; j < frame_h; j++)
|
||||||
|
for (int i = 0; i < frame_w; i++) {
|
||||||
|
|
||||||
|
int center_pixel_location = j * frame_w + i;
|
||||||
|
|
||||||
|
Vector3 central_color = color_buffer[center_pixel_location];
|
||||||
|
bool center_roughness = roughness_buffer[center_pixel_location];
|
||||||
|
/*
|
||||||
|
if (center_roughness < 0.8) {
|
||||||
|
filter_buffer[center_pixel_location] = color_buffer[center_pixel_location];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#define WINDOW_SIZE 3
|
||||||
|
|
||||||
|
Vector3 samples[WINDOW_SIZE*WINDOW_SIZE];
|
||||||
|
int num_samples = 0;
|
||||||
|
|
||||||
|
for (int u = 0; u < WINDOW_SIZE; u++)
|
||||||
|
for (int v = 0; v < WINDOW_SIZE; v++) {
|
||||||
|
|
||||||
|
int g = j + u - WINDOW_SIZE / 2;
|
||||||
|
int t = i + v - WINDOW_SIZE / 2;
|
||||||
|
|
||||||
|
if (g < 0 || t < 0 || t >= frame_w || g >= frame_h)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int neighbor_pixel_location = g * frame_w + t;
|
||||||
|
|
||||||
|
if (absf(depth_buffer[center_pixel_location] - depth_buffer[neighbor_pixel_location]) > 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool neighbor_roughness = roughness_buffer[neighbor_pixel_location];
|
||||||
|
if (absf(neighbor_roughness - center_roughness) > 0.2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
samples[num_samples++] = color_buffer[neighbor_pixel_location];
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(samples, num_samples, sizeof(Vector3), compare_vector_luminosity);
|
||||||
|
|
||||||
|
filter_buffer[center_pixel_location] = samples[num_samples/2];
|
||||||
|
/*
|
||||||
|
float error = (absf(central_color.x - result.x) + absf(central_color.y - result.y) + absf(central_color.z - result.z)) / 3;
|
||||||
|
if (error > 0.05)
|
||||||
|
filter_buffer[center_pixel_location] = result;
|
||||||
|
else
|
||||||
|
filter_buffer[center_pixel_location] = color_buffer[center_pixel_location];
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
//memcpy(color_buffer, filter_buffer, sizeof(Vector3) * frame_w * frame_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void smooth_filter()
|
||||||
|
{
|
||||||
|
for (int j = 0; j < frame_h; j++)
|
||||||
|
for (int i = 0; i < frame_w; i++) {
|
||||||
|
Vector3 samples = {0, 0, 0};
|
||||||
|
int num_samples = 0;
|
||||||
|
float weight_sum = 0;
|
||||||
|
|
||||||
|
int center_pixel_location = j * frame_w + i;
|
||||||
|
|
||||||
|
Vector3 central_color = color_buffer[center_pixel_location];
|
||||||
|
bool center_roughness = roughness_buffer[center_pixel_location];
|
||||||
|
/*
|
||||||
|
if (center_roughness < 0.8) {
|
||||||
|
filter_buffer[center_pixel_location] = color_buffer[center_pixel_location];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#define WINDOW_SIZE 3
|
||||||
|
for (int u = 0; u < WINDOW_SIZE; u++)
|
||||||
|
for (int v = 0; v < WINDOW_SIZE; v++) {
|
||||||
|
|
||||||
|
int g = j + u - WINDOW_SIZE / 2;
|
||||||
|
int t = i + v - WINDOW_SIZE / 2;
|
||||||
|
|
||||||
|
if (g < 0 || t < 0 || t >= frame_w || g >= frame_h)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int neighbor_pixel_location = g * frame_w + t;
|
||||||
|
|
||||||
|
if (absf(depth_buffer[center_pixel_location] - depth_buffer[neighbor_pixel_location]) > 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool neighbor_roughness = roughness_buffer[neighbor_pixel_location];
|
||||||
|
if (absf(neighbor_roughness - center_roughness) > 0.2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vector3 center_normal = normal_buffer[center_pixel_location];
|
||||||
|
Vector3 neighbor_normal = normal_buffer[neighbor_pixel_location];
|
||||||
|
float simil = dotv(center_normal, neighbor_normal);
|
||||||
|
if (simil < 0.9)
|
||||||
|
continue;
|
||||||
|
float weight;
|
||||||
|
if (t == i && g == j)
|
||||||
|
weight = 1 - center_roughness;
|
||||||
|
else
|
||||||
|
weight = center_roughness;
|
||||||
|
weight += 0.1;
|
||||||
|
|
||||||
|
samples.x += weight * color_buffer[neighbor_pixel_location].x;
|
||||||
|
samples.y += weight * color_buffer[neighbor_pixel_location].y;
|
||||||
|
samples.z += weight * color_buffer[neighbor_pixel_location].z;
|
||||||
|
|
||||||
|
weight_sum += weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 result;
|
||||||
|
result.x = samples.x / weight_sum;
|
||||||
|
result.y = samples.y / weight_sum;
|
||||||
|
result.z = samples.z / weight_sum;
|
||||||
|
|
||||||
|
filter_buffer[center_pixel_location] = result;
|
||||||
|
/*
|
||||||
|
float error = (absf(central_color.x - result.x) + absf(central_color.y - result.y) + absf(central_color.z - result.z)) / 3;
|
||||||
|
if (error > 0.05)
|
||||||
|
filter_buffer[center_pixel_location] = result;
|
||||||
|
else
|
||||||
|
filter_buffer[center_pixel_location] = color_buffer[center_pixel_location];
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
memcpy(color_buffer, filter_buffer, sizeof(Vector3) * frame_w * frame_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t start_time_ns;
|
||||||
|
uint64_t start_time_cycles;
|
||||||
|
|
||||||
void update_frame(void)
|
void update_frame(void)
|
||||||
{
|
{
|
||||||
|
uint64_t frame_start = __rdtsc();
|
||||||
|
|
||||||
os_mutex_lock(&frame_mutex);
|
os_mutex_lock(&frame_mutex);
|
||||||
|
|
||||||
if (frame_buffer_size_doesnt_match_window())
|
if (frame_buffer_size_doesnt_match_window())
|
||||||
@@ -542,32 +672,54 @@ void update_frame(void)
|
|||||||
|
|
||||||
int column_w = frame_w / num_columns;
|
int column_w = frame_w / num_columns;
|
||||||
|
|
||||||
// Wait for the workers to produce a frame
|
completed = 0;
|
||||||
// (each worker produces a column)
|
global_frame_index++;
|
||||||
for (int i = 0; i < num_columns; i++) {
|
for (int i = 0; i < num_columns; i++)
|
||||||
while (accum_counts[i] < 0.0001)
|
os_condvar_signal(&start_work);
|
||||||
os_condvar_wait(&accum_conds[i], &frame_mutex, -1);
|
|
||||||
|
while (completed < num_columns)
|
||||||
|
os_condvar_wait(&completed_work, &frame_mutex, -1);
|
||||||
|
|
||||||
|
color_buffer_frames++;
|
||||||
|
|
||||||
|
bool smooth = false;
|
||||||
|
if (smooth_index == smooth_limit) {
|
||||||
|
smooth = true;
|
||||||
|
smooth_index = 0;
|
||||||
|
smooth_limit++;
|
||||||
|
printf("smoothing!\n");
|
||||||
|
}
|
||||||
|
smooth_index++;
|
||||||
|
|
||||||
|
if (smooth) {
|
||||||
|
smooth_filter();
|
||||||
|
for (int i = 0; i < frame_w * frame_h; i++) {
|
||||||
|
filter_buffer[i].x = clamp(filter_buffer[i].x, 0, 1);
|
||||||
|
filter_buffer[i].y = clamp(filter_buffer[i].y, 0, 1);
|
||||||
|
filter_buffer[i].z = clamp(filter_buffer[i].z, 0, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < frame_w * frame_h; i++) {
|
||||||
|
filter_buffer[i].x = clamp(color_buffer[i].x, 0, 1);
|
||||||
|
filter_buffer[i].y = clamp(color_buffer[i].y, 0, 1);
|
||||||
|
filter_buffer[i].z = clamp(color_buffer[i].z, 0, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy pixels from the accumulation buffer to the frame buffer
|
move_frame_to_the_gpu(frame_w, frame_h, filter_buffer);
|
||||||
for (int j = 0; j < frame_h; j++)
|
|
||||||
for (int i = 0; i < frame_w; i++) {
|
|
||||||
|
|
||||||
float u = (float) i / (frame_w - 1);
|
uint64_t cycles_per_pixel = global_cycle_pixel_sum / global_pixel_count;
|
||||||
float v = (float) j / (frame_h - 1);
|
|
||||||
u = 1 - u;
|
|
||||||
v = 1 - v;
|
|
||||||
|
|
||||||
int pixel_index = j * frame_w + i;
|
|
||||||
frame[pixel_index] = scalev(accum[pixel_index], 1.0f / accum_counts[i / column_w]);
|
|
||||||
}
|
|
||||||
|
|
||||||
move_frame_to_the_gpu(frame_w, frame_h, frame);
|
|
||||||
|
|
||||||
printf("pixel -> %lu cycles\n", loop_cycles / loop_count);
|
|
||||||
printf("frame -> %lu cycles\n", frame_cycles / frame_count);
|
|
||||||
|
|
||||||
os_mutex_unlock(&frame_mutex);
|
os_mutex_unlock(&frame_mutex);
|
||||||
|
|
||||||
|
uint64_t cycles_per_frame = __rdtsc() - frame_start;
|
||||||
|
|
||||||
|
uint64_t current_time_cycles = __rdtsc();
|
||||||
|
uint64_t current_time_ns = get_relative_time_ns();
|
||||||
|
double cy2ns = (double) (current_time_ns - start_time_ns) / (current_time_cycles - start_time_cycles);
|
||||||
|
|
||||||
|
printf("pixel -> %llu cycles (%f ns)\n", cycles_per_pixel, cycles_per_pixel * cy2ns);
|
||||||
|
printf("frame -> %llu cycles (%f ns)\n", cycles_per_frame, cycles_per_frame * cy2ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
@@ -625,6 +777,9 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
fprintf(stderr, "Workers started\n");
|
fprintf(stderr, "Workers started\n");
|
||||||
|
|
||||||
|
start_time_ns = get_relative_time_ns();
|
||||||
|
start_time_cycles = __rdtsc();
|
||||||
|
|
||||||
for (bool exit = false; !exit; ) {
|
for (bool exit = false; !exit; ) {
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -760,9 +915,9 @@ void screenshot(void)
|
|||||||
fprintf(stderr, "Couldn't take screenshot (out of memory)\n");
|
fprintf(stderr, "Couldn't take screenshot (out of memory)\n");
|
||||||
}
|
}
|
||||||
for (int i = 0; i < frame_w * frame_h; i++) {
|
for (int i = 0; i < frame_w * frame_h; i++) {
|
||||||
converted[i * 3 + 0] = frame[i].x * 255;
|
converted[i * 3 + 0] = filter_buffer[i].x * 255;
|
||||||
converted[i * 3 + 1] = frame[i].y * 255;
|
converted[i * 3 + 1] = filter_buffer[i].y * 255;
|
||||||
converted[i * 3 + 2] = frame[i].z * 255;
|
converted[i * 3 + 2] = filter_buffer[i].z * 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
stbi_flip_vertically_on_write(1);
|
stbi_flip_vertically_on_write(1);
|
||||||
@@ -794,8 +949,8 @@ void start_workers(void)
|
|||||||
|
|
||||||
os_mutex_create(&frame_mutex);
|
os_mutex_create(&frame_mutex);
|
||||||
|
|
||||||
for (int i = 0; i < num_columns; i++)
|
os_condvar_create(&start_work);
|
||||||
os_condvar_create(&accum_conds[i]);
|
os_condvar_create(&completed_work);
|
||||||
|
|
||||||
for (int i = 0; i < num_columns; i++)
|
for (int i = 0; i < num_columns; i++)
|
||||||
os_thread_create(&workers[i], (void*) i, worker);
|
os_thread_create(&workers[i], (void*) i, worker);
|
||||||
@@ -805,10 +960,12 @@ void stop_workers(void)
|
|||||||
{
|
{
|
||||||
os_mutex_lock(&frame_mutex);
|
os_mutex_lock(&frame_mutex);
|
||||||
workers_should_stop = true;
|
workers_should_stop = true;
|
||||||
|
for (int i = 0; i < num_columns; i++)
|
||||||
|
os_condvar_signal(&start_work);
|
||||||
os_mutex_unlock(&frame_mutex);
|
os_mutex_unlock(&frame_mutex);
|
||||||
for (int i = 0; i < num_columns; i++)
|
for (int i = 0; i < num_columns; i++)
|
||||||
os_thread_join(workers[i]);
|
os_thread_join(workers[i]);
|
||||||
|
|
||||||
for (int i = 0; i < num_columns; i++)
|
os_condvar_delete(&start_work);
|
||||||
os_condvar_delete(&accum_conds[i]);
|
os_condvar_delete(&completed_work);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user