init
This commit is contained in:
		
						commit
						8db9e4cfba
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| build/* | ||||
| mandelbrot | ||||
							
								
								
									
										
											BIN
										
									
								
								docs/demo.mp4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/demo.mp4
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								docs/demo_1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/demo_1.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.6 MiB | 
							
								
								
									
										
											BIN
										
									
								
								docs/demo_2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/demo_2.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.9 MiB | 
							
								
								
									
										
											BIN
										
									
								
								docs/demo_3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/demo_3.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.1 MiB | 
							
								
								
									
										
											BIN
										
									
								
								docs/dia/diagram.dia
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/dia/diagram.dia
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								docs/dia/div_request.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/dia/div_request.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 155 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docs/p4.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/p4.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								gdbinit.gdb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								gdbinit.gdb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 
 | ||||
							
								
								
									
										46
									
								
								libpng_wrapper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								libpng_wrapper.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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); } | ||||
							
								
								
									
										42
									
								
								libpng_wrapper.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								libpng_wrapper.hpp
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
							
								
								
									
										208
									
								
								main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								main.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										21
									
								
								makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								makefile
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||
							
								
								
									
										204
									
								
								mthread.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								mthread.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| } | ||||
							
								
								
									
										71
									
								
								mthread.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								mthread.hpp
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
							
								
								
									
										109
									
								
								notes
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								notes
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
							
								
								
									
										35
									
								
								test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								test.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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)); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Brett Weiland
						Brett Weiland