Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 9413841222 |
4 changed files with 60 additions and 49 deletions
26
Makefile
26
Makefile
|
|
@ -8,35 +8,35 @@ LDFLAGS = `pkg-config --libs gtk4` -lm -lpthread
|
|||
|
||||
CC = gcc
|
||||
|
||||
SRC = mandelbrot-visualizer.c
|
||||
SRC = visor.c
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
||||
all: mandelbrot-visualizer
|
||||
all: visor
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
|
||||
mandelbrot-visualizer: $(OBJ)
|
||||
visor: $(OBJ)
|
||||
$(CC) -o $@ $(OBJ) $(LDFLAGS)
|
||||
|
||||
dist: clean
|
||||
mkdir -p mandelbrot-visualizer-$(VERSION)
|
||||
cp -R Makefile $(SRC) mandelbrot-visualizer-$(VERSION) # Add other files once repo changes
|
||||
tar -cf - mandelbrot-visualizer-$(VERSION) | gzip > mandelbrot-visualizer-$(VERSION).tar.gz
|
||||
rm -rf mandelbrot-visualizer-$(VERSION)
|
||||
mkdir -p visor-$(VERSION)
|
||||
cp -R Makefile $(SRC) visor-$(VERSION) # Add other files once repo changes
|
||||
tar -cf - visor-$(VERSION) | gzip > visor-$(VERSION).tar.gz
|
||||
rm -rf visor-$(VERSION)
|
||||
|
||||
clean:
|
||||
rm -f mandelbrot-visualizer $(OBJ)
|
||||
rm -f visor $(OBJ)
|
||||
|
||||
install: all
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||
cp -f mandelbrot-visualizer $(DESTDIR)$(PREFIX)/bin
|
||||
chmod 755 $(DESTDIR)$(PREFIX)/bin/mandelbrot-visualizer
|
||||
cp -f visor $(DESTDIR)$(PREFIX)/bin
|
||||
chmod 755 $(DESTDIR)$(PREFIX)/bin/visor
|
||||
#mkdir -p $(DESTDIR)$(MANPREFIX)/man1
|
||||
#chmod 644 $(DESTDIR)$(MANPREFIX)/man1/mandelbrot-visualizer.1
|
||||
#chmod 644 $(DESTDIR)$(MANPREFIX)/man1/visor.1
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/mandelbrot-visualizer#\
|
||||
#$(DESTDIR)$(MANPREFIX)/man1/mandelbrot-visualizer.1
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/visor#\
|
||||
#$(DESTDIR)$(MANPREFIX)/man1/visor.1
|
||||
|
||||
.PHONY: all options dist clean install uninstall
|
||||
|
|
|
|||
BIN
visor
Executable file
BIN
visor
Executable file
Binary file not shown.
|
|
@ -10,9 +10,7 @@
|
|||
|
||||
#define DEFAULT_THREADS 4
|
||||
#define MAX_THREADS 32
|
||||
// Stringizing requires extra step for the constants to get replaced before stringizing
|
||||
#define _STR(x) #x
|
||||
#define STR(x) _STR(x)
|
||||
#define STR(x) #x
|
||||
|
||||
|
||||
|
||||
|
|
@ -30,24 +28,15 @@ struct threadInfo {
|
|||
|
||||
/*
|
||||
* Concurrency model explained:
|
||||
* One main thread, along with many workers in a thread pool, who split the work of
|
||||
* rendering roughly equally. The workers don't do any work until the pixel map which they
|
||||
* are meant to work on is marked as available, and they are all singalled to start
|
||||
* drawing.
|
||||
* One reader thread (the GUI thread) of the pixel buffer, along with many
|
||||
* writers, who split the work of rendering roughly equally. The writers don't
|
||||
* do any work until the pixel map which they are meant to work on is marked as
|
||||
* available.
|
||||
*
|
||||
* Once it is marked as such, this means that the planeView structure is well
|
||||
* defined, that pixmap points to a memory region, which is allocated with
|
||||
* enough memory for the plain view, and they can all start writing without an
|
||||
* issue.
|
||||
*
|
||||
* Once they are all finished, the last thread to exit the drawing function will signal
|
||||
* the main thread to "blit" the finished image to the screen.
|
||||
*
|
||||
* If any kind of change by the user happens (window resize, panning/zooming (TODO: not
|
||||
* implemented yet), the main thread will cause all worker threads to stop working, if
|
||||
* they haven't already (they occassionally poll while drawing to see if they should
|
||||
* stop). Once they've all stopped, the main thread will update and replace the drawing
|
||||
* area, and naturally, signal them to start drawing again.
|
||||
*/
|
||||
|
||||
pthread_mutex_t pixmapMutex;
|
||||
|
|
@ -84,7 +73,7 @@ void color_lookup(int *r, int *g, int *b, double mu);
|
|||
// worker code
|
||||
gboolean queue_redraw_plane(void *arg);
|
||||
|
||||
void *worker_thread(void *arg)
|
||||
void *writer_thread(void *arg)
|
||||
{
|
||||
struct threadInfo *info = arg;
|
||||
while (true) {
|
||||
|
|
@ -121,7 +110,7 @@ void draw_mandelbrot(struct threadInfo *info)
|
|||
pthread_mutex_unlock(&pixmapMutex);
|
||||
return;
|
||||
// after this, the thread will again enter the
|
||||
// loop in the worker_thread function, and
|
||||
// loop in the writer_thread function, and
|
||||
// enter a wait state
|
||||
}
|
||||
pthread_mutex_unlock(&pixmapMutex);
|
||||
|
|
@ -138,13 +127,13 @@ void draw_mandelbrot(struct threadInfo *info)
|
|||
}
|
||||
pthread_mutex_lock(&pixmapMutex);
|
||||
info->complete = true;
|
||||
bool allWorkersComplete = true;
|
||||
bool allWritersComplete = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (!threads[i].complete) {
|
||||
allWorkersComplete = false;
|
||||
allWritersComplete = false;
|
||||
}
|
||||
}
|
||||
if (allWorkersComplete) {
|
||||
if (allWritersComplete) {
|
||||
cairo_surface_mark_dirty(surface);
|
||||
g_main_context_invoke(NULL, queue_redraw_plane, NULL);
|
||||
}
|
||||
|
|
@ -259,7 +248,7 @@ int main(int argc, char **argv)
|
|||
// integer, if the thread fails to start, it will simply be
|
||||
// overwritten by the same value in the next run of the loop
|
||||
pthread_t id;
|
||||
if (pthread_create(&id, NULL, &worker_thread, &threads[count])) {
|
||||
if (pthread_create(&id, NULL, &writer_thread, &threads[count])) {
|
||||
pthread_detach(id);
|
||||
fprintf(stderr, "Failed to create thread %d\n", i);
|
||||
continue;
|
||||
|
|
@ -284,7 +273,7 @@ int main(int argc, char **argv)
|
|||
app = gtk_application_new("cool.bonsai.mandelbrot-visualizer",
|
||||
G_APPLICATION_DEFAULT_FLAGS);
|
||||
g_signal_connect(app, "activate", G_CALLBACK(app_activate), NULL);
|
||||
stat = g_application_run(G_APPLICATION(app), 0, NULL);
|
||||
stat = g_application_run(G_APPLICATION(app), 1, argv);
|
||||
g_object_unref(app);
|
||||
|
||||
return stat;
|
||||
|
|
@ -306,12 +295,33 @@ GtkDrawingArea *da;
|
|||
static void app_activate(GApplication *app)
|
||||
{
|
||||
GtkWidget *win;
|
||||
GtkWidget *btn1;
|
||||
GtkWidget *btn2;
|
||||
GtkWidget *bigBox;
|
||||
GtkWidget *vertBox;
|
||||
|
||||
win = gtk_application_window_new(GTK_APPLICATION(app));
|
||||
gtk_window_set_title(GTK_WINDOW(win), "Mandelbrot visualiser");
|
||||
gtk_window_set_default_size(GTK_WINDOW(win), 800, 600);
|
||||
|
||||
btn1 = gtk_button_new_with_label("A");
|
||||
gtk_widget_set_size_request(GTK_WIDGET(btn1), 20, 20);
|
||||
gtk_widget_set_margin_bottom(GTK_WIDGET(btn1), 5);
|
||||
gtk_widget_set_margin_top(GTK_WIDGET(btn1), 5);
|
||||
gtk_widget_set_margin_start(GTK_WIDGET(btn1), 5);
|
||||
gtk_widget_set_margin_end(GTK_WIDGET(btn1), 5);
|
||||
btn2 = gtk_button_new_with_label("B");
|
||||
gtk_widget_set_size_request(GTK_WIDGET(btn2), 20, 20);
|
||||
gtk_widget_set_margin_bottom(GTK_WIDGET(btn2), 5);
|
||||
gtk_widget_set_margin_top(GTK_WIDGET(btn2), 5);
|
||||
gtk_widget_set_margin_start(GTK_WIDGET(btn2), 5);
|
||||
gtk_widget_set_margin_end(GTK_WIDGET(btn2), 5);
|
||||
|
||||
vertBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
||||
gtk_widget_set_valign(GTK_WIDGET(vertBox), GTK_ALIGN_START);
|
||||
gtk_box_append(GTK_BOX(vertBox), btn1);
|
||||
gtk_box_append(GTK_BOX(vertBox), btn2);
|
||||
|
||||
bigBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
|
||||
// global to enable other functions to call for a redraw
|
||||
|
|
@ -327,6 +337,7 @@ static void app_activate(GApplication *app)
|
|||
// TODO: implement later drag = gtk_gesture_drag_new();
|
||||
|
||||
gtk_box_append(GTK_BOX(bigBox), GTK_WIDGET(da));
|
||||
gtk_box_append(GTK_BOX(bigBox), vertBox);
|
||||
gtk_window_set_child(GTK_WINDOW(win), bigBox);
|
||||
|
||||
gtk_window_present(GTK_WINDOW(win));
|
||||
|
|
@ -335,15 +346,15 @@ static void app_activate(GApplication *app)
|
|||
gboolean queue_redraw_plane(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
bool allWorkersHadCompleted = true;
|
||||
bool allWritersHadCompleted = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (!threads[i].complete) {
|
||||
allWorkersHadCompleted = false;
|
||||
allWritersHadCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allWorkersHadCompleted) {
|
||||
if (!allWritersHadCompleted) {
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
|
|
@ -357,14 +368,14 @@ void blit_plane(GtkDrawingArea *da, cairo_t *cr, int width, int height, gpointer
|
|||
(void) width;
|
||||
(void) height;
|
||||
(void) data;
|
||||
bool allWorkersHadCompleted = true;
|
||||
bool allWritersHadCompleted = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (!threads[i].complete) {
|
||||
allWorkersHadCompleted = false;
|
||||
allWritersHadCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allWorkersHadCompleted) {
|
||||
if (allWritersHadCompleted) {
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_paint(cr);
|
||||
}
|
||||
|
|
@ -382,27 +393,27 @@ void create_surface(GtkWidget *widget)
|
|||
pixmapAvailable = false; // Mark drawing area as unavailable
|
||||
pthread_mutex_unlock(&pixmapMutex);
|
||||
|
||||
bool allWorkersStopped;
|
||||
bool allWritersStopped;
|
||||
do { // Wait until all writing threads have been stopped
|
||||
allWorkersStopped = true;
|
||||
allWritersStopped = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (threads[i].drawing) {
|
||||
allWorkersStopped = false;
|
||||
allWritersStopped = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!allWorkersStopped);
|
||||
} while (!allWritersStopped);
|
||||
|
||||
// If all drawers hadn't completed, we need to call
|
||||
// cairo_surface_mark_dirty in the main thread
|
||||
bool allWorkersHadCompleted = true;
|
||||
bool allWritersHadCompleted = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (!threads[i].complete) {
|
||||
allWorkersHadCompleted = false;
|
||||
allWritersHadCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allWorkersHadCompleted && surface != NULL) {
|
||||
if (!allWritersHadCompleted && surface != NULL) {
|
||||
cairo_surface_mark_dirty(surface);
|
||||
}
|
||||
|
||||
BIN
visor.o
Normal file
BIN
visor.o
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue