Compare commits
13 commits
efac829915
...
8ad76abac9
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8ad76abac9 | ||
![]() |
f5be463c05 | ||
![]() |
64f921eff7 | ||
![]() |
99fc7ce405 | ||
![]() |
96087d17a1 | ||
![]() |
94176c8d7c | ||
![]() |
7f73925769 | ||
![]() |
270c00cefc | ||
![]() |
9b19884286 | ||
![]() |
ea699e5c48 | ||
![]() |
4b150c8097 | ||
![]() |
ba685cc0ca | ||
![]() |
2a3fee2c3f |
3 changed files with 339 additions and 167 deletions
6
Makefile
6
Makefile
|
@ -3,10 +3,10 @@ VERSION = 0.0
|
|||
PREFIX = /usr/local
|
||||
MANPREFIX = $(PREFIX)/share/man
|
||||
|
||||
CFLAGS = `pkg-config --cflags gtk4`
|
||||
LDFLAGS = `pkg-config --libs gtk4` -lm
|
||||
CFLAGS = `pkg-config --cflags gtk4` -std=c99 -Wall -Wextra -g -O0
|
||||
LDFLAGS = `pkg-config --libs gtk4` -lm -lpthread
|
||||
|
||||
CC = mpicc
|
||||
CC = gcc
|
||||
|
||||
SRC = visor.c
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# Mandelbrot visualiser
|
||||
A simple GTK application for drawing the mandelbrot set, meant to showcase some features of OpenMPI.
|
||||
A simple GTK application for drawing the mandelbrot set.
|
||||
|
||||
This was originally written to showcase some features of OpenMPI for a school
|
||||
project, I've since rewritten it using pthreads.
|
||||
|
||||
This was written for a faculty project, and I will likely rewrite it to use pthreads soon.
|
||||
|
|
494
visor.c
494
visor.c
|
@ -1,30 +1,172 @@
|
|||
#include <gtk/gtk.h>
|
||||
#include <mpi.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <complex.h>
|
||||
#include <pthread.h>
|
||||
|
||||
int procRank, commSz;
|
||||
MPI_Datatype planeView_t;
|
||||
#include <gtk/gtk.h>
|
||||
#include <cairo.h>
|
||||
|
||||
MPI_Win window;
|
||||
#define DEFAULT_THREADS 4
|
||||
#define MAX_THREADS 32
|
||||
#define STR(x) #x
|
||||
|
||||
unsigned char *pixmap;
|
||||
|
||||
|
||||
/******* Program data structures *******/
|
||||
|
||||
|
||||
|
||||
int32_t thread_count;
|
||||
|
||||
struct threadInfo {
|
||||
int32_t index; // unique number from 0 to thread_count-1
|
||||
bool drawing; // marks if we are currently drawing for a given thread
|
||||
bool complete; // marks if the drawing that it was supposed to
|
||||
};
|
||||
|
||||
/*
|
||||
* Concurrency model explained:
|
||||
* 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.
|
||||
*/
|
||||
|
||||
pthread_mutex_t pixmapMutex;
|
||||
pthread_cond_t pixmapCond;
|
||||
|
||||
struct threadInfo threads[MAX_THREADS];
|
||||
// this will store an array with status info for all the threads
|
||||
|
||||
cairo_surface_t *surface = NULL;
|
||||
|
||||
#define SCR_DIMENSION 1000
|
||||
|
||||
bool pixmapAvailable = false;
|
||||
unsigned char *pixmap;
|
||||
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};
|
||||
// struct planeView mandelbrot = { 0.00000000001, 0, 0, 0.001643721971153, 0.822467633298876};
|
||||
struct planeView mandelbrot = { 1, 0, 0, 0, 0};
|
||||
|
||||
|
||||
|
||||
/******* Worker thread code *******/
|
||||
|
||||
|
||||
|
||||
void draw_mandelbrot(struct threadInfo *info);
|
||||
|
||||
void color_from_iteration(int *r, int *g, int *b, double x0, double y0);
|
||||
void color_lookup(int *r, int *g, int *b, double mu);
|
||||
|
||||
// This function is from the main thread, but it needs to be referenced by
|
||||
// worker code
|
||||
gboolean queue_redraw_plane(void *arg);
|
||||
|
||||
void *writer_thread(void *arg)
|
||||
{
|
||||
struct threadInfo *info = arg;
|
||||
while (true) {
|
||||
pthread_mutex_lock(&pixmapMutex);
|
||||
while (!pixmapAvailable || info->complete) {
|
||||
pthread_cond_wait(&pixmapCond, &pixmapMutex);
|
||||
}
|
||||
pthread_mutex_unlock(&pixmapMutex);
|
||||
info->drawing = true;
|
||||
draw_mandelbrot(info);
|
||||
info->drawing = false;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_mandelbrot(struct threadInfo *info)
|
||||
{
|
||||
int h = mandelbrot.height;
|
||||
int w = mandelbrot.width;
|
||||
int p = info->index, c = thread_count;
|
||||
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;
|
||||
// [a..b) is the range of rows a given thread is meant to write pixels
|
||||
|
||||
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;
|
||||
pthread_mutex_lock(&pixmapMutex);
|
||||
if (!pixmapAvailable) {
|
||||
pthread_mutex_unlock(&pixmapMutex);
|
||||
return;
|
||||
// after this, the thread will again enter the
|
||||
// loop in the writer_thread function, and
|
||||
// enter a wait state
|
||||
}
|
||||
pthread_mutex_unlock(&pixmapMutex);
|
||||
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;
|
||||
}
|
||||
}
|
||||
pthread_mutex_lock(&pixmapMutex);
|
||||
info->complete = true;
|
||||
bool allWritersComplete = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (!threads[i].complete) {
|
||||
allWritersComplete = false;
|
||||
}
|
||||
}
|
||||
if (allWritersComplete) {
|
||||
cairo_surface_mark_dirty(surface);
|
||||
g_main_context_invoke(NULL, queue_redraw_plane, NULL);
|
||||
}
|
||||
pthread_mutex_unlock(&pixmapMutex);
|
||||
}
|
||||
|
||||
#define MAX_ITERATION 1000
|
||||
#define ESC_RAD 20.0
|
||||
int color_lookup(int *r, int *g, int *b, double mu)
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
void color_lookup(int *r, int *g, int *b, double mu)
|
||||
{
|
||||
static const int table16[16][3] = {
|
||||
{ 66, 30, 15 },
|
||||
|
@ -55,98 +197,100 @@ int color_lookup(int *r, int *g, int *b, double mu)
|
|||
*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)
|
||||
|
||||
|
||||
/******* Main thread code *******/
|
||||
|
||||
|
||||
|
||||
void print_usage(FILE *stream);
|
||||
static void app_activate(GApplication *app);
|
||||
|
||||
void blit_plane(GtkDrawingArea *da, cairo_t *cr, int width, int height, gpointer data);
|
||||
void plane_resize(GtkWidget *widget);
|
||||
void create_surface(GtkWidget *widget);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
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;
|
||||
pthread_mutex_init(&pixmapMutex, NULL);
|
||||
pthread_cond_init(&pixmapCond, NULL);
|
||||
|
||||
if (argc == 1) {
|
||||
thread_count = 4;
|
||||
} else if (argc == 2 && !strcmp(argv[1], "-h")) {
|
||||
print_usage(stdout);
|
||||
exit(0);
|
||||
} else if (argc == 2) {
|
||||
char *endptr;
|
||||
thread_count = strtol(argv[1], &endptr, 10);
|
||||
if (argv[1][0] == '\0' || *endptr != '\0' ||
|
||||
thread_count < 1 ||
|
||||
thread_count > MAX_THREADS) {
|
||||
// if argv[1] is not a valid decimal number, or
|
||||
// is not in the range
|
||||
fprintf(stderr, "Invalid number of threads, "
|
||||
"terminating application!\n");
|
||||
print_usage(stderr);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Wrong arguments, terminating application!\n");
|
||||
print_usage(stderr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
int i, count;
|
||||
for (i = 0, count = 0; i < thread_count; i++) {
|
||||
threads[count].index = count;
|
||||
// we set this before the thread starts so it points to a valid
|
||||
// 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, &writer_thread, &threads[count])) {
|
||||
pthread_detach(id);
|
||||
fprintf(stderr, "Failed to create thread %d\n", i);
|
||||
continue;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
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);
|
||||
thread_count = count;
|
||||
// Update thread count to show the number of threads actually created,
|
||||
// not the number requested
|
||||
|
||||
unsigned char *data = cairo_image_surface_get_data(surface);
|
||||
// draw_mandelbrot(data, h, w, mandelbrot.centerX, mandelbrot.centerY, mandelbrot.scale);
|
||||
if (thread_count == 0) {
|
||||
fprintf(stderr, "Failed to start any threads, terminating "
|
||||
"application\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
MPI_Win_fence(0, window);
|
||||
memcpy(data, pixmap, 4*h*w);
|
||||
cairo_surface_mark_dirty(surface);
|
||||
// cairo_paint(cr);
|
||||
// Main thread actually running GTK UI from here on out
|
||||
|
||||
cairo_destroy(cr);
|
||||
int stat = 0;
|
||||
|
||||
GtkApplication *app;
|
||||
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), argc, argv);
|
||||
g_object_unref(app);
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
void plane_resize(GtkWidget *widget, int width, int height)
|
||||
void print_usage(FILE *stream)
|
||||
{
|
||||
if (!surface)
|
||||
create_surface(widget);
|
||||
char *err_msg = "Mandelbrot visualiser (visor):\n"
|
||||
"Usage:\n"
|
||||
"visor -h : Show this help\n"
|
||||
"visor [threads] : Run the GTK visualizer with a given number\n"
|
||||
" of threads (default " STR(DEFAULT_THREADS) ")\n"
|
||||
" [1.." STR(MAX_THREADS) "]\n";
|
||||
fprintf(stream, "%s", err_msg);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
GtkDrawingArea *da;
|
||||
|
||||
static void app_activate(GApplication *app)
|
||||
{
|
||||
|
@ -155,7 +299,6 @@ static void app_activate(GApplication *app)
|
|||
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");
|
||||
|
@ -181,13 +324,14 @@ static void app_activate(GApplication *app)
|
|||
|
||||
bigBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
|
||||
// global to enable other functions to call for a redraw
|
||||
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);
|
||||
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(da), blit_plane, NULL, NULL);
|
||||
g_signal_connect(da, "resize", G_CALLBACK(plane_resize), NULL);
|
||||
|
||||
// TODO: implement later drag = gtk_gesture_drag_new();
|
||||
|
@ -199,81 +343,107 @@ static void app_activate(GApplication *app)
|
|||
gtk_window_present(GTK_WINDOW(win));
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
gboolean queue_redraw_plane(void *arg)
|
||||
{
|
||||
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;
|
||||
(void) arg;
|
||||
bool allWritersHadCompleted = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (!threads[i].complete) {
|
||||
allWritersHadCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (!allWritersHadCompleted) {
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
|
||||
MPI_Finalize();
|
||||
gtk_widget_queue_draw(GTK_WIDGET(da));
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void blit_plane(GtkDrawingArea *da, cairo_t *cr, int width, int height, gpointer data)
|
||||
{
|
||||
(void) da;
|
||||
(void) width;
|
||||
(void) height;
|
||||
(void) data;
|
||||
bool allWritersHadCompleted = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (!threads[i].complete) {
|
||||
allWritersHadCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allWritersHadCompleted) {
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_paint(cr);
|
||||
}
|
||||
}
|
||||
|
||||
void plane_resize(GtkWidget *widget)
|
||||
{
|
||||
create_surface(widget);
|
||||
}
|
||||
|
||||
void create_surface(GtkWidget *widget)
|
||||
{
|
||||
// Stop all current drawers
|
||||
pthread_mutex_lock(&pixmapMutex);
|
||||
pixmapAvailable = false; // Mark drawing area as unavailable
|
||||
pthread_mutex_unlock(&pixmapMutex);
|
||||
|
||||
bool allWritersStopped;
|
||||
do { // Wait until all writing threads have been stopped
|
||||
allWritersStopped = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (threads[i].drawing) {
|
||||
allWritersStopped = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!allWritersStopped);
|
||||
|
||||
// If all drawers hadn't completed, we need to call
|
||||
// cairo_surface_mark_dirty in the main thread
|
||||
bool allWritersHadCompleted = true;
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
if (!threads[i].complete) {
|
||||
allWritersHadCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allWritersHadCompleted && surface != NULL) {
|
||||
cairo_surface_mark_dirty(surface);
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < thread_count; i++) {
|
||||
threads[i].complete = false;
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
|
||||
gtk_widget_get_width(widget),
|
||||
gtk_widget_get_height(widget));
|
||||
|
||||
int h = cairo_image_surface_get_height(surface);
|
||||
int w = cairo_image_surface_get_width(surface);
|
||||
mandelbrot.height = h;
|
||||
mandelbrot.width = w;
|
||||
|
||||
// Mark the surface as ready to be drawn by memory access
|
||||
// Then notify other threads to start drawing
|
||||
// One of these threads will make the corresponding call using
|
||||
// cairo_surface_mark_dirty, to notify they are all done drawing
|
||||
cairo_surface_flush(surface);
|
||||
pixmap = cairo_image_surface_get_data(surface);
|
||||
|
||||
// This lock is unnecessary since all other threads are stopped already
|
||||
pthread_mutex_lock(&pixmapMutex);
|
||||
pixmapAvailable = true;
|
||||
pthread_mutex_unlock(&pixmapMutex);
|
||||
pthread_cond_broadcast(&pixmapCond);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue