summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--docs/demo.mp4bin0 -> 40490910 bytes
-rw-r--r--docs/demo_1.pngbin0 -> 2678925 bytes
-rw-r--r--docs/demo_2.pngbin0 -> 2044428 bytes
-rw-r--r--docs/demo_3.pngbin0 -> 1114088 bytes
-rw-r--r--docs/dia/diagram.diabin0 -> 2942 bytes
-rw-r--r--docs/dia/div_request.pngbin0 -> 158900 bytes
-rw-r--r--docs/p4.pdfbin0 -> 5197562 bytes
-rw-r--r--gdbinit.gdb1
-rw-r--r--libpng_wrapper.cpp46
-rw-r--r--libpng_wrapper.hpp42
-rw-r--r--main.cpp208
-rw-r--r--makefile21
-rw-r--r--mthread.cpp204
-rw-r--r--mthread.hpp71
-rw-r--r--notes109
-rw-r--r--out.pngbin0 -> 2679921 bytes
-rw-r--r--out1.pngbin0 -> 2679899 bytes
-rw-r--r--test.cpp35
-rw-r--r--test.hpp6
20 files changed, 745 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0f7faa4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build/*
+mandelbrot
diff --git a/docs/demo.mp4 b/docs/demo.mp4
new file mode 100644
index 0000000..0a40287
--- /dev/null
+++ b/docs/demo.mp4
Binary files differ
diff --git a/docs/demo_1.png b/docs/demo_1.png
new file mode 100644
index 0000000..94f583e
--- /dev/null
+++ b/docs/demo_1.png
Binary files differ
diff --git a/docs/demo_2.png b/docs/demo_2.png
new file mode 100644
index 0000000..cb26cba
--- /dev/null
+++ b/docs/demo_2.png
Binary files differ
diff --git a/docs/demo_3.png b/docs/demo_3.png
new file mode 100644
index 0000000..60b35a2
--- /dev/null
+++ b/docs/demo_3.png
Binary files differ
diff --git a/docs/dia/diagram.dia b/docs/dia/diagram.dia
new file mode 100644
index 0000000..45cb6cc
--- /dev/null
+++ b/docs/dia/diagram.dia
Binary files differ
diff --git a/docs/dia/div_request.png b/docs/dia/div_request.png
new file mode 100644
index 0000000..1812630
--- /dev/null
+++ b/docs/dia/div_request.png
Binary files differ
diff --git a/docs/p4.pdf b/docs/p4.pdf
new file mode 100644
index 0000000..858774b
--- /dev/null
+++ b/docs/p4.pdf
Binary files differ
diff --git a/gdbinit.gdb b/gdbinit.gdb
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/gdbinit.gdb
@@ -0,0 +1 @@
+
diff --git a/libpng_wrapper.cpp b/libpng_wrapper.cpp
new file mode 100644
index 0000000..a0d48e3
--- /dev/null
+++ b/libpng_wrapper.cpp
@@ -0,0 +1,46 @@
+#include <string>
+#include <stdio.h>
+#include <string.h>
+#include "libpng_wrapper.hpp"
+
+using namespace std;
+
+png::png(string filename, uint32_t width, uint32_t height) {
+ //have to use the old C way to access files for libPNGs sake
+ output_fp = fopen(filename.c_str(), "wb");
+ if(!output_fp) throw PNG_FILE_ERROR;
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if(!png_ptr) throw PNG_LIBPNG_ERROR;
+
+ png_info_ptr = png_create_info_struct(png_ptr);
+ if(!png_info_ptr) throw PNG_LIBPNG_ERROR;
+
+ png_init_io(png_ptr, output_fp);
+
+ png_set_IHDR(png_ptr, png_info_ptr, width, height, DEFAULT_BIT_DEPTH, DEFAULT_COLOR_TYPE,
+ (DEFAULT_INTERLACE) ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ row_pointers = new png_bytep[height * sizeof(png_bytep)];
+ for(size_t row = 0; row < height; row++)
+ row_pointers[row] = new png_byte[png_get_rowbytes(png_ptr, png_info_ptr)];
+}
+
+png::~png() {
+ png_write_info(png_ptr, png_info_ptr);
+ png_write_image(png_ptr, row_pointers);
+ png_write_end(png_ptr, NULL);
+ png_destroy_write_struct(&png_ptr, &png_info_ptr);
+ fclose(output_fp);
+}
+
+void png::set_pixel(uint32_t x, uint32_t y, png_byte r, png_byte g, png_byte b) {
+ row_pointers[y][x * 3] = r;
+ row_pointers[y][(x * 3) + 1] = g;
+ row_pointers[y][(x * 3) + 2] = b;
+}
+
+
+uint32_t png::width() { return png_get_image_width(png_ptr, png_info_ptr); }
+uint32_t png::height() { return png_get_image_height(png_ptr, png_info_ptr); }
diff --git a/libpng_wrapper.hpp b/libpng_wrapper.hpp
new file mode 100644
index 0000000..cf95dff
--- /dev/null
+++ b/libpng_wrapper.hpp
@@ -0,0 +1,42 @@
+#ifndef PNGWRAP_H
+#define PNGWRAP_H
+
+#include <string>
+#include <array>
+#include <png.h>
+
+#define PNG_FILE_ERROR 0
+#define PNG_LIBPNG_ERROR 1
+
+struct rgb {
+ png_byte r;
+ png_byte g;
+ png_byte b;
+};
+
+
+class png {
+ protected:
+ const int DEFAULT_BIT_DEPTH = 8;
+ const int DEFAULT_COLOR_TYPE = PNG_COLOR_TYPE_RGB;
+ const bool DEFAULT_INTERLACE = false;
+ const int DEFAULT_TRANSFORMS = PNG_TRANSFORM_IDENTITY;
+ FILE *output_fp;
+ png_structp png_ptr;
+ png_infop png_info_ptr;
+ png_byte **row_pointers;
+
+ public:
+ //the user doesn't have a lot of control over png output settings.
+ //Will expand class to allow so if it's found nessesary.
+ ~png();
+ png(std::string filename, uint32_t width, uint32_t height);
+ void save();
+
+ void set_pixel(uint32_t x, uint32_t y, png_byte r, png_byte g, png_byte b);
+
+ uint32_t width();
+ uint32_t height();
+};
+
+#endif
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..e801015
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,208 @@
+#include <thread>
+#include <vector>
+#include <iostream>
+#include <chrono>
+#include <complex>
+#include <atomic>
+#include <unistd.h>
+#include <sstream>
+#include "libpng_wrapper.hpp"
+#include "mthread.hpp"
+#include "test.hpp"
+
+using namespace std;
+
+//defaults
+
+//TODO remove temp_settings
+const uint32_t DEFAULT_WIDTH = 1920;
+const uint32_t DEFAULT_HEIGHT = 1080;
+const int DEFAULT_JOBS = 1;
+const string DEFAULT_IMG_PATH = "out.png";
+
+//CONTENDOR_HI REPLACEMENT - 50000 iterations
+const complex<double> DEFAULT_MIN_CORD (-0.74364386269 - 0.00000003000, 0.13182590271 - 0.00000003000);
+const complex<double> DEFAULT_MAX_CORD (-0.74364386269 + 0.00000003000, 0.13182590271 + 0.00000003000);
+
+//CONTENDOR_ZOOM- 50000 iterations
+//const complex<double> DEFAULT_MIN_CORD (-0.74364386269 - 0.00000001000, 0.13182590271 - 0.00000001000);
+//const complex<double> DEFAULT_MAX_CORD (-0.74364386269 + 0.00000001000, 0.13182590271 + 0.00000001000);
+
+const unsigned int DEFAULT_ITERS = 50000;
+const unsigned int DEFAULT_BAILOUT = 256;
+
+void print_help(char *arg, bool error) {
+ stringstream help_text;
+ help_text.precision(numeric_limits<double>::max_digits10);
+ help_text << "Usage: " << arg << " [options]\n"
+ "Options:\n"
+ "\t-h\tthis cruft\n"
+ "\t-w\timage width \t\t\t\tdefault: " << DEFAULT_WIDTH << "\n"
+ "\t-H\timage height\t\t\t\tdefault: " << DEFAULT_HEIGHT << "\n"
+ "\t-o\timage output path\t\t\tdefault: " << DEFAULT_IMG_PATH << "\n"
+ "\t-j\tjobs -- set this to your corecount\tdefault: " << DEFAULT_JOBS << "\n"
+ "\t-c\tcomplex bottom border\t\t\tdefault: " << DEFAULT_MIN_CORD << "\n"
+ "\t-C\tcomplex top border\t\t\tdefualt: " << DEFAULT_MAX_CORD << " \n"
+ "\t-i\tfractal iterations\t\t\tdefault: " << DEFAULT_ITERS << "\n"
+ "\t-I\tbailout value\t\t\t\tdefault: " << DEFAULT_BAILOUT << "\n"
+ "\nFOR COMPLEX NUMBERS: if you want to input, say, 2-3i, your option argument will be \"(2,-3)\".\n";
+ cout << help_text.str() << endl;
+ exit(error);
+}
+
+template <class t>
+bool getopt_int(t& number, char *optarg, char opt, char *arg) {
+ try { number = stoi(optarg); }
+ catch(invalid_argument const&) {
+ cout << "You must supply an integer for option -" << opt << "." << endl;
+ print_help(arg, true);
+ return true;
+ }
+ catch(out_of_range const&) {
+ cout << "You must supply an integer under " << numeric_limits<t>::max() << " for option -" << opt << "." << endl;
+ print_help(arg, true);
+ return true;
+ }
+ return false;
+}
+
+
+
+int main(int argc, char **argv) {
+
+ //argument options
+ uint32_t width = DEFAULT_WIDTH;
+ uint32_t height = DEFAULT_HEIGHT;
+ unsigned int jobs = DEFAULT_JOBS;
+ string img_path = DEFAULT_IMG_PATH;
+ complex<double> min_cord = DEFAULT_MIN_CORD;
+ complex<double> max_cord = DEFAULT_MAX_CORD;
+ unsigned int m_iters = DEFAULT_ITERS;
+ unsigned int bailout = DEFAULT_BAILOUT;
+ bool jobs_set = false;
+
+ //I could not find a better way to turn a string into a complex variable
+ stringstream complex_str_buffer;
+ int arg;
+ while((arg = getopt(argc, argv, "hw:H:o:j:c:C:i:I:")) != -1) {
+ switch(arg) {
+ case 'h':
+ print_help(argv[0], false);
+ break;
+ case 'w':
+ getopt_int(width, optarg, 'w', argv[0]);
+ break;
+ case 'H':
+ getopt_int(height, optarg, 'H', argv[0]);
+ break;
+ case 'o':
+ img_path = optarg;
+ break;
+ case 'j':
+ getopt_int(jobs, optarg, 'j', argv[0]);
+ jobs_set = true;
+ break;
+ case 'c':
+ complex_str_buffer << optarg;
+ complex_str_buffer >> min_cord;
+ break;
+ case 'C':
+ complex_str_buffer << optarg;
+ complex_str_buffer >> max_cord;
+ break;
+ case 'i':
+ getopt_int(m_iters, optarg, 'i', argv[0]);
+ break;
+ case 'I':
+ getopt_int(bailout, optarg, 'I', argv[0]);
+ break;
+ default:
+ cout << "Invalid option." << endl;
+ print_help(argv[0], true);
+ exit(1);
+ break;
+ }
+ }
+
+ if(!jobs_set) {
+ cout << "\nPERFORMANCE TIP: for best preformance, set jobs to the number of cores in your CPU.\n"
+ "See " << argv[0] << " -h for help.\n" << endl;
+ }
+
+ double *vmap = new double[width * height];
+ unsigned int *histogram = new unsigned int[m_iters]();
+ unsigned int histogram_sum = 0;
+ double *freq_hue = new double[m_iters]();
+ double current_hue = 0;
+
+ unsigned int width_per_job = width / jobs;
+ atomic<uint32_t> progress(0);
+
+ png image(img_path, width, height);
+ thread threads[jobs];
+
+
+ //allocate worker threads, spawn workers
+ mthread** worker_objects = (mthread **)malloc(sizeof(mthread) * jobs);
+
+
+ for(unsigned int j = 0; j < jobs - 1; j++) {
+ worker_objects[j] = new mthread(j * width_per_job, (j + 1) * width_per_job,
+ min_cord, max_cord, bailout, m_iters,
+ image, vmap, histogram, worker_objects, j, jobs, progress);
+ }
+
+ //last worker thread needs the width to go all the way to the edge of the screen,
+ //regardless of rounding issues
+ worker_objects[jobs - 1] = new mthread((jobs - 1) * width_per_job, width - 1,
+ min_cord, max_cord, bailout, m_iters,
+ image, vmap, histogram, worker_objects, jobs - 1, jobs, progress);
+
+
+ for(unsigned int j = 0; j < jobs; j++) worker_objects[j]->dispatch();
+
+ //the progress variables is simply how many pixels we have calculated
+ while(progress < (height * jobs)) {
+ cout << "\033[2K\033[0GCalculating pixel values... " << ((float)progress / (height * jobs)) * 100 << "\% complete" << flush;
+ this_thread::sleep_for(chrono::milliseconds(100));
+ }
+ cout << endl;
+
+ for(unsigned int j = 0; j < jobs; j++) worker_objects[j]->join();
+
+ //now to color the image
+ cout << "Coloring image... (this shouldn't take more then a few seconds)" << endl;
+
+ //find the sum of all histogram values, we could ajust this to increase or decrease contrast
+ for(unsigned int p = 0; p < m_iters; p++) histogram_sum += histogram[p];
+
+ for(unsigned int i = 0; i < m_iters; i++) {
+ current_hue += histogram[i] / (double)histogram_sum;
+ freq_hue[i] = current_hue;
+ }
+
+ //now to calculate the colors
+ {
+ double below, above, hue;
+ int c;
+ uint32_t x, y;
+ int rgb[3];
+ for(y = 0; y < height; y++) {
+ for(x = 0; x < width; x++) {
+ below = freq_hue[(int)vmap[(y * width) + x]];
+ above = freq_hue[(int)ceil(vmap[((y * width) + x) + 1])];
+ hue = (((above - below) * fmod(vmap[(y * width) + x], 1.0)) + below);
+ rgb[0] = 255 * cos((M_PI * hue) - M_PI);
+ rgb[1] = 255 * cos((M_PI * hue) - ((M_PI) / 2.0));
+ rgb[2] = 255 * cos(M_PI * hue);
+ for(c = 0; c < 3; c++) if(rgb[c] < 0) rgb[c] = 0;
+ image.set_pixel(x, y, (png_byte)rgb[0], (png_byte)rgb[1], (png_byte)rgb[2]);
+ }
+ }
+ }
+
+ for(unsigned int j = 0; j < jobs; j++) delete worker_objects[j];
+
+ cout << "Image exported to " << img_path << "." << endl;
+
+}
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..fad6a86
--- /dev/null
+++ b/makefile
@@ -0,0 +1,21 @@
+CCOPTS=-ggdb -O0 -Wall -lpng -pthread
+OUTFILE=mandelbrot
+
+SRCFILES := $(wildcard *.cpp)
+
+OBJFILES := $(patsubst %.cpp,build/%.o,$(SRCFILES))
+
+all: $(OBJFILES)
+ g++ $(CCOPTS) -o $(OUTFILE) $(OBJFILES)
+
+build/%.o: %.cpp
+ if [ ! -d "build" ]; then mkdir -p build; fi
+ g++ $(CCOPTS) -c -o $@ $<
+
+clean:
+ rm -f build/*.o
+ rm -f *.out
+ rm -f compile_commands.json
+
+run:
+ ./$(OUTFILE)
diff --git a/mthread.cpp b/mthread.cpp
new file mode 100644
index 0000000..796055e
--- /dev/null
+++ b/mthread.cpp
@@ -0,0 +1,204 @@
+#include "mthread.hpp"
+#include <iostream>
+#include <complex>
+#include <unistd.h>
+#include <thread>
+#include <chrono>
+#include <cmath>
+#include <algorithm>
+#include <atomic>
+using namespace std;
+
+mthread::mthread(
+ unsigned int x_mn, unsigned int x_mx, complex<double> c_min, complex<double> c_max,
+ unsigned int inf_cutoff, unsigned int max_iter, png& image, double *g_vmap, unsigned int *g_histogram,
+ mthread **worker_list, unsigned int id, unsigned int jobs, atomic<uint32_t>& progress)
+ : x_min_orig(x_mn), x_max_orig(x_mx),
+ c_min(c_min), c_max(c_max),
+ inf_cutoff(inf_cutoff), max_iter(max_iter), image(image), id(id), worker_cnt(jobs), progress(progress){
+
+ workers = worker_list;
+ x_min = x_mn;
+ x_max = x_mx;
+ y_min = 0;
+ y_max = image.height();
+ vmap = g_vmap;
+ histogram = g_histogram;
+ step = (c_max - c_min) / complex<double>(image.height(), image.width());
+ my_thread = NULL;
+}
+
+void mthread::dispatch() {
+ if((my_thread) && (my_thread->joinable())) delete my_thread;
+ my_thread = new thread([this] {render();});
+}
+
+
+mthread::~mthread() {
+ if((my_thread) && (my_thread->joinable())) {
+ my_thread->join();
+ delete my_thread;
+ }
+}
+
+
+void mthread::join() {
+ if((my_thread) && (my_thread->joinable())) my_thread->join();
+}
+
+
+void mthread::render() {
+ uint32_t image_width = image.width();
+ unsigned int iter;
+ unsigned int worker, workers_finished;
+ uint32_t loads[worker_cnt];
+ double pixel_value;
+ complex<double> c, a;
+ struct mthread_status *peer_status;
+ struct mthread_divinfo divinfo;
+
+ unique_lock<mutex> ack;
+ unique_lock<mutex> syn_ack;
+
+
+ status.status_lock.lock();
+ status.searching = false;
+ status.share_finished = false;
+ status.div_syn = false;
+ status.div_error = false;
+ status.status_lock.unlock();
+
+
+ y_min = 0;
+ y_max = image.height();
+
+
+
+ for(;;) {
+ //thread is actively rendering
+ for(on_y = y_min; on_y < y_max; on_y++) {
+ progress++;
+ status.status_lock.lock();
+ status.row_load = y_max - on_y;
+ if(status.div_syn) {
+
+ status.div_error = status.row_load <= min_lines;
+
+ if(status.div_error) {
+ status.ack_lk.unlock();
+ status.msg_notify.notify_all();
+ }
+ else {
+ syn_ack = unique_lock<mutex>(status.syn_ack_lk);
+ status.ack_lk.unlock();
+ status.msg_notify.notify_all();
+ status.msg_notify.wait(syn_ack);
+ status.row_load = y_max - on_y;
+ syn_ack.unlock();
+ //new x/y min/max is ajusted by other thread, we can continue as normal.
+ }
+ }
+ status.status_lock.unlock();
+
+ for(on_x = x_min; on_x < x_max; on_x++) {
+ c = (step * complex<double>(on_x,on_y)) + c_min;
+ a = 0;
+ for(iter = 0; iter < max_iter; iter++) {
+ if(abs(a) >= inf_cutoff) break;
+ a = a*a + c;
+ }
+ if(iter >= max_iter) {
+ iter = 0;
+ vmap[(on_y * image_width) + on_x] = 0;
+ }
+ else {
+ pixel_value = (iter + 1) - (log((log(pow(abs(a), 2.0)) / 2.0) / log(2.0)));
+ vmap[(on_y * image_width) + on_x] = pixel_value;
+ histogram[(int)pixel_value]++;
+ }
+ }
+ }
+
+
+ //thread is now searching for work
+
+ /** 2022 comment:
+ * this state should have been moved to a seperate function to allow rendering methods to differ
+ * from inherited mthreads without needing to reimpliment searching **/
+ status.status_lock.lock();
+ status.searching = true;
+ status.share_finished = true;
+ status.status_lock.unlock();
+
+ while(status.searching) {
+ workers_finished = 0;
+ for(worker = 0; worker < worker_cnt; worker++) {
+ peer_status = &workers[worker]->status;
+ peer_status->status_lock.lock();
+
+ if((peer_status->share_finished) && (worker != id)) {
+ workers_finished++;
+ }
+ if((worker == id) ||
+ (peer_status->searching) || (peer_status->div_syn) || (peer_status->row_load < min_lines)) {
+ loads[worker] = 0;
+ peer_status->status_lock.unlock();
+ continue;
+ }
+ loads[worker] = peer_status->row_load;
+ peer_status->status_lock.unlock();
+ }
+ if(workers_finished >= worker_cnt - 1) {
+ return;
+ }
+ for(;;) {
+ worker = distance(loads, max_element(loads, &loads[worker_cnt]));
+ if(!loads[worker]) break;
+ peer_status = &workers[worker]->status;
+ peer_status->status_lock.lock();
+ if((peer_status->searching) || (peer_status->div_syn) || (peer_status->row_load < min_lines)) {
+ loads[worker] = 0;
+ peer_status->status_lock.unlock();
+ continue;
+ }
+ ack = unique_lock<mutex>(peer_status->ack_lk);
+ peer_status->div_syn = true;
+ peer_status->status_lock.unlock();
+ peer_status->msg_notify.wait(ack);
+ ack.unlock();
+ if(peer_status->div_error) {
+ loads[worker] = 0;
+ peer_status->status_lock.lock();
+ peer_status->div_error = false;
+ peer_status->div_syn = false;
+ peer_status->status_lock.unlock();
+ continue;
+ }
+
+ divinfo = workers[worker]->divide();
+ peer_status->syn_ack_lk.unlock();
+ peer_status->msg_notify.notify_all();
+ y_min = divinfo.y_min;
+ y_max = divinfo.y_max;
+ x_min = divinfo.x_min;
+ x_max = divinfo.x_max;
+ status.status_lock.lock();
+ status.searching = false;
+ status.status_lock.unlock();
+ break;
+ }
+ }
+ }
+}
+
+struct mthread_divinfo mthread::divide() {
+ struct mthread_divinfo ret;
+ ret.x_min = x_min;
+ ret.x_max = x_max;
+ ret.y_min = ((y_max - on_y) / 2) + on_y;
+ ret.y_max = y_max;
+ y_min = on_y;
+ y_max = ret.y_min;
+ status.div_syn = false;
+ return ret;
+}
diff --git a/mthread.hpp b/mthread.hpp
new file mode 100644
index 0000000..c70647e
--- /dev/null
+++ b/mthread.hpp
@@ -0,0 +1,71 @@
+#ifndef MTHREAD_H
+#define MTHREAD_H
+
+#include <mutex>
+#include <atomic>
+#include <thread>
+#include <complex>
+#include <condition_variable>
+#include "libpng_wrapper.hpp"
+
+struct mthread_status {
+ std::mutex status_lock;
+ unsigned int row_load;
+ bool share_finished;
+ bool searching;
+
+ bool div_syn;
+ std::mutex ack_lk;
+ std::mutex syn_ack_lk;
+ bool div_error;
+
+ std::condition_variable msg_notify;
+};
+
+struct mthread_divinfo {
+ uint32_t x_min, x_max;
+ uint32_t y_min, y_max;
+};
+
+class mthread {
+ public:
+ mthread(unsigned int x_mn, unsigned int x_mx,
+ std::complex<double> c_min, std::complex<double> c_max,
+ unsigned int inf_cutoff, unsigned int max_iter, png& image, double *g_vmap, unsigned int *g_histogram,
+ mthread **worker_list, unsigned int id, unsigned int jobs, std::atomic<uint32_t>& progress);
+ ~mthread();
+ void join();
+ void dispatch();
+
+
+ struct mthread_status status;
+ unsigned int *histogram;
+ protected:
+ const unsigned int min_lines = 4;
+ const unsigned int x_min_orig, x_max_orig;
+ const std::complex<double> c_min;
+ const std::complex<double> c_max;
+ const unsigned int inf_cutoff;
+ const unsigned int max_iter;
+ png& image;
+ double *vmap;
+ const unsigned int id;
+ mthread **workers;
+ std::complex<double> c_current_min;
+ std::complex<double> c_current_max;
+ std::complex<double> step;
+ std::thread *my_thread;
+ const unsigned int worker_cnt;
+ std::atomic<uint32_t>& progress;
+
+ uint32_t x_min, x_max, y_min, y_max;
+ uint32_t on_x, on_y;
+ unsigned int divisions;
+ void render();
+ int state;
+ struct mthread_divinfo divide();
+
+
+};
+
+#endif
diff --git a/notes b/notes
new file mode 100644
index 0000000..23b8396
--- /dev/null
+++ b/notes
@@ -0,0 +1,109 @@
+NOTES TO SELF
+
+atomic struct mthread_status {
+ unsigned int area_covered
+ bool undevidable
+ bool searching
+ bool division_syn
+ bool division_ack
+ bool division_syn_ack
+}
+
+
+Thread finishes its job.
+1: set "searching" local atomic struct to true
+2: for each thread:
+
+ if done=true; exit
+
+ if searing, division_syn, division_ack, undevidable: go to next
+ if all seem to be searching, set global done=true
+
+ if not: continue below
+
+3: record area_covered in own array, if more threads proceed to next, else continue below
+
+4: pick thread with largest area_covered:
+5: if division_syn or division_ack or searching or undevidable, go to next
+
+5: set division_syn to true, wait for division_ack
+6: divide(): copy over half of x/y, copy divisions, check and set undevidable, start to do work
+
+thread is working.
+1:
+for y:
+ update area_covered
+ check division_syn
+ if true: 2 inline here
+ for x:
+ do some math stuff here
+searching = true, "if thread finishes its job" inline here.
+
+
+2:
+set division_ack
+wait for division_syn_ack
+check and set undevidable
+// there should be no conflict with updating variable y is dependent on,
+// as it's always more then current, and test will be accurate next loop.
+
+
+thread is starting.
+1:
+for y:
+ update area_covered
+ check division_syn
+ if true: 2 inline here
+ for x:
+ do some math stuff here
+searching = true, "if thread finishes its job" inline here.
+
+
+PREFORMANCE:
+without tasks helping eachother (commit c30fc7596810f7033dfd9a7452c808153bd2e14a):
+
+const uint32_t WIDTH = 1920;
+const uint32_t HEIGHT = 1080;
+const int JOBS = 6; //test uneven stuff
+const std::complex<double> b_min (-0.7463-0.005, 0.1102-0.005);
+const std::complex<double> b_max (-0.7463+0.005, 0.1102+0.005);
+#define MAX_ITER 1000
+#define INF_CUTOFF 256
+#define COLOR_RAMP 100
+
+/usr/bin/time -f '%p' -p ./mandelbrot
+
+real 36.41
+user 105.00
+sys 0.02
+
+real 36.38
+user 104.98
+sys 0.02
+
+real 36.32
+user 105.00
+sys 0.05
+
+With tasks helping eachother: (commit 3663966b88681f44ec8939e39e33ef922227b7a7):
+
+real 19.21
+user 111.70
+sys 0.03
+
+real 19.29
+user 111.61
+sys 0.02
+
+real 19.30
+user 111.84
+sys 0.02
+
+
+//CONTENDOR_HI REPLACEMENT - 50000 iterations
+//const complex<double> DEFAULT_B_MIN (-0.74364386269 - 0.00000003000, 0.13182590271 - 0.00000003000);
+//const complex<double> DEFAULT_B_MAX (-0.74364386269 + 0.00000003000, 0.13182590271 + 0.00000003000);
+
+//CONTENDOR_ZOOM- 50000 iterations
+//const complex<double> DEFAULT_B_MIN (-0.74364386269 - 0.00000001000, 0.13182590271 - 0.00000001000);
+//const complex<double> DEFAULT_B_MAX (-0.74364386269 + 0.00000001000, 0.13182590271 + 0.00000001000);
diff --git a/out.png b/out.png
new file mode 100644
index 0000000..c85eb9b
--- /dev/null
+++ b/out.png
Binary files differ
diff --git a/out1.png b/out1.png
new file mode 100644
index 0000000..f572131
--- /dev/null
+++ b/out1.png
Binary files differ
diff --git a/test.cpp b/test.cpp
new file mode 100644
index 0000000..bc2366c
--- /dev/null
+++ b/test.cpp
@@ -0,0 +1,35 @@
+#include <iostream>
+#include <string>
+#include "libpng_wrapper.hpp"
+using namespace std;
+
+void test_png() {
+ png test_image("test_png.png", 500, 500);
+ uint32_t w = test_image.width();
+ uint32_t h = test_image.height();
+
+ uint32_t square_w = w / 2;
+ uint32_t square_h = h / 2;
+ uint32_t square_x = square_w;
+ uint32_t square_y = square_h;
+
+ for(uint32_t y = 0; y < h; y++) {
+ for(uint32_t x = 0; x < w; x++) {
+ if((x < (w / 2)) && (y < (h / 2))) {
+ test_image.set_pixel(x, y, 255, 0, 0);
+ }
+ else if((x > (w / 2)) && (y < (h / 2))) {
+ test_image.set_pixel(x, y, 0, 255, 0);
+ }
+ else if((x < (w / 2)) && (y > (h / 2))) {
+ test_image.set_pixel(x, y, 0, 0, 255);
+ }
+ else if((x > (w / 2)) && (y > (h / 2))) {
+ test_image.set_pixel(x, y,
+ (255 / square_w) * (x - square_x),
+ (255 / square_h) * (y - square_y),
+ (255 / square_w) * (square_x - x));
+ }
+ }
+ }
+}
diff --git a/test.hpp b/test.hpp
new file mode 100644
index 0000000..3347496
--- /dev/null
+++ b/test.hpp
@@ -0,0 +1,6 @@
+#ifndef TEST_H
+#define TEST_H
+
+void test_png();
+
+#endif