From efac8299150583342ef78eb1e33e2cdb73220792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petar=20Kapri=C5=A1?= Date: Fri, 23 Feb 2024 13:03:44 +0100 Subject: [PATCH] Initial commit --- Makefile | 42 ++++++++ README.md | 4 + visor.c | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 visor.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..567ef6e --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +VERSION = 0.0 + +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +CFLAGS = `pkg-config --cflags gtk4` +LDFLAGS = `pkg-config --libs gtk4` -lm + +CC = mpicc + +SRC = visor.c +OBJ = ${SRC:.c=.o} + +all: visor + +.c.o: + $(CC) -c $(CFLAGS) $< + +visor: $(OBJ) + $(CC) -o $@ $(OBJ) $(LDFLAGS) + +dist: clean + 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 visor $(OBJ) + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f visor $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/visor + #mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + #chmod 644 $(DESTDIR)$(MANPREFIX)/man1/visor.1 + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/visor#\ + #$(DESTDIR)$(MANPREFIX)/man1/visor.1 + +.PHONY: all options dist clean install uninstall diff --git a/README.md b/README.md new file mode 100644 index 0000000..61acac0 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Mandelbrot visualiser +A simple GTK application for drawing the mandelbrot set, meant to showcase some features of OpenMPI. + +This was written for a faculty project, and I will likely rewrite it to use pthreads soon. diff --git a/visor.c b/visor.c new file mode 100644 index 0000000..68ac1a9 --- /dev/null +++ b/visor.c @@ -0,0 +1,279 @@ +#include +#include +#include + +int procRank, commSz; +MPI_Datatype planeView_t; + +MPI_Win window; + +unsigned char *pixmap; + +cairo_surface_t *surface = NULL; + +#define SCR_DIMENSION 1000 + +struct planeView { + double scale; // scale is represented as unit/pixel + int width, height; + double centerX, centerY; +}; + +struct planeView mandelbrot = { 0.00000000001, 0, 0, 0.001643721971153, 0.822467633298876}; +// struct planeView mandelbrot = { 1, 0, 0, 0, 0}; + +#define MAX_ITERATION 1000 +#define ESC_RAD 20.0 +int color_lookup(int *r, int *g, int *b, double mu) +{ + static const int table16[16][3] = { + { 66, 30, 15 }, + { 25, 7, 26 }, + { 9, 1, 47 }, + { 4, 4, 73 }, + { 0, 7, 100 }, + { 12, 44, 138 }, + { 24, 82, 177 }, + { 57, 125, 209 }, + { 134, 181, 229 }, + { 211, 236, 248 }, + { 241, 233, 191 }, + { 248, 201, 95 }, + { 255, 170, 0 }, + { 204, 128, 0 }, + { 153, 87, 0 }, + { 106, 52, 3 } + }; + // *r = 0; // (1000.0-i)/1000.0*256.0; + // *g = 0; // (1000.0-i)/1000.0*256.0; + // *b = ((double) MAX_ITERATION - i - 1) / MAX_ITERATION * 255.0 - log(log(2)) / log(2); // (i/MAX_ITERATION) + int mod = (int) mu % 16; + *r = table16[mod][0]; + *g = table16[mod][1]; + *b = table16[mod][2]; + if (mu == 0 || mu >= MAX_ITERATION - 50) { + *r = *g = *b = 0; + } +} + +void color_from_iteration(int *r, int *g, int *b, double x0, double y0) +{ + double x = 0; + double y = 0; + int iteration = 0; + while (x*x + y*y <= ESC_RAD*ESC_RAD && iteration < MAX_ITERATION) { + double xtmp = xtmp = x*x - y*y + x0; + y = 2*x*y + y0; + x = xtmp; + iteration++; + } + // at this point iteration is the number of iterations required for the value to + // escape. X and Y contain the first escaped value + + double mu = (iteration + 1 - log(log(sqrt(x*x+y*y)))/log(2)); + if (mu < 0) + mu = 0; + if (mu > MAX_ITERATION) + mu = MAX_ITERATION; + + color_lookup(r, g, b, mu); + +#undef MAX_ITERATION +} + +void draw_mandelbrot(unsigned char* data, int height, int width, double planeCenterX, double planeCenterY, double scale) +{ + for (int i = 0; i < height; i++) { + double yRange = (double) i / height * 2.0 - 1.0; + double y = planeCenterY+yRange*scale; + for (int j = 0; j < width; j++) { + double xRange = (double) j / width * 2.0 - 1.0; + double x = planeCenterX+xRange*scale; + int r, g, b; + color_from_iteration(&r, &g, &b, x, y); + data[4*(i*width + j) + 2] = r; + data[4*(i*width + j) + 1] = g; + data[4*(i*width + j) + 0] = b; + } + } +} + +void create_surface(GtkWidget *widget) +{ + cairo_t *cr; + + if (surface) + cairo_surface_destroy(surface); + + surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, + gtk_widget_get_width(widget), + gtk_widget_get_height(widget)); + + cr = cairo_create(surface); + + cairo_surface_flush(surface); + int h = cairo_image_surface_get_height(surface); + int w = cairo_image_surface_get_width(surface); + mandelbrot.height = h; + mandelbrot.width = w; + for (int p = 1; p < commSz; p++) { + MPI_Send(&mandelbrot, 1, planeView_t, p, 0, MPI_COMM_WORLD); + } + pixmap = malloc(sizeof(unsigned char) * 4 * w * h); + MPI_Win_create(pixmap, 4*w*h, 1, + MPI_INFO_NULL, MPI_COMM_WORLD, &window); + MPI_Win_fence(0, window); + + unsigned char *data = cairo_image_surface_get_data(surface); + // draw_mandelbrot(data, h, w, mandelbrot.centerX, mandelbrot.centerY, mandelbrot.scale); + + + MPI_Win_fence(0, window); + memcpy(data, pixmap, 4*h*w); + cairo_surface_mark_dirty(surface); + // cairo_paint(cr); + + cairo_destroy(cr); +} + +void plane_resize(GtkWidget *widget, int width, int height) +{ + if (!surface) + create_surface(widget); +} + +void draw_plane(GtkDrawingArea *da, cairo_t *cr, int width, int height, gpointer data) +{ + cairo_set_source_surface(cr, surface, 0, 0); + cairo_paint(cr); +} + +static void app_activate(GApplication *app) +{ + GtkWidget *win; + GtkWidget *btn1; + GtkWidget *btn2; + GtkWidget *bigBox; + GtkWidget *vertBox; + GtkDrawingArea *da; + + 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); + + da = g_object_new(GTK_TYPE_DRAWING_AREA, + "accessible-role", GTK_ACCESSIBLE_ROLE_IMG, + NULL); + gtk_widget_set_hexpand(GTK_WIDGET(da), true); + gtk_drawing_area_set_content_width(GTK_DRAWING_AREA(da), 300); + gtk_drawing_area_set_content_height(GTK_DRAWING_AREA(da), 200); + gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(da), draw_plane, NULL, NULL); + g_signal_connect(da, "resize", G_CALLBACK(plane_resize), NULL); + + // 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)); +} + + + +int main(int argc, char **argv) +{ + MPI_Init(NULL, NULL); + + MPI_Comm_rank(MPI_COMM_WORLD, &procRank); + MPI_Comm_size(MPI_COMM_WORLD, &commSz); + + int blocklengths[5] = {1,1,1,1,1}; + const MPI_Aint displs[] = {0, sizeof(double), sizeof(double)+sizeof(int), + sizeof(double)+2*sizeof(int), 2*sizeof(double)+2*sizeof(int)}; + MPI_Datatype types[5] = {MPI_DOUBLE, MPI_INT, MPI_INT, MPI_DOUBLE, MPI_DOUBLE}; + MPI_Type_create_struct(5, blocklengths, displs, types, &planeView_t); + MPI_Type_commit(&planeView_t); + + if (commSz == 1) { + printf("This application needs to be run with more than one" + " process. Try using mpiexec.\n"); + MPI_Finalize(); + return -1; + } + + if (procRank == 0) { + int stat = 0; + + GtkApplication *app; + app = gtk_application_new("com.github.ToshioCP.pr1", + G_APPLICATION_DEFAULT_FLAGS); + g_signal_connect(app, "activate", + G_CALLBACK(app_activate), NULL); + stat = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); + + MPI_Abort(MPI_COMM_WORLD, 0); + MPI_Finalize(); + return stat; + } + + MPI_Recv(&mandelbrot, 1, planeView_t, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + pixmap = malloc(sizeof(unsigned char) * 4 * mandelbrot.width * mandelbrot.height); + MPI_Win_create(pixmap, 3*mandelbrot.width*mandelbrot.height, 1, + MPI_INFO_NULL, MPI_COMM_WORLD, &window); + MPI_Win_fence(0, window); + + int h = mandelbrot.height; + int w = mandelbrot.width; + int p = procRank - 1, c = commSz - 1; + int mod = h % c, div = h / c; + int a = MIN(p, mod)*(div+1) + (MAX(p, mod)-mod)*div, + b = p < mod ? a + div + 1 : a + div; + + + double centerX = mandelbrot.centerX; + double centerY = mandelbrot.centerY; + double scale = mandelbrot.scale; + + for (int i = a; i < b; i++) { + double yRange = (double) i / h * 2.0 - 1.0; + double y = centerY+yRange*scale; + for (int j = 0; j < w; j++) { + double xRange = (double) j / w * 2.0 - 1.0; + double x = centerX+xRange*scale; + + int r, g, b; + color_from_iteration(&r, &g, &b, x, y); + pixmap[4*(i*w + j) + 2] = r; + pixmap[4*(i*w + j) + 1] = g; + pixmap[4*(i*w + j) + 0] = b; + } + } + + MPI_Put(pixmap + 4*a*w, 4*(b-a)*w, MPI_UNSIGNED_CHAR, 0, 4*a*w, 4*(b-a)*w, MPI_UNSIGNED_CHAR, window); + MPI_Win_fence(0, window); + + + MPI_Finalize(); +}