multi-threaded rendering via pthreads pyrit
authorRadek Brich <radek.brich@devl.cz>
Sat, 17 Nov 2007 17:20:36 +0100
branchpyrit
changeset 4 c73bc405ee7a
parent 3 8f9cb0526c47
child 5 2d97ea5e711a
multi-threaded rendering via pthreads added README
.bzrignore
Makefile
README
TODO
src/raytracer.cc
src/raytracer.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.bzrignore	Sat Nov 17 17:20:36 2007 +0100
@@ -0,0 +1,1 @@
+demos/*.png
--- a/Makefile	Fri Nov 16 10:25:12 2007 +0100
+++ b/Makefile	Sat Nov 17 17:20:36 2007 +0100
@@ -1,4 +1,4 @@
-CCFLAGS=-Wall -O3 -I./src
+CCFLAGS=-Wall -O3 -I./src -DPTHREADS -pthread -fno-strict-aliasing
 LDFLAGS=
 
 ifeq ($(OS), Windows_NT)
@@ -10,7 +10,7 @@
   MODULENAME=raytracermodule.so
 endif
 
-# optimisations
+# optimizations
 #CCFLAGS+=-pipe -fomit-frame-pointer -ffast-math -msse3
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Sat Nov 17 17:20:36 2007 +0100
@@ -0,0 +1,11 @@
+==================
+ Pyrit Ray Tracer
+==================
+
+Pthreads
+--------
+Threads can be used to render rows of picture paralelly. Arbitrary number
+of threads can be used, even numbers like 17 are acceptable.
+
+To completely disable this feature just remove "-DPTHREADS -pthreads"
+from flags in makefile.
--- a/TODO	Fri Nov 16 10:25:12 2007 +0100
+++ b/TODO	Sat Nov 17 17:20:36 2007 +0100
@@ -1,4 +1,4 @@
- * pthreads (pro Windows http://sources.redhat.com/pthreads-win32/)
+ * pthreads for Windows: http://sources.redhat.com/pthreads-win32/
  * kd-tree
 
 
--- a/src/raytracer.cc	Fri Nov 16 10:25:12 2007 +0100
+++ b/src/raytracer.cc	Sat Nov 17 17:20:36 2007 +0100
@@ -5,6 +5,10 @@
  * Radek Brich, 2006
  */
 
+#ifdef PTHREADS
+#include <pthread.h>
+#endif
+
 #include <stdio.h>
 #include <malloc.h>
 #include <float.h>
@@ -165,10 +169,47 @@
 	}
 }
 
