distray
changeset 0:cf494adee646 tip
distance field raytracer
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Fri, 25 Dec 2015 05:41:10 +0200 |
parents | |
children | |
files | Makefile src/main.cc src/rend.cc src/rend.h src/threadpool.cc src/threadpool.h |
diffstat | 6 files changed, 514 insertions(+), 0 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Makefile Fri Dec 25 05:41:10 2015 +0200 1.3 @@ -0,0 +1,33 @@ 1.4 +src = $(wildcard src/*.cc) 1.5 +obj = $(src:.cc=.o) 1.6 +dep = $(obj:.o=.d) 1.7 +bin = distray 1.8 + 1.9 +opt = -O3 1.10 +dbg = -g 1.11 + 1.12 +CXXFLAGS = -std=c++11 -pedantic -Wall $(opt) $(dbg) 1.13 +LDFLAGS = $(libgl) -lm -lvmath 1.14 + 1.15 +sys := $(shell uname -s) 1.16 +ifeq ($(sys), Darwin) 1.17 + libgl = -framework OpenGL -framework GLUT -lGLEW 1.18 +else 1.19 + libgl = -lGL -lGLU -lglut -lGLEW 1.20 +endif 1.21 + 1.22 +$(bin): $(obj) 1.23 + $(CXX) -o $@ $(obj) $(LDFLAGS) 1.24 + 1.25 +-include $(dep) 1.26 + 1.27 +%.d: %.cc 1.28 + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ 1.29 + 1.30 +.PHONY: clean 1.31 +clean: 1.32 + rm -f $(obj) $(bin) 1.33 + 1.34 +.PHONY: cleandep 1.35 +cleandep: 1.36 + rm -f $(dep)
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/src/main.cc Fri Dec 25 05:41:10 2015 +0200 2.3 @@ -0,0 +1,197 @@ 2.4 +#include <stdio.h> 2.5 +#include <stdlib.h> 2.6 +#include <assert.h> 2.7 +#include <GL/glew.h> 2.8 +#include <GL/glut.h> 2.9 +#include <vmath/vmath.h> 2.10 +#include "rend.h" 2.11 + 2.12 +static bool init(); 2.13 +static void cleanup(); 2.14 +static void display(); 2.15 +static void reshape(int x, int y); 2.16 +static void keyb(unsigned char key, int x, int y); 2.17 +static void mouse(int bn, int st, int x, int y); 2.18 +static void motion(int x, int y); 2.19 +static void resize_fbtex(int xsz, int ysz); 2.20 +static void update_fbtex(); 2.21 +static int next_pow2(int x); 2.22 +static float dfunc(float x, float y, float z); 2.23 + 2.24 +static int pix_scale = 2; 2.25 +static int width = 640; 2.26 +static int height = 384; 2.27 +static int win_width = width * pix_scale; 2.28 +static int win_height = height * pix_scale; 2.29 + 2.30 +static float *pixels; 2.31 +static unsigned int tex; 2.32 +static int tex_width, tex_height; 2.33 + 2.34 +static float cam_theta, cam_phi = 25, cam_dist = 10; 2.35 + 2.36 + 2.37 +int main(int argc, char **argv) 2.38 +{ 2.39 + glutInit(&argc, argv); 2.40 + glutInitWindowSize(win_width, win_height); 2.41 + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); 2.42 + glutCreateWindow("distance field raytracer"); 2.43 + 2.44 + glutDisplayFunc(display); 2.45 + glutReshapeFunc(reshape); 2.46 + glutKeyboardFunc(keyb); 2.47 + glutMouseFunc(mouse); 2.48 + glutMotionFunc(motion); 2.49 + 2.50 + if(!init()) { 2.51 + return 1; 2.52 + } 2.53 + atexit(cleanup); 2.54 + 2.55 + glutMainLoop(); 2.56 + return 0; 2.57 +} 2.58 + 2.59 +static bool init() 2.60 +{ 2.61 + glewInit(); 2.62 + 2.63 + glEnable(GL_CULL_FACE); 2.64 + 2.65 + glGenTextures(1, &tex); 2.66 + glBindTexture(GL_TEXTURE_2D, tex); 2.67 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 2.68 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 2.69 + 2.70 + glEnable(GL_TEXTURE_2D); 2.71 + 2.72 + set_distance_function(dfunc); 2.73 + 2.74 + return true; 2.75 +} 2.76 + 2.77 +static void cleanup() 2.78 +{ 2.79 + glDeleteTextures(1, &tex); 2.80 +} 2.81 + 2.82 +static void display() 2.83 +{ 2.84 + Matrix4x4 cmat; 2.85 + cmat.translate(Vector3(0, 0, -cam_dist)); 2.86 + cmat.rotate(Vector3(1, 0, 0), DEG_TO_RAD(cam_phi)); 2.87 + cmat.rotate(Vector3(0, 1, 0), DEG_TO_RAD(cam_theta)); 2.88 + 2.89 + set_camera_matrix(cmat); 2.90 + render(); 2.91 + update_fbtex(); 2.92 + 2.93 + glMatrixMode(GL_TEXTURE); 2.94 + glLoadIdentity(); 2.95 + glScalef((float)tex_width / (float)width, (float)tex_height / (float)height, 1); 2.96 + 2.97 + glBegin(GL_QUADS); 2.98 + glTexCoord2f(0, 0); glVertex2f(-1, -1); 2.99 + glTexCoord2f(1, 0); glVertex2f(1, -1); 2.100 + glTexCoord2f(1, 1); glVertex2f(1, 1); 2.101 + glTexCoord2f(0, 1); glVertex2f(-1, 1); 2.102 + glEnd(); 2.103 + 2.104 + glutSwapBuffers(); 2.105 + assert(glGetError() == GL_NO_ERROR); 2.106 +} 2.107 + 2.108 +static void reshape(int x, int y) 2.109 +{ 2.110 + static int prev_x , prev_y; 2.111 + 2.112 + glViewport(0, 0, x, y); 2.113 + resize_fbtex(x, y); 2.114 + 2.115 + if(x != prev_x || y != prev_y) { 2.116 + delete [] pixels; 2.117 + pixels = new float[x * y * 4]; 2.118 + set_framebuffer(x, y, pixels); 2.119 + prev_x = x; 2.120 + prev_y = y; 2.121 + } 2.122 +} 2.123 + 2.124 +static void resize_fbtex(int xsz, int ysz) 2.125 +{ 2.126 + int txsz = next_pow2(xsz); 2.127 + int tysz = next_pow2(ysz); 2.128 + 2.129 + if(txsz > tex_width || tysz > tex_height) { 2.130 + tex_width = txsz; 2.131 + tex_height = tysz; 2.132 + 2.133 + glBindTexture(GL_TEXTURE_2D, tex); 2.134 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, txsz, tysz, 0, GL_RGBA, GL_FLOAT, 0); 2.135 + } 2.136 +} 2.137 + 2.138 +static void update_fbtex() 2.139 +{ 2.140 + glBindTexture(GL_TEXTURE_2D, tex); 2.141 + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, pixels); 2.142 +} 2.143 + 2.144 +static int next_pow2(int x) 2.145 +{ 2.146 + --x; 2.147 + x |= x >> 1; 2.148 + x |= x >> 2; 2.149 + x |= x >> 4; 2.150 + x |= x >> 8; 2.151 + x |= x >> 16; 2.152 + return x + 1; 2.153 +} 2.154 + 2.155 +static float dfunc(float x, float y, float z) 2.156 +{ 2.157 + return sqrt(x*x + y*y + z*z) - 1.0; 2.158 +} 2.159 + 2.160 +static void keyb(unsigned char key, int x, int y) 2.161 +{ 2.162 + switch(key) { 2.163 + case 27: 2.164 + exit(0); 2.165 + } 2.166 +} 2.167 + 2.168 +static int prev_x, prev_y; 2.169 +static bool bnstate[8]; 2.170 + 2.171 +static void mouse(int bn, int st, int x, int y) 2.172 +{ 2.173 + bnstate[bn - GLUT_LEFT_BUTTON] = st == GLUT_DOWN; 2.174 + prev_x = x; 2.175 + prev_y = y; 2.176 +} 2.177 + 2.178 +static void motion(int x, int y) 2.179 +{ 2.180 + int dx = x - prev_x; 2.181 + int dy = y - prev_y; 2.182 + prev_x = x; 2.183 + prev_y = y; 2.184 + 2.185 + if(!dx && !dy) return; 2.186 + 2.187 + if(bnstate[0]) { 2.188 + cam_theta -= dx * 0.5; 2.189 + cam_phi -= dy * 0.5; 2.190 + 2.191 + if(cam_phi < -90) cam_phi = -90; 2.192 + if(cam_phi > 90) cam_phi = 90; 2.193 + glutPostRedisplay(); 2.194 + } 2.195 + if(bnstate[2]) { 2.196 + cam_dist += dy * 0.1; 2.197 + if(cam_dist < 0.0) cam_dist = 0.0; 2.198 + glutPostRedisplay(); 2.199 + } 2.200 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/rend.cc Fri Dec 25 05:41:10 2015 +0200 3.3 @@ -0,0 +1,54 @@ 3.4 +#include "rend.h" 3.5 +#include "threadpool.h" 3.6 + 3.7 +static float *pixels; 3.8 +static int width, height; 3.9 + 3.10 +static Matrix4x4 cam_matrix, inv_cam_matrix; 3.11 +static float (*distfunc)(float, float, float); 3.12 + 3.13 +static ThreadPool tpool; 3.14 + 3.15 + 3.16 +void set_framebuffer(int x, int y, float *pix) 3.17 +{ 3.18 + pixels = pix; 3.19 + width = x; 3.20 + height = y; 3.21 +} 3.22 + 3.23 +void set_distance_function(float (*dfunc)(float, float, float)) 3.24 +{ 3.25 + distfunc = dfunc; 3.26 +} 3.27 + 3.28 +void set_camera_matrix(const Matrix4x4 &xform) 3.29 +{ 3.30 + cam_matrix = xform; 3.31 + inv_cam_matrix = xform.inverse(); 3.32 +} 3.33 + 3.34 +static void render_scanline(int y) 3.35 +{ 3.36 + float *pptr = pixels + y * width * 4; 3.37 + for(int i=0; i<width; i++) { 3.38 + } 3.39 +} 3.40 + 3.41 +void start_render() 3.42 +{ 3.43 + for(int i=0; i<height; i++) { 3.44 + tpool.add_work(std::bind(render_scanline, i)); 3.45 + } 3.46 +} 3.47 + 3.48 +void wait_render() 3.49 +{ 3.50 + tpool.wait(); 3.51 +} 3.52 + 3.53 +void render() 3.54 +{ 3.55 + start_render(); 3.56 + wait_render(); 3.57 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/src/rend.h Fri Dec 25 05:41:10 2015 +0200 4.3 @@ -0,0 +1,15 @@ 4.4 +#ifndef REND_H_ 4.5 +#define REND_H_ 4.6 + 4.7 +#include <vmath/vmath.h> 4.8 + 4.9 +void set_framebuffer(int width, int height, float *pixels); 4.10 +void set_distance_function(float (*dfunc)(float, float, float)); 4.11 +void set_camera_matrix(const Matrix4x4 &xform); 4.12 + 4.13 +void start_render(); 4.14 +void wait_render(); 4.15 + 4.16 +void render(); 4.17 + 4.18 +#endif // REND_H_
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/threadpool.cc Fri Dec 25 05:41:10 2015 +0200 5.3 @@ -0,0 +1,160 @@ 5.4 +#include <algorithm> 5.5 +#include <chrono> 5.6 +#include "threadpool.h" 5.7 + 5.8 +using namespace std::chrono; 5.9 + 5.10 +ThreadPool::ThreadPool(int num_threads) 5.11 +{ 5.12 + quit = false; 5.13 + qsize = 0; 5.14 + nactive = 0; 5.15 + 5.16 + if(num_threads == -1) { 5.17 + num_threads = std::thread::hardware_concurrency(); 5.18 + } 5.19 + 5.20 + printf("creating thread pool with %d threads\n", num_threads); 5.21 + 5.22 + thread = new std::thread[num_threads]; 5.23 + for(int i=0; i<num_threads; i++) { 5.24 + thread[i] = std::thread(&ThreadPool::thread_func, this); 5.25 + 5.26 +#ifdef _MSC_VER 5.27 + /* detach the thread to avoid having to join them in the destructor, which 5.28 + * causes a deadlock in msvc implementation when called after main returns 5.29 + */ 5.30 + thread[i].detach(); 5.31 +#endif 5.32 + } 5.33 + this->num_threads = num_threads; 5.34 +} 5.35 + 5.36 +ThreadPool::~ThreadPool() 5.37 +{ 5.38 +#ifdef _MSC_VER 5.39 + clear_work(); 5.40 +#endif 5.41 + 5.42 + quit = true; 5.43 + workq_condvar.notify_all(); 5.44 + 5.45 + printf("ThreadPool: waiting for %d worker threads to stop ", num_threads); 5.46 + fflush(stdout); 5.47 +#ifndef _MSC_VER 5.48 + for(int i=0; i<num_threads; i++) { 5.49 + thread[i].join(); 5.50 + putchar('.'); 5.51 + fflush(stdout); 5.52 + } 5.53 +#else 5.54 + // spin until all threads are done... 5.55 + std::unique_lock<std::mutex> lock(workq_mutex); 5.56 + while(nactive > 0) { 5.57 + lock.unlock(); 5.58 + std::this_thread::sleep_for(std::chrono::milliseconds(128)); 5.59 + putchar('.'); 5.60 + fflush(stdout); 5.61 + lock.lock(); 5.62 + } 5.63 +#endif // _MSC_VER 5.64 + 5.65 + putchar('\n'); 5.66 + delete [] thread; 5.67 +} 5.68 + 5.69 +void ThreadPool::add_work(std::function<void ()> func) 5.70 +{ 5.71 + add_work(func, std::function<void ()>{}); 5.72 +} 5.73 + 5.74 +void ThreadPool::add_work(std::function<void ()> work_func, std::function<void ()> done_func) 5.75 +{ 5.76 + std::unique_lock<std::mutex> lock(workq_mutex); 5.77 + workq.push_back(WorkItem{work_func, done_func}); 5.78 + ++qsize; 5.79 + workq_condvar.notify_all(); 5.80 +} 5.81 + 5.82 +void ThreadPool::clear_work() 5.83 +{ 5.84 + std::unique_lock<std::mutex> lock(workq_mutex); 5.85 + workq.clear(); 5.86 + qsize = 0; 5.87 +} 5.88 + 5.89 +int ThreadPool::queued() const 5.90 +{ 5.91 + std::unique_lock<std::mutex> lock(workq_mutex); 5.92 + return qsize; 5.93 +} 5.94 + 5.95 +int ThreadPool::active() const 5.96 +{ 5.97 + std::unique_lock<std::mutex> lock(workq_mutex); 5.98 + return nactive; 5.99 +} 5.100 + 5.101 +int ThreadPool::pending() const 5.102 +{ 5.103 + std::unique_lock<std::mutex> lock(workq_mutex); 5.104 + return nactive + qsize; 5.105 +} 5.106 + 5.107 +long ThreadPool::wait() 5.108 +{ 5.109 + auto start_time = steady_clock::now(); 5.110 + 5.111 + std::unique_lock<std::mutex> lock(workq_mutex); 5.112 + done_condvar.wait(lock, [this](){ return nactive == 0 && workq.empty(); }); 5.113 + 5.114 + auto dur = steady_clock::now() - start_time; 5.115 + return duration_cast<milliseconds>(dur).count(); 5.116 +} 5.117 + 5.118 +long ThreadPool::wait(long timeout) 5.119 +{ 5.120 + auto start_time = steady_clock::now(); 5.121 + duration<long, std::milli> dur, timeout_dur(std::max(timeout, 5L)); 5.122 + 5.123 + std::unique_lock<std::mutex> lock(workq_mutex); 5.124 + while(timeout_dur.count() > 0 && (nactive > 0 || !workq.empty())) { 5.125 + if(done_condvar.wait_for(lock, timeout_dur) == std::cv_status::timeout) { 5.126 + break; 5.127 + } 5.128 + dur = duration_cast<milliseconds>(steady_clock::now() - start_time); 5.129 + timeout_dur = milliseconds(std::max(timeout, 5L)) - dur; 5.130 + } 5.131 + 5.132 + /*printf("waited for: %ld ms (%ld req) (na %d,qs %d,em %s)\n", dur.count(), timeout, 5.133 + nactive, qsize, workq.empty() ? "true" : "false");*/ 5.134 + return dur.count(); 5.135 +} 5.136 + 5.137 +void ThreadPool::thread_func() 5.138 +{ 5.139 + std::unique_lock<std::mutex> lock(workq_mutex); 5.140 + for(;;) { 5.141 + if(quit) break; 5.142 + 5.143 + workq_condvar.wait(lock); 5.144 + 5.145 + while(!quit && !workq.empty()) { 5.146 + WorkItem witem = workq.front(); 5.147 + workq.pop_front(); 5.148 + ++nactive; 5.149 + --qsize; 5.150 + lock.unlock(); 5.151 + 5.152 + witem.work(); 5.153 + if(witem.done) { 5.154 + witem.done(); 5.155 + } 5.156 + 5.157 + lock.lock(); 5.158 + --nactive; 5.159 + done_condvar.notify_all(); 5.160 + } 5.161 + } 5.162 +} 5.163 +
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/threadpool.h Fri Dec 25 05:41:10 2015 +0200 6.3 @@ -0,0 +1,55 @@ 6.4 +#ifndef THREAD_POOL_H_ 6.5 +#define THREAD_POOL_H_ 6.6 + 6.7 +#include <list> 6.8 +#include <functional> 6.9 +#include <thread> 6.10 +#include <mutex> 6.11 +#include <condition_variable> 6.12 + 6.13 +class ThreadPool { 6.14 +private: 6.15 + int num_threads; 6.16 + std::thread *thread; // array of threads 6.17 + 6.18 + struct WorkItem { 6.19 + std::function<void ()> work; 6.20 + std::function<void ()> done; 6.21 + }; 6.22 + 6.23 + int qsize; 6.24 + std::list<WorkItem> workq; 6.25 + mutable std::mutex workq_mutex; 6.26 + std::condition_variable workq_condvar; 6.27 + 6.28 + int nactive; // number of active workers (not sleeping) 6.29 + 6.30 + // condvar used by wait 6.31 + std::condition_variable done_condvar; 6.32 + 6.33 + bool quit; 6.34 + 6.35 + void thread_func(); 6.36 + 6.37 +public: 6.38 + // passing num_threads == -1 auto-detects based on number of processors 6.39 + explicit ThreadPool(int num_threads = -1); 6.40 + ~ThreadPool(); 6.41 + 6.42 + void add_work(std::function<void ()> func); 6.43 + void add_work(std::function<void ()> work_func, std::function<void ()> done_func); 6.44 + void clear_work(); 6.45 + 6.46 + // returns the number of queued work items 6.47 + int queued() const; 6.48 + // returns the number of active threads 6.49 + int active() const; 6.50 + // returns number of pending work items (both in the queue and active) 6.51 + int pending() const; 6.52 + 6.53 + // waits for all work to be completed 6.54 + long wait(); 6.55 + long wait(long timeout); 6.56 +}; 6.57 + 6.58 +#endif // THREAD_POOL_H_