diff options
| author | Charles <sircharlesaze@gmail.com> | 2019-09-20 16:01:46 +0200 |
|---|---|---|
| committer | Charles <sircharlesaze@gmail.com> | 2019-09-20 16:01:46 +0200 |
| commit | 5efde1e3e65af769cb629d55f0a4dd4f87caebe9 (patch) | |
| tree | 88c685fad561e39fac869a36a93e98866e2ccb62 | |
| parent | 95b209426dd7a9f844cf1aa093a2b1c4301f049b (diff) | |
| download | mandelbrot_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-- | Makefile | 4 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | graphics.c | 79 | ||||
| -rw-r--r-- | header.h | 25 | ||||
| -rw-r--r-- | helper.c | 12 | ||||
| -rw-r--r-- | main.c | 16 | ||||
| -rw-r--r-- | mandelbrot.c | 70 |
7 files changed, 139 insertions, 73 deletions
@@ -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 @@ -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 @@ -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); @@ -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 @@ -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; +} @@ -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) |