+static void *renderrow(void *data)
+{
+	RenderrowData *d = (RenderrowData*) data;
+	for (int x = 0; x < d->w; x++) {
+		// generate a ray from eye passing through this pixel
+#if 1
+		// no oversampling
+		Vector3 dir = Vector3(d->vx, d->vy, 0) - d->eye;
+		dir.normalize();
+		Ray ray(d->eye, dir);
+		Colour c = d->rt->raytrace(ray, 0, NULL);
+#else
+		// 5x oversampling
+		Vector3 dir = Vector3();
+		Colour c = Colour();
+
+		for (int i = 0; i < 5; i++)
+		{
+			float osax[] = {0.0, -0.4, +0.4, +0.4, -0.4};
+			float osay[] = {0.0, -0.4, -0.4, +0.4, +0.4};
+			dir = Vector3(d->vx + osax[i] * d->dx,
+				d->vy + osay[i]*d->dy , 0) - d->eye;
+			dir.normalize();
+			Ray ray(d->eye, dir);
+			c += d->rt->raytrace(ray, 0, NULL);
+		}
+		c = c * (1./5);
+#endif
+		*(d->iter++) = c.r;
+		*d->iter++ = c.g;
+		*d->iter++ = c.b;
+		d->vx += d->dx;
+	}
+#ifdef PTHREADS
+	pthread_exit((void *)d);
+#endif
+}
+
 float *Raytracer::render(int w, int h)
 {
-	int x, y;
-	float *data, *iter;
+	float *data;
 
 	data = (float *) malloc(w*h*3*sizeof(float));
 	if (!data)
@@ -176,7 +217,8 @@
 
 	float startx = -1.0 * 4, starty = (float)h/w * 4;
 	float dx = -2*startx/w, dy = -2*starty/h;
-	float vx, vy;
+	float vy;
+	RenderrowData *d;
 
 	//srand(time(NULL));
 
@@ -184,43 +226,59 @@
 	Vector3 eye(0, 0, -5);
 
 	// for each pixel...
-	iter = data;
-	for (vy = starty, y = 0; y < h; y++) {
-		vx = startx;
-		for (x = 0; x < w; x++) {
-			// generate a ray from eye passing through this pixel
-#if 1
-			// no oversampling
-			Vector3 dir = Vector3(vx, vy, 0) - eye;
-			dir.normalize();
-			Ray ray(eye, dir);
-			Colour c = raytrace(ray, 0, NULL);
-#else
-			// 5x oversampling
-			Vector3 dir = Vector3();
-			Colour c = Colour();
+	vy = starty;
+
+#ifdef PTHREADS
+	int num_threads = 20;
+	printf("* pthreads enabled, using %d threads\n", num_threads);
+	pthread_t threads[num_threads];
+	for (int t = 0; t < num_threads; t++)
+		threads[t] = NULL;
+	int t = 0;
+#endif
 
-			for (int i = 0; i < 5; i++)
-			{
-				float osax[] = {0.0, -0.4, +0.4, +0.4, -0.4};
-				float osay[] = {0.0, -0.4, -0.4, +0.4, +0.4};
-				dir = Vector3(vx + osax[i]*dx,
-					vy + osay[i]*dy , 0) - eye;
-				dir.normalize();
-				Ray ray(eye, dir);
-				c += raytrace(ray, 0, NULL);
-			}
-			c = c * (1./5);
+	printf("* rendering row   0 (  0% done)");
+	for (int y = 0; y < h; y++) {
+		d = (RenderrowData*) malloc(sizeof(RenderrowData));
+		d->rt = this;
+		d->w = w;
+		d->vx = startx;
+		d->vy = vy;
+		d->dx = dx;
+		d->dy = dy;
+		d->eye = eye;
+		d->iter = data + y*3*w;
+#ifdef PTHREADS
+		/* create new thread and increase 't' */
+		int rc = pthread_create(&threads[t++], NULL, renderrow, (void *)d);
+		if (rc) {
+			printf("\nERROR: return code from pthread_create() is %d\n", rc);
+			exit(1);
+		}
+		/* when 't' owerflows, reset it */
+		if (t >= num_threads)
+			t = 0;
+		/* wait for next thread in fifo queue, so the descriptor can be reused;
+		   this also limits number of running threads */
+		if (threads[t] != NULL)
+			if (pthread_join(threads[t], (void**)&d) == 0)
+				free(d);
+#else
+		renderrow((void *)d);
+		free(d);
 #endif
-			*iter++ = c.r;
-			*iter++ = c.g;
-			*iter++ = c.b;
-			vx += dx;
-		}
 		vy += dy;
-		printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%2d%% done (row %4d)", y*100/(h-1), y);
+		printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4d (%2d%% done)", y, y*100/(h-1));
 	}
 	printf("\n");
+
+#ifdef PTHREADS
+	printf("* waiting for threads to finish\n");
+	for (t = 0; t < num_threads; t++)
+		if (pthread_join(threads[t], (void**)&d) == 0)
+			free(d);
+#endif
+
 	return data;
 }
 
--- a/src/raytracer.h	Fri Nov 16 10:25:12 2007 +0100
+++ b/src/raytracer.h	Sat Nov 17 17:20:36 2007 +0100
@@ -24,6 +24,16 @@
 	AABB extends() { return AABB(); };
 };
 
+class Raytracer;
+struct RenderrowData {
+	Raytracer *rt;
+	int w;
+	float vx, vy, dx, dy;
+	Vector3 eye;
+	float *iter;
+};
+static void *renderrow(void *data);
+
 class Raytracer
 {
 	ShapeList shapes;
@@ -35,11 +45,11 @@
 	Vector3 SphereDistribute(int i, int n, float extent, Vector3 &normal);
 	inline Shape *nearest_intersection(const Shape *origin_shape, const Ray &ray,
 		float &nearest_distance);
-	Colour raytrace(Ray &ray, int depth, Shape *origin_shape);
 public:
 	Raytracer(): shapes(), lights(), bg_colour(0.0, 0.0, 0.0), ao_samples(0) {};
 	~Raytracer() {};
 	float *render(int w, int h);
+	Colour raytrace(Ray &ray, int depth, Shape *origin_shape);
 	void addshape(Shape *shape);
 	void addlight(Light *light);