aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles <sircharlesaze@gmail.com>2019-09-20 16:01:46 +0200
committerCharles <sircharlesaze@gmail.com>2019-09-20 16:01:46 +0200
commit5efde1e3e65af769cb629d55f0a4dd4f87caebe9 (patch)
tree88c685fad561e39fac869a36a93e98866e2ccb62
parent95b209426dd7a9f844cf1aa093a2b1c4301f049b (diff)
downloadmandelbrot_cpu-5efde1e3e65af769cb629d55f0a4dd4f87caebe9.tar.gz
mandelbrot_cpu-5efde1e3e65af769cb629d55f0a4dd4f87caebe9.tar.bz2
mandelbrot_cpu-5efde1e3e65af769cb629d55f0a4dd4f87caebe9.zip
Parallel computation, pixel array
- All the pixels are stored in an array, put in a surface then a texture so we don't draw each pixel one by one. - The computation of this array is done in parallel (one thread by line) - Use sscanf instead of atoi hack to parse options
-rw-r--r--Makefile4
-rw-r--r--README.md6
-rw-r--r--graphics.c79
-rw-r--r--header.h25
-rw-r--r--helper.c12
-rw-r--r--main.c16
-rw-r--r--mandelbrot.c70
7 files changed, 139 insertions, 73 deletions
diff --git a/Makefile b/Makefile
index 904f534..85660a0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
NAME = mandel
CC = gcc
CCFLAGS = -Wall -Wextra
-LDFLAGS = $(shell sdl2-config --libs --cflags)
+LDFLAGS = -lpthread $(shell sdl2-config --libs --cflags)
HEADER = header.h
SRC = main.c graphics.c mandelbrot.c helper.c
@@ -19,7 +19,7 @@ $(NAME): $(OBJ)
$(CC) $(LDFLAGS) $(CCFLAGS) -c -o $@ $<
.PHONY: debug
-debug: CCFLAGS += -g
+debug: CCFLAGS += -g -fsanitize=address
debug: re
.PHONY: clean
diff --git a/README.md b/README.md
index e0108e4..24a5002 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ SDL2: `sudo apt install libsdl2-dev`
- [ ] Create color spectrum.
- [ ] Smooth shading for out of set iteration colors.
- [ ] Draw to bmp image.
-- [ ] Create pixel array and update the render line by line.
+- [x] Create pixel array and update the render line by line.
- [ ] Display coordinates and other useful info.
-- [ ] Computation done in parallel.
-- [ ] Command line options
+- [x] Computation done in parallel.
+- [x] Command line options
diff --git a/graphics.c b/graphics.c
index ce1384b..d8c6997 100644
--- a/graphics.c
+++ b/graphics.c
@@ -1,4 +1,5 @@
#include <stdbool.h>
+#include <pthread.h>
#include <SDL2/SDL.h>
#include "header.h"
@@ -19,9 +20,14 @@
#define SET_DRAW_COLOR(renderer, c) ( \
SDL_SetRenderDrawColor(renderer, c.rgb.r, c.rgb.g, c.rgb.b, SDL_ALPHA_OPAQUE));
+#define RED_MASK 0x0000ff
+#define GREEN_MASK 0x00ff00
+#define BLUE_MASK 0xff0000
+#define PITCH(ch, width) (ch * width)
+
static void update(GState *state);
static void event_handler(GState *state);
-/* static Color *create_palette(Color start, Color end); */
+static Color *create_palette(Color start, Color end);
/* static void move_center(GState *state, int motion_x, int motion_y); */
static void recenter(GState *state, int x, int y);
static void recenter_x(GState *state, int x);
@@ -53,6 +59,12 @@ GState *graphics_init(Config *config)
state->palette = create_palette(start, end);
if (state->palette == NULL)
error_exit_state(state, "unable to create color palette");
+ state->canvas = SDL_CreateTexture(
+ state->renderer, SDL_PIXELFORMAT_RGBA8888,
+ SDL_TEXTUREACCESS_TARGET, config->window_w, config->window_h
+ );
+ if (state->canvas == NULL)
+ error_exit_state(state, "unable to create canvas texture");
state->running = true;
state->window_w = config->window_w;
state->window_h = config->window_h;
@@ -62,6 +74,7 @@ GState *graphics_init(Config *config)
state->center.y = config->center_y;
state->in_set_color.hexcode = IN_SET_COLOR;
state->moving = false;
+ state->changed = true;
return state;
}
@@ -76,35 +89,52 @@ void graphics_run(GState *state)
while (state->running)
{
event_handler(state);
- update(state);
+ if (state->changed)
+ {
+ update(state);
+ state->changed = false;
+ }
SDL_Delay(REFRESH_RATE);
}
}
static void update(GState *state)
{
- double a, b;
- Color color;
- SET_DRAW_COLOR(state->renderer, state->in_set_color);
- SDL_RenderClear(state->renderer);
- for (int x = 0; x < state->window_w; x++)
+ void *pixels = mandelbrot_pixels(REAL_LO(state), REAL_HI(state),
+ IMAG_LO(state), IMAG_HI(state),
+ state->window_w, state->window_h, state->palette);
+ if (pixels == NULL)
+ error_exit_state(state, "unable to create pixels");
+ SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(
+ pixels, state->window_w, state->window_h,
+ PIXELS_DEPTH, PITCH(PIXELS_CHANELS, state->window_w),
+ RED_MASK, BLUE_MASK, GREEN_MASK, 0
+ );
+ if (surface == NULL)
{
- for (int y = 0; y < state->window_h; y++)
- {
- a = map_range((double)x, 0, state->window_w, REAL_LO(state), REAL_HI(state));
- b = map_range((double)y, 0, state->window_h, IMAG_LO(state), IMAG_HI(state));
- int frac_steps = mandelbrot_in_set(a, b);
- if (frac_steps == -1)
- continue;
- color = state->palette[frac_steps];
- /* color.rgb.r = 100 * frac_steps; */
- /* color.rgb.g = 100 * frac_steps; */
- /* color.rgb.b = 100 * frac_steps; */
- SET_DRAW_COLOR(state->renderer, color);
- SDL_RenderDrawPoint(state->renderer, x, y);
- }
+ free(pixels);
+ error_exit_state(state, "unable to create pixels surface");
+ }
+ SDL_Texture *texture = SDL_CreateTextureFromSurface(state->renderer, surface);
+ free(pixels);
+ SDL_FreeSurface(surface);
+ if (texture == NULL)
+ error_exit_state(state, "unable to create texture");
+ SDL_Rect frame;
+ if (SDL_QueryTexture(texture, NULL, NULL, &frame.w, &frame.h) != 0)
+ {
+ SDL_DestroyTexture(texture);
+ error_exit_state(state, "unable to load texture");
+ }
+ frame.x = 0;
+ frame.y = 0;
+ if (SDL_RenderCopy(state->renderer, texture, NULL, &frame) != 0)
+ {
+ SDL_DestroyTexture(texture);
+ error_exit_state(state, "unable to render texture");
}
SDL_RenderPresent(state->renderer);
+ SDL_DestroyTexture(texture);
}
static void event_handler(GState *state)
@@ -177,16 +207,17 @@ static void event_handler(GState *state)
/* printf("%f, %f\n", state->center.x, state->center.y); */
/* } */
}
+ state->changed = true;
}
}
- Color *create_palette(Color start, Color end)
+ static Color *create_palette(Color start, Color end)
{
int red_step = abs(end.rgb.r - start.rgb.r) / MAX_ITERATION;
int green_step = abs(end.rgb.g - start.rgb.g) / MAX_ITERATION;
int blue_step = abs(end.rgb.b - start.rgb.b) / MAX_ITERATION;
- Color *palette = (Color*)malloc(sizeof(Color) * MAX_ITERATION);
+ Color *palette = (Color*)malloc(sizeof(Color) * (MAX_ITERATION + 1));
if (palette == NULL)
return NULL;
for (int i = 0; i < MAX_ITERATION; i++)
@@ -195,6 +226,7 @@ static void event_handler(GState *state)
palette[i].rgb.g = i * green_step + start.rgb.g;
palette[i].rgb.b = i * blue_step + start.rgb.b;
}
+ palette[MAX_ITERATION].hexcode = 0x0;
return palette;
}
@@ -246,6 +278,7 @@ static void destroy_state(GState *state)
if (state == NULL)
return;
free(state->palette);
+ SDL_DestroyTexture(state->canvas);
SDL_DestroyRenderer(state->renderer);
SDL_DestroyWindow(state->window);
free(state);
diff --git a/header.h b/header.h
index 216db2d..fbaca08 100644
--- a/header.h
+++ b/header.h
@@ -4,11 +4,14 @@
# include <stdbool.h>
# include <SDL2/SDL.h>
-# define MAX_ITERATION 200
+# define MAX_ITERATION 210
# define ESCAPE_RADIUS 2
# define ESCAPE_RADIUS_SQUARED (ESCAPE_RADIUS * ESCAPE_RADIUS)
-typedef unsigned char Byte;
+# define PIXELS_CHANELS 3
+# define PIXELS_DEPTH (PIXELS_CHANELS * 8)
+
+typedef uint8_t Byte;
typedef int ColorHexcode;
typedef union
@@ -41,6 +44,8 @@ typedef struct
Color *palette;
Color in_set_color;
bool moving;
+ bool changed;
+ SDL_Texture *canvas;
} GState;
typedef struct
@@ -53,19 +58,29 @@ typedef struct
double center_y;
} Config;
+typedef struct
+{
+ double real_lo;
+ double real_hi;
+ int width;
+ double imag;
+ Color *palette;
+ uint8_t *row;
+} ThreadArgs;
+
// mandelbrot.c
int mandelbrot_in_set(double a, double b);
void mandelbrot_print(void);
-int *mandelbrot_array(Point center, double real_range, double imag_range,
- double real_len, double imag_len);
+void *mandelbrot_pixels(double real_lo, double real_hi, double imag_lo,
+ double imag_hi, int width, int height, Color *palette);
// graphics.c
GState *graphics_init(Config *config);
void graphics_quit(GState *state);
void graphics_run(GState *state);
-Color *create_palette(Color start, Color end);
// helper.c
double map_range(double x, double src_lo, double src_hi, double dest_lo, double dest_hi);
+int *inclusive_range(int start, int end);
#endif
diff --git a/helper.c b/helper.c
index 9920c5d..0290563 100644
--- a/helper.c
+++ b/helper.c
@@ -8,3 +8,15 @@ double map_range(double x, double src_lo, double src_hi, double dest_lo, double
return (x - src_lo) / src_len * dest_len + dest_lo;
}
+
+int *inclusive_range(int start, int end)
+{
+ if (end < start)
+ return NULL;
+ int *range = malloc(sizeof(int) * (end - start + 1));
+ if (range == NULL)
+ return NULL;
+ for (int i = 0; start < end; i++)
+ range[i] = start++;
+ return range;
+}
diff --git a/main.c b/main.c
index 4b19a53..51ac8c9 100644
--- a/main.c
+++ b/main.c
@@ -4,9 +4,8 @@
#include <string.h>
#include "header.h"
-#define CHAR_SIZE sizeof(char)
-#define DEFAULT_WINDOW_W 200
-#define DEFAULT_WINDOW_H 200
+#define DEFAULT_WINDOW_W 300
+#define DEFAULT_WINDOW_H 300
#define DEFAULT_CENTER_X 0.0
#define DEFAULT_CENTER_Y 0.0
#define DEFAULT_REAL_RANGE 4.0
@@ -41,20 +40,17 @@ int main(int argc, char **argv)
exit(EXIT_SUCCESS);
break;
case 's':
- config.window_w = atoi(optarg);
- config.window_h = atoi(strstr(optarg, ",") + CHAR_SIZE);
+ sscanf(optarg, "%d,%d", &config.window_w, &config.window_h);
break;
case 'r':
- config.real_range = atof(optarg);
- config.imag_range = atof(strstr(optarg, ",") + CHAR_SIZE);
+ sscanf(optarg, "%lf,%lf", &config.real_range, &config.imag_range);
break;
case 'c':
- config.center_x = atof(optarg);
- config.center_y = atof(strstr(optarg, ",") + CHAR_SIZE);
+ sscanf(optarg, "%lf,%lf", &config.center_x, &config.center_y);
break;
case '?':
default:
- fprintf(stderr, "Usage %s [pwh]", argv[0]);
+ fprintf(stderr, "Try: %s -h for more information", argv[0]);
exit(EXIT_FAILURE);
}
diff --git a/mandelbrot.c b/mandelbrot.c
index 9e7aa1b..1112575 100644
--- a/mandelbrot.c
+++ b/mandelbrot.c
@@ -1,5 +1,5 @@
#include <stdio.h>
-#include <math.h>
+#include <pthread.h>
#include "header.h"
#define PRINT_REAL_LO -2.0
@@ -13,13 +13,16 @@
#define IN_CHAR '*'
#define OUT_CHAR ' '
+static void *pixel_row(void *args);
+
int mandelbrot_in_set(double ca, double cb)
{
double zr = ca;
double zi = cb;
double zr_square;
double zi_square;
- for (int n = 0; n < MAX_ITERATION; n++)
+ int n;
+ for (n = 0; n < MAX_ITERATION; n++)
{
zi_square = zi * zi;
zr_square = zr * zr;
@@ -31,43 +34,50 @@ int mandelbrot_in_set(double ca, double cb)
zi += cb;
zr += ca;
}
- return -1;
+ return n;
}
-ColorHexcode mandelbrot_in_set_color(Color *spectrum, double a, double b)
+void *mandelbrot_pixels(double real_lo, double real_hi, double imag_lo,
+ double imag_hi, int width, int height, Color *palette)
{
- int steps = mandelbrot_in_set(a, b);
- int brightness = (255 / MAX_ITERATION) * steps;
- Color color;
- color.rgb.r = brightness;
- color.rgb.g = brightness;
- color.rgb.b = brightness;
- return color.hexcode;
-}
-
-uint8_t *mandelbrot_create_bits(int width, int height)
-{
- uint8_t *bits = (uint8_t*)malloc(3 * width * height);
- if (bits == NULL)
+ Byte *pixels = (Byte*)malloc(width * height * PIXELS_CHANELS);
+ if (pixels == NULL)
+ return NULL;
+ pthread_t *threads = (pthread_t*)malloc(sizeof(pthread_t) * height);
+ if (threads == NULL)
return NULL;
- return bits;
+ for (int y = 0; y < height; y++)
+ {
+ ThreadArgs *args = (ThreadArgs*)malloc(sizeof(ThreadArgs));
+ if (args == NULL)
+ return NULL;
+ args->real_lo = real_lo;
+ args->real_hi = real_hi;
+ args->width = width;
+ args->imag = map_range((double)y, 0, height, imag_lo, imag_hi);
+ args->palette = palette;
+ args->row = pixels + PIXELS_CHANELS * width * y;
+ pthread_create(&threads[y], NULL, pixel_row, args);
+ }
+ for (int y = 0; y < height; y++)
+ pthread_join(threads[y], NULL);
+ free(threads);
+ return (void*)pixels;
}
-void mandelbrot_set_bits(uint8_t *bits, Color *spectrum, Point center, double real_range,
- double imag_range, int width, int height)
+static void *pixel_row(void *void_args)
{
- double i = center.y - imag_range / 2;
- double r = center.x - real_range / 2;
-
- for (int array_i = 0; array_i < height; array_i++)
+ ThreadArgs *args = (ThreadArgs*)void_args;
+ for (int x = 0; x < args->width; x++)
{
- for (int array_j = 0; array_j < width; array_j++)
- {
- bits[array_i * (int)height + array_j] = mandelbrot_in_set_color(spectrum, r, i);
- r += real_range / width;
- }
- i += imag_range / height;
+ double a = map_range((double)x, 0, args->width, args->real_lo, args->real_hi);
+ Color color = args->palette[mandelbrot_in_set(a, args->imag)];
+ args->row[x * PIXELS_CHANELS] = color.rgb.r;
+ args->row[x * PIXELS_CHANELS + 1] = color.rgb.g;
+ args->row[x * PIXELS_CHANELS + 2] = color.rgb.b;
}
+ free(args);
+ return NULL;
}
void mandelbrot_print(void)