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_