# HG changeset patch # User John Tsiombikas # Date 1451014870 -7200 # Node ID cf494adee64636422dce54d50894d12c006e85e9 distance field raytracer diff -r 000000000000 -r cf494adee646 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Fri Dec 25 05:41:10 2015 +0200 @@ -0,0 +1,33 @@ +src = $(wildcard src/*.cc) +obj = $(src:.cc=.o) +dep = $(obj:.o=.d) +bin = distray + +opt = -O3 +dbg = -g + +CXXFLAGS = -std=c++11 -pedantic -Wall $(opt) $(dbg) +LDFLAGS = $(libgl) -lm -lvmath + +sys := $(shell uname -s) +ifeq ($(sys), Darwin) + libgl = -framework OpenGL -framework GLUT -lGLEW +else + libgl = -lGL -lGLU -lglut -lGLEW +endif + +$(bin): $(obj) + $(CXX) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +%.d: %.cc + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(bin) + +.PHONY: cleandep +cleandep: + rm -f $(dep) diff -r 000000000000 -r cf494adee646 src/main.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.cc Fri Dec 25 05:41:10 2015 +0200 @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include +#include +#include "rend.h" + +static bool init(); +static void cleanup(); +static void display(); +static void reshape(int x, int y); +static void keyb(unsigned char key, int x, int y); +static void mouse(int bn, int st, int x, int y); +static void motion(int x, int y); +static void resize_fbtex(int xsz, int ysz); +static void update_fbtex(); +static int next_pow2(int x); +static float dfunc(float x, float y, float z); + +static int pix_scale = 2; +static int width = 640; +static int height = 384; +static int win_width = width * pix_scale; +static int win_height = height * pix_scale; + +static float *pixels; +static unsigned int tex; +static int tex_width, tex_height; + +static float cam_theta, cam_phi = 25, cam_dist = 10; + + +int main(int argc, char **argv) +{ + glutInit(&argc, argv); + glutInitWindowSize(win_width, win_height); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); + glutCreateWindow("distance field raytracer"); + + glutDisplayFunc(display); + glutReshapeFunc(reshape); + glutKeyboardFunc(keyb); + glutMouseFunc(mouse); + glutMotionFunc(motion); + + if(!init()) { + return 1; + } + atexit(cleanup); + + glutMainLoop(); + return 0; +} + +static bool init() +{ + glewInit(); + + glEnable(GL_CULL_FACE); + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glEnable(GL_TEXTURE_2D); + + set_distance_function(dfunc); + + return true; +} + +static void cleanup() +{ + glDeleteTextures(1, &tex); +} + +static void display() +{ + Matrix4x4 cmat; + cmat.translate(Vector3(0, 0, -cam_dist)); + cmat.rotate(Vector3(1, 0, 0), DEG_TO_RAD(cam_phi)); + cmat.rotate(Vector3(0, 1, 0), DEG_TO_RAD(cam_theta)); + + set_camera_matrix(cmat); + render(); + update_fbtex(); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef((float)tex_width / (float)width, (float)tex_height / (float)height, 1); + + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(-1, -1); + glTexCoord2f(1, 0); glVertex2f(1, -1); + glTexCoord2f(1, 1); glVertex2f(1, 1); + glTexCoord2f(0, 1); glVertex2f(-1, 1); + glEnd(); + + glutSwapBuffers(); + assert(glGetError() == GL_NO_ERROR); +} + +static void reshape(int x, int y) +{ + static int prev_x , prev_y; + + glViewport(0, 0, x, y); + resize_fbtex(x, y); + + if(x != prev_x || y != prev_y) { + delete [] pixels; + pixels = new float[x * y * 4]; + set_framebuffer(x, y, pixels); + prev_x = x; + prev_y = y; + } +} + +static void resize_fbtex(int xsz, int ysz) +{ + int txsz = next_pow2(xsz); + int tysz = next_pow2(ysz); + + if(txsz > tex_width || tysz > tex_height) { + tex_width = txsz; + tex_height = tysz; + + glBindTexture(GL_TEXTURE_2D, tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, txsz, tysz, 0, GL_RGBA, GL_FLOAT, 0); + } +} + +static void update_fbtex() +{ + glBindTexture(GL_TEXTURE_2D, tex); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, pixels); +} + +static int next_pow2(int x) +{ + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; +} + +static float dfunc(float x, float y, float z) +{ + return sqrt(x*x + y*y + z*z) - 1.0; +} + +static void keyb(unsigned char key, int x, int y) +{ + switch(key) { + case 27: + exit(0); + } +} + +static int prev_x, prev_y; +static bool bnstate[8]; + +static void mouse(int bn, int st, int x, int y) +{ + bnstate[bn - GLUT_LEFT_BUTTON] = st == GLUT_DOWN; + prev_x = x; + prev_y = y; +} + +static void motion(int x, int y) +{ + int dx = x - prev_x; + int dy = y - prev_y; + prev_x = x; + prev_y = y; + + if(!dx && !dy) return; + + if(bnstate[0]) { + cam_theta -= dx * 0.5; + cam_phi -= dy * 0.5; + + if(cam_phi < -90) cam_phi = -90; + if(cam_phi > 90) cam_phi = 90; + glutPostRedisplay(); + } + if(bnstate[2]) { + cam_dist += dy * 0.1; + if(cam_dist < 0.0) cam_dist = 0.0; + glutPostRedisplay(); + } +} diff -r 000000000000 -r cf494adee646 src/rend.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rend.cc Fri Dec 25 05:41:10 2015 +0200 @@ -0,0 +1,54 @@ +#include "rend.h" +#include "threadpool.h" + +static float *pixels; +static int width, height; + +static Matrix4x4 cam_matrix, inv_cam_matrix; +static float (*distfunc)(float, float, float); + +static ThreadPool tpool; + + +void set_framebuffer(int x, int y, float *pix) +{ + pixels = pix; + width = x; + height = y; +} + +void set_distance_function(float (*dfunc)(float, float, float)) +{ + distfunc = dfunc; +} + +void set_camera_matrix(const Matrix4x4 &xform) +{ + cam_matrix = xform; + inv_cam_matrix = xform.inverse(); +} + +static void render_scanline(int y) +{ + float *pptr = pixels + y * width * 4; + for(int i=0; i + +void set_framebuffer(int width, int height, float *pixels); +void set_distance_function(float (*dfunc)(float, float, float)); +void set_camera_matrix(const Matrix4x4 &xform); + +void start_render(); +void wait_render(); + +void render(); + +#endif // REND_H_ diff -r 000000000000 -r cf494adee646 src/threadpool.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/threadpool.cc Fri Dec 25 05:41:10 2015 +0200 @@ -0,0 +1,160 @@ +#include +#include +#include "threadpool.h" + +using namespace std::chrono; + +ThreadPool::ThreadPool(int num_threads) +{ + quit = false; + qsize = 0; + nactive = 0; + + if(num_threads == -1) { + num_threads = std::thread::hardware_concurrency(); + } + + printf("creating thread pool with %d threads\n", num_threads); + + thread = new std::thread[num_threads]; + for(int i=0; inum_threads = num_threads; +} + +ThreadPool::~ThreadPool() +{ +#ifdef _MSC_VER + clear_work(); +#endif + + quit = true; + workq_condvar.notify_all(); + + printf("ThreadPool: waiting for %d worker threads to stop ", num_threads); + fflush(stdout); +#ifndef _MSC_VER + for(int i=0; i lock(workq_mutex); + while(nactive > 0) { + lock.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(128)); + putchar('.'); + fflush(stdout); + lock.lock(); + } +#endif // _MSC_VER + + putchar('\n'); + delete [] thread; +} + +void ThreadPool::add_work(std::function func) +{ + add_work(func, std::function{}); +} + +void ThreadPool::add_work(std::function work_func, std::function done_func) +{ + std::unique_lock lock(workq_mutex); + workq.push_back(WorkItem{work_func, done_func}); + ++qsize; + workq_condvar.notify_all(); +} + +void ThreadPool::clear_work() +{ + std::unique_lock lock(workq_mutex); + workq.clear(); + qsize = 0; +} + +int ThreadPool::queued() const +{ + std::unique_lock lock(workq_mutex); + return qsize; +} + +int ThreadPool::active() const +{ + std::unique_lock lock(workq_mutex); + return nactive; +} + +int ThreadPool::pending() const +{ + std::unique_lock lock(workq_mutex); + return nactive + qsize; +} + +long ThreadPool::wait() +{ + auto start_time = steady_clock::now(); + + std::unique_lock lock(workq_mutex); + done_condvar.wait(lock, [this](){ return nactive == 0 && workq.empty(); }); + + auto dur = steady_clock::now() - start_time; + return duration_cast(dur).count(); +} + +long ThreadPool::wait(long timeout) +{ + auto start_time = steady_clock::now(); + duration dur, timeout_dur(std::max(timeout, 5L)); + + std::unique_lock lock(workq_mutex); + while(timeout_dur.count() > 0 && (nactive > 0 || !workq.empty())) { + if(done_condvar.wait_for(lock, timeout_dur) == std::cv_status::timeout) { + break; + } + dur = duration_cast(steady_clock::now() - start_time); + timeout_dur = milliseconds(std::max(timeout, 5L)) - dur; + } + + /*printf("waited for: %ld ms (%ld req) (na %d,qs %d,em %s)\n", dur.count(), timeout, + nactive, qsize, workq.empty() ? "true" : "false");*/ + return dur.count(); +} + +void ThreadPool::thread_func() +{ + std::unique_lock lock(workq_mutex); + for(;;) { + if(quit) break; + + workq_condvar.wait(lock); + + while(!quit && !workq.empty()) { + WorkItem witem = workq.front(); + workq.pop_front(); + ++nactive; + --qsize; + lock.unlock(); + + witem.work(); + if(witem.done) { + witem.done(); + } + + lock.lock(); + --nactive; + done_condvar.notify_all(); + } + } +} + diff -r 000000000000 -r cf494adee646 src/threadpool.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/threadpool.h Fri Dec 25 05:41:10 2015 +0200 @@ -0,0 +1,55 @@ +#ifndef THREAD_POOL_H_ +#define THREAD_POOL_H_ + +#include +#include +#include +#include +#include + +class ThreadPool { +private: + int num_threads; + std::thread *thread; // array of threads + + struct WorkItem { + std::function work; + std::function done; + }; + + int qsize; + std::list workq; + mutable std::mutex workq_mutex; + std::condition_variable workq_condvar; + + int nactive; // number of active workers (not sleeping) + + // condvar used by wait + std::condition_variable done_condvar; + + bool quit; + + void thread_func(); + +public: + // passing num_threads == -1 auto-detects based on number of processors + explicit ThreadPool(int num_threads = -1); + ~ThreadPool(); + + void add_work(std::function func); + void add_work(std::function work_func, std::function done_func); + void clear_work(); + + // returns the number of queued work items + int queued() const; + // returns the number of active threads + int active() const; + // returns number of pending work items (both in the queue and active) + int pending() const; + + // waits for all work to be completed + long wait(); + long wait(long timeout); +}; + +#endif // THREAD_POOL_H_