Add progress bars and argument parsing

This commit is contained in:
David 2021-08-29 21:24:17 +02:00
commit a45ae025d6
7 changed files with 4897 additions and 29 deletions

3
.gitignore vendored
View file

@ -11,3 +11,6 @@ image.ppm
# Profiler data
perf.*
# Core dumps
core

View file

@ -8,7 +8,7 @@ raytracer: camera.hpp color.hpp hittable.hpp hittable_list.hpp main.cpp material
make debug:
image: raytracer
@./raytracer > image.ppm
@./raytracer -o image.ppm
@if [ $$TERM = "xterm-kitty" ]; then\
kitty icat image.ppm;\
fi

View file

@ -24,4 +24,12 @@ void write_color(FILE *fp, color c, uint32_t samples_per_pixel)
(uint8_t) (255 * clamp(b, 0, 1)));
}
void write_image(color *image, uint64_t n, FILE *fp, uint32_t samples_per_pixel)
{
for (int64_t i = n-1; i >= 0; --i)
{
write_color(fp, image[i], samples_per_pixel);
}
}
#endif

4649
include/indicators.hpp Normal file

File diff suppressed because it is too large Load diff

261
main.cpp
View file

@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <getopt.h>
#define RMT_ENABLED 0
@ -12,6 +13,8 @@
#include <Remotery.c>
#pragma GCC diagnostic pop
#include <indicators.hpp>
// Internal includes
#include "rtweekend.hpp"
#include "color.hpp"
@ -25,11 +28,41 @@
#define print_timers()
#endif
// Threading structs
struct thread_args
{
int32_t thread_id;
int32_t start;
int32_t end;
};
// Function signatures
color ray_color(const ray& r, const hittable& world, int32_t depth);
float hit_sphere(const point3& center, float radius, const ray& r);
void *raytrace_lines(void *arg);
hittable_list<sphere> random_scene();
// Global vars
indicators::DynamicProgress<indicators::BlockProgressBar> progress_bars;
const char *default_file = "image.ppm";
FILE *output_file_handle;
// Image
float aspect_ratio;
int32_t image_width;
int32_t image_height;
int32_t samples_per_pixel;
int32_t max_depth;
color *image;
uint64_t bytes_per_line;
uint64_t bytes_per_pixel;
// World
hittable_list<sphere> world;
camera *global_camera;
hittable_list<sphere> random_scene() {
hittable_list<sphere> world;
@ -127,22 +160,81 @@ float hit_sphere(const point3& center, float radius, const ray& r)
else
return (-half_b - sqrt(discriminant)) / a;
}
int32_t main()
{
int32_t main(int argc, char *argv[])
{
/* Argument parsing */
int32_t c;
bool using_default_output = true;
while (1)
{
static struct option long_options[] =
{
{"output", required_argument, 0, 'o'},
{0, 0, 0, 0}
};
/* getopt_long stores the option index here. */
int option_index = 0;
c = getopt_long (argc, argv, "o:",
long_options, &option_index);
/* Detect the end of the options. */
if (c == -1)
break;
switch (c)
{
case 0:
/* If this option set a flag, do nothing else now. */
if (long_options[option_index].flag != 0)
break;
printf ("option %s", long_options[option_index].name);
if (optarg)
printf (" with arg %s", optarg);
printf ("\n");
break;
case 'o':
using_default_output = false;
output_file_handle = fopen(optarg, "w");
break;
case '?':
/* getopt_long already printed an error message. */
break;
default:
abort();
}
}
if (using_default_output)
{
output_file_handle = fopen(default_file, "w");
}
/* Profiling library initialization */
Remotery *rmt;
if (RMT_ERROR_NONE != rmt_CreateGlobalInstance(&rmt))
{
fprintf(stderr, "Error starting Remotery\n");
}
//indicators::show_console_cursor(false);
// Image
const float aspect_ratio = 3.0 / 2.0;
const int32_t image_width = 1200;
const int32_t image_height = (int32_t) (image_width / aspect_ratio);
int32_t samples_per_pixel = 500;
const int32_t max_depth = 50;
aspect_ratio = 3.0 / 2.0;
image_width = 1200;
image_height = (int32_t) (image_width / aspect_ratio);
samples_per_pixel = 500;
max_depth = 50;
image = (color*) malloc(image_width * image_height * sizeof(color));
bytes_per_line = sizeof(color) * image_width;
bytes_per_pixel = sizeof(color);
if (getenv("SPP"))
{
@ -150,7 +242,7 @@ int32_t main()
}
// World
hittable_list<sphere> world = random_scene();
world = random_scene();
// Camera
point3 lookfrom(13,2,3);
@ -159,37 +251,146 @@ int32_t main()
float dist_to_focus = 10.0;
float aperture = 0.1;
camera cam(lookfrom, lookat, vup, 20, aspect_ratio, aperture, dist_to_focus);
camera cam = camera(lookfrom, lookat, vup, 20, aspect_ratio, aperture, dist_to_focus);
global_camera = &cam;
// Render
printf("P3\n%d %d\n255\n", image_width, image_height);
fprintf(output_file_handle, "P3\n%d %d\n255\n", image_width, image_height);
int32_t ncores = sysconf(_SC_NPROCESSORS_ONLN); // Get the number of logical CPUs
std::vector<pthread_t> threads;
std::vector<thread_args> args;
threads.reserve(ncores);
args.reserve(ncores);
std::vector<indicators::BlockProgressBar*> bar_memory;
bar_memory.reserve(ncores);
for (int32_t i = 0; i < ncores; ++i)
{
bar_memory[i] = new indicators::BlockProgressBar{indicators::option::BarWidth{50},
indicators::option::ForegroundColor{indicators::Color::white},
indicators::option::ShowElapsedTime{true},
indicators::option::ShowRemainingTime{true},
indicators::option::PrefixText{"Thread #" + std::to_string(i)}
};
progress_bars.push_back(*bar_memory[i]);
int32_t start;
int32_t end;
// Divide work among cores
start = image_height/ncores * i;
end = image_height/ncores * (i+1);
// Make sure we complete the whole picture even if the work is not perfectly divisible
if (i == ncores)
end = image_height;
args[i].start = start;
args[i].end = end;
args[i].thread_id = i;
// TODO: Check for errors
pthread_create(&threads[i], NULL, raytrace_lines, &args[i]);
}
for (int32_t i = 0; i < ncores; ++i)
{
switch (pthread_join(threads[i], NULL))
{
case EDEADLK:
fprintf(stderr, "A deadlock was detected (e.g., two threads tried to join with each other); or thread specifies the calling thread.\n");
break;
case EINVAL:
fprintf(stderr, "thread is not a joinable thread OR\n"
"Another thread is already waiting to join with this thread.\n");
break;
case ESRCH:
fprintf(stderr, "No thread with the ID thread could be found.\n");
break;
default:
break;
}
}
write_image(image, image_width*image_height, output_file_handle, samples_per_pixel);
/* Obsolete non-threaded implementation */
// for (int32_t j = image_height - 1; j >= 0; --j)
// {
// rmt_ScopedCPUSample(OuterLoop, RMTSF_Aggregate);
// fprintf(stderr, "\rScanlines remaining: %d ", j);
// print_timers();
// fflush(stderr);
for (int32_t j = image_height - 1; j >= 0; --j)
{
rmt_ScopedCPUSample(OuterLoop, RMTSF_Aggregate);
fprintf(stderr, "\rScanlines remaining: %d ", j);
print_timers();
fflush(stderr);
for (int32_t i = 0; i < image_width; ++i)
{
rmt_ScopedCPUSample(InnerLoop, RMTSF_Aggregate);
color pixel_color = color(0,0,0);
// for (int32_t i = 0; i < image_width; ++i)
// {
// rmt_ScopedCPUSample(InnerLoop, RMTSF_Aggregate);
// color pixel_color = color(0,0,0);
// for (int32_t s = 0; s < samples_per_pixel; ++s)
// {
// float u = ((i + random_float()) / (image_width-1));
// float v = ((j + random_float()) / (image_height-1));
// ray r = cam.get_ray(u,v);
// pixel_color += ray_color(r, world, max_depth);
// }
// write_color(output_file_handle, pixel_color, samples_per_pixel);
// }
// }
fprintf(stderr, "\nDone\n");
rmt_DestroyGlobalInstance(rmt);
free(image);
fclose(output_file_handle);
//indicators::show_console_cursor(true);
}
void *raytrace_lines(void *arg)
{
thread_args arguments = *((thread_args*)arg);
int32_t start = arguments.start;
int32_t end = arguments.end;
int32_t thread_id = arguments.thread_id;
for (int32_t j = end - 1; j >= start; --j)
{
int32_t lines_expected = end-start;
int32_t lines_completed = end-j;
progress_bars[thread_id].set_option(indicators::option::PostfixText{std::to_string(lines_completed) + "/" + std::to_string(lines_expected)});
progress_bars[thread_id].set_progress(((float)lines_completed/lines_expected)*100);
rmt_ScopedCPUSample(OuterLoop, RMTSF_Aggregate);
for (int32_t i = 0; i < image_width; ++i)
{
color pixel_color = color(0,0,0);
for (int32_t s = 0; s < samples_per_pixel; ++s)
{
float u = ((i + random_float()) / (image_width-1));
float v = ((j + random_float()) / (image_height-1));
ray r = cam.get_ray(u,v);
ray r = global_camera->get_ray(u,v);
pixel_color += ray_color(r, world, max_depth);
}
write_color(stdout, pixel_color, samples_per_pixel);
}
int32_t index = j * image_width + i;
image[index] = pixel_color;
}
}
fprintf(stderr, "\nDone\n");
rmt_DestroyGlobalInstance(rmt);
return nullptr;
}
#ifdef DEBUG

View file

@ -3,6 +3,8 @@
#include <math.h>
#include <memory>
#include <pthread.h>
#include <unistd.h>
#include "timer.hpp"
#include "random.h"

View file

@ -92,6 +92,11 @@ typedef vec3 color;
/* More overloads */
inline bool operator==(const vec3 &u, const vec3 &v)
{
return u.x == v.x && u.y == v.y && u.z == v.z;
}
// Straightforward vector sum
inline vec3 operator+(const vec3 &u, const vec3 &v)
{
@ -191,7 +196,7 @@ vec3 reflect(const vec3& v, const vec3 n)
return v - 2*dot(v,n)*n;
}
vec3 refract (const vec3& uv, const vec3& n, float etai_over_etat)
vec3 refract(const vec3& uv, const vec3& n, float etai_over_etat)
{
float cos_theta = fmin(dot(-uv, n), 1.0);
vec3 r_out_perp = etai_over_etat * (uv + cos_theta*n);