Parallelize
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
EXT = .exe
|
EXT = .exe
|
||||||
CFLAGS = -ggdb -I3p/glad/include -I3p/glfw-3.4.bin.WIN64/include -L3p/glfw-3.4.bin.WIN64/lib-mingw-w64
|
CFLAGS = -O2 -ggdb -I3p/glad/include -I3p/glfw-3.4.bin.WIN64/include -L3p/glfw-3.4.bin.WIN64/lib-mingw-w64
|
||||||
LDFLAGS = -lglfw3 -lopengl32 -lgdi32
|
LDFLAGS = -lglfw3 -lopengl32 -lgdi32
|
||||||
else
|
else
|
||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
ifeq ($(UNAME_S),Linux)
|
ifeq ($(UNAME_S),Linux)
|
||||||
EXT =
|
EXT =
|
||||||
CFLAGS = -ggdb -I3p/glad/include #-fsanitize=address,undefined
|
CFLAGS = -O2 -ggdb -I3p/glad/include #-fsanitize=address,undefined
|
||||||
LDFLAGS = -lglfw -lm
|
LDFLAGS = -lglfw -lm
|
||||||
endif
|
endif
|
||||||
ifeq ($(UNAME_S),Darwin)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
@@ -20,7 +20,7 @@ endif
|
|||||||
all: path_trace$(EXT)
|
all: path_trace$(EXT)
|
||||||
|
|
||||||
path_trace$(EXT): Makefile $(wildcard src/*.c src/*.h)
|
path_trace$(EXT): Makefile $(wildcard src/*.c src/*.h)
|
||||||
gcc -o $@ src/main.c src/utils.c src/camera.c src/mesh.c src/vector.c 3p/glad/src/glad.c -std=c11 $(CFLAGS) $(LDFLAGS)
|
gcc -o $@ src/main.c src/utils.c src/camera.c src/mesh.c src/vector.c src/thread.c src/sync.c src/clock.c src/profile.c 3p/glad/src/glad.c -std=c11 $(CFLAGS) $(LDFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm path_trace path_trace.exe
|
rm path_trace path_trace.exe
|
||||||
|
|||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* This is free and unencumbered software released into the public domain.
|
||||||
|
*
|
||||||
|
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
* distribute this software, either in source code form or as a compiled
|
||||||
|
* binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
* means.
|
||||||
|
*
|
||||||
|
* In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
* of this software dedicate any and all copyright interest in the
|
||||||
|
* software to the public domain. We make this dedication for the benefit
|
||||||
|
* of the public at large and to the detriment of our heirs and
|
||||||
|
* successors. We intend this dedication to be an overt act of
|
||||||
|
* relinquishment in perpetuity of all present and future rights to this
|
||||||
|
* software under copyright law.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* For more information, please refer to <http://unlicense.org/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "clock.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_MEAN_AND_LEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current absolute time in microsecods
|
||||||
|
* TODO: Specify since when the time is calculated
|
||||||
|
*/
|
||||||
|
uint64_t get_absolute_time_us(void)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
FILETIME filetime;
|
||||||
|
GetSystemTimePreciseAsFileTime(&filetime);
|
||||||
|
uint64_t time = (uint64_t) filetime.dwLowDateTime | ((uint64_t) filetime.dwHighDateTime << 32);
|
||||||
|
time /= 10;
|
||||||
|
return time;
|
||||||
|
#else
|
||||||
|
struct timespec buffer;
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &buffer))
|
||||||
|
abort();
|
||||||
|
uint64_t time = buffer.tv_sec * 1000000 + buffer.tv_nsec / 1000;
|
||||||
|
return time;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current time in nanoseconds since
|
||||||
|
* an unspecified point in time.
|
||||||
|
*/
|
||||||
|
uint64_t get_relative_time_ns(void)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
{
|
||||||
|
int64_t count;
|
||||||
|
int64_t freq;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
ok = QueryPerformanceCounter((LARGE_INTEGER*) &count);
|
||||||
|
if (!ok) abort();
|
||||||
|
|
||||||
|
ok = QueryPerformanceFrequency((LARGE_INTEGER*) &freq);
|
||||||
|
if (!ok) abort();
|
||||||
|
|
||||||
|
uint64_t res = 1000000000 * (double) count / freq;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
struct timespec time;
|
||||||
|
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &time))
|
||||||
|
abort();
|
||||||
|
|
||||||
|
uint64_t res;
|
||||||
|
|
||||||
|
uint64_t sec = time.tv_sec;
|
||||||
|
if (sec > UINT64_MAX / 1000000000)
|
||||||
|
abort();
|
||||||
|
res = sec * 1000000000;
|
||||||
|
|
||||||
|
uint64_t nsec = time.tv_nsec;
|
||||||
|
if (res > UINT64_MAX - nsec)
|
||||||
|
abort();
|
||||||
|
res += nsec;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* This is free and unencumbered software released into the public domain.
|
||||||
|
*
|
||||||
|
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
* distribute this software, either in source code form or as a compiled
|
||||||
|
* binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
* means.
|
||||||
|
*
|
||||||
|
* In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
* of this software dedicate any and all copyright interest in the
|
||||||
|
* software to the public domain. We make this dedication for the benefit
|
||||||
|
* of the public at large and to the detriment of our heirs and
|
||||||
|
* successors. We intend this dedication to be an overt act of
|
||||||
|
* relinquishment in perpetuity of all present and future rights to this
|
||||||
|
* software under copyright law.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* For more information, please refer to <http://unlicense.org/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint64_t get_absolute_time_us(void);
|
||||||
|
uint64_t get_relative_time_ns(void);
|
||||||
+159
-49
@@ -11,6 +11,8 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
#include "thread.h"
|
||||||
|
#include "sync.h"
|
||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -150,11 +152,11 @@ void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
|||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_accum(void);
|
void invalidate_accumulation(void);
|
||||||
|
|
||||||
void cursor_pos_callback(GLFWwindow *window, double x, double y)
|
void cursor_pos_callback(GLFWwindow *window, double x, double y)
|
||||||
{
|
{
|
||||||
reset_accum();
|
invalidate_accumulation();
|
||||||
rotate_camera(x, y);
|
rotate_camera(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,25 +338,25 @@ unsigned int pcg_hash(unsigned int input)
|
|||||||
return (word >> 22U) ^ word;
|
return (word >> 22U) ^ word;
|
||||||
}
|
}
|
||||||
|
|
||||||
float random_float(unsigned int *seed)
|
float random_float(void)
|
||||||
{
|
{
|
||||||
// *seed = pcg_hash(*seed);
|
// *seed = pcg_hash(*seed);
|
||||||
// return (float) *seed / UINT_MAX;
|
// return (float) *seed / UINT_MAX;
|
||||||
return (float) rand() / RAND_MAX;
|
return (float) rand() / RAND_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 random_vector(unsigned int seed)
|
Vector3 random_vector(void)
|
||||||
{
|
{
|
||||||
return (Vector3) {
|
return (Vector3) {
|
||||||
.x = random_float(&seed) * 2 - 1,
|
.x = random_float() * 2 - 1,
|
||||||
.y = random_float(&seed) * 2 - 1,
|
.y = random_float() * 2 - 1,
|
||||||
.z = random_float(&seed) * 2 - 1,
|
.z = random_float() * 2 - 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 random_direction(unsigned int seed)
|
Vector3 random_direction(void)
|
||||||
{
|
{
|
||||||
return normalize(random_vector(seed));
|
return normalize(random_vector());
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 reflect(Vector3 dir, Vector3 normal)
|
Vector3 reflect(Vector3 dir, Vector3 normal)
|
||||||
@@ -418,13 +420,16 @@ HitInfo trace_ray(Ray ray)
|
|||||||
|
|
||||||
Vector3 pixel(float x, float y)
|
Vector3 pixel(float x, float y)
|
||||||
{
|
{
|
||||||
Ray ray = ray_through_screen_at(x, y, (float) screen_w/screen_h);
|
Ray original_ray = ray_through_screen_at(x, y, (float) screen_w/screen_h);
|
||||||
|
|
||||||
Vector3 contrib = {1, 1, 1};
|
Vector3 contrib = {1, 1, 1};
|
||||||
Vector3 light = {0, 0, 0};
|
Vector3 light = {0, 0, 0};
|
||||||
|
|
||||||
int rays_per_pixel = 1;
|
int diffuse_rays = 1;
|
||||||
for (int j = 0; j < rays_per_pixel; j++) {
|
int specular_rays = 1;
|
||||||
|
|
||||||
|
Ray ray = original_ray;
|
||||||
|
for (int j = 0; j < diffuse_rays; j++) {
|
||||||
|
|
||||||
int bounces = 4;
|
int bounces = 4;
|
||||||
for (int i = 0; i < bounces; i++) {
|
for (int i = 0; i < bounces; i++) {
|
||||||
@@ -440,43 +445,129 @@ Vector3 pixel(float x, float y)
|
|||||||
Material material = objects[hit.object].material;
|
Material material = objects[hit.object].material;
|
||||||
contrib = mulv(contrib, material.albedo);
|
contrib = mulv(contrib, material.albedo);
|
||||||
light = combine(light, material.emission_color, 1, material.emission_power);
|
light = combine(light, material.emission_color, 1, material.emission_power);
|
||||||
#if 0
|
|
||||||
Vector3 reflect_dir = reflect(ray.direction, scale(hit.normal, -1));
|
|
||||||
Vector3 noise_dir = scale(random_direction(), 0.5);
|
|
||||||
if (dotv(noise_dir, reflect_dir) < 0)
|
|
||||||
noise_dir = scale(noise_dir, -1);
|
|
||||||
|
|
||||||
float roughness = objects[hit.object].material.roughness;
|
Vector3 new_dir = random_direction();
|
||||||
Vector3 new_dir = combine(noise_dir, reflect_dir, roughness, 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Vector3 new_dir = random_direction(i * 1000000 + x * 1000 + y);
|
|
||||||
/*
|
|
||||||
if (dotv(new_dir, hit.normal) < 0)
|
if (dotv(new_dir, hit.normal) < 0)
|
||||||
new_dir = scale(new_dir, -1);
|
new_dir = scale(new_dir, -1);
|
||||||
*/
|
|
||||||
ray = (Ray) { combine(hit.point, new_dir, 1, 0.001), new_dir };
|
ray = (Ray) { combine(hit.point, new_dir, 1, 0.001), new_dir };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
light = scale(light, 1.0f/rays_per_pixel);
|
|
||||||
|
contrib = (Vector3) {1, 1, 1};
|
||||||
|
ray = original_ray;
|
||||||
|
for (int j = 0; j < specular_rays; j++) {
|
||||||
|
|
||||||
|
int bounces = 4;
|
||||||
|
for (int i = 0; i < bounces; i++) {
|
||||||
|
|
||||||
|
HitInfo hit = trace_ray(ray);
|
||||||
|
if (hit.object == -1) {
|
||||||
|
Vector3 sky_color = {0.6, 0.7, 0.9};
|
||||||
|
//Vector3 sky_color = {0, 0, 0};
|
||||||
|
light = combine(light, mulv(sky_color, contrib), 1, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 reflect_dir = reflect(ray.direction, scale(hit.normal, -1));
|
||||||
|
|
||||||
|
float roughness = objects[hit.object].material.roughness;
|
||||||
|
Vector3 noise_dir = scale(random_direction(), roughness);
|
||||||
|
if (dotv(noise_dir, reflect_dir) < 0)
|
||||||
|
noise_dir = scale(noise_dir, -1);
|
||||||
|
|
||||||
|
Vector3 new_dir = combine(noise_dir, reflect_dir, roughness, 1 - roughness);
|
||||||
|
|
||||||
|
ray = (Ray) { combine(hit.point, new_dir, 1, 0.001), new_dir };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
light = scale(light, 1.0f/(specular_rays + diffuse_rays));
|
||||||
|
|
||||||
return light;
|
return light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
int accum_index = 1;
|
uint64_t accum_index = 1;
|
||||||
|
os_mutex_t frame_mutex;
|
||||||
|
|
||||||
void reset_accum(void)
|
os_threadreturn worker(void*)
|
||||||
{
|
{
|
||||||
|
uint32_t local_accum_generation = 0;
|
||||||
|
Vector3 *local_accum = NULL;
|
||||||
|
uint64_t local_accum_index = 1;
|
||||||
|
int local_frame_w = 0;
|
||||||
|
int local_frame_h = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
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_index += local_accum_index;
|
||||||
|
}
|
||||||
|
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_index = 1;
|
||||||
|
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) {
|
||||||
|
for (int j = 0; j < local_frame_h; j++)
|
||||||
|
for (int i = 0; i < local_frame_w; i++) {
|
||||||
|
float u = (float) i / (local_frame_w - 1);
|
||||||
|
float v = (float) j / (local_frame_h - 1);
|
||||||
|
u = 1 - u;
|
||||||
|
v = 1 - v;
|
||||||
|
|
||||||
|
Vector3 color = pixel(u, v);
|
||||||
|
|
||||||
|
int pixel_index = j * local_frame_w + i;
|
||||||
|
local_accum[pixel_index] = combine(local_accum[pixel_index], color, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
local_accum_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void invalidate_accumulation(void)
|
||||||
|
{
|
||||||
|
os_mutex_lock(&frame_mutex);
|
||||||
accum_index = 1;
|
accum_index = 1;
|
||||||
|
accum_generation++;
|
||||||
|
os_mutex_unlock(&frame_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_frame_texture(float s)
|
void update_frame_texture(float s)
|
||||||
{
|
{
|
||||||
|
os_mutex_lock(&frame_mutex);
|
||||||
|
|
||||||
if (frame_w != s * screen_w || frame_h != s * screen_h) {
|
if (frame_w != s * screen_w || frame_h != s * screen_h) {
|
||||||
frame_w = s * screen_w;
|
frame_w = s * screen_w;
|
||||||
frame_h = s * screen_h;
|
frame_h = s * screen_h;
|
||||||
@@ -493,8 +584,9 @@ void update_frame_texture(float s)
|
|||||||
accum_index = 1;
|
accum_index = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accum_index == 1)
|
if (accum_index == 1) {
|
||||||
memset(accum, 0, sizeof(Vector3) * frame_w * frame_h);
|
|
||||||
|
//memset(accum, 0, sizeof(Vector3) * frame_w * frame_h);
|
||||||
|
|
||||||
for (int j = 0; j < frame_h; j++)
|
for (int j = 0; j < frame_h; j++)
|
||||||
for (int i = 0; i < frame_w; i++) {
|
for (int i = 0; i < frame_w; i++) {
|
||||||
@@ -502,32 +594,50 @@ void update_frame_texture(float s)
|
|||||||
float v = (float) j / (frame_h - 1);
|
float v = (float) j / (frame_h - 1);
|
||||||
u = 1 - u;
|
u = 1 - u;
|
||||||
v = 1 - v;
|
v = 1 - v;
|
||||||
|
|
||||||
Vector3 color = pixel(u, v);
|
|
||||||
|
|
||||||
int pixel_index = j * frame_w + i;
|
int pixel_index = j * frame_w + i;
|
||||||
accum[pixel_index] = combine(accum[pixel_index], color, 1, 1);
|
accum[pixel_index] = pixel(u, v);
|
||||||
frame[pixel_index] = scale(accum[pixel_index], 1.0f / accum_index);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accum_index++;
|
for (int j = 0; j < frame_h; j++)
|
||||||
|
for (int i = 0; i < frame_w; i++) {
|
||||||
|
|
||||||
|
float u = (float) i / (frame_w - 1);
|
||||||
|
float v = (float) j / (frame_h - 1);
|
||||||
|
u = 1 - u;
|
||||||
|
v = 1 - v;
|
||||||
|
|
||||||
|
int pixel_index = j * frame_w + i;
|
||||||
|
frame[pixel_index] = scale(accum[pixel_index], 1.0f / accum_index);
|
||||||
|
}
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, frame_texture);
|
glBindTexture(GL_TEXTURE_2D, frame_texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, frame_w, frame_h, 0, GL_RGB, GL_FLOAT, frame);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, frame_w, frame_h, 0, GL_RGB, GL_FLOAT, frame);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
os_mutex_unlock(&frame_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
/*
|
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {1, 0, 0}}, (Vector3) {0, 0, 0}, (Vector3) {10, 5, 0.1}));
|
||||||
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {1, 0, 0}}, (Vector3) {0, 0, 0}, (Vector3) {10, 5, 0.1})),
|
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {1, 0, 0}}, (Vector3) {0, 0, 0}, (Vector3) {0.1, 5, 10}));
|
||||||
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {1, 0, 0}}, (Vector3) {0, 0, 0}, (Vector3) {0.1, 5, 10})),
|
|
||||||
*/
|
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {0.4, 0.3, 0.9}}, (Vector3) {0, -0.1, 0}, (Vector3) {10, 0.1, 10}));
|
||||||
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {0.4, 0.3, 0.9}}, (Vector3) {0, -0.1, 0}, (Vector3) {10, 0.1, 10})),
|
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {1, 0, 0}}, (Vector3) {7, 0, 8}, (Vector3) {1, 1, 1}));
|
||||||
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {1, 0, 0}}, (Vector3) {7, 0, 8}, (Vector3) {1, 1, 1})),
|
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {1, 0, 1}}, (Vector3) {6, 0, 7}, (Vector3) {1, 1, 1}));
|
||||||
add_object(cube((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=1, .albedo=(Vector3) {1, 0, 1}}, (Vector3) {6, 0, 7}, (Vector3) {1, 1, 1})),
|
add_object(sphere((Material) {.emission_color={1, 0.4, 0}, .emission_power=3, .metallic=0, .roughness=0, .albedo=(Vector3) {1, 0.4, 0}}, (Vector3) {3, 1, 3}, 1));
|
||||||
add_object(sphere((Material) {.emission_color={1, 0, 0}, .emission_power=0.3, .metallic=0, .roughness=0, .albedo=(Vector3) {1, 0, 0}}, (Vector3) {3, 1, 3}, 1)),
|
add_object(sphere((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=0, .albedo=(Vector3) {0, 1, 0}}, (Vector3) {5, 1, 3}, 1));
|
||||||
add_object(sphere((Material) {.emission_color={0}, .emission_power=0, .metallic=0, .roughness=0, .albedo=(Vector3) {0, 1, 0}}, (Vector3) {5, 1, 3}, 1)),
|
|
||||||
|
os_mutex_create(&frame_mutex);
|
||||||
|
|
||||||
|
os_thread workers[16];
|
||||||
|
int num_workers = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
os_thread_create(&workers[i], NULL, worker);
|
||||||
|
num_workers++;
|
||||||
|
}
|
||||||
|
|
||||||
glfwSetErrorCallback(error_callback);
|
glfwSetErrorCallback(error_callback);
|
||||||
|
|
||||||
@@ -604,14 +714,14 @@ int main(void)
|
|||||||
glfwGetWindowSize(window, &screen_w, &screen_h);
|
glfwGetWindowSize(window, &screen_w, &screen_h);
|
||||||
|
|
||||||
float speed = 0.5;
|
float speed = 0.5;
|
||||||
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { move_camera(UP, speed); accum_index = 1; }
|
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { move_camera(UP, speed); invalidate_accumulation(); }
|
||||||
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { move_camera(DOWN, speed); accum_index = 1; }
|
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { move_camera(DOWN, speed); invalidate_accumulation(); }
|
||||||
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { move_camera(LEFT, speed); accum_index = 1; }
|
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { move_camera(LEFT, speed); invalidate_accumulation(); }
|
||||||
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { move_camera(RIGHT, speed); accum_index = 1; }
|
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { move_camera(RIGHT, speed); invalidate_accumulation(); }
|
||||||
|
|
||||||
Vector3 clear_color = {1, 1, 1};
|
Vector3 clear_color = {1, 1, 1};
|
||||||
|
|
||||||
update_frame_texture(1);
|
update_frame_texture(0.4);
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "profile.h"
|
||||||
|
|
||||||
|
void human_readable_time_interval(uint64_t ns, char *dst, size_t max)
|
||||||
|
{
|
||||||
|
if (ns > 1000000000)
|
||||||
|
snprintf(dst, max, "%.1Lf s", (long double) ns / 1000000000);
|
||||||
|
else if (ns > 1000000)
|
||||||
|
snprintf(dst, max, "%.1Lf ms", (long double) ns / 1000000);
|
||||||
|
else if (ns > 1000)
|
||||||
|
snprintf(dst, max, "%.1Lf us", (long double) ns / 1000);
|
||||||
|
else
|
||||||
|
snprintf(dst, max, "%.1Lf ns", (long double) ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sort_callback(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const profile_t *p1 = a;
|
||||||
|
const profile_t *p2 = b;
|
||||||
|
uint64_t n1 = atomic_load(&p1->elapsed_cycles);
|
||||||
|
uint64_t n2 = atomic_load(&p2->elapsed_cycles);
|
||||||
|
if (n1 > n2) return +1;
|
||||||
|
if (n1 < n2) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_profile_results(profile_results_t res_list[], int num_results, long double ns_per_cycle)
|
||||||
|
{
|
||||||
|
int entry_count = 0;
|
||||||
|
for (int i = 0; i < num_results; i++)
|
||||||
|
entry_count += res_list[i].count;
|
||||||
|
|
||||||
|
profile_t entries_[128];
|
||||||
|
profile_t *entries = entries_;
|
||||||
|
|
||||||
|
if (entry_count >= (int) (sizeof(entries_)/sizeof(profile_t))) {
|
||||||
|
entries = malloc(entry_count * sizeof(profile_t));
|
||||||
|
if (entries == NULL) abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0, k = 0; i < num_results; i++)
|
||||||
|
for (int j = 0; j < res_list[i].count; j++)
|
||||||
|
entries[k++] = res_list[i].array[j];
|
||||||
|
|
||||||
|
qsort(entries, entry_count, sizeof(profile_t), sort_callback);
|
||||||
|
|
||||||
|
if (entry_count > 0)
|
||||||
|
fprintf(stderr, "| %-30s | %-10s | %-10s | %-10s |\n",
|
||||||
|
"Name", "Calls", "Total", "Latency");
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < entry_count; i++) {
|
||||||
|
if (!entries[i].name) continue;
|
||||||
|
int call_count = entries[i].call_count;
|
||||||
|
uint64_t elapsed_cycles = entries[i].elapsed_cycles;
|
||||||
|
|
||||||
|
long double total_ns = ns_per_cycle * elapsed_cycles;
|
||||||
|
long double latency_ns = total_ns / call_count;
|
||||||
|
|
||||||
|
char total[128];
|
||||||
|
human_readable_time_interval(total_ns, total, sizeof(total));
|
||||||
|
|
||||||
|
char latency[128];
|
||||||
|
human_readable_time_interval(latency_ns, latency, sizeof(latency));
|
||||||
|
|
||||||
|
fprintf(stderr, "| %-30s | %-10d | %-10s | %-10s |\n",
|
||||||
|
entries[i].name, call_count, total, latency);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef PROFILE_H
|
||||||
|
#define PROFILE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <x86intrin.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *_Atomic name;
|
||||||
|
_Atomic uint64_t elapsed_cycles;
|
||||||
|
_Atomic uint64_t call_count;
|
||||||
|
} profile_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
profile_t *array;
|
||||||
|
int count;
|
||||||
|
} profile_results_t;
|
||||||
|
|
||||||
|
#ifdef PROFILE
|
||||||
|
|
||||||
|
#define PROFILE_START \
|
||||||
|
profile_t *profile__ = &profile_table__[__COUNTER__]; \
|
||||||
|
profile__->name = __func__; \
|
||||||
|
uint64_t profile_start__ = __rdtsc();
|
||||||
|
|
||||||
|
#define PROFILE_END \
|
||||||
|
atomic_fetch_add(&profile__->elapsed_cycles, __rdtsc() - profile_start__); \
|
||||||
|
atomic_fetch_add(&profile__->call_count, 1);
|
||||||
|
|
||||||
|
#define PROFILE_GLOBAL_START \
|
||||||
|
static profile_t profile_table__[];
|
||||||
|
|
||||||
|
#define PROFILE_GLOBAL_END \
|
||||||
|
static profile_t profile_table__[__COUNTER__];
|
||||||
|
|
||||||
|
#define PROFILE_RESULTS (profile_results_t) {profile_table__, sizeof(profile_table__) / sizeof(profile_table__[0])}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define PROFILE_START
|
||||||
|
#define PROFILE_END
|
||||||
|
#define PROFILE_GLOBAL_START
|
||||||
|
#define PROFILE_GLOBAL_END
|
||||||
|
#define PROFILE_RESULTS (profile_results_t) {NULL, 0}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void human_readable_time_interval(uint64_t ns, char *dst, size_t max);
|
||||||
|
void print_profile_results(profile_results_t res_list[], int num_results, long double ns_per_cycle);
|
||||||
+353
@@ -0,0 +1,353 @@
|
|||||||
|
/*
|
||||||
|
* This is free and unencumbered software released into the public domain.
|
||||||
|
*
|
||||||
|
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
* distribute this software, either in source code form or as a compiled
|
||||||
|
* binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
* means.
|
||||||
|
*
|
||||||
|
* In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
* of this software dedicate any and all copyright interest in the
|
||||||
|
* software to the public domain. We make this dedication for the benefit
|
||||||
|
* of the public at large and to the detriment of our heirs and
|
||||||
|
* successors. We intend this dedication to be an overt act of
|
||||||
|
* relinquishment in perpetuity of all present and future rights to this
|
||||||
|
* software under copyright law.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* For more information, please refer to <http://unlicense.org/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "sync.h"
|
||||||
|
#include "clock.h"
|
||||||
|
|
||||||
|
//#define SYNC_PRINT_ERRORS
|
||||||
|
#ifdef SYNC_PRINT_ERRORS
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_GLOBAL_START;
|
||||||
|
|
||||||
|
void os_mutex_create(os_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
InitializeCriticalSection(mutex);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_mutex_init(mutex, NULL))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_mutex_delete(os_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
DeleteCriticalSection(mutex);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_mutex_destroy(mutex))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_mutex_lock(os_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
EnterCriticalSection(mutex);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_mutex_lock(mutex))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_mutex_unlock(os_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
LeaveCriticalSection(mutex);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_mutex_unlock(mutex))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_condvar_create(os_condvar_t *condvar)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
InitializeConditionVariable(condvar);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_cond_init(condvar, NULL))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) condvar;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_condvar_delete(os_condvar_t *condvar)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
if (pthread_cond_destroy(condvar))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) condvar;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_condvar_wait(os_condvar_t *condvar, os_mutex_t *mutex, int timeout_ms)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
DWORD timeout = INFINITE;
|
||||||
|
if (timeout_ms >= 0) timeout = timeout_ms;
|
||||||
|
if (!SleepConditionVariableCS(condvar, mutex, timeout)) {
|
||||||
|
if (GetLastError() == ERROR_TIMEOUT) {
|
||||||
|
PROFILE_END;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
|
||||||
|
int err;
|
||||||
|
if (timeout_ms < 0)
|
||||||
|
err = pthread_cond_wait(condvar, mutex);
|
||||||
|
else {
|
||||||
|
uint64_t wakeup_ms = (uint64_t) timeout_ms + get_absolute_time_us() / 1000;
|
||||||
|
struct timespec abstime = {
|
||||||
|
.tv_sec = wakeup_ms / 1000,
|
||||||
|
.tv_nsec = (wakeup_ms % 1000) * 1000000,
|
||||||
|
};
|
||||||
|
err = pthread_cond_timedwait(condvar, mutex, &abstime);
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
if (err == ETIMEDOUT) {
|
||||||
|
PROFILE_END;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef SYNC_PRINT_ERRORS
|
||||||
|
fprintf(stderr, "ERROR!! pthread_cond_wait/timedwait: %s\n", strerror(err));
|
||||||
|
#endif
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
#else
|
||||||
|
PROFILE_END;
|
||||||
|
(void) condvar;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_condvar_signal(os_condvar_t *condvar)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
WakeConditionVariable(condvar);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_cond_signal(condvar))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) condvar;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
void semaphore_create(semaphore_t *sem, int count)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
sem->count = count;
|
||||||
|
os_mutex_create(&sem->mutex);
|
||||||
|
os_condvar_create(&sem->cond);
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
void semaphore_delete(semaphore_t *sem)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
os_mutex_delete(&sem->mutex);
|
||||||
|
os_condvar_delete(&sem->cond);
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool semaphore_wait(semaphore_t *sem, int count, int timeout_ms)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
assert(count > 0);
|
||||||
|
|
||||||
|
uint64_t start_time_ms = get_relative_time_ns() / 1000000;
|
||||||
|
|
||||||
|
os_mutex_lock(&sem->mutex);
|
||||||
|
while (sem->count < count) {
|
||||||
|
|
||||||
|
uint64_t current_time_ms = get_relative_time_ns() / 1000000;
|
||||||
|
|
||||||
|
int remaining_ms = -1;
|
||||||
|
if (timeout_ms >= 0)
|
||||||
|
remaining_ms = timeout_ms - (int) (current_time_ms - start_time_ms);
|
||||||
|
|
||||||
|
if (!os_condvar_wait(&sem->cond, &sem->mutex, remaining_ms)) {
|
||||||
|
os_mutex_unlock(&sem->mutex);
|
||||||
|
PROFILE_END;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sem->count -= count;
|
||||||
|
os_mutex_unlock(&sem->mutex);
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void semaphore_signal(semaphore_t *sem, int count)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
assert(count > 0);
|
||||||
|
|
||||||
|
os_mutex_lock(&sem->mutex);
|
||||||
|
sem->count += count;
|
||||||
|
if (sem->count > 0)
|
||||||
|
os_condvar_signal(&sem->cond);
|
||||||
|
os_mutex_unlock(&sem->mutex);
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_semaphore_create(os_semaphore_t *sem, int count, int max)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
SECURITY_ATTRIBUTES *attr = NULL; // Default
|
||||||
|
const char *name = NULL; // No name
|
||||||
|
void *handle = CreateSemaphoreA(attr, count, max, name);
|
||||||
|
if (handle == NULL) {
|
||||||
|
PROFILE_END;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sem->data = handle;
|
||||||
|
ok = 1;
|
||||||
|
#else
|
||||||
|
(void) max; // POSIX doesn't use this
|
||||||
|
ok = sem_init(&sem->data, 0, count) == 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_semaphore_delete(os_semaphore_t *sem)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
CloseHandle(sem->data);
|
||||||
|
ok = 1;
|
||||||
|
#else
|
||||||
|
ok = sem_destroy(&sem->data) == 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_semaphore_wait(os_semaphore_t *sem)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
ok = WaitForSingleObject(sem->data, INFINITE) == WAIT_OBJECT_0;
|
||||||
|
#else
|
||||||
|
ok = sem_wait(&sem->data) == 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_semaphore_signal(os_semaphore_t *sem)
|
||||||
|
{
|
||||||
|
PROFILE_START;
|
||||||
|
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
ok = ReleaseSemaphore(sem->data, 1, NULL);
|
||||||
|
#else
|
||||||
|
ok = sem_post(&sem->data) == 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROFILE_END;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROFILE_GLOBAL_END;
|
||||||
|
|
||||||
|
profile_results_t sync_profile_results(void)
|
||||||
|
{
|
||||||
|
return PROFILE_RESULTS;
|
||||||
|
}
|
||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* This is free and unencumbered software released into the public domain.
|
||||||
|
*
|
||||||
|
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
* distribute this software, either in source code form or as a compiled
|
||||||
|
* binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
* means.
|
||||||
|
*
|
||||||
|
* In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
* of this software dedicate any and all copyright interest in the
|
||||||
|
* software to the public domain. We make this dedication for the benefit
|
||||||
|
* of the public at large and to the detriment of our heirs and
|
||||||
|
* successors. We intend this dedication to be an overt act of
|
||||||
|
* relinquishment in perpetuity of all present and future rights to this
|
||||||
|
* software under copyright law.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* For more information, please refer to <http://unlicense.org/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "profile.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_MEAN_AND_LEAN
|
||||||
|
#include <windows.h>
|
||||||
|
typedef CRITICAL_SECTION os_mutex_t;
|
||||||
|
typedef CONDITION_VARIABLE os_condvar_t;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
typedef pthread_mutex_t os_mutex_t;
|
||||||
|
typedef pthread_cond_t os_condvar_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
#ifdef _WIN32
|
||||||
|
void *data;
|
||||||
|
#else
|
||||||
|
sem_t data;
|
||||||
|
#endif
|
||||||
|
} os_semaphore_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int count;
|
||||||
|
os_mutex_t mutex;
|
||||||
|
os_condvar_t cond;
|
||||||
|
} semaphore_t;
|
||||||
|
|
||||||
|
void os_mutex_create(os_mutex_t *mutex);
|
||||||
|
void os_mutex_delete(os_mutex_t *mutex);
|
||||||
|
void os_mutex_lock (os_mutex_t *mutex);
|
||||||
|
void os_mutex_unlock(os_mutex_t *mutex);
|
||||||
|
|
||||||
|
void os_condvar_create(os_condvar_t *condvar);
|
||||||
|
void os_condvar_delete(os_condvar_t *condvar);
|
||||||
|
bool os_condvar_wait (os_condvar_t *condvar, os_mutex_t *mutex, int timeout_ms);
|
||||||
|
void os_condvar_signal(os_condvar_t *condvar);
|
||||||
|
|
||||||
|
void semaphore_create(semaphore_t *sem, int count);
|
||||||
|
void semaphore_delete(semaphore_t *sem);
|
||||||
|
bool semaphore_wait (semaphore_t *sem, int count, int timeout_ms);
|
||||||
|
void semaphore_signal(semaphore_t *sem, int count);
|
||||||
|
|
||||||
|
bool os_semaphore_create(os_semaphore_t *sem, int count, int max);
|
||||||
|
bool os_semaphore_delete(os_semaphore_t *sem);
|
||||||
|
bool os_semaphore_wait (os_semaphore_t *sem);
|
||||||
|
bool os_semaphore_signal(os_semaphore_t *sem);
|
||||||
|
|
||||||
|
profile_results_t sync_profile_results(void);
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* This is free and unencumbered software released into the public domain.
|
||||||
|
*
|
||||||
|
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
* distribute this software, either in source code form or as a compiled
|
||||||
|
* binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
* means.
|
||||||
|
*
|
||||||
|
* In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
* of this software dedicate any and all copyright interest in the
|
||||||
|
* software to the public domain. We make this dedication for the benefit
|
||||||
|
* of the public at large and to the detriment of our heirs and
|
||||||
|
* successors. We intend this dedication to be an overt act of
|
||||||
|
* relinquishment in perpetuity of all present and future rights to this
|
||||||
|
* software under copyright law.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* For more information, please refer to <http://unlicense.org/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#else
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "thread.h"
|
||||||
|
|
||||||
|
void os_thread_create(os_thread *thread, void *arg, os_threadreturn (*func)(void*))
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
os_thread thread_ = CreateThread(NULL, 0, func, arg, 0, NULL);
|
||||||
|
if (thread_ == INVALID_HANDLE_VALUE)
|
||||||
|
abort();
|
||||||
|
*thread = thread_;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
int ret = pthread_create(thread, NULL, func, arg);
|
||||||
|
if (ret) abort();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
os_threadreturn os_thread_join(os_thread thread)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
os_threadreturn result;
|
||||||
|
WaitForSingleObject(thread, INFINITE);
|
||||||
|
if (!GetExitCodeThread(thread, &result))
|
||||||
|
abort();
|
||||||
|
CloseHandle(thread);
|
||||||
|
return result;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
os_threadreturn result;
|
||||||
|
int ret = pthread_join(thread, &result);
|
||||||
|
if (ret) abort();
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
(void) thread;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t get_thread_id(void)
|
||||||
|
{
|
||||||
|
static _Atomic uint64_t next_id = 1;
|
||||||
|
static _Thread_local uint64_t id = 0;
|
||||||
|
if (id == 0) id = atomic_fetch_add(&next_id, 1);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* This is free and unencumbered software released into the public domain.
|
||||||
|
*
|
||||||
|
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
* distribute this software, either in source code form or as a compiled
|
||||||
|
* binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
* means.
|
||||||
|
*
|
||||||
|
* In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
* of this software dedicate any and all copyright interest in the
|
||||||
|
* software to the public domain. We make this dedication for the benefit
|
||||||
|
* of the public at large and to the detriment of our heirs and
|
||||||
|
* successors. We intend this dedication to be an overt act of
|
||||||
|
* relinquishment in perpetuity of all present and future rights to this
|
||||||
|
* software under copyright law.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* For more information, please refer to <http://unlicense.org/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define WIN32_MEAN_AND_LEAN
|
||||||
|
#include <windows.h>
|
||||||
|
typedef void *os_thread;
|
||||||
|
typedef unsigned long os_threadreturn;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#include <pthread.h>
|
||||||
|
typedef pthread_t os_thread;
|
||||||
|
typedef void *os_threadreturn;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint64_t get_thread_id(void);
|
||||||
|
|
||||||
|
void os_thread_create(os_thread *thread, void *arg, os_threadreturn (*func)(void*));
|
||||||
|
os_threadreturn os_thread_join(os_thread thread);
|
||||||
Reference in New Issue
Block a user