summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libdimension/raytrace.c102
-rw-r--r--libdimension/threads.c51
-rw-r--r--libdimension/threads.h20
3 files changed, 93 insertions, 80 deletions
diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c
index ff89d6f..4e762f3 100644
--- a/libdimension/raytrace.c
+++ b/libdimension/raytrace.c
@@ -25,7 +25,6 @@
#include "dimension-impl.h"
#include <stdlib.h>
-#include <pthread.h>
/*
* Boilerplate for multithreading
@@ -36,22 +35,21 @@ typedef struct {
dmnsn_progress *progress;
dmnsn_scene *scene;
dmnsn_prtree *prtree;
-
- /* For multithreading */
- unsigned int index, threads;
} dmnsn_raytrace_payload;
-/** Background thread callback. */
-static int dmnsn_raytrace_scene_thread(void *ptr);
-
/* Raytrace a scene */
void
dmnsn_raytrace_scene(dmnsn_scene *scene)
{
dmnsn_progress *progress = dmnsn_raytrace_scene_async(scene);
- dmnsn_finish_progress(progress);
+ if (dmnsn_finish_progress(progress) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_HIGH, "Error occured while raytracing.");
+ }
}
+/** Background thread callback. */
+static int dmnsn_raytrace_scene_thread(void *ptr);
+
/* Raytrace a scene in the background */
dmnsn_progress *
dmnsn_raytrace_scene_async(dmnsn_scene *scene)
@@ -69,7 +67,8 @@ dmnsn_raytrace_scene_async(dmnsn_scene *scene)
}
/** Worker thread callback. */
-static void *dmnsn_raytrace_scene_multithread_thread(void *ptr);
+static int dmnsn_raytrace_scene_concurrent(void *ptr, unsigned int thread,
+ unsigned int nthreads);
/* Thread callback -- set up the multithreaded engine */
static int
@@ -85,82 +84,19 @@ dmnsn_raytrace_scene_thread(void *ptr)
payload->prtree = dmnsn_new_prtree(payload->scene->objects);
dmnsn_complete_timer(payload->scene->bounding_timer);
- dmnsn_raytrace_payload *payloads;
- pthread_t *threads;
-
- int nthreads = payload->scene->nthreads;
- /* Sanity check */
- if (nthreads < 1)
- nthreads = 1;
-
- payloads = dmnsn_malloc(nthreads*sizeof(dmnsn_raytrace_payload));
- threads = dmnsn_malloc(nthreads*sizeof(pthread_t));
-
/* Set up the progress object */
dmnsn_set_progress_total(payload->progress, payload->scene->canvas->height);
- /* Create the payloads */
- for (int i = 0; i < nthreads; ++i) {
- payloads[i] = *payload;
- payloads[i].index = i;
- payloads[i].threads = nthreads;
- }
-
/* Time the render itself */
payload->scene->render_timer = dmnsn_new_timer();
- /* Create the threads */
- for (int i = 0; i < nthreads; ++i) {
- if (pthread_create(&threads[i], NULL,
- &dmnsn_raytrace_scene_multithread_thread,
- &payloads[i]) != 0)
- {
- dmnsn_error(DMNSN_SEVERITY_HIGH,
- "Couldn't start worker thread in raytrace engine.");
- }
- }
-
- /* Join the rest of the threads after detecting an error, so we don't
- free anything out from under them */
- bool join_failed = false;
- bool error_seen = false;
- for (int i = 0; i < nthreads; ++i) {
- void *ptr = NULL;
- if (pthread_join(threads[i], &ptr) != 0)
- join_failed = true;
- if (!ptr)
- error_seen = true;
- }
- if (join_failed) {
- dmnsn_error(DMNSN_SEVERITY_MEDIUM,
- "Couldn't join worker thread in raytrace engine.");
- }
- if (error_seen) {
- dmnsn_error(DMNSN_SEVERITY_HIGH, "Error occurred in worker thread.");
- }
+ int ret = dmnsn_execute_concurrently(&dmnsn_raytrace_scene_concurrent,
+ payload, payload->scene->nthreads);
dmnsn_complete_timer(payload->scene->render_timer);
- dmnsn_free(threads);
- dmnsn_free(payloads);
dmnsn_delete_prtree(payload->prtree);
dmnsn_free(payload);
- return 0;
-}
-
-/** Actual raytracing implementation. */
-static void dmnsn_raytrace_scene_impl(dmnsn_progress *progress,
- dmnsn_scene *scene,
- dmnsn_prtree *prtree,
- unsigned int index, unsigned int threads);
-
-/* Multi-threading thread callback */
-static void *
-dmnsn_raytrace_scene_multithread_thread(void *ptr)
-{
- dmnsn_raytrace_payload *payload = ptr;
- dmnsn_raytrace_scene_impl(payload->progress, payload->scene,
- payload->prtree, payload->index, payload->threads);
- return payload; /* Return non-NULL for success */
+ return ret;
}
/*
@@ -192,11 +128,15 @@ static dmnsn_color dmnsn_raytrace_shoot(dmnsn_raytrace_state *state,
dmnsn_line ray);
/* Actually raytrace a scene */
-static void
-dmnsn_raytrace_scene_impl(dmnsn_progress *progress, dmnsn_scene *scene,
- dmnsn_prtree *prtree,
- unsigned int index, unsigned int threads)
+static int
+dmnsn_raytrace_scene_concurrent(void *ptr, unsigned int thread,
+ unsigned int nthreads)
{
+ const dmnsn_raytrace_payload *payload = ptr;
+ dmnsn_progress *progress = payload->progress;
+ dmnsn_scene *scene = payload->scene;
+ dmnsn_prtree *prtree = payload->prtree;
+
dmnsn_raytrace_state state = {
.parent = NULL,
.scene = scene,
@@ -204,7 +144,7 @@ dmnsn_raytrace_scene_impl(dmnsn_progress *progress, dmnsn_scene *scene,
};
/* Iterate through each pixel */
- for (size_t y = index; y < scene->canvas->height; y += threads) {
+ for (size_t y = thread; y < scene->canvas->height; y += nthreads) {
for (size_t x = 0; x < scene->canvas->width; ++x) {
/* Get the ray corresponding to the (x,y)'th pixel */
dmnsn_line ray = dmnsn_camera_ray(
@@ -223,6 +163,8 @@ dmnsn_raytrace_scene_impl(dmnsn_progress *progress, dmnsn_scene *scene,
dmnsn_increment_progress(progress);
}
+
+ return 0;
}
/** Get the intersection texture. */
diff --git a/libdimension/threads.c b/libdimension/threads.c
index 5e39fdc..35127ac 100644
--- a/libdimension/threads.c
+++ b/libdimension/threads.c
@@ -71,3 +71,54 @@ dmnsn_new_thread(dmnsn_progress *progress, dmnsn_thread_fn *thread_fn,
dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't start thread.");
}
}
+
+typedef struct dmnsn_concurrent_thread_payload {
+ dmnsn_concurrent_thread_fn *thread_fn;
+ void *arg;
+ unsigned int thread, nthreads;
+ int ret;
+} dmnsn_concurrent_thread_payload;
+
+static void *
+dmnsn_concurrent_thread(void *ptr)
+{
+ dmnsn_concurrent_thread_payload *payload = ptr;
+ payload->ret = (*payload->thread_fn)(payload->arg, payload->thread,
+ payload->nthreads);
+ return NULL;
+}
+
+int
+dmnsn_execute_concurrently(dmnsn_concurrent_thread_fn *thread_fn,
+ void *arg, unsigned int nthreads)
+{
+ pthread_t threads[nthreads];
+ dmnsn_concurrent_thread_payload payloads[nthreads];
+
+ for (unsigned int i = 0; i < nthreads; ++i) {
+ payloads[i].thread_fn = thread_fn;
+ payloads[i].arg = arg;
+ payloads[i].thread = i;
+ payloads[i].nthreads = nthreads;
+ payloads[i].ret = -1;
+ if (pthread_create(&threads[i], NULL, &dmnsn_concurrent_thread,
+ &payloads[i]) != 0)
+ {
+ dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't start worker thread.");
+ }
+ }
+
+ int ret = 0;
+ for (unsigned int i = 0; i < nthreads; ++i) {
+ if (pthread_join(threads[i], NULL) == 0) {
+ if (payloads[i].ret != 0) {
+ ret = payloads[i].ret;
+ }
+ } else {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't join worker thread.");
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
diff --git a/libdimension/threads.h b/libdimension/threads.h
index 7ae6288..fdf2df7 100644
--- a/libdimension/threads.h
+++ b/libdimension/threads.h
@@ -34,6 +34,16 @@
typedef int dmnsn_thread_fn(void *ptr);
/**
+ * Thread callback type for parallel tasks.
+ * @param[in,out] ptr An arbitrary pointer.
+ * @param[in] thread An ID for this thread, in [0, \p nthreads).
+ * @param[in] nthreads The number of concurrent threads.
+ * @return 0 on success, non-zero on failure.
+ */
+typedef int dmnsn_concurrent_thread_fn(void *ptr, unsigned int thread,
+ unsigned int nthreads);
+
+/**
* Create a thread that cleans up after itself on errors.
* @param[in,out] progress The progress object to associate with the thread.
* @param[in] thread_fn The thread callback.
@@ -42,4 +52,14 @@ typedef int dmnsn_thread_fn(void *ptr);
void dmnsn_new_thread(dmnsn_progress *progress, dmnsn_thread_fn *thread_fn,
void *arg);
+/**
+ * Run \p nthreads threads in parallel.
+ * @param[in] thread_fn The routine to run in each concurrent thread.
+ * @param[in,out] arg The pointer to pass to the thread callbacks.
+ * @param[in] nthreads The number of concurrent threads to run.
+ * @return 0 if all threads were successful, and an error code otherwise.
+ */
+int dmnsn_execute_concurrently(dmnsn_concurrent_thread_fn *thread_fn,
+ void *arg, unsigned int nthreads);
+
#endif /* DIMENSION_IMPL_THREADS_H */